UniNEnetPrivate.cpp [plain text]
#include "UniNEnet.h"
#include "UniNEnetMII.h"
#include <libkern/OSByteOrder.h>
extern void *kernel_pmap;
extern globals g;
bool UniNEnet::allocateMemory()
{
UInt32 rxRingSize, txRingSize;
UInt32 i, n;
UInt8 *virtAddr;
UInt32 physBase;
UInt32 physAddr;
txRingSize = (TX_RING_LENGTH * sizeof(enet_txdma_cmd_t) + 2048 - 1) & ~(2048-1);
rxRingSize = (RX_RING_LENGTH * sizeof(enet_dma_cmd_t) + 2048 - 1) & ~(2048-1);
ELG( IOThreadSelf(), txRingSize << 16 | rxRingSize, 'Allo', "allocateMemory" );
dmaCommandsSize = round_page( txRingSize + rxRingSize );
if ( !dmaCommands )
{
dmaCommands = (UInt8 *)IOMallocContiguous( dmaCommandsSize, PAGE_SIZE, 0 );
if ( dmaCommands == 0 )
{
IOLog( "Ethernet(UniN): Cant allocate channel dma commands\n\r" );
return false;
}
}
n = (dmaCommandsSize - PAGE_SIZE) / PAGE_SIZE;
physBase = pmap_extract(kernel_pmap, (vm_address_t) dmaCommands);
virtAddr = (UInt8 *) dmaCommands;
for( i=0; i < n; i++, virtAddr += PAGE_SIZE )
{
physAddr = pmap_extract(kernel_pmap, (vm_address_t) virtAddr);
if (physAddr != (physBase + i * PAGE_SIZE) )
{
IOLog( "Ethernet(UniN): Cant allocate contiguous memory for dma commands\n\r" );
return false;
}
}
rxDMACommands = (enet_dma_cmd_t*)dmaCommands;
txDMACommands = (enet_txdma_cmd_t*)(dmaCommands + rxRingSize);
bzero( rxMbuf, sizeof( rxMbuf ) ); bzero( txMbuf, sizeof( txMbuf ) );
return true;
}
bool UniNEnet::initTxRing()
{
UInt32 i;
ELG( this, txDMACommands, 'ITxR', "initTxRing" );
bzero( (void *)txDMACommands, sizeof(enet_txdma_cmd_t) * TX_RING_LENGTH);
txCommandHead = 0;
txCommandTail = 0;
txDMACommandsPhys = pmap_extract(kernel_pmap, (vm_address_t) txDMACommands);
if ( txDMACommandsPhys == 0 )
{
IOLog( "Ethernet(UniN): Bad dma command buf - %08x\n\r",
(int)txDMACommands );
}
for ( i = 0; i < TX_RING_LENGTH; i++ )
{
if ( txMbuf[ i ] )
{
ELG( i, txMbuf[ i ], 'txpf', "UniNEnet::initTxRing - free the packet" );
freePacket( txMbuf[ i ] );
txMbuf[ i ] = 0;
}
}
txCommandsAvail = TX_RING_LENGTH - 1;
txIntCnt = 0;
txWDCount = 0;
return true;
}
bool UniNEnet::initRxRing()
{
UInt32 i;
bool status;
ELG( this, rxDMACommands, 'IRxR', "initRxRing" );
bzero( (void*)rxDMACommands, sizeof( enet_dma_cmd_t ) * RX_RING_LENGTH );
rxDMACommandsPhys = pmap_extract(kernel_pmap, (vm_address_t) rxDMACommands);
if ( rxDMACommandsPhys == 0 )
{
IOLog( "Ethernet(UniN): Bad dma command buf - %08x\n\r",
(int) rxDMACommands );
return false;
}
for ( i = 0; i < RX_RING_LENGTH; i++ )
{
if (rxMbuf[i] == NULL)
{
rxMbuf[i] = allocatePacket(NETWORK_BUFSIZE);
if (rxMbuf[i] == NULL)
{
IOLog("Ethernet(UniN): NULL packet in initRxRing\n");
return false;
}
}
status = updateDescriptorFromMbuf(rxMbuf[i], &rxDMACommands[i], true);
if (status == false)
{
IOLog("Ethernet(UniN): updateDescriptorFromMbuf error in "
"initRxRing\n");
return false;
}
}
i-=4;
rxCommandHead = 0;
rxCommandTail = i;
return true;
}
void UniNEnet::flushRings( bool flushTx, bool flushRx )
{
UInt32 i;
ELG( TX_RING_LENGTH, RX_RING_LENGTH, 'FluR', "flushRings" );
if ( flushRx )
{
for ( i = 0; i < RX_RING_LENGTH; i++ )
{
if ( rxMbuf[ i ] )
{
ELG( i, rxMbuf[ i ], 'flRx', "UniNEnet::flushRings" );
freePacket( rxMbuf[ i ] );
rxMbuf[i] = 0;
}
}
}
if ( flushTx )
{
for ( i = 0; i < TX_RING_LENGTH; i++ )
{
if ( txMbuf[ i ] )
{
ELG( i, txMbuf[ i ], 'flTx', "UniNEnet::flushRings" );
freePacket( txMbuf[ i ] );
txMbuf[ i ] = 0;
}
}
}
return;
}
void UniNEnet::startChip()
{
UInt32 gemReg;
ELG( this, 0, 'SChp', "startChip" );
gemReg = READ_REGISTER( TxConfiguration ); gemReg |= kTxConfiguration_Tx_DMA_Enable;
WRITE_REGISTER( TxConfiguration, gemReg );
gemReg = READ_REGISTER( RxConfiguration ); gemReg |= kRxConfiguration_Rx_DMA_Enable;
WRITE_REGISTER( RxConfiguration, gemReg );
gemReg = READ_REGISTER( TxMACConfiguration ); gemReg |= kTxMACConfiguration_TxMac_Enable;
WRITE_REGISTER( TxMACConfiguration, gemReg );
rxMacConfigReg = kRxMACConfiguration_Rx_Mac_Enable | kRxMACConfiguration_Strip_FCS;
WRITE_REGISTER( RxMACConfiguration, rxMacConfigReg );
gemReg = ~(kStatus_TX_INT_ME | kStatus_RX_DONE); WRITE_REGISTER( InterruptMask, gemReg );
return;
}
void UniNEnet::stopChip()
{
UInt32 gemReg;
ELG( READ_REGISTER( TxConfiguration ), READ_REGISTER( RxConfiguration ), 'HChp', "stopChip" );
gemReg = READ_REGISTER( TxConfiguration ); gemReg &= ~kTxConfiguration_Tx_DMA_Enable;
WRITE_REGISTER( TxConfiguration, gemReg );
gemReg = READ_REGISTER( RxConfiguration ); gemReg &= ~kRxConfiguration_Rx_DMA_Enable;
WRITE_REGISTER( RxConfiguration, gemReg );
IOSleep( 1 );
gemReg = READ_REGISTER( TxMACConfiguration ); gemReg &= ~kTxMACConfiguration_TxMac_Enable;
WRITE_REGISTER( TxMACConfiguration, gemReg );
rxMacConfigReg = READ_REGISTER( RxMACConfiguration ); rxMacConfigReg &= ~kRxMACConfiguration_Rx_Mac_Enable;
WRITE_REGISTER( RxMACConfiguration, rxMacConfigReg );
IOSleep( 4 );
return;
}
bool UniNEnet::getPhyType()
{
UInt16 *pPhyType;
UInt16 phyWord;
ELG( this, phyId, 'gPhT', "getPhyType" );
miiResetPHY( phyId );
pPhyType = (UInt16 *)&phyType;
miiReadWord( pPhyType, MII_ID0, phyId );
miiReadWord( pPhyType+1, MII_ID1, phyId );
ELG( phyId, phyType, '=FyT', "UniNEnet::getPhyType" );
if ( (phyType & MII_BCM5400_MASK) == MII_BCM5400_ID )
{
phyBCMType = 5400;
ELG( this, phyId, '5400', "UniNEnet::getPhyType" );
miiReadWord( &phyWord, MII_BCM5400_AUXCONTROL, phyId );
phyWord |= MII_BCM5400_AUXCONTROL_PWR10BASET;
miiWriteWord( phyWord, MII_BCM5400_AUXCONTROL, phyId );
miiReadWord( &phyWord, MII_BCM5400_1000BASETCONTROL, phyId );
phyWord |= MII_BCM5400_1000BASETCONTROL_FULLDUPLEXCAP;
miiWriteWord( phyWord, MII_BCM5400_1000BASETCONTROL, phyId );
IODelay(100);
miiResetPHY( 0x1F );
miiReadWord( &phyWord, MII_BCM5201_MULTIPHY, 0x1F );
phyWord |= MII_BCM5201_MULTIPHY_SERIALMODE;
miiWriteWord( phyWord, MII_BCM5201_MULTIPHY, 0x1F );
miiReadWord( &phyWord, MII_BCM5400_AUXCONTROL, phyId );
phyWord &= ~MII_BCM5400_AUXCONTROL_PWR10BASET;
miiWriteWord( phyWord, MII_BCM5400_AUXCONTROL, phyId );
}
else if ( (phyType & MII_BCM5400_MASK) == MII_BCM5401_ID )
{
phyBCMType = 5401;
ELG( this, phyId, '5401', "UniNEnet::getPhyType" );
miiReadWord( &phyWord, MII_ID1, phyId );
phyWord &= 0x000F ;
if ( (phyWord == 0x0001 ) || (phyWord == 0x0003) )
{
miiWriteWord( 0x0C20, 0x018, phyId );
miiWriteWord( 0x0012, 0x017, phyId );
miiWriteWord( 0x1804, 0x015, phyId );
miiWriteWord( 0x0013, 0x017, phyId );
miiWriteWord( 0x1204, 0x015, phyId );
miiWriteWord( 0x8006, 0x017, phyId );
miiWriteWord( 0x0132, 0x015, phyId );
miiWriteWord( 0x8006, 0x017, phyId );
miiWriteWord( 0x0232, 0x015, phyId );
miiWriteWord( 0x201F, 0x017, phyId );
miiWriteWord( 0x0A20, 0x015, phyId );
}
miiReadWord( &phyWord, MII_BCM5400_1000BASETCONTROL, phyId );
phyWord |= MII_BCM5400_1000BASETCONTROL_FULLDUPLEXCAP;
miiWriteWord( phyWord, MII_BCM5400_1000BASETCONTROL, phyId );
IODelay( 10 );
miiResetPHY( 0x1F );
miiReadWord( &phyWord, MII_BCM5201_MULTIPHY, 0x1F );
phyWord |= MII_BCM5201_MULTIPHY_SERIALMODE;
miiWriteWord( phyWord, MII_BCM5201_MULTIPHY, 0x1F );
}
else if ( (phyType & MII_BCM5400_MASK) == MII_BCM5411_ID ) {
phyBCMType = 5411;
ELG( this, phyId, '5411', "UniNEnet::getPhyType - Broadcom 5411" );
miiWriteWord( 0x8C23, 0x01C, phyId ); miiWriteWord( 0x8CA3, 0x01C, phyId );
miiWriteWord( 0x8C23, 0x01C, phyId );
miiWriteWord( 0x8000, 0x000, phyId );
miiWriteWord( 0x1340, 0x000, phyId );
miiReadWord( &phyWord, MII_BCM5400_1000BASETCONTROL, phyId );
phyWord |= MII_BCM5400_1000BASETCONTROL_FULLDUPLEXCAP;
miiWriteWord( phyWord, MII_BCM5400_1000BASETCONTROL, phyId );
IODelay( 10 );
miiResetPHY( 0x1F );
}
else if ( (phyType & MII_BCM5201_MASK) == MII_BCM5201_ID )
{
phyBCMType = 5201;
ELG( this, phyId, '5201', "UniNEnet::getPhyType" );
}
else
{
phyBCMType = 0;
ELG( this, phyType, 'phy?', "UniNEnet::getPhyType" );
}
return true;
}
bool UniNEnet::initChip()
{
UInt32 i, temp;
mach_timespec_t timeStamp;
UInt32 rxFifoSize;
UInt32 rxOff;
UInt32 rxOn;
u_int16_t *p16;
ELG( 0, phyId, 'ChpI', "initChip" );
if ( fBuiltin == false )
{
WRITE_REGISTER( DatapathMode, kDatapathMode_ExtSERDESMode );
WRITE_REGISTER( SerialinkControl, kSerialinkControl_DisableLoopback
| kSerialinkControl_EnableSyncDet );
WRITE_REGISTER( Advertisement, kAdvertisement_Full_Duplex
| kAdvertisement_PAUSE );
WRITE_REGISTER( PCSMIIControl, kPCSMIIControl_Auto_Negotiation_Enable
| kPCSMIIControl_Restart_Auto_Negotiation );
WRITE_REGISTER( PCSConfiguration, kPCSConfiguration_Enable );
fXIFConfiguration = kXIFConfiguration_Tx_MII_OE
| kXIFConfiguration_GMIIMODE
| kXIFConfiguration_FDPLXLED;
}
else
{
WRITE_REGISTER( DatapathMode, kDatapathMode_GMIIMode );
fXIFConfiguration = kXIFConfiguration_Tx_MII_OE;
}
WRITE_REGISTER( XIFConfiguration, fXIFConfiguration );
WRITE_REGISTER( SendPauseCommand, kSendPauseCommand_default );
WRITE_REGISTER( MACControlConfiguration,kMACControlConfiguration_Receive_Pause_Enable );
WRITE_REGISTER( InterruptMask, kInterruptMask_None );
WRITE_REGISTER( TxMACMask, kTxMACMask_default );
WRITE_REGISTER( RxMACMask, kRxMACMask_default );
WRITE_REGISTER( MACControlMask, kMACControlMask_default );
WRITE_REGISTER( Configuration, kConfiguration_TX_DMA_Limit
| kConfiguration_RX_DMA_Limit
| kConfiguration_Infinite_Burst );
WRITE_REGISTER( InterPacketGap0, kInterPacketGap0_default );
WRITE_REGISTER( InterPacketGap1, kInterPacketGap1_default );
WRITE_REGISTER( InterPacketGap2, kInterPacketGap2_default );
WRITE_REGISTER( SlotTime, kSlotTime_default );
WRITE_REGISTER( MinFrameSize, kMinFrameSize_default );
WRITE_REGISTER( MaxFrameSize, kMaxFrameSize_default );
WRITE_REGISTER( PASize, kPASize_default );
WRITE_REGISTER( JamSize, kJamSize_default );
WRITE_REGISTER( AttemptLimit, kAttemptLimit_default );
WRITE_REGISTER( MACControlType, kMACControlType_default );
p16 = (u_int16_t *) myAddress.bytes;
for ( i=0; i < sizeof(IOEthernetAddress) / 2; i++ )
WRITE_REGISTER( MACAddress[ i ], p16[ 2 - i ] );
for ( i=0; i < 3; i ++ )
{
WRITE_REGISTER( MACAddress[ i + 3 ], 0 );
WRITE_REGISTER( AddressFilter[ i ], 0 );
}
WRITE_REGISTER( MACAddress[ 6 ], kMACAddress_default_6 );
WRITE_REGISTER( MACAddress[ 7 ], kMACAddress_default_7 );
WRITE_REGISTER( MACAddress[ 8 ], kMACAddress_default_8 );
WRITE_REGISTER( AddressFilter2_1Mask, 0 );
WRITE_REGISTER( AddressFilter0Mask, 0 );
for ( i=0; i < 16; i++ )
WRITE_REGISTER( HashTable[ i ], 0 );
WRITE_REGISTER( NormalCollisionCounter, 0 );
WRITE_REGISTER( FirstAttemptSuccessfulCollisionCounter, 0 );
WRITE_REGISTER( ExcessiveCollisionCounter, 0 );
WRITE_REGISTER( LateCollisionCounter, 0 );
WRITE_REGISTER( DeferTimer, 0 );
WRITE_REGISTER( PeakAttempts, 0 );
WRITE_REGISTER( ReceiveFrameCounter, 0 );
WRITE_REGISTER( LengthErrorCounter, 0 );
WRITE_REGISTER( AlignmentErrorCounter, 0 );
WRITE_REGISTER( FCSErrorCounter, 0 );
WRITE_REGISTER( RxCodeViolationErrorCounter, 0 );
IOGetTime(&timeStamp);
WRITE_REGISTER( RandomNumberSeed, timeStamp.tv_nsec & 0xFFFF );
WRITE_REGISTER( TxDescriptorBaseLow, txDMACommandsPhys );
WRITE_REGISTER( TxDescriptorBaseHigh, 0 );
temp = kTxConfiguration_TxFIFO_Threshold
| TX_RING_LENGTH_FACTOR << kTxConfiguration_Tx_Desc_Ring_Size_Shift;
WRITE_REGISTER( TxConfiguration, temp );
WRITE_REGISTER( TxMACConfiguration, 0 );
IOSleep( 4 );
setDuplexMode( (phyId == 0xff) ? true : false );
WRITE_REGISTER( RxDescriptorBaseLow, rxDMACommandsPhys );
WRITE_REGISTER( RxDescriptorBaseHigh, 0 );
WRITE_REGISTER( RxKick, RX_RING_LENGTH - 4 );
temp = kRxConfiguration_RX_DMA_Threshold
| RX_RING_LENGTH_FACTOR << kRxConfiguration_Rx_Desc_Ring_Size_Shift
| kRxConfiguration_Checksum_Start_Offset;
WRITE_REGISTER( RxConfiguration, temp );
rxMacConfigReg = 0;
WRITE_REGISTER( RxMACConfiguration, rxMacConfigReg );
IOSleep( 4 );
rxFifoSize = READ_REGISTER( RxFIFOSize );
rxOff = rxFifoSize - ((kGEMMacMaxFrameSize_Aligned + 8) * 2 / kPauseThresholds_Factor);
rxOn = rxFifoSize - ((kGEMMacMaxFrameSize_Aligned + 8) * 3 / kPauseThresholds_Factor);
WRITE_REGISTER( PauseThresholds,
(rxOff << kPauseThresholds_OFF_Threshold_Shift)
| (rxOn << kPauseThresholds_ON_Threshold_Shift) );
temp = READ_REGISTER( BIFConfiguration );
if ( temp & kBIFConfiguration_M66EN )
temp = kRxBlanking_default_66;
else temp = kRxBlanking_default_33;
WRITE_REGISTER( RxBlanking, temp );
if ( fBuiltin )
WRITE_REGISTER( WOLWakeupCSR, 0 );
ELG( READ_REGISTER( TxFIFOSize ), READ_REGISTER( RxFIFOSize ), 'FFsz', "initChip - done" );
return true;
}
void UniNEnet::setDuplexMode( bool duplexMode )
{
UInt32 txMacConfig;
isFullDuplex = duplexMode;
txMacConfig = READ_REGISTER( TxMACConfiguration );
ELG( txMacConfig, duplexMode, 'DupM', "setDuplexMode" );
WRITE_REGISTER( TxMACConfiguration, txMacConfig & ~kTxMACConfiguration_TxMac_Enable );
while ( READ_REGISTER( TxMACConfiguration ) & kTxMACConfiguration_TxMac_Enable )
;
if ( isFullDuplex )
{
txMacConfig |= (kTxMACConfiguration_Ignore_Collisions | kTxMACConfiguration_Ignore_Carrier_Sense);
fXIFConfiguration &= ~kXIFConfiguration_Disable_Echo;
}
else
{
txMacConfig &= ~(kTxMACConfiguration_Ignore_Collisions | kTxMACConfiguration_Ignore_Carrier_Sense);
fXIFConfiguration |= kXIFConfiguration_Disable_Echo;
}
WRITE_REGISTER( TxMACConfiguration, txMacConfig );
WRITE_REGISTER( XIFConfiguration, fXIFConfiguration );
return;
}
void UniNEnet::restartReceiver()
{
WRITE_REGISTER( RxMACSoftwareResetCommand, kRxMACSoftwareResetCommand_Reset );
for ( int i = 0; i < 5000; i++ )
{
if ( ( READ_REGISTER( RxMACSoftwareResetCommand )
& kRxMACSoftwareResetCommand_Reset ) == 0 )
{
break; }
IODelay(1);
}
WRITE_REGISTER( RxMACConfiguration, 0 );
for ( int i = 0; i < 5000; i++ )
{
if ( ( READ_REGISTER( RxMACConfiguration )
& kRxMACConfiguration_Rx_Mac_Enable ) == 0 )
{
break; }
IODelay(1);
}
WRITE_REGISTER( RxMACConfiguration, rxMacConfigReg );
return;
}
bool UniNEnet::transmitPacket( struct mbuf *packet )
{
GEMTxDescriptor *dp; UInt32 i,j,k;
struct mbuf *m;
UInt32 dataPhys;
for ( m = packet, i=1; m->m_next; m=m->m_next, i++ )
;
ELG( i, packet, ' Tx', "UniNEnet::transmitPacket" );
if ( i > txCommandsAvail )
return false;
j = txCommandTail;
OSAddAtomic( -i, (SInt32*)&txCommandsAvail );
m = packet;
do
{
k = j;
dataPhys = (UInt32)mcl_to_paddr( mtod( m, char* ) );
if ( dataPhys == 0 )
dataPhys = pmap_extract( kernel_pmap, mtod( m, vm_offset_t ) );
dp = &txDMACommands[ j ].desc_seg[ 0 ];
OSWriteLittleInt32( &dp->bufferAddrLo, 0, dataPhys );
OSWriteLittleInt32( &dp->flags0, 0, m->m_len );
dp->flags1 = 0;
txIntCnt++;
j = (j + 1) & TX_RING_WRAP_MASK;
} while ( (m = m->m_next) != 0 );
txMbuf[ k ] = packet;
txDMACommands[ k ].desc_seg[ 0 ].flags0 |= OSSwapHostToLittleConstInt32( kGEMTxDescFlags0_EndOfFrame );
txDMACommands[ txCommandTail ].desc_seg[ 0 ].flags0 |= OSSwapHostToLittleConstInt32( kGEMTxDescFlags0_StartOfFrame );
if ( txIntCnt >= TX_DESC_PER_INT )
{
txDMACommands[ txCommandTail ].desc_seg[ 0 ].flags1 |= OSSwapHostToLittleConstInt32( kGEMTxDescFlags1_Int );
txIntCnt = txIntCnt % TX_DESC_PER_INT;
}
#ifdef HDW_CHECKSUM
UInt32 demandMask;
UInt16 param0, param1;
getChecksumDemand( packet,
kChecksumFamilyTCPIP,
&demandMask, ¶m0, ¶m1 ); if ( demandMask & kChecksumTCPSum16 )
{
txDMACommands[ txCommandTail ].desc_seg[ 0 ].flags0
|= OSSwapHostToLittleConstInt32( kGEMTxDescFlags0_ChecksumEnable
| param0 << kGEMTxDescFlags0_ChecksumStart_Shift
| param1 << kGEMTxDescFlags0_ChecksumStuff_Shift );
}
#endif // HDW_CHECKSUM
txCommandTail = j;
WRITE_REGISTER( TxKick, j );
return true;
}
void UniNEnet::receivePacket( void * pkt,
UInt32 * pkt_len,
UInt32 timeout )
{
mach_timespec_t startTime;
mach_timespec_t currentTime;
UInt32 elapsedTimeMS;
*pkt_len = 0;
if (ready == false)
{
return;
}
#if USE_ELG
UInt32 elgFlag = g.evLogFlag; g.evLogFlag = 0;
#endif // USE_ELG
debuggerPkt = pkt;
debuggerPktSize = 0;
IOGetTime(&startTime);
do
{
receivePackets( true );
IOGetTime( ¤tTime );
elapsedTimeMS = (currentTime.tv_nsec - startTime.tv_nsec) / (1000*1000);
}
while ( (debuggerPktSize == 0) && (elapsedTimeMS < timeout) );
*pkt_len = debuggerPktSize;
#if USE_ELG
g.evLogFlag = elgFlag;
#endif // USE_ELG
return;
}
void UniNEnet::packetToDebugger( struct mbuf *packet, u_int size )
{
ELG( packet, size, 'ToDb', "packetToDebugger" );
debuggerPktSize = size;
bcopy( mtod(packet, char*), debuggerPkt, size );
return;
}
void UniNEnet::sendPacket( void *pkt, UInt32 pkt_len )
{
mach_timespec_t startTime;
mach_timespec_t currentTime;
UInt32 elapsedTimeMS;
if (!ready || !pkt || (pkt_len > ETHERMAXPACKET))
{
return;
}
#if USE_ELG
UInt32 elgFlag = g.evLogFlag; g.evLogFlag = 0;
#endif // USE_ELG
IOGetTime(&startTime);
do
{
debugTransmitInterruptOccurred();
IOGetTime(¤tTime);
elapsedTimeMS = (currentTime.tv_nsec - startTime.tv_nsec) / (1000*1000);
}
while ( (txCommandHead != txCommandTail) && (elapsedTimeMS < TX_KDB_TIMEOUT) );
if ( txCommandHead != txCommandTail )
{
IOLog( "Ethernet(UniN): Polled tranmit timeout - 1\n\r");
return;
}
txDebuggerPkt->m_next = 0;
txDebuggerPkt->m_data = (caddr_t) pkt;
txDebuggerPkt->m_pkthdr.len = txDebuggerPkt->m_len = pkt_len;
transmitPacket(txDebuggerPkt);
do
{
debugTransmitInterruptOccurred();
IOGetTime(¤tTime);
elapsedTimeMS = (currentTime.tv_nsec - startTime.tv_nsec) / (1000*1000);
}
while ( (txCommandHead != txCommandTail) &&
(elapsedTimeMS < TX_KDB_TIMEOUT) );
if ( txCommandHead != txCommandTail )
{
IOLog( "Ethernet(UniN): Polled tranmit timeout - 2\n\r");
}
#if USE_ELG
g.evLogFlag = elgFlag;
#endif // USE_ELG
return;
}
void UniNEnet::sendDummyPacket()
{
union
{
UInt8 bytes[64];
IOEthernetAddress enet_addr[2];
} dummyPacket;
ELG( &dummyPacket, sizeof( dummyPacket ), 'SenD', "sendDummyPacket" );
bzero( &dummyPacket, sizeof(dummyPacket) );
dummyPacket.enet_addr[0] = myAddress;
dummyPacket.enet_addr[1] = myAddress;
sendPacket( (void*)dummyPacket.bytes, (unsigned int)sizeof( dummyPacket ) );
return;
}
bool UniNEnet::receiveInterruptOccurred()
{
return receivePackets( false );
}
bool UniNEnet::receivePackets( bool fDebugger )
{
struct mbuf *packet;
UInt32 i, last;
int receivedFrameSize = 0;
UInt16 dmaFlags;
UInt16 checksum;
UInt32 rxPktStatus = 0;
bool passPacketUp;
bool reusePkt;
bool status;
bool useNetif = !fDebugger && netifEnabled;
bool packetsQueued = false;
bool replaced;
last = (UInt32)-1;
i = rxCommandHead;
ELG( rxCommandTail, rxCommandHead, 'Rx I', "receivePackets" );
while ( 1 )
{
passPacketUp = false;
reusePkt = false;
dmaFlags = OSReadLittleInt16( &rxDMACommands[ i ].desc_seg[ 0 ].frameDataSize, 0 );
checksum = OSReadLittleInt16( &rxDMACommands[ i ].desc_seg[ 0 ].tcpPseudoChecksum, 0 );
if ( dmaFlags & kGEMRxDescFrameSize_Own )
{
break;
}
receivedFrameSize = dmaFlags & kGEMRxDescFrameSize_Mask;
rxPktStatus = OSReadLittleInt32( &rxDMACommands[ i ].desc_seg[ 0 ].flags, 0 );
if ( receivedFrameSize < (ETHERMINPACKET - ETHERCRC) ||
receivedFrameSize > (ETHERMAXPACKET + ETHERCRC) ||
rxPktStatus & kGEMRxDescFlags_BadCRC )
{
reusePkt = true;
NETWORK_STAT_ADD( inputErrors );
if ( receivedFrameSize < (ETHERMINPACKET - ETHERCRC) )
ETHERNET_STAT_ADD( dot3RxExtraEntry.frameTooShorts );
else ETHERNET_STAT_ADD( dot3StatsEntry.frameTooLongs );
ELG( rxPktStatus, receivedFrameSize, '-Rx-', "receivePackets - mutant or bad CRC." );
}
else if ( useNetif == false )
{
reusePkt = true;
if (fDebugger)
{
packetToDebugger(rxMbuf[i], receivedFrameSize);
}
}
packet = 0;
if ( reusePkt == false )
{
packet = replaceOrCopyPacket(&rxMbuf[i], receivedFrameSize, &replaced);
reusePkt = true;
if (packet && replaced)
{
status = updateDescriptorFromMbuf(rxMbuf[i], &rxDMACommands[i], true);
if (status)
{
reusePkt = false;
}
else
{
ELG( packet, rxMbuf[i], 'upd-', "UniNEnet::receivePackets" );
freePacket(rxMbuf[i]); rxMbuf[i] = packet; packet = 0; IOLog("Ethernet(UniN): updateDescriptorFromMbuf error\n");
}
}
if ( packet == 0 )
NETWORK_STAT_ADD( inputErrors );
}
if ( reusePkt == true )
{
rxDMACommands[i].desc_seg[0].flags = 0;
rxDMACommands[i].desc_seg[0].frameDataSize = OSSwapHostToLittleConstInt16( NETWORK_BUFSIZE | kGEMRxDescFrameSize_Own );
}
last = i;
i = (i + 1) & RX_RING_WRAP_MASK;
if ( (i & 3) == 0 ) WRITE_REGISTER( RxKick, (i - 4) & RX_RING_WRAP_MASK );
if ( fDebugger )
break;
if ( packet )
{
KERNEL_DEBUG( DBG_UniN_RXCOMPLETE | DBG_FUNC_NONE,
(int)packet, (int)receivedFrameSize, 0, 0, 0 );
#ifdef HDW_CHECKSUM
if ( (receivedFrameSize > 64) && (checksum != 0) )
setChecksumResult( packet,
kChecksumFamilyTCPIP,
kChecksumTCPSum16,
0, checksum, kRxHwCksumStartOffset );
#endif // HDW_CHECKSUM
networkInterface->inputPacket( packet, receivedFrameSize, true );
NETWORK_STAT_ADD( inputPackets );
packetsQueued = true;
}
}
if ( last != (UInt32)-1 )
{
rxCommandTail = last;
rxCommandHead = i;
}
return packetsQueued;
}
bool UniNEnet::transmitInterruptOccurred()
{
UInt32 i;
bool serviced = false;
i = READ_REGISTER( TxCompletion );
ELG( txCommandHead, i, ' Tx+', "transmitInterruptOccurred" );
while ( i != txCommandHead ) {
do {
KERNEL_DEBUG( DBG_UniN_TXCOMPLETE | DBG_FUNC_NONE,
txMbuf[ txCommandHead ], 0, 0, 0, 0 );
OSIncrementAtomic( (SInt32*)&txCommandsAvail );
if ( txMbuf[ txCommandHead ] )
{
freePacket( txMbuf[ txCommandHead ] );
txMbuf[ txCommandHead ] = 0;
NETWORK_STAT_ADD( outputPackets );
}
txCommandHead = (txCommandHead + 1) & TX_RING_WRAP_MASK;
} while ( i != txCommandHead );
serviced = true;
i = READ_REGISTER( TxCompletion ); }
return serviced;
}
void UniNEnet::debugTransmitInterruptOccurred()
{
UInt32 i;
debugTxPoll = true;
i = READ_REGISTER( TxCompletion );
while ( i != txCommandHead )
{
OSIncrementAtomic( (SInt32*)&txCommandsAvail );
KERNEL_DEBUG( DBG_UniN_TXCOMPLETE | DBG_FUNC_NONE,
(int)txMbuf[ txCommandHead ],
(int)txMbuf[ txCommandHead ]->m_pkthdr.len, 0, 0, 0 );
if ( txMbuf[ txCommandHead ] )
{
if ( txMbuf[ txCommandHead ] != txDebuggerPkt )
debugQueue->enqueue( txMbuf[ txCommandHead ] );
txMbuf[ txCommandHead ] = 0;
}
txCommandHead = (txCommandHead + 1) & TX_RING_WRAP_MASK;
}
return;
}
void UniNEnet::debugTransmitCleanup()
{
debugQueue->flush();
transmitQueue->start();
return;
}
bool UniNEnet::updateDescriptorFromMbuf(struct mbuf * m, enet_dma_cmd_t *desc, bool isReceive)
{
struct IOPhysicalSegment segVector[1];
UInt32 segments;
segments = mbufCursor->getPhysicalSegmentsWithCoalesce(m, segVector);
if ( segments == 0 || segments > 1 )
{
IOLog("Ethernet(UniN): updateDescriptorFromMbuf error, %d segments\n", (int)segments);
return false;
}
if ( isReceive )
{
enet_dma_cmd_t *rxCmd = (enet_dma_cmd_t *)desc;
OSWriteLittleInt32( &rxCmd->desc_seg[0].bufferAddrLo, 0, segVector[0].location );
OSWriteLittleInt16( &rxCmd->desc_seg[0].frameDataSize, 0, segVector[0].length | kGEMRxDescFrameSize_Own );
rxCmd->desc_seg[0].flags = 0;
}
else
{
enet_txdma_cmd_t *txCmd = (enet_txdma_cmd_t *)desc;
OSWriteLittleInt32( &txCmd->desc_seg[0].bufferAddrLo, 0, segVector[0].location );
OSWriteLittleInt32( &txCmd->desc_seg[0].flags0, 0, segVector[0].length
| kGEMTxDescFlags0_StartOfFrame
| kGEMTxDescFlags0_EndOfFrame );
txCmd->desc_seg[0].flags1 = 0;
txIntCnt += 1;
if ( (txIntCnt % TX_DESC_PER_INT) == 0 ) txCmd->desc_seg[0].flags1 = OSSwapHostToLittleConstInt32( kGEMTxDescFlags1_Int );
}
return true;
}
void UniNEnet::monitorLinkStatus( bool firstPoll )
{
UInt32 gemReg;
UInt16 phyStatus;
UInt16 linkStatus;
UInt16 linkMode;
UInt16 lpAbility;
UInt16 phyStatusChange;
bool fullDuplex = false;
UInt32 linkSpeed = 0;
IOMediumType mediumType = kIOMediumEthernetNone;
IONetworkMedium *medium;
ELG( phyId, firstPoll, ' MLS', "monitorLinkStatus" );
if ( firstPoll )
{
phyStatusPrev = 0;
linkStatusPrev = kLinkStatusUnknown;
}
if ( phyId == 0xff )
{
phyStatus = READ_REGISTER( PCSMIIStatus ) & 0x0000FFFF;
lpAbility = READ_REGISTER( PCSMIILinkPartnerAbility ) & 0x0000FFFF;
}
else
{
if ( miiReadWord( &phyStatus, MII_STATUS, phyId) != true )
{
return;
}
miiReadWord( &lpAbility, MII_STATUS, phyId);
}
ELG( lpAbility, phyStatus, ' mls', "monitorLinkStatus - LinkPartnerAbility and Status" );
phyStatusChange = (phyStatusPrev ^ phyStatus) &
( MII_STATUS_LINK_STATUS |
MII_STATUS_NEGOTIATION_COMPLETE );
if ( phyStatusChange || firstPoll )
{
if ( firstPoll && fBuiltin )
{
miiWaitForAutoNegotiation( phyId );
miiReadWord(&phyStatus, MII_STATUS, phyId);
miiReadWord(&phyStatus, MII_STATUS, phyId);
}
gemReg = READ_REGISTER( MACControlConfiguration );
if ( lpAbility & MII_LPAR_PAUSE )
gemReg |= kMACControlConfiguration_Send_Pause_Enable;
else gemReg &= ~kMACControlConfiguration_Send_Pause_Enable;
WRITE_REGISTER( MACControlConfiguration, gemReg );
if ( (phyStatus & MII_STATUS_LINK_STATUS) &&
( firstPoll || (phyStatus & MII_STATUS_NEGOTIATION_COMPLETE) ) )
{
if ( phyId == 0xff )
{
linkSpeed = 1000;
fullDuplex = true;
mediumType = kIOMediumEthernet1000BaseSX;
}
else if ( (phyType & MII_LXT971_MASK) == MII_LXT971_ID )
{
miiReadWord( &linkStatus, MII_LXT971_STATUS_2, phyId );
linkSpeed = (linkStatus & MII_LXT971_STATUS_2_SPEED) ?
100 : 10;
fullDuplex = (linkStatus & MII_LXT971_STATUS_2_DUPLEX) ?
true : false;
mediumType = (linkSpeed == 10) ? kIOMediumEthernet10BaseT :
kIOMediumEthernet100BaseTX;
}
else if ( (phyType & MII_BCM5201_MASK) == MII_BCM5201_ID )
{
miiReadWord( &linkStatus, MII_BCM5201_AUXSTATUS, phyId );
linkSpeed = (linkStatus & MII_BCM5201_AUXSTATUS_SPEED) ?
100 : 10;
fullDuplex = (linkStatus & MII_BCM5201_AUXSTATUS_DUPLEX) ?
true : false;
mediumType = (linkSpeed == 10) ? kIOMediumEthernet10BaseT :
kIOMediumEthernet100BaseTX;
}
else if ( ((phyType & MII_BCM5400_MASK) == MII_BCM5400_ID)
|| ((phyType & MII_BCM5400_MASK) == MII_BCM5401_ID) || ((phyType & MII_BCM5400_MASK) == MII_BCM5411_ID) ) {
miiReadWord( &linkStatus, MII_BCM5400_AUXSTATUS, phyId );
linkMode = (linkStatus & MII_BCM5400_AUXSTATUS_LINKMODE_MASK) /
MII_BCM5400_AUXSTATUS_LINKMODE_BIT;
if ( linkMode < 6 )
fXIFConfiguration &= ~kXIFConfiguration_GMIIMODE;
else fXIFConfiguration |= kXIFConfiguration_GMIIMODE;
WRITE_REGISTER( XIFConfiguration, fXIFConfiguration );
if ( linkMode == 0 )
{
linkSpeed = 0;
}
else if ( linkMode < 3 )
{
linkSpeed = 10;
fullDuplex = ( linkMode < 2 ) ? false : true;
mediumType = kIOMediumEthernet10BaseT;
}
else if ( linkMode < 6 )
{
linkSpeed = 100;
fullDuplex = ( linkMode < 5 ) ? false : true;
mediumType = kIOMediumEthernet100BaseTX;
}
else
{
linkSpeed = 1000;
fullDuplex = true;
mediumType = kIOMediumEthernet1000BaseTX;
}
}
setDuplexMode( fullDuplex );
if ( ready == true )
startChip();
if ( linkSpeed != 0 )
{
mediumType |= (fullDuplex == true) ?
kIOMediumOptionFullDuplex :
kIOMediumOptionHalfDuplex;
}
medium = IONetworkMedium::getMediumWithType( mediumDict,
mediumType );
setLinkStatus( kIONetworkLinkActive | kIONetworkLinkValid,
medium,
linkSpeed * 1000000 );
IOLog( "UniNEnet::monitorLinkStatus - Link is up at %ld Mbps - %s Duplex\n\r",
linkSpeed, fullDuplex ? "Full" : "Half" );
linkStatusPrev = kLinkStatusUp;
}
else
{
if ( (linkStatusPrev == kLinkStatusUp) ||
(linkStatusPrev == kLinkStatusUnknown) )
{
stopChip();
medium = IONetworkMedium::getMediumWithType( mediumDict,
mediumType );
setLinkStatus( kIONetworkLinkValid,
medium,
0 );
if ( linkStatusPrev != kLinkStatusUnknown )
{
IOLog( "Ethernet(UniN): Link is down.\n\r" );
}
txIntCnt = 0;
if ( txCommandHead != txCommandTail )
{
initTxRing();
txCommandHead = READ_REGISTER( TxCompletion );
txCommandTail = txCommandHead;
}
}
linkStatusPrev = kLinkStatusDown;
}
phyStatusPrev = phyStatus;
}
return;
}
IOReturn UniNEnet::getHardwareAddress(IOEthernetAddress *ea)
{
UInt32 i;
OSData *macEntry;
UInt8 *macAddress;
UInt32 len;
macEntry = OSDynamicCast( OSData, nub->getProperty( "local-mac-address" ) );
if ( macEntry == 0 )
{
return kIOReturnError;
}
macAddress = (UInt8 *)macEntry->getBytesNoCopy();
if ( macAddress == 0 )
{
return kIOReturnError;
}
len = macEntry->getLength();
if ( len != 6 )
{
return kIOReturnError;
}
for (i = 0; i < sizeof(*ea); i++)
{
ea->bytes[i] = macAddress[i];
}
return kIOReturnSuccess;
}
#define ENET_CRCPOLY 0x04c11db7
static UInt32 crc416(UInt32 current, UInt16 nxtval )
{
register UInt32 counter;
register int highCRCBitSet, lowDataBitSet;
nxtval = ((nxtval & 0x00FF) << 8) | (nxtval >> 8);
for (counter = 0; counter != 16; ++counter)
{
if ((current & 0x80000000) == 0)
highCRCBitSet = 0;
else
highCRCBitSet = 1;
current = current << 1;
if ((nxtval & 0x0001) == 0)
lowDataBitSet = 0;
else
lowDataBitSet = 1;
nxtval = nxtval >> 1;
if (highCRCBitSet ^ lowDataBitSet)
current = current ^ ENET_CRCPOLY;
}
return current;
}
static UInt32 mace_crc(UInt16 *address)
{
register UInt32 newcrc;
newcrc = crc416(0xffffffff, *address);
newcrc = crc416(newcrc, address[1]);
newcrc = crc416(newcrc, address[2]);
return(newcrc);
}
void UniNEnet::addToHashTableMask(UInt8 *addr)
{
UInt32 i,j;
UInt32 crcBitIndex;
UInt16 mask;
j = mace_crc((UInt16 *)addr) & 0xFF;
for ( crcBitIndex = i = 0; i < 8; i++ )
{
crcBitIndex >>= 1;
crcBitIndex |= (j & 0x80);
j <<= 1;
}
crcBitIndex ^= 0xFF;
if (hashTableUseCount[crcBitIndex]++)
return;
mask = crcBitIndex % 16;
mask = 1 << mask;
hashTableMask[crcBitIndex/16] |= mask;
}
void UniNEnet::resetHashTableMask()
{
bzero(hashTableUseCount, sizeof(hashTableUseCount));
bzero(hashTableMask, sizeof(hashTableMask));
}
void UniNEnet::updateHashTableMask()
{
UInt32 i;
rxMacConfigReg = READ_REGISTER( RxMACConfiguration );
ELG( this, rxMacConfigReg, 'updH', "updateHashTableMask" );
WRITE_REGISTER( RxMACConfiguration,
rxMacConfigReg & ~(kRxMACConfiguration_Rx_Mac_Enable
| kRxMACConfiguration_Hash_Filter_Enable) );
while ( READ_REGISTER( RxMACConfiguration ) & (kRxMACConfiguration_Rx_Mac_Enable
| kRxMACConfiguration_Hash_Filter_Enable) )
;
for ( i= 0; i < 16; i++ )
WRITE_REGISTER( HashTable[ i ], hashTableMask[ 15 - i ] );
rxMacConfigReg |= kRxMACConfiguration_Hash_Filter_Enable;
WRITE_REGISTER( RxMACConfiguration, rxMacConfigReg );
}