mirror of https://github.com/acidanthera/audk.git
3548 lines
91 KiB
C
3548 lines
91 KiB
C
/** @file
|
|
Provides basic function upon network adapter card.
|
|
|
|
Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
|
|
This program and the accompanying materials
|
|
are licensed and made available under the terms and conditions of the BSD License
|
|
which accompanies this distribution. The full text of the license may be found at
|
|
http://opensource.org/licenses/bsd-license.php
|
|
|
|
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
|
|
|
**/
|
|
|
|
#include "Undi32.h"
|
|
|
|
UINT8 basic_config_cmd[22] = {
|
|
22, 0x08,
|
|
0, 0,
|
|
0, (UINT8)0x80,
|
|
0x32, 0x03,
|
|
1, 0,
|
|
0x2E, 0,
|
|
0x60, 0,
|
|
(UINT8)0xf2, 0x48,
|
|
0, 0x40,
|
|
(UINT8)0xf2, (UINT8)0x80, // 0x40=Force full-duplex
|
|
0x3f, 0x05,
|
|
};
|
|
|
|
//
|
|
// How to wait for the command unit to accept a command.
|
|
// Typically this takes 0 ticks.
|
|
//
|
|
#define wait_for_cmd_done(cmd_ioaddr) \
|
|
{ \
|
|
INT16 wait_count = 2000; \
|
|
while ((InByte (AdapterInfo, cmd_ioaddr) != 0) && --wait_count >= 0) \
|
|
DelayIt (AdapterInfo, 10); \
|
|
if (wait_count == 0) \
|
|
DelayIt (AdapterInfo, 50); \
|
|
}
|
|
|
|
|
|
/**
|
|
This function calls the MemIo callback to read a byte from the device's
|
|
address space
|
|
Since UNDI3.0 uses the TmpMemIo function (instead of the callback routine)
|
|
which also takes the UniqueId parameter (as in UNDI3.1 spec) we don't have
|
|
to make undi3.0 a special case
|
|
|
|
@param Port Which port to read from.
|
|
|
|
@retval Results The data read from the port.
|
|
|
|
**/
|
|
// TODO: AdapterInfo - add argument and description to function comment
|
|
UINT8
|
|
InByte (
|
|
IN NIC_DATA_INSTANCE *AdapterInfo,
|
|
IN UINT32 Port
|
|
)
|
|
{
|
|
UINT8 Results;
|
|
|
|
(*AdapterInfo->Mem_Io) (
|
|
AdapterInfo->Unique_ID,
|
|
PXE_MEM_READ,
|
|
1,
|
|
(UINT64)Port,
|
|
(UINT64) (UINTN) &Results
|
|
);
|
|
return Results;
|
|
}
|
|
|
|
|
|
/**
|
|
This function calls the MemIo callback to read a word from the device's
|
|
address space
|
|
Since UNDI3.0 uses the TmpMemIo function (instead of the callback routine)
|
|
which also takes the UniqueId parameter (as in UNDI3.1 spec) we don't have
|
|
to make undi3.0 a special case
|
|
|
|
@param Port Which port to read from.
|
|
|
|
@retval Results The data read from the port.
|
|
|
|
**/
|
|
// TODO: AdapterInfo - add argument and description to function comment
|
|
UINT16
|
|
InWord (
|
|
IN NIC_DATA_INSTANCE *AdapterInfo,
|
|
IN UINT32 Port
|
|
)
|
|
{
|
|
UINT16 Results;
|
|
|
|
(*AdapterInfo->Mem_Io) (
|
|
AdapterInfo->Unique_ID,
|
|
PXE_MEM_READ,
|
|
2,
|
|
(UINT64)Port,
|
|
(UINT64)(UINTN)&Results
|
|
);
|
|
return Results;
|
|
}
|
|
|
|
|
|
/**
|
|
This function calls the MemIo callback to read a dword from the device's
|
|
address space
|
|
Since UNDI3.0 uses the TmpMemIo function (instead of the callback routine)
|
|
which also takes the UniqueId parameter (as in UNDI3.1 spec) we don't have
|
|
to make undi3.0 a special case
|
|
|
|
@param Port Which port to read from.
|
|
|
|
@retval Results The data read from the port.
|
|
|
|
**/
|
|
// TODO: AdapterInfo - add argument and description to function comment
|
|
UINT32
|
|
InLong (
|
|
IN NIC_DATA_INSTANCE *AdapterInfo,
|
|
IN UINT32 Port
|
|
)
|
|
{
|
|
UINT32 Results;
|
|
|
|
(*AdapterInfo->Mem_Io) (
|
|
AdapterInfo->Unique_ID,
|
|
PXE_MEM_READ,
|
|
4,
|
|
(UINT64)Port,
|
|
(UINT64)(UINTN)&Results
|
|
);
|
|
return Results;
|
|
}
|
|
|
|
|
|
/**
|
|
This function calls the MemIo callback to write a byte from the device's
|
|
address space
|
|
Since UNDI3.0 uses the TmpMemIo function (instead of the callback routine)
|
|
which also takes the UniqueId parameter (as in UNDI3.1 spec) we don't have
|
|
to make undi3.0 a special case
|
|
|
|
@param Data Data to write to Port.
|
|
@param Port Which port to write to.
|
|
|
|
@return none
|
|
|
|
**/
|
|
// TODO: AdapterInfo - add argument and description to function comment
|
|
VOID
|
|
OutByte (
|
|
IN NIC_DATA_INSTANCE *AdapterInfo,
|
|
IN UINT8 Data,
|
|
IN UINT32 Port
|
|
)
|
|
{
|
|
UINT8 Val;
|
|
|
|
Val = Data;
|
|
(*AdapterInfo->Mem_Io) (
|
|
AdapterInfo->Unique_ID,
|
|
PXE_MEM_WRITE,
|
|
1,
|
|
(UINT64)Port,
|
|
(UINT64)(UINTN)(UINTN)&Val
|
|
);
|
|
return ;
|
|
}
|
|
|
|
|
|
/**
|
|
This function calls the MemIo callback to write a word from the device's
|
|
address space
|
|
Since UNDI3.0 uses the TmpMemIo function (instead of the callback routine)
|
|
which also takes the UniqueId parameter (as in UNDI3.1 spec) we don't have
|
|
to make undi3.0 a special case
|
|
|
|
@param Data Data to write to Port.
|
|
@param Port Which port to write to.
|
|
|
|
@return none
|
|
|
|
**/
|
|
// TODO: AdapterInfo - add argument and description to function comment
|
|
VOID
|
|
OutWord (
|
|
IN NIC_DATA_INSTANCE *AdapterInfo,
|
|
IN UINT16 Data,
|
|
IN UINT32 Port
|
|
)
|
|
{
|
|
UINT16 Val;
|
|
|
|
Val = Data;
|
|
(*AdapterInfo->Mem_Io) (
|
|
AdapterInfo->Unique_ID,
|
|
PXE_MEM_WRITE,
|
|
2,
|
|
(UINT64)Port,
|
|
(UINT64)(UINTN)&Val
|
|
);
|
|
return ;
|
|
}
|
|
|
|
|
|
/**
|
|
This function calls the MemIo callback to write a dword from the device's
|
|
address space
|
|
Since UNDI3.0 uses the TmpMemIo function (instead of the callback routine)
|
|
which also takes the UniqueId parameter (as in UNDI3.1 spec) we don't have
|
|
to make undi3.0 a special case
|
|
|
|
@param Data Data to write to Port.
|
|
@param Port Which port to write to.
|
|
|
|
@return none
|
|
|
|
**/
|
|
// TODO: AdapterInfo - add argument and description to function comment
|
|
VOID
|
|
OutLong (
|
|
IN NIC_DATA_INSTANCE *AdapterInfo,
|
|
IN UINT32 Data,
|
|
IN UINT32 Port
|
|
)
|
|
{
|
|
UINT32 Val;
|
|
|
|
Val = Data;
|
|
(*AdapterInfo->Mem_Io) (
|
|
AdapterInfo->Unique_ID,
|
|
PXE_MEM_WRITE,
|
|
4,
|
|
(UINT64)Port,
|
|
(UINT64)(UINTN)&Val
|
|
);
|
|
return ;
|
|
}
|
|
|
|
|
|
/**
|
|
TODO: Add function description
|
|
|
|
@param AdapterInfo TODO: add argument description
|
|
@param MemAddr TODO: add argument description
|
|
@param Size TODO: add argument description
|
|
@param Direction TODO: add argument description
|
|
@param MappedAddr TODO: add argument description
|
|
|
|
@return TODO: add return values
|
|
|
|
**/
|
|
UINTN
|
|
MapIt (
|
|
IN NIC_DATA_INSTANCE *AdapterInfo,
|
|
IN UINT64 MemAddr,
|
|
IN UINT32 Size,
|
|
IN UINT32 Direction,
|
|
OUT UINT64 MappedAddr
|
|
)
|
|
{
|
|
UINT64 *PhyAddr;
|
|
|
|
PhyAddr = (UINT64 *) (UINTN) MappedAddr;
|
|
//
|
|
// mapping is different for theold and new NII protocols
|
|
//
|
|
if (AdapterInfo->VersionFlag == 0x30) {
|
|
if (AdapterInfo->Virt2Phys_30 == (VOID *) NULL) {
|
|
*PhyAddr = (UINT64) AdapterInfo->MemoryPtr;
|
|
} else {
|
|
(*AdapterInfo->Virt2Phys_30) (MemAddr, (UINT64) (UINTN) PhyAddr);
|
|
}
|
|
|
|
if (*PhyAddr > FOUR_GIGABYTE) {
|
|
return PXE_STATCODE_INVALID_PARAMETER;
|
|
}
|
|
} else {
|
|
if (AdapterInfo->Map_Mem == (VOID *) NULL) {
|
|
//
|
|
// this UNDI cannot handle addresses beyond 4 GB without a map routine
|
|
//
|
|
if (MemAddr > FOUR_GIGABYTE) {
|
|
return PXE_STATCODE_INVALID_PARAMETER;
|
|
} else {
|
|
*PhyAddr = MemAddr;
|
|
}
|
|
} else {
|
|
(*AdapterInfo->Map_Mem) (
|
|
AdapterInfo->Unique_ID,
|
|
MemAddr,
|
|
Size,
|
|
Direction,
|
|
MappedAddr
|
|
);
|
|
}
|
|
}
|
|
|
|
return PXE_STATCODE_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
TODO: Add function description
|
|
|
|
@param AdapterInfo TODO: add argument description
|
|
@param MemAddr TODO: add argument description
|
|
@param Size TODO: add argument description
|
|
@param Direction TODO: add argument description
|
|
@param MappedAddr TODO: add argument description
|
|
|
|
@return TODO: add return values
|
|
|
|
**/
|
|
VOID
|
|
UnMapIt (
|
|
IN NIC_DATA_INSTANCE *AdapterInfo,
|
|
IN UINT64 MemAddr,
|
|
IN UINT32 Size,
|
|
IN UINT32 Direction,
|
|
IN UINT64 MappedAddr
|
|
)
|
|
{
|
|
if (AdapterInfo->VersionFlag > 0x30) {
|
|
//
|
|
// no mapping service
|
|
//
|
|
if (AdapterInfo->UnMap_Mem != (VOID *) NULL) {
|
|
(*AdapterInfo->UnMap_Mem) (
|
|
AdapterInfo->Unique_ID,
|
|
MemAddr,
|
|
Size,
|
|
Direction,
|
|
MappedAddr
|
|
);
|
|
|
|
}
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
|
|
/**
|
|
|
|
@param AdapterInfo Pointer to the NIC data structure
|
|
information which the UNDI driver is
|
|
layering on..
|
|
|
|
|
|
**/
|
|
// TODO: MicroSeconds - add argument and description to function comment
|
|
VOID
|
|
DelayIt (
|
|
IN NIC_DATA_INSTANCE *AdapterInfo,
|
|
UINT16 MicroSeconds
|
|
)
|
|
{
|
|
if (AdapterInfo->VersionFlag == 0x30) {
|
|
(*AdapterInfo->Delay_30) (MicroSeconds);
|
|
} else {
|
|
(*AdapterInfo->Delay) (AdapterInfo->Unique_ID, MicroSeconds);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
|
|
@param AdapterInfo Pointer to the NIC data structure
|
|
information which the UNDI driver is
|
|
layering on..
|
|
|
|
|
|
**/
|
|
// TODO: flag - add argument and description to function comment
|
|
VOID
|
|
BlockIt (
|
|
IN NIC_DATA_INSTANCE *AdapterInfo,
|
|
UINT32 flag
|
|
)
|
|
{
|
|
if (AdapterInfo->VersionFlag == 0x30) {
|
|
(*AdapterInfo->Block_30) (flag);
|
|
} else {
|
|
(*AdapterInfo->Block) (AdapterInfo->Unique_ID, flag);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
TODO: Add function description
|
|
|
|
@param AdapterInfo TODO: add argument description
|
|
|
|
@return TODO: add return values
|
|
|
|
**/
|
|
UINT8
|
|
Load_Base_Regs (
|
|
NIC_DATA_INSTANCE *AdapterInfo
|
|
)
|
|
{
|
|
//
|
|
// we will use the linear (flat) memory model and fill our base registers
|
|
// with 0's so that the entire physical address is our offset
|
|
//
|
|
//
|
|
// we reset the statistics totals here because this is where we are loading stats addr
|
|
//
|
|
AdapterInfo->RxTotals = 0;
|
|
AdapterInfo->TxTotals = 0;
|
|
|
|
//
|
|
// Load the statistics block address.
|
|
//
|
|
wait_for_cmd_done (AdapterInfo->ioaddr + SCBCmd);
|
|
OutLong (AdapterInfo, (UINT32) AdapterInfo->stat_phy_addr, AdapterInfo->ioaddr + SCBPointer);
|
|
OutByte (AdapterInfo, CU_STATSADDR, AdapterInfo->ioaddr + SCBCmd);
|
|
AdapterInfo->statistics->done_marker = 0;
|
|
|
|
wait_for_cmd_done (AdapterInfo->ioaddr + SCBCmd);
|
|
OutLong (AdapterInfo, 0, AdapterInfo->ioaddr + SCBPointer);
|
|
OutByte (AdapterInfo, RX_ADDR_LOAD, AdapterInfo->ioaddr + SCBCmd);
|
|
|
|
wait_for_cmd_done (AdapterInfo->ioaddr + SCBCmd);
|
|
OutLong (AdapterInfo, 0, AdapterInfo->ioaddr + SCBPointer);
|
|
OutByte (AdapterInfo, CU_CMD_BASE, AdapterInfo->ioaddr + SCBCmd);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
TODO: Add function description
|
|
|
|
@param AdapterInfo TODO: add argument description
|
|
@param cmd_ptr TODO: add argument description
|
|
|
|
@return TODO: add return values
|
|
|
|
**/
|
|
UINT8
|
|
IssueCB (
|
|
NIC_DATA_INSTANCE *AdapterInfo,
|
|
TxCB *cmd_ptr
|
|
)
|
|
{
|
|
UINT16 status;
|
|
|
|
wait_for_cmd_done (AdapterInfo->ioaddr + SCBCmd);
|
|
|
|
//
|
|
// read the CU status, if it is idle, write the address of cb_ptr
|
|
// in the scbpointer and issue a cu_start,
|
|
// if it is suspended, remove the suspend bit in the previous command
|
|
// block and issue a resume
|
|
//
|
|
// Ensure that the CU Active Status bit is not on from previous CBs.
|
|
//
|
|
status = InWord (AdapterInfo, AdapterInfo->ioaddr + SCBStatus);
|
|
|
|
//
|
|
// Skip acknowledging the interrupt if it is not already set
|
|
//
|
|
|
|
//
|
|
// ack only the cna the integer
|
|
//
|
|
if ((status & SCB_STATUS_CNA) != 0) {
|
|
OutWord (AdapterInfo, SCB_STATUS_CNA, AdapterInfo->ioaddr + SCBStatus);
|
|
|
|
}
|
|
|
|
if ((status & SCB_STATUS_CU_MASK) == SCB_STATUS_CU_IDLE) {
|
|
//
|
|
// give a cu_start
|
|
//
|
|
OutLong (AdapterInfo, cmd_ptr->PhysTCBAddress, AdapterInfo->ioaddr + SCBPointer);
|
|
OutByte (AdapterInfo, CU_START, AdapterInfo->ioaddr + SCBCmd);
|
|
} else {
|
|
//
|
|
// either active or suspended, give a resume
|
|
//
|
|
|
|
cmd_ptr->PrevTCBVirtualLinkPtr->cb_header.command &= ~(CmdSuspend | CmdIntr);
|
|
OutByte (AdapterInfo, CU_RESUME, AdapterInfo->ioaddr + SCBCmd);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
TODO: Add function description
|
|
|
|
@param AdapterInfo TODO: add argument description
|
|
|
|
@return TODO: add return values
|
|
|
|
**/
|
|
UINT8
|
|
Configure (
|
|
NIC_DATA_INSTANCE *AdapterInfo
|
|
)
|
|
{
|
|
//
|
|
// all command blocks are of TxCB format
|
|
//
|
|
TxCB *cmd_ptr;
|
|
UINT8 *data_ptr;
|
|
volatile INT16 Index;
|
|
UINT8 my_filter;
|
|
|
|
cmd_ptr = GetFreeCB (AdapterInfo);
|
|
ASSERT (cmd_ptr != NULL);
|
|
data_ptr = (UINT8 *) cmd_ptr + sizeof (struct CB_Header);
|
|
|
|
//
|
|
// start the config data right after the command header
|
|
//
|
|
for (Index = 0; Index < sizeof (basic_config_cmd); Index++) {
|
|
data_ptr[Index] = basic_config_cmd[Index];
|
|
}
|
|
|
|
my_filter = (UINT8) ((AdapterInfo->Rx_Filter & PXE_OPFLAGS_RECEIVE_FILTER_PROMISCUOUS) ? 1 : 0);
|
|
my_filter = (UINT8) (my_filter | ((AdapterInfo->Rx_Filter & PXE_OPFLAGS_RECEIVE_FILTER_BROADCAST) ? 0 : 2));
|
|
|
|
data_ptr[15] = (UINT8) (data_ptr[15] | my_filter);
|
|
data_ptr[19] = (UINT8) (AdapterInfo->Duplex ? 0xC0 : 0x80);
|
|
data_ptr[21] = (UINT8) ((AdapterInfo->Rx_Filter & PXE_OPFLAGS_RECEIVE_FILTER_ALL_MULTICAST) ? 0x0D : 0x05);
|
|
|
|
//
|
|
// check if we have to use the AUI port instead
|
|
//
|
|
if ((AdapterInfo->PhyRecord[0] & 0x8000) != 0) {
|
|
data_ptr[15] |= 0x80;
|
|
data_ptr[8] = 0;
|
|
}
|
|
|
|
BlockIt (AdapterInfo, TRUE);
|
|
cmd_ptr->cb_header.command = CmdSuspend | CmdConfigure;
|
|
|
|
IssueCB (AdapterInfo, cmd_ptr);
|
|
wait_for_cmd_done (AdapterInfo->ioaddr + SCBCmd);
|
|
|
|
BlockIt (AdapterInfo, FALSE);
|
|
|
|
CommandWaitForCompletion (cmd_ptr, AdapterInfo);
|
|
|
|
//
|
|
// restore the cb values for tx
|
|
//
|
|
cmd_ptr->PhysTBDArrayAddres = cmd_ptr->PhysArrayAddr;
|
|
cmd_ptr->ByteCount = cmd_ptr->Threshold = cmd_ptr->TBDCount = 0;
|
|
//
|
|
// fields beyond the immediatedata are assumed to be safe
|
|
// add the CB to the free list again
|
|
//
|
|
SetFreeCB (AdapterInfo, cmd_ptr);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
TODO: Add function description
|
|
|
|
@param AdapterInfo TODO: add argument description
|
|
|
|
@return TODO: add return values
|
|
|
|
**/
|
|
UINT8
|
|
E100bSetupIAAddr (
|
|
NIC_DATA_INSTANCE *AdapterInfo
|
|
)
|
|
{
|
|
//
|
|
// all command blocks are of TxCB format
|
|
//
|
|
TxCB *cmd_ptr;
|
|
UINT16 *data_ptr;
|
|
UINT16 *eaddrs;
|
|
|
|
eaddrs = (UINT16 *) AdapterInfo->CurrentNodeAddress;
|
|
|
|
cmd_ptr = GetFreeCB (AdapterInfo);
|
|
ASSERT (cmd_ptr != NULL);
|
|
data_ptr = (UINT16 *) ((UINT8 *) cmd_ptr +sizeof (struct CB_Header));
|
|
|
|
//
|
|
// AVOID a bug (?!) here by marking the command already completed.
|
|
//
|
|
cmd_ptr->cb_header.command = (CmdSuspend | CmdIASetup);
|
|
cmd_ptr->cb_header.status = 0;
|
|
data_ptr[0] = eaddrs[0];
|
|
data_ptr[1] = eaddrs[1];
|
|
data_ptr[2] = eaddrs[2];
|
|
|
|
BlockIt (AdapterInfo, TRUE);
|
|
IssueCB (AdapterInfo, cmd_ptr);
|
|
wait_for_cmd_done (AdapterInfo->ioaddr + SCBCmd);
|
|
BlockIt (AdapterInfo, FALSE);
|
|
|
|
CommandWaitForCompletion (cmd_ptr, AdapterInfo);
|
|
|
|
//
|
|
// restore the cb values for tx
|
|
//
|
|
cmd_ptr->PhysTBDArrayAddres = cmd_ptr->PhysArrayAddr;
|
|
cmd_ptr->ByteCount = cmd_ptr->Threshold = cmd_ptr->TBDCount = 0;
|
|
//
|
|
// fields beyond the immediatedata are assumed to be safe
|
|
// add the CB to the free list again
|
|
//
|
|
SetFreeCB (AdapterInfo, cmd_ptr);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
Instructs the NIC to stop receiving packets.
|
|
|
|
@param AdapterInfo Pointer to the NIC data structure
|
|
information which the UNDI driver is
|
|
layering on..
|
|
|
|
|
|
**/
|
|
VOID
|
|
StopRU (
|
|
IN NIC_DATA_INSTANCE *AdapterInfo
|
|
)
|
|
{
|
|
if (AdapterInfo->Receive_Started) {
|
|
|
|
//
|
|
// Todo: verify that we must wait for previous command completion.
|
|
//
|
|
wait_for_cmd_done (AdapterInfo->ioaddr + SCBCmd);
|
|
|
|
//
|
|
// Disable interrupts, and stop the chip's Rx process.
|
|
//
|
|
OutWord (AdapterInfo, INT_MASK, AdapterInfo->ioaddr + SCBCmd);
|
|
OutWord (AdapterInfo, INT_MASK | RX_ABORT, AdapterInfo->ioaddr + SCBCmd);
|
|
|
|
AdapterInfo->Receive_Started = FALSE;
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
|
|
/**
|
|
Instructs the NIC to start receiving packets.
|
|
|
|
@param AdapterInfo Pointer to the NIC data structure
|
|
information which the UNDI driver is
|
|
layering on..
|
|
|
|
@retval 0 Successful
|
|
@retval -1 Already Started
|
|
|
|
**/
|
|
INT8
|
|
StartRU (
|
|
NIC_DATA_INSTANCE *AdapterInfo
|
|
)
|
|
{
|
|
|
|
if (AdapterInfo->Receive_Started) {
|
|
//
|
|
// already started
|
|
//
|
|
return -1;
|
|
}
|
|
|
|
AdapterInfo->cur_rx_ind = 0;
|
|
AdapterInfo->Int_Status = 0;
|
|
|
|
wait_for_cmd_done (AdapterInfo->ioaddr + SCBCmd);
|
|
|
|
OutLong (AdapterInfo, (UINT32) AdapterInfo->rx_phy_addr, AdapterInfo->ioaddr + SCBPointer);
|
|
OutByte (AdapterInfo, RX_START, AdapterInfo->ioaddr + SCBCmd);
|
|
|
|
wait_for_cmd_done (AdapterInfo->ioaddr + SCBCmd);
|
|
|
|
AdapterInfo->Receive_Started = TRUE;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
Configures the chip. This routine expects the NIC_DATA_INSTANCE structure to be filled in.
|
|
|
|
@param AdapterInfo Pointer to the NIC data structure
|
|
information which the UNDI driver is
|
|
layering on..
|
|
|
|
@retval 0 Successful
|
|
@retval PXE_STATCODE_NOT_ENOUGH_MEMORY Insufficient length of locked memory
|
|
@retval other Failure initializing chip
|
|
|
|
**/
|
|
UINTN
|
|
E100bInit (
|
|
IN NIC_DATA_INSTANCE *AdapterInfo
|
|
)
|
|
{
|
|
PCI_CONFIG_HEADER *CfgHdr;
|
|
UINTN stat;
|
|
UINTN rx_size;
|
|
UINTN tx_size;
|
|
|
|
if (AdapterInfo->MemoryLength < MEMORY_NEEDED) {
|
|
return PXE_STATCODE_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
stat = MapIt (
|
|
AdapterInfo,
|
|
AdapterInfo->MemoryPtr,
|
|
AdapterInfo->MemoryLength,
|
|
TO_AND_FROM_DEVICE,
|
|
(UINT64)(UINTN) &AdapterInfo->Mapped_MemoryPtr
|
|
);
|
|
|
|
if (stat != 0) {
|
|
return stat;
|
|
}
|
|
|
|
CfgHdr = (PCI_CONFIG_HEADER *) &(AdapterInfo->Config[0]);
|
|
|
|
//
|
|
// fill in the ioaddr, int... from the config space
|
|
//
|
|
AdapterInfo->int_num = CfgHdr->int_line;
|
|
|
|
//
|
|
// we don't need to validate integer number, what if they don't want to assign one?
|
|
// if (AdapterInfo->int_num == 0 || AdapterInfo->int_num == 0xff)
|
|
// return PXE_STATCODE_DEVICE_FAILURE;
|
|
//
|
|
AdapterInfo->ioaddr = 0;
|
|
AdapterInfo->VendorID = CfgHdr->VendorID;
|
|
AdapterInfo->DeviceID = CfgHdr->DeviceID;
|
|
AdapterInfo->RevID = CfgHdr->RevID;
|
|
AdapterInfo->SubVendorID = CfgHdr->SubVendorID;
|
|
AdapterInfo->SubSystemID = CfgHdr->SubSystemID;
|
|
AdapterInfo->flash_addr = 0;
|
|
|
|
//
|
|
// Read the station address EEPROM before doing the reset.
|
|
// Perhaps this should even be done before accepting the device,
|
|
// then we wouldn't have a device name with which to report the error.
|
|
//
|
|
if (E100bReadEepromAndStationAddress (AdapterInfo) != 0) {
|
|
return PXE_STATCODE_DEVICE_FAILURE;
|
|
|
|
}
|
|
//
|
|
// ## calculate the buffer #s depending on memory given
|
|
// ## calculate the rx and tx ring pointers
|
|
//
|
|
|
|
AdapterInfo->TxBufCnt = TX_BUFFER_COUNT;
|
|
AdapterInfo->RxBufCnt = RX_BUFFER_COUNT;
|
|
rx_size = (AdapterInfo->RxBufCnt * sizeof (RxFD));
|
|
tx_size = (AdapterInfo->TxBufCnt * sizeof (TxCB));
|
|
AdapterInfo->rx_ring = (RxFD *) (UINTN) (AdapterInfo->MemoryPtr);
|
|
AdapterInfo->tx_ring = (TxCB *) (UINTN) (AdapterInfo->MemoryPtr + rx_size);
|
|
AdapterInfo->statistics = (struct speedo_stats *) (UINTN) (AdapterInfo->MemoryPtr + rx_size + tx_size);
|
|
|
|
AdapterInfo->rx_phy_addr = AdapterInfo->Mapped_MemoryPtr;
|
|
AdapterInfo->tx_phy_addr = AdapterInfo->Mapped_MemoryPtr + rx_size;
|
|
AdapterInfo->stat_phy_addr = AdapterInfo->tx_phy_addr + tx_size;
|
|
|
|
//
|
|
// auto detect.
|
|
//
|
|
AdapterInfo->PhyAddress = 0xFF;
|
|
AdapterInfo->Rx_Filter = PXE_OPFLAGS_RECEIVE_FILTER_BROADCAST;
|
|
AdapterInfo->Receive_Started = FALSE;
|
|
AdapterInfo->mcast_list.list_len = 0;
|
|
return InitializeChip (AdapterInfo);
|
|
}
|
|
|
|
|
|
/**
|
|
Sets the interrupt state for the NIC.
|
|
|
|
@param AdapterInfo Pointer to the NIC data structure
|
|
information which the UNDI driver is
|
|
layering on..
|
|
|
|
@retval 0 Successful
|
|
|
|
**/
|
|
UINT8
|
|
E100bSetInterruptState (
|
|
IN NIC_DATA_INSTANCE *AdapterInfo
|
|
)
|
|
{
|
|
//
|
|
// don't set receive interrupt if receiver is disabled...
|
|
//
|
|
UINT16 cmd_word;
|
|
|
|
if ((AdapterInfo->int_mask & PXE_OPFLAGS_INTERRUPT_RECEIVE) != 0) {
|
|
cmd_word = InWord (AdapterInfo, AdapterInfo->ioaddr + SCBCmd);
|
|
cmd_word &= ~INT_MASK;
|
|
OutWord (AdapterInfo, cmd_word, AdapterInfo->ioaddr + SCBCmd);
|
|
} else {
|
|
//
|
|
// disable ints, should not be given for SW Int.
|
|
//
|
|
OutWord (AdapterInfo, INT_MASK, AdapterInfo->ioaddr + SCBCmd);
|
|
}
|
|
|
|
if ((AdapterInfo->int_mask & PXE_OPFLAGS_INTERRUPT_SOFTWARE) != 0) {
|
|
//
|
|
// reset the bit in our mask, it is only one time!!
|
|
//
|
|
AdapterInfo->int_mask &= ~(PXE_OPFLAGS_INTERRUPT_SOFTWARE);
|
|
cmd_word = InWord (AdapterInfo, AdapterInfo->ioaddr + SCBCmd);
|
|
cmd_word |= DRVR_INT;
|
|
OutWord (AdapterInfo, cmd_word, AdapterInfo->ioaddr + SCBCmd);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
//
|
|
// we are not going to disable broadcast for the WOL's sake!
|
|
//
|
|
|
|
/**
|
|
Instructs the NIC to start receiving packets.
|
|
|
|
@param AdapterInfo Pointer to the NIC data structure
|
|
information which the UNDI driver is
|
|
layering on.. new_filter
|
|
- cpb -
|
|
cpbsize -
|
|
|
|
@retval 0 Successful
|
|
@retval -1 Already Started
|
|
|
|
**/
|
|
UINTN
|
|
E100bSetfilter (
|
|
NIC_DATA_INSTANCE *AdapterInfo,
|
|
UINT16 new_filter,
|
|
UINT64 cpb,
|
|
UINT32 cpbsize
|
|
)
|
|
{
|
|
PXE_CPB_RECEIVE_FILTERS *mc_list = (PXE_CPB_RECEIVE_FILTERS *) (UINTN)cpb;
|
|
UINT16 cfg_flt;
|
|
UINT16 old_filter;
|
|
UINT16 Index;
|
|
UINT16 Index2;
|
|
UINT16 mc_count;
|
|
TxCB *cmd_ptr;
|
|
struct MC_CB_STRUCT *data_ptr;
|
|
UINT16 mc_byte_cnt;
|
|
|
|
old_filter = AdapterInfo->Rx_Filter;
|
|
|
|
//
|
|
// only these bits need a change in the configuration
|
|
// actually change in bcast requires configure but we ignore that change
|
|
//
|
|
cfg_flt = PXE_OPFLAGS_RECEIVE_FILTER_PROMISCUOUS |
|
|
PXE_OPFLAGS_RECEIVE_FILTER_ALL_MULTICAST;
|
|
|
|
if ((old_filter & cfg_flt) != (new_filter & cfg_flt)) {
|
|
XmitWaitForCompletion (AdapterInfo);
|
|
|
|
if (AdapterInfo->Receive_Started) {
|
|
StopRU (AdapterInfo);
|
|
}
|
|
|
|
AdapterInfo->Rx_Filter = (UINT8) (new_filter | PXE_OPFLAGS_RECEIVE_FILTER_BROADCAST);
|
|
Configure (AdapterInfo);
|
|
}
|
|
|
|
//
|
|
// check if mcast setting changed
|
|
//
|
|
if ( ((new_filter & PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST) !=
|
|
(old_filter & PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST) ) ||
|
|
(mc_list != NULL) ) {
|
|
|
|
|
|
if (mc_list != NULL) {
|
|
mc_count = AdapterInfo->mcast_list.list_len = (UINT16) (cpbsize / PXE_MAC_LENGTH);
|
|
|
|
for (Index = 0; (Index < mc_count && Index < MAX_MCAST_ADDRESS_CNT); Index++) {
|
|
for (Index2 = 0; Index2 < PXE_MAC_LENGTH; Index2++) {
|
|
AdapterInfo->mcast_list.mc_list[Index][Index2] = mc_list->MCastList[Index][Index2];
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// are we setting the list or resetting??
|
|
//
|
|
if ((new_filter & PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST) != 0) {
|
|
//
|
|
// we are setting a new list!
|
|
//
|
|
mc_count = AdapterInfo->mcast_list.list_len;
|
|
//
|
|
// count should be the actual # of bytes in the list
|
|
// so multiply this with 6
|
|
//
|
|
mc_byte_cnt = (UINT16) ((mc_count << 2) + (mc_count << 1));
|
|
AdapterInfo->Rx_Filter |= PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST;
|
|
} else {
|
|
//
|
|
// disabling the list in the NIC.
|
|
//
|
|
mc_byte_cnt = mc_count = 0;
|
|
AdapterInfo->Rx_Filter &= (~PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST);
|
|
}
|
|
|
|
//
|
|
// before issuing any new command!
|
|
//
|
|
XmitWaitForCompletion (AdapterInfo);
|
|
|
|
if (AdapterInfo->Receive_Started) {
|
|
StopRU (AdapterInfo);
|
|
|
|
}
|
|
|
|
cmd_ptr = GetFreeCB (AdapterInfo);
|
|
if (cmd_ptr == NULL) {
|
|
return PXE_STATCODE_QUEUE_FULL;
|
|
}
|
|
//
|
|
// fill the command structure and issue
|
|
//
|
|
data_ptr = (struct MC_CB_STRUCT *) (&cmd_ptr->PhysTBDArrayAddres);
|
|
//
|
|
// first 2 bytes are the count;
|
|
//
|
|
data_ptr->count = mc_byte_cnt;
|
|
for (Index = 0; Index < mc_count; Index++) {
|
|
for (Index2 = 0; Index2 < PXE_HWADDR_LEN_ETHER; Index2++) {
|
|
data_ptr->m_list[Index][Index2] = AdapterInfo->mcast_list.mc_list[Index][Index2];
|
|
}
|
|
}
|
|
|
|
cmd_ptr->cb_header.command = CmdSuspend | CmdMulticastList;
|
|
cmd_ptr->cb_header.status = 0;
|
|
|
|
BlockIt (AdapterInfo, TRUE);
|
|
IssueCB (AdapterInfo, cmd_ptr);
|
|
wait_for_cmd_done (AdapterInfo->ioaddr + SCBCmd);
|
|
|
|
BlockIt (AdapterInfo, FALSE);
|
|
|
|
CommandWaitForCompletion (cmd_ptr, AdapterInfo);
|
|
|
|
cmd_ptr->PhysTBDArrayAddres = cmd_ptr->PhysArrayAddr;
|
|
cmd_ptr->ByteCount = cmd_ptr->Threshold = cmd_ptr->TBDCount = 0;
|
|
//
|
|
// fields beyond the immediatedata are assumed to be safe
|
|
// add the CB to the free list again
|
|
//
|
|
SetFreeCB (AdapterInfo, cmd_ptr);
|
|
}
|
|
|
|
if (new_filter != 0) {
|
|
//
|
|
// enable unicast and start the RU
|
|
//
|
|
AdapterInfo->Rx_Filter = (UINT8) (AdapterInfo->Rx_Filter | (new_filter | PXE_OPFLAGS_RECEIVE_FILTER_UNICAST));
|
|
StartRU (AdapterInfo);
|
|
} else {
|
|
//
|
|
// may be disabling everything!
|
|
//
|
|
if (AdapterInfo->Receive_Started) {
|
|
StopRU (AdapterInfo);
|
|
}
|
|
|
|
AdapterInfo->Rx_Filter |= (~PXE_OPFLAGS_RECEIVE_FILTER_UNICAST);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
TODO: Add function description
|
|
|
|
@param AdapterInfo TODO: add argument description
|
|
@param cpb TODO: add argument description
|
|
@param opflags TODO: add argument description
|
|
|
|
@return TODO: add return values
|
|
|
|
**/
|
|
UINTN
|
|
E100bTransmit (
|
|
NIC_DATA_INSTANCE *AdapterInfo,
|
|
UINT64 cpb,
|
|
UINT16 opflags
|
|
)
|
|
{
|
|
PXE_CPB_TRANSMIT_FRAGMENTS *tx_ptr_f;
|
|
PXE_CPB_TRANSMIT *tx_ptr_1;
|
|
TxCB *tcb_ptr;
|
|
UINT64 Tmp_ptr;
|
|
UINTN stat;
|
|
INT32 Index;
|
|
UINT16 wait_sec;
|
|
|
|
tx_ptr_1 = (PXE_CPB_TRANSMIT *) (UINTN) cpb;
|
|
tx_ptr_f = (PXE_CPB_TRANSMIT_FRAGMENTS *) (UINTN) cpb;
|
|
Tmp_ptr = 0;
|
|
|
|
//
|
|
// stop reentrancy here
|
|
//
|
|
if (AdapterInfo->in_transmit) {
|
|
return PXE_STATCODE_BUSY;
|
|
|
|
}
|
|
|
|
AdapterInfo->in_transmit = TRUE;
|
|
|
|
//
|
|
// Prevent interrupts from changing the Tx ring from underneath us.
|
|
//
|
|
// Calculate the Tx descriptor entry.
|
|
//
|
|
if ((tcb_ptr = GetFreeCB (AdapterInfo)) == NULL) {
|
|
AdapterInfo->in_transmit = FALSE;
|
|
return PXE_STATCODE_QUEUE_FULL;
|
|
}
|
|
|
|
AdapterInfo->TxTotals++;
|
|
|
|
tcb_ptr->cb_header.command = (CmdSuspend | CmdTx | CmdTxFlex);
|
|
tcb_ptr->cb_header.status = 0;
|
|
|
|
//
|
|
// no immediate data, set EOF in the ByteCount
|
|
//
|
|
tcb_ptr->ByteCount = 0x8000;
|
|
|
|
//
|
|
// The data region is always in one buffer descriptor, Tx FIFO
|
|
// threshold of 256.
|
|
// 82557 multiplies the threashold value by 8, so give 256/8
|
|
//
|
|
tcb_ptr->Threshold = 32;
|
|
if ((opflags & PXE_OPFLAGS_TRANSMIT_FRAGMENTED) != 0) {
|
|
|
|
if (tx_ptr_f->FragCnt > MAX_XMIT_FRAGMENTS) {
|
|
SetFreeCB (AdapterInfo, tcb_ptr);
|
|
AdapterInfo->in_transmit = FALSE;
|
|
return PXE_STATCODE_INVALID_PARAMETER;
|
|
}
|
|
|
|
tcb_ptr->TBDCount = (UINT8) tx_ptr_f->FragCnt;
|
|
|
|
for (Index = 0; Index < tx_ptr_f->FragCnt; Index++) {
|
|
stat = MapIt (
|
|
AdapterInfo,
|
|
tx_ptr_f->FragDesc[Index].FragAddr,
|
|
tx_ptr_f->FragDesc[Index].FragLen,
|
|
TO_DEVICE,
|
|
(UINT64)(UINTN) &Tmp_ptr
|
|
);
|
|
if (stat != 0) {
|
|
SetFreeCB (AdapterInfo, tcb_ptr);
|
|
AdapterInfo->in_transmit = FALSE;
|
|
return PXE_STATCODE_INVALID_PARAMETER;
|
|
}
|
|
|
|
tcb_ptr->TBDArray[Index].phys_buf_addr = (UINT32) Tmp_ptr;
|
|
tcb_ptr->TBDArray[Index].buf_len = tx_ptr_f->FragDesc[Index].FragLen;
|
|
}
|
|
|
|
tcb_ptr->free_data_ptr = tx_ptr_f->FragDesc[0].FragAddr;
|
|
|
|
} else {
|
|
//
|
|
// non fragmented case
|
|
//
|
|
tcb_ptr->TBDCount = 1;
|
|
stat = MapIt (
|
|
AdapterInfo,
|
|
tx_ptr_1->FrameAddr,
|
|
tx_ptr_1->DataLen + tx_ptr_1->MediaheaderLen,
|
|
TO_DEVICE,
|
|
(UINT64)(UINTN) &Tmp_ptr
|
|
);
|
|
if (stat != 0) {
|
|
SetFreeCB (AdapterInfo, tcb_ptr);
|
|
AdapterInfo->in_transmit = FALSE;
|
|
return PXE_STATCODE_INVALID_PARAMETER;
|
|
}
|
|
|
|
tcb_ptr->TBDArray[0].phys_buf_addr = (UINT32) (Tmp_ptr);
|
|
tcb_ptr->TBDArray[0].buf_len = tx_ptr_1->DataLen + tx_ptr_1->MediaheaderLen;
|
|
tcb_ptr->free_data_ptr = tx_ptr_1->FrameAddr;
|
|
}
|
|
|
|
//
|
|
// must wait for previous command completion only if it was a non-transmit
|
|
//
|
|
BlockIt (AdapterInfo, TRUE);
|
|
IssueCB (AdapterInfo, tcb_ptr);
|
|
BlockIt (AdapterInfo, FALSE);
|
|
|
|
//
|
|
// see if we need to wait for completion here
|
|
//
|
|
if ((opflags & PXE_OPFLAGS_TRANSMIT_BLOCK) != 0) {
|
|
//
|
|
// don't wait for more than 1 second!!!
|
|
//
|
|
wait_sec = 1000;
|
|
while (tcb_ptr->cb_header.status == 0) {
|
|
DelayIt (AdapterInfo, 10);
|
|
wait_sec--;
|
|
if (wait_sec == 0) {
|
|
break;
|
|
}
|
|
}
|
|
//
|
|
// we need to un-map any mapped buffers here
|
|
//
|
|
if ((opflags & PXE_OPFLAGS_TRANSMIT_FRAGMENTED) != 0) {
|
|
|
|
for (Index = 0; Index < tx_ptr_f->FragCnt; Index++) {
|
|
Tmp_ptr = tcb_ptr->TBDArray[Index].phys_buf_addr;
|
|
UnMapIt (
|
|
AdapterInfo,
|
|
tx_ptr_f->FragDesc[Index].FragAddr,
|
|
tx_ptr_f->FragDesc[Index].FragLen,
|
|
TO_DEVICE,
|
|
(UINT64) Tmp_ptr
|
|
);
|
|
}
|
|
} else {
|
|
Tmp_ptr = tcb_ptr->TBDArray[0].phys_buf_addr;
|
|
UnMapIt (
|
|
AdapterInfo,
|
|
tx_ptr_1->FrameAddr,
|
|
tx_ptr_1->DataLen + tx_ptr_1->MediaheaderLen,
|
|
TO_DEVICE,
|
|
(UINT64) Tmp_ptr
|
|
);
|
|
}
|
|
|
|
if (tcb_ptr->cb_header.status == 0) {
|
|
SetFreeCB (AdapterInfo, tcb_ptr);
|
|
AdapterInfo->in_transmit = FALSE;
|
|
return PXE_STATCODE_DEVICE_FAILURE;
|
|
}
|
|
|
|
SetFreeCB (AdapterInfo, tcb_ptr);
|
|
}
|
|
//
|
|
// CB will be set free later in get_status (or when we run out of xmit buffers
|
|
//
|
|
AdapterInfo->in_transmit = FALSE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
TODO: Add function description
|
|
|
|
@param AdapterInfo TODO: add argument description
|
|
@param cpb TODO: add argument description
|
|
@param db TODO: add argument description
|
|
|
|
@return TODO: add return values
|
|
|
|
**/
|
|
UINTN
|
|
E100bReceive (
|
|
NIC_DATA_INSTANCE *AdapterInfo,
|
|
UINT64 cpb,
|
|
UINT64 db
|
|
)
|
|
{
|
|
PXE_CPB_RECEIVE *rx_cpbptr;
|
|
PXE_DB_RECEIVE *rx_dbptr;
|
|
RxFD *rx_ptr;
|
|
INT32 status;
|
|
INT32 Index;
|
|
UINT16 pkt_len;
|
|
UINT16 ret_code;
|
|
PXE_FRAME_TYPE pkt_type;
|
|
UINT16 Tmp_len;
|
|
EtherHeader *hdr_ptr;
|
|
ret_code = PXE_STATCODE_NO_DATA;
|
|
pkt_type = PXE_FRAME_TYPE_NONE;
|
|
status = InWord (AdapterInfo, AdapterInfo->ioaddr + SCBStatus);
|
|
AdapterInfo->Int_Status = (UINT16) (AdapterInfo->Int_Status | status);
|
|
//
|
|
// acknoledge the interrupts
|
|
//
|
|
OutWord (AdapterInfo, (UINT16) (status & 0xfc00), (UINT32) (AdapterInfo->ioaddr + SCBStatus));
|
|
|
|
//
|
|
// include the prev ints as well
|
|
//
|
|
status = AdapterInfo->Int_Status;
|
|
rx_cpbptr = (PXE_CPB_RECEIVE *) (UINTN) cpb;
|
|
rx_dbptr = (PXE_DB_RECEIVE *) (UINTN) db;
|
|
|
|
rx_ptr = &AdapterInfo->rx_ring[AdapterInfo->cur_rx_ind];
|
|
|
|
//
|
|
// be in a loop just in case (we may drop a pkt)
|
|
//
|
|
while ((status = rx_ptr->cb_header.status) & RX_COMPLETE) {
|
|
|
|
AdapterInfo->RxTotals++;
|
|
//
|
|
// If we own the next entry, it's a new packet. Send it up.
|
|
//
|
|
if (rx_ptr->forwarded) {
|
|
goto FreeRFD;
|
|
|
|
}
|
|
|
|
//
|
|
// discard bad frames
|
|
//
|
|
|
|
//
|
|
// crc, align, dma overrun, too short, receive error (v22 no coll)
|
|
//
|
|
if ((status & 0x0D90) != 0) {
|
|
goto FreeRFD;
|
|
|
|
}
|
|
|
|
//
|
|
// make sure the status is OK
|
|
//
|
|
if ((status & 0x02000) == 0) {
|
|
goto FreeRFD;
|
|
}
|
|
|
|
pkt_len = (UINT16) (rx_ptr->ActualCount & 0x3fff);
|
|
|
|
if (pkt_len != 0) {
|
|
|
|
Tmp_len = pkt_len;
|
|
if (pkt_len > rx_cpbptr->BufferLen) {
|
|
Tmp_len = (UINT16) rx_cpbptr->BufferLen;
|
|
}
|
|
|
|
CopyMem ((INT8 *) (UINTN) rx_cpbptr->BufferAddr, (INT8 *) &rx_ptr->RFDBuffer, Tmp_len);
|
|
|
|
hdr_ptr = (EtherHeader *) &rx_ptr->RFDBuffer;
|
|
//
|
|
// fill the CDB and break the loop
|
|
//
|
|
|
|
//
|
|
// includes header
|
|
//
|
|
rx_dbptr->FrameLen = pkt_len;
|
|
rx_dbptr->MediaHeaderLen = PXE_MAC_HEADER_LEN_ETHER;
|
|
|
|
for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) {
|
|
if (hdr_ptr->dest_addr[Index] != AdapterInfo->CurrentNodeAddress[Index]) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Index >= PXE_HWADDR_LEN_ETHER) {
|
|
pkt_type = PXE_FRAME_TYPE_UNICAST;
|
|
} else {
|
|
for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) {
|
|
if (hdr_ptr->dest_addr[Index] != AdapterInfo->BroadcastNodeAddress[Index]) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Index >= PXE_HWADDR_LEN_ETHER) {
|
|
pkt_type = PXE_FRAME_TYPE_BROADCAST;
|
|
} else {
|
|
if ((hdr_ptr->dest_addr[0] & 1) == 1) {
|
|
//
|
|
// mcast
|
|
//
|
|
|
|
pkt_type = PXE_FRAME_TYPE_FILTERED_MULTICAST;
|
|
} else {
|
|
pkt_type = PXE_FRAME_TYPE_PROMISCUOUS;
|
|
}
|
|
}
|
|
}
|
|
|
|
rx_dbptr->Type = pkt_type;
|
|
rx_dbptr->Protocol = hdr_ptr->type;
|
|
|
|
for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) {
|
|
rx_dbptr->SrcAddr[Index] = hdr_ptr->src_addr[Index];
|
|
rx_dbptr->DestAddr[Index] = hdr_ptr->dest_addr[Index];
|
|
}
|
|
|
|
rx_ptr->forwarded = TRUE;
|
|
//
|
|
// success
|
|
//
|
|
ret_code = 0;
|
|
Recycle_RFD (AdapterInfo, AdapterInfo->cur_rx_ind);
|
|
AdapterInfo->cur_rx_ind++;
|
|
if (AdapterInfo->cur_rx_ind == AdapterInfo->RxBufCnt) {
|
|
AdapterInfo->cur_rx_ind = 0;
|
|
}
|
|
break;
|
|
}
|
|
|
|
FreeRFD:
|
|
Recycle_RFD (AdapterInfo, AdapterInfo->cur_rx_ind);
|
|
AdapterInfo->cur_rx_ind++;
|
|
if (AdapterInfo->cur_rx_ind == AdapterInfo->RxBufCnt) {
|
|
AdapterInfo->cur_rx_ind = 0;
|
|
}
|
|
|
|
rx_ptr = &AdapterInfo->rx_ring[AdapterInfo->cur_rx_ind];
|
|
}
|
|
|
|
if (pkt_type == PXE_FRAME_TYPE_NONE) {
|
|
AdapterInfo->Int_Status &= (~SCB_STATUS_FR);
|
|
}
|
|
|
|
status = InWord (AdapterInfo, AdapterInfo->ioaddr + SCBStatus);
|
|
if ((status & SCB_RUS_NO_RESOURCES) != 0) {
|
|
//
|
|
// start the receive unit here!
|
|
// leave all the filled frames,
|
|
//
|
|
SetupReceiveQueues (AdapterInfo);
|
|
OutLong (AdapterInfo, (UINT32) AdapterInfo->rx_phy_addr, AdapterInfo->ioaddr + SCBPointer);
|
|
OutWord (AdapterInfo, RX_START, AdapterInfo->ioaddr + SCBCmd);
|
|
AdapterInfo->cur_rx_ind = 0;
|
|
}
|
|
|
|
return ret_code;
|
|
}
|
|
|
|
|
|
/**
|
|
TODO: Add function description
|
|
|
|
@param AdapterInfo TODO: add argument description
|
|
|
|
@return TODO: add return values
|
|
|
|
**/
|
|
INT16
|
|
E100bReadEepromAndStationAddress (
|
|
NIC_DATA_INSTANCE *AdapterInfo
|
|
)
|
|
{
|
|
INT32 Index;
|
|
INT32 Index2;
|
|
UINT16 sum;
|
|
UINT16 eeprom_len;
|
|
UINT8 addr_len;
|
|
UINT16 *eedata;
|
|
|
|
eedata = (UINT16 *) (&AdapterInfo->NVData[0]);
|
|
|
|
sum = 0;
|
|
addr_len = E100bGetEepromAddrLen (AdapterInfo);
|
|
|
|
//
|
|
// in words
|
|
//
|
|
AdapterInfo->NVData_Len = eeprom_len = (UINT16) (1 << addr_len);
|
|
for (Index2 = 0, Index = 0; ((Index2 < PXE_MAC_LENGTH - 1) && (Index < eeprom_len)); Index++) {
|
|
UINT16 value;
|
|
value = E100bReadEeprom (AdapterInfo, Index, addr_len);
|
|
eedata[Index] = value;
|
|
sum = (UINT16) (sum + value);
|
|
if (Index < 3) {
|
|
AdapterInfo->PermNodeAddress[Index2++] = (UINT8) value;
|
|
AdapterInfo->PermNodeAddress[Index2++] = (UINT8) (value >> 8);
|
|
}
|
|
}
|
|
|
|
if (sum != 0xBABA) {
|
|
return -1;
|
|
}
|
|
|
|
for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) {
|
|
AdapterInfo->CurrentNodeAddress[Index] = AdapterInfo->PermNodeAddress[Index];
|
|
}
|
|
|
|
for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) {
|
|
AdapterInfo->BroadcastNodeAddress[Index] = 0xff;
|
|
}
|
|
|
|
for (Index = PXE_HWADDR_LEN_ETHER; Index < PXE_MAC_LENGTH; Index++) {
|
|
AdapterInfo->CurrentNodeAddress[Index] = 0;
|
|
AdapterInfo->PermNodeAddress[Index] = 0;
|
|
AdapterInfo->BroadcastNodeAddress[Index] = 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// CBList is a circular linked list
|
|
// 1) When all are free, Tail->next == Head and FreeCount == # allocated
|
|
// 2) When none are free, Tail == Head and FreeCount == 0
|
|
// 3) when one is free, Tail == Head and Freecount == 1
|
|
// 4) First non-Free frame is always at Tail->next
|
|
//
|
|
|
|
/**
|
|
TODO: Add function description
|
|
|
|
@param AdapterInfo TODO: add argument description
|
|
|
|
@return TODO: add return values
|
|
|
|
**/
|
|
UINT8
|
|
SetupCBlink (
|
|
NIC_DATA_INSTANCE *AdapterInfo
|
|
)
|
|
{
|
|
TxCB *head_ptr;
|
|
TxCB *tail_ptr;
|
|
TxCB *cur_ptr;
|
|
INT32 Index;
|
|
UINTN array_off;
|
|
|
|
cur_ptr = &(AdapterInfo->tx_ring[0]);
|
|
array_off = (UINTN) (&cur_ptr->TBDArray) - (UINTN) cur_ptr;
|
|
for (Index = 0; Index < AdapterInfo->TxBufCnt; Index++) {
|
|
cur_ptr[Index].cb_header.status = 0;
|
|
cur_ptr[Index].cb_header.command = 0;
|
|
|
|
cur_ptr[Index].PhysTCBAddress =
|
|
(UINT32) AdapterInfo->tx_phy_addr + (Index * sizeof (TxCB));
|
|
|
|
cur_ptr[Index].PhysArrayAddr = (UINT32)(cur_ptr[Index].PhysTCBAddress + array_off);
|
|
cur_ptr[Index].PhysTBDArrayAddres = (UINT32)(cur_ptr[Index].PhysTCBAddress + array_off);
|
|
|
|
cur_ptr->free_data_ptr = (UINT64) 0;
|
|
|
|
if (Index < AdapterInfo->TxBufCnt - 1) {
|
|
cur_ptr[Index].cb_header.link = cur_ptr[Index].PhysTCBAddress + sizeof (TxCB);
|
|
cur_ptr[Index].NextTCBVirtualLinkPtr = &cur_ptr[Index + 1];
|
|
cur_ptr[Index + 1].PrevTCBVirtualLinkPtr = &cur_ptr[Index];
|
|
}
|
|
}
|
|
|
|
head_ptr = &cur_ptr[0];
|
|
tail_ptr = &cur_ptr[AdapterInfo->TxBufCnt - 1];
|
|
tail_ptr->cb_header.link = head_ptr->PhysTCBAddress;
|
|
tail_ptr->NextTCBVirtualLinkPtr = head_ptr;
|
|
head_ptr->PrevTCBVirtualLinkPtr = tail_ptr;
|
|
|
|
AdapterInfo->FreeCBCount = AdapterInfo->TxBufCnt;
|
|
AdapterInfo->FreeTxHeadPtr = head_ptr;
|
|
//
|
|
// set tail of the free list, next to this would be either in use
|
|
// or the head itself
|
|
//
|
|
AdapterInfo->FreeTxTailPtr = tail_ptr;
|
|
|
|
AdapterInfo->xmit_done_head = AdapterInfo->xmit_done_tail = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
TODO: Add function description
|
|
|
|
@param AdapterInfo TODO: add argument description
|
|
|
|
@return TODO: add return values
|
|
|
|
**/
|
|
TxCB *
|
|
GetFreeCB (
|
|
NIC_DATA_INSTANCE *AdapterInfo
|
|
)
|
|
{
|
|
TxCB *free_cb_ptr;
|
|
|
|
//
|
|
// claim any hanging free CBs
|
|
//
|
|
if (AdapterInfo->FreeCBCount <= 1) {
|
|
CheckCBList (AdapterInfo);
|
|
}
|
|
|
|
//
|
|
// don't use up the last CB problem if the previous CB that the CU used
|
|
// becomes the last CB we submit because of the SUSPEND bit we set.
|
|
// the CU thinks it was never cleared.
|
|
//
|
|
|
|
if (AdapterInfo->FreeCBCount <= 1) {
|
|
return NULL;
|
|
}
|
|
|
|
BlockIt (AdapterInfo, TRUE);
|
|
free_cb_ptr = AdapterInfo->FreeTxHeadPtr;
|
|
AdapterInfo->FreeTxHeadPtr = free_cb_ptr->NextTCBVirtualLinkPtr;
|
|
--AdapterInfo->FreeCBCount;
|
|
BlockIt (AdapterInfo, FALSE);
|
|
return free_cb_ptr;
|
|
}
|
|
|
|
|
|
/**
|
|
TODO: Add function description
|
|
|
|
@param AdapterInfo TODO: add argument description
|
|
@param cb_ptr TODO: add argument description
|
|
|
|
@return TODO: add return values
|
|
|
|
**/
|
|
VOID
|
|
SetFreeCB (
|
|
IN NIC_DATA_INSTANCE *AdapterInfo,
|
|
IN TxCB *cb_ptr
|
|
)
|
|
{
|
|
//
|
|
// here we assume cb are returned in the order they are taken out
|
|
// and we link the newly freed cb at the tail of free cb list
|
|
//
|
|
cb_ptr->cb_header.status = 0;
|
|
cb_ptr->free_data_ptr = (UINT64) 0;
|
|
|
|
AdapterInfo->FreeTxTailPtr = cb_ptr;
|
|
++AdapterInfo->FreeCBCount;
|
|
return ;
|
|
}
|
|
|
|
|
|
/**
|
|
TODO: Add function description
|
|
|
|
@param ind TODO: add argument description
|
|
|
|
@return TODO: add return values
|
|
|
|
**/
|
|
UINT16
|
|
next (
|
|
IN UINT16 ind
|
|
)
|
|
{
|
|
UINT16 Tmp;
|
|
|
|
Tmp = (UINT16) (ind + 1);
|
|
if (Tmp >= (TX_BUFFER_COUNT << 1)) {
|
|
Tmp = 0;
|
|
}
|
|
|
|
return Tmp;
|
|
}
|
|
|
|
|
|
/**
|
|
TODO: Add function description
|
|
|
|
@param AdapterInfo TODO: add argument description
|
|
|
|
@return TODO: add return values
|
|
|
|
**/
|
|
UINT16
|
|
CheckCBList (
|
|
IN NIC_DATA_INSTANCE *AdapterInfo
|
|
)
|
|
{
|
|
TxCB *Tmp_ptr;
|
|
UINT16 cnt;
|
|
|
|
cnt = 0;
|
|
while (1) {
|
|
Tmp_ptr = AdapterInfo->FreeTxTailPtr->NextTCBVirtualLinkPtr;
|
|
if ((Tmp_ptr->cb_header.status & CMD_STATUS_MASK) != 0) {
|
|
//
|
|
// check if Q is full
|
|
//
|
|
if (next (AdapterInfo->xmit_done_tail) != AdapterInfo->xmit_done_head) {
|
|
ASSERT (AdapterInfo->xmit_done_tail < TX_BUFFER_COUNT << 1);
|
|
AdapterInfo->xmit_done[AdapterInfo->xmit_done_tail] = Tmp_ptr->free_data_ptr;
|
|
|
|
UnMapIt (
|
|
AdapterInfo,
|
|
Tmp_ptr->free_data_ptr,
|
|
Tmp_ptr->TBDArray[0].buf_len,
|
|
TO_DEVICE,
|
|
(UINT64) Tmp_ptr->TBDArray[0].phys_buf_addr
|
|
);
|
|
|
|
AdapterInfo->xmit_done_tail = next (AdapterInfo->xmit_done_tail);
|
|
}
|
|
|
|
SetFreeCB (AdapterInfo, Tmp_ptr);
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return cnt;
|
|
}
|
|
//
|
|
// Description : Initialize the RFD list list by linking each element together
|
|
// in a circular list. The simplified memory model is used.
|
|
// All data is in the RFD. The RFDs are linked together and the
|
|
// last one points back to the first one. When the current RFD
|
|
// is processed (frame received), its EL bit is set and the EL
|
|
// bit in the previous RXFD is cleared.
|
|
// Allocation done during INIT, this is making linked list.
|
|
//
|
|
|
|
/**
|
|
TODO: Add function description
|
|
|
|
@param AdapterInfo TODO: add argument description
|
|
|
|
@return TODO: add return values
|
|
|
|
**/
|
|
UINT8
|
|
SetupReceiveQueues (
|
|
IN NIC_DATA_INSTANCE *AdapterInfo
|
|
)
|
|
{
|
|
RxFD *rx_ptr;
|
|
RxFD *tail_ptr;
|
|
UINT16 Index;
|
|
|
|
AdapterInfo->cur_rx_ind = 0;
|
|
rx_ptr = (&AdapterInfo->rx_ring[0]);
|
|
|
|
for (Index = 0; Index < AdapterInfo->RxBufCnt; Index++) {
|
|
rx_ptr[Index].cb_header.status = 0;
|
|
rx_ptr[Index].cb_header.command = 0;
|
|
rx_ptr[Index].RFDSize = RX_BUFFER_SIZE;
|
|
rx_ptr[Index].ActualCount = 0;
|
|
//
|
|
// RBDs not used, simple memory model
|
|
//
|
|
rx_ptr[Index].rx_buf_addr = (UINT32) (-1);
|
|
|
|
//
|
|
// RBDs not used, simple memory model
|
|
//
|
|
rx_ptr[Index].forwarded = FALSE;
|
|
|
|
//
|
|
// don't use Tmp_ptr if it is beyond the last one
|
|
//
|
|
if (Index < AdapterInfo->RxBufCnt - 1) {
|
|
rx_ptr[Index].cb_header.link = (UINT32) AdapterInfo->rx_phy_addr + ((Index + 1) * sizeof (RxFD));
|
|
}
|
|
}
|
|
|
|
tail_ptr = (&AdapterInfo->rx_ring[AdapterInfo->RxBufCnt - 1]);
|
|
tail_ptr->cb_header.link = (UINT32) AdapterInfo->rx_phy_addr;
|
|
|
|
//
|
|
// set the EL bit
|
|
//
|
|
tail_ptr->cb_header.command = 0xC000;
|
|
AdapterInfo->RFDTailPtr = tail_ptr;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
TODO: Add function description
|
|
|
|
@param AdapterInfo TODO: add argument description
|
|
@param rx_index TODO: add argument description
|
|
|
|
@return TODO: add return values
|
|
|
|
**/
|
|
VOID
|
|
Recycle_RFD (
|
|
IN NIC_DATA_INSTANCE *AdapterInfo,
|
|
IN UINT16 rx_index
|
|
)
|
|
{
|
|
RxFD *rx_ptr;
|
|
RxFD *tail_ptr;
|
|
//
|
|
// change the EL bit and change the AdapterInfo->RxTailPtr
|
|
// rx_ptr is assumed to be the head of the Q
|
|
// AdapterInfo->rx_forwarded[rx_index] = FALSE;
|
|
//
|
|
rx_ptr = &AdapterInfo->rx_ring[rx_index];
|
|
tail_ptr = AdapterInfo->RFDTailPtr;
|
|
//
|
|
// set el_bit and suspend bit
|
|
//
|
|
rx_ptr->cb_header.command = 0xc000;
|
|
rx_ptr->cb_header.status = 0;
|
|
rx_ptr->ActualCount = 0;
|
|
rx_ptr->forwarded = FALSE;
|
|
AdapterInfo->RFDTailPtr = rx_ptr;
|
|
//
|
|
// resetting the el_bit.
|
|
//
|
|
tail_ptr->cb_header.command = 0;
|
|
//
|
|
// check the receive unit, fix if there is any problem
|
|
//
|
|
return ;
|
|
}
|
|
//
|
|
// Serial EEPROM section.
|
|
//
|
|
// EEPROM_Ctrl bits.
|
|
//
|
|
#define EE_SHIFT_CLK 0x01 /* EEPROM shift clock. */
|
|
#define EE_CS 0x02 /* EEPROM chip select. */
|
|
#define EE_DI 0x04 /* EEPROM chip data in. */
|
|
#define EE_WRITE_0 0x01
|
|
#define EE_WRITE_1 0x05
|
|
#define EE_DO 0x08 /* EEPROM chip data out. */
|
|
#define EE_ENB (0x4800 | EE_CS)
|
|
|
|
//
|
|
// Delay between EEPROM clock transitions.
|
|
// This will actually work with no delay on 33Mhz PCI.
|
|
//
|
|
#define eeprom_delay(nanosec) DelayIt (AdapterInfo, nanosec);
|
|
|
|
//
|
|
// The EEPROM commands include the alway-set leading bit.
|
|
//
|
|
#define EE_WRITE_CMD 5 // 101b
|
|
#define EE_READ_CMD 6 // 110b
|
|
#define EE_ERASE_CMD (7 << 6)
|
|
|
|
VOID
|
|
shift_bits_out (
|
|
IN NIC_DATA_INSTANCE *AdapterInfo,
|
|
IN UINT16 val,
|
|
IN UINT8 num_bits
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
TODO: Add function description
|
|
|
|
Arguments:
|
|
|
|
AdapterInfo - TODO: add argument description
|
|
val - TODO: add argument description
|
|
num_bits - TODO: add argument description
|
|
|
|
Returns:
|
|
|
|
TODO: add return values
|
|
|
|
--*/
|
|
{
|
|
INT32 Index;
|
|
UINT8 Tmp;
|
|
UINT32 EEAddr;
|
|
|
|
EEAddr = AdapterInfo->ioaddr + SCBeeprom;
|
|
|
|
for (Index = num_bits; Index >= 0; Index--) {
|
|
INT16 dataval;
|
|
|
|
//
|
|
// will be 0 or 4
|
|
//
|
|
dataval = (INT16) ((val & (1 << Index)) ? EE_DI : 0);
|
|
|
|
//
|
|
// mask off the data_in bit
|
|
//
|
|
Tmp = (UINT8) (InByte (AdapterInfo, EEAddr) &~EE_DI);
|
|
Tmp = (UINT8) (Tmp | dataval);
|
|
OutByte (AdapterInfo, Tmp, EEAddr);
|
|
eeprom_delay (100);
|
|
//
|
|
// raise the eeprom clock
|
|
//
|
|
OutByte (AdapterInfo, (UINT8) (Tmp | EE_SHIFT_CLK), EEAddr);
|
|
eeprom_delay (150);
|
|
//
|
|
// lower the eeprom clock
|
|
//
|
|
OutByte (AdapterInfo, (UINT8) (Tmp &~EE_SHIFT_CLK), EEAddr);
|
|
eeprom_delay (150);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
TODO: Add function description
|
|
|
|
@param AdapterInfo TODO: add argument description
|
|
|
|
@return TODO: add return values
|
|
|
|
**/
|
|
UINT16
|
|
shift_bits_in (
|
|
IN NIC_DATA_INSTANCE *AdapterInfo
|
|
)
|
|
{
|
|
UINT8 Tmp;
|
|
INT32 Index;
|
|
UINT16 retval;
|
|
UINT32 EEAddr;
|
|
|
|
EEAddr = AdapterInfo->ioaddr + SCBeeprom;
|
|
|
|
retval = 0;
|
|
for (Index = 15; Index >= 0; Index--) {
|
|
//
|
|
// raise the clock
|
|
//
|
|
|
|
//
|
|
// mask off the data_in bit
|
|
//
|
|
Tmp = InByte (AdapterInfo, EEAddr);
|
|
OutByte (AdapterInfo, (UINT8) (Tmp | EE_SHIFT_CLK), EEAddr);
|
|
eeprom_delay (100);
|
|
Tmp = InByte (AdapterInfo, EEAddr);
|
|
retval = (UINT16) ((retval << 1) | ((Tmp & EE_DO) ? 1 : 0));
|
|
//
|
|
// lower the clock
|
|
//
|
|
OutByte (AdapterInfo, (UINT8) (Tmp &~EE_SHIFT_CLK), EEAddr);
|
|
eeprom_delay (100);
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
/**
|
|
This routine sets the EEPROM lockout bit to gain exclusive access to the
|
|
eeprom. the access bit is the most significant bit in the General Control
|
|
Register 2 in the SCB space.
|
|
|
|
@param AdapterInfo Pointer to the NIC data structure
|
|
information which the UNDI driver is
|
|
layering on..
|
|
|
|
@retval TRUE if it got the access
|
|
@retval FALSE if it fails to get the exclusive access
|
|
|
|
**/
|
|
BOOLEAN
|
|
E100bSetEepromLockOut (
|
|
IN NIC_DATA_INSTANCE *AdapterInfo
|
|
)
|
|
{
|
|
UINTN wait;
|
|
UINT8 tmp;
|
|
|
|
if ((AdapterInfo->DeviceID == D102_DEVICE_ID) ||
|
|
(AdapterInfo->RevID >= D102_REVID)) {
|
|
|
|
wait = 500;
|
|
|
|
while (wait--) {
|
|
|
|
tmp = InByte (AdapterInfo, AdapterInfo->ioaddr + SCBGenCtrl2);
|
|
tmp |= GCR2_EEPROM_ACCESS_SEMAPHORE;
|
|
OutByte (AdapterInfo, tmp, AdapterInfo->ioaddr + SCBGenCtrl2);
|
|
|
|
DelayIt (AdapterInfo, 50);
|
|
tmp = InByte (AdapterInfo, AdapterInfo->ioaddr + SCBGenCtrl2);
|
|
|
|
if (tmp & GCR2_EEPROM_ACCESS_SEMAPHORE) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/**
|
|
This routine Resets the EEPROM lockout bit to giveup access to the
|
|
eeprom. the access bit is the most significant bit in the General Control
|
|
Register 2 in the SCB space.
|
|
|
|
@param AdapterInfo Pointer to the NIC data structure
|
|
information which the UNDI driver is
|
|
layering on..
|
|
|
|
@return None
|
|
|
|
**/
|
|
VOID
|
|
E100bReSetEepromLockOut (
|
|
IN NIC_DATA_INSTANCE *AdapterInfo
|
|
)
|
|
{
|
|
UINT8 tmp;
|
|
|
|
if ((AdapterInfo->DeviceID == D102_DEVICE_ID) ||
|
|
(AdapterInfo->RevID >= D102_REVID)) {
|
|
|
|
tmp = InByte (AdapterInfo, AdapterInfo->ioaddr + SCBGenCtrl2);
|
|
tmp &= ~(GCR2_EEPROM_ACCESS_SEMAPHORE);
|
|
OutByte (AdapterInfo, tmp, AdapterInfo->ioaddr + SCBGenCtrl2);
|
|
|
|
DelayIt (AdapterInfo, 50);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
Using the NIC data structure information, read the EEPROM to get a Word of data for the MAC address.
|
|
|
|
@param AdapterInfo Pointer to the NIC data structure
|
|
information which the UNDI driver is
|
|
layering on..
|
|
@param Location Word offset into the MAC address to read.
|
|
@param AddrLen Number of bits of address length.
|
|
|
|
@retval RetVal The word read from the EEPROM.
|
|
|
|
**/
|
|
UINT16
|
|
E100bReadEeprom (
|
|
IN NIC_DATA_INSTANCE *AdapterInfo,
|
|
IN INT32 Location,
|
|
IN UINT8 AddrLen
|
|
)
|
|
{
|
|
UINT16 RetVal;
|
|
UINT8 Tmp;
|
|
|
|
UINT32 EEAddr;
|
|
UINT16 ReadCmd;
|
|
|
|
EEAddr = AdapterInfo->ioaddr + SCBeeprom;
|
|
ReadCmd = (UINT16) (Location | (EE_READ_CMD << AddrLen));
|
|
|
|
RetVal = 0;
|
|
|
|
//
|
|
// get exclusive access to the eeprom first!
|
|
//
|
|
E100bSetEepromLockOut (AdapterInfo);
|
|
|
|
//
|
|
// eeprom control reg bits: x,x,x,x,DO,DI,CS,SK
|
|
// to write the opcode+data value out one bit at a time in DI starting at msb
|
|
// and then out a 1 to sk, wait, out 0 to SK and wait
|
|
// repeat this for all the bits to be written
|
|
//
|
|
|
|
//
|
|
// 11110010b
|
|
//
|
|
Tmp = (UINT8) (InByte (AdapterInfo, EEAddr) & 0xF2);
|
|
OutByte (AdapterInfo, (UINT8) (Tmp | EE_CS), EEAddr);
|
|
|
|
//
|
|
// 3 for the read opcode 110b
|
|
//
|
|
shift_bits_out (AdapterInfo, ReadCmd, (UINT8) (3 + AddrLen));
|
|
|
|
//
|
|
// read the eeprom word one bit at a time
|
|
//
|
|
RetVal = shift_bits_in (AdapterInfo);
|
|
|
|
//
|
|
// Terminate the EEPROM access and leave eeprom in a clean state.
|
|
//
|
|
Tmp = InByte (AdapterInfo, EEAddr);
|
|
Tmp &= ~(EE_CS | EE_DI);
|
|
OutByte (AdapterInfo, Tmp, EEAddr);
|
|
|
|
//
|
|
// raise the clock and lower the eeprom shift clock
|
|
//
|
|
OutByte (AdapterInfo, (UINT8) (Tmp | EE_SHIFT_CLK), EEAddr);
|
|
eeprom_delay (100);
|
|
|
|
OutByte (AdapterInfo, (UINT8) (Tmp &~EE_SHIFT_CLK), EEAddr);
|
|
eeprom_delay (100);
|
|
|
|
//
|
|
// giveup access to the eeprom
|
|
//
|
|
E100bReSetEepromLockOut (AdapterInfo);
|
|
|
|
return RetVal;
|
|
}
|
|
|
|
|
|
/**
|
|
Using the NIC data structure information, read the EEPROM to determine how many bits of address length
|
|
this EEPROM is in Words.
|
|
|
|
@param AdapterInfo Pointer to the NIC data structure
|
|
information which the UNDI driver is
|
|
layering on..
|
|
|
|
@retval RetVal The word read from the EEPROM.
|
|
|
|
**/
|
|
UINT8
|
|
E100bGetEepromAddrLen (
|
|
IN NIC_DATA_INSTANCE *AdapterInfo
|
|
)
|
|
{
|
|
UINT8 Tmp;
|
|
UINT8 AddrLen;
|
|
UINT32 EEAddr;
|
|
//
|
|
// assume 64word eeprom (so,6 bits of address_length)
|
|
//
|
|
UINT16 ReadCmd;
|
|
|
|
EEAddr = AdapterInfo->ioaddr + SCBeeprom;
|
|
ReadCmd = (EE_READ_CMD << 6);
|
|
|
|
//
|
|
// get exclusive access to the eeprom first!
|
|
//
|
|
E100bSetEepromLockOut (AdapterInfo);
|
|
|
|
//
|
|
// address we are trying to read is 0
|
|
// eeprom control reg bits: x,x,x,x,DO,,DI,,CS,SK
|
|
// to write the opcode+data value out one bit at a time in DI starting at msb
|
|
// and then out a 1 to sk, wait, out 0 to SK and wait
|
|
// repeat this for all the bits to be written
|
|
//
|
|
Tmp = (UINT8) (InByte (AdapterInfo, EEAddr) & 0xF2);
|
|
|
|
//
|
|
// enable eeprom access
|
|
//
|
|
OutByte (AdapterInfo, (UINT8) (Tmp | EE_CS), EEAddr);
|
|
|
|
//
|
|
// 3 for opcode, 6 for the default address len
|
|
//
|
|
shift_bits_out (AdapterInfo, ReadCmd, (UINT8) (3 + 6));
|
|
|
|
//
|
|
// (in case of a 64 word eeprom).
|
|
// read the "dummy zero" from EE_DO to say that the address we wrote
|
|
// (six 0s) is accepted, write more zeros (until 8) to get a "dummy zero"
|
|
//
|
|
|
|
//
|
|
// assume the smallest
|
|
//
|
|
AddrLen = 6;
|
|
Tmp = InByte (AdapterInfo, EEAddr);
|
|
while ((AddrLen < 8) && ((Tmp & EE_DO) != 0)) {
|
|
OutByte (AdapterInfo, (UINT8) (Tmp &~EE_DI), EEAddr);
|
|
eeprom_delay (100);
|
|
|
|
//
|
|
// raise the eeprom clock
|
|
//
|
|
OutByte (AdapterInfo, (UINT8) (Tmp | EE_SHIFT_CLK), EEAddr);
|
|
eeprom_delay (150);
|
|
|
|
//
|
|
// lower the eeprom clock
|
|
//
|
|
OutByte (AdapterInfo, (UINT8) (Tmp &~EE_SHIFT_CLK), EEAddr);
|
|
eeprom_delay (150);
|
|
Tmp = InByte (AdapterInfo, EEAddr);
|
|
AddrLen++;
|
|
}
|
|
|
|
//
|
|
// read the eeprom word, even though we don't need this
|
|
//
|
|
shift_bits_in (AdapterInfo);
|
|
|
|
//
|
|
// Terminate the EEPROM access.
|
|
//
|
|
Tmp = InByte (AdapterInfo, EEAddr);
|
|
Tmp &= ~(EE_CS | EE_DI);
|
|
OutByte (AdapterInfo, Tmp, EEAddr);
|
|
|
|
//
|
|
// raise the clock and lower the eeprom shift clock
|
|
//
|
|
OutByte (AdapterInfo, (UINT8) (Tmp | EE_SHIFT_CLK), EEAddr);
|
|
eeprom_delay (100);
|
|
|
|
OutByte (AdapterInfo, (UINT8) (Tmp &~EE_SHIFT_CLK), EEAddr);
|
|
eeprom_delay (100);
|
|
|
|
//
|
|
// giveup access to the eeprom!
|
|
//
|
|
E100bReSetEepromLockOut (AdapterInfo);
|
|
|
|
return AddrLen;
|
|
}
|
|
|
|
|
|
/**
|
|
TODO: Add function description
|
|
|
|
@param AdapterInfo TODO: add argument description
|
|
@param DBaddr TODO: add argument description
|
|
@param DBsize TODO: add argument description
|
|
|
|
@return TODO: add return values
|
|
|
|
**/
|
|
UINTN
|
|
E100bStatistics (
|
|
NIC_DATA_INSTANCE *AdapterInfo,
|
|
UINT64 DBaddr,
|
|
UINT16 DBsize
|
|
)
|
|
{
|
|
PXE_DB_STATISTICS db;
|
|
//
|
|
// wait upto one second (each wait is 100 micro s)
|
|
//
|
|
UINT32 Wait;
|
|
Wait = 10000;
|
|
wait_for_cmd_done (AdapterInfo->ioaddr + SCBCmd);
|
|
|
|
//
|
|
// Clear statistics done marker.
|
|
//
|
|
AdapterInfo->statistics->done_marker = 0;
|
|
|
|
//
|
|
// Issue statistics dump (or dump w/ reset) command.
|
|
//
|
|
OutByte (
|
|
AdapterInfo,
|
|
(UINT8) (DBsize ? CU_SHOWSTATS : CU_DUMPSTATS),
|
|
(UINT32) (AdapterInfo->ioaddr + SCBCmd)
|
|
);
|
|
|
|
//
|
|
// Wait for command to complete.
|
|
//
|
|
// zero the db here just to chew up a little more time.
|
|
//
|
|
|
|
ZeroMem ((VOID *) &db, sizeof db);
|
|
|
|
while (Wait != 0) {
|
|
//
|
|
// Wait a bit before checking.
|
|
//
|
|
|
|
DelayIt (AdapterInfo, 100);
|
|
|
|
//
|
|
// Look for done marker at end of statistics.
|
|
//
|
|
|
|
switch (AdapterInfo->statistics->done_marker) {
|
|
case 0xA005:
|
|
case 0xA007:
|
|
break;
|
|
|
|
default:
|
|
Wait--;
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// if we did not "continue" from the above switch, we are done,
|
|
//
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If this is a reset, we are out of here!
|
|
//
|
|
if (DBsize == 0) {
|
|
return PXE_STATCODE_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Convert NIC statistics counter format to EFI/UNDI
|
|
// specification statistics counter format.
|
|
//
|
|
|
|
//
|
|
// 54 3210 fedc ba98 7654 3210
|
|
// db.Supported = 01 0000 0100 1101 0001 0111;
|
|
//
|
|
db.Supported = 0x104D17;
|
|
|
|
//
|
|
// Statistics from the NIC
|
|
//
|
|
|
|
db.Data[0x01] = AdapterInfo->statistics->rx_good_frames;
|
|
|
|
db.Data[0x02] = AdapterInfo->statistics->rx_runt_errs;
|
|
|
|
db.Data[0x08] = AdapterInfo->statistics->rx_crc_errs +
|
|
AdapterInfo->statistics->rx_align_errs;
|
|
|
|
db.Data[0x04] = db.Data[0x02] +
|
|
db.Data[0x08] +
|
|
AdapterInfo->statistics->rx_resource_errs +
|
|
AdapterInfo->statistics->rx_overrun_errs;
|
|
|
|
db.Data[0x00] = db.Data[0x01] + db.Data[0x04];
|
|
|
|
db.Data[0x0B] = AdapterInfo->statistics->tx_good_frames;
|
|
|
|
db.Data[0x0E] = AdapterInfo->statistics->tx_coll16_errs +
|
|
AdapterInfo->statistics->tx_late_colls +
|
|
AdapterInfo->statistics->tx_underruns +
|
|
AdapterInfo->statistics->tx_one_colls +
|
|
AdapterInfo->statistics->tx_multi_colls;
|
|
|
|
db.Data[0x14] = AdapterInfo->statistics->tx_total_colls;
|
|
|
|
db.Data[0x0A] = db.Data[0x0B] +
|
|
db.Data[0x0E] +
|
|
AdapterInfo->statistics->tx_lost_carrier;
|
|
|
|
if (DBsize > sizeof db) {
|
|
DBsize = (UINT16) sizeof (db);
|
|
}
|
|
|
|
CopyMem ((VOID *) (UINTN) DBaddr, (VOID *) &db, (UINTN) DBsize);
|
|
|
|
return PXE_STATCODE_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
TODO: Add function description
|
|
|
|
@param AdapterInfo TODO: add argument description
|
|
@param OpFlags TODO: add argument description
|
|
|
|
@return TODO: add return values
|
|
|
|
**/
|
|
UINTN
|
|
E100bReset (
|
|
IN NIC_DATA_INSTANCE *AdapterInfo,
|
|
IN INT32 OpFlags
|
|
)
|
|
{
|
|
|
|
UINT16 save_filter;
|
|
//
|
|
// disable the interrupts
|
|
//
|
|
OutWord (AdapterInfo, INT_MASK, AdapterInfo->ioaddr + SCBCmd);
|
|
|
|
//
|
|
// wait for the tx queue to complete
|
|
//
|
|
CheckCBList (AdapterInfo);
|
|
|
|
XmitWaitForCompletion (AdapterInfo);
|
|
|
|
if (AdapterInfo->Receive_Started) {
|
|
StopRU (AdapterInfo);
|
|
}
|
|
|
|
InitializeChip (AdapterInfo);
|
|
|
|
//
|
|
// check the opflags and restart receive filters
|
|
//
|
|
if ((OpFlags & PXE_OPFLAGS_RESET_DISABLE_FILTERS) == 0) {
|
|
|
|
save_filter = AdapterInfo->Rx_Filter;
|
|
//
|
|
// if we give the filter same as Rx_Filter,
|
|
// this routine will not set mcast list (it thinks there is no change)
|
|
// to force it, we will reset that flag in the Rx_Filter
|
|
//
|
|
AdapterInfo->Rx_Filter &= (~PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST);
|
|
E100bSetfilter (AdapterInfo, save_filter, (UINT64) 0, (UINT32) 0);
|
|
}
|
|
|
|
if ((OpFlags & PXE_OPFLAGS_RESET_DISABLE_INTERRUPTS) != 0) {
|
|
//
|
|
// disable the interrupts
|
|
//
|
|
AdapterInfo->int_mask = 0;
|
|
}
|
|
//
|
|
// else leave the interrupt in the pre-set state!!!
|
|
//
|
|
E100bSetInterruptState (AdapterInfo);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
TODO: Add function description
|
|
|
|
@param AdapterInfo TODO: add argument description
|
|
|
|
@return TODO: add return values
|
|
|
|
**/
|
|
UINTN
|
|
E100bShutdown (
|
|
IN NIC_DATA_INSTANCE *AdapterInfo
|
|
)
|
|
{
|
|
//
|
|
// disable the interrupts
|
|
//
|
|
OutWord (AdapterInfo, INT_MASK, AdapterInfo->ioaddr + SCBCmd);
|
|
|
|
//
|
|
// stop the receive unit
|
|
//
|
|
if (AdapterInfo->Receive_Started) {
|
|
StopRU (AdapterInfo);
|
|
}
|
|
|
|
//
|
|
// wait for the tx queue to complete
|
|
//
|
|
CheckCBList (AdapterInfo);
|
|
if (AdapterInfo->FreeCBCount != AdapterInfo->TxBufCnt) {
|
|
wait_for_cmd_done (AdapterInfo->ioaddr + SCBCmd);
|
|
}
|
|
|
|
//
|
|
// we do not want to reset the phy, it takes a long time to renegotiate the
|
|
// link after that (3-4 seconds)
|
|
//
|
|
InitializeChip (AdapterInfo);
|
|
SelectiveReset (AdapterInfo);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
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.
|
|
|
|
@param AdapterInfo pointer to the structure that contains
|
|
the NIC's context.
|
|
@param RegAddress The MII register that we are writing to
|
|
@param PhyAddress The MDI address of the Phy component.
|
|
@param DataValue The value that we are writing to the MII
|
|
register.
|
|
|
|
@return nothing
|
|
|
|
**/
|
|
VOID
|
|
MdiWrite (
|
|
IN NIC_DATA_INSTANCE *AdapterInfo,
|
|
IN UINT8 RegAddress,
|
|
IN UINT8 PhyAddress,
|
|
IN UINT16 DataValue
|
|
)
|
|
{
|
|
UINT32 WriteCommand;
|
|
|
|
WriteCommand = ((UINT32) DataValue) |
|
|
((UINT32)(RegAddress << 16)) |
|
|
((UINT32)(PhyAddress << 21)) |
|
|
((UINT32)(MDI_WRITE << 26));
|
|
|
|
//
|
|
// Issue the write command to the MDI control register.
|
|
//
|
|
OutLong (AdapterInfo, WriteCommand, AdapterInfo->ioaddr + SCBCtrlMDI);
|
|
|
|
//
|
|
// wait 20usec before checking status
|
|
//
|
|
DelayIt (AdapterInfo, 20);
|
|
|
|
//
|
|
// poll for the mdi write to complete
|
|
while ((InLong (AdapterInfo, AdapterInfo->ioaddr + SCBCtrlMDI) &
|
|
MDI_PHY_READY) == 0){
|
|
DelayIt (AdapterInfo, 20);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
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.
|
|
|
|
@param AdapterInfo pointer to the structure that contains
|
|
the NIC's context.
|
|
@param RegAddress The MII register that we are reading from
|
|
@param PhyAddress The MDI address of the Phy component.
|
|
@param DataValue pointer to the value that we read from
|
|
the MII register.
|
|
|
|
|
|
**/
|
|
VOID
|
|
MdiRead (
|
|
IN NIC_DATA_INSTANCE *AdapterInfo,
|
|
IN UINT8 RegAddress,
|
|
IN UINT8 PhyAddress,
|
|
IN OUT UINT16 *DataValue
|
|
)
|
|
{
|
|
UINT32 ReadCommand;
|
|
|
|
ReadCommand = ((UINT32) (RegAddress << 16)) |
|
|
((UINT32) (PhyAddress << 21)) |
|
|
((UINT32) (MDI_READ << 26));
|
|
|
|
//
|
|
// Issue the read command to the MDI control register.
|
|
//
|
|
OutLong (AdapterInfo, ReadCommand, AdapterInfo->ioaddr + SCBCtrlMDI);
|
|
|
|
//
|
|
// wait 20usec before checking status
|
|
//
|
|
DelayIt (AdapterInfo, 20);
|
|
|
|
//
|
|
// poll for the mdi read to complete
|
|
//
|
|
while ((InLong (AdapterInfo, AdapterInfo->ioaddr + SCBCtrlMDI) &
|
|
MDI_PHY_READY) == 0) {
|
|
DelayIt (AdapterInfo, 20);
|
|
|
|
}
|
|
|
|
*DataValue = InWord (AdapterInfo, AdapterInfo->ioaddr + SCBCtrlMDI);
|
|
}
|
|
|
|
|
|
/**
|
|
This routine will reset the PHY that the adapter is currently
|
|
configured to use.
|
|
|
|
@param AdapterInfo pointer to the structure that contains
|
|
the NIC's context.
|
|
|
|
|
|
**/
|
|
VOID
|
|
PhyReset (
|
|
NIC_DATA_INSTANCE *AdapterInfo
|
|
)
|
|
{
|
|
UINT16 MdiControlReg;
|
|
|
|
MdiControlReg = (MDI_CR_AUTO_SELECT |
|
|
MDI_CR_RESTART_AUTO_NEG |
|
|
MDI_CR_RESET);
|
|
|
|
//
|
|
// Write the MDI control register with our new Phy configuration
|
|
//
|
|
MdiWrite (
|
|
AdapterInfo,
|
|
MDI_CONTROL_REG,
|
|
AdapterInfo->PhyAddress,
|
|
MdiControlReg
|
|
);
|
|
|
|
return ;
|
|
}
|
|
|
|
|
|
/**
|
|
This routine will detect what phy we are using, set the line
|
|
speed, FDX or HDX, and configure the phy if necessary.
|
|
The following combinations are supported:
|
|
- TX or T4 PHY alone at PHY address 1
|
|
- T4 or TX PHY at address 1 and MII PHY at address 0
|
|
- 82503 alone (10Base-T mode, no full duplex support)
|
|
- 82503 and MII PHY (TX or T4) at address 0
|
|
The sequence / priority of detection is as follows:
|
|
- PHY 1 with cable termination
|
|
- PHY 0 with cable termination
|
|
- PHY 1 (if found) without cable termination
|
|
- 503 interface
|
|
Additionally auto-negotiation capable (NWAY) and parallel
|
|
detection PHYs are supported. The flow-chart is described in
|
|
the 82557 software writer's manual.
|
|
NOTE: 1. All PHY MDI registers are read in polled mode.
|
|
2. The routines assume that the 82557 has been RESET and we have
|
|
obtained the virtual memory address of the CSR.
|
|
3. PhyDetect will not RESET the PHY.
|
|
4. If FORCEFDX is set, SPEED should also be set. The driver will
|
|
check the values for inconsistency with the detected PHY
|
|
technology.
|
|
5. PHY 1 (the PHY on the adapter) may have an address in the range
|
|
1 through 31 inclusive. The driver will accept addresses in
|
|
this range.
|
|
6. Driver ignores FORCEFDX and SPEED overrides if a 503 interface
|
|
is detected.
|
|
|
|
@param AdapterInfo pointer to the structure that contains
|
|
the NIC's context.
|
|
|
|
@retval TRUE If a Phy was detected, and configured
|
|
correctly.
|
|
@retval FALSE If a valid phy could not be detected and
|
|
configured.
|
|
|
|
**/
|
|
BOOLEAN
|
|
PhyDetect (
|
|
NIC_DATA_INSTANCE *AdapterInfo
|
|
)
|
|
{
|
|
UINT16 *eedata;
|
|
UINT16 MdiControlReg;
|
|
UINT16 MdiStatusReg;
|
|
BOOLEAN FoundPhy1;
|
|
UINT8 ReNegotiateTime;
|
|
|
|
eedata = (UINT16 *) (&AdapterInfo->NVData[0]);
|
|
|
|
FoundPhy1 = FALSE;
|
|
ReNegotiateTime = 35;
|
|
//
|
|
// EEPROM word [6] contains the Primary PHY record in which the least 3 bits
|
|
// indicate the PHY address
|
|
// and word [7] contains the secondary PHY record
|
|
//
|
|
AdapterInfo->PhyRecord[0] = eedata[6];
|
|
AdapterInfo->PhyRecord[1] = eedata[7];
|
|
AdapterInfo->PhyAddress = (UINT8) (AdapterInfo->PhyRecord[0] & 7);
|
|
|
|
//
|
|
// Check for a phy address over-ride of 32 which indicates force use of 82503
|
|
// not detecting the link in this case
|
|
//
|
|
if (AdapterInfo->PhyAddress == 32) {
|
|
//
|
|
// 503 interface over-ride
|
|
// Record the current speed and duplex. We will be in half duplex
|
|
// mode unless the user used the force full duplex over-ride.
|
|
//
|
|
AdapterInfo->LinkSpeed = 10;
|
|
return (TRUE);
|
|
}
|
|
|
|
//
|
|
// If the Phy Address is between 1-31 then we must first look for phy 1,
|
|
// at that address.
|
|
//
|
|
if ((AdapterInfo->PhyAddress > 0) && (AdapterInfo->PhyAddress < 32)) {
|
|
|
|
//
|
|
// Read the MDI control and status registers at phy 1
|
|
// and check if we found a valid phy
|
|
//
|
|
MdiRead (
|
|
AdapterInfo,
|
|
MDI_CONTROL_REG,
|
|
AdapterInfo->PhyAddress,
|
|
&MdiControlReg
|
|
);
|
|
|
|
MdiRead (
|
|
AdapterInfo,
|
|
MDI_STATUS_REG,
|
|
AdapterInfo->PhyAddress,
|
|
&MdiStatusReg
|
|
);
|
|
|
|
if (!((MdiControlReg == 0xffff) ||
|
|
((MdiStatusReg == 0) && (MdiControlReg == 0)))) {
|
|
|
|
//
|
|
// we have a valid phy1
|
|
// Read the status register again because of sticky bits
|
|
//
|
|
FoundPhy1 = TRUE;
|
|
MdiRead (
|
|
AdapterInfo,
|
|
MDI_STATUS_REG,
|
|
AdapterInfo->PhyAddress,
|
|
&MdiStatusReg
|
|
);
|
|
|
|
//
|
|
// If there is a valid link then use this Phy.
|
|
//
|
|
if (MdiStatusReg & MDI_SR_LINK_STATUS) {
|
|
return (SetupPhy(AdapterInfo));
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Next try to detect a PHY at address 0x00 because there was no Phy 1,
|
|
// or Phy 1 didn't have link, or we had a phy 0 over-ride
|
|
//
|
|
|
|
//
|
|
// Read the MDI control and status registers at phy 0
|
|
//
|
|
MdiRead (AdapterInfo, MDI_CONTROL_REG, 0, &MdiControlReg);
|
|
MdiRead (AdapterInfo, MDI_STATUS_REG, 0, &MdiStatusReg);
|
|
|
|
//
|
|
// check if we found a valid phy 0
|
|
//
|
|
if (((MdiControlReg == 0xffff) ||
|
|
((MdiStatusReg == 0) && (MdiControlReg == 0)))) {
|
|
|
|
//
|
|
// we don't have a valid phy at address 0
|
|
// if phy address was forced to 0, then error out because we
|
|
// didn't find a phy at that address
|
|
//
|
|
if (AdapterInfo->PhyAddress == 0x0000) {
|
|
return (FALSE);
|
|
} else {
|
|
//
|
|
// at this point phy1 does not have link and there is no phy 0 at all
|
|
// if we are forced to detect the cable, error out here!
|
|
//
|
|
if (AdapterInfo->CableDetect != 0) {
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
if (FoundPhy1) {
|
|
//
|
|
// no phy 0, but there is a phy 1 (no link I guess), so use phy 1
|
|
//
|
|
return SetupPhy (AdapterInfo);
|
|
} else {
|
|
//
|
|
// didn't find phy 0 or phy 1, so assume a 503 interface
|
|
//
|
|
AdapterInfo->PhyAddress = 32;
|
|
|
|
//
|
|
// Record the current speed and duplex. We'll be in half duplex
|
|
// mode unless the user used the force full duplex over-ride.
|
|
//
|
|
AdapterInfo->LinkSpeed = 10;
|
|
return (TRUE);
|
|
}
|
|
}
|
|
} else {
|
|
//
|
|
// We have a valid phy at address 0. If phy 0 has a link then we use
|
|
// phy 0. If Phy 0 doesn't have a link then we use Phy 1 (no link)
|
|
// if phy 1 is present, or phy 0 if phy 1 is not present
|
|
// If phy 1 was present, then we must isolate phy 1 before we enable
|
|
// phy 0 to see if Phy 0 has a link.
|
|
//
|
|
if (FoundPhy1) {
|
|
//
|
|
// isolate phy 1
|
|
//
|
|
MdiWrite (
|
|
AdapterInfo,
|
|
MDI_CONTROL_REG,
|
|
AdapterInfo->PhyAddress,
|
|
MDI_CR_ISOLATE
|
|
);
|
|
|
|
//
|
|
// wait 100 microseconds for the phy to isolate.
|
|
//
|
|
DelayIt (AdapterInfo, 100);
|
|
}
|
|
|
|
//
|
|
// Since this Phy is at address 0, we must enable it. So clear
|
|
// the isolate bit, and set the auto-speed select bit
|
|
//
|
|
MdiWrite (
|
|
AdapterInfo,
|
|
MDI_CONTROL_REG,
|
|
0,
|
|
MDI_CR_AUTO_SELECT
|
|
);
|
|
|
|
//
|
|
// wait 100 microseconds for the phy to be enabled.
|
|
//
|
|
DelayIt (AdapterInfo, 100);
|
|
|
|
//
|
|
// restart the auto-negotion process
|
|
//
|
|
MdiWrite (
|
|
AdapterInfo,
|
|
MDI_CONTROL_REG,
|
|
0,
|
|
MDI_CR_RESTART_AUTO_NEG | MDI_CR_AUTO_SELECT
|
|
);
|
|
|
|
//
|
|
// wait no more than 3.5 seconds for auto-negotiation to complete
|
|
//
|
|
while (ReNegotiateTime) {
|
|
//
|
|
// Read the status register twice because of sticky bits
|
|
//
|
|
MdiRead (AdapterInfo, MDI_STATUS_REG, 0, &MdiStatusReg);
|
|
MdiRead (AdapterInfo, MDI_STATUS_REG, 0, &MdiStatusReg);
|
|
|
|
if (MdiStatusReg & MDI_SR_AUTO_NEG_COMPLETE) {
|
|
break;
|
|
}
|
|
|
|
DelayIt (AdapterInfo, 100);
|
|
ReNegotiateTime--;
|
|
}
|
|
|
|
//
|
|
// Read the status register again because of sticky bits
|
|
//
|
|
MdiRead (AdapterInfo, MDI_STATUS_REG, 0, &MdiStatusReg);
|
|
|
|
//
|
|
// If the link was not set
|
|
//
|
|
if ((MdiStatusReg & MDI_SR_LINK_STATUS) == 0) {
|
|
//
|
|
// PHY1 does not have a link and phy 0 does not have a link
|
|
// do not proceed if we need to detect the link!
|
|
//
|
|
if (AdapterInfo->CableDetect != 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// the link wasn't set, so use phy 1 if phy 1 was present
|
|
//
|
|
if (FoundPhy1) {
|
|
//
|
|
// isolate phy 0
|
|
//
|
|
MdiWrite (AdapterInfo, MDI_CONTROL_REG, 0, MDI_CR_ISOLATE);
|
|
|
|
//
|
|
// wait 100 microseconds for the phy to isolate.
|
|
//
|
|
DelayIt (AdapterInfo, 100);
|
|
|
|
//
|
|
// Now re-enable PHY 1
|
|
//
|
|
MdiWrite (
|
|
AdapterInfo,
|
|
MDI_CONTROL_REG,
|
|
AdapterInfo->PhyAddress,
|
|
MDI_CR_AUTO_SELECT
|
|
);
|
|
|
|
//
|
|
// wait 100 microseconds for the phy to be enabled
|
|
//
|
|
DelayIt (AdapterInfo, 100);
|
|
|
|
//
|
|
// restart the auto-negotion process
|
|
//
|
|
MdiWrite (
|
|
AdapterInfo,
|
|
MDI_CONTROL_REG,
|
|
AdapterInfo->PhyAddress,
|
|
MDI_CR_RESTART_AUTO_NEG | MDI_CR_AUTO_SELECT
|
|
);
|
|
|
|
//
|
|
// Don't wait for it to complete (we didn't have link earlier)
|
|
//
|
|
return (SetupPhy (AdapterInfo));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Definitely using Phy 0
|
|
//
|
|
AdapterInfo->PhyAddress = 0;
|
|
return (SetupPhy(AdapterInfo));
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
This routine will setup phy 1 or phy 0 so that it is configured
|
|
to match a speed and duplex over-ride option. If speed or
|
|
duplex mode is not explicitly specified in the registry, the
|
|
driver will skip the speed and duplex over-ride code, and
|
|
assume the adapter is automatically setting the line speed, and
|
|
the duplex mode. At the end of this routine, any truly Phy
|
|
specific code will be executed (each Phy has its own quirks,
|
|
and some require that certain special bits are set).
|
|
NOTE: The driver assumes that SPEED and FORCEFDX are specified at the
|
|
same time. If FORCEDPX is set without speed being set, the driver
|
|
will encouter a fatal error and log a message into the event viewer.
|
|
|
|
@param AdapterInfo pointer to the structure that contains
|
|
the NIC's context.
|
|
|
|
@retval TRUE If the phy could be configured correctly
|
|
@retval FALSE If the phy couldn't be configured
|
|
correctly, because an unsupported
|
|
over-ride option was used
|
|
|
|
**/
|
|
BOOLEAN
|
|
SetupPhy (
|
|
IN NIC_DATA_INSTANCE *AdapterInfo
|
|
)
|
|
{
|
|
UINT16 MdiControlReg;
|
|
UINT16 MdiStatusReg;
|
|
UINT16 MdiIdLowReg;
|
|
UINT16 MdiIdHighReg;
|
|
UINT16 MdiMiscReg;
|
|
UINT32 PhyId;
|
|
BOOLEAN ForcePhySetting;
|
|
|
|
ForcePhySetting = FALSE;
|
|
|
|
//
|
|
// If we are NOT forcing a setting for line speed or full duplex, then
|
|
// we won't force a link setting, and we'll jump down to the phy
|
|
// specific code.
|
|
//
|
|
if (((AdapterInfo->LinkSpeedReq) || (AdapterInfo->DuplexReq))) {
|
|
//
|
|
// Find out what kind of technology this Phy is capable of.
|
|
//
|
|
MdiRead (
|
|
AdapterInfo,
|
|
MDI_STATUS_REG,
|
|
AdapterInfo->PhyAddress,
|
|
&MdiStatusReg
|
|
);
|
|
|
|
//
|
|
// Read the MDI control register at our phy
|
|
//
|
|
MdiRead (
|
|
AdapterInfo,
|
|
MDI_CONTROL_REG,
|
|
AdapterInfo->PhyAddress,
|
|
&MdiControlReg
|
|
);
|
|
|
|
//
|
|
// Now check the validity of our forced option. If the force option is
|
|
// valid, then force the setting. If the force option is not valid,
|
|
// we'll set a flag indicating that we should error out.
|
|
//
|
|
|
|
//
|
|
// If speed is forced to 10mb
|
|
//
|
|
if (AdapterInfo->LinkSpeedReq == 10) {
|
|
//
|
|
// If half duplex is forced
|
|
//
|
|
if ((AdapterInfo->DuplexReq & PXE_FORCE_HALF_DUPLEX) != 0) {
|
|
if (MdiStatusReg & MDI_SR_10T_HALF_DPX) {
|
|
|
|
MdiControlReg &= ~(MDI_CR_10_100 | MDI_CR_AUTO_SELECT | MDI_CR_FULL_HALF);
|
|
ForcePhySetting = TRUE;
|
|
}
|
|
} else if ((AdapterInfo->DuplexReq & PXE_FORCE_FULL_DUPLEX) != 0) {
|
|
|
|
//
|
|
// If full duplex is forced
|
|
//
|
|
if (MdiStatusReg & MDI_SR_10T_FULL_DPX) {
|
|
|
|
MdiControlReg &= ~(MDI_CR_10_100 | MDI_CR_AUTO_SELECT);
|
|
MdiControlReg |= MDI_CR_FULL_HALF;
|
|
ForcePhySetting = TRUE;
|
|
}
|
|
} else {
|
|
//
|
|
// If auto duplex (we actually set phy to 1/2)
|
|
//
|
|
if (MdiStatusReg & (MDI_SR_10T_FULL_DPX | MDI_SR_10T_HALF_DPX)) {
|
|
|
|
MdiControlReg &= ~(MDI_CR_10_100 | MDI_CR_AUTO_SELECT | MDI_CR_FULL_HALF);
|
|
ForcePhySetting = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// If speed is forced to 100mb
|
|
//
|
|
else if (AdapterInfo->LinkSpeedReq == 100) {
|
|
//
|
|
// If half duplex is forced
|
|
//
|
|
if ((AdapterInfo->DuplexReq & PXE_FORCE_HALF_DUPLEX) != 0) {
|
|
if (MdiStatusReg & (MDI_SR_TX_HALF_DPX | MDI_SR_T4_CAPABLE)) {
|
|
|
|
MdiControlReg &= ~(MDI_CR_AUTO_SELECT | MDI_CR_FULL_HALF);
|
|
MdiControlReg |= MDI_CR_10_100;
|
|
ForcePhySetting = TRUE;
|
|
}
|
|
} else if ((AdapterInfo->DuplexReq & PXE_FORCE_FULL_DUPLEX) != 0) {
|
|
//
|
|
// If full duplex is forced
|
|
//
|
|
if (MdiStatusReg & MDI_SR_TX_FULL_DPX) {
|
|
MdiControlReg &= ~MDI_CR_AUTO_SELECT;
|
|
MdiControlReg |= (MDI_CR_10_100 | MDI_CR_FULL_HALF);
|
|
ForcePhySetting = TRUE;
|
|
}
|
|
} else {
|
|
//
|
|
// If auto duplex (we set phy to 1/2)
|
|
//
|
|
if (MdiStatusReg & (MDI_SR_TX_HALF_DPX | MDI_SR_T4_CAPABLE)) {
|
|
|
|
MdiControlReg &= ~(MDI_CR_AUTO_SELECT | MDI_CR_FULL_HALF);
|
|
MdiControlReg |= MDI_CR_10_100;
|
|
ForcePhySetting = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!ForcePhySetting) {
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Write the MDI control register with our new Phy configuration
|
|
//
|
|
MdiWrite (
|
|
AdapterInfo,
|
|
MDI_CONTROL_REG,
|
|
AdapterInfo->PhyAddress,
|
|
MdiControlReg
|
|
);
|
|
|
|
//
|
|
// wait 100 milliseconds for auto-negotiation to complete
|
|
//
|
|
DelayIt (AdapterInfo, 100);
|
|
}
|
|
|
|
//
|
|
// Find out specifically what Phy this is. We do this because for certain
|
|
// phys there are specific bits that must be set so that the phy and the
|
|
// 82557 work together properly.
|
|
//
|
|
|
|
MdiRead (
|
|
AdapterInfo,
|
|
PHY_ID_REG_1,
|
|
AdapterInfo->PhyAddress,
|
|
&MdiIdLowReg
|
|
);
|
|
MdiRead (
|
|
AdapterInfo,
|
|
PHY_ID_REG_2,
|
|
AdapterInfo->PhyAddress,
|
|
&MdiIdHighReg
|
|
);
|
|
|
|
PhyId = ((UINT32) MdiIdLowReg | ((UINT32) MdiIdHighReg << 16));
|
|
|
|
//
|
|
// And out the revsion field of the Phy ID so that we'll be able to detect
|
|
// future revs of the same Phy.
|
|
//
|
|
PhyId &= PHY_MODEL_REV_ID_MASK;
|
|
|
|
//
|
|
// Handle the National TX
|
|
//
|
|
if (PhyId == PHY_NSC_TX) {
|
|
|
|
MdiRead (
|
|
AdapterInfo,
|
|
NSC_CONG_CONTROL_REG,
|
|
AdapterInfo->PhyAddress,
|
|
&MdiMiscReg
|
|
);
|
|
|
|
MdiMiscReg |= (NSC_TX_CONG_TXREADY | NSC_TX_CONG_F_CONNECT);
|
|
|
|
MdiWrite (
|
|
AdapterInfo,
|
|
NSC_CONG_CONTROL_REG,
|
|
AdapterInfo->PhyAddress,
|
|
MdiMiscReg
|
|
);
|
|
}
|
|
|
|
FindPhySpeedAndDpx (AdapterInfo, PhyId);
|
|
|
|
//
|
|
// We put a hardware fix on to our adapters to work-around the PHY_100 errata
|
|
// described below. The following code is only compiled in, if we wanted
|
|
// to attempt a software workaround to the PHY_100 A/B step problem.
|
|
//
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
/**
|
|
This routine will figure out what line speed and duplex mode
|
|
the PHY is currently using.
|
|
|
|
@param AdapterInfo pointer to the structure that contains
|
|
the NIC's context.
|
|
@param PhyId The ID of the PHY in question.
|
|
|
|
@return NOTHING
|
|
|
|
**/
|
|
VOID
|
|
FindPhySpeedAndDpx (
|
|
IN NIC_DATA_INSTANCE *AdapterInfo,
|
|
IN UINT32 PhyId
|
|
)
|
|
{
|
|
UINT16 MdiStatusReg;
|
|
UINT16 MdiMiscReg;
|
|
UINT16 MdiOwnAdReg;
|
|
UINT16 MdiLinkPartnerAdReg;
|
|
|
|
//
|
|
// If there was a speed and/or duplex override, then set our current
|
|
// value accordingly
|
|
//
|
|
AdapterInfo->LinkSpeed = AdapterInfo->LinkSpeedReq;
|
|
AdapterInfo->Duplex = (UINT8) ((AdapterInfo->DuplexReq & PXE_FORCE_FULL_DUPLEX) ?
|
|
FULL_DUPLEX : HALF_DUPLEX);
|
|
|
|
//
|
|
// If speed and duplex were forced, then we know our current settings, so
|
|
// we'll just return. Otherwise, we'll need to figure out what NWAY set
|
|
// us to.
|
|
//
|
|
if (AdapterInfo->LinkSpeed && AdapterInfo->Duplex) {
|
|
return ;
|
|
|
|
}
|
|
//
|
|
// If we didn't have a valid link, then we'll assume that our current
|
|
// speed is 10mb half-duplex.
|
|
//
|
|
|
|
//
|
|
// Read the status register twice because of sticky bits
|
|
//
|
|
MdiRead (
|
|
AdapterInfo,
|
|
MDI_STATUS_REG,
|
|
AdapterInfo->PhyAddress,
|
|
&MdiStatusReg
|
|
);
|
|
MdiRead (
|
|
AdapterInfo,
|
|
MDI_STATUS_REG,
|
|
AdapterInfo->PhyAddress,
|
|
&MdiStatusReg
|
|
);
|
|
|
|
//
|
|
// If there wasn't a valid link then use default speed & duplex
|
|
//
|
|
if (!(MdiStatusReg & MDI_SR_LINK_STATUS)) {
|
|
|
|
AdapterInfo->LinkSpeed = 10;
|
|
AdapterInfo->Duplex = HALF_DUPLEX;
|
|
return ;
|
|
}
|
|
|
|
//
|
|
// If this is an Intel PHY (a T4 PHY_100 or a TX PHY_TX), then read bits
|
|
// 1 and 0 of extended register 0, to get the current speed and duplex
|
|
// settings.
|
|
//
|
|
if ((PhyId == PHY_100_A) || (PhyId == PHY_100_C) || (PhyId == PHY_TX_ID)) {
|
|
//
|
|
// Read extended register 0
|
|
//
|
|
MdiRead (
|
|
AdapterInfo,
|
|
EXTENDED_REG_0,
|
|
AdapterInfo->PhyAddress,
|
|
&MdiMiscReg
|
|
);
|
|
|
|
//
|
|
// Get current speed setting
|
|
//
|
|
if (MdiMiscReg & PHY_100_ER0_SPEED_INDIC) {
|
|
AdapterInfo->LinkSpeed = 100;
|
|
} else {
|
|
AdapterInfo->LinkSpeed = 10;
|
|
}
|
|
|
|
//
|
|
// Get current duplex setting -- if bit is set then FDX is enabled
|
|
//
|
|
if (MdiMiscReg & PHY_100_ER0_FDX_INDIC) {
|
|
AdapterInfo->Duplex = FULL_DUPLEX;
|
|
} else {
|
|
AdapterInfo->Duplex = HALF_DUPLEX;
|
|
}
|
|
|
|
return ;
|
|
}
|
|
//
|
|
// Read our link partner's advertisement register
|
|
//
|
|
MdiRead (
|
|
AdapterInfo,
|
|
AUTO_NEG_LINK_PARTNER_REG,
|
|
AdapterInfo->PhyAddress,
|
|
&MdiLinkPartnerAdReg
|
|
);
|
|
|
|
//
|
|
// See if Auto-Negotiation was complete (bit 5, reg 1)
|
|
//
|
|
MdiRead (
|
|
AdapterInfo,
|
|
MDI_STATUS_REG,
|
|
AdapterInfo->PhyAddress,
|
|
&MdiStatusReg
|
|
);
|
|
|
|
//
|
|
// If a True NWAY connection was made, then we can detect speed/duplex by
|
|
// ANDing our adapter's advertised abilities with our link partner's
|
|
// advertised ablilities, and then assuming that the highest common
|
|
// denominator was chosed by NWAY.
|
|
//
|
|
if ((MdiLinkPartnerAdReg & NWAY_LP_ABILITY) &&
|
|
(MdiStatusReg & MDI_SR_AUTO_NEG_COMPLETE)) {
|
|
|
|
//
|
|
// Read our advertisement register
|
|
//
|
|
MdiRead (
|
|
AdapterInfo,
|
|
AUTO_NEG_ADVERTISE_REG,
|
|
AdapterInfo->PhyAddress,
|
|
&MdiOwnAdReg
|
|
);
|
|
|
|
//
|
|
// AND the two advertisement registers together, and get rid of any
|
|
// extraneous bits.
|
|
//
|
|
MdiOwnAdReg = (UINT16) (MdiOwnAdReg & (MdiLinkPartnerAdReg & NWAY_LP_ABILITY));
|
|
|
|
//
|
|
// Get speed setting
|
|
//
|
|
if (MdiOwnAdReg & (NWAY_AD_TX_HALF_DPX | NWAY_AD_TX_FULL_DPX | NWAY_AD_T4_CAPABLE)) {
|
|
AdapterInfo->LinkSpeed = 100;
|
|
} else {
|
|
AdapterInfo->LinkSpeed = 10;
|
|
}
|
|
|
|
//
|
|
// Get duplex setting -- use priority resolution algorithm
|
|
//
|
|
if (MdiOwnAdReg & (NWAY_AD_T4_CAPABLE)) {
|
|
AdapterInfo->Duplex = HALF_DUPLEX;
|
|
return ;
|
|
} else if (MdiOwnAdReg & (NWAY_AD_TX_FULL_DPX)) {
|
|
AdapterInfo->Duplex = FULL_DUPLEX;
|
|
return ;
|
|
} else if (MdiOwnAdReg & (NWAY_AD_TX_HALF_DPX)) {
|
|
AdapterInfo->Duplex = HALF_DUPLEX;
|
|
return ;
|
|
} else if (MdiOwnAdReg & (NWAY_AD_10T_FULL_DPX)) {
|
|
AdapterInfo->Duplex = FULL_DUPLEX;
|
|
return ;
|
|
} else {
|
|
AdapterInfo->Duplex = HALF_DUPLEX;
|
|
return ;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we are connected to a dumb (non-NWAY) repeater or hub, and the line
|
|
// speed was determined automatically by parallel detection, then we have
|
|
// no way of knowing exactly what speed the PHY is set to unless that PHY
|
|
// has a propietary register which indicates speed in this situation. The
|
|
// NSC TX PHY does have such a register. Also, since NWAY didn't establish
|
|
// the connection, the duplex setting should HALF duplex.
|
|
//
|
|
AdapterInfo->Duplex = HALF_DUPLEX;
|
|
|
|
if (PhyId == PHY_NSC_TX) {
|
|
//
|
|
// Read register 25 to get the SPEED_10 bit
|
|
//
|
|
MdiRead (
|
|
AdapterInfo,
|
|
NSC_SPEED_IND_REG,
|
|
AdapterInfo->PhyAddress,
|
|
&MdiMiscReg
|
|
);
|
|
|
|
//
|
|
// If bit 6 was set then we're at 10mb
|
|
//
|
|
if (MdiMiscReg & NSC_TX_SPD_INDC_SPEED) {
|
|
AdapterInfo->LinkSpeed = 10;
|
|
} else {
|
|
AdapterInfo->LinkSpeed = 100;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we don't know what line speed we are set at, then we'll default to
|
|
// 10mbs
|
|
//
|
|
else {
|
|
AdapterInfo->LinkSpeed = 10;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
TODO: Add function description
|
|
|
|
@param AdapterInfo TODO: add argument description
|
|
|
|
@return TODO: add return values
|
|
|
|
**/
|
|
VOID
|
|
XmitWaitForCompletion (
|
|
NIC_DATA_INSTANCE *AdapterInfo
|
|
)
|
|
{
|
|
TxCB *TxPtr;
|
|
|
|
if (AdapterInfo->FreeCBCount == AdapterInfo->TxBufCnt) {
|
|
return ;
|
|
}
|
|
|
|
//
|
|
// used xmit cb list starts right after the free tail (ends before the
|
|
// free head ptr)
|
|
//
|
|
TxPtr = AdapterInfo->FreeTxTailPtr->NextTCBVirtualLinkPtr;
|
|
while (TxPtr != AdapterInfo->FreeTxHeadPtr) {
|
|
CommandWaitForCompletion (TxPtr, AdapterInfo);
|
|
SetFreeCB (AdapterInfo, TxPtr);
|
|
TxPtr = TxPtr->NextTCBVirtualLinkPtr;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
TODO: Add function description
|
|
|
|
@param cmd_ptr TODO: add argument description
|
|
@param AdapterInfo TODO: add argument description
|
|
|
|
@return TODO: add return values
|
|
|
|
**/
|
|
INT8
|
|
CommandWaitForCompletion (
|
|
TxCB *cmd_ptr,
|
|
NIC_DATA_INSTANCE *AdapterInfo
|
|
)
|
|
{
|
|
INT16 wait;
|
|
wait = 5000;
|
|
while ((cmd_ptr->cb_header.status == 0) && (--wait > 0)) {
|
|
DelayIt (AdapterInfo, 10);
|
|
}
|
|
|
|
if (cmd_ptr->cb_header.status == 0) {
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
TODO: Add function description
|
|
|
|
@param AdapterInfo TODO: add argument description
|
|
|
|
@return TODO: add return values
|
|
|
|
**/
|
|
INT8
|
|
SoftwareReset (
|
|
NIC_DATA_INSTANCE *AdapterInfo
|
|
)
|
|
{
|
|
UINT8 tco_stat;
|
|
UINT16 wait;
|
|
|
|
tco_stat = 0;
|
|
|
|
//
|
|
// Reset the chip: stop Tx and Rx processes and clear counters.
|
|
// This takes less than 10usec and will easily finish before the next
|
|
// action.
|
|
//
|
|
|
|
OutLong (AdapterInfo, PORT_RESET, AdapterInfo->ioaddr + SCBPort);
|
|
//
|
|
// wait for 5 milli seconds here!
|
|
//
|
|
DelayIt (AdapterInfo, 5000);
|
|
//
|
|
// TCO Errata work around for 559s only
|
|
// -----------------------------------------------------------------------------------
|
|
// TCO Workaround Code
|
|
// haifa workaround
|
|
// -----------------------------------------------------------------------------------
|
|
// 1. Issue SW-RST ^^^ (already done above)
|
|
// 2. Issue a redundant Set CU Base CMD immediately
|
|
// Do not set the General Pointer before the Set CU Base cycle
|
|
// Do not check the SCB CMD before the Set CU Base cycle
|
|
// 3. Wait for the SCB-CMD to be cleared
|
|
// this indicates the transition to post-driver
|
|
// 4. Poll the TCO-Req bit in the PMDR to be cleared
|
|
// this indicates the tco activity has stopped for real
|
|
// 5. Proceed with the nominal Driver Init:
|
|
// Actual Set CU & RU Base ...
|
|
//
|
|
// Check for ICH2 device ID. If this is an ICH2,
|
|
// do the TCO workaround code.
|
|
//
|
|
if (AdapterInfo->VendorID == D102_DEVICE_ID ||
|
|
AdapterInfo->VendorID == ICH3_DEVICE_ID_1 ||
|
|
AdapterInfo->VendorID == ICH3_DEVICE_ID_2 ||
|
|
AdapterInfo->VendorID == ICH3_DEVICE_ID_3 ||
|
|
AdapterInfo->VendorID == ICH3_DEVICE_ID_4 ||
|
|
AdapterInfo->VendorID == ICH3_DEVICE_ID_5 ||
|
|
AdapterInfo->VendorID == ICH3_DEVICE_ID_6 ||
|
|
AdapterInfo->VendorID == ICH3_DEVICE_ID_7 ||
|
|
AdapterInfo->VendorID == ICH3_DEVICE_ID_8 ||
|
|
AdapterInfo->RevID >= 8) { // do the TCO fix
|
|
//
|
|
// donot load the scb pointer but just give load_cu cmd.
|
|
//
|
|
OutByte (AdapterInfo, CU_CMD_BASE, AdapterInfo->ioaddr + SCBCmd);
|
|
//
|
|
// wait for command to be accepted.
|
|
//
|
|
wait_for_cmd_done (AdapterInfo->ioaddr + SCBCmd);
|
|
//
|
|
// read PMDR register and check bit 1 in it to see if TCO is active
|
|
//
|
|
|
|
//
|
|
// wait for 5 milli seconds
|
|
//
|
|
wait = 5000;
|
|
while (wait) {
|
|
tco_stat = InByte (AdapterInfo, AdapterInfo->ioaddr + 0x1b);
|
|
if ((tco_stat & 2) == 0) {
|
|
//
|
|
// is the activity bit clear??
|
|
//
|
|
break;
|
|
}
|
|
|
|
wait--;
|
|
DelayIt (AdapterInfo, 1);
|
|
}
|
|
|
|
if ((tco_stat & 2) != 0) {
|
|
//
|
|
// not zero??
|
|
//
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
TODO: Add function description
|
|
|
|
@param AdapterInfo TODO: add argument description
|
|
|
|
@return TODO: add return values
|
|
|
|
**/
|
|
UINT8
|
|
SelectiveReset (
|
|
IN NIC_DATA_INSTANCE *AdapterInfo
|
|
)
|
|
{
|
|
UINT16 wait;
|
|
UINT32 stat;
|
|
|
|
wait = 10;
|
|
stat = 0;
|
|
OutLong (AdapterInfo, POR_SELECTIVE_RESET, AdapterInfo->ioaddr + SCBPort);
|
|
//
|
|
// wait for this to complete
|
|
//
|
|
|
|
//
|
|
// wait for 2 milli seconds here!
|
|
//
|
|
DelayIt (AdapterInfo, 2000);
|
|
while (wait > 0) {
|
|
wait--;
|
|
stat = InLong (AdapterInfo, AdapterInfo->ioaddr + SCBPort);
|
|
if (stat == 0) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// wait for 1 milli second
|
|
//
|
|
DelayIt (AdapterInfo, 1000);
|
|
}
|
|
|
|
if (stat != 0) {
|
|
return PXE_STATCODE_DEVICE_FAILURE;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
TODO: Add function description
|
|
|
|
@param AdapterInfo TODO: add argument description
|
|
|
|
@return TODO: add return values
|
|
|
|
**/
|
|
UINT16
|
|
InitializeChip (
|
|
IN NIC_DATA_INSTANCE *AdapterInfo
|
|
)
|
|
{
|
|
UINT16 ret_val;
|
|
if (SoftwareReset (AdapterInfo) != 0) {
|
|
return PXE_STATCODE_DEVICE_FAILURE;
|
|
}
|
|
|
|
//
|
|
// disable interrupts
|
|
//
|
|
OutWord (AdapterInfo, INT_MASK, AdapterInfo->ioaddr + SCBCmd);
|
|
|
|
//
|
|
// Load the base registers with 0s (we will give the complete address as
|
|
// offset later when we issue any command
|
|
//
|
|
if ((ret_val = Load_Base_Regs (AdapterInfo)) != 0) {
|
|
return ret_val;
|
|
}
|
|
|
|
if ((ret_val = SetupCBlink (AdapterInfo)) != 0) {
|
|
return ret_val;
|
|
}
|
|
|
|
if ((ret_val = SetupReceiveQueues (AdapterInfo)) != 0) {
|
|
return ret_val;
|
|
}
|
|
|
|
//
|
|
// detect the PHY only if we need to detect the cable as requested by the
|
|
// initialize parameters
|
|
//
|
|
AdapterInfo->PhyAddress = 0xFF;
|
|
|
|
if (AdapterInfo->CableDetect != 0) {
|
|
if (!PhyDetect (AdapterInfo)) {
|
|
return PXE_STATCODE_DEVICE_FAILURE;
|
|
}
|
|
}
|
|
|
|
if ((ret_val = E100bSetupIAAddr (AdapterInfo)) != 0) {
|
|
return ret_val;
|
|
}
|
|
|
|
if ((ret_val = Configure (AdapterInfo)) != 0) {
|
|
return ret_val;
|
|
}
|
|
|
|
return 0;
|
|
}
|