#include <sys/systm.h> // snprintf
#include "3C90x.h"
void Apple3Com3C90x::resetMedia( const IONetworkMedium * medium )
{
UInt32 mediumType;
LOG_MEDIA("%s::%s(%p)\n", getName(), __FUNCTION__, medium);
_media.mediaPort = kMediaPortAuto;
_media.duplexMode = kHalfDuplex;
_media.phyLink = kMIILinkNone;
_media.phyStatus = 0;
_media.linkStatus = kIONetworkLinkValid;
_media.syncStatus = true;
if ( medium )
{
MediaPort port = (MediaPort) medium->getIndex();
DuplexMode mode;
if ( medium->getType() & kIOMediumOptionFullDuplex )
mode = kFullDuplex;
else
mode = kHalfDuplex;
if ( checkMediaPortSupport( port, mode ) )
{
_media.mediaPort = port;
_media.duplexMode = mode;
}
}
mediumType = getIOMediumType( _media.mediaPort, _media.duplexMode );
medium = IONetworkMedium::getMediumWithType( _mediumDict, mediumType );
if ( medium ) setSelectedMedium( medium );
setLinkStatus( _media.linkStatus );
if ( _media.mediaPort == kMediaPortAuto )
{
if ( _adapterInfo->deviceID == 0x9000 )
{
_media.mediaPort = kMediaPort10BaseT;
_media.duplexMode = kHalfDuplex;
}
else
{
autoSelectMediaPort();
}
}
if ( _media.mediaPort != kMediaPortAuto )
{
mapSelectionToMIIMediaPort();
setMediaRegisters();
configurePHY();
resetAndEnableAdapter( true );
_media.phyConfigured = false;
}
}
void Apple3Com3C90x::monitorLinkStatus()
{
UInt32 linkStatus = _media.linkStatus;
DuplexMode duplexMode = _media.duplexMode;
LinkSpeed linkSpeed = _media.linkSpeed;
MediaPort mediaPort = _media.mediaPort;
MIILink phyLink;
switch ( _media.mediaPort )
{
case kMediaPortAuto:
resetMedia( getSelectedMedium() );
return;
case kMediaPortAutoNeg:
case kMediaPortMII:
if ( _media.phyConfigured == false )
{
configurePHY();
return;
}
phyLink = phyGetBestNegotiatedLink( _media.phyAddress,
_media.phyLink,
_media.phyLinkActive,
&_media.phyStatus );
if ( phyLink == _media.phyLinkActive )
break;
_media.phyLinkActive = phyLink;
phyLink &= kMIILinkMask;
linkStatus &= ~kIONetworkLinkActive;
if ( phyLink != kMIILinkNone )
{
linkStatus |= kIONetworkLinkActive;
if ( phyLink >= kMIILink100TX )
linkSpeed = kLinkSpeed100;
else
linkSpeed = kLinkSpeed10;
duplexMode = getDuplexModeFromMIILink( phyLink );
mediaPort = getMediaPortFromMIILink( phyLink );
}
break;
case kMediaPort10BaseT:
if ( getMediaStatus() & kMediaStatusLinkDetectMask )
linkStatus |= kIONetworkLinkActive;
else
linkStatus &= ~kIONetworkLinkActive;
linkSpeed = kLinkSpeed10;
break;
case kMediaPort100BaseTX:
case kMediaPort100BaseFX:
if ( getMediaStatus() & kMediaStatusLinkDetectMask )
linkStatus |= kIONetworkLinkActive;
else
linkStatus &= ~kIONetworkLinkActive;
linkSpeed = kLinkSpeed100;
break;
default:
linkStatus |= kIONetworkLinkActive;
linkSpeed = kLinkSpeed10;
break;
}
if ( linkStatus != _media.linkStatus )
{
IONetworkMedium * activeMedium = 0;
LOG_MEDIA("%s: link %lx speed %d duplex %d status %lx\n",
getName(), _media.phyLinkActive, linkSpeed,
duplexMode, linkStatus);
if ( linkStatus & kIONetworkLinkActive )
{
activeMedium = IONetworkMedium::getMediumWithType(
_mediumDict,
getIOMediumType( mediaPort, duplexMode ) );
}
setLinkStatus( linkStatus, activeMedium );
_media.linkStatus = linkStatus;
}
if ( duplexMode != _media.duplexMode )
{
setDuplexMode( duplexMode );
_media.duplexMode = duplexMode;
}
if ( linkSpeed != _media.linkSpeed )
{
setLinkSpeed( linkSpeed );
_media.linkSpeed = linkSpeed;
}
}
bool Apple3Com3C90x::autoSelectMediaPort()
{
bool success = true;
MediaPort activePort = kMediaPortAuto;
_linkTest = true;
if ( ( _mediaOptions &
( kMediaOptionsBaseTXMask | kMediaOptionsBase10BTMask ) ) &&
( _adapterInfo->type >= kAdapterType3C90xB ) )
{
LOG_MEDIA("%s::%s kMediaPortAutoNeg\n", getName(), __FUNCTION__);
selectTransceiverPort( kMediaPortAutoNeg );
if ( testNwayMIIPort() )
{
activePort = kMediaPortAutoNeg;
goto done;
}
}
if ( _mediaOptions &
( kMediaOptionsBaseMIIMask | kMediaOptionsBaseT4Mask ) )
{
LOG_MEDIA("%s::%s kMediaPortMII\n", getName(), __FUNCTION__);
selectTransceiverPort( kMediaPortMII );
if ( testNwayMIIPort() )
{
activePort = kMediaPortMII;
goto done;
}
}
if ( _mediaOptions & kMediaOptionsBaseFXMask )
{
LOG_MEDIA("%s::%s kMediaPort100BaseFX\n", getName(), __FUNCTION__);
selectTransceiverPort( kMediaPort100BaseFX );
if ( testLinkStatus() )
{
activePort = kMediaPort100BaseFX;
goto done;
}
}
if ( ( _mediaOptions & kMediaOptionsBaseTXMask ) &&
( _adapterInfo->type == kAdapterType3C90x ) )
{
LOG_MEDIA("%s::%s kMediaPort100BaseFX\n", getName(), __FUNCTION__);
selectTransceiverPort( kMediaPort100BaseTX );
if ( testLinkStatus() )
{
activePort = kMediaPort100BaseTX;
goto done;
}
}
if ( ( _mediaOptions & kMediaOptionsBase10BTMask ) &&
( _adapterInfo->type == kAdapterType3C90x ) )
{
LOG_MEDIA("%s::%s kMediaPort10BaseT\n", getName(), __FUNCTION__);
selectTransceiverPort( kMediaPort10BaseT );
if ( testLinkStatus() )
{
activePort = kMediaPort10BaseT;
goto done;
}
}
if ( _mediaOptions & kMediaOptionsBaseAUIMask )
{
LOG_MEDIA("%s::%s kMediaPortAUI\n", getName(), __FUNCTION__);
selectTransceiverPort( kMediaPortAUI );
if ( testLoopBack( kMediaPortAUI ) )
{
activePort = kMediaPortAUI;
goto done;
}
}
if ( _mediaOptions & kMediaOptionsBaseCoaxMask )
{
LOG_MEDIA("%s::%s kMediaPort10Base2\n", getName(), __FUNCTION__);
selectTransceiverPort( kMediaPort10Base2 );
if ( testLoopBack( kMediaPort10Base2 ) )
{
activePort = kMediaPort10Base2;
goto done;
}
}
success = false;
LOG_MEDIA("%s::%s no active port found\n", getName(), __FUNCTION__);
done:
_linkTest = false;
_media.mediaPort = activePort;
_media.duplexMode = kHalfDuplex;
return success;
}
bool Apple3Com3C90x::testLoopBack( MediaPort port )
{
UInt16 macCtrl;
bool success = false;
macCtrl = getMacControl();
macCtrl |= kMacControlFullDuplexEnableMask;
setMacControl( macCtrl );
if ( port == kMediaPort10Base2 )
{
sendCommand( EnableDCConv );
IOSleep(2);
}
for ( int i = 0; i < 2; i++ )
{
flushReceiveRing();
transmitTestFrame();
if ( receiveTestFrame( 1500 ) )
{
success = true;
break;
}
}
if ( port == kMediaPort10Base2 )
{
sendCommand( DisableDCConv );
IOSleep(2);
}
macCtrl &= ~kMacControlFullDuplexEnableMask;
setMacControl( macCtrl );
return success;
}
bool Apple3Com3C90x::testLinkStatus()
{
UInt32 i;
UInt16 mediaStatus;
const UInt32 maxLoops = 5000;
UInt32 csCounter = 0;
bool success = false;
mediaStatus = getMediaStatus();
mediaStatus |= ( kMediaStatusLinkBeatEnabledMask |
kMediaStatusJabberGuardEnabledMask );
setMediaStatus( mediaStatus );
sendCommand( SetRxFilter, kFilterPromiscuous );
transmitTestFrame();
flushReceiveRing();
for ( i = 0; i < maxLoops; i++ )
{
mediaStatus = getMediaStatus();
if ( mediaStatus & kMediaStatusCarrierSenseMask )
csCounter++;
if ( ( mediaStatus & kMediaStatusLinkDetectMask ) == 0 )
{
success = false;
break;
}
if ( receiveTestFrame( 0 ) )
{
success = true;
LOG_MEDIA("%s::%s frame received\n", getName(), __FUNCTION__);
break;
}
}
mediaStatus &= ~( kMediaStatusLinkBeatEnabledMask |
kMediaStatusJabberGuardEnabledMask );
setMediaStatus( mediaStatus );
sendCommand( SetRxFilter, _rxFilterMask );
if ( i >= maxLoops )
{
success = ( csCounter < (maxLoops / 4) );
}
LOG_MEDIA("%s::%s link is %s\n", getName(), __FUNCTION__,
success ? "Good" : "Bad");
return success;
}
void Apple3Com3C90x::transmitTestFrame()
{
UInt8 packet[ kIOEthernetMinPacketSize ];
bcopy( &_etherAddress, packet, kIOEthernetAddressSize );
bcopy( &_etherAddress, packet + kIOEthernetAddressSize, kIOEthernetAddressSize );
sendPacket( packet, kIOEthernetMinPacketSize );
}
bool Apple3Com3C90x::receiveTestFrame( UInt32 timeoutMS )
{
UInt32 len = 0;
receivePacket( NULL, &len, timeoutMS );
return ( len > 0 );
}
void Apple3Com3C90x::flushReceiveRing()
{
UInt32 len;
for ( UInt32 i = 0; i < _rxRingSize; i++ )
{
receivePacket( NULL, &len, 1 );
}
}
bool Apple3Com3C90x::testNwayMIIPort()
{
return ( _media.phyAddress != kPHYAddressInvalid );
}
void Apple3Com3C90x::probeMediaSupport()
{
bzero( &_media, sizeof(_media) );
_media.phyAddress = kPHYAddressInvalid;
_media.phyID = kPHYIDInvalid;
_media.mediaPortMask = (1 << kMediaPortAuto);
if ( _mediaOptions & kMediaOptionsBase10BTMask )
_media.mediaPortMask |= (1 << kMediaPort10BaseT);
if ( _mediaOptions & kMediaOptionsBaseAUIMask )
_media.mediaPortMask |= (1 << kMediaPortAUI);
if ( _mediaOptions & kMediaOptionsBaseCoaxMask )
_media.mediaPortMask |= (1 << kMediaPort10Base2);
if ( _mediaOptions & kMediaOptionsBaseTXMask )
_media.mediaPortMask |= (1 << kMediaPort100BaseTX);
if ( _mediaOptions & kMediaOptionsBaseFXMask )
_media.mediaPortMask |= (1 << kMediaPort100BaseFX);
for ( PHYAddress addr = kPHYAddrMin; addr <= kPHYAddrMax; addr++ )
{
UInt32 linkMask = phyGetSupportedLinks( addr );
if ( linkMask )
{
_media.phyAddress = addr;
_media.miiLinkMask = linkMask;
_media.phyID = phyGetIdentifier( addr );
break;
}
}
if ( _media.miiLinkMask & kMIILink10BT )
_media.mediaPortMask |= (1 << kMediaPort10BaseT);
if ( _media.miiLinkMask & kMIILink100TX )
_media.mediaPortMask |= (1 << kMediaPort100BaseTX);
if ( _media.miiLinkMask & kMIILink100T4 )
_media.mediaPortMask |= (1 << kMediaPort100BaseT4);
if ( _media.miiLinkMask & kMIILink10BT_FD )
_media.mediaPortFDMask |= (1 << kMediaPort10BaseT);
if ( _media.miiLinkMask & kMIILink100TX_FD )
_media.mediaPortFDMask |= (1 << kMediaPort100BaseTX);
LOG_MEDIA("%s::%s mediaPortMask %04lx FDMask %04lx miiLinkMask %02lx\n",
getName(), __FUNCTION__,
_media.mediaPortMask, _media.mediaPortFDMask,
_media.miiLinkMask);
}
UInt32 Apple3Com3C90x::getIOMediumType( MediaPort mediaPort,
DuplexMode duplexMode )
{
UInt32 mediumType = mediaPortTable[mediaPort].ioType;
if ( duplexMode == kFullDuplex )
mediumType |= kIOMediumOptionFullDuplex;
else if ( mediaPort != kMediaPortAuto )
mediumType |= kIOMediumOptionHalfDuplex;
return mediumType;
}
bool Apple3Com3C90x::addMediaPort( OSDictionary * mediaDict,
MediaPort mediaPort,
DuplexMode duplexMode )
{
#define MBPS 1000000
IONetworkMedium * medium;
char mediumName[ 40 ];
UInt32 mediumType;
bool ret = false;
snprintf( mediumName, 40, "%s%s", mediaPortTable[mediaPort].name,
duplexMode == kFullDuplex ?
" Full-Duplex" : "");
mediumType = getIOMediumType( mediaPort, duplexMode );
medium = IONetworkMedium::medium( mediumType,
mediaPortTable[mediaPort].speed * MBPS,
0,
mediaPort,
mediumName );
if ( medium )
{
ret = IONetworkMedium::addMedium( mediaDict, medium );
medium->release();
}
return ret;
}
bool Apple3Com3C90x::publishMediaCapability( OSDictionary * mediaDict )
{
for ( UInt32 port = 0; port < kMediaPortCount; port++ )
{
if ( mediaPortTable[port].selectable &&
( (1 << port) & _media.mediaPortMask ) )
{
addMediaPort( mediaDict, (MediaPort) port, kHalfDuplex );
if ( (1 << port) & _media.mediaPortFDMask )
{
addMediaPort( mediaDict, (MediaPort) port, kFullDuplex );
}
}
}
return publishMediumDictionary( mediaDict );
}
bool Apple3Com3C90x::checkMediaPortSupport( MediaPort mediaPort,
DuplexMode duplexMode )
{
bool support = false;
do {
if ( mediaPort >= kMediaPortCount )
break;
if ( !( _media.mediaPortMask & (1 << mediaPort) ) )
break;
if ( ( duplexMode == kFullDuplex ) &&
!( _media.mediaPortFDMask & (1 << mediaPort) ) )
break;
support = true;
}
while ( false );
LOG_MEDIA("%s::%s %s support = %s\n", getName(), __FUNCTION__,
mediaPortTable[mediaPort].name,
support ? "true" : "false");
return support;
}
MIILink
Apple3Com3C90x::getMIILinkFromMediaPort( MediaPort mediaPort,
DuplexMode duplexMode )
{
MIILink link;
switch ( mediaPort )
{
case kMediaPort10BaseT:
if ( duplexMode == kFullDuplex )
link = kMIILink10BT_FD;
else
link = kMIILink10BT;
break;
case kMediaPort100BaseTX:
if ( duplexMode == kFullDuplex )
link = kMIILink100TX_FD;
else
link = kMIILink100TX;
break;
case kMediaPort100BaseT4:
link = kMIILink100T4;
break;
default:
link = kMIILinkNone;
}
LOG_MEDIA("%s::%s port %d converted to %lx\n", getName(), __FUNCTION__,
mediaPort, link);
return link;
}
MediaPort
Apple3Com3C90x::getMediaPortFromMIILink( MIILink miiLink )
{
MediaPort mediaPort;
switch ( miiLink & kMIILinkMask )
{
case kMIILink10BT:
case kMIILink10BT_FD:
mediaPort = kMediaPort10BaseT;
break;
case kMIILink100TX:
case kMIILink100TX_FD:
mediaPort = kMediaPort100BaseTX;
break;
case kMIILink100T4:
mediaPort = kMediaPort100BaseT4;
break;
default:
mediaPort = kMediaPortAuto;
break;
}
LOG_MEDIA("%s::%s link %lx converted to %d\n", getName(), __FUNCTION__,
miiLink, mediaPort);
return mediaPort;
}
DuplexMode
Apple3Com3C90x::getDuplexModeFromMIILink( MIILink miiLink )
{
if ( miiLink & ( kMIILink10BT_FD | kMIILink100TX_FD ) )
return kFullDuplex;
else
return kHalfDuplex;
}
bool
Apple3Com3C90x::mapSelectionToMIIMediaPort()
{
bool success = true;
if ( _media.phyAddress == kPHYAddressInvalid )
return false;
_media.phyConfigured = false;
if ( ( _media.mediaPort == kMediaPortAutoNeg ) ||
( _media.mediaPort == kMediaPortMII ) )
{
LOG_MEDIA("%s::%s NWay\n", getName(), __FUNCTION__);
_media.phyLink = ( kMIILinkNway | kMIILinkNone );
}
else
{
MIILink link = getMIILinkFromMediaPort( _media.mediaPort,
_media.duplexMode );
if ( link & _media.miiLinkMask )
{
LOG_MEDIA("%s::%s Non-NWay link %lx\n", getName(), __FUNCTION__,
link);
if ( ( _adapterInfo->type >= kAdapterType3C90xB ) &&
( _media.phyAddress == kPHYAddrCyclone ) )
_media.mediaPort = kMediaPortAutoNeg;
else
_media.mediaPort = kMediaPortMII;
_media.phyLink = link;
}
else
{
success = false; }
}
LOG_MEDIA("%s::%s %s\n", getName(), __FUNCTION__,
success ? "success" : "failed");
return success;
}
bool
Apple3Com3C90x::configurePHY()
{
bool success = false;
if ( ( _media.mediaPort == kMediaPortAutoNeg ) ||
( _media.mediaPort == kMediaPortMII ) )
{
if ( _media.phyLink & kMIILinkNway )
{
success = phyStartNegotiation( _media.phyAddress );
}
else
{
success = phyForceMIILink( _media.phyAddress,
_media.phyLink );
}
_media.phyConfigured = true;
_media.phyLinkActive = kMIILinkNone;
_media.phyStatus = 0;
IOSleep( 250 );
}
return success;
}
bool
Apple3Com3C90x::setMediaRegisters()
{
UInt16 macControl;
UInt16 mediaStatus;
if ( _media.phyAddress != kPHYAddressInvalid )
{
LOG_MEDIA("%s::%s MII link on PHY %d\n", getName(), __FUNCTION__,
_media.phyAddress);
}
else
{
LOG_MEDIA("%s: %s%s media port\n",
getName(), mediaPortTable[_media.mediaPort].name,
(_media.duplexMode == kFullDuplex) ? " Full-Duplex" : "");
}
LOG_MEDIA(" Port : %s\n", mediaPortTable[_media.mediaPort].name);
LOG_MEDIA(" MIILink: %lx\n", _media.phyLink);
LOG_MEDIA(" PHYAddr: %d\n", _media.phyAddress);
LOG_MEDIA(" PHYID : %08lx\n", _media.phyID);
LOG_MEDIA(" FDuplex: %s\n", _media.duplexMode == kFullDuplex ?
"Yes" : "No");
setDuplexMode( _media.duplexMode );
setLinkSpeed( _media.linkSpeed );
macControl = getMacControl();
if ( _extendAfterCollision )
macControl |= kMacControlExtendAfterCollisionMask;
else
macControl &= ~kMacControlExtendAfterCollisionMask;
setMacControl( macControl );
selectTransceiverPort( _media.mediaPort );
if ( _media.mediaPort == kMediaPort10Base2 )
sendCommand( EnableDCConv );
else
sendCommand( DisableDCConv );
IOSleep(2);
mediaStatus = getMediaStatus();
mediaStatus &= ~kMediaCodeMask;
mediaStatus |= mediaPortTable[_media.mediaPort].mediaCode;
setMediaStatus( mediaStatus );
LOG_MEDIA("%s: port: %d mediaCode: %04x mediaStatus: %04x\n", getName(),
_media.mediaPort, mediaPortTable[_media.mediaPort].mediaCode,
mediaStatus);
return true;
}
void
Apple3Com3C90x::setLinkSpeed( LinkSpeed speed )
{
LOG_MEDIA("%s: set %s link speed\n", getName(),
speed == kLinkSpeed10 ? "10" : "100");
if ( _storeAndForward == false )
{
if ( speed == kLinkSpeed10 )
sendCommand( SetTxStartThresh, _txStartThresh_10 >> 2 );
else
sendCommand( SetTxStartThresh, _txStartThresh_100 >> 2 );
}
}
void
Apple3Com3C90x::setDuplexMode( DuplexMode mode )
{
UInt16 macControl;
LOG_MEDIA("%s: set %s duplex mode\n", getName(),
mode == kFullDuplex ? "full" : "half");
macControl = getMacControl();
macControl &= ~( kMacControlFullDuplexEnableMask
| kMacControlFlowControlEnableMask );
if ( mode == kFullDuplex )
{
macControl |= kMacControlFullDuplexEnableMask;
if ( _flowControlEnabled )
macControl |= kMacControlFlowControlEnableMask;
}
setMacControl( macControl );
}