#include <mach/vm_param.h> // PAGE_SIZE
#include "3C90x.h"
static bool
GetPhysicalFromVirtual( IOBufferMemoryDescriptor * mem,
IOVirtualAddress vaddr, IOPhysicalAddress * paddr )
{
IOByteCount segLength;
*paddr = mem->getPhysicalSegment(
vaddr - (IOVirtualAddress) mem->getBytesNoCopy(),
&segLength);
return ( *paddr != 0 );
}
bool
Apple3Com3C90x::allocateDescMemory( IOBufferMemoryDescriptor ** mem,
IOByteCount size )
{
LOG_DEBUG("%s::%s %ld\n", getName(), __FUNCTION__, size);
if ( (size == 0) || (size > PAGE_SIZE) || *mem )
{
IOLog("%s: unable to allocate descriptor memory\n", getName());
return false;
}
*mem = IOBufferMemoryDescriptor::withOptions( kIOMemoryUnshared,
size, PAGE_SIZE );
if ( *mem == 0 )
IOLog("%s: can't allocate %ld bytes of memory\n", getName(), size);
return (*mem != 0);
}
void
Apple3Com3C90x::freeDescMemory( IOBufferMemoryDescriptor ** mem )
{
if ( *mem )
{
(*mem)->release();
*mem = 0;
}
}
bool Apple3Com3C90x::allocateMemory()
{
IOByteCount bytes;
if ( ( (sizeof(TxDescriptor) % kDescriptorAlignment) != 0 ) ||
( (sizeof(RxDescriptor) % kDescriptorAlignment) != 0 ) )
{
IOLog("%s: descriptor size not multiple of %d bytes!\n",
getName(), kDescriptorAlignment);
return false;
}
bytes = sizeof(TxDescriptor) * _txRingSize;
if ( allocateDescMemory( &_txRingMem, bytes ) == false )
return false;
_txRing = (TxDescriptor *) _txRingMem->getBytesNoCopy();
bzero( (void *) _txRing, bytes );
bytes = sizeof(RxDescriptor) * _rxRingSize;
if ( allocateDescMemory( &_rxRingMem, bytes ) == false )
return false;
_rxRing = (RxDescriptor *) _rxRingMem->getBytesNoCopy();
bzero( (void *) _rxRing, bytes );
_rxRingInited = false;
_txRingInited = false;
return true;
}
bool
Apple3Com3C90x::updateTxDescriptor( TxDescriptor * desc,
struct mbuf * packet )
{
UInt32 segmentCount;
segmentCount = _mbufCursor->getPhysicalSegmentsWithCoalesce(
packet,
(IOPhysicalSegment *) desc->fragments,
kTxFragmentCount );
if ( segmentCount == 0 )
{
LOG_DEBUG("%s: TX IOMbufMemoryCursor error\n", getName());
return false;
}
desc->header = SetBitField( TxFragment, Length,
packet->m_pkthdr.len );
desc->fragments[segmentCount - 1].command |= kTxFragmentLastMask;
return true;
}
bool
Apple3Com3C90x::updateRxDescriptor( RxDescriptor * desc,
struct mbuf * packet )
{
UInt32 segmentCount;
segmentCount = _mbufCursor->getPhysicalSegments(
packet,
(IOPhysicalSegment *) desc->fragments,
kRxFragmentCount );
if ( segmentCount == 0 )
{
LOG_DEBUG("%s: RX IOMbufMemoryCursor error\n", getName());
return false;
}
desc->fragments[segmentCount - 1].command |= kRxFragmentLastMask;
return true;
}
bool Apple3Com3C90x::initRxRing()
{
UInt32 i;
LOG_DEBUG("%s::%s\n", getName(), __FUNCTION__);
if ( _rxRingInited )
{
for ( i = 0; i < _rxRingSize; i++ ) _rxRing[i].status = 0;
goto ResetPointers;
}
for ( i = 0; i < _rxRingSize; i++ )
{
_rxRing[i].status = 0;
bzero( &_rxRing[i].fragments, sizeof(_rxRing[i].fragments) );
if ( i < (_rxRingSize - 1) )
{
if ( GetPhysicalFromVirtual( _rxRingMem,
(IOVirtualAddress) &_rxRing[i+1],
&_rxRing[i].nextPtr ) != true )
{
return false;
}
}
if ( _rxRing[i].drvMbuf == NULL )
{
_rxRing[i].drvMbuf = allocatePacket( kRxPacketBufferSize );
if ( _rxRing[i].drvMbuf == NULL )
{
IOLog("%s: initRxRing allocatePacket error\n", getName());
return false;
}
}
if ( updateRxDescriptor( &_rxRing[i], _rxRing[i].drvMbuf) == false )
{
IOLog("%s: initRxRing updateDescriptor error", getName());
return false;
}
_rxRing[i].drvNext = &_rxRing[i+1];
}
if ( GetPhysicalFromVirtual( _rxRingMem,
(IOVirtualAddress) &_rxRing[0],
&_rxRing[i-1].nextPtr ) != true )
{
return false;
}
_rxRing[i-1].drvNext = &_rxRing[0];
ResetPointers:
_rxRingTail = _rxRing;
_rxRingInited = true;
return true;
}
bool Apple3Com3C90x::initTxRing()
{
UInt32 i;
LOG_DEBUG("%s::%s\n", getName(), __FUNCTION__);
for ( i = 0; i < _txRingSize; i++ )
{
if ( _txRing[i].drvMbuf )
{
freePacket( _txRing[i].drvMbuf );
_txRing[i].drvMbuf = 0;
}
}
if ( _txRingInited ) goto ResetPointers;
bzero( (void *) &_txRing[0], sizeof(TxDescriptor) * _txRingSize );
for ( i = 0; i < _txRingSize; i++ )
{
if ( GetPhysicalFromVirtual( _txRingMem,
(IOVirtualAddress) &_txRing[i],
&_txRing[i].drvPhysAddr ) != true )
{
return false;
}
_txRing[i].drvNext = &_txRing[i+1];
if ( i > 0 )
_txRing[i].drvPrevious = &_txRing[i-1];
}
_txRing[i-1].drvNext = &_txRing[0];
_txRing[0].drvPrevious = &_txRing[i-1];
ResetPointers:
_txRingHead = _txRing;
_txRingTail = _txRing;
_txRingFree = _txRingSize;
_txRingInited = true;
return true;
}
void Apple3Com3C90x::resetAdapter()
{
LOG_DEBUG("%s::%s\n", getName(), __FUNCTION__);
sendCommandWait( TxReset );
sendCommandWait( RxReset );
}
bool Apple3Com3C90x::resetAndEnableAdapter( bool enableIRQ )
{
resetAdapter();
if ( !initRxRing() || !initTxRing() )
{
IOLog("%s: initRings failure\n", getName());
return false;
}
initAdapter();
enableAdapter( enableIRQ );
return true;
}
void
Apple3Com3C90x::initTransmitterParameters()
{
setTxFreeThresh( kTxFreeTreshSizePacket );
setDnBurstThresh( _dnBurstThresh >> 5 );
setDnPriorityThresh( _dnPriorityThresh >> 5 );
if ( _adapterInfo->type >= kAdapterType3C90xB )
{
sendCommand( SetTxReclaimThresh, _txReclaimThresh >> 4 );
}
}
void
Apple3Com3C90x::initReceiverParameters()
{
sendCommand( SetRxEarlyThresh, 0x1ffc );
setDMACtrl( getDMACtrl() | kDMACtrlUpRxEarlyEnableMask );
setUpBurstThresh( _upBurstThresh >> 5 );
setUpPriorityThresh( _upPriorityThresh >> 5 );
}
bool Apple3Com3C90x::initAdapter()
{
UInt16 reg16;
LOG_DEBUG("%s::%s\n", getName(), __FUNCTION__);
sendCommandWait( UpStall );
setUpListPtr( _rxRing[ _rxRingSize - 1 ].nextPtr ); sendCommand( UpUnStall );
setDnListPtr( 0 );
initTransmitterParameters();
initReceiverParameters();
setStationAddress( &_etherAddress );
sendCommand( SetRxFilter, _rxFilterMask );
sendCommand( StatsDisable );
updateStatsInterruptHandler();
reg16 = getNetworkDiagnostic();
reg16 |= kNetworkDiagnosticUpperByteEnableMask;
setNetworkDiagnostic( reg16 );
sendCommand( StatsEnable );
#if 0
reg16 = getResetOptions();
reg16 &= ~( kResetOptionsDisableAdvFDMask |
kResetOptionsDisableAdv100Mask |
kResetOptionsDisableAutoNegMask );
setResetOptions( reg16 );
LOG_DEBUG("%s::%s ResetOptions = %04x\n", getName(), __FUNCTION__,
getResetOptions());
#endif
return true;
}
void Apple3Com3C90x::disableAdapterInterrupts()
{
sendCommand( SetIntrEnable, 0 );
sendCommand( SetIndEnable, 0 );
_interruptMask = 0;
}
void Apple3Com3C90x::enableAdapterInterrupts()
{
_interruptMask = kCommandStatusDnCompleteMask |
kCommandStatusUpCompleteMask |
kCommandStatusTxCompleteMask |
kCommandStatusUpdateStatsMask |
kCommandStatusHostErrorMask |
kCommandStatusInterruptLatchMask;
sendCommand( SetIndEnable, _interruptMask );
sendCommand( AckIntr, _interruptMask );
sendCommand( SetIntrEnable, _interruptMask );
}
void Apple3Com3C90x::disableLinkEventInterrupt()
{
_interruptMask &= ~kAckLinkEvent;
sendCommand( SetIndEnable, _interruptMask );
sendCommand( SetIntrEnable, _interruptMask );
}
void Apple3Com3C90x::enableLinkEventInterrupt()
{
_interruptMask |= kAckLinkEvent;
sendCommand( SetIndEnable, _interruptMask );
sendCommand( SetIntrEnable, _interruptMask );
}
void Apple3Com3C90x::enableTransmitter()
{
LOG_DEBUG("%s: enable transmitter\n", getName());
sendCommand( TxEnable );
}
void Apple3Com3C90x::enableReceiver()
{
LOG_DEBUG("%s: enable receiver\n", getName());
sendCommand( RxEnable );
}
void Apple3Com3C90x::enableAdapter( bool enableInterrupts )
{
enableTransmitter();
enableReceiver();
if ( enableInterrupts )
{
enableAdapterInterrupts();
}
sendCommand( UpUnStall );
sendCommand( DnUnStall );
}
UInt32
Apple3Com3C90x::outputPacket( struct mbuf * m, void * param )
{
LOG_TX("%s: transmit [%d]\n", getName(), m->m_pkthdr.len);
if ( _driverEnableCount == 0 )
{
LOG_DEBUG("%s::%s adapter not ready\n", getName(), __FUNCTION__);
freePacket(m);
return kIOReturnOutputDropped;
}
if ( _txRingFree == 0 )
{
return kIOReturnOutputStall;
}
reserveDebuggerLock();
if ( _txRingHead->drvMbuf != NULL )
{
IOLog("%s::%s drvMbuf not NULL\n", getName(), __FUNCTION__);
freePacket( _txRingHead->drvMbuf );
}
if ( updateTxDescriptor( _txRingHead, m ) == false )
{
releaseDebuggerLock();
freePacket(m);
_netStats->outputErrors++;
IOLog("%s::%s updateDescriptor error\n", getName(), __FUNCTION__);
return kIOReturnOutputDropped;
}
_txRingHead->drvMbuf = m;
_txRingHead->nextPtr = 0;
if ( ++_txRingInt >= _txIntThreshold )
{
_txRingHead->header |= kTxDescHeaderDnIndicateMask;
_txRingInt = 0;
}
sendCommandWait( DnStall );
_txRingHead->drvPrevious->nextPtr = _txRingHead->drvPhysAddr;
#if 0
if ( getDnListPtr() == 0 )
{
setDnListPtr( _txRingHead->drvPhysAddr );
}
#else
setDnListPtr( _txRingHead->drvPhysAddr );
#endif
sendCommand( DnUnStall );
_txRingHead = _txRingHead->drvNext;
_txRingFree--;
releaseDebuggerLock();
if ( _txRingInt == 0 )
{
_newWDCounters[ kWDInterruptsPending ] += 1;
}
return kIOReturnOutputSuccess;
}
void
Apple3Com3C90x::transmitInterruptHandler()
{
UInt32 listPtrReg;
UInt32 retiredCount = 0;
LOG_INT("%s:%s\n", getName(), __FUNCTION__);
listPtrReg = getDnListPtr();
while ( _txRingFree < _txRingSize )
{
if ( listPtrReg == _txRingTail->drvPhysAddr )
break;
#if 0
if ( _adapterInfo->type == ADAPTER_TYPE_3C90XB )
{
if ( ( _txRingTail->header.bits.dnComplete ) == 0 )
{
IOLog("3C90xB: entry %d not yet downloaded!\n", entry);
}
}
#endif
if ( _txRingTail->drvMbuf != NULL )
{
freePacket( _txRingTail->drvMbuf );
_txRingTail->drvMbuf = NULL;
}
_txRingTail = _txRingTail->drvNext;
_txRingFree++;
retiredCount++;
}
if ( retiredCount )
{
_transmitQueue->service();
_netStats->outputPackets += retiredCount;
_newWDCounters[ kWDInterruptsRetired ] += 1;
}
}
void
Apple3Com3C90x::receiveInterruptHandler()
{
struct mbuf * inputPkt;
UInt32 pktSize;
UInt32 status;
bool replaced;
UInt32 pktCount = 0;
LOG_INT("%s::%s index:%d\n", getName(), __FUNCTION__,
rxRingTail - rxRing);
for ( status = _rxRingTail->status;
status & kRxDescStatusUpCompleteMask;
status = _rxRingTail->status )
{
inputPkt = NULL;
pktSize = GetBitField( RxDescStatus, Length, status );
if ( ( status & kRxDescStatusUpErrorMask ) ||
( pktSize < (kIOEthernetMinPacketSize - kIOEthernetCRCSize) ) )
{
_netStats->inputErrors++;
if ( status & kRxDescStatusUpOverrunMask )
_etherStats->dot3RxExtraEntry.overruns++;
if ( status & kRxDescStatusRuntFrameMask )
_etherStats->dot3RxExtraEntry.frameTooShorts++;
if ( status & kRxDescStatusAlignmentErrorMask )
_etherStats->dot3StatsEntry.alignmentErrors++;
if ( status & kRxDescStatusCRCErrorMask )
_etherStats->dot3StatsEntry.fcsErrors++;
if ( status & kRxDescStatusOversizedFrameMask )
_etherStats->dot3StatsEntry.frameTooLongs++;
goto next;
}
inputPkt = replaceOrCopyPacket( &_rxRingTail->drvMbuf, pktSize,
&replaced );
if ( inputPkt == 0 )
{
_netStats->inputErrors++;
_etherStats->dot3RxExtraEntry.resourceErrors++;
goto next;
}
if ( replaced &&
( updateRxDescriptor( _rxRingTail,
_rxRingTail->drvMbuf ) == false ) )
{
freePacket( _rxRingTail->drvMbuf ); _rxRingTail->drvMbuf = inputPkt; inputPkt = 0; _netStats->inputErrors++;
IOLog("%s: updateDescriptor() error\n", getName());
}
next:
_rxRingTail->status = 0;
_rxRingTail = _rxRingTail->drvNext;
if ( inputPkt && _netifEnabled )
{
_netif->inputPacket( inputPkt, pktSize,
IONetworkInterface::kInputOptionQueuePacket );
_netStats->inputPackets++;
pktCount++;
}
}
sendCommand( UpUnStall );
if ( pktCount )
{
_netif->flushInputQueue();
}
}
void
Apple3Com3C90x::transmitErrorInterruptHandler()
{
UInt8 txStat;
UInt32 downListPtr;
UInt16 intStatus;
LOG_INT("%s: transmit error interrupt\n", getName());
txStat = getTxStatus();
intStatus = getCommandStatus();
downListPtr = getDnListPtr();
setTxStatus( txStat );
_netStats->outputErrors++;
if ( txStat & kTxStatusUnderrunMask )
{
sendCommandWait( DnStall );
waitForTransmitterIdle();
sendCommandWait( TxReset );
_etherStats->dot3TxExtraEntry.underruns++;
_etherStats->dot3TxExtraEntry.resets++;
}
else if ( txStat & kTxStatusJabberMask )
{
sendCommandWait( TxReset );
_etherStats->dot3TxExtraEntry.jabbers++;
_etherStats->dot3TxExtraEntry.resets++;
}
else
{
sendCommand( TxEnable );
#if 0
if ( txStat & kTxStatusMaxCollisionsMask )
if ( txStat & kTxStatusOverflowMask )
if ( txStat & kTxStatusReclaimErrorMask )
#endif
return;
}
sendCommand( TxEnable );
initTransmitterParameters();
setDnListPtr( downListPtr );
sendCommand( DnUnStall );
}
void
Apple3Com3C90x::hostErrorInterruptHandler()
{
IOLog("%s: Fatal bus error detected\n", getName());
if ( resetAndEnable( true ) == false )
{
IOLog("%s:%s resetAndEnable error\n", getName(), __FUNCTION__);
}
}
void
Apple3Com3C90x::linkEventInterruptHandler()
{
LOG_INT("%s: linkEventInterruptHandler\n", getName());
monitorLinkStatus();
}
void
Apple3Com3C90x::updateStatsInterruptHandler()
{
LOG_INT("%s::%s\n", getName(), __FUNCTION__);
UInt8 lateCollisions = getLateCollisions();
UInt8 multiCollisions = getMultipleCollisions();
UInt8 singleCollisions = getSingleCollisions();
UInt8 rxOverruns = getRxOverruns();
UInt8 carrierLost = getCarrierLost();
UInt8 sqeErrors = getSqeErrors();
_netStats->collisions += lateCollisions
+ multiCollisions
+ singleCollisions;
_netStats->outputErrors += carrierLost + sqeErrors;
_etherStats->dot3StatsEntry.deferredTransmissions += getFramesDeferred();
_etherStats->dot3StatsEntry.singleCollisionFrames += singleCollisions;
_etherStats->dot3StatsEntry.multipleCollisionFrames += multiCollisions;
_etherStats->dot3StatsEntry.lateCollisions += lateCollisions;
_etherStats->dot3StatsEntry.sqeTestErrors += sqeErrors;
_etherStats->dot3StatsEntry.carrierSenseErrors += carrierLost;
_etherStats->dot3StatsEntry.missedFrames += rxOverruns;
getFramesXmittedOk();
getBytesXmittedOk();
getFramesRcvdOk();
getBytesRcvdOk();
_netStats->inputErrors += rxOverruns + getBadSSD();
}
void
Apple3Com3C90x::interruptHandler( IOInterruptEventSource * src )
{
UInt16 status;
if ( _driverEnableCount == 0 )
{
LOG_INT("%s: unexpected interrupt\n", getName());
return;
}
reserveDebuggerLock();
while ( 1 )
{
status = getCommandStatus();
status &= _interruptMask;
sendCommand( AckIntr, status );
if ( status == 0 ) break;
if ( status & kCommandStatusUpCompleteMask )
{
receiveInterruptHandler();
_etherStats->dot3RxExtraEntry.interrupts++;
}
if ( status & kCommandStatusDnCompleteMask )
{
transmitInterruptHandler();
_etherStats->dot3TxExtraEntry.interrupts++;
}
if ( status & ( kCommandStatusUpdateStatsMask |
kCommandStatusTxCompleteMask |
kCommandStatusLinkEventMask |
kCommandStatusHostErrorMask ) )
{
if ( status & kCommandStatusUpdateStatsMask )
{
updateStatsInterruptHandler();
}
if ( status & kCommandStatusTxCompleteMask )
{
transmitErrorInterruptHandler();
}
if ( status & kCommandStatusLinkEventMask )
{
linkEventInterruptHandler();
}
if ( status & kCommandStatusHostErrorMask )
{
hostErrorInterruptHandler();
}
}
}
releaseDebuggerLock();
}
void
Apple3Com3C90x::setupMulticastHashFilter( IOEthernetAddress * addrs,
UInt32 count )
{
for ( UInt32 i = 0; i < 256; i++ )
{
sendCommand( SetHashFilterBit, i | kHashFilterBitOff );
}
for ( UInt32 i = 0; i < count; i++ )
{
UInt16 bitSlot = hashMulticastAddress( addrs[i].bytes );
sendCommand( SetHashFilterBit, bitSlot | kHashFilterBitOn );
}
}