mirror of https://github.com/lianthony/NT4.0
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.
1321 lines
30 KiB
1321 lines
30 KiB
/*++
|
|
|
|
Copyright (c) 1990 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
card.c
|
|
|
|
Abstract:
|
|
|
|
Ndis 3.0 MAC driver for the 3Com Etherlink III
|
|
|
|
Author:
|
|
|
|
Brian Lieuallen BrianLie 09/22/92
|
|
|
|
Environment:
|
|
|
|
Kernel Mode Operating Systems : NT
|
|
|
|
Revision History:
|
|
|
|
Portions borrowed from ELNK3 driver by
|
|
Earle R. Horton (EarleH)
|
|
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
|
|
|
#include <ndis.h>
|
|
#include <efilter.h>
|
|
|
|
#include "debug.h"
|
|
|
|
|
|
#include "elnk3hrd.h"
|
|
#include "elnk3sft.h"
|
|
#include "elnk3.h"
|
|
|
|
#include "keywords.h"
|
|
|
|
|
|
#pragma NDIS_INIT_FUNCTION(Elnk3FindIsaBoards)
|
|
#pragma NDIS_INIT_FUNCTION(Elnk3ActivateIsaBoard)
|
|
#pragma NDIS_INIT_FUNCTION(ReadStationAddress)
|
|
#pragma NDIS_INIT_FUNCTION(ELNK3WriteIDSequence)
|
|
#pragma NDIS_INIT_FUNCTION(ELNK3ContentionTest)
|
|
#pragma NDIS_INIT_FUNCTION(Elnk3GetEisaResources)
|
|
#pragma NDIS_INIT_FUNCTION(CardEnable)
|
|
#pragma NDIS_INIT_FUNCTION(CardTest)
|
|
#pragma NDIS_INIT_FUNCTION(ELNK3ReadEEProm)
|
|
#pragma NDIS_INIT_FUNCTION(Elnk3ProgramEEProm)
|
|
#pragma NDIS_INIT_FUNCTION(Elnk3WriteEEProm)
|
|
#pragma NDIS_INIT_FUNCTION(Elnk3WaitEEPromNotBusy)
|
|
|
|
|
|
BOOLEAN
|
|
CardTest (
|
|
OUT PELNK3_ADAPTER pAdapter
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
|
|
IF_INIT_LOUD (DbgPrint("Elnk3: CardTest(): Entered\n");)
|
|
|
|
|
|
if (!ReadStationAddress(pAdapter)) {
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
if (!CardEnable(pAdapter)) {
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
CardEnable(
|
|
IN PELNK3_ADAPTER pAdapter
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine enables the card hardware and sets the dma channel and
|
|
interrupts number by writing to the Cards option register
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
Note:
|
|
|
|
This should not be called before the registry is read and the Dma
|
|
and interrupt info is in place.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
USHORT Temp;
|
|
UINT i;
|
|
ULONG Limit;
|
|
USHORT AddressConfig;
|
|
USHORT RevisionLevel;
|
|
|
|
|
|
IF_INIT_LOUD(DbgPrint("ELNK3: CardEnable\n");)
|
|
|
|
|
|
CardReset(pAdapter);
|
|
|
|
ELNK3_SELECT_WINDOW(pAdapter,WNO_FIFO);
|
|
|
|
ELNK3ReadAdapterUshort(pAdapter,PORT_FREE_RX_BYTES,&pAdapter->RxFifoSize);
|
|
|
|
|
|
//
|
|
// Set the station address
|
|
|
|
ELNK3_SELECT_WINDOW(pAdapter,WNO_STATIONADDRESS);
|
|
|
|
for (i=0;i<3;i++) {
|
|
Temp=pAdapter->StationAddress[i*2] | (((USHORT)pAdapter->StationAddress[i*2+1])<<8);
|
|
ELNK3WriteAdapterUshort(pAdapter,(i*2),Temp);
|
|
}
|
|
|
|
//
|
|
// Read address config register to find out transceiver type
|
|
//
|
|
ELNK3_SELECT_WINDOW(pAdapter,WNO_SETUP);
|
|
|
|
ELNK3ReadAdapterUshort(pAdapter,PORT_CfgAddress,&AddressConfig);
|
|
|
|
ELNK3_SELECT_WINDOW(pAdapter,WNO_DIAGNOSTICS);
|
|
|
|
IF_INIT_LOUD(DbgPrint("ELNK3: Address config register is %04x\n",AddressConfig);)
|
|
|
|
if ((AddressConfig >> 14) == 0) {
|
|
//
|
|
// Enable link beat on TP
|
|
//
|
|
if (pAdapter->EEpromSoftwareInfo & LINK_BEAT_DISABLED) {
|
|
|
|
IF_INIT_LOUD(DbgPrint("ELNK3: CardEnable: NOT Enabling link beat on 10 Base-T\n");)
|
|
|
|
ELNK3WriteAdapterUshort(pAdapter,PORT_MEDIA_TYPE, MEDIA_JABBER);
|
|
|
|
} else {
|
|
|
|
IF_INIT_LOUD(DbgPrint("ELNK3: CardEnable: Enabling link beat on 10 Base-T\n");)
|
|
|
|
ELNK3WriteAdapterUshort(pAdapter,PORT_MEDIA_TYPE,MEDIA_LINK_BEAT | MEDIA_JABBER);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ELNK3ReadAdapterUshort(pAdapter,PORT_NET_DIAG,&RevisionLevel);
|
|
|
|
RevisionLevel= ((RevisionLevel & 0x1e) >> 1);
|
|
|
|
pAdapter->RevisionLevel=(UCHAR)RevisionLevel;
|
|
|
|
ELNK3_SELECT_WINDOW(pAdapter,WNO_OPERATING);
|
|
|
|
//
|
|
// Test to see if the interrupt works or not
|
|
//
|
|
ELNK3_COMMAND(pAdapter,EC_ACKNOWLEDGE_INTERRUPT,0xff);
|
|
ELNK3_COMMAND(pAdapter,EC_SET_INTERRUPT_MASK,EC_INT_INTERRUPT_REQUESTED);
|
|
ELNK3_COMMAND(pAdapter,EC_SET_READ_ZERO_MASK,0xff);
|
|
|
|
pAdapter->InitInterrupt=FALSE;
|
|
|
|
pAdapter->AdapterInitializing=TRUE;
|
|
|
|
ELNK3_COMMAND(pAdapter,EC_REQUEST_INTERRUPT,0);
|
|
|
|
Limit=2000;
|
|
|
|
while ((--Limit>0) && (!pAdapter->InitInterrupt)) {
|
|
NdisStallExecution(10);
|
|
}
|
|
|
|
pAdapter->AdapterInitializing=FALSE;
|
|
|
|
//
|
|
// Did it time out or did we get an interrupt
|
|
//
|
|
if (Limit==0) {
|
|
IF_INIT_LOUD(DbgPrint("ELNK3: Did not receive interrupt\n");)
|
|
return FALSE;
|
|
}
|
|
|
|
ELNK3_COMMAND(pAdapter,EC_RX_RESET,0);
|
|
ELNK3_WAIT_NOT_BUSY(pAdapter);
|
|
|
|
|
|
|
|
// ELNK3_COMMAND(pAdapter,EC_RX_ENABLE,0);
|
|
// ELNK3_COMMAND(pAdapter,EC_TX_ENABLE,0);
|
|
|
|
if ((AddressConfig >> 14) == 3) {
|
|
//
|
|
// turn on 10base2 converter
|
|
//
|
|
IF_INIT_LOUD(DbgPrint("ELNK3: CardEnable: Turning on 10base2 converter\n");)
|
|
|
|
ELNK3_COMMAND(pAdapter,EC_START_COAX_XCVR,0);
|
|
|
|
NdisStallExecution(800);
|
|
}
|
|
|
|
|
|
|
|
pAdapter->CurrentInterruptMask=EC_INT_RX_EARLY |
|
|
EC_INT_TX_COMPLETE |
|
|
EC_INT_RX_COMPLETE |
|
|
EC_INT_TX_AVAILABLE |
|
|
EC_INT_ADAPTER_FAILURE |
|
|
EC_INT_INTERRUPT_REQUESTED;
|
|
|
|
ELNK3_COMMAND(pAdapter,EC_SET_INTERRUPT_MASK,pAdapter->CurrentInterruptMask);
|
|
ELNK3_COMMAND(pAdapter,EC_SET_READ_ZERO_MASK,pAdapter->CurrentInterruptMask);
|
|
|
|
|
|
IF_INIT_LOUD(DbgPrint("ELNK3: Adapter initialized correctly\n");)
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
CardReInit(
|
|
IN PELNK3_ADAPTER pAdapter
|
|
)
|
|
|
|
{
|
|
USHORT FifoStatus;
|
|
USHORT FreeFifoBytes;
|
|
|
|
ELNK3_SELECT_WINDOW(pAdapter,WNO_DIAGNOSTICS);
|
|
|
|
ELNK3ReadAdapterUshort(pAdapter,PORT_FIFO_DIAG,&FifoStatus);
|
|
|
|
ELNK3_SELECT_WINDOW(pAdapter,WNO_OPERATING);
|
|
|
|
if (FifoStatus & RX_UNDERRUN) {
|
|
IF_LOUD(DbgPrint("ELNK3: Receive Underrun\n");)
|
|
|
|
ELNK3_COMMAND(pAdapter,EC_RX_RESET,0);
|
|
ELNK3_WAIT_NOT_BUSY(pAdapter);
|
|
|
|
ELNK3_COMMAND(pAdapter,EC_RX_ENABLE,0);
|
|
|
|
ELNK3_COMMAND(pAdapter, EC_SET_RX_FILTER, pAdapter->RxFilter);
|
|
}
|
|
|
|
if (FifoStatus & TX_OVERRUN) {
|
|
|
|
ELNK3ReadAdapterUshort(pAdapter,PORT_TxFree,&FreeFifoBytes);
|
|
|
|
IF_LOUD(DbgPrint("ELNK3: Transmit Overrun - Bytesfree=%d\n",FreeFifoBytes);)
|
|
|
|
|
|
ELNK3_COMMAND(pAdapter,EC_TX_RESET,0);
|
|
ELNK3_WAIT_NOT_BUSY(pAdapter);
|
|
|
|
ELNK3_COMMAND(pAdapter,EC_TX_ENABLE,0);
|
|
}
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
CardReStart(
|
|
IN PELNK3_ADAPTER pAdapter
|
|
)
|
|
|
|
{
|
|
|
|
IF_LOUD(DbgPrint("ELNK3: CardRestart\n");)
|
|
|
|
//
|
|
// put the filter back
|
|
//
|
|
ELNK3_COMMAND(pAdapter, EC_SET_RX_FILTER, 0);
|
|
|
|
|
|
ELNK3_COMMAND(pAdapter,EC_SET_READ_ZERO_MASK,0x00);
|
|
|
|
pAdapter->CurrentInterruptMask=EC_INT_RX_EARLY |
|
|
EC_INT_TX_COMPLETE |
|
|
EC_INT_RX_COMPLETE |
|
|
EC_INT_TX_AVAILABLE |
|
|
EC_INT_ADAPTER_FAILURE |
|
|
EC_INT_INTERRUPT_REQUESTED;
|
|
|
|
ELNK3_COMMAND(pAdapter,EC_SET_INTERRUPT_MASK,pAdapter->CurrentInterruptMask);
|
|
|
|
|
|
|
|
//
|
|
// Reset and re-enable the receiver
|
|
//
|
|
ELNK3_COMMAND(pAdapter,EC_RX_RESET,0);
|
|
ELNK3_WAIT_NOT_BUSY(pAdapter);
|
|
|
|
ELNK3_COMMAND(pAdapter,EC_RX_ENABLE,0);
|
|
|
|
|
|
//
|
|
// reset and restart the transmitter
|
|
//
|
|
ELNK3_COMMAND(pAdapter,EC_TX_RESET,0);
|
|
ELNK3_WAIT_NOT_BUSY(pAdapter);
|
|
|
|
ELNK3_COMMAND(pAdapter,EC_TX_ENABLE,0);
|
|
|
|
ELNK3_COMMAND(pAdapter,EC_SET_TX_START,pAdapter->TxStartThreshold & 0x7ff);
|
|
|
|
//
|
|
// clear all pending interrupts
|
|
//
|
|
ELNK3_COMMAND(pAdapter,EC_ACKNOWLEDGE_INTERRUPT,0xff);
|
|
|
|
//
|
|
// reset the receive buffer info
|
|
//
|
|
pAdapter->CurrentPacket=0;
|
|
|
|
pAdapter->TransContext[0].BytesAlreadyRead=0;
|
|
pAdapter->TransContext[0].PacketLength=0;
|
|
|
|
pAdapter->TransContext[1].BytesAlreadyRead=0;
|
|
pAdapter->TransContext[1].PacketLength=0;
|
|
}
|
|
|
|
|
|
VOID
|
|
CardReStartDone(
|
|
IN PELNK3_ADAPTER pAdapter
|
|
)
|
|
|
|
{
|
|
|
|
IF_LOUD(DbgPrint("ELNK3: CardRestartDone\n");)
|
|
|
|
ELNK3_COMMAND(pAdapter,EC_SET_READ_ZERO_MASK,0xff);
|
|
//
|
|
// put the filter back
|
|
//
|
|
ELNK3_COMMAND(pAdapter, EC_SET_RX_FILTER, pAdapter->RxFilter);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
CardReset(
|
|
IN PELNK3_ADAPTER pAdapter
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
|
|
IF_INIT_LOUD(DbgPrint("ELNK3: Reset Card\n");)
|
|
|
|
//
|
|
// Mask everything
|
|
//
|
|
ELNK3_COMMAND(pAdapter,EC_SET_INTERRUPT_MASK,0);
|
|
|
|
//
|
|
// Clear all interrupt reasons
|
|
//
|
|
ELNK3_COMMAND(pAdapter,EC_ACKNOWLEDGE_INTERRUPT,0xff);
|
|
|
|
|
|
ELNK3_COMMAND(pAdapter,EC_RX_RESET,0);
|
|
ELNK3_WAIT_NOT_BUSY(pAdapter);
|
|
|
|
ELNK3_COMMAND(pAdapter,EC_TX_RESET,0);
|
|
ELNK3_WAIT_NOT_BUSY(pAdapter);
|
|
}
|
|
|
|
|
|
|
|
|
|
BOOLEAN
|
|
ReadStationAddress(
|
|
OUT PELNK3_ADAPTER pAdapter
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Read the station address from the PROM
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
UINT i,Sum;
|
|
USHORT Temp;
|
|
|
|
Sum=0;
|
|
|
|
IF_INIT_LOUD( DbgPrint("Elnk3: The Station address is ");)
|
|
|
|
ELNK3_SELECT_WINDOW(pAdapter,0);
|
|
|
|
|
|
//
|
|
// Read the eeprom software info to see if we need
|
|
// to enable link beat of not
|
|
//
|
|
pAdapter->EEpromSoftwareInfo=ELNK3ReadEEProm(
|
|
pAdapter,
|
|
(UCHAR)EEPROM_SOFTWARE_INFO
|
|
);
|
|
|
|
|
|
|
|
for (i=0;i<3;i++) {
|
|
Temp=ELNK3ReadEEProm(
|
|
pAdapter,
|
|
(UCHAR)i
|
|
);
|
|
|
|
|
|
pAdapter->PermanentAddress[i*2] = Temp>>8;
|
|
pAdapter->PermanentAddress[i*2+1] = Temp & 0x00ff;
|
|
IF_INIT_LOUD( DbgPrint(" %04X",Temp);)
|
|
}
|
|
|
|
//
|
|
// Put the product id value back in the eeprom data register
|
|
//
|
|
ELNK3ReadEEProm(
|
|
pAdapter,
|
|
(UCHAR)EEPROM_PRODUCT_ID
|
|
);
|
|
|
|
|
|
|
|
|
|
//
|
|
// Copy in permanent address if necessary
|
|
//
|
|
|
|
if (!(pAdapter->StationAddress[0] |
|
|
pAdapter->StationAddress[1] |
|
|
pAdapter->StationAddress[2] |
|
|
pAdapter->StationAddress[3] |
|
|
pAdapter->StationAddress[4] |
|
|
pAdapter->StationAddress[5])) {
|
|
|
|
ETH_COPY_NETWORK_ADDRESS(pAdapter->StationAddress,
|
|
pAdapter->PermanentAddress
|
|
);
|
|
}
|
|
|
|
|
|
IF_INIT_LOUD( DbgPrint("\n");)
|
|
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
UINT
|
|
Elnk3FindIsaBoards(
|
|
IN PMAC_BLOCK pMacBlock
|
|
)
|
|
|
|
{
|
|
|
|
PVOID IdPort=pMacBlock->TranslatedIdPort;
|
|
UINT i;
|
|
USHORT ProductId;
|
|
|
|
|
|
if (pMacBlock->IsaAdaptersFound != 0) {
|
|
|
|
IF_INIT_LOUD(DbgPrint("Elnk3: FindIsaBorads: called again\n");)
|
|
|
|
return pMacBlock->IsaAdaptersFound;
|
|
}
|
|
|
|
IF_INIT_LOUD(DbgPrint("Elnk3: FindIsaBorads: called, first time\n");)
|
|
|
|
//
|
|
// Untag all adapters
|
|
//
|
|
|
|
ELNK3WriteIDSequence( IdPort );
|
|
|
|
NdisRawWritePortUchar(IdPort, IDCMD_SET_TAG+0);
|
|
|
|
//
|
|
// We find all the ISA boards in the system and tag them
|
|
// We note each ones configured I/O base, so that
|
|
// we can reset all of the ones not configured as EISA
|
|
//
|
|
|
|
for (i=0; i<7 ; i++) {
|
|
|
|
//
|
|
// Get the cards' attention
|
|
//
|
|
ELNK3WriteIDSequence( IdPort );
|
|
|
|
//
|
|
// See if there any cards out there
|
|
//
|
|
if ( ELNK3ContentionTest( IdPort, EE_MANUFACTURER_CODE ) == EISA_MANUFACTURER_ID ) {
|
|
//
|
|
// we found at least one
|
|
//
|
|
ELNK3ContentionTest( IdPort, EE_TCOM_NODE_ADDR_WORD0 );
|
|
ELNK3ContentionTest( IdPort, EE_TCOM_NODE_ADDR_WORD1 );
|
|
ELNK3ContentionTest( IdPort, EE_TCOM_NODE_ADDR_WORD2 );
|
|
|
|
//
|
|
// This one won the contention battle
|
|
// Get it's i/o base and irq from the eeprom
|
|
//
|
|
|
|
ProductId=ELNK3ContentionTest( IdPort, EE_VULCAN_PROD_ID );
|
|
|
|
ProductId&=0xf0ff;
|
|
|
|
if (ProductId == 0x9050) {
|
|
//
|
|
// This one is a elnk3 adapter
|
|
//
|
|
pMacBlock->IsaCards[i].AddressConfigRegister=
|
|
ELNK3ContentionTest( IdPort, EE_ADDR_CONFIGURATION );
|
|
|
|
pMacBlock->IsaCards[i].IOPort= 0x200 + ((pMacBlock->IsaCards[i].AddressConfigRegister & 0x1f)<<4);
|
|
|
|
} else {
|
|
//
|
|
// We found a 3Com card that is not an elnk3.
|
|
// We will set the base address, to make it look like an EISA
|
|
// one so that code below will leave it alone
|
|
//
|
|
IF_INIT_LOUD(DbgPrint("Elnk3: Found non-elnk3 during contention ProductId=%04x\n",ProductId);)
|
|
|
|
pMacBlock->IsaCards[i].IOPort=0x3f0;
|
|
}
|
|
|
|
pMacBlock->IsaCards[i].Tagged=TRUE;
|
|
//
|
|
// Tag it so it don't bother us again
|
|
//
|
|
NdisRawWritePortUchar(IdPort, IDCMD_SET_TAG+(i+1));
|
|
|
|
|
|
IF_INIT_LOUD(DbgPrint("ELNK3: Found Elnk3 card #%d, io=%04x\n",
|
|
i,
|
|
pMacBlock->IsaCards[i].IOPort
|
|
);)
|
|
|
|
|
|
} else {
|
|
//
|
|
// No more elnk3 cards
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Now all of the ISA adapters have been found and tagged
|
|
// We now go through and reset all of the ones that are
|
|
// not configured as EISA.
|
|
//
|
|
|
|
for (i=0; i<7 ; i++) {
|
|
|
|
//
|
|
// Get the cards' attention
|
|
//
|
|
ELNK3WriteIDSequence( IdPort );
|
|
|
|
//
|
|
// If it is not configured as eisa reset it
|
|
//
|
|
if ((pMacBlock->IsaCards[i].IOPort != 0x03f0) && pMacBlock->IsaCards[i].Tagged) {
|
|
|
|
IF_INIT_LOUD(DbgPrint("ELNK3: Reseting Elnk3 card #%d\n",i);)
|
|
|
|
//
|
|
// Put the others back to sleep
|
|
//
|
|
NdisRawWritePortUchar(IdPort, IDCMD_TEST_TAG+(i+1));
|
|
|
|
//
|
|
// Reset this one
|
|
//
|
|
NdisRawWritePortUchar(IdPort, IDCMD_RESET);
|
|
|
|
NdisStallExecution(500);
|
|
|
|
} else {
|
|
#if DBG
|
|
if (pMacBlock->IsaCards[i].Tagged) {
|
|
|
|
IF_INIT_LOUD(DbgPrint("ELNK3: NOT Reseting EISA configured Elnk3 card #%d\n",i);)
|
|
}
|
|
#endif
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Now we go and find the adapters for real.
|
|
//
|
|
|
|
//
|
|
// Untag all adapters
|
|
//
|
|
|
|
ELNK3WriteIDSequence( IdPort );
|
|
|
|
NdisRawWritePortUchar(IdPort, IDCMD_SET_TAG+0);
|
|
|
|
|
|
for (i=0; i<7 ; i++) {
|
|
|
|
//
|
|
// Get the cards' attention
|
|
//
|
|
ELNK3WriteIDSequence( IdPort );
|
|
|
|
//
|
|
// See if there any cards out there
|
|
//
|
|
if ( ELNK3ContentionTest( IdPort, EE_MANUFACTURER_CODE ) == EISA_MANUFACTURER_ID ) {
|
|
|
|
//
|
|
// we found at least one
|
|
//
|
|
ELNK3ContentionTest( IdPort, EE_TCOM_NODE_ADDR_WORD0 );
|
|
ELNK3ContentionTest( IdPort, EE_TCOM_NODE_ADDR_WORD1 );
|
|
ELNK3ContentionTest( IdPort, EE_TCOM_NODE_ADDR_WORD2 );
|
|
|
|
//
|
|
// This one won the contention battle
|
|
// Get it's i/o base and irq from the eeprom
|
|
//
|
|
|
|
ProductId=ELNK3ContentionTest( IdPort, EE_VULCAN_PROD_ID );
|
|
|
|
ProductId&=0xf0ff;
|
|
|
|
if (ProductId == 0x9050) {
|
|
//
|
|
// This one is a elnk3 adapter
|
|
//
|
|
|
|
pMacBlock->IsaCards[i].AddressConfigRegister=
|
|
ELNK3ContentionTest( IdPort, EE_ADDR_CONFIGURATION );
|
|
|
|
pMacBlock->IsaCards[i].ResourceConfigRegister=
|
|
ELNK3ContentionTest( IdPort, EE_RESOURCE_CONFIGURATION );
|
|
|
|
|
|
pMacBlock->IsaCards[i].IOPort= 0x200 + ((pMacBlock->IsaCards[i].AddressConfigRegister & 0x1f)<<4);
|
|
pMacBlock->IsaCards[i].Irq=pMacBlock->IsaCards[i].ResourceConfigRegister >> 12;
|
|
|
|
if (pMacBlock->IsaCards[i].IOPort == 0x03f0) {
|
|
//
|
|
// This one is configured as EISA. Mark it as active
|
|
// so it will be ignored
|
|
//
|
|
IF_INIT_LOUD(DbgPrint("ELNK3: Found Elnk3 ISA configed as EISA\n");)
|
|
|
|
pMacBlock->IsaCards[i].Active=TRUE;
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// We found a 3Com card that is not an elnk3.
|
|
// We will set the base address, to make it look like an EISA
|
|
// one so that code below will leave it alone
|
|
//
|
|
IF_INIT_LOUD(DbgPrint("Elnk3: Found non-elnk3 during contention ProductId=%04x\n",ProductId);)
|
|
|
|
pMacBlock->IsaCards[i].IOPort=0x3f0;
|
|
|
|
pMacBlock->IsaCards[i].Active=TRUE;
|
|
}
|
|
|
|
|
|
//
|
|
// Tag it so it don't bother us again
|
|
//
|
|
NdisRawWritePortUchar(IdPort, IDCMD_SET_TAG+(i+1));
|
|
|
|
|
|
IF_INIT_LOUD(DbgPrint("ELNK3: Found Elnk3 card #%d, io=%04x, irq=%d\n",
|
|
i,
|
|
pMacBlock->IsaCards[i].IOPort,
|
|
pMacBlock->IsaCards[i].Irq
|
|
);)
|
|
|
|
//
|
|
// One more found
|
|
//
|
|
pMacBlock->IsaAdaptersFound++;
|
|
|
|
} else {
|
|
//
|
|
// No more elnk3 cards
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
|
|
return pMacBlock->IsaAdaptersFound;
|
|
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
Elnk3ActivateIsaBoard(
|
|
IN PMAC_BLOCK pMacBlock,
|
|
IN PELNK3_ADAPTER pAdapter,
|
|
IN NDIS_HANDLE AdapterHandle,
|
|
IN PUCHAR TranslatedIoBase,
|
|
IN ULONG ConfigIoBase,
|
|
IN OUT PULONG Irq,
|
|
IN ULONG Transceiver,
|
|
IN NDIS_HANDLE ConfigurationHandle
|
|
)
|
|
|
|
{
|
|
|
|
PVOID IdPort=pMacBlock->TranslatedIdPort;
|
|
UINT i;
|
|
USHORT AddressConfig;
|
|
USHORT ResourceConfig;
|
|
|
|
for (i=0; i<pMacBlock->IsaAdaptersFound; i++) {
|
|
//
|
|
// Search our table for a card that matches the I/O base passed
|
|
// in from the registry
|
|
//
|
|
|
|
if ((pMacBlock->IsaCards[i].IOPort == ConfigIoBase)
|
|
&&
|
|
(pMacBlock->IsaCards[i].Irq==*Irq)
|
|
&&
|
|
((pMacBlock->IsaCards[i].AddressConfigRegister>>14) == (USHORT)Transceiver)
|
|
&&
|
|
(!pMacBlock->IsaCards[i].Active)) {
|
|
//
|
|
// Everything matched and it is not currently active
|
|
//
|
|
|
|
IF_LOUD(DbgPrint("ELNK3: Found Match-Activating ISA card # %d at %04x\n",i,ConfigIoBase);)
|
|
//
|
|
// Found one, Now get the cards' attention
|
|
//
|
|
|
|
ELNK3WriteIDSequence( IdPort );
|
|
|
|
//
|
|
// Put the others back to sleep
|
|
//
|
|
NdisRawWritePortUchar(IdPort, IDCMD_TEST_TAG+(i+1));
|
|
|
|
//
|
|
// Activate it at the its configured base.
|
|
//
|
|
NdisRawWritePortUchar(IdPort,IDCMD_ACTIVATE + ((ConfigIoBase-0x200) >> 4));
|
|
|
|
//
|
|
// this one is in use now
|
|
//
|
|
pMacBlock->IsaCards[i].Active=TRUE;
|
|
|
|
//
|
|
// enable the IRQ drivers
|
|
//
|
|
NdisRawWritePortUchar(
|
|
TranslatedIoBase+PORT_CfgControl,
|
|
CCR_ENABLE
|
|
);
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
IF_LOUD(DbgPrint("ELNK3: Did not find an adapter that matched i/O base %04x\n",ConfigIoBase);)
|
|
|
|
//
|
|
// Did not find a match, So now we activate the first unused
|
|
// card at the address supplied in the registry.
|
|
//
|
|
if ((*Irq != 0) && (Transceiver != 2)) {
|
|
//
|
|
// The irq and transceiver parameters are present,
|
|
// so we will reprogram the card to make them match
|
|
//
|
|
|
|
for (i=0; i<pMacBlock->IsaAdaptersFound; i++) {
|
|
//
|
|
// Search our table for a card that has not been activated
|
|
//
|
|
|
|
if ((!pMacBlock->IsaCards[i].Active)) {
|
|
|
|
IF_LOUD(DbgPrint("ELNK3: Reconfiguring ISA card # %d to %04x, %d, %d\n",i,ConfigIoBase,*Irq,Transceiver);)
|
|
|
|
//
|
|
// Found one, Now get the cards' attention
|
|
//
|
|
|
|
ELNK3WriteIDSequence( IdPort );
|
|
|
|
//
|
|
// Put the others back to sleep
|
|
//
|
|
NdisRawWritePortUchar(IdPort, IDCMD_TEST_TAG+(i+1));
|
|
|
|
//
|
|
// Activate where we want it
|
|
//
|
|
NdisRawWritePortUchar((PUCHAR)IdPort,IDCMD_ACTIVATE + ((ConfigIoBase-0x200) >> 4));
|
|
|
|
//
|
|
// in use now
|
|
//
|
|
pMacBlock->IsaCards[i].Active=TRUE;
|
|
|
|
//
|
|
// set the transceiver bits in the address config register
|
|
//
|
|
NdisRawReadPortUshort(
|
|
TranslatedIoBase+PORT_CfgAddress,
|
|
&AddressConfig
|
|
);
|
|
|
|
//
|
|
// only want the change the transceiver bits and io base
|
|
//
|
|
AddressConfig &= 0x3fc0;
|
|
|
|
AddressConfig |= (Transceiver << 14) | ((ConfigIoBase - 0x200) >> 4);
|
|
|
|
NdisRawWritePortUshort(
|
|
TranslatedIoBase+PORT_CfgAddress,
|
|
AddressConfig
|
|
);
|
|
|
|
//
|
|
// set the irq number in resources config register
|
|
//
|
|
ResourceConfig=((USHORT)*Irq << 12) | 0x0f00;
|
|
|
|
|
|
NdisRawWritePortUshort(
|
|
TranslatedIoBase+PORT_CfgResource,
|
|
ResourceConfig
|
|
);
|
|
|
|
//
|
|
// enable the IRQ drivers
|
|
//
|
|
NdisRawWritePortUchar(
|
|
TranslatedIoBase+PORT_CfgControl,
|
|
CCR_ENABLE
|
|
);
|
|
|
|
|
|
//
|
|
// Program the eeprom to match the registry
|
|
//
|
|
Elnk3ProgramEEProm(
|
|
pAdapter,
|
|
AddressConfig,
|
|
ResourceConfig
|
|
);
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Did not find any un activated cards
|
|
//
|
|
IF_LOUD(DbgPrint("ELNK3: Did not find an un activated adapter\n");)
|
|
|
|
return FALSE;
|
|
|
|
} else {
|
|
//
|
|
// the irq and/or transceiver values were missing from the registry.
|
|
// Activate the card where the eeprom says it should be.
|
|
//
|
|
IF_LOUD(DbgPrint("Elnk3: irq and transceiver values missing\n");)
|
|
|
|
for (i=0; i<pMacBlock->IsaAdaptersFound; i++) {
|
|
//
|
|
// Search our table for a card that has not been activated
|
|
//
|
|
if ((pMacBlock->IsaCards[i].IOPort == ConfigIoBase)
|
|
&&
|
|
(!pMacBlock->IsaCards[i].Active)) {
|
|
|
|
IF_LOUD(DbgPrint("ELNK3: Activating ISA card # %d at %04x\n",i,ConfigIoBase);)
|
|
|
|
//
|
|
// Found one, Now get the cards' attention
|
|
//
|
|
|
|
ELNK3WriteIDSequence( IdPort );
|
|
|
|
//
|
|
// Put the others back to sleep
|
|
//
|
|
NdisRawWritePortUchar(IdPort, IDCMD_TEST_TAG+(i+1));
|
|
|
|
//
|
|
// Activate where we want it
|
|
//
|
|
NdisRawWritePortUchar((PUCHAR)IdPort,IDCMD_ACTIVATE + ((ConfigIoBase-0x200) >> 4));
|
|
|
|
//
|
|
// enable the IRQ drivers
|
|
//
|
|
NdisRawWritePortUchar(
|
|
TranslatedIoBase+PORT_CfgControl,
|
|
CCR_ENABLE
|
|
);
|
|
|
|
//
|
|
// in use now
|
|
//
|
|
pMacBlock->IsaCards[i].Active=TRUE;
|
|
|
|
*Irq=pMacBlock->IsaCards[i].Irq;
|
|
|
|
IF_INIT_LOUD(DbgPrint("Elnk3: Missing IRQ is now %d\n",*Irq);)
|
|
|
|
{
|
|
NDIS_CONFIGURATION_PARAMETER Value;
|
|
NDIS_STATUS Status;
|
|
NDIS_STRING IrqKeyword = INTERRUPT;
|
|
NDIS_STRING TransceiverKeyword = TRANSCEIVER;
|
|
NDIS_HANDLE ConfigHandle = NULL;
|
|
|
|
//
|
|
// Open the configuration database.
|
|
//
|
|
NdisOpenConfiguration(
|
|
&Status,
|
|
&ConfigHandle,
|
|
ConfigurationHandle);
|
|
if (Status != NDIS_STATUS_SUCCESS)
|
|
{
|
|
return(FALSE);
|
|
}
|
|
|
|
Value.ParameterType = NdisParameterInteger;
|
|
Value.ParameterData.IntegerData = *Irq;
|
|
NdisWriteConfiguration(
|
|
&Status,
|
|
ConfigHandle,
|
|
&IrqKeyword,
|
|
&Value);
|
|
|
|
Value.ParameterType = NdisParameterInteger;
|
|
Value.ParameterData.IntegerData =
|
|
pMacBlock->IsaCards[i].AddressConfigRegister>>14;
|
|
NdisWriteConfiguration(
|
|
&Status,
|
|
ConfigHandle,
|
|
&TransceiverKeyword,
|
|
&Value);
|
|
|
|
NdisCloseConfiguration(ConfigHandle);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Did not find any un activated cards
|
|
//
|
|
IF_LOUD(DbgPrint("ELNK3: Did not find an un activated adapter\n");)
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
Elnk3ProgramEEProm(
|
|
PELNK3_ADAPTER pAdapter,
|
|
USHORT AddressConfig,
|
|
USHORT ResourceConfig
|
|
)
|
|
|
|
{
|
|
USHORT Temp;
|
|
USHORT CheckSum;
|
|
|
|
Elnk3WriteEEProm(
|
|
pAdapter,
|
|
EE_ADDR_CONFIGURATION,
|
|
AddressConfig
|
|
);
|
|
|
|
Elnk3WriteEEProm(
|
|
pAdapter,
|
|
EE_RESOURCE_CONFIGURATION,
|
|
ResourceConfig
|
|
);
|
|
|
|
Temp=ELNK3ReadEEProm(
|
|
pAdapter,
|
|
EE_SOFTWARE_CONFIG_INFO
|
|
);
|
|
|
|
CheckSum=((AddressConfig>>8) ^ (AddressConfig & 0x00ff));
|
|
|
|
CheckSum= CheckSum ^ (ResourceConfig >> 8);
|
|
CheckSum= CheckSum ^ (ResourceConfig & 0x00ff);
|
|
|
|
CheckSum= CheckSum ^ (Temp >> 8);
|
|
CheckSum= CheckSum ^ (Temp & 0x00ff);
|
|
|
|
Temp=ELNK3ReadEEProm(
|
|
pAdapter,
|
|
EE_CHECK_SUM
|
|
);
|
|
|
|
Elnk3WriteEEProm(
|
|
pAdapter,
|
|
EE_CHECK_SUM,
|
|
(USHORT)((Temp & 0xff00) | (CheckSum & 0x00ff))
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
ELNK3WriteIDSequence(
|
|
IN PVOID IdPort
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Writes the magic EtherLink III wake up sequence to a port.
|
|
|
|
This puts all uninitialized EtherLink III cards on the bus in the ID_CMD
|
|
state.
|
|
|
|
Must be called with exclusive access to all 1x0h ports, where x is any
|
|
hex digit.
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
|
|
--*/
|
|
|
|
|
|
{
|
|
USHORT outval;
|
|
UINT i;
|
|
|
|
NdisRawWritePortUchar(IdPort,0);
|
|
NdisRawWritePortUchar(IdPort,0);
|
|
|
|
|
|
for ( outval = 0xff, i = 255 ; i-- ; ) {
|
|
NdisRawWritePortUchar(IdPort,outval);
|
|
outval <<= 1;
|
|
if ( ( outval & 0x0100 ) != 0 ){
|
|
outval ^= 0xCF;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
USHORT
|
|
ELNK3ContentionTest(
|
|
IN PVOID IdPort,
|
|
IN UCHAR EEPromWord
|
|
)
|
|
{
|
|
|
|
UCHAR data;
|
|
USHORT result;
|
|
UINT i;
|
|
|
|
NdisRawWritePortUchar((PUCHAR)IdPort,IDCMD_READ_PROM + EEPromWord);
|
|
|
|
|
|
/*
|
|
3COM's detection code has a 400 microsecond delay here.
|
|
*/
|
|
NdisStallExecution(400);
|
|
|
|
for ( i = 16, result = 0 ; i-- ; ) {
|
|
result <<= 1;
|
|
NdisRawReadPortUchar(IdPort,&data);
|
|
result += (data & 1);
|
|
}
|
|
return (result);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
USHORT
|
|
ELNK3ReadEEProm(
|
|
IN PELNK3_ADAPTER Adapter,
|
|
IN UCHAR EEPromWord
|
|
)
|
|
{
|
|
USHORT result;
|
|
ULONG Limit=10;
|
|
|
|
// Issue an EEPROM read command.
|
|
|
|
NdisRawWritePortUchar(Adapter->PortOffsets[PORT_EECmd],(UCHAR)(IDCMD_READ_PROM+EEPromWord));
|
|
|
|
//
|
|
// Spin until the EEPROM busy bit goes off.
|
|
//
|
|
Elnk3WaitEEPromNotBusy(Adapter);
|
|
|
|
//
|
|
// Fetch the data from the EEPROM data register.
|
|
//
|
|
ELNK3ReadAdapterUshort(Adapter,PORT_EEData,&result);
|
|
|
|
return ( result );
|
|
}
|
|
|
|
VOID
|
|
Elnk3WriteEEProm(
|
|
IN PELNK3_ADAPTER pAdapter,
|
|
IN UCHAR EEPromWord,
|
|
IN USHORT Value
|
|
)
|
|
|
|
{
|
|
Elnk3WaitEEPromNotBusy(pAdapter);
|
|
|
|
//
|
|
// put the data in the data register
|
|
//
|
|
ELNK3WriteAdapterUshort(pAdapter,PORT_EEData,Value);
|
|
|
|
//
|
|
// enable erase and write
|
|
//
|
|
ELNK3WriteAdapterUchar(pAdapter,PORT_EECmd,(UCHAR)EE_COMMAND_EW_ENABLE);
|
|
|
|
Elnk3WaitEEPromNotBusy(pAdapter);
|
|
|
|
//
|
|
// erase the register
|
|
//
|
|
ELNK3WriteAdapterUchar(pAdapter,PORT_EECmd,(UCHAR)EE_COMMAND_ERASE+EEPromWord);
|
|
|
|
Elnk3WaitEEPromNotBusy(pAdapter);
|
|
|
|
//
|
|
// enable erase and write again
|
|
//
|
|
ELNK3WriteAdapterUchar(pAdapter,PORT_EECmd,(UCHAR)EE_COMMAND_EW_ENABLE);
|
|
|
|
Elnk3WaitEEPromNotBusy(pAdapter);
|
|
|
|
//
|
|
// Now write the data
|
|
//
|
|
ELNK3WriteAdapterUchar(pAdapter,PORT_EECmd,(UCHAR)EE_COMMAND_WRITE+EEPromWord);
|
|
|
|
Elnk3WaitEEPromNotBusy(pAdapter);
|
|
|
|
}
|
|
|
|
VOID
|
|
Elnk3WaitEEPromNotBusy(
|
|
PELNK3_ADAPTER Adapter
|
|
)
|
|
|
|
{
|
|
|
|
USHORT result;
|
|
ULONG Limit;
|
|
//
|
|
// Spin until the EEPROM busy bit goes off.
|
|
//
|
|
Limit=100;
|
|
|
|
ELNK3ReadAdapterUshort(Adapter,PORT_EECmd,&result);
|
|
|
|
while ((--Limit>0) && ( (result & EE_BUSY) != 0)) {
|
|
|
|
NdisStallExecution(1000);
|
|
ELNK3ReadAdapterUshort(Adapter,PORT_EECmd,&result);
|
|
|
|
}
|
|
|
|
#if DBG
|
|
if (Limit == 0) {
|
|
|
|
IF_LOUD(DbgPrint("Elnk3: time out waiting for eeprom to not be busy\n");)
|
|
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
Elnk3GetEisaResources(
|
|
IN PELNK3_ADAPTER pAdapter,
|
|
IN OUT PULONG Irq
|
|
)
|
|
|
|
{
|
|
USHORT Config;
|
|
|
|
ELNK3_SELECT_WINDOW(pAdapter, WNO_SETUP);
|
|
|
|
ELNK3ReadAdapterUshort(pAdapter,PORT_CfgResource,&Config);
|
|
|
|
IF_LOUD(DbgPrint("Elnk3: Eisa Irq is %d\n",Config>>12);)
|
|
|
|
*Irq=Config>>12;
|
|
|
|
}
|
|
|
|
|
|
#ifdef IO_DBG
|
|
USHORT ELNK3_READ_PORT_USHORT( PVOID Adapter, ULONG Offset )
|
|
{
|
|
USHORT data;
|
|
data = READ_PORT_USHORT((PUSHORT)((PELNK3_ADAPTER)Adapter)->PortOffsets[Offset]);
|
|
IF_IO_LOUD(DbgPrint("read ushort %x from port %x\n", data, Offset );)
|
|
return data;
|
|
}
|
|
USHORT ELNK3_READ_PORT_USHORT_DIRECT( PVOID Offset )
|
|
{
|
|
USHORT data;
|
|
data = READ_PORT_USHORT((PUSHORT)Offset);
|
|
IF_IO_LOUD(DbgPrint("read ushort %x from port %x\n", data, Offset );)
|
|
return data;
|
|
}
|
|
#endif
|
|
|