You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
419 lines
16 KiB
419 lines
16 KiB
/****************************************************************************
|
|
** COPYRIGHT (C) 1994-1997 INTEL CORPORATION **
|
|
** DEVELOPED FOR MICROSOFT BY INTEL CORP., HILLSBORO, OREGON **
|
|
** HTTP://WWW.INTEL.COM/ **
|
|
** THIS FILE IS PART OF THE INTEL ETHEREXPRESS PRO/100B(TM) AND **
|
|
** ETHEREXPRESS PRO/100+(TM) NDIS 5.0 MINIPORT SAMPLE DRIVER **
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
Module Name:
|
|
routines.c
|
|
|
|
This driver runs on the following hardware:
|
|
- 82558 based PCI 10/100Mb ethernet adapters
|
|
(aka Intel EtherExpress(TM) PRO Adapters)
|
|
|
|
Environment:
|
|
Kernel Mode - Or whatever is the equivalent on WinNT
|
|
|
|
Revision History
|
|
- JCB 8/14/97 Example Driver Created
|
|
- Dchen 11-01-99 Modified for the new sample driver
|
|
*****************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
#pragma warning (disable: 4514 4706)
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Procedure: MdiWrite
|
|
//
|
|
// Description: This routine will write a value to the specified MII register
|
|
// of an external MDI compliant device (e.g. PHY 100). The
|
|
// command will execute in polled mode.
|
|
//
|
|
// Arguments:
|
|
// Adapter - ptr to Adapter object instance
|
|
// RegAddress - The MII register that we are writing to
|
|
// PhyAddress - The MDI address of the Phy component.
|
|
// DataValue - The value that we are writing to the MII register.
|
|
//
|
|
// Returns:
|
|
// NOTHING
|
|
//-----------------------------------------------------------------------------
|
|
VOID MdiWrite(
|
|
IN PMP_ADAPTER Adapter,
|
|
IN ULONG RegAddress,
|
|
IN ULONG PhyAddress,
|
|
IN USHORT DataValue)
|
|
{
|
|
BOOLEAN bResult;
|
|
|
|
// Issue the write command to the MDI control register.
|
|
Adapter->CSRAddress->MDIControl = (((ULONG) DataValue) |
|
|
(RegAddress << 16) |
|
|
(PhyAddress << 21) |
|
|
(MDI_WRITE << 26));
|
|
|
|
// wait 20usec before checking status
|
|
NdisStallExecution(20);
|
|
|
|
// wait 2 seconds for the mdi write to complete
|
|
MP_STALL_AND_WAIT(Adapter->CSRAddress->MDIControl & MDI_PHY_READY, 2000, bResult);
|
|
|
|
if (!bResult)
|
|
{
|
|
MP_SET_HARDWARE_ERROR(Adapter);
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Procedure: MdiRead
|
|
//
|
|
// Description: This routine will read a value from the specified MII register
|
|
// of an external MDI compliant device (e.g. PHY 100), and return
|
|
// it to the calling routine. The command will execute in polled
|
|
// mode.
|
|
//
|
|
// Arguments:
|
|
// Adapter - ptr to Adapter object instance
|
|
// RegAddress - The MII register that we are reading from
|
|
// PhyAddress - The MDI address of the Phy component.
|
|
// Recoverable - Whether the hardware error(if any)if recoverable or not
|
|
//
|
|
// Results:
|
|
// DataValue - The value that we read from the MII register.
|
|
//
|
|
// Returns:
|
|
// None
|
|
//-----------------------------------------------------------------------------
|
|
BOOLEAN MdiRead(
|
|
IN PMP_ADAPTER Adapter,
|
|
IN ULONG RegAddress,
|
|
IN ULONG PhyAddress,
|
|
IN BOOLEAN Recoverable,
|
|
IN OUT PUSHORT DataValue)
|
|
{
|
|
BOOLEAN bResult;
|
|
|
|
// Issue the read command to the MDI control register.
|
|
Adapter->CSRAddress->MDIControl = ((RegAddress << 16) |
|
|
(PhyAddress << 21) |
|
|
(MDI_READ << 26));
|
|
|
|
// wait 20usec before checking status
|
|
NdisStallExecution(20);
|
|
|
|
// Wait up to 2 seconds for the mdi read to complete
|
|
MP_STALL_AND_WAIT(Adapter->CSRAddress->MDIControl & MDI_PHY_READY, 2000, bResult);
|
|
if (!bResult)
|
|
{
|
|
if (!Recoverable)
|
|
{
|
|
MP_SET_NON_RECOVER_ERROR(Adapter);
|
|
}
|
|
MP_SET_HARDWARE_ERROR(Adapter);
|
|
return bResult;
|
|
}
|
|
|
|
*DataValue = (USHORT) Adapter->CSRAddress->MDIControl;
|
|
return bResult;
|
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Procedure: DumpStatsCounters
|
|
//
|
|
// Description: This routine will dump and reset the 82557's internal
|
|
// Statistics counters. The current stats dump values will be
|
|
// added to the "Adapter's" overall statistics.
|
|
// Arguments:
|
|
// Adapter - ptr to Adapter object instance
|
|
//
|
|
// Returns:
|
|
// NOTHING
|
|
//-----------------------------------------------------------------------------
|
|
VOID DumpStatsCounters(
|
|
IN PMP_ADAPTER Adapter)
|
|
{
|
|
BOOLEAN bResult;
|
|
|
|
// The query is for a driver statistic, so we need to first
|
|
// update our statistics in software.
|
|
|
|
// clear the dump counters complete DWORD
|
|
Adapter->StatsCounters->CommandComplete = 0;
|
|
|
|
NdisAcquireSpinLock(&Adapter->Lock);
|
|
|
|
// Dump and reset the hardware's statistic counters
|
|
D100IssueScbCommand(Adapter, SCB_CUC_DUMP_RST_STAT, TRUE);
|
|
|
|
// Restore the resume transmit software flag. After the dump counters
|
|
// command is issued, we should do a WaitSCB before issuing the next send.
|
|
Adapter->ResumeWait = TRUE;
|
|
|
|
NdisReleaseSpinLock(&Adapter->Lock);
|
|
|
|
// wait up to 2 seconds for the dump/reset to complete
|
|
MP_STALL_AND_WAIT(Adapter->StatsCounters->CommandComplete == 0xA007, 2000, bResult);
|
|
if (!bResult)
|
|
{
|
|
MP_SET_HARDWARE_ERROR(Adapter);
|
|
return;
|
|
}
|
|
|
|
// Output the debug counters to the debug terminal.
|
|
DBGPRINT(MP_INFO, ("Good Transmits %d\n", Adapter->StatsCounters->XmtGoodFrames));
|
|
DBGPRINT(MP_INFO, ("Good Receives %d\n", Adapter->StatsCounters->RcvGoodFrames));
|
|
DBGPRINT(MP_INFO, ("Max Collisions %d\n", Adapter->StatsCounters->XmtMaxCollisions));
|
|
DBGPRINT(MP_INFO, ("Late Collisions %d\n", Adapter->StatsCounters->XmtLateCollisions));
|
|
DBGPRINT(MP_INFO, ("Transmit Underruns %d\n", Adapter->StatsCounters->XmtUnderruns));
|
|
DBGPRINT(MP_INFO, ("Transmit Lost CRS %d\n", Adapter->StatsCounters->XmtLostCRS));
|
|
DBGPRINT(MP_INFO, ("Transmits Deferred %d\n", Adapter->StatsCounters->XmtDeferred));
|
|
DBGPRINT(MP_INFO, ("One Collision xmits %d\n", Adapter->StatsCounters->XmtSingleCollision));
|
|
DBGPRINT(MP_INFO, ("Mult Collision xmits %d\n", Adapter->StatsCounters->XmtMultCollisions));
|
|
DBGPRINT(MP_INFO, ("Total Collisions %d\n", Adapter->StatsCounters->XmtTotalCollisions));
|
|
|
|
DBGPRINT(MP_INFO, ("Receive CRC errors %d\n", Adapter->StatsCounters->RcvCrcErrors));
|
|
DBGPRINT(MP_INFO, ("Receive Alignment errors %d\n", Adapter->StatsCounters->RcvAlignmentErrors));
|
|
DBGPRINT(MP_INFO, ("Receive no resources %d\n", Adapter->StatsCounters->RcvResourceErrors));
|
|
DBGPRINT(MP_INFO, ("Receive overrun errors %d\n", Adapter->StatsCounters->RcvOverrunErrors));
|
|
DBGPRINT(MP_INFO, ("Receive CDT errors %d\n", Adapter->StatsCounters->RcvCdtErrors));
|
|
DBGPRINT(MP_INFO, ("Receive short frames %d\n", Adapter->StatsCounters->RcvShortFrames));
|
|
|
|
// update packet counts
|
|
Adapter->GoodTransmits += Adapter->StatsCounters->XmtGoodFrames;
|
|
Adapter->GoodReceives += Adapter->StatsCounters->RcvGoodFrames;
|
|
|
|
// update transmit error counts
|
|
Adapter->TxAbortExcessCollisions += Adapter->StatsCounters->XmtMaxCollisions;
|
|
Adapter->TxLateCollisions += Adapter->StatsCounters->XmtLateCollisions;
|
|
Adapter->TxDmaUnderrun += Adapter->StatsCounters->XmtUnderruns;
|
|
Adapter->TxLostCRS += Adapter->StatsCounters->XmtLostCRS;
|
|
Adapter->TxOKButDeferred += Adapter->StatsCounters->XmtDeferred;
|
|
Adapter->OneRetry += Adapter->StatsCounters->XmtSingleCollision;
|
|
Adapter->MoreThanOneRetry += Adapter->StatsCounters->XmtMultCollisions;
|
|
Adapter->TotalRetries += Adapter->StatsCounters->XmtTotalCollisions;
|
|
|
|
// update receive error counts
|
|
Adapter->RcvCrcErrors += Adapter->StatsCounters->RcvCrcErrors;
|
|
Adapter->RcvAlignmentErrors += Adapter->StatsCounters->RcvAlignmentErrors;
|
|
Adapter->RcvResourceErrors += Adapter->StatsCounters->RcvResourceErrors;
|
|
Adapter->RcvDmaOverrunErrors += Adapter->StatsCounters->RcvOverrunErrors;
|
|
Adapter->RcvCdtFrames += Adapter->StatsCounters->RcvCdtErrors;
|
|
Adapter->RcvRuntErrors += Adapter->StatsCounters->RcvShortFrames;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Procedure: NICIssueSelectiveReset
|
|
//
|
|
// Description: This routine will issue a selective reset, forcing the adapter
|
|
// the CU and RU back into their idle states. The receive unit
|
|
// will then be re-enabled if it was previously enabled, because
|
|
// an RNR interrupt will be generated when we abort the RU.
|
|
//
|
|
// Arguments:
|
|
// Adapter - ptr to Adapter object instance
|
|
//
|
|
// Returns:
|
|
// NOTHING
|
|
//-----------------------------------------------------------------------------
|
|
|
|
VOID NICIssueSelectiveReset(
|
|
PMP_ADAPTER Adapter)
|
|
{
|
|
NDIS_STATUS Status;
|
|
BOOLEAN bResult;
|
|
|
|
// Wait for the SCB to clear before we check the CU status.
|
|
if (!MP_TEST_FLAG(Adapter, fMP_ADAPTER_HARDWARE_ERROR))
|
|
{
|
|
WaitScb(Adapter);
|
|
}
|
|
|
|
// If we have issued any transmits, then the CU will either be active, or
|
|
// in the suspended state. If the CU is active, then we wait for it to be
|
|
// suspended. If the the CU is suspended, then we need to put the CU back
|
|
// into the idle state by issuing a selective reset.
|
|
if (Adapter->TransmitIdle == FALSE)
|
|
{
|
|
// Wait up to 2 seconds for suspended state
|
|
MP_STALL_AND_WAIT((Adapter->CSRAddress->ScbStatus & SCB_CUS_MASK) != SCB_CUS_ACTIVE, 2000, bResult)
|
|
if (!bResult)
|
|
{
|
|
MP_SET_HARDWARE_ERROR(Adapter);
|
|
}
|
|
|
|
// Check the current status of the receive unit
|
|
if ((Adapter->CSRAddress->ScbStatus & SCB_RUS_MASK) != SCB_RUS_IDLE)
|
|
{
|
|
// Issue an RU abort. Since an interrupt will be issued, the
|
|
// RU will be started by the DPC.
|
|
Status = D100IssueScbCommand(Adapter, SCB_RUC_ABORT, TRUE);
|
|
}
|
|
|
|
// Issue a selective reset.
|
|
DBGPRINT(MP_INFO, ("CU suspended. ScbStatus=%04x Issue selective reset\n", Adapter->CSRAddress->ScbStatus));
|
|
Adapter->CSRAddress->Port = PORT_SELECTIVE_RESET;
|
|
|
|
// Wait after a port sel-reset command
|
|
NdisStallExecution(NIC_DELAY_POST_RESET);
|
|
|
|
// wait up to 2 ms for port command to complete
|
|
MP_STALL_AND_WAIT(Adapter->CSRAddress->Port == 0, 2, bResult)
|
|
if (!bResult)
|
|
{
|
|
MP_SET_HARDWARE_ERROR(Adapter);
|
|
}
|
|
|
|
// disable interrupts after issuing reset, because the int
|
|
// line gets raised when reset completes.
|
|
NICDisableInterrupt(Adapter);
|
|
|
|
// Restore the transmit software flags.
|
|
Adapter->TransmitIdle = TRUE;
|
|
Adapter->ResumeWait = TRUE;
|
|
}
|
|
}
|
|
|
|
VOID NICIssueFullReset(
|
|
PMP_ADAPTER Adapter)
|
|
{
|
|
BOOLEAN bResult;
|
|
|
|
NICIssueSelectiveReset(Adapter);
|
|
|
|
Adapter->CSRAddress->Port = PORT_SOFTWARE_RESET;
|
|
|
|
// wait up to 2 ms for port command to complete
|
|
MP_STALL_AND_WAIT(Adapter->CSRAddress->Port == 0, 2, bResult);
|
|
if (!bResult)
|
|
{
|
|
MP_SET_HARDWARE_ERROR(Adapter);
|
|
return;
|
|
}
|
|
|
|
NICDisableInterrupt(Adapter);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Procedure: D100SubmitCommandBlockAndWait
|
|
//
|
|
// Description: This routine will submit a command block to be executed, and
|
|
// then it will wait for that command block to be executed. Since
|
|
// board ints will be disabled, we will ack the interrupt in
|
|
// this routine.
|
|
//
|
|
// Arguments:
|
|
// Adapter - ptr to Adapter object instance
|
|
//
|
|
// Returns:
|
|
// NDIS_STATUS_SUCCESS
|
|
// NDIS_STATUS_HARD_ERRORS
|
|
//-----------------------------------------------------------------------------
|
|
|
|
NDIS_STATUS D100SubmitCommandBlockAndWait(
|
|
IN PMP_ADAPTER Adapter)
|
|
{
|
|
NDIS_STATUS Status;
|
|
BOOLEAN bResult;
|
|
|
|
// Points to the Non Tx Command Block.
|
|
volatile PNON_TRANSMIT_CB CommandBlock = Adapter->NonTxCmdBlock;
|
|
|
|
// Set the Command Block to be the last command block
|
|
CommandBlock->NonTxCb.Config.ConfigCBHeader.CbCommand |= CB_EL_BIT;
|
|
|
|
// Clear the status of the command block
|
|
CommandBlock->NonTxCb.Config.ConfigCBHeader.CbStatus = 0;
|
|
|
|
#if DBG
|
|
// Don't try to start the CU if the command unit is active.
|
|
if ((Adapter->CSRAddress->ScbStatus & SCB_CUS_MASK) == SCB_CUS_ACTIVE)
|
|
{
|
|
DBGPRINT(MP_ERROR, ("Scb "PTR_FORMAT" ScbStatus %04x\n", Adapter->CSRAddress, Adapter->CSRAddress->ScbStatus));
|
|
ASSERT(FALSE);
|
|
MP_SET_HARDWARE_ERROR(Adapter);
|
|
return(NDIS_STATUS_HARD_ERRORS);
|
|
}
|
|
#endif
|
|
|
|
// Start the command unit.
|
|
D100IssueScbCommand(Adapter, SCB_CUC_START, FALSE);
|
|
|
|
// Wait for the SCB to clear, indicating the completion of the command.
|
|
if (!WaitScb(Adapter))
|
|
{
|
|
return(NDIS_STATUS_HARD_ERRORS);
|
|
}
|
|
|
|
// Wait for some status, timeout value 3 secs
|
|
MP_STALL_AND_WAIT(CommandBlock->NonTxCb.Config.ConfigCBHeader.CbStatus & CB_STATUS_COMPLETE, 3000, bResult);
|
|
if (!bResult)
|
|
{
|
|
MP_SET_HARDWARE_ERROR(Adapter);
|
|
return(NDIS_STATUS_HARD_ERRORS);
|
|
}
|
|
|
|
// Ack any interrupts
|
|
if (Adapter->CSRAddress->ScbStatus & SCB_ACK_MASK)
|
|
{
|
|
// Ack all pending interrupts now
|
|
Adapter->CSRAddress->ScbStatus &= SCB_ACK_MASK;
|
|
}
|
|
|
|
// Check the status of the command, and if the command failed return FALSE,
|
|
// otherwise return TRUE.
|
|
if (!(CommandBlock->NonTxCb.Config.ConfigCBHeader.CbStatus & CB_STATUS_OK))
|
|
{
|
|
DBGPRINT(MP_ERROR, ("Command failed\n"));
|
|
MP_SET_HARDWARE_ERROR(Adapter);
|
|
Status = NDIS_STATUS_HARD_ERRORS;
|
|
}
|
|
else
|
|
Status = NDIS_STATUS_SUCCESS;
|
|
|
|
return(Status);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Procedure: GetConnectionStatus
|
|
//
|
|
// Description: This function returns the connection status that is
|
|
// a required indication for PC 97 specification from MS
|
|
// the value we are looking for is if there is link to the
|
|
// wire or not.
|
|
//
|
|
// Arguments: IN Adapter structure pointer
|
|
//
|
|
// Returns: NdisMediaStateConnected
|
|
// NdisMediaStateDisconnected
|
|
//-----------------------------------------------------------------------------
|
|
NDIS_MEDIA_STATE NICGetMediaState(IN PMP_ADAPTER Adapter)
|
|
{
|
|
USHORT MdiStatusReg = 0;
|
|
BOOLEAN bResult1;
|
|
BOOLEAN bResult2;
|
|
|
|
|
|
// Read the status register at phy 1
|
|
bResult1 = MdiRead(Adapter, MDI_STATUS_REG, Adapter->PhyAddress, TRUE, &MdiStatusReg);
|
|
bResult2 = MdiRead(Adapter, MDI_STATUS_REG, Adapter->PhyAddress, TRUE, &MdiStatusReg);
|
|
|
|
// if there is hardware failure, or let the state remains the same
|
|
if (!bResult1 || !bResult2)
|
|
{
|
|
return Adapter->MediaState;
|
|
}
|
|
if (MdiStatusReg & MDI_SR_LINK_STATUS)
|
|
return(NdisMediaStateConnected);
|
|
else
|
|
return(NdisMediaStateDisconnected);
|
|
|
|
}
|