/*+
* file: media.c
*
* Copyright (C) 1992-1995 by
* Digital Equipment Corporation, Maynard, Massachusetts.
* All rights reserved.
*
* This software is furnished under a license and may be used and copied
* only in accordance of the terms of such license and with the
* inclusion of the above copyright notice. This software or any other
* copies thereof may not be provided or otherwise made available to any
* other person. No title to and ownership of the software is hereby
* transferred.
*
* The information in this software is subject to change without notice
* and should not be construed as a commitment by digital equipment
* corporation.
*
* Digital assumes no responsibility for the use or reliability of its
* software on equipment which is not supplied by digital.
*
*
* Abstract: This file contains the Media detection code of the
* NDIS 4.0 miniport driver for DEC's DC21X4 Ethernet Adapter
* family.
*
* Author: Philippe Klein
*
* Revision History:
*
* phk 01-Dec-1994 Initial entry
* phk 31-Jan-1994 Add Polarity support
* phk 26-Apr-1995 Modify the DC21140 MediaDetect and
* AutoSense routines
*
-*/
#include <precomp.h>
#define MII100_TICK 30 // 2.5ms (81.9 us/tick)
#define EXT10_TICK 12 // 2.5ms (204.8 us/tick)
#define MII10_TICK 3 // 2.5ms (819.2 us/tick)
#define ONE_SECOND_DELAY 400 // * 2.5 ms
#define FIVE_MILLISECONDS_DELAY 2 // * 2.5 ms
#define MAX_RETRY 4
#define GEP_READ_DELAY 200 // milliseconds
#define DC21X4_LINK_STATUS(_status,_adapter,_medium) \
(BOOLEAN) ( ( ( (_status) ^ (_adapter)->Media[(_medium)].Polarity) \
& (_adapter)->Media[(_medium)].SenseMask ) != 0)
#if __DBG
PUCHAR MediumString[] = {
"10BaseT",
"10Base2",
"10Base5",
"100BaseTx",
"10BaseT_FD",
"100BaseTx_FD",
"100BaseT4",
"100BaseFx",
"100BaseFx_FD",
"Mii10BaseT",
"Mii10BaseT_FD",
"Mii10Base2",
"Mii10Base5",
"Mii100BaseTx",
"Mii100BaseTx_FD",
"Mii100BaseT4",
"Mii100BaseFx",
"Mii100BaseFx_FD"
};
#endif
/*+
* DC21X4MediaDetect
*
* Routine Description:
*
* DC21X4MediaDetect:
*
* checks the DC2104x media ports in the following order:
* 10BaseT -> 10Base2 -> 10Base5
*
* checks the DC21140 link status
*
* Arguments:
*
* Adapter
*
* Return Value:
*
* TRUE if the Autosense timer should be fired
*
-*/
extern
BOOLEAN
DC21X4MediaDetect(
IN PDC21X4_ADAPTER Adapter
)
{
PDC21X4_TRANSMIT_DESCRIPTOR TxmDescriptor;
ULONG Status;
UINT PacketSize = 64;
INT Time;
INT CurrentMedium=0;
INT i;
INT j;
BOOLEAN Link=FALSE;
BOOLEAN Sensed=FALSE;
#if __DBG
DbgPrint("DC21X4MediaDetect\n");
#endif
switch (Adapter->DeviceId) {
case DC21140_CFID:
if (Adapter->MediaType & MEDIA_AUTOSENSE) {
//AutoSense mode:
//Initialize the General Purpose Control register
CurrentMedium = Adapter->MediaPrecedence[0];
DC21X4_WRITE_PORT(
DC21X4_GEN_PURPOSE,
Adapter->Media[CurrentMedium].GeneralPurposeCtrl
);
//Check the link status of each medium supported
//by the adapter until afirst link is detected up.
for (i=Adapter->MediaCount; i>0; i--) {
CurrentMedium = Adapter->MediaPrecedence[i-1];
#if __DBG
DbgPrint("DC21X4MediaDetect: %d - Check medium %x \n",
i,CurrentMedium);
#endif
if (( (CurrentMedium == Medium100BaseTx)
||(CurrentMedium == Medium10BaseT)
)
&& (!Sensed)
) {
// 100BaseTx or 10BaseT medium:
// Check the 100BaseTx & the 10BaseT link
Link = DC2114Sense100BaseTxLink(Adapter);
Sensed = TRUE;
#if __DBG
DbgPrint(" Link[%x]=%d\n",
(Link?Adapter->SelectedMedium:CurrentMedium),Link);
#endif
if (Link) {
// a link was detected up
break;
}
}
else {
//Medium is not 100BaseTx or 10BaseT:
//Initialize the Mode and GEP registers
//and check the link status
DC21X4_WRITE_PORT(
DC21X4_OPERATION_MODE,
((Adapter->OperationMode & ~(DC21X4_MEDIUM_MASK))
| Adapter->Media[CurrentMedium].Mode)
);
DC21X4_WRITE_PORT(
DC21X4_GEN_PURPOSE,
Adapter->Media[CurrentMedium].GeneralPurposeData
);
for (j=0;j<(GEP_READ_DELAY/5);j++) {
NdisStallExecution(5*MILLISECOND);
}
DC21X4_READ_PORT(
DC21X4_GEN_PURPOSE,
&Status
);
Link = DC21X4_LINK_STATUS(Status,Adapter,CurrentMedium);
#if __DBG
DbgPrint(" Link[%x]=%d\n",CurrentMedium,Link);
#endif
if (Link) {
// The link was detected up:
// select the current medium
Adapter->SelectedMedium = CurrentMedium;
Adapter->OperationMode &= ~(DC21X4_MEDIUM_MASK);
Adapter->OperationMode |= Adapter->Media[CurrentMedium].Mode;
break;
}
}
}
DC21X4IndicateMediaStatus(
Adapter,
Link ? LinkPass : LinkFail
);
if (!Link) {
//No link detected: select the default medium
#if __DBG
DbgPrint("MediaDetect: No link - Select the default Medium (%x)\n",
Adapter->DefaultMedium);
#endif
Adapter->SelectedMedium = Adapter->DefaultMedium;
Adapter->OperationMode &= ~(DC21X4_MEDIUM_MASK);
Adapter->OperationMode |= Adapter->Media[Adapter->SelectedMedium].Mode;
DC21X4_WRITE_PORT(
DC21X4_OPERATION_MODE,
Adapter->OperationMode
);
DC21X4_WRITE_PORT(
DC21X4_GEN_PURPOSE,
Adapter->Media[Adapter->SelectedMedium].GeneralPurposeData
);
}
}
else {
// Not AutoSense mode
if (Adapter->Media[Adapter->SelectedMedium].SenseMask) {
//Check the link status of the select medium
DC21X4_READ_PORT(
DC21X4_GEN_PURPOSE,
&Status
);
Link = DC21X4_LINK_STATUS(Status,Adapter,Adapter->SelectedMedium);
DC21X4IndicateMediaStatus(
Adapter,
Link ? LinkPass : LinkFail
);
}
else {
//There is no link status reported in the
//General Purpose Register for the selected medium:
//Set the LinkPass flag but do not start AutoSense
if (!Adapter->PhyPresent) {
DC21X4IndicateMediaStatus(Adapter,LinkPass);
}
return Adapter->PhyPresent;
}
}
// if DynamicAutoSense mode is disabled clear
// the AutoSense flag in MediaType to disable
// the medium link dynamic check but fire the
// Spa timer anyway to check the link status
// of the selected medium
if (!Adapter->DynamicAutoSense) {
Adapter->MediaType &= ~(MEDIA_AUTOSENSE);
}
return TRUE;
case DC21041_CFID:
case DC21142_CFID:
if (!(Adapter->MediaType & MEDIA_AUTOSENSE)) {
return Adapter->PhyPresent;
}
if (Adapter->SelectedMedium != Medium10BaseT) {
// Selected medium is 10Base2 or 10Base5
DC21X4IndicateMediaStatus(Adapter,LinkPass);
return TRUE;
}
return Adapter->PhyPresent;
case DC21040_CFID:
if (Adapter->SelectedMedium != Medium10BaseT) {
DC21X4IndicateMediaStatus(Adapter,LinkPass);
return FALSE;
}
// Selected Medium = 10BaseT:
// Check the TP Link status
do {
// Read the SIA satus
DC21X4_READ_PORT(
DC21X4_SIA_STATUS,
&Status
);
#if __DBG
DbgPrint("Sia Status = %08x\n",Status);
#endif
if (!(Status & DC21X4_LINKFAIL_10)) {
#if __DBG
DbgPrint("MediaDetect: TP Link established\n");
#endif
DC21X4IndicateMediaStatus(Adapter,LinkPass);
return FALSE;
}
if (Status & DC21X4_NETWORK_CONNECTION_ERROR) {
#if __DBG
DbgPrint("MediaDetect: TP Link failure\n");
#endif
break;
}
}
while ((Status & DC21X4_LINKFAIL_10)
&& !(Status & DC21X4_NETWORK_CONNECTION_ERROR));
if (!(Adapter->MediaType & MEDIA_AUTOSENSE)) {
DC21X4IndicateMediaStatus(Adapter,LinkFail);
return FALSE;
}
// AutoSense mode: Link Pass failure
#if __DBG
DbgPrint(" 10BaseT link failure: switch to 10Base2 \n");
#endif
Adapter->SelectedMedium = Medium10Base2;
DC2104InitializeSiaRegisters(
Adapter
);
// wait at least 300ms for the 10Base2 transceivers
// to stabilize
for (i=0; i< max(Adapter->TransceiverDelay,30); i++) {
for (j=0;j<2;j++) {
NdisStallExecution(5*MILLISECOND);
}
}
//Send a minimal size packet (with an false CRC) to check
//the carrier status
ZERO_MEMORY (
Adapter->MaxTransmitBuffer[Adapter->MaxTransmitBufferIndex].Va,
PacketSize
);
MOVE_MEMORY (
Adapter->MaxTransmitBuffer[Adapter->MaxTransmitBufferIndex].Va,
Adapter->CurrentNetworkAddress,
ETH_LENGTH_OF_ADDRESS
);
MOVE_MEMORY (
Adapter->MaxTransmitBuffer[Adapter->MaxTransmitBufferIndex].Va + ETH_LENGTH_OF_ADDRESS,
Adapter->CurrentNetworkAddress,
ETH_LENGTH_OF_ADDRESS
);
TxmDescriptor = Adapter->EnqueueTransmitDescriptor;
Adapter->EnqueueTransmitDescriptor = (Adapter->EnqueueTransmitDescriptor)->Next;
Adapter->DequeueTransmitDescriptor = Adapter->EnqueueTransmitDescriptor;
// Initialize the descriptor
TxmDescriptor->Control &= DC21X4_TDES_SECOND_ADDR_CHAINED;
TxmDescriptor->Control |=
( DC21X4_TDES_FIRST_SEGMENT | DC21X4_TDES_LAST_SEGMENT
| DC21X4_TDES_ADD_CRC_DISABLE | PacketSize );
TxmDescriptor->FirstBufferAddress =
Adapter->MaxTransmitBuffer[Adapter->MaxTransmitBufferIndex].Pa;
TxmDescriptor->Status = DESC_OWNED_BY_DC21X4;
// Mask the interrupts
DC21X4_WRITE_PORT(
DC21X4_INTERRUPT_MASK,
0
);
#if __DBG
DbgPrint(" send a test packet\n");
#endif
DC21X4_WRITE_PORT(
DC21X4_TXM_POLL_DEMAND,
1
);
// Poll for completion
Time = DC21X4_TXM_TIMEOUT;
while (--Time) {
for (j=0;j<2;j++) {
NdisStallExecution(5*MILLISECOND);
}
DC21X4_READ_PORT(
DC21X4_STATUS,
&Status
);
#if __DBG
DbgPrint(" CSR5 = %08x Time = %d\n",Status,Time);
#endif
if (Status & DC21X4_TXM_BUFFER_UNAVAILABLE) {
break;
}
}
#if __DBG
DbgPrint(" Desc status = %08x Time = %d \n",TxmDescriptor->Status,Time);
#endif
//Check if Status_Error or Timeout
if (TxmDescriptor->Status & DC21X4_TDES_ERROR_SUMMARY || (Time <= 0) ) {
//switch to AUI Port
#if __DBG
DbgPrint(" 10Base2 failure: switch to 10Base5\n");
#endif
Adapter->SelectedMedium = Medium10Base5;
//Reset the adapter to clean up the Txm path
DC21X4InitializeRegisters(
Adapter
);
DC2104InitializeSiaRegisters(
Adapter
);
Adapter->DequeueReceiveDescriptor =
(PDC21X4_RECEIVE_DESCRIPTOR)Adapter->ReceiveDescriptorRingVa;
Adapter->EnqueueTransmitDescriptor =
(PDC21X4_TRANSMIT_DESCRIPTOR)Adapter->TransmitDescriptorRingVa;
Adapter->DequeueTransmitDescriptor =
Adapter->EnqueueTransmitDescriptor;
DC21X4StartAdapter(Adapter);
}
else {
//Clear Txm interrupts
DC21X4_WRITE_PORT(
DC21X4_STATUS,
Status
);
}
//Restore the interrupt mask
DC21X4_WRITE_PORT(
DC21X4_INTERRUPT_MASK,
Adapter->InterruptMask
);
DC21X4IndicateMediaStatus(Adapter,LinkPass);
return FALSE;
}
return FALSE;
}
/*+
*
*DC2114Sense100BaseTxLink
*
* Routine Description:
*
* Sense the DC2114X 100BaseTx and 10BaseT link status
*
* Arguments:
*
* Adapter
*
* Return Value:
*
* Link Status
*
-*/
extern
BOOLEAN
DC2114Sense100BaseTxLink(
IN PDC21X4_ADAPTER Adapter
)
{
ULONG Status;
ULONG Mode100Tx;
ULONG CFDA_Data;
INT Loop;
INT Retry = MAX_RETRY;
INT AssertionTime;
INT CurrentTime;
INT AssertionThreshold;
INT Timeout;
INT Medium10Tick;
INT i;
BOOLEAN Link = FALSE;
BOOLEAN Scrambler;
#if __DBG
DbgPrint("Sense100BaseTxLink\n");
#endif
// If the adapter is in Snooze mode,
// switch to regular mode to enable the
// built-in timer
if (Adapter->PciDriverArea & CFDA_SNOOZE_MODE) {
CFDA_Data = Adapter->PciDriverArea & ~CFDA_SNOOZE_MODE;
NdisWritePciSlotInformation(
Adapter->MiniportAdapterHandle,
Adapter->SlotNumber,
PCI_CFDA_OFFSET,
&CFDA_Data,
sizeof(CFDA_Data)
);
}
//Mask the Timer_Expired interrupt
DC21X4_WRITE_PORT(
DC21X4_INTERRUPT_MASK,
Adapter->InterruptMask & ~(DC21X4_MSK_TIMER_EXPIRED)
);
//Sense the 100BaseTx & 10BaseT link
Mode100Tx =
( (Adapter->OperationMode & ~(DC21X4_MEDIUM_MASK))
| Adapter->Media[Medium100BaseTx].Mode
)
& ~(DC21X4_SCRAMBLER);
// Set the 10 Mbps timer tick
Medium10Tick =
(Adapter->Media[Medium10BaseT].Mode & DC21X4_PORT_SELECT) ?
MII10_TICK : EXT10_TICK;
while (Retry-- && !Link) {
//if DC21140 Rev1.1 disable the scrambler
Scrambler = (Adapter->RevisionNumber != DC21140_REV1_1);
Loop=2;
while(Loop-- && !Link) {
AssertionThreshold =
Scrambler? FIVE_MILLISECONDS_DELAY : ONE_SECOND_DELAY;
Timeout = (3 * AssertionThreshold);
if (Adapter->MediaCapable & MEDIUM_100BTX) {
//Select 100BaseTx
DC21X4_WRITE_PORT(
DC21X4_OPERATION_MODE,
Mode100Tx
| ( Scrambler ? DC21X4_SCRAMBLER : 0 )
);
DC21X4_WRITE_PORT(
DC21X4_GEN_PURPOSE,
Adapter->Media[Medium100BaseTx].GeneralPurposeData
);
// Check 100BaseTx Symbol Link for a
// continuous assertion of 'AssertionThreshold'
AssertionTime = 0;
CurrentTime = 0;
//Start the built_in timer in cyclic mode of
//2.5 ms ticks
DC21X4_WRITE_PORT(
DC21X4_TIMER,
MII100_TICK | DC21X4_TIMER_CON_MODE
);
while ((CurrentTime < (Timeout+1)) && !Link) {
DC21X4_READ_PORT(
DC21X4_STATUS,
&Status
);
if (Status & DC21X4_MSK_TIMER_EXPIRED) {
DC21X4_WRITE_PORT(
DC21X4_STATUS,
DC21X4_MSK_TIMER_EXPIRED
);
CurrentTime++;
}
DC21X4_READ_PORT(
DC21X4_GEN_PURPOSE,
&Status
);
if (DC21X4_LINK_STATUS(Status,Adapter,Medium100BaseTx)) {
// Link is asserted
if (AssertionTime == 0 ) {
//First assertion
AssertionTime = CurrentTime+1;
}
else {
Link = ((CurrentTime - AssertionTime) >= AssertionThreshold);
}
}
else {
// No link
AssertionTime = 0;
}
}
//Stop the built_in timer
DC21X4_WRITE_PORT(
DC21X4_TIMER,
0
);
DC21X4_WRITE_PORT(
DC21X4_STATUS,
DC21X4_MSK_TIMER_EXPIRED
);
if (Link) {
// 100BaseTx link detected: Select 100BaseTx
Adapter->SelectedMedium = Medium100BaseTx;
Adapter->OperationMode &= ~(DC21X4_MEDIUM_MASK);
Adapter->OperationMode |= Adapter->Media[Medium100BaseTx].Mode;
if (!Scrambler) {
//Turn the scrambler on
DC21X4_WRITE_PORT(
DC21X4_OPERATION_MODE,
Adapter->OperationMode
);
}
break;
}
}
// No 100BaseTx link detected:
if (Adapter->MediaCapable & MEDIUM_10BT) {
// Switch to 10BaseT
DC21X4_WRITE_PORT(
DC21X4_OPERATION_MODE,
(Adapter->OperationMode &~(DC21X4_MEDIUM_MASK))
| Adapter->Media[Medium10BaseT].Mode
);
DC21X4_WRITE_PORT(
DC21X4_GEN_PURPOSE,
Adapter->Media[Medium10BaseT].GeneralPurposeData
);
//Check the 10BaseT link status
// Start the built_in timer for
// half the 100BaseTx timeout
DC21X4_WRITE_PORT(
DC21X4_TIMER,
(Timeout/2) * Medium10Tick
);
while (!Link) {
//Check the 10BaseT link
for (i=0,Link=TRUE; i<2; i++) {
DC21X4_READ_PORT(
DC21X4_GEN_PURPOSE,
&Status
);
Link = Link && DC21X4_LINK_STATUS(Status,Adapter,Medium10BaseT);
}
DC21X4_READ_PORT(
DC21X4_STATUS,
&Status
);
if (Status & DC21X4_MSK_TIMER_EXPIRED) {
break;
}
}
}
if (Link) {
// 10BaseT link detected:
//Stop the timer
DC21X4_WRITE_PORT(
DC21X4_TIMER,
0
);
DC21X4_WRITE_PORT(
DC21X4_STATUS,
DC21X4_MSK_TIMER_EXPIRED
);
if (Loop) {
// first detection of 10BT link:
// check the 100BaseTx link again to reject
// a 'false' 10BT link link induced by the 100BTX
Link = FALSE;
}
else {
// 10BT link detected twice:
// select Medium10BaseT
Adapter->SelectedMedium = Medium10BaseT;
Adapter->OperationMode &= ~(DC21X4_MEDIUM_MASK);
Adapter->OperationMode |= Adapter->Media[Medium10BaseT].Mode;
}
}
else if (Loop) {
if (Scrambler) {
//Disable the scrambler and restart the
//link check
Scrambler = FALSE;
Loop = 2;
}
else {
// First loop & Scrambler disbled:
// leave the loop and select the default medium
Retry = 0;
break;
}
}
} // endwhile Loop
} //endwhile Retry
//Demask the Timer_Expired interrupt
DC21X4_WRITE_PORT(
DC21X4_STATUS,
DC21X4_MSK_TIMER_EXPIRED
);
DC21X4_WRITE_PORT(
DC21X4_INTERRUPT_MASK,
Adapter->InterruptMask
);
if (Adapter->PciDriverArea & CFDA_SNOOZE_MODE) {
//set to the initial snooze mode
NdisWritePciSlotInformation(
Adapter->MiniportAdapterHandle,
Adapter->SlotNumber,
PCI_CFDA_OFFSET,
&Adapter->PciDriverArea,
sizeof(Adapter->PciDriverArea)
);
}
#if __DBG
if (Link)
DbgPrint("Sense: SelectMedium = %s\n",
Adapter->SelectedMedium==Medium100BaseTx?"100BaseTx":"10BaseT");
#endif
return Link;
}
/*+
* DC21X4DynamicAutoSense
*
* Routine Description:
*
* Autosense between the PHY's Autosense routine and the
* other media autosense's routine
*
* Arguments:
*
* Adapter
*
-*/
extern
VOID
DC21X4DynamicAutoSense (
IN PVOID Systemspecific1,
IN PDC21X4_ADAPTER Adapter,
IN PVOID Systemspecific2,
IN PVOID Systemspecific3
)
{
BOOLEAN LinkStatus=FALSE;
BOOLEAN StartTimer=TRUE;
BOOLEAN LoopbackMode;
#if _DBG
DbgPrint("DC21X4DynamicAutoSense\n");
#endif
if ( (Adapter->PhyPresent)
&& (!Adapter->Force10)
){
LinkStatus = DC21X4MiiAutoSense(Adapter);
}
if (Adapter->Indicate10BTLink) {
Adapter->Indicate10BTLink = FALSE;
if (!LinkStatus) {
// The current link is a 10BaseT link
#if 0
DC21X4SetPhyControl(
Adapter,
(USHORT)MiiGenAdminIsolate
);
#endif
DC21X4SetPhyControl(
Adapter,
(USHORT)((Adapter->OperationMode & DC21X4_FULL_DUPLEX_MODE) ?
MiiGenAdminForce10Fd : MiiGenAdminForce10)
);
DC21X4IndicateMediaStatus(Adapter,LinkPass);
}
}
if ( !LinkStatus
&& (Adapter->MediaCapable)
) {
StartTimer = DC21X4AutoSense(Adapter);
}
// Restart the Autosense timer
if (StartTimer) {
DC21X4StartAutoSenseTimer(
Adapter,
(UINT)((Adapter->PhyPresent) ? DC21X4_MII_TICK : DC21X4_SPA_TICK)
);
}
}
/*+
* DC21X4AutoSense
*
* Routine Description:
*
* Autosense the DC21041 10Base2/10Base5 port
* Autosense the DC21140 100BaseTx/10BaseT link
*
* Arguments:
*
* Adapter
*
* Return:
*
* TRUE if AutoSense Timer should be fired
*
-*/
extern
BOOLEAN
DC21X4AutoSense (
IN PDC21X4_ADAPTER Adapter
)
{
ULONG Status;
BOOLEAN SelectedPortActive;
BOOLEAN SwitchMedium;
BOOLEAN FullDuplex;
INT CurrentMedium=0;
INT NextMedium;
INT index;
#if _DBG
DbgPrint("Autosense routine\n");
#endif
switch (Adapter->DeviceId) {
case DC21040_CFID:
if (Adapter->LinkStatus != LinkFail) {
return FALSE;
}
//DC21040 supports Link_Fail interrupt
//but does not support Link_Pass
//Check the Link status:
//If link is up wait for Link_Fail interrupt
//otherwhise poll the link status
DC21X4_READ_PORT(
DC21X4_SIA_STATUS,
&Status
);
if ((Status & DC21X4_LINKFAIL_10) == 0) {
DC21X4IndicateMediaStatus(Adapter,LinkPass);
return FALSE;
}
break;
case DC21041_CFID:
case DC21142_CFID:
if (!(Adapter->MediaType & MEDIA_AUTOSENSE)) {
return Adapter->PhyPresent;
}
if (Adapter->IgnoreTimer) {
Adapter->IgnoreTimer = FALSE;
return FALSE;
}
switch (Adapter->TimerFlag) {
case AncPolling:
if (Adapter->PollCount--) {
//Read the SIA Status
DC21X4_READ_PORT(
DC21X4_SIA_STATUS,
&Status
);
//check the AutoNegotation State
switch (Status & DC21X4_AUTO_NEGOTIATION_STATE) {
case ANS_ACKNOWLEDGE_DETECTED:
case ANS_ACKNOWLEDGE_COMPLETED:
//store the Sia status snapshot
Adapter->SiaStatus = Status;
default:
// Restart the Poll timer
NdisMSetTimer(
&Adapter->Timer,
DC21X4_POLL_DELAY
);
return FALSE;
case ANS_AUTO_NEGOTIATION_COMPLETED:
//Check the Link Partner capabilities
// LPN SF 10BT_FD 10BT Link_Partner Medium
// 0 xx x x not_negotiable 10BT
// 1 01 1 x 10BT_FD capable 10BT_FD
// 1 01 0 1 10BT_HD capable 10BT
// 1 01 0 0 no_common_mode 10B2/5
// 1 !01 x x no_common_mode 10B2/5
if (!(Status & DC21X4_LINK_PARTNER_NEGOTIABLE)) {
#if __DBG
DbgPrint("Link Partner not negotiable\n");
#endif
if (++Adapter->AutoNegotiationCount < 2) {
//Fire the Restart AutoNegotation Timer
// to defer the restart of the auto_negotiation
Adapter->TimerFlag=DeferredAnc;
NdisMSetTimer(
&Adapter->Timer,
DC21X4_ANC_DELAY
);
return FALSE;
}
else {
NextMedium = Medium10BaseT;
FullDuplex=FALSE;
}
}
else {
// Link Partner negotiable
if (!Adapter->SiaStatus) {
//Restart the AutoNegotiation
//Reinitialize the Poll timeout counter
Adapter->PollCount= POLL_COUNT_TIMEOUT;
DC21X4_WRITE_PORT(
DC21X4_SIA_STATUS,
DC21X4_RESTART_AUTO_NEGOTIATION
);
//Restart the Poll timer to
//poll on AutoNegotiation State
Adapter->TimerFlag=AncPolling;
NdisMSetTimer(
&Adapter->Timer,
DC21X4_POLL_DELAY
);
return FALSE;
}
else if ((Adapter->SiaStatus & DC21X4_SELECTED_FIELD_MASK) != DC21X4_SELECTED_FIELD) {
NextMedium=Medium10Base2_5;
FullDuplex=FALSE;
}
else if (Adapter->SiaStatus & DC21X4_LINK_PARTNER_10BT_FD) {
//10BT Full Duplex capable
NextMedium=Medium10BaseT;
FullDuplex=TRUE;
}
else if (Adapter->SiaStatus & DC21X4_LINK_PARTNER_10BT) {
//10BT Half Duplex capable
NextMedium=Medium10BaseT;
FullDuplex=FALSE;
}
else {
//no common mode
NextMedium=Medium10Base2_5;
FullDuplex=FALSE;
}
}
}
}
else {
//poll timeout
NextMedium = Medium10Base2_5;
FullDuplex=FALSE;
}
Adapter->TimerFlag = NoTimer;
//Reinitialize the Sia status snapshot
Adapter->SiaStatus = 0;
if (!FullDuplex) {
// Stop the Receiver and Transmitter to
// reset the Full_duplex mode
DC21X4StopReceiverAndTransmitter(
Adapter
);
Adapter->OperationMode &= ~(DC21X4_FULL_DUPLEX_MODE);
}
// Switch to the selected medium
Adapter->NwayEnabled=FALSE;
if (NextMedium == Medium10Base2_5) {
DC21X4SwitchMedia(
Adapter,
Medium10Base2_5
);
}
else {
//10BaseT: Disable NWAY
DC21X4_WRITE_PORT(
DC21X4_SIA_MODE_1,
Adapter->Media[Medium10BaseT].SiaRegister[1]
);
DC21X4IndicateMediaStatus(Adapter,LinkPass);
}
if (!FullDuplex) {
//Restart the Receiver and Transmitter
DC21X4_WRITE_PORT(
DC21X4_OPERATION_MODE,
Adapter->OperationMode
);
}
return FALSE;
case DeferredAnc:
//Restart the AutoNegotiation
//Reinitialize the Poll timeout counter
Adapter->PollCount= POLL_COUNT_TIMEOUT;
Adapter->SiaStatus = 0;
DC21X4_WRITE_PORT(
DC21X4_SIA_STATUS,
DC21X4_RESTART_AUTO_NEGOTIATION
);
//Restart the Poll timer to
//poll on AutoNegotiation State
Adapter->TimerFlag=AncPolling;
NdisMSetTimer(
&Adapter->Timer,
DC21X4_POLL_DELAY
);
return FALSE;
case AncTimeout:
// AutoNegotiation timeout:
Adapter->TimerFlag = NoTimer;
if (Adapter->MediaType & MEDIA_AUTOSENSE) {
// Switch medium from 10BaseT
DC21X4SwitchMedia(
Adapter,
Medium10Base2_5
);
}
return FALSE;
case DeferredLinkCheck:
Adapter->TimerFlag = NoTimer;
//Check the 10BaseT Link Status
DC21X4_READ_PORT(
DC21X4_SIA_STATUS,
&Status
);
if ((Status & DC21X4_LINK_PASS_MASK) == DC21X4_LINK_PASS_STATUS) {
//Link Pass:
if (Adapter->SelectedMedium == Medium10BaseT) {
//10BaseT link is up
DC21X4IndicateMediaStatus(Adapter,LinkPass);
return Adapter->PhyPresent;
}
else {
//Switch the medium to 10BaseT and start the
//Anc Timer to timeout if the Nway Autonegotiation
//does not complete
Adapter->TimerFlag = AncTimeout;
NdisMSetTimer(
&Adapter->Timer,
DC21X4_ANC_TIMEOUT
);
DC21X4SwitchMedia(
Adapter,
Medium10BaseT
);
return FALSE;
}
}
else {
// No 10baseT link:
// If the current Medium is not 10BaseT,
//ignore this state and enters the Selected_Port_Active check
//instead
if (Adapter->SelectedMedium == Medium10BaseT) {
if (Adapter->MediaType & MEDIA_AUTOSENSE) {
// Switch medium from 10BaseT
DC21X4SwitchMedia(
Adapter,
Medium10Base2_5
);
}
return FALSE;
}
}
case SpaTimer:
//Selected_Port_Active (10Base2/10Base5 media) periodic check
if (Adapter->SelectedMedium == Medium10BaseT) {
break;
}
if ((Adapter->MediaCapable & (MEDIUM_10B2 | MEDIUM_10B5)) !=
(MEDIUM_10B2 | MEDIUM_10B5)) {
//The board does not support both 10Base2 & 10Base5 ports
break;
}
// Read the Selected_Port_Active Status
DC21X4_READ_PORT(
DC21X4_SIA_STATUS,
&Status
);
SelectedPortActive = ((Status & DC21X4_SELECTED_PORT_ACTIVE) != 0);
#if __DBG
DbgPrint("Autosense: SelectePortActive=%d Nocarrier=%d ExcessColl=%d\n",
SelectedPortActive,
Adapter->NoCarrierCount,
Adapter->ExcessCollisionsCount);
#endif
if ( (!SelectedPortActive)
|| (Adapter->NoCarrierCount >= NO_CARRIER_THRESHOLD)
|| (Adapter->ExcessCollisionsCount >= EXCESS_COLLISIONS_THRESHOLD)
) {
//Switch medium port
Adapter->SelectedMedium = (Adapter->SelectedMedium == Medium10Base2) ?
Medium10Base5 : Medium10Base2;
#if __DBG
DbgPrint("Autosense: Switch Media to %s\n",
MediumString[Adapter->SelectedMedium]);
#endif
DC21X4_WRITE_PORT(
DC21X4_SIA_MODE_2,
Adapter->Media[Adapter->SelectedMedium].SiaRegister[2]
);
//reset the NoCarrier and ExcessCollisions counters
Adapter->NoCarrierCount = 0;
Adapter->ExcessCollisionsCount = 0;
}
// clear the SPA flag into the Sia Status register:
DC21X4_WRITE_PORT(
DC21X4_SIA_STATUS,
DC21X4_SELECTED_PORT_ACTIVE
);
break;
case NoTimer:
return FALSE;
}
break;
case DC21140_CFID:
if (!Adapter->Media[Adapter->SelectedMedium].SenseMask) {
DC21X4IndicateMediaStatus(Adapter,LinkPass);
return Adapter->PhyPresent;
}
if ( (!(Adapter->MediaType & MEDIA_AUTOSENSE))
&& (Adapter->PhyPresent)
) {
return TRUE;
}
//Check the Link status
DC21X4_READ_PORT(
DC21X4_GEN_PURPOSE,
&Status
);
DC21X4IndicateMediaStatus(
Adapter,
DC21X4_LINK_STATUS(Status,Adapter,Adapter->SelectedMedium) ?
LinkPass : LinkFail
);
if (Adapter->MediaType & MEDIA_AUTOSENSE) {
//Check the link status of every medium supported
//by the adapter
for (index=Adapter->MediaCount; index>0; index--) {
CurrentMedium = Adapter->MediaPrecedence[index-1];
if (DC21X4_LINK_STATUS(Status,Adapter,CurrentMedium)) {
break;
}
}
if (index > 0) {
// A link was detected,switch to this medium if:
// current > selected (a medium link of higher precedence is up
// current < selected (selected medium link is down)
SwitchMedium = (CurrentMedium != Adapter->SelectedMedium);
}
else {
// no link detected:
// switch to the default medium if defined and different
// of the selected medium
// otherwise stay with the selected medium
CurrentMedium = Adapter->DefaultMedium;
SwitchMedium = Adapter->DefaultMediumFlag
&& (Adapter->SelectedMedium != Adapter->DefaultMedium);
}
if (SwitchMedium) {
#if __DBG
DbgPrint("Autosense: 21140 - Switch Medium to %s\n",
MediumString[CurrentMedium]);
#endif
DC21X4SwitchMedia(
Adapter,
CurrentMedium
);
DC21X4_READ_PORT(
DC21X4_GEN_PURPOSE,
&Status
);
#if __DBG
DbgPrint("Autosense 21140: Link=%s\n",
Adapter->LinkStatus ? "UP":"DOWN");
#endif
}
}
break;
}
return TRUE;
}
/*++
*
* DC21X4SwitchMedia
*
* Routine Description:
*
* This routine switches DC21X4's media ports
*
* Arguments:
*
* Adapter
* NewMedium : the new medium to switch to
*
* Return Value:
*
* None
*
-*/
extern
VOID
DC21X4SwitchMedia(
IN PDC21X4_ADAPTER Adapter,
IN LONG NewMedium
)
{
ULONG Status;
BOOLEAN SpaTimer=FALSE;
ULONG FullDuplex=0;
UINT j;
#if __DBG
DbgPrint("DC21X4SwitchMedia [->%x]\n",NewMedium);
#endif
DC21X4IndicateMediaStatus(Adapter,LinkFail);
switch (Adapter->DeviceId) {
case DC21142_CFID:
case DC21041_CFID:
switch (NewMedium) {
case Medium10BaseT:
#if __DBG
DbgPrint("Medium = %s %s \n",
MediumString[NewMedium],
FullDuplex ? "Full_Duplex" : "");
#endif
Adapter->SelectedMedium = NewMedium;
DC2104InitializeSiaRegisters(Adapter);
return;
case Medium10BaseTNway:
#if __DBG
DbgPrint("Medium = 10BaseT - Nway enabled\n");
#endif
NewMedium &= MEDIA_MASK;
Adapter->SelectedMedium = NewMedium;
Adapter->NwayEnabled=TRUE;
//Stop the Receiver and the Transmitter
DC21X4StopReceiverAndTransmitter(Adapter);
//enable Nway
Adapter->Media[Medium10BaseT].SiaRegister[1] |= DC21X4_NWAY_ENABLED;
DC2104InitializeSiaRegisters(Adapter);
Adapter->Media[Medium10BaseT].SiaRegister[1] &= ~(DC21X4_NWAY_ENABLED);
//Restart the Receiver and Transmitter in Full Duplex mode
Adapter->OperationMode |= DC21X4_FULL_DUPLEX_MODE;
DC21X4_WRITE_PORT(
DC21X4_OPERATION_MODE,
Adapter->OperationMode
);
return;
case Medium10Base2:
//switch medium to 10Base2
SpaTimer = (Adapter->MediaCapable & MEDIUM_10B5);
break;
case Medium10Base5:
//switch medium to 10Base5
SpaTimer = (Adapter->MediaCapable & MEDIUM_10B2);
break;
case Medium10Base2_5:
switch (Adapter->MediaCapable & (MEDIUM_10B2 | MEDIUM_10B5)) {
case (MEDIUM_10B2 | MEDIUM_10B5) :
// 10Base2 & 10Base5 ports are both populated:
// if Non_Selected_Port_Active select 10Base5
// otherwise select 10Base2
DC21X4_READ_PORT(
DC21X4_SIA_STATUS,
&Status
);
NewMedium = (Status & DC21X4_NON_SELECTED_PORT_ACTIVE) ?
Medium10Base5 : Medium10Base2;
SpaTimer = TRUE;
break;
case MEDIUM_10B2 :
// 10Base2 port only is populated:
NewMedium = Medium10Base2;
break;
case MEDIUM_10B5 :
// 10Base5 port only is populated
NewMedium = Medium10Base5;
break;
default:
if (Adapter->PhyPresent) {
//Start the AutoSense Timer to poll the PHY Link
DC21X4StartAutoSenseTimer(
Adapter,
(UINT)DC21X4_MII_TICK
);
}
return;
}
}
#if __DBG
DbgPrint("Medium = %s %s\n",
MediumString[NewMedium],
Adapter->Media[NewMedium].SiaRegister[1] & DC21X4_AUTO_NEGOTIATION_ENABLE ?
"- NWAY Enabled" : "");
#endif
Adapter->SelectedMedium=NewMedium;
DC2104InitializeSiaRegisters(Adapter);
DC21X4IndicateMediaStatus(Adapter,LinkPass);
if (SpaTimer || Adapter->PhyPresent) {
#if __DBG
DbgPrint("Start the AutoSense timer\n");
#endif
//Reset the NoCarrier and ExcessCollisions counters
Adapter->NoCarrierCount = 0;
Adapter->ExcessCollisionsCount = 0;
//Start the AutoSense Timer
DC21X4StartAutoSenseTimer(
Adapter,
(UINT)((Adapter->PhyPresent) ? DC21X4_MII_TICK : DC21X4_SPA_TICK)
);
}
break;
case DC21140_CFID:
// Switch medium:
// Reload GEP and OperationMode registers
Adapter->OperationMode &= ~(DC21X4_MEDIUM_MASK);
Adapter->OperationMode |= Adapter->Media[NewMedium].Mode;
Adapter->SelectedMedium = NewMedium;
DC21X4_WRITE_PORT(
DC21X4_GEN_PURPOSE,
Adapter->Media[NewMedium].GeneralPurposeData
);
DC21X4_WRITE_PORT(
DC21X4_OPERATION_MODE,
Adapter->OperationMode
);
for (j=0;j<(GEP_READ_DELAY/5);j++) {
NdisStallExecution(5*MILLISECOND);
}
DC21X4_READ_PORT(
DC21X4_GEN_PURPOSE,
&Status
);
DC21X4IndicateMediaStatus(
Adapter,
DC21X4_LINK_STATUS(Status,Adapter,Adapter->SelectedMedium) ?
LinkPass : LinkFail
);
return;
}
}
/*++
*
* DC21X4StartAutoSenseTimer
*
* Routine Description:
*
* Start the AutoSense Timer
*
* Arguments:
*
* Adapter
*
* Return Value:
*
* None
*
-*/
extern
VOID
DC21X4StartAutoSenseTimer(
IN PDC21X4_ADAPTER Adapter,
IN UINT Value
)
{
Adapter->TimerFlag=SpaTimer;
NdisMSetTimer(
&Adapter->Timer,
Value
);
}
/*++
*
* DC21X4StopAutoSenseTimer
*
* Routine Description:
*
* Stop the AutoSense Timer
*
* Arguments:
*
* Adapter
*
* Return Value:
*
* None
*
-*/
extern
VOID
DC21X4StopAutoSenseTimer(
IN PDC21X4_ADAPTER Adapter
)
{
BOOLEAN Canceled;
Adapter->TimerFlag = NoTimer;
NdisMCancelTimer(
&Adapter->Timer,
&Canceled
);
Adapter->IgnoreTimer = !Canceled;
}
/*+
* DC21X4EnableNway
*
* Routine Description:
*
* Enable the Nway Negotiation
*
* Arguments:
*
* Adapter - The adapter in question.
*
* Return Value:
*
* None
*
-*/
extern
VOID
DC21X4EnableNway(
IN PDC21X4_ADAPTER Adapter
)
{
ULONG Mask;
#if __DBG
DbgPrint("Enable Nway Negotiation\n");
#endif
switch (Adapter->DeviceId) {
case DC21041_CFID:
switch (Adapter->RevisionNumber) {
case DC21041_REV2_0:
if (Adapter->NwayProtocol) {
Adapter->MediaNway = TRUE;
Adapter->LinkHandlerMode=NwayWorkAround;
break;
}
case DC21041_REV1_1:
case DC21041_REV1_0:
Adapter->MediaNway = FALSE;
Adapter->LinkHandlerMode=NoNway;
break;
default:
Adapter->MediaNway = TRUE;
Adapter->LinkHandlerMode=Nway;
Adapter->Media[Medium10BaseT].SiaRegister[1] |= DC21X4_NWAY_ENABLED;
Adapter->Media[Medium10Base2].SiaRegister[1] |= DC21X4_NWAY_ENABLED;
Adapter->Media[Medium10Base5].SiaRegister[1] |= DC21X4_NWAY_ENABLED;
Adapter->Media[Medium10BaseT].Mode |= DC21X4_FULL_DUPLEX_MODE;
Adapter->Media[Medium10Base2].Mode |= DC21X4_FULL_DUPLEX_MODE;
Adapter->Media[Medium10Base5].Mode |= DC21X4_FULL_DUPLEX_MODE;
}
break;
case DC21142_CFID:
switch (Adapter->RevisionNumber) {
case DC21142_REV1_0:
case DC21142_REV1_1:
if (Adapter->NwayProtocol) {
Adapter->MediaNway = TRUE;
Adapter->LinkHandlerMode=NwayWorkAround;
}
else {
Adapter->MediaNway = FALSE;
Adapter->LinkHandlerMode=NoNway;
}
break;
default:
Adapter->MediaNway = TRUE;
Adapter->LinkHandlerMode=Nway;
Adapter->Media[Medium10BaseT].SiaRegister[1] |= DC21X4_NWAY_ENABLED;
Adapter->Media[Medium10Base2].SiaRegister[1] |= DC21X4_NWAY_ENABLED;
Adapter->Media[Medium10Base5].SiaRegister[1] |= DC21X4_NWAY_ENABLED;
Adapter->Media[Medium10BaseT].Mode |= DC21X4_FULL_DUPLEX_MODE;
Adapter->Media[Medium10Base2].Mode |= DC21X4_FULL_DUPLEX_MODE;
Adapter->Media[Medium10Base5].Mode |= DC21X4_FULL_DUPLEX_MODE;
}
break;
}
}
/*+
* DC21X4DisableNway
*
* Routine Description:
*
* Disable the Nway Negotiation
*
* Arguments:
*
* Adapter - The adapter in question.
*
* Return Value:
*
* None
*
-*/
extern
VOID
DC21X4DisableNway(
IN PDC21X4_ADAPTER Adapter
)
{
#if __DBG
DbgPrint("Disable Nway Negotiation\n");
#endif
Adapter->MediaNway = FALSE;
Adapter->LinkHandlerMode=NoNway;
switch (Adapter->DeviceId) {
case DC21041_CFID:
case DC21142_CFID:
Adapter->Media[Medium10BaseT].SiaRegister[1] &= ~DC21X4_NWAY_ENABLED;
Adapter->Media[Medium10Base2].SiaRegister[1] &= ~DC21X4_NWAY_ENABLED;
Adapter->Media[Medium10Base5].SiaRegister[1] &= ~DC21X4_NWAY_ENABLED;
Adapter->Media[Medium10BaseT].Mode &= ~DC21X4_FULL_DUPLEX_MODE;
Adapter->Media[Medium10Base2].Mode &= ~DC21X4_FULL_DUPLEX_MODE;
Adapter->Media[Medium10Base5].Mode &= ~DC21X4_FULL_DUPLEX_MODE;
break;
}
}
/*++
*
* DC21X4IndicateMediaStatus
*
* Routine Description:
*
* Indicate the media status
*
* Arguments:
*
* Adapter
*
* Return Value:
*
* None
*
-*/
extern
VOID
DC21X4IndicateMediaStatus(
IN PDC21X4_ADAPTER Adapter,
IN UINT Status
)
{
ULONG LinkPartner;
Adapter->LinkStatus=Status;
if (Status != Adapter->PreviousLinkStatus) {
switch (Adapter->LinkStatus) {
#if __DBG
case LinkFail:
DbgPrint("IndicateMediaStatus: Link FAIL\n");
break;
#endif
case LinkPass:
switch (Adapter->SelectedMedium) {
case Medium100BaseTx:
case Medium100BaseT4:
case Medium100BaseFx:
Adapter->LinkSpeed = ONE_HUNDRED_MBPS;
break;
default:
Adapter->LinkSpeed = TEN_MBPS;
break;
}
if (Adapter->MediaNway) {
//Nway: read the Link Partner Ability to check
// Half/Full Duplex link mode
DC21X4_READ_PORT(
DC21X4_SIA_STATUS,
&LinkPartner
);
Adapter->FullDuplexLink =
LinkPartner & (DC21X4_LINK_PARTNER_10BT_FD) ?
TRUE : FALSE ;
}
else {
Adapter->FullDuplexLink =
(Adapter->MediaType & MEDIA_FULL_DUPLEX) != 0;
}
#if __DBG
DbgPrint("IndicateMediaStatus: %s%s Link PASS\n",
MediumString[Adapter->SelectedMedium],
Adapter->FullDuplexLink ? " Full_Duplex" : "");
#endif
break;
case MiiLinkPass:
Adapter->LinkSpeed = TEN_MBPS;
Adapter->FullDuplexLink = FALSE;
switch (Adapter->MiiMediaType & MEDIA_MASK) {
case MediumMii10BaseTFd:
Adapter->FullDuplexLink = TRUE;
break;
case MediumMii100BaseTxFd:
case MediumMii100BaseFxFd:
Adapter->FullDuplexLink = TRUE;
case MediumMii100BaseTx:
case MediumMii100BaseT4:
case MediumMii100BaseFx:
Adapter->LinkSpeed = ONE_HUNDRED_MBPS;
break;
}
#if __DBG
DbgPrint("IndicateMediaStatus: %s%s MiiLink PASS\n",
MediumString[Adapter->MiiMediaType & MEDIA_MASK],
Adapter->FullDuplexLink ? " Full_Duplex" : "");
#endif
break;
}
if (Adapter->FullDuplexLink) {
Adapter->TransmitDescriptorErrorMask &=
~(DC21X4_TDES_NO_CARRIER | DC21X4_TDES_LOSS_OF_CARRIER);
}
else {
Adapter->TransmitDescriptorErrorMask =
Adapter->TransmitDefaultDescriptorErrorMask;
}
if (!Adapter->Initializing) {
NdisMIndicateStatus (
Adapter->MiniportAdapterHandle,
(Status == LinkFail ? NDIS_STATUS_MEDIA_DISCONNECT : NDIS_STATUS_MEDIA_CONNECT),
NULL,
0
);
}
Adapter->PreviousLinkStatus = Status;
}
}