From 51ebae6b69f88c0e4b72202d7e97c5b280bd074a Mon Sep 17 00:00:00 2001 From: vanjeff Date: Fri, 3 Jul 2009 14:35:24 +0000 Subject: [PATCH] add UndiRuntimeDxe from MdeModulePkg to OptionRomPkg. git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@8738 6f19259b-4bc3-4df7-8a09-765794883524 --- OptionRomPkg/OptionRomPkg.dsc | 2 + OptionRomPkg/UndiRuntimeDxe/Decode.c | 1477 +++++++ OptionRomPkg/UndiRuntimeDxe/E100b.c | 3543 +++++++++++++++++ OptionRomPkg/UndiRuntimeDxe/E100b.h | 671 ++++ OptionRomPkg/UndiRuntimeDxe/Init.c | 1055 +++++ OptionRomPkg/UndiRuntimeDxe/Undi32.h | 361 ++ .../UndiRuntimeDxe/UndiRuntimeDxe.inf | 72 + 7 files changed, 7181 insertions(+) create mode 100644 OptionRomPkg/UndiRuntimeDxe/Decode.c create mode 100644 OptionRomPkg/UndiRuntimeDxe/E100b.c create mode 100644 OptionRomPkg/UndiRuntimeDxe/E100b.h create mode 100644 OptionRomPkg/UndiRuntimeDxe/Init.c create mode 100644 OptionRomPkg/UndiRuntimeDxe/Undi32.h create mode 100644 OptionRomPkg/UndiRuntimeDxe/UndiRuntimeDxe.inf diff --git a/OptionRomPkg/OptionRomPkg.dsc b/OptionRomPkg/OptionRomPkg.dsc index 29ef5cd51c..df5f83ecff 100644 --- a/OptionRomPkg/OptionRomPkg.dsc +++ b/OptionRomPkg/OptionRomPkg.dsc @@ -55,6 +55,7 @@ PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf DevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf + UefiRuntimeLib|MdePkg/Library/UefiRuntimeLib/UefiRuntimeLib.inf ################################################################################ # @@ -101,4 +102,5 @@ [Components.common] OptionRomPkg/AtapiPassThruDxe/AtapiPassThruDxe.inf OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430Dxe.inf + OptionRomPkg/UndiRuntimeDxe/UndiRuntimeDxe.inf diff --git a/OptionRomPkg/UndiRuntimeDxe/Decode.c b/OptionRomPkg/UndiRuntimeDxe/Decode.c new file mode 100644 index 0000000000..670fa18d53 --- /dev/null +++ b/OptionRomPkg/UndiRuntimeDxe/Decode.c @@ -0,0 +1,1477 @@ +/** @file + Provides the basic UNID functions. + +Copyright (c) 2006 - 2007, Intel Corporation +All rights reserved. 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" + +// +// Global variables defined in this file +// +UNDI_CALL_TABLE api_table[PXE_OPCODE_LAST_VALID+1] = { \ + {PXE_CPBSIZE_NOT_USED,PXE_DBSIZE_NOT_USED,0, (UINT16)(ANY_STATE),UNDI_GetState },\ + {(UINT16)(DONT_CHECK),PXE_DBSIZE_NOT_USED,0,(UINT16)(ANY_STATE),UNDI_Start },\ + {PXE_CPBSIZE_NOT_USED,PXE_DBSIZE_NOT_USED,0,MUST_BE_STARTED,UNDI_Stop },\ + {PXE_CPBSIZE_NOT_USED,sizeof(PXE_DB_GET_INIT_INFO),0,MUST_BE_STARTED, UNDI_GetInitInfo },\ + {PXE_CPBSIZE_NOT_USED,sizeof(PXE_DB_GET_CONFIG_INFO),0,MUST_BE_STARTED, UNDI_GetConfigInfo },\ + {sizeof(PXE_CPB_INITIALIZE),(UINT16)(DONT_CHECK),(UINT16)(DONT_CHECK),MUST_BE_STARTED,UNDI_Initialize },\ + {PXE_CPBSIZE_NOT_USED,PXE_DBSIZE_NOT_USED,(UINT16)(DONT_CHECK), MUST_BE_INITIALIZED,UNDI_Reset },\ + {PXE_CPBSIZE_NOT_USED,PXE_DBSIZE_NOT_USED,0, MUST_BE_INITIALIZED,UNDI_Shutdown },\ + {PXE_CPBSIZE_NOT_USED,PXE_DBSIZE_NOT_USED,(UINT16)(DONT_CHECK), MUST_BE_INITIALIZED,UNDI_Interrupt },\ + {(UINT16)(DONT_CHECK),(UINT16)(DONT_CHECK),(UINT16)(DONT_CHECK), MUST_BE_INITIALIZED, UNDI_RecFilter },\ + {(UINT16)(DONT_CHECK),(UINT16)(DONT_CHECK),(UINT16)(DONT_CHECK), MUST_BE_INITIALIZED, UNDI_StnAddr },\ + {PXE_CPBSIZE_NOT_USED, (UINT16)(DONT_CHECK), (UINT16)(DONT_CHECK), MUST_BE_INITIALIZED, UNDI_Statistics },\ + {sizeof(PXE_CPB_MCAST_IP_TO_MAC),sizeof(PXE_DB_MCAST_IP_TO_MAC), (UINT16)(DONT_CHECK),MUST_BE_INITIALIZED, UNDI_ip2mac },\ + {(UINT16)(DONT_CHECK),(UINT16)(DONT_CHECK),(UINT16)(DONT_CHECK), MUST_BE_INITIALIZED, UNDI_NVData },\ + {PXE_CPBSIZE_NOT_USED,(UINT16)(DONT_CHECK),(UINT16)(DONT_CHECK), MUST_BE_INITIALIZED, UNDI_Status },\ + {(UINT16)(DONT_CHECK),PXE_DBSIZE_NOT_USED,(UINT16)(DONT_CHECK), MUST_BE_INITIALIZED, UNDI_FillHeader },\ + {(UINT16)(DONT_CHECK),PXE_DBSIZE_NOT_USED,(UINT16)(DONT_CHECK), MUST_BE_INITIALIZED, UNDI_Transmit },\ + {sizeof(PXE_CPB_RECEIVE),sizeof(PXE_DB_RECEIVE),0,MUST_BE_INITIALIZED, UNDI_Receive } \ +}; + +// +// end of global variables +// + + +/** + This routine determines the operational state of the UNDI. It updates the state flags in the + Command Descriptor Block based on information derived from the AdapterInfo instance data. + To ensure the command has completed successfully, CdbPtr->StatCode will contain the result of + the command execution. + The CdbPtr->StatFlags will contain a STOPPED, STARTED, or INITIALIZED state once the command + has successfully completed. + Keep in mind the AdapterInfo->State is the active state of the adapter (based on software + interrogation), and the CdbPtr->StateFlags is the passed back information that is reflected + to the caller of the UNDI API. + + @param CdbPtr Pointer to the command descriptor block. + @param AdapterInfo Pointer to the NIC data structure information which + the UNDI driver is layering on.. + + @return None + +**/ +VOID +UNDI_GetState ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ) +{ + CdbPtr->StatFlags = (PXE_STATFLAGS) (CdbPtr->StatFlags | AdapterInfo->State); + return ; +} + + +/** + This routine is used to change the operational state of the UNDI from stopped to started. + It will do this as long as the adapter's state is PXE_STATFLAGS_GET_STATE_STOPPED, otherwise + the CdbPtr->StatFlags will reflect a command failure, and the CdbPtr->StatCode will reflect the + UNDI as having already been started. + This routine is modified to reflect the undi 1.1 specification changes. The + changes in the spec are mainly in the callback routines, the new spec adds + 3 more callbacks and a unique id. + Since this UNDI supports both old and new undi specifications, + The NIC's data structure is filled in with the callback routines (depending + on the version) pointed to in the caller's CpbPtr. This seeds the Delay, + Virt2Phys, Block, and Mem_IO for old and new versions and Map_Mem, UnMap_Mem + and Sync_Mem routines and a unique id variable for the new version. + This is the function which an external entity (SNP, O/S, etc) would call + to provide it's I/O abstraction to the UNDI. + It's final action is to change the AdapterInfo->State to PXE_STATFLAGS_GET_STATE_STARTED. + + @param CdbPtr Pointer to the command descriptor block. + @param AdapterInfo Pointer to the NIC data structure information which + the UNDI driver is layering on.. + + @return None + +**/ +VOID +UNDI_Start ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ) +{ + PXE_CPB_START_30 *CpbPtr; + PXE_CPB_START_31 *CpbPtr_31; + + // + // check if it is already started. + // + if (AdapterInfo->State != PXE_STATFLAGS_GET_STATE_STOPPED) { + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + CdbPtr->StatCode = PXE_STATCODE_ALREADY_STARTED; + return ; + } + + if (CdbPtr->CPBsize != sizeof(PXE_CPB_START_30) && + CdbPtr->CPBsize != sizeof(PXE_CPB_START_31)) { + + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + CdbPtr->StatCode = PXE_STATCODE_INVALID_CDB; + return ; + } + + CpbPtr = (PXE_CPB_START_30 *) (UINTN) (CdbPtr->CPBaddr); + CpbPtr_31 = (PXE_CPB_START_31 *) (UINTN) (CdbPtr->CPBaddr); + + if (AdapterInfo->VersionFlag == 0x30) { + AdapterInfo->Delay_30 = (bsptr_30) (UINTN) CpbPtr->Delay; + AdapterInfo->Virt2Phys_30 = (virtphys_30) (UINTN) CpbPtr->Virt2Phys; + AdapterInfo->Block_30 = (block_30) (UINTN) CpbPtr->Block; + // + // patch for old buggy 3.0 code: + // In EFI1.0 undi used to provide the full (absolute) I/O address to the + // i/o calls and SNP used to provide a callback that used GlobalIoFncs and + // everything worked fine! In EFI 1.1, UNDI is not using the full + // i/o or memory address to access the device, The base values for the i/o + // and memory address is abstracted by the device specific PciIoFncs and + // UNDI only uses the offset values. Since UNDI3.0 cannot provide any + // identification to SNP, SNP cannot use nic specific PciIoFncs callback! + // + // To fix this and make undi3.0 work with SNP in EFI1.1 we + // use a TmpMemIo function that is defined in init.c + // This breaks the runtime driver feature of undi, but what to do + // if we have to provide the 3.0 compatibility (including the 3.0 bugs) + // + // This TmpMemIo function also takes a UniqueId parameter + // (as in undi3.1 design) and so initialize the UniqueId as well here + // Note: AdapterInfo->Mem_Io_30 is just filled for consistency with other + // parameters but never used, we only use Mem_Io field in the In/Out routines + // inside e100b.c. + // + AdapterInfo->Mem_Io_30 = (mem_io_30) (UINTN) CpbPtr->Mem_IO; + AdapterInfo->Mem_Io = (mem_io) (UINTN) TmpMemIo; + AdapterInfo->Unique_ID = (UINT64) (UINTN) AdapterInfo; + + } else { + AdapterInfo->Delay = (bsptr) (UINTN) CpbPtr_31->Delay; + AdapterInfo->Virt2Phys = (virtphys) (UINTN) CpbPtr_31->Virt2Phys; + AdapterInfo->Block = (block) (UINTN) CpbPtr_31->Block; + AdapterInfo->Mem_Io = (mem_io) (UINTN) CpbPtr_31->Mem_IO; + + AdapterInfo->Map_Mem = (map_mem) (UINTN) CpbPtr_31->Map_Mem; + AdapterInfo->UnMap_Mem = (unmap_mem) (UINTN) CpbPtr_31->UnMap_Mem; + AdapterInfo->Sync_Mem = (sync_mem) (UINTN) CpbPtr_31->Sync_Mem; + AdapterInfo->Unique_ID = CpbPtr_31->Unique_ID; + } + + AdapterInfo->State = PXE_STATFLAGS_GET_STATE_STARTED; + + return ; +} + + +/** + This routine is used to change the operational state of the UNDI from started to stopped. + It will not do this if the adapter's state is PXE_STATFLAGS_GET_STATE_INITIALIZED, otherwise + the CdbPtr->StatFlags will reflect a command failure, and the CdbPtr->StatCode will reflect the + UNDI as having already not been shut down. + The NIC's data structure will have the Delay, Virt2Phys, and Block, pointers zero'd out.. + It's final action is to change the AdapterInfo->State to PXE_STATFLAGS_GET_STATE_STOPPED. + + @param CdbPtr Pointer to the command descriptor block. + @param AdapterInfo Pointer to the NIC data structure information which + the UNDI driver is layering on.. + + @return None + +**/ +VOID +UNDI_Stop ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ) +{ + if (AdapterInfo->State == PXE_STATFLAGS_GET_STATE_INITIALIZED) { + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + CdbPtr->StatCode = PXE_STATCODE_NOT_SHUTDOWN; + return ; + } + + AdapterInfo->Delay_30 = 0; + AdapterInfo->Virt2Phys_30 = 0; + AdapterInfo->Block_30 = 0; + + AdapterInfo->Delay = 0; + AdapterInfo->Virt2Phys = 0; + AdapterInfo->Block = 0; + + AdapterInfo->Map_Mem = 0; + AdapterInfo->UnMap_Mem = 0; + AdapterInfo->Sync_Mem = 0; + + AdapterInfo->State = PXE_STATFLAGS_GET_STATE_STOPPED; + + return ; +} + + +/** + This routine is used to retrieve the initialization information that is needed by drivers and + applications to initialize the UNDI. This will fill in data in the Data Block structure that is + pointed to by the caller's CdbPtr->DBaddr. The fields filled in are as follows: + MemoryRequired, FrameDataLen, LinkSpeeds[0-3], NvCount, NvWidth, MediaHeaderLen, HWaddrLen, + MCastFilterCnt, TxBufCnt, TxBufSize, RxBufCnt, RxBufSize, IFtype, Duplex, and LoopBack. + In addition, the CdbPtr->StatFlags ORs in that this NIC supports cable detection. (APRIORI knowledge) + + @param CdbPtr Pointer to the command descriptor block. + @param AdapterInfo Pointer to the NIC data structure information which + the UNDI driver is layering on.. + + @return None + +**/ +VOID +UNDI_GetInitInfo ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ) +{ + PXE_DB_GET_INIT_INFO *DbPtr; + + DbPtr = (PXE_DB_GET_INIT_INFO *) (UINTN) (CdbPtr->DBaddr); + + DbPtr->MemoryRequired = MEMORY_NEEDED; + DbPtr->FrameDataLen = PXE_MAX_TXRX_UNIT_ETHER; + DbPtr->LinkSpeeds[0] = 10; + DbPtr->LinkSpeeds[1] = 100; + DbPtr->LinkSpeeds[2] = DbPtr->LinkSpeeds[3] = 0; + DbPtr->NvCount = MAX_EEPROM_LEN; + DbPtr->NvWidth = 4; + DbPtr->MediaHeaderLen = PXE_MAC_HEADER_LEN_ETHER; + DbPtr->HWaddrLen = PXE_HWADDR_LEN_ETHER; + DbPtr->MCastFilterCnt = MAX_MCAST_ADDRESS_CNT; + + DbPtr->TxBufCnt = TX_BUFFER_COUNT; + DbPtr->TxBufSize = sizeof (TxCB); + DbPtr->RxBufCnt = RX_BUFFER_COUNT; + DbPtr->RxBufSize = sizeof (RxFD); + + DbPtr->IFtype = PXE_IFTYPE_ETHERNET; + DbPtr->SupportedDuplexModes = PXE_DUPLEX_ENABLE_FULL_SUPPORTED | + PXE_DUPLEX_FORCE_FULL_SUPPORTED; + DbPtr->SupportedLoopBackModes = PXE_LOOPBACK_INTERNAL_SUPPORTED | + PXE_LOOPBACK_EXTERNAL_SUPPORTED; + + CdbPtr->StatFlags |= PXE_STATFLAGS_CABLE_DETECT_SUPPORTED; + return ; +} + + +/** + This routine is used to retrieve the configuration information about the NIC being controlled by + this driver. This will fill in data in the Data Block structure that is pointed to by the caller's CdbPtr->DBaddr. + The fields filled in are as follows: + DbPtr->pci.BusType, DbPtr->pci.Bus, DbPtr->pci.Device, and DbPtr->pci. + In addition, the DbPtr->pci.Config.Dword[0-63] grabs a copy of this NIC's PCI configuration space. + + @param CdbPtr Pointer to the command descriptor block. + @param AdapterInfo Pointer to the NIC data structure information which + the UNDI driver is layering on.. + + @return None + +**/ +VOID +UNDI_GetConfigInfo ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ) +{ + UINT16 Index; + PXE_DB_GET_CONFIG_INFO *DbPtr; + + DbPtr = (PXE_DB_GET_CONFIG_INFO *) (UINTN) (CdbPtr->DBaddr); + + DbPtr->pci.BusType = PXE_BUSTYPE_PCI; + DbPtr->pci.Bus = AdapterInfo->Bus; + DbPtr->pci.Device = AdapterInfo->Device; + DbPtr->pci.Function = AdapterInfo->Function; + + for (Index = 0; Index < MAX_PCI_CONFIG_LEN; Index++) { + DbPtr->pci.Config.Dword[Index] = AdapterInfo->Config[Index]; + } + + return ; +} + + +/** + This routine resets the network adapter and initializes the UNDI using the parameters supplied in + the CPB. This command must be issued before the network adapter can be setup to transmit and + receive packets. + Once the memory requirements of the UNDI are obtained by using the GetInitInfo command, a block + of non-swappable memory may need to be allocated. The address of this memory must be passed to + UNDI during the Initialize in the CPB. This memory is used primarily for transmit and receive buffers. + The fields CableDetect, LinkSpeed, Duplex, LoopBack, MemoryPtr, and MemoryLength are set with information + that was passed in the CPB and the NIC is initialized. + If the NIC initialization fails, the CdbPtr->StatFlags are updated with PXE_STATFLAGS_COMMAND_FAILED + Otherwise, AdapterInfo->State is updated with PXE_STATFLAGS_GET_STATE_INITIALIZED showing the state of + the UNDI is now initialized. + + @param CdbPtr Pointer to the command descriptor block. + @param AdapterInfo Pointer to the NIC data structure information which + the UNDI driver is layering on.. + + @return None + +**/ +VOID +UNDI_Initialize ( + IN PXE_CDB *CdbPtr, + NIC_DATA_INSTANCE *AdapterInfo + ) +{ + PXE_CPB_INITIALIZE *CpbPtr; + + if ((CdbPtr->OpFlags != PXE_OPFLAGS_INITIALIZE_DETECT_CABLE) && + (CdbPtr->OpFlags != PXE_OPFLAGS_INITIALIZE_DO_NOT_DETECT_CABLE)) { + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + CdbPtr->StatCode = PXE_STATCODE_INVALID_CDB; + return ; + } + + // + // check if it is already initialized + // + if (AdapterInfo->State == PXE_STATFLAGS_GET_STATE_INITIALIZED) { + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + CdbPtr->StatCode = PXE_STATCODE_ALREADY_INITIALIZED; + return ; + } + + CpbPtr = (PXE_CPB_INITIALIZE *) (UINTN) CdbPtr->CPBaddr; + + if (CpbPtr->MemoryLength < (UINT32) MEMORY_NEEDED) { + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + CdbPtr->StatCode = PXE_STATCODE_INVALID_CPB; + return ; + } + + // + // default behaviour is to detect the cable, if the 3rd param is 1, + // do not do that + // + AdapterInfo->CableDetect = (UINT8) ((CdbPtr->OpFlags == (UINT16) PXE_OPFLAGS_INITIALIZE_DO_NOT_DETECT_CABLE) ? (UINT8) 0 : (UINT8) 1); + AdapterInfo->LinkSpeedReq = (UINT16) CpbPtr->LinkSpeed; + AdapterInfo->DuplexReq = CpbPtr->DuplexMode; + AdapterInfo->LoopBack = CpbPtr->LoopBackMode; + AdapterInfo->MemoryPtr = CpbPtr->MemoryAddr; + AdapterInfo->MemoryLength = CpbPtr->MemoryLength; + + CdbPtr->StatCode = (PXE_STATCODE) E100bInit (AdapterInfo); + + if (CdbPtr->StatCode != PXE_STATCODE_SUCCESS) { + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + } else { + AdapterInfo->State = PXE_STATFLAGS_GET_STATE_INITIALIZED; + } + + return ; +} + + +/** + This routine resets the network adapter and initializes the UNDI using the parameters supplied in + the CPB. The transmit and receive queues are emptied and any pending interrupts are cleared. + If the NIC reset fails, the CdbPtr->StatFlags are updated with PXE_STATFLAGS_COMMAND_FAILED + + @param CdbPtr Pointer to the command descriptor block. + @param AdapterInfo Pointer to the NIC data structure information which + the UNDI driver is layering on.. + + @return None + +**/ +VOID +UNDI_Reset ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ) +{ + if (CdbPtr->OpFlags != PXE_OPFLAGS_NOT_USED && + CdbPtr->OpFlags != PXE_OPFLAGS_RESET_DISABLE_INTERRUPTS && + CdbPtr->OpFlags != PXE_OPFLAGS_RESET_DISABLE_FILTERS ) { + + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + CdbPtr->StatCode = PXE_STATCODE_INVALID_CDB; + return ; + } + + CdbPtr->StatCode = (UINT16) E100bReset (AdapterInfo, CdbPtr->OpFlags); + + if (CdbPtr->StatCode != PXE_STATCODE_SUCCESS) { + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + } +} + + +/** + This routine resets the network adapter and leaves it in a safe state for another driver to + initialize. Any pending transmits or receives are lost. Receive filters and external + interrupt enables are disabled. Once the UNDI has been shutdown, it can then be stopped + or initialized again. + If the NIC reset fails, the CdbPtr->StatFlags are updated with PXE_STATFLAGS_COMMAND_FAILED + Otherwise, AdapterInfo->State is updated with PXE_STATFLAGS_GET_STATE_STARTED showing the state of + the NIC as being started. + + @param CdbPtr Pointer to the command descriptor block. + @param AdapterInfo Pointer to the NIC data structure information which + the UNDI driver is layering on.. + + @return None + +**/ +VOID +UNDI_Shutdown ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ) +{ + // + // do the shutdown stuff here + // + CdbPtr->StatCode = (UINT16) E100bShutdown (AdapterInfo); + + if (CdbPtr->StatCode != PXE_STATCODE_SUCCESS) { + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + } else { + AdapterInfo->State = PXE_STATFLAGS_GET_STATE_STARTED; + } + + return ; +} + + +/** + This routine can be used to read and/or change the current external interrupt enable + settings. Disabling an external interrupt enable prevents and external (hardware) + interrupt from being signaled by the network device. Internally the interrupt events + can still be polled by using the UNDI_GetState command. + The resulting information on the interrupt state will be passed back in the CdbPtr->StatFlags. + + @param CdbPtr Pointer to the command descriptor block. + @param AdapterInfo Pointer to the NIC data structure information which + the UNDI driver is layering on.. + + @return None + +**/ +VOID +UNDI_Interrupt ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ) +{ + UINT8 IntMask; + + IntMask = (UINT8)(UINTN)(CdbPtr->OpFlags & (PXE_OPFLAGS_INTERRUPT_RECEIVE | + PXE_OPFLAGS_INTERRUPT_TRANSMIT | + PXE_OPFLAGS_INTERRUPT_COMMAND | + PXE_OPFLAGS_INTERRUPT_SOFTWARE)); + + switch (CdbPtr->OpFlags & PXE_OPFLAGS_INTERRUPT_OPMASK) { + case PXE_OPFLAGS_INTERRUPT_READ: + break; + + case PXE_OPFLAGS_INTERRUPT_ENABLE: + if (IntMask == 0) { + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + CdbPtr->StatCode = PXE_STATCODE_INVALID_CDB; + return ; + } + + AdapterInfo->int_mask = IntMask; + E100bSetInterruptState (AdapterInfo); + break; + + case PXE_OPFLAGS_INTERRUPT_DISABLE: + if (IntMask != 0) { + AdapterInfo->int_mask = (UINT16) (AdapterInfo->int_mask & ~(IntMask)); + E100bSetInterruptState (AdapterInfo); + break; + } + + // + // else fall thru. + // + default: + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + CdbPtr->StatCode = PXE_STATCODE_INVALID_CDB; + return ; + } + + if ((AdapterInfo->int_mask & PXE_OPFLAGS_INTERRUPT_RECEIVE) != 0) { + CdbPtr->StatFlags |= PXE_STATFLAGS_INTERRUPT_RECEIVE; + + } + + if ((AdapterInfo->int_mask & PXE_OPFLAGS_INTERRUPT_TRANSMIT) != 0) { + CdbPtr->StatFlags |= PXE_STATFLAGS_INTERRUPT_TRANSMIT; + + } + + if ((AdapterInfo->int_mask & PXE_OPFLAGS_INTERRUPT_COMMAND) != 0) { + CdbPtr->StatFlags |= PXE_STATFLAGS_INTERRUPT_COMMAND; + + } + + return ; +} + + +/** + This routine is used to read and change receive filters and, if supported, read + and change multicast MAC address filter list. + + @param CdbPtr Pointer to the command descriptor block. + @param AdapterInfo Pointer to the NIC data structure information which + the UNDI driver is layering on.. + + @return None + +**/ +VOID +UNDI_RecFilter ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ) +{ + UINT16 NewFilter; + UINT16 OpFlags; + PXE_DB_RECEIVE_FILTERS *DbPtr; + UINT8 *MacAddr; + UINTN MacCount; + UINT16 Index; + UINT16 copy_len; + UINT8 *ptr1; + UINT8 *ptr2; + OpFlags = CdbPtr->OpFlags; + NewFilter = (UINT16) (OpFlags & 0x1F); + + switch (OpFlags & PXE_OPFLAGS_RECEIVE_FILTER_OPMASK) { + case PXE_OPFLAGS_RECEIVE_FILTER_READ: + + // + // not expecting a cpb, not expecting any filter bits + // + if ((NewFilter != 0) || (CdbPtr->CPBsize != 0)) { + goto BadCdb; + + } + + if ((NewFilter & PXE_OPFLAGS_RECEIVE_FILTER_RESET_MCAST_LIST) == 0) { + goto JustRead; + + } + + NewFilter = (UINT16) (NewFilter | AdapterInfo->Rx_Filter); + // + // all other flags are ignored except mcast_reset + // + break; + + case PXE_OPFLAGS_RECEIVE_FILTER_ENABLE: + // + // there should be atleast one other filter bit set. + // + if (NewFilter == 0) { + // + // nothing to enable + // + goto BadCdb; + } + + if (CdbPtr->CPBsize != 0) { + // + // this must be a multicast address list! + // don't accept the list unless selective_mcast is set + // don't accept confusing mcast settings with this + // + if (((NewFilter & PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST) == 0) || + ((NewFilter & PXE_OPFLAGS_RECEIVE_FILTER_RESET_MCAST_LIST) != 0) || + ((NewFilter & PXE_OPFLAGS_RECEIVE_FILTER_ALL_MULTICAST) != 0) || + ((CdbPtr->CPBsize % sizeof (PXE_MAC_ADDR)) != 0) ) { + goto BadCdb; + } + + MacAddr = (UINT8 *) ((UINTN) (CdbPtr->CPBaddr)); + MacCount = CdbPtr->CPBsize / sizeof (PXE_MAC_ADDR); + + for (; MacCount-- != 0; MacAddr += sizeof (PXE_MAC_ADDR)) { + if (MacAddr[0] != 0x01 || MacAddr[1] != 0x00 || MacAddr[2] != 0x5E || (MacAddr[3] & 0x80) != 0) { + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + CdbPtr->StatCode = PXE_STATCODE_INVALID_CPB; + return ; + } + } + } + + // + // check selective mcast case enable case + // + if ((OpFlags & PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST) != 0) { + if (((OpFlags & PXE_OPFLAGS_RECEIVE_FILTER_RESET_MCAST_LIST) != 0) || + ((OpFlags & PXE_OPFLAGS_RECEIVE_FILTER_ALL_MULTICAST) != 0) ) { + goto BadCdb; + + } + // + // if no cpb, make sure we have an old list + // + if ((CdbPtr->CPBsize == 0) && (AdapterInfo->mcast_list.list_len == 0)) { + goto BadCdb; + } + } + // + // if you want to enable anything, you got to have unicast + // and you have what you already enabled! + // + NewFilter = (UINT16) (NewFilter | (PXE_OPFLAGS_RECEIVE_FILTER_UNICAST | AdapterInfo->Rx_Filter)); + + break; + + case PXE_OPFLAGS_RECEIVE_FILTER_DISABLE: + + // + // mcast list not expected, i.e. no cpb here! + // + if (CdbPtr->CPBsize != PXE_CPBSIZE_NOT_USED) { + goto BadCdb; + } + + NewFilter = (UINT16) ((~(CdbPtr->OpFlags & 0x1F)) & AdapterInfo->Rx_Filter); + + break; + + default: + goto BadCdb; + } + + if ((OpFlags & PXE_OPFLAGS_RECEIVE_FILTER_RESET_MCAST_LIST) != 0) { + AdapterInfo->mcast_list.list_len = 0; + NewFilter &= (~PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST); + } + + E100bSetfilter (AdapterInfo, NewFilter, CdbPtr->CPBaddr, CdbPtr->CPBsize); + +JustRead: + // + // give the current mcast list + // + if ((CdbPtr->DBsize != 0) && (AdapterInfo->mcast_list.list_len != 0)) { + // + // copy the mc list to db + // + + DbPtr = (PXE_DB_RECEIVE_FILTERS *) (UINTN) CdbPtr->DBaddr; + ptr1 = (UINT8 *) (&DbPtr->MCastList[0]); + + // + // DbPtr->mc_count = AdapterInfo->mcast_list.list_len; + // + copy_len = (UINT16) (AdapterInfo->mcast_list.list_len * PXE_MAC_LENGTH); + + if (copy_len > CdbPtr->DBsize) { + copy_len = CdbPtr->DBsize; + + } + + ptr2 = (UINT8 *) (&AdapterInfo->mcast_list.mc_list[0]); + for (Index = 0; Index < copy_len; Index++) { + ptr1[Index] = ptr2[Index]; + } + } + // + // give the stat flags here + // + if (AdapterInfo->Receive_Started) { + CdbPtr->StatFlags = (PXE_STATFLAGS) (CdbPtr->StatFlags | AdapterInfo->Rx_Filter); + + } + + return ; + +BadCdb: + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + CdbPtr->StatCode = PXE_STATCODE_INVALID_CDB; +} + + +/** + This routine is used to get the current station and broadcast MAC addresses, and to change the + current station MAC address. + + @param CdbPtr Pointer to the command descriptor block. + @param AdapterInfo Pointer to the NIC data structure information which + the UNDI driver is layering on.. + + @return None + +**/ +VOID +UNDI_StnAddr ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ) +{ + PXE_CPB_STATION_ADDRESS *CpbPtr; + PXE_DB_STATION_ADDRESS *DbPtr; + UINT16 Index; + + if (CdbPtr->OpFlags == PXE_OPFLAGS_STATION_ADDRESS_RESET) { + // + // configure the permanent address. + // change the AdapterInfo->CurrentNodeAddress field. + // + if (CompareMem ( + &AdapterInfo->CurrentNodeAddress[0], + &AdapterInfo->PermNodeAddress[0], + PXE_MAC_LENGTH + ) != 0) { + for (Index = 0; Index < PXE_MAC_LENGTH; Index++) { + AdapterInfo->CurrentNodeAddress[Index] = AdapterInfo->PermNodeAddress[Index]; + } + + E100bSetupIAAddr (AdapterInfo); + } + } + + if (CdbPtr->CPBaddr != (UINT64) 0) { + CpbPtr = (PXE_CPB_STATION_ADDRESS *) (UINTN) (CdbPtr->CPBaddr); + // + // configure the new address + // + for (Index = 0; Index < PXE_MAC_LENGTH; Index++) { + AdapterInfo->CurrentNodeAddress[Index] = CpbPtr->StationAddr[Index]; + } + + E100bSetupIAAddr (AdapterInfo); + } + + if (CdbPtr->DBaddr != (UINT64) 0) { + DbPtr = (PXE_DB_STATION_ADDRESS *) (UINTN) (CdbPtr->DBaddr); + // + // fill it with the new values + // + for (Index = 0; Index < PXE_MAC_LENGTH; Index++) { + DbPtr->StationAddr[Index] = AdapterInfo->CurrentNodeAddress[Index]; + DbPtr->BroadcastAddr[Index] = AdapterInfo->BroadcastNodeAddress[Index]; + DbPtr->PermanentAddr[Index] = AdapterInfo->PermNodeAddress[Index]; + } + } + + return ; +} + + +/** + This routine is used to read and clear the NIC traffic statistics. This command is supported only + if the !PXE structure's Implementation flags say so. + Results will be parsed out in the following manner: + CdbPtr->DBaddr.Data[0] R Total Frames (Including frames with errors and dropped frames) + CdbPtr->DBaddr.Data[1] R Good Frames (All frames copied into receive buffer) + CdbPtr->DBaddr.Data[2] R Undersize Frames (Frames below minimum length for media <64 for ethernet) + CdbPtr->DBaddr.Data[4] R Dropped Frames (Frames that were dropped because receive buffers were full) + CdbPtr->DBaddr.Data[8] R CRC Error Frames (Frames with alignment or CRC errors) + CdbPtr->DBaddr.Data[A] T Total Frames (Including frames with errors and dropped frames) + CdbPtr->DBaddr.Data[B] T Good Frames (All frames copied into transmit buffer) + CdbPtr->DBaddr.Data[C] T Undersize Frames (Frames below minimum length for media <64 for ethernet) + CdbPtr->DBaddr.Data[E] T Dropped Frames (Frames that were dropped because of collisions) + CdbPtr->DBaddr.Data[14] T Total Collision Frames (Total collisions on this subnet) + + @param CdbPtr Pointer to the command descriptor block. + @param AdapterInfo Pointer to the NIC data structure information which + the UNDI driver is layering on.. + + @return None + +**/ +VOID +UNDI_Statistics ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ) +{ + if ((CdbPtr->OpFlags &~(PXE_OPFLAGS_STATISTICS_RESET)) != 0) { + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + CdbPtr->StatCode = PXE_STATCODE_INVALID_CDB; + return ; + } + + if ((CdbPtr->OpFlags & PXE_OPFLAGS_STATISTICS_RESET) != 0) { + // + // Reset the statistics + // + CdbPtr->StatCode = (UINT16) E100bStatistics (AdapterInfo, 0, 0); + } else { + CdbPtr->StatCode = (UINT16) E100bStatistics (AdapterInfo, CdbPtr->DBaddr, CdbPtr->DBsize); + } + + return ; +} + + +/** + This routine is used to translate a multicast IP address to a multicast MAC address. + This results in a MAC address composed of 25 bits of fixed data with the upper 23 bits of the IP + address being appended to it. Results passed back in the equivalent of CdbPtr->DBaddr->MAC[0-5]. + + @param CdbPtr Pointer to the command descriptor block. + @param AdapterInfo Pointer to the NIC data structure information which + the UNDI driver is layering on.. + + @return None + +**/ +VOID +UNDI_ip2mac ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ) +{ + PXE_CPB_MCAST_IP_TO_MAC *CpbPtr; + PXE_DB_MCAST_IP_TO_MAC *DbPtr; + UINT8 *TmpPtr; + + CpbPtr = (PXE_CPB_MCAST_IP_TO_MAC *) (UINTN) CdbPtr->CPBaddr; + DbPtr = (PXE_DB_MCAST_IP_TO_MAC *) (UINTN) CdbPtr->DBaddr; + + if ((CdbPtr->OpFlags & PXE_OPFLAGS_MCAST_IPV6_TO_MAC) != 0) { + // + // for now this is not supported + // + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + CdbPtr->StatCode = PXE_STATCODE_UNSUPPORTED; + return ; + } + + TmpPtr = (UINT8 *) (&CpbPtr->IP.IPv4); + // + // check if the ip given is a mcast IP + // + if ((TmpPtr[0] & 0xF0) != 0xE0) { + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + CdbPtr->StatCode = PXE_STATCODE_INVALID_CPB; + } + // + // take the last 23 bits in IP. + // be very careful. accessing word on a non-word boundary will hang motherboard codenamed Big Sur + // casting the mac array (in the middle) to a UINT32 pointer and accessing + // the UINT32 content hung the system... + // + DbPtr->MAC[0] = 0x01; + DbPtr->MAC[1] = 0x00; + DbPtr->MAC[2] = 0x5e; + DbPtr->MAC[3] = (UINT8) (TmpPtr[1] & 0x7f); + DbPtr->MAC[4] = (UINT8) TmpPtr[2]; + DbPtr->MAC[5] = (UINT8) TmpPtr[3]; + + return ; +} + + +/** + This routine is used to read and write non-volatile storage on the NIC (if supported). The NVRAM + could be EEPROM, FLASH, or battery backed RAM. + This is an optional function according to the UNDI specification (or will be......) + + @param CdbPtr Pointer to the command descriptor block. + @param AdapterInfo Pointer to the NIC data structure information which + the UNDI driver is layering on.. + + @return None + +**/ +VOID +UNDI_NVData ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ) +{ + PXE_DB_NVDATA *DbPtr; + UINT16 Index; + + if ((CdbPtr->OpFlags == PXE_OPFLAGS_NVDATA_READ) != 0) { + + if ((CdbPtr->DBsize == PXE_DBSIZE_NOT_USED) != 0) { + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + CdbPtr->StatCode = PXE_STATCODE_INVALID_CDB; + return ; + } + + DbPtr = (PXE_DB_NVDATA *) (UINTN) CdbPtr->DBaddr; + + for (Index = 0; Index < MAX_PCI_CONFIG_LEN; Index++) { + DbPtr->Data.Dword[Index] = AdapterInfo->NVData[Index]; + + } + + } else { + // + // no write for now + // + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + CdbPtr->StatCode = PXE_STATCODE_UNSUPPORTED; + } + + return ; +} + + +/** + This routine returns the current interrupt status and/or the transmitted buffer addresses. + If the current interrupt status is returned, pending interrupts will be acknowledged by this + command. Transmitted buffer addresses that are written to the DB are removed from the transmit + buffer queue. + Normally, this command would be polled with interrupts disabled. + The transmit buffers are returned in CdbPtr->DBaddr->TxBufer[0 - NumEntries]. + The interrupt status is returned in CdbPtr->StatFlags. + + @param CdbPtr Pointer to the command descriptor block. + @param AdapterInfo Pointer to the NIC data structure information which + the UNDI driver is layering on.. + + @return None + +**/ +VOID +UNDI_Status ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ) +{ + PXE_DB_GET_STATUS *DbPtr; + PXE_DB_GET_STATUS TmpGetStatus; + UINT16 Index; + UINT16 Status; + UINT16 NumEntries; + RxFD *RxPtr; + + // + // Fill in temporary GetStatus storage. + // + RxPtr = &AdapterInfo->rx_ring[AdapterInfo->cur_rx_ind]; + + if ((RxPtr->cb_header.status & RX_COMPLETE) != 0) { + TmpGetStatus.RxFrameLen = RxPtr->ActualCount & 0x3fff; + } else { + TmpGetStatus.RxFrameLen = 0; + } + + TmpGetStatus.reserved = 0; + + // + // Fill in size of next available receive packet and + // reserved field in caller's DB storage. + // + DbPtr = (PXE_DB_GET_STATUS *) (UINTN) CdbPtr->DBaddr; + + if (CdbPtr->DBsize > 0 && CdbPtr->DBsize < sizeof (UINT32) * 2) { + CopyMem (DbPtr, &TmpGetStatus, CdbPtr->DBsize); + } else { + CopyMem (DbPtr, &TmpGetStatus, sizeof (UINT32) * 2); + } + + // + // + // + if ((CdbPtr->OpFlags & PXE_OPFLAGS_GET_TRANSMITTED_BUFFERS) != 0) { + // + // DBsize of zero is invalid if Tx buffers are requested. + // + if (CdbPtr->DBsize == 0) { + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + CdbPtr->StatCode = PXE_STATCODE_INVALID_CDB; + return ; + } + + // + // remember this b4 we overwrite + // + NumEntries = (UINT16) (CdbPtr->DBsize - sizeof (UINT64)); + + // + // We already filled in 2 UINT32s. + // + CdbPtr->DBsize = sizeof (UINT32) * 2; + + // + // will claim any hanging free CBs + // + CheckCBList (AdapterInfo); + + if (AdapterInfo->xmit_done_head == AdapterInfo->xmit_done_tail) { + CdbPtr->StatFlags |= PXE_STATFLAGS_GET_STATUS_TXBUF_QUEUE_EMPTY; + } else { + for (Index = 0; NumEntries >= sizeof (UINT64); Index++, NumEntries -= sizeof (UINT64)) { + if (AdapterInfo->xmit_done_head != AdapterInfo->xmit_done_tail) { + DbPtr->TxBuffer[Index] = AdapterInfo->xmit_done[AdapterInfo->xmit_done_head]; + AdapterInfo->xmit_done_head = next (AdapterInfo->xmit_done_head); + CdbPtr->DBsize += sizeof (UINT64); + } else { + break; + } + } + } + + if (AdapterInfo->xmit_done_head != AdapterInfo->xmit_done_tail) { + CdbPtr->StatFlags |= PXE_STATFLAGS_DB_WRITE_TRUNCATED; + + } + // + // check for a receive buffer and give it's size in db + // + } + // + // + // + if ((CdbPtr->OpFlags & PXE_OPFLAGS_GET_INTERRUPT_STATUS) != 0) { + + 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)); + + // + // report all the outstanding interrupts + // + Status = AdapterInfo->Int_Status; + if ((Status & SCB_STATUS_FR) != 0) { + CdbPtr->StatFlags |= PXE_STATFLAGS_GET_STATUS_RECEIVE; + } + + if ((Status & SCB_STATUS_SWI) != 0) { + CdbPtr->StatFlags |= PXE_STATFLAGS_GET_STATUS_SOFTWARE; + } + } + + return ; +} + + +/** + This routine is used to fill media header(s) in transmit packet(s). + Copies the MAC address into the media header whether it is dealing + with fragmented or non-fragmented packets. + + @param CdbPtr Pointer to the command descriptor block. + @param AdapterInfo Pointer to the NIC data structure information which + the UNDI driver is layering on.. + + @return None + +**/ +VOID +UNDI_FillHeader ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ) +{ + PXE_CPB_FILL_HEADER *Cpb; + PXE_CPB_FILL_HEADER_FRAGMENTED *Cpbf; + EtherHeader *MacHeader; + UINTN Index; + + if (CdbPtr->CPBsize == PXE_CPBSIZE_NOT_USED) { + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + CdbPtr->StatCode = PXE_STATCODE_INVALID_CDB; + return ; + } + + if ((CdbPtr->OpFlags & PXE_OPFLAGS_FILL_HEADER_FRAGMENTED) != 0) { + Cpbf = (PXE_CPB_FILL_HEADER_FRAGMENTED *) (UINTN) CdbPtr->CPBaddr; + + // + // assume 1st fragment is big enough for the mac header + // + if ((Cpbf->FragCnt == 0) || (Cpbf->FragDesc[0].FragLen < PXE_MAC_HEADER_LEN_ETHER)) { + // + // no buffers given + // + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + CdbPtr->StatCode = PXE_STATCODE_INVALID_CDB; + return ; + } + + MacHeader = (EtherHeader *) (UINTN) Cpbf->FragDesc[0].FragAddr; + // + // we don't swap the protocol bytes + // + MacHeader->type = Cpbf->Protocol; + + for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) { + MacHeader->dest_addr[Index] = Cpbf->DestAddr[Index]; + MacHeader->src_addr[Index] = Cpbf->SrcAddr[Index]; + } + } else { + Cpb = (PXE_CPB_FILL_HEADER *) (UINTN) CdbPtr->CPBaddr; + + MacHeader = (EtherHeader *) (UINTN) Cpb->MediaHeader; + // + // we don't swap the protocol bytes + // + MacHeader->type = Cpb->Protocol; + + for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) { + MacHeader->dest_addr[Index] = Cpb->DestAddr[Index]; + MacHeader->src_addr[Index] = Cpb->SrcAddr[Index]; + } + } + + return ; +} + + +/** + This routine is used to place a packet into the transmit queue. The data buffers given to + this command are to be considered locked and the application or network driver loses + ownership of these buffers and must not free or relocate them until the ownership returns. + When the packets are transmitted, a transmit complete interrupt is generated (if interrupts + are disabled, the transmit interrupt status is still set and can be checked using the UNDI_Status + command. + Some implementations and adapters support transmitting multiple packets with one transmit + command. If this feature is supported, the transmit CPBs can be linked in one transmit + command. + All UNDIs support fragmented frames, now all network devices or protocols do. If a fragmented + frame CPB is given to UNDI and the network device does not support fragmented frames + (see !PXE.Implementation flag), the UNDI will have to copy the fragments into a local buffer + before transmitting. + + @param CdbPtr Pointer to the command descriptor block. + @param AdapterInfo Pointer to the NIC data structure information which + the UNDI driver is layering on.. + + @return None + +**/ +VOID +UNDI_Transmit ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ) +{ + + if (CdbPtr->CPBsize == PXE_CPBSIZE_NOT_USED) { + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + CdbPtr->StatCode = PXE_STATCODE_INVALID_CDB; + return ; + } + + CdbPtr->StatCode = (PXE_STATCODE) E100bTransmit (AdapterInfo, CdbPtr->CPBaddr, CdbPtr->OpFlags); + + if (CdbPtr->StatCode != PXE_STATCODE_SUCCESS) { + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + } + + return ; +} + + +/** + When the network adapter has received a frame, this command is used to copy the frame + into the driver/application storage location. Once a frame has been copied, it is + removed from the receive queue. + + @param CdbPtr Pointer to the command descriptor block. + @param AdapterInfo Pointer to the NIC data structure information which + the UNDI driver is layering on.. + + @return None + +**/ +VOID +UNDI_Receive ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ) +{ + + // + // check if RU has started... + // + if (!AdapterInfo->Receive_Started) { + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + CdbPtr->StatCode = PXE_STATCODE_NOT_INITIALIZED; + return ; + } + + + CdbPtr->StatCode = (UINT16) E100bReceive (AdapterInfo, CdbPtr->CPBaddr, CdbPtr->DBaddr); + if (CdbPtr->StatCode != PXE_STATCODE_SUCCESS) { + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + + } + + return ; +} + + + +/** + This is the main SW UNDI API entry using the newer nii protocol. + The parameter passed in is a 64 bit flat model virtual + address of the cdb. We then jump into the common routine for both old and + new nii protocol entries. + + @param CdbPtr Pointer to the command descriptor block. + @param AdapterInfo Pointer to the NIC data structure information which + the UNDI driver is layering on.. + + @return None + +**/ +// TODO: cdb - add argument and description to function comment +VOID +UNDI_APIEntry_new ( + IN UINT64 cdb + ) +{ + PXE_CDB *CdbPtr; + NIC_DATA_INSTANCE *AdapterInfo; + + if (cdb == (UINT64) 0) { + return ; + + } + + CdbPtr = (PXE_CDB *) (UINTN) cdb; + + if (CdbPtr->IFnum >= pxe_31->IFcnt) { + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + CdbPtr->StatCode = PXE_STATCODE_INVALID_CDB; + return ; + } + + AdapterInfo = &(UNDI32DeviceList[CdbPtr->IFnum]->NicInfo); + // + // entering from older entry point + // + AdapterInfo->VersionFlag = 0x31; + UNDI_APIEntry_Common (cdb); +} + + +/** + This is the common routine for both old and new entry point procedures. + The parameter passed in is a 64 bit flat model virtual + address of the cdb. We then jump into the service routine pointed to by the + Api_Table[OpCode]. + + @param CdbPtr Pointer to the command descriptor block. + @param AdapterInfo Pointer to the NIC data structure information which + the UNDI driver is layering on.. + + @return None + +**/ +// TODO: cdb - add argument and description to function comment +VOID +UNDI_APIEntry_Common ( + IN UINT64 cdb + ) +{ + PXE_CDB *CdbPtr; + NIC_DATA_INSTANCE *AdapterInfo; + UNDI_CALL_TABLE *tab_ptr; + + CdbPtr = (PXE_CDB *) (UINTN) cdb; + + // + // check the OPCODE range + // + if ((CdbPtr->OpCode > PXE_OPCODE_LAST_VALID) || + (CdbPtr->StatCode != PXE_STATCODE_INITIALIZE) || + (CdbPtr->StatFlags != PXE_STATFLAGS_INITIALIZE) || + (CdbPtr->IFnum >= pxe_31->IFcnt) ) { + goto badcdb; + + } + + if (CdbPtr->CPBsize == PXE_CPBSIZE_NOT_USED) { + if (CdbPtr->CPBaddr != PXE_CPBADDR_NOT_USED) { + goto badcdb; + } + } else if (CdbPtr->CPBaddr == PXE_CPBADDR_NOT_USED) { + goto badcdb; + } + + if (CdbPtr->DBsize == PXE_DBSIZE_NOT_USED) { + if (CdbPtr->DBaddr != PXE_DBADDR_NOT_USED) { + goto badcdb; + } + } else if (CdbPtr->DBaddr == PXE_DBADDR_NOT_USED) { + goto badcdb; + } + + // + // check if cpbsize and dbsize are as needed + // check if opflags are as expected + // + tab_ptr = &api_table[CdbPtr->OpCode]; + + if (tab_ptr->cpbsize != (UINT16) (DONT_CHECK) && tab_ptr->cpbsize != CdbPtr->CPBsize) { + goto badcdb; + } + + if (tab_ptr->dbsize != (UINT16) (DONT_CHECK) && tab_ptr->dbsize != CdbPtr->DBsize) { + goto badcdb; + } + + if (tab_ptr->opflags != (UINT16) (DONT_CHECK) && tab_ptr->opflags != CdbPtr->OpFlags) { + goto badcdb; + + } + + AdapterInfo = &(UNDI32DeviceList[CdbPtr->IFnum]->NicInfo); + + // + // check if UNDI_State is valid for this call + // + if (tab_ptr->state != (UINT16) (-1)) { + // + // should atleast be started + // + if (AdapterInfo->State == PXE_STATFLAGS_GET_STATE_STOPPED) { + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + CdbPtr->StatCode = PXE_STATCODE_NOT_STARTED; + return ; + } + // + // check if it should be initialized + // + if (tab_ptr->state == 2) { + if (AdapterInfo->State != PXE_STATFLAGS_GET_STATE_INITIALIZED) { + CdbPtr->StatCode = PXE_STATCODE_NOT_INITIALIZED; + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + return ; + } + } + } + // + // set the return variable for success case here + // + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE; + CdbPtr->StatCode = PXE_STATCODE_SUCCESS; + + tab_ptr->api_ptr (CdbPtr, AdapterInfo); + return ; + // + // %% AVL - check for command linking + // +badcdb: + CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; + CdbPtr->StatCode = PXE_STATCODE_INVALID_CDB; + return ; +} + + +/** + When called with a null NicPtr, this routine decrements the number of NICs + this UNDI is supporting and removes the NIC_DATA_POINTER from the array. + Otherwise, it increments the number of NICs this UNDI is supported and + updates the pxe.Fudge to ensure a proper check sum results. + + @param NicPtr Pointer to the NIC data structure. + + @return None + +**/ +VOID +PxeUpdate ( + IN NIC_DATA_INSTANCE *NicPtr, + IN PXE_SW_UNDI *PxePtr + ) +{ + if (NicPtr == NULL) { + if (PxePtr->IFcnt > 0) { + // + // number of NICs this undi supports + // + PxePtr->IFcnt--; + } + + PxePtr->Fudge = (UINT8) (PxePtr->Fudge - CalculateSum8 ((VOID *) PxePtr, PxePtr->Len)); + return ; + } + + // + // number of NICs this undi supports + // + PxePtr->IFcnt++; + PxePtr->Fudge = (UINT8) (PxePtr->Fudge - CalculateSum8 ((VOID *) PxePtr, PxePtr->Len)); + + return ; +} + + +/** + Initialize the !PXE structure + + @param PxePtr Pointer to SW_UNDI data structure. + + @retval EFI_SUCCESS This driver is added to Controller. + @retval other This driver does not support this device. + +**/ +VOID +PxeStructInit ( + IN PXE_SW_UNDI *PxePtr + ) +{ + // + // Initialize the !PXE structure + // + PxePtr->Signature = PXE_ROMID_SIGNATURE; + PxePtr->Len = sizeof (PXE_SW_UNDI); + // + // cksum + // + PxePtr->Fudge = 0; + // + // number of NICs this undi supports + // + PxePtr->IFcnt = 0; + PxePtr->Rev = PXE_ROMID_REV; + PxePtr->MajorVer = PXE_ROMID_MAJORVER; + PxePtr->MinorVer = PXE_ROMID_MINORVER; + PxePtr->reserved1 = 0; + + PxePtr->Implementation = PXE_ROMID_IMP_SW_VIRT_ADDR | + PXE_ROMID_IMP_FRAG_SUPPORTED | + PXE_ROMID_IMP_CMD_LINK_SUPPORTED | + PXE_ROMID_IMP_NVDATA_READ_ONLY | + PXE_ROMID_IMP_STATION_ADDR_SETTABLE | + PXE_ROMID_IMP_PROMISCUOUS_MULTICAST_RX_SUPPORTED | + PXE_ROMID_IMP_PROMISCUOUS_RX_SUPPORTED | + PXE_ROMID_IMP_BROADCAST_RX_SUPPORTED | + PXE_ROMID_IMP_FILTERED_MULTICAST_RX_SUPPORTED | + PXE_ROMID_IMP_SOFTWARE_INT_SUPPORTED | + PXE_ROMID_IMP_PACKET_RX_INT_SUPPORTED; + + PxePtr->EntryPoint = (UINT64) (UINTN) UNDI_APIEntry_new; + PxePtr->MinorVer = PXE_ROMID_MINORVER_31; + + PxePtr->reserved2[0] = 0; + PxePtr->reserved2[1] = 0; + PxePtr->reserved2[2] = 0; + PxePtr->BusCnt = 1; + PxePtr->BusType[0] = PXE_BUSTYPE_PCI; + + PxePtr->Fudge = (UINT8) (PxePtr->Fudge - CalculateSum8 ((VOID *) PxePtr, PxePtr->Len)); +} + diff --git a/OptionRomPkg/UndiRuntimeDxe/E100b.c b/OptionRomPkg/UndiRuntimeDxe/E100b.c new file mode 100644 index 0000000000..c0600c5e5a --- /dev/null +++ b/OptionRomPkg/UndiRuntimeDxe/E100b.c @@ -0,0 +1,3543 @@ +/** @file + Provides basic function upon network adapter card. + +Copyright (c) 2006, Intel Corporation +All rights reserved. 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); + data_ptr = (UINT8 *) (&cmd_ptr->PhysTBDArrayAddres); + + // + // 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); + data_ptr = (UINT16 *) (&cmd_ptr->PhysTBDArrayAddres); + + // + // 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; + + // + // 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; 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) { + 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 = 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; +} diff --git a/OptionRomPkg/UndiRuntimeDxe/E100b.h b/OptionRomPkg/UndiRuntimeDxe/E100b.h new file mode 100644 index 0000000000..60acdb824c --- /dev/null +++ b/OptionRomPkg/UndiRuntimeDxe/E100b.h @@ -0,0 +1,671 @@ +/** @file + Definitions for network adapter card. + +Copyright (c) 2006 - 2007, Intel Corporation +All rights reserved. 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. + +**/ + +#ifndef _E100B_H_ +#define _E100B_H_ + +// pci config offsets: + +#define RX_BUFFER_COUNT 32 +#define TX_BUFFER_COUNT 32 + +#define PCI_VENDOR_ID_INTEL 0x8086 +#define PCI_DEVICE_ID_INTEL_82557 0x1229 +#define D100_VENDOR_ID 0x8086 +#define D100_DEVICE_ID 0x1229 +#define D102_DEVICE_ID 0x2449 + +#define ICH3_DEVICE_ID_1 0x1031 +#define ICH3_DEVICE_ID_2 0x1032 +#define ICH3_DEVICE_ID_3 0x1033 +#define ICH3_DEVICE_ID_4 0x1034 +#define ICH3_DEVICE_ID_5 0x1035 +#define ICH3_DEVICE_ID_6 0x1036 +#define ICH3_DEVICE_ID_7 0x1037 +#define ICH3_DEVICE_ID_8 0x1038 + +#define SPEEDO_DEVICE_ID 0x1227 +#define SPLASH1_DEVICE_ID 0x1226 + + +// bit fields for the command +#define PCI_COMMAND_MASTER 0x04 // bit 2 +#define PCI_COMMAND_IO 0x01 // bit 0 +#define PCI_COMMAND 0x04 +#define PCI_LATENCY_TIMER 0x0D + +#define ETHER_MAC_ADDR_LEN 6 +#ifdef AVL_XXX +#define ETHER_HEADER_LEN 14 +// media interface type +// #define INTERFACE_TYPE " + +// Hardware type values +#define HW_ETHER_TYPE 1 +#define HW_EXPERIMENTAL_ETHER_TYPE 2 +#define HW_IEEE_TYPE 6 +#define HW_ARCNET_TYPE 7 + +#endif // AVL_XXX + +#define MAX_ETHERNET_PKT_SIZE 1514 // including eth header +#define RX_BUFFER_SIZE 1536 // including crc and padding +#define TX_BUFFER_SIZE 64 +#define ETH_MTU 1500 // does not include ethernet header length + +#define SPEEDO3_TOTAL_SIZE 0x20 + +#pragma pack(1) + +typedef struct eth { + UINT8 dest_addr[PXE_HWADDR_LEN_ETHER]; + UINT8 src_addr[PXE_HWADDR_LEN_ETHER]; + UINT16 type; +} EtherHeader; + +#pragma pack(1) +typedef struct CONFIG_HEADER { + UINT16 VendorID; + UINT16 DeviceID; + UINT16 Command; + UINT16 Status; + UINT16 RevID; + UINT16 ClassID; + UINT8 CacheLineSize; + UINT8 LatencyTimer; + UINT8 HeaderType; // must be zero to impose this structure... + UINT8 BIST; // built-in self test + UINT32 BaseAddressReg_0; // memory mapped address + UINT32 BaseAddressReg_1; //io mapped address, Base IO address + UINT32 BaseAddressReg_2; // option rom address + UINT32 BaseAddressReg_3; + UINT32 BaseAddressReg_4; + UINT32 BaseAddressReg_5; + UINT32 CardBusCISPtr; + UINT16 SubVendorID; + UINT16 SubSystemID; + UINT32 ExpansionROMBaseAddr; + UINT8 CapabilitiesPtr; + UINT8 reserved1; + UINT16 Reserved2; + UINT32 Reserved3; + UINT8 int_line; + UINT8 int_pin; + UINT8 Min_gnt; + UINT8 Max_lat; +} PCI_CONFIG_HEADER; +#pragma pack() + +//------------------------------------------------------------------------- +// Offsets to the various registers. +// All accesses need not be longword aligned. +//------------------------------------------------------------------------- +enum speedo_offsets { + SCBStatus = 0, SCBCmd = 2, // Rx/Command Unit command and status. + SCBPointer = 4, // General purpose pointer. + SCBPort = 8, // Misc. commands and operands. + SCBflash = 12, SCBeeprom = 14, // EEPROM and flash memory control. + SCBCtrlMDI = 16, // MDI interface control. + SCBEarlyRx = 20, // Early receive byte count. + SCBEarlyRxInt = 24, SCBFlowCtrlReg = 25, SCBPmdr = 27, + // offsets for general control registers (GCRs) + SCBGenCtrl = 28, SCBGenStatus = 29, SCBGenCtrl2 = 30, SCBRsvd = 31 +}; + +#define GCR2_EEPROM_ACCESS_SEMAPHORE 0x80 // bit offset into the gcr2 + +//------------------------------------------------------------------------- +// Action commands - Commands that can be put in a command list entry. +//------------------------------------------------------------------------- +enum commands { + CmdNOp = 0, CmdIASetup = 1, CmdConfigure = 2, CmdMulticastList = 3, + CmdTx = 4, CmdTDR = 5, CmdDump = 6, CmdDiagnose = 7, + CmdSuspend = 0x4000, /* Suspend after completion. */ + CmdIntr = 0x2000, /* Interrupt after completion. */ + CmdTxFlex = 0x0008 /* Use "Flexible mode" for CmdTx command. */ +}; + +//------------------------------------------------------------------------- +// port commands +//------------------------------------------------------------------------- +#define PORT_RESET 0 +#define PORT_SELF_TEST 1 +#define POR_SELECTIVE_RESET 2 +#define PORT_DUMP_POINTER 2 + +//------------------------------------------------------------------------- +// SCB Command Word bit definitions +//------------------------------------------------------------------------- +//- CUC fields +#define CU_START 0x0010 +#define CU_RESUME 0x0020 +#define CU_STATSADDR 0x0040 +#define CU_SHOWSTATS 0x0050 /* Dump statistics counters. */ +#define CU_CMD_BASE 0x0060 /* Base address to add to add CU commands. */ +#define CU_DUMPSTATS 0x0070 /* Dump then reset stats counters. */ + +//- RUC fields +#define RX_START 0x0001 +#define RX_RESUME 0x0002 +#define RX_ABORT 0x0004 +#define RX_ADDR_LOAD 0x0006 /* load ru_base_reg */ +#define RX_RESUMENR 0x0007 + +// Interrupt fields (assuming byte addressing) +#define INT_MASK 0x0100 +#define DRVR_INT 0x0200 /* Driver generated interrupt. */ + +//- CB Status Word +#define CMD_STATUS_COMPLETE 0x8000 +#define RX_STATUS_COMPLETE 0x8000 +#define CMD_STATUS_MASK 0xF000 + +//------------------------------------------------------------------------- +//- SCB Status bits: +// Interrupts are ACKed by writing to the upper 6 interrupt bits +//------------------------------------------------------------------------- +#define SCB_STATUS_MASK 0xFC00 // bits 2-7 - STATUS/ACK Mask +#define SCB_STATUS_CX_TNO 0x8000 // BIT_15 - CX or TNO Interrupt +#define SCB_STATUS_FR 0x4000 // BIT_14 - FR Interrupt +#define SCB_STATUS_CNA 0x2000 // BIT_13 - CNA Interrupt +#define SCB_STATUS_RNR 0x1000 // BIT_12 - RNR Interrupt +#define SCB_STATUS_MDI 0x0800 // BIT_11 - MDI R/W Done Interrupt +#define SCB_STATUS_SWI 0x0400 // BIT_10 - SWI Interrupt + +// CU STATUS: bits 6 & 7 +#define SCB_STATUS_CU_MASK 0x00C0 // bits 6 & 7 +#define SCB_STATUS_CU_IDLE 0x0000 // 00 +#define SCB_STATUS_CU_SUSPEND 0x0040 // 01 +#define SCB_STATUS_CU_ACTIVE 0x0080 // 10 + +// RU STATUS: bits 2-5 +#define SCB_RUS_IDLE 0x0000 +#define SCB_RUS_SUSPENDED 0x0004 // bit 2 +#define SCB_RUS_NO_RESOURCES 0x0008 // bit 3 +#define SCB_RUS_READY 0x0010 // bit 4 + +//------------------------------------------------------------------------- +// Bit Mask definitions +//------------------------------------------------------------------------- +#define BIT_0 0x0001 +#define BIT_1 0x0002 +#define BIT_2 0x0004 +#define BIT_3 0x0008 +#define BIT_4 0x0010 +#define BIT_5 0x0020 +#define BIT_6 0x0040 +#define BIT_7 0x0080 +#define BIT_8 0x0100 +#define BIT_9 0x0200 +#define BIT_10 0x0400 +#define BIT_11 0x0800 +#define BIT_12 0x1000 +#define BIT_13 0x2000 +#define BIT_14 0x4000 +#define BIT_15 0x8000 +#define BIT_24 0x01000000 +#define BIT_28 0x10000000 + + +//------------------------------------------------------------------------- +// MDI Control register bit definitions +//------------------------------------------------------------------------- +#define MDI_DATA_MASK BIT_0_15 // MDI Data port +#define MDI_REG_ADDR BIT_16_20 // which MDI register to read/write +#define MDI_PHY_ADDR BIT_21_25 // which PHY to read/write +#define MDI_PHY_OPCODE BIT_26_27 // which PHY to read/write +#define MDI_PHY_READY BIT_28 // PHY is ready for another MDI cycle +#define MDI_PHY_INT_ENABLE BIT_29 // Assert INT at MDI cycle completion + +#define BIT_0_2 0x0007 +#define BIT_0_3 0x000F +#define BIT_0_4 0x001F +#define BIT_0_5 0x003F +#define BIT_0_6 0x007F +#define BIT_0_7 0x00FF +#define BIT_0_8 0x01FF +#define BIT_0_13 0x3FFF +#define BIT_0_15 0xFFFF +#define BIT_1_2 0x0006 +#define BIT_1_3 0x000E +#define BIT_2_5 0x003C +#define BIT_3_4 0x0018 +#define BIT_4_5 0x0030 +#define BIT_4_6 0x0070 +#define BIT_4_7 0x00F0 +#define BIT_5_7 0x00E0 +#define BIT_5_9 0x03E0 +#define BIT_5_12 0x1FE0 +#define BIT_5_15 0xFFE0 +#define BIT_6_7 0x00c0 +#define BIT_7_11 0x0F80 +#define BIT_8_10 0x0700 +#define BIT_9_13 0x3E00 +#define BIT_12_15 0xF000 + +#define BIT_16_20 0x001F0000 +#define BIT_21_25 0x03E00000 +#define BIT_26_27 0x0C000000 + +//------------------------------------------------------------------------- +// MDI Control register opcode definitions +//------------------------------------------------------------------------- +#define MDI_WRITE 1 // Phy Write +#define MDI_READ 2 // Phy read + +//------------------------------------------------------------------------- +// PHY 100 MDI Register/Bit Definitions +//------------------------------------------------------------------------- +// MDI register set +#define MDI_CONTROL_REG 0x00 // MDI control register +#define MDI_STATUS_REG 0x01 // MDI Status regiser +#define PHY_ID_REG_1 0x02 // Phy indentification reg (word 1) +#define PHY_ID_REG_2 0x03 // Phy indentification reg (word 2) +#define AUTO_NEG_ADVERTISE_REG 0x04 // Auto-negotiation advertisement +#define AUTO_NEG_LINK_PARTNER_REG 0x05 // Auto-negotiation link partner ability +#define AUTO_NEG_EXPANSION_REG 0x06 // Auto-negotiation expansion +#define AUTO_NEG_NEXT_PAGE_REG 0x07 // Auto-negotiation next page transmit +#define EXTENDED_REG_0 0x10 // Extended reg 0 (Phy 100 modes) +#define EXTENDED_REG_1 0x14 // Extended reg 1 (Phy 100 error indications) +#define NSC_CONG_CONTROL_REG 0x17 // National (TX) congestion control +#define NSC_SPEED_IND_REG 0x19 // National (TX) speed indication + +// MDI Control register bit definitions +#define MDI_CR_COLL_TEST_ENABLE BIT_7 // Collision test enable +#define MDI_CR_FULL_HALF BIT_8 // FDX =1, half duplex =0 +#define MDI_CR_RESTART_AUTO_NEG BIT_9 // Restart auto negotiation +#define MDI_CR_ISOLATE BIT_10 // Isolate PHY from MII +#define MDI_CR_POWER_DOWN BIT_11 // Power down +#define MDI_CR_AUTO_SELECT BIT_12 // Auto speed select enable +#define MDI_CR_10_100 BIT_13 // 0 = 10Mbs, 1 = 100Mbs +#define MDI_CR_LOOPBACK BIT_14 // 0 = normal, 1 = loopback +#define MDI_CR_RESET BIT_15 // 0 = normal, 1 = PHY reset + +// MDI Status register bit definitions +#define MDI_SR_EXT_REG_CAPABLE BIT_0 // Extended register capabilities +#define MDI_SR_JABBER_DETECT BIT_1 // Jabber detected +#define MDI_SR_LINK_STATUS BIT_2 // Link Status -- 1 = link +#define MDI_SR_AUTO_SELECT_CAPABLE BIT_3 // Auto speed select capable +#define MDI_SR_REMOTE_FAULT_DETECT BIT_4 // Remote fault detect +#define MDI_SR_AUTO_NEG_COMPLETE BIT_5 // Auto negotiation complete +#define MDI_SR_10T_HALF_DPX BIT_11 // 10BaseT Half Duplex capable +#define MDI_SR_10T_FULL_DPX BIT_12 // 10BaseT full duplex capable +#define MDI_SR_TX_HALF_DPX BIT_13 // TX Half Duplex capable +#define MDI_SR_TX_FULL_DPX BIT_14 // TX full duplex capable +#define MDI_SR_T4_CAPABLE BIT_15 // T4 capable + +// Auto-Negotiation advertisement register bit definitions +#define NWAY_AD_SELCTOR_FIELD BIT_0_4 // identifies supported protocol +#define NWAY_AD_ABILITY BIT_5_12 // technologies that are supported +#define NWAY_AD_10T_HALF_DPX BIT_5 // 10BaseT Half Duplex capable +#define NWAY_AD_10T_FULL_DPX BIT_6 // 10BaseT full duplex capable +#define NWAY_AD_TX_HALF_DPX BIT_7 // TX Half Duplex capable +#define NWAY_AD_TX_FULL_DPX BIT_8 // TX full duplex capable +#define NWAY_AD_T4_CAPABLE BIT_9 // T4 capable +#define NWAY_AD_REMOTE_FAULT BIT_13 // indicates local remote fault +#define NWAY_AD_RESERVED BIT_14 // reserved +#define NWAY_AD_NEXT_PAGE BIT_15 // Next page (not supported) + +// Auto-Negotiation link partner ability register bit definitions +#define NWAY_LP_SELCTOR_FIELD BIT_0_4 // identifies supported protocol +#define NWAY_LP_ABILITY BIT_5_9 // technologies that are supported +#define NWAY_LP_REMOTE_FAULT BIT_13 // indicates partner remote fault +#define NWAY_LP_ACKNOWLEDGE BIT_14 // acknowledge +#define NWAY_LP_NEXT_PAGE BIT_15 // Next page (not supported) + +// Auto-Negotiation expansion register bit definitions +#define NWAY_EX_LP_NWAY BIT_0 // link partner is NWAY +#define NWAY_EX_PAGE_RECEIVED BIT_1 // link code word received +#define NWAY_EX_NEXT_PAGE_ABLE BIT_2 // local is next page able +#define NWAY_EX_LP_NEXT_PAGE_ABLE BIT_3 // partner is next page able +#define NWAY_EX_PARALLEL_DET_FLT BIT_4 // parallel detection fault +#define NWAY_EX_RESERVED BIT_5_15 // reserved + + +// PHY 100 Extended Register 0 bit definitions +#define PHY_100_ER0_FDX_INDIC BIT_0 // 1 = FDX, 0 = half duplex +#define PHY_100_ER0_SPEED_INDIC BIT_1 // 1 = 100mbs, 0= 10mbs +#define PHY_100_ER0_WAKE_UP BIT_2 // Wake up DAC +#define PHY_100_ER0_RESERVED BIT_3_4 // Reserved +#define PHY_100_ER0_REV_CNTRL BIT_5_7 // Revsion control (A step = 000) +#define PHY_100_ER0_FORCE_FAIL BIT_8 // Force Fail is enabled +#define PHY_100_ER0_TEST BIT_9_13 // Revsion control (A step = 000) +#define PHY_100_ER0_LINKDIS BIT_14 // Link integrity test is disabled +#define PHY_100_ER0_JABDIS BIT_15 // Jabber function is disabled + + +// PHY 100 Extended Register 1 bit definitions +#define PHY_100_ER1_RESERVED BIT_0_8 // Reserved +#define PHY_100_ER1_CH2_DET_ERR BIT_9 // Channel 2 EOF detection error +#define PHY_100_ER1_MANCH_CODE_ERR BIT_10 // Manchester code error +#define PHY_100_ER1_EOP_ERR BIT_11 // EOP error +#define PHY_100_ER1_BAD_CODE_ERR BIT_12 // bad code error +#define PHY_100_ER1_INV_CODE_ERR BIT_13 // invalid code error +#define PHY_100_ER1_DC_BAL_ERR BIT_14 // DC balance error +#define PHY_100_ER1_PAIR_SKEW_ERR BIT_15 // Pair skew error + +// National Semiconductor TX phy congestion control register bit definitions +#define NSC_TX_CONG_TXREADY BIT_10 // Makes TxReady an input +#define NSC_TX_CONG_ENABLE BIT_8 // Enables congestion control +#define NSC_TX_CONG_F_CONNECT BIT_5 // Enables congestion control + +// National Semiconductor TX phy speed indication register bit definitions +#define NSC_TX_SPD_INDC_SPEED BIT_6 // 0 = 100mb, 1=10mb + +//------------------------------------------------------------------------- +// Phy related constants +//------------------------------------------------------------------------- +#define PHY_503 0 +#define PHY_100_A 0x000003E0 +#define PHY_100_C 0x035002A8 +#define PHY_TX_ID 0x015002A8 +#define PHY_NSC_TX 0x5c002000 +#define PHY_OTHER 0xFFFF + +#define PHY_MODEL_REV_ID_MASK 0xFFF0FFFF +#define PARALLEL_DETECT 0 +#define N_WAY 1 + +#define RENEGOTIATE_TIME 35 // (3.5 Seconds) + +#define CONNECTOR_AUTO 0 +#define CONNECTOR_TPE 1 +#define CONNECTOR_MII 2 + +//------------------------------------------------------------------------- + +/* The Speedo3 Rx and Tx frame/buffer descriptors. */ +#pragma pack(1) +struct CB_Header { /* A generic descriptor. */ + UINT16 status; /* Offset 0. */ + UINT16 command; /* Offset 2. */ + UINT32 link; /* struct descriptor * */ +}; + +/* transmit command block structure */ +#pragma pack(1) +typedef struct s_TxCB { + struct CB_Header cb_header; + UINT32 PhysTBDArrayAddres; /* address of an array that contains + physical TBD pointers */ + UINT16 ByteCount; /* immediate data count = 0 always */ + UINT8 Threshold; + UINT8 TBDCount; + UINT8 ImmediateData[TX_BUFFER_SIZE]; + /* following fields are not seen by the 82557 */ + struct TBD { + UINT32 phys_buf_addr; + UINT32 buf_len; + } TBDArray[MAX_XMIT_FRAGMENTS]; + UINT32 PhysArrayAddr; /* in case the one in the header is lost */ + UINT32 PhysTCBAddress; /* for this TCB */ + struct s_TxCB *NextTCBVirtualLinkPtr; + struct s_TxCB *PrevTCBVirtualLinkPtr; + UINT64 free_data_ptr; // to be given to the upper layer when this xmit completes1 +}TxCB; + +/* The Speedo3 Rx and Tx buffer descriptors. */ +#pragma pack(1) +typedef struct s_RxFD { /* Receive frame descriptor. */ + struct CB_Header cb_header; + UINT32 rx_buf_addr; /* VOID * */ + UINT16 ActualCount; + UINT16 RFDSize; + UINT8 RFDBuffer[RX_BUFFER_SIZE]; + UINT8 forwarded; + UINT8 junk[3]; +}RxFD; + +/* Elements of the RxFD.status word. */ +#define RX_COMPLETE 0x8000 +#define RX_FRAME_OK 0x2000 + +/* Elements of the dump_statistics block. This block must be lword aligned. */ +#pragma pack(1) +struct speedo_stats { + UINT32 tx_good_frames; + UINT32 tx_coll16_errs; + UINT32 tx_late_colls; + UINT32 tx_underruns; + UINT32 tx_lost_carrier; + UINT32 tx_deferred; + UINT32 tx_one_colls; + UINT32 tx_multi_colls; + UINT32 tx_total_colls; + UINT32 rx_good_frames; + UINT32 rx_crc_errs; + UINT32 rx_align_errs; + UINT32 rx_resource_errs; + UINT32 rx_overrun_errs; + UINT32 rx_colls_errs; + UINT32 rx_runt_errs; + UINT32 done_marker; +}; +#pragma pack() + + +struct Krn_Mem{ + RxFD rx_ring[RX_BUFFER_COUNT]; + TxCB tx_ring[TX_BUFFER_COUNT]; + struct speedo_stats statistics; +}; +#define MEMORY_NEEDED sizeof(struct Krn_Mem) + +/* The parameters for a CmdConfigure operation. + There are so many options that it would be difficult to document each bit. + We mostly use the default or recommended settings. +*/ + +/* + *-------------------------------------------------------------------------- + * Configuration CB Parameter Bit Definitions + *-------------------------------------------------------------------------- + */ +// - Byte 0 (Default Value = 16h) +#define CFIG_BYTE_COUNT 0x16 // 22 Configuration Bytes + +//- Byte 1 (Default Value = 88h) +#define CFIG_TXRX_FIFO_LIMIT 0x88 + +//- Byte 2 (Default Value = 0) +#define CFIG_ADAPTIVE_IFS 0 + +//- Byte 3 (Default Value = 0, ALWAYS. This byte is RESERVED) +#define CFIG_RESERVED 0 + +//- Byte 4 (Default Value = 0. Default implies that Rx DMA cannot be +//- preempted). +#define CFIG_RXDMA_BYTE_COUNT 0 + +//- Byte 5 (Default Value = 80h. Default implies that Tx DMA cannot be +//- preempted. However, setting these counters is enabled.) +#define CFIG_DMBC_ENABLE 0x80 + +//- Byte 6 (Default Value = 33h. Late SCB enabled, No TNO interrupts, +//- CNA interrupts and do not save bad frames.) +#define CFIG_LATE_SCB 1 // BIT 0 +#define CFIG_TNO_INTERRUPT 0x4 // BIT 2 +#define CFIG_CI_INTERRUPT 0x8 // BIT 3 +#define CFIG_SAVE_BAD_FRAMES 0x80 // BIT_7 + +//- Byte 7 (Default Value = 7h. Discard short frames automatically and +//- attempt upto 3 retries on transmit.) +#define CFIG_DISCARD_SHORTRX 0x00001 +#define CFIG_URUN_RETRY BIT_1 OR BIT_2 + +//- Byte 8 (Default Value = 1. Enable MII mode.) +#define CFIG_503_MII BIT_0 + +//- Byte 9 (Default Value = 0, ALWAYS) + +//- Byte 10 (Default Value = 2Eh) +#define CFIG_NSAI BIT_3 +#define CFIG_PREAMBLE_LENGTH BIT_5 ;- Bit 5-4 = 1-0 +#define CFIG_NO_LOOPBACK 0 +#define CFIG_INTERNAL_LOOPBACK BIT_6 +#define CFIG_EXT_LOOPBACK BIT_7 +#define CFIG_EXT_PIN_LOOPBACK BIT_6 OR BIT_7 + +//- Byte 11 (Default Value = 0) +#define CFIG_LINEAR_PRIORITY 0 + +//- Byte 12 (Default Value = 60h) +#define CFIG_LPRIORITY_MODE 0 +#define CFIG_IFS 6 ;- 6 * 16 = 96 + +//- Byte 13 (Default Value = 0, ALWAYS) + +//- Byte 14 (Default Value = 0F2h, ALWAYS) + +//- Byte 15 (Default Value = E8h) +#define CFIG_PROMISCUOUS_MODE BIT_0 +#define CFIG_BROADCAST_DISABLE BIT_1 +#define CFIG_CRS_CDT BIT_7 + +//- Byte 16 (Default Value = 0, ALWAYS) + +//- Byte 17 (Default Value = 40h, ALWAYS) + +//- Byte 18 (Default Value = F2h) +#define CFIG_STRIPPING BIT_0 +#define CFIG_PADDING BIT_1 +#define CFIG_RX_CRC_TRANSFER BIT_2 + +//- Byte 19 (Default Value = 80h) +#define CFIG_FORCE_FDX BIT_6 +#define CFIG_FDX_PIN_ENABLE BIT_7 + +//- Byte 20 (Default Value = 3Fh) +#define CFIG_MULTI_IA BIT_6 + +//- Byte 21 (Default Value = 05) +#define CFIG_MC_ALL BIT_3 + +/*-----------------------------------------------------------------------*/ +#define D102_REVID 0x0b + +#define HALF_DUPLEX 1 +#define FULL_DUPLEX 2 + +typedef struct s_data_instance { + + UINT16 State; // stopped, started or initialized + UINT16 Bus; + UINT8 Device; + UINT8 Function; + UINT16 VendorID; + UINT16 DeviceID; + UINT16 RevID; + UINT16 SubVendorID; + UINT16 SubSystemID; + + UINT8 PermNodeAddress[PXE_MAC_LENGTH]; + UINT8 CurrentNodeAddress[PXE_MAC_LENGTH]; + UINT8 BroadcastNodeAddress[PXE_MAC_LENGTH]; + UINT32 Config[MAX_PCI_CONFIG_LEN]; + UINT32 NVData[MAX_EEPROM_LEN]; + + UINT32 ioaddr; + UINT32 flash_addr; + + UINT16 LinkSpeed; // actual link speed setting + UINT16 LinkSpeedReq; // requested (forced) link speed + UINT8 DuplexReq; // requested duplex + UINT8 Duplex; // Duplex set + UINT8 CableDetect; // 1 to detect and 0 not to detect the cable + UINT8 LoopBack; + + UINT16 TxBufCnt; + UINT16 TxBufSize; + UINT16 RxBufCnt; + UINT16 RxBufSize; + UINT32 RxTotals; + UINT32 TxTotals; + + UINT16 int_mask; + UINT16 Int_Status; + UINT16 PhyRecord[2]; // primary and secondary PHY record registers from eeprom + UINT8 PhyAddress; + UINT8 int_num; + UINT16 NVData_Len; + UINT32 MemoryLength; + + RxFD *rx_ring; // array of rx buffers + TxCB *tx_ring; // array of tx buffers + struct speedo_stats *statistics; + TxCB *FreeTxHeadPtr; + TxCB *FreeTxTailPtr; + RxFD *RFDTailPtr; + + UINT64 rx_phy_addr; // physical addresses + UINT64 tx_phy_addr; + UINT64 stat_phy_addr; + UINT64 MemoryPtr; + UINT64 Mapped_MemoryPtr; + + UINT64 xmit_done[TX_BUFFER_COUNT << 1]; // circular buffer + UINT16 xmit_done_head; // index into the xmit_done array + UINT16 xmit_done_tail; // where are we filling now (index into xmit_done) + UINT16 cur_rx_ind; // current RX Q head index + UINT16 FreeCBCount; + + BOOLEAN in_interrupt; + BOOLEAN in_transmit; + BOOLEAN Receive_Started; + UINT8 Rx_Filter; + UINT8 VersionFlag; // UNDI30 or UNDI31?? + UINT8 rsvd[3]; + + struct mc{ + UINT16 reserved [3]; // padding for this structure to make it 8 byte aligned + UINT16 list_len; + UINT8 mc_list[MAX_MCAST_ADDRESS_CNT][PXE_MAC_LENGTH]; // 8*32 is the size + } mcast_list; + + UINT64 Unique_ID; + + EFI_PCI_IO_PROTOCOL *Io_Function; + // + // Original PCI attributes + // + UINT64 OriginalPciAttributes; + + VOID (*Delay_30)(UINTN); // call back routine + VOID (*Virt2Phys_30)(UINT64 virtual_addr, UINT64 physical_ptr); // call back routine + VOID (*Block_30)(UINT32 enable); // call back routine + VOID (*Mem_Io_30)(UINT8 read_write, UINT8 len, UINT64 port, UINT64 buf_addr); + VOID (*Delay)(UINT64, UINTN); // call back routine + VOID (*Virt2Phys)(UINT64 unq_id, UINT64 virtual_addr, UINT64 physical_ptr); // call back routine + VOID (*Block)(UINT64 unq_id, UINT32 enable); // call back routine + VOID (*Mem_Io)(UINT64 unq_id, UINT8 read_write, UINT8 len, UINT64 port, + UINT64 buf_addr); + VOID (*Map_Mem)(UINT64 unq_id, UINT64 virtual_addr, UINT32 size, + UINT32 Direction, UINT64 mapped_addr); + VOID (*UnMap_Mem)(UINT64 unq_id, UINT64 virtual_addr, UINT32 size, + UINT32 Direction, UINT64 mapped_addr); + VOID (*Sync_Mem)(UINT64 unq_id, UINT64 virtual_addr, + UINT32 size, UINT32 Direction, UINT64 mapped_addr); +} NIC_DATA_INSTANCE; + +#pragma pack(1) +struct MC_CB_STRUCT{ + UINT16 count; + UINT8 m_list[MAX_MCAST_ADDRESS_CNT][ETHER_MAC_ADDR_LEN]; +}; +#pragma pack() + +#define FOUR_GIGABYTE (UINT64)0x100000000ULL + +#endif + diff --git a/OptionRomPkg/UndiRuntimeDxe/Init.c b/OptionRomPkg/UndiRuntimeDxe/Init.c new file mode 100644 index 0000000000..42dd5fd21f --- /dev/null +++ b/OptionRomPkg/UndiRuntimeDxe/Init.c @@ -0,0 +1,1055 @@ +/** @file + Initialization functions for EFI UNDI32 driver. + +Copyright (c) 2006 - 2008, Intel Corporation +All rights reserved. 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" +// +// Global Variables +// + +PXE_SW_UNDI *pxe_31 = NULL; // 3.1 entry +UNDI32_DEV *UNDI32DeviceList[MAX_NIC_INTERFACES]; +NII_TABLE *UndiDataPointer = NULL; + +// +// UNDI Class Driver Global Variables +// +EFI_DRIVER_BINDING_PROTOCOL gUndiDriverBinding = { + UndiDriverSupported, + UndiDriverStart, + UndiDriverStop, + 0xa, + NULL, + NULL +}; + + +/** + When address mapping changes to virtual this should make the appropriate + address conversions. + + (Standard Event handler) + + @return None + +**/ +VOID +EFIAPI +UndiNotifyVirtual ( + EFI_EVENT Event, + VOID *Context + ) +{ + UINT16 Index; + VOID *Pxe31Pointer; + + if (pxe_31 != NULL) { + Pxe31Pointer = (VOID *) pxe_31; + + EfiConvertPointer ( + EFI_OPTIONAL_PTR, + (VOID **) &Pxe31Pointer + ); + + // + // UNDI32DeviceList is an array of pointers + // + for (Index = 0; Index < pxe_31->IFcnt; Index++) { + UNDI32DeviceList[Index]->NIIProtocol_31.Id = (UINT64) (UINTN) Pxe31Pointer; + EfiConvertPointer ( + EFI_OPTIONAL_PTR, + (VOID **) &(UNDI32DeviceList[Index]) + ); + } + + EfiConvertPointer ( + EFI_OPTIONAL_PTR, + (VOID **) &(pxe_31->EntryPoint) + ); + pxe_31 = Pxe31Pointer; + } + + for (Index = 0; Index <= PXE_OPCODE_LAST_VALID; Index++) { + EfiConvertPointer ( + EFI_OPTIONAL_PTR, + (VOID **) &api_table[Index].api_ptr + ); + } +} + + +/** + When EFI is shuting down the boot services, we need to install a + configuration table for UNDI to work at runtime! + + (Standard Event handler) + + @return None + +**/ +VOID +EFIAPI +UndiNotifyExitBs ( + EFI_EVENT Event, + VOID *Context + ) +{ + InstallConfigTable (); +} + + +/** + Test to see if this driver supports ControllerHandle. Any ControllerHandle + than contains a DevicePath, PciIo protocol, Class code of 2, Vendor ID of 0x8086, + and DeviceId of (D100_DEVICE_ID || D102_DEVICE_ID || ICH3_DEVICE_ID_1 || + ICH3_DEVICE_ID_2 || ICH3_DEVICE_ID_3 || ICH3_DEVICE_ID_4 || ICH3_DEVICE_ID_5 || + ICH3_DEVICE_ID_6 || ICH3_DEVICE_ID_7 || ICH3_DEVICE_ID_8) can be supported. + + @param This Protocol instance pointer. + @param Controller Handle of device to test. + @param RemainingDevicePath Not used. + + @retval EFI_SUCCESS This driver supports this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +UndiDriverSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_PCI_IO_PROTOCOL *PciIo; + PCI_TYPE00 Pci; + + Status = gBS->OpenProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + NULL, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gBS->OpenProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + (VOID **) &PciIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = PciIo->Pci.Read ( + PciIo, + EfiPciIoWidthUint8, + 0, + sizeof (PCI_CONFIG_HEADER), + &Pci + ); + + if (!EFI_ERROR (Status)) { + Status = EFI_UNSUPPORTED; + + if (Pci.Hdr.ClassCode[2] == 0x02 && Pci.Hdr.VendorId == PCI_VENDOR_ID_INTEL) { + switch (Pci.Hdr.DeviceId) { + case D100_DEVICE_ID: + case D102_DEVICE_ID: + case ICH3_DEVICE_ID_1: + case ICH3_DEVICE_ID_2: + case ICH3_DEVICE_ID_3: + case ICH3_DEVICE_ID_4: + case ICH3_DEVICE_ID_5: + case ICH3_DEVICE_ID_6: + case ICH3_DEVICE_ID_7: + case ICH3_DEVICE_ID_8: + case 0x1039: + case 0x103A: + case 0x103B: + case 0x103C: + case 0x103D: + case 0x103E: + case 0x1050: + case 0x1051: + case 0x1052: + case 0x1053: + case 0x1054: + case 0x1055: + case 0x1056: + case 0x1057: + case 0x1059: + case 0x1064: + Status = EFI_SUCCESS; + } + } + } + + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + return Status; +} + + +/** + Start this driver on Controller by opening PciIo and DevicePath protocol. + Initialize PXE structures, create a copy of the Controller Device Path with the + NIC's MAC address appended to it, install the NetworkInterfaceIdentifier protocol + on the newly created Device Path. + + @param This Protocol instance pointer. + @param Controller Handle of device to work with. + @param RemainingDevicePath Not used, always produce all possible children. + + @retval EFI_SUCCESS This driver is added to Controller. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +UndiDriverStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_DEVICE_PATH_PROTOCOL *UndiDevicePath; + PCI_CONFIG_HEADER *CfgHdr; + UNDI32_DEV *UNDI32Device; + UINT16 NewCommand; + UINT8 *TmpPxePointer; + EFI_PCI_IO_PROTOCOL *PciIoFncs; + UINTN Len; + UINT64 Supports; + BOOLEAN PciAttributesSaved; + + Status = gBS->OpenProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + (VOID **) &PciIoFncs, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gBS->OpenProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + (VOID **) &UndiDevicePath, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (EFI_ERROR (Status)) { + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + return Status; + } + + PciAttributesSaved = FALSE; + + Status = gBS->AllocatePool ( + EfiRuntimeServicesData, + sizeof (UNDI32_DEV), + (VOID **) &UNDI32Device + ); + + if (EFI_ERROR (Status)) { + goto UndiError; + } + + ZeroMem ((CHAR8 *) UNDI32Device, sizeof (UNDI32_DEV)); + + // + // Get original PCI attributes + // + Status = PciIoFncs->Attributes ( + PciIoFncs, + EfiPciIoAttributeOperationGet, + 0, + &UNDI32Device->NicInfo.OriginalPciAttributes + ); + + if (EFI_ERROR (Status)) { + goto UndiErrorDeleteDevice; + } + PciAttributesSaved = TRUE; + + // + // allocate and initialize both (old and new) the !pxe structures here, + // there should only be one copy of each of these structure for any number + // of NICs this undi supports. Also, these structures need to be on a + // paragraph boundary as per the spec. so, while allocating space for these, + // make sure that there is space for 2 !pxe structures (old and new) and a + // 32 bytes padding for alignment adjustment (in case) + // + TmpPxePointer = NULL; + if (pxe_31 == NULL) { + Status = gBS->AllocatePool ( + EfiRuntimeServicesData, + (sizeof (PXE_SW_UNDI) + sizeof (PXE_SW_UNDI) + 32), + (VOID **) &TmpPxePointer + ); + + if (EFI_ERROR (Status)) { + goto UndiErrorDeleteDevice; + } + + ZeroMem ( + TmpPxePointer, + sizeof (PXE_SW_UNDI) + sizeof (PXE_SW_UNDI) + 32 + ); + // + // check for paragraph alignment here, assuming that the pointer is + // already 8 byte aligned. + // + if (((UINTN) TmpPxePointer & 0x0F) != 0) { + pxe_31 = (PXE_SW_UNDI *) ((UINTN) (TmpPxePointer + 8)); + } else { + pxe_31 = (PXE_SW_UNDI *) TmpPxePointer; + } + + PxeStructInit (pxe_31); + } + + UNDI32Device->NIIProtocol_31.Id = (UINT64) (UINTN) (pxe_31); + + Status = PciIoFncs->Attributes ( + PciIoFncs, + EfiPciIoAttributeOperationSupported, + 0, + &Supports + ); + if (!EFI_ERROR (Status)) { + Supports &= EFI_PCI_DEVICE_ENABLE; + Status = PciIoFncs->Attributes ( + PciIoFncs, + EfiPciIoAttributeOperationEnable, + Supports, + NULL + ); + } + // + // Read all the registers from device's PCI Configuration space + // + Status = PciIoFncs->Pci.Read ( + PciIoFncs, + EfiPciIoWidthUint32, + 0, + MAX_PCI_CONFIG_LEN, + &UNDI32Device->NicInfo.Config + ); + + CfgHdr = (PCI_CONFIG_HEADER *) &(UNDI32Device->NicInfo.Config[0]); + + // + // make sure that this device is a PCI bus master + // + + NewCommand = (UINT16) (CfgHdr->Command | PCI_COMMAND_MASTER | PCI_COMMAND_IO); + if (CfgHdr->Command != NewCommand) { + PciIoFncs->Pci.Write ( + PciIoFncs, + EfiPciIoWidthUint16, + PCI_COMMAND, + 1, + &NewCommand + ); + CfgHdr->Command = NewCommand; + } + + // + // make sure that the latency timer is at least 32 + // + if (CfgHdr->LatencyTimer < 32) { + CfgHdr->LatencyTimer = 32; + PciIoFncs->Pci.Write ( + PciIoFncs, + EfiPciIoWidthUint8, + PCI_LATENCY_TIMER, + 1, + &CfgHdr->LatencyTimer + ); + } + // + // the IfNum index for the current interface will be the total number + // of interfaces initialized so far + // + UNDI32Device->NIIProtocol_31.IfNum = pxe_31->IFcnt; + + PxeUpdate (&UNDI32Device->NicInfo, pxe_31); + + UNDI32Device->NicInfo.Io_Function = PciIoFncs; + UNDI32DeviceList[UNDI32Device->NIIProtocol_31.IfNum] = UNDI32Device; + UNDI32Device->Undi32BaseDevPath = UndiDevicePath; + + Status = AppendMac2DevPath ( + &UNDI32Device->Undi32DevPath, + UNDI32Device->Undi32BaseDevPath, + &UNDI32Device->NicInfo + ); + + if (Status != 0) { + goto UndiErrorDeletePxe; + } + + UNDI32Device->Signature = UNDI_DEV_SIGNATURE; + + UNDI32Device->NIIProtocol_31.Revision = EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL_REVISION_31; + UNDI32Device->NIIProtocol_31.Type = EfiNetworkInterfaceUndi; + UNDI32Device->NIIProtocol_31.MajorVer = PXE_ROMID_MAJORVER; + UNDI32Device->NIIProtocol_31.MinorVer = PXE_ROMID_MINORVER_31; + UNDI32Device->NIIProtocol_31.ImageSize = 0; + UNDI32Device->NIIProtocol_31.ImageAddr = 0; + UNDI32Device->NIIProtocol_31.Ipv6Supported = FALSE; + + UNDI32Device->NIIProtocol_31.StringId[0] = 'U'; + UNDI32Device->NIIProtocol_31.StringId[1] = 'N'; + UNDI32Device->NIIProtocol_31.StringId[2] = 'D'; + UNDI32Device->NIIProtocol_31.StringId[3] = 'I'; + + UNDI32Device->DeviceHandle = NULL; + + // + // install both the 3.0 and 3.1 NII protocols. + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &UNDI32Device->DeviceHandle, + &gEfiNetworkInterfaceIdentifierProtocolGuid_31, + &UNDI32Device->NIIProtocol_31, + &gEfiDevicePathProtocolGuid, + UNDI32Device->Undi32DevPath, + NULL + ); + + if (EFI_ERROR (Status)) { + goto UndiErrorDeleteDevicePath; + } + + // + // if the table exists, free it and alloc again, or alloc it directly + // + if (UndiDataPointer != NULL) { + Status = gBS->FreePool(UndiDataPointer); + } + if (EFI_ERROR (Status)) { + goto UndiErrorDeleteDevicePath; + } + + Len = (pxe_31->IFcnt * sizeof (NII_ENTRY)) + sizeof (UndiDataPointer); + Status = gBS->AllocatePool (EfiRuntimeServicesData, Len, (VOID **) &UndiDataPointer); + + if (EFI_ERROR (Status)) { + goto UndiErrorAllocDataPointer; + } + + // + // Open For Child Device + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + (VOID **) &PciIoFncs, + This->DriverBindingHandle, + UNDI32Device->DeviceHandle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + + return EFI_SUCCESS; +UndiErrorAllocDataPointer: + gBS->UninstallMultipleProtocolInterfaces ( + &UNDI32Device->DeviceHandle, + &gEfiNetworkInterfaceIdentifierProtocolGuid_31, + &UNDI32Device->NIIProtocol_31, + &gEfiDevicePathProtocolGuid, + UNDI32Device->Undi32DevPath, + NULL + ); + +UndiErrorDeleteDevicePath: + UNDI32DeviceList[UNDI32Device->NIIProtocol_31.IfNum] = NULL; + gBS->FreePool (UNDI32Device->Undi32DevPath); + +UndiErrorDeletePxe: + PxeUpdate (NULL, pxe_31); + if (TmpPxePointer != NULL) { + gBS->FreePool (TmpPxePointer); + + } + +UndiErrorDeleteDevice: + if (PciAttributesSaved) { + // + // Restore original PCI attributes + // + PciIoFncs->Attributes ( + PciIoFncs, + EfiPciIoAttributeOperationSet, + UNDI32Device->NicInfo.OriginalPciAttributes, + NULL + ); + } + + gBS->FreePool (UNDI32Device); + +UndiError: + gBS->CloseProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + return Status; +} + + +/** + Stop this driver on Controller by removing NetworkInterfaceIdentifier protocol and + closing the DevicePath and PciIo protocols on Controller. + + @param This Protocol instance pointer. + @param Controller Handle of device to stop driver on. + @param NumberOfChildren How many children need to be stopped. + @param ChildHandleBuffer Not used. + + @retval EFI_SUCCESS This driver is removed Controller. + @retval other This driver was not removed from this device. + +**/ +// TODO: EFI_DEVICE_ERROR - add return value to function comment +EFI_STATUS +EFIAPI +UndiDriverStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_STATUS Status; + BOOLEAN AllChildrenStopped; + UINTN Index; + UNDI32_DEV *UNDI32Device; + EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL *NIIProtocol; + EFI_PCI_IO_PROTOCOL *PciIo; + + // + // Complete all outstanding transactions to Controller. + // Don't allow any new transaction to Controller to be started. + // + if (NumberOfChildren == 0) { + + // + // Close the bus driver + // + Status = gBS->CloseProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + Status = gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + return Status; + } + + AllChildrenStopped = TRUE; + + for (Index = 0; Index < NumberOfChildren; Index++) { + + Status = gBS->OpenProtocol ( + ChildHandleBuffer[Index], + &gEfiNetworkInterfaceIdentifierProtocolGuid_31, + (VOID **) &NIIProtocol, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + + UNDI32Device = UNDI_DEV_FROM_THIS (NIIProtocol); + + // + // Restore original PCI attributes + // + Status = UNDI32Device->NicInfo.Io_Function->Attributes ( + UNDI32Device->NicInfo.Io_Function, + EfiPciIoAttributeOperationSet, + UNDI32Device->NicInfo.OriginalPciAttributes, + NULL + ); + ASSERT_EFI_ERROR (Status); + + Status = gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + ChildHandleBuffer[Index] + ); + + Status = gBS->UninstallMultipleProtocolInterfaces ( + ChildHandleBuffer[Index], + &gEfiDevicePathProtocolGuid, + UNDI32Device->Undi32DevPath, + &gEfiNetworkInterfaceIdentifierProtocolGuid_31, + &UNDI32Device->NIIProtocol_31, + NULL + ); + + if (EFI_ERROR (Status)) { + gBS->OpenProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + (VOID **) &PciIo, + This->DriverBindingHandle, + ChildHandleBuffer[Index], + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + } else { + gBS->FreePool (UNDI32Device->Undi32DevPath); + gBS->FreePool (UNDI32Device); + } + } + + if (EFI_ERROR (Status)) { + AllChildrenStopped = FALSE; + } + } + + if (!AllChildrenStopped) { + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; + +} + + +/** + Use the EFI boot services to produce a pause. This is also the routine which + gets replaced during RunTime by the O/S in the NIC_DATA_INSTANCE so it can + do it's own pause. + + @param UnqId Runtime O/S routine might use this, this temp + routine does not use it + @param MicroSeconds Determines the length of pause. + + @return none + +**/ +VOID +TmpDelay ( + IN UINT64 UnqId, + IN UINTN MicroSeconds + ) +{ + gBS->Stall ((UINT32) MicroSeconds); +} + + +/** + Use the PCI IO abstraction to issue memory or I/O reads and writes. This is also the routine which + gets replaced during RunTime by the O/S in the NIC_DATA_INSTANCE so it can do it's own I/O abstractions. + + @param UnqId Runtime O/S routine may use this field, this temp + routine does not. + @param ReadWrite Determine if it is an I/O or Memory Read/Write + Operation. + @param Len Determines the width of the data operation. + @param Port What port to Read/Write from. + @param BuffAddr Address to read to or write from. + + @return none + +**/ +VOID +TmpMemIo ( + IN UINT64 UnqId, + IN UINT8 ReadWrite, + IN UINT8 Len, + IN UINT64 Port, + IN UINT64 BuffAddr + ) +{ + EFI_PCI_IO_PROTOCOL_WIDTH Width; + NIC_DATA_INSTANCE *AdapterInfo; + + Width = (EFI_PCI_IO_PROTOCOL_WIDTH) 0; + AdapterInfo = (NIC_DATA_INSTANCE *) (UINTN) UnqId; + switch (Len) { + case 2: + Width = (EFI_PCI_IO_PROTOCOL_WIDTH) 1; + break; + + case 4: + Width = (EFI_PCI_IO_PROTOCOL_WIDTH) 2; + break; + + case 8: + Width = (EFI_PCI_IO_PROTOCOL_WIDTH) 3; + break; + } + + switch (ReadWrite) { + case PXE_IO_READ: + AdapterInfo->Io_Function->Io.Read ( + AdapterInfo->Io_Function, + Width, + 1, + Port, + 1, + (VOID *) (UINTN) (BuffAddr) + ); + break; + + case PXE_IO_WRITE: + AdapterInfo->Io_Function->Io.Write ( + AdapterInfo->Io_Function, + Width, + 1, + Port, + 1, + (VOID *) (UINTN) (BuffAddr) + ); + break; + + case PXE_MEM_READ: + AdapterInfo->Io_Function->Mem.Read ( + AdapterInfo->Io_Function, + Width, + 0, + Port, + 1, + (VOID *) (UINTN) (BuffAddr) + ); + break; + + case PXE_MEM_WRITE: + AdapterInfo->Io_Function->Mem.Write ( + AdapterInfo->Io_Function, + Width, + 0, + Port, + 1, + (VOID *) (UINTN) (BuffAddr) + ); + break; + } + + return ; +} + + +/** + Using the NIC data structure information, read the EEPROM to get the MAC address and then allocate space + for a new devicepath (**DevPtr) which will contain the original device path the NIC was found on (*BaseDevPtr) + and an added MAC node. + + @param DevPtr Pointer which will point to the newly created device + path with the MAC node attached. + @param BaseDevPtr Pointer to the device path which the UNDI device + driver is latching on to. + @param AdapterInfo Pointer to the NIC data structure information which + the UNDI driver is layering on.. + + @retval EFI_SUCCESS A MAC address was successfully appended to the Base + Device Path. + @retval other Not enough resources available to create new Device + Path node. + +**/ +EFI_STATUS +AppendMac2DevPath ( + IN OUT EFI_DEVICE_PATH_PROTOCOL **DevPtr, + IN EFI_DEVICE_PATH_PROTOCOL *BaseDevPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ) +{ + EFI_MAC_ADDRESS MACAddress; + PCI_CONFIG_HEADER *CfgHdr; + INT32 Val; + INT32 Index; + INT32 Index2; + UINT8 AddrLen; + MAC_ADDR_DEVICE_PATH MacAddrNode; + EFI_DEVICE_PATH_PROTOCOL *EndNode; + UINT8 *DevicePtr; + UINT16 TotalPathLen; + UINT16 BasePathLen; + EFI_STATUS Status; + + // + // set the environment ready (similar to UNDI_Start call) so that we can + // execute the other UNDI_ calls to get the mac address + // we are using undi 3.1 style + // + AdapterInfo->Delay = TmpDelay; + AdapterInfo->Virt2Phys = (VOID *) 0; + AdapterInfo->Block = (VOID *) 0; + AdapterInfo->Map_Mem = (VOID *) 0; + AdapterInfo->UnMap_Mem = (VOID *) 0; + AdapterInfo->Sync_Mem = (VOID *) 0; + AdapterInfo->Mem_Io = TmpMemIo; + // + // these tmp call-backs follow 3.1 undi style + // i.e. they have the unique_id parameter. + // + AdapterInfo->VersionFlag = 0x31; + AdapterInfo->Unique_ID = (UINT64) (UINTN) AdapterInfo; + + // + // undi init portion + // + CfgHdr = (PCI_CONFIG_HEADER *) &(AdapterInfo->Config[0]); + AdapterInfo->ioaddr = 0; + AdapterInfo->RevID = CfgHdr->RevID; + + AddrLen = E100bGetEepromAddrLen (AdapterInfo); + + for (Index = 0, Index2 = 0; Index < 3; Index++) { + Val = E100bReadEeprom (AdapterInfo, Index, AddrLen); + MACAddress.Addr[Index2++] = (UINT8) Val; + MACAddress.Addr[Index2++] = (UINT8) (Val >> 8); + } + + SetMem (MACAddress.Addr + Index2, sizeof (EFI_MAC_ADDRESS) - Index2, 0); + //for (; Index2 < sizeof (EFI_MAC_ADDRESS); Index2++) { + // MACAddress.Addr[Index2] = 0; + //} + // + // stop undi + // + AdapterInfo->Delay = (VOID *) 0; + AdapterInfo->Mem_Io = (VOID *) 0; + + // + // fill the mac address node first + // + ZeroMem ((CHAR8 *) &MacAddrNode, sizeof MacAddrNode); + CopyMem ( + (CHAR8 *) &MacAddrNode.MacAddress, + (CHAR8 *) &MACAddress, + sizeof (EFI_MAC_ADDRESS) + ); + + MacAddrNode.Header.Type = MESSAGING_DEVICE_PATH; + MacAddrNode.Header.SubType = MSG_MAC_ADDR_DP; + MacAddrNode.Header.Length[0] = sizeof (MacAddrNode); + MacAddrNode.Header.Length[1] = 0; + + // + // find the size of the base dev path. + // + EndNode = BaseDevPtr; + + while (!IsDevicePathEnd (EndNode)) { + EndNode = NextDevicePathNode (EndNode); + } + + BasePathLen = (UINT16) ((UINTN) (EndNode) - (UINTN) (BaseDevPtr)); + + // + // create space for full dev path + // + TotalPathLen = (UINT16) (BasePathLen + sizeof (MacAddrNode) + sizeof (EFI_DEVICE_PATH_PROTOCOL)); + + Status = gBS->AllocatePool ( + EfiRuntimeServicesData, + TotalPathLen, + (VOID **) &DevicePtr + ); + + if (Status != EFI_SUCCESS) { + return Status; + } + // + // copy the base path, mac addr and end_dev_path nodes + // + *DevPtr = (EFI_DEVICE_PATH_PROTOCOL *) DevicePtr; + CopyMem (DevicePtr, (CHAR8 *) BaseDevPtr, BasePathLen); + DevicePtr += BasePathLen; + CopyMem (DevicePtr, (CHAR8 *) &MacAddrNode, sizeof (MacAddrNode)); + DevicePtr += sizeof (MacAddrNode); + CopyMem (DevicePtr, (CHAR8 *) EndNode, sizeof (EFI_DEVICE_PATH_PROTOCOL)); + + return EFI_SUCCESS; +} + + +/** + Install a GUID/Pointer pair into the system's configuration table. + + none + + @retval EFI_SUCCESS Install a GUID/Pointer pair into the system's + configuration table. + @retval other Did not successfully install the GUID/Pointer pair + into the configuration table. + +**/ +// TODO: VOID - add argument and description to function comment +EFI_STATUS +InstallConfigTable ( + IN VOID + ) +{ + EFI_STATUS Status; + EFI_CONFIGURATION_TABLE *CfgPtr; + NII_TABLE *TmpData; + UINT16 Index; + NII_TABLE *UndiData; + + if (pxe_31 == NULL) { + return EFI_SUCCESS; + } + + if(UndiDataPointer == NULL) { + return EFI_SUCCESS; + } + + UndiData = (NII_TABLE *)UndiDataPointer; + + UndiData->NumEntries = pxe_31->IFcnt; + UndiData->NextLink = NULL; + + for (Index = 0; Index < pxe_31->IFcnt; Index++) { + UndiData->NiiEntry[Index].InterfacePointer = &UNDI32DeviceList[Index]->NIIProtocol_31; + UndiData->NiiEntry[Index].DevicePathPointer = UNDI32DeviceList[Index]->Undi32DevPath; + } + + // + // see if there is an entry in the config table already + // + CfgPtr = gST->ConfigurationTable; + + for (Index = 0; Index < gST->NumberOfTableEntries; Index++) { + Status = CompareGuid ( + &CfgPtr->VendorGuid, + &gEfiNetworkInterfaceIdentifierProtocolGuid_31 + ); + if (Status != EFI_SUCCESS) { + break; + } + + CfgPtr++; + } + + if (Index < gST->NumberOfTableEntries) { + TmpData = (NII_TABLE *) CfgPtr->VendorTable; + + // + // go to the last link + // + while (TmpData->NextLink != NULL) { + TmpData = TmpData->NextLink; + } + + TmpData->NextLink = UndiData; + + // + // 1st one in chain + // + UndiData = (NII_TABLE *) CfgPtr->VendorTable; + } + + // + // create an entry in the configuration table for our GUID + // + Status = gBS->InstallConfigurationTable ( + &gEfiNetworkInterfaceIdentifierProtocolGuid_31, + UndiData + ); + return Status; +} + +/** + +**/ +EFI_STATUS +EFIAPI +InitializeUndi( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_EVENT Event; + EFI_STATUS Status; + + Status = EfiLibInstallDriverBinding ( + ImageHandle, + SystemTable, + &gUndiDriverBinding, + ImageHandle + ); + ASSERT_EFI_ERROR (Status); + + Status = gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + UndiNotifyExitBs, + NULL, + &gEfiEventExitBootServicesGuid, + &Event + ); + ASSERT_EFI_ERROR (Status); + + Status = gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + UndiNotifyVirtual, + NULL, + &gEfiEventVirtualAddressChangeGuid, + &Event + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} diff --git a/OptionRomPkg/UndiRuntimeDxe/Undi32.h b/OptionRomPkg/UndiRuntimeDxe/Undi32.h new file mode 100644 index 0000000000..fc6e3525ce --- /dev/null +++ b/OptionRomPkg/UndiRuntimeDxe/Undi32.h @@ -0,0 +1,361 @@ +/** @file + EFI internal structures for the EFI UNDI driver. + +Copyright (c) 2006 - 2007, Intel Corporation. +All rights reserved. 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. + +**/ + +#ifndef _UNDI_32_H_ +#define _UNDI_32_H_ + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +#include "E100b.h" + +#define MAX_NIC_INTERFACES 16 + +#define EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL_REVISION_31 0x00010001 +#define PXE_ROMID_MINORVER_31 0x10 +#define PXE_STATFLAGS_DB_WRITE_TRUNCATED 0x2000 + +// +// UNDI_CALL_TABLE.state can have the following values +// +#define DONT_CHECK -1 +#define ANY_STATE -1 +#define MUST_BE_STARTED 1 +#define MUST_BE_INITIALIZED 2 + +#define UNDI_DEV_SIGNATURE SIGNATURE_32('u','n','d','i') +#define UNDI_DEV_FROM_THIS(a) CR(a, UNDI32_DEV, NIIProtocol_31, UNDI_DEV_SIGNATURE) +#define UNDI_DEV_FROM_NIC(a) CR(a, UNDI32_DEV, NicInfo, UNDI_DEV_SIGNATURE) + +typedef struct { + UINTN Signature; + EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL NIIProtocol_31; + EFI_HANDLE DeviceHandle; + EFI_DEVICE_PATH_PROTOCOL *Undi32BaseDevPath; + EFI_DEVICE_PATH_PROTOCOL *Undi32DevPath; + NIC_DATA_INSTANCE NicInfo; +} UNDI32_DEV; + +typedef struct { + UINT16 cpbsize; + UINT16 dbsize; + UINT16 opflags; + UINT16 state; + VOID (*api_ptr)(); +} UNDI_CALL_TABLE; + +typedef struct { + EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL *InterfacePointer; + EFI_DEVICE_PATH_PROTOCOL *DevicePathPointer; +} NII_ENTRY; + +typedef struct NII_CONFIG_ENTRY { + UINT32 NumEntries; + UINT32 Reserved; // padding for alignment + struct NII_CONFIG_ENTRY *NextLink; + NII_ENTRY NiiEntry[1]; +} NII_TABLE; + +typedef VOID (*ptr)(VOID); +typedef VOID (*bsptr_30)(UINTN); +typedef VOID (*virtphys_30)(UINT64, UINT64); +typedef VOID (*block_30)(UINT32); +typedef VOID (*mem_io_30)(UINT8, UINT8, UINT64, UINT64); + +typedef VOID (*bsptr)(UINT64, UINTN); +typedef VOID (*virtphys)(UINT64, UINT64, UINT64); +typedef VOID (*block)(UINT64, UINT32); +typedef VOID (*mem_io)(UINT64, UINT8, UINT8, UINT64, UINT64); + +typedef VOID (*map_mem)(UINT64, UINT64, UINT32, UINT32, UINT64); +typedef VOID (*unmap_mem)(UINT64, UINT64, UINT32, UINT32, UINT64); +typedef VOID (*sync_mem)(UINT64, UINT64, UINT32, UINT32, UINT64); + +extern UNDI_CALL_TABLE api_table[]; +extern PXE_SW_UNDI *pxe_31; // !pxe structure for 3.1 drivers +extern UNDI32_DEV *UNDI32DeviceList[MAX_NIC_INTERFACES]; + +// +// functions defined in e100b.c +// +UINT8 InByte (NIC_DATA_INSTANCE *AdapterInfo, UINT32 Port); +UINT16 InWord (NIC_DATA_INSTANCE *AdapterInfo, UINT32 Port); +UINT32 InLong (NIC_DATA_INSTANCE *AdapterInfo, UINT32 Port); +VOID OutByte (NIC_DATA_INSTANCE *AdapterInfo, UINT8 Data, UINT32 Port); +VOID OutWord (NIC_DATA_INSTANCE *AdapterInfo, UINT16 Data, UINT32 Port); +VOID OutLong (NIC_DATA_INSTANCE *AdapterInfo, UINT32 Data, UINT32 Port); + +UINTN E100bInit (NIC_DATA_INSTANCE *AdapterInfo); +UINTN E100bReset (NIC_DATA_INSTANCE *AdapterInfo, INT32 OpFlags); +UINTN E100bShutdown (NIC_DATA_INSTANCE *AdapterInfo); +UINTN E100bTransmit (NIC_DATA_INSTANCE *AdapterInfo, UINT64 cpb, UINT16 opflags); +UINTN E100bReceive (NIC_DATA_INSTANCE *AdapterInfo, UINT64 cpb, UINT64 db); +UINTN E100bSetfilter (NIC_DATA_INSTANCE *AdapterInfo, UINT16 New_filter, + UINT64 cpb, UINT32 cpbsize); +UINTN E100bStatistics(NIC_DATA_INSTANCE *AdapterInfo, UINT64 db, UINT16 dbsize); +UINT8 E100bSetupIAAddr (NIC_DATA_INSTANCE *AdapterInfo); +UINT8 E100bSetInterruptState (NIC_DATA_INSTANCE *AdapterInfo); + +UINT8 E100bGetEepromAddrLen (NIC_DATA_INSTANCE *AdapterInfo); +UINT16 E100bReadEeprom (NIC_DATA_INSTANCE *AdapterInfo, INT32 Location, UINT8 address_len); +INT16 E100bReadEepromAndStationAddress (NIC_DATA_INSTANCE *AdapterInfo); + +UINT16 next(UINT16); +UINT8 SetupCBlink (NIC_DATA_INSTANCE *AdapterInfo); +VOID SetFreeCB (NIC_DATA_INSTANCE *AdapterInfo,TxCB *); +TxCB *GetFreeCB (NIC_DATA_INSTANCE *AdapterInfo); +UINT16 CheckCBList (NIC_DATA_INSTANCE *AdapterInfo); + +UINT8 SelectiveReset (NIC_DATA_INSTANCE *AdapterInfo); +UINT16 InitializeChip (NIC_DATA_INSTANCE *AdapterInfo); +UINT8 SetupReceiveQueues (NIC_DATA_INSTANCE *AdapterInfo); +VOID Recycle_RFD (NIC_DATA_INSTANCE *AdapterInfo, UINT16); +VOID XmitWaitForCompletion (NIC_DATA_INSTANCE *AdapterInfo); +INT8 CommandWaitForCompletion (TxCB *cmd_ptr, NIC_DATA_INSTANCE *AdapterInfo); + +BOOLEAN PhyDetect (NIC_DATA_INSTANCE *AdapterInfo); +VOID PhyReset (NIC_DATA_INSTANCE *AdapterInfo); +VOID +MdiWrite ( + IN NIC_DATA_INSTANCE *AdapterInfo, + IN UINT8 RegAddress, + IN UINT8 PhyAddress, + IN UINT16 DataValue + ); + +VOID +MdiRead( + IN NIC_DATA_INSTANCE *AdapterInfo, + IN UINT8 RegAddress, + IN UINT8 PhyAddress, + IN OUT UINT16 *DataValue + ); + +BOOLEAN SetupPhy (NIC_DATA_INSTANCE *AdapterInfo); +VOID FindPhySpeedAndDpx (NIC_DATA_INSTANCE *AdapterInfo, UINT32 PhyId); + + + +// +// functions defined in init.c +// +EFI_STATUS +InstallConfigTable ( + IN VOID + ); + +EFI_STATUS +EFIAPI +InitializeUNDIDriver ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ); + +VOID +UNDI_notify_virtual ( + EFI_EVENT event, + VOID *context + ); + +VOID +EFIAPI +UndiNotifyExitBs ( + EFI_EVENT Event, + VOID *Context + ); + +EFI_STATUS +EFIAPI +UndiDriverSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +EFI_STATUS +EFIAPI +UndiDriverStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +EFI_STATUS +EFIAPI +UndiDriverStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +EFI_STATUS +AppendMac2DevPath ( + IN OUT EFI_DEVICE_PATH_PROTOCOL **DevPtr, + IN EFI_DEVICE_PATH_PROTOCOL *BaseDevPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ); + +VOID +TmpDelay ( + IN UINT64 UnqId, + IN UINTN MicroSeconds + ); + +VOID +TmpMemIo ( + IN UINT64 UnqId, + IN UINT8 ReadWrite, + IN UINT8 Len, + IN UINT64 Port, + IN UINT64 BufAddr + ); + +// +// functions defined in decode.c +// +VOID +UNDI_GetState ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ); + +VOID +UNDI_Start ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ); + +VOID +UNDI_Stop ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ); + +VOID +UNDI_GetInitInfo ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ); + +VOID +UNDI_GetConfigInfo ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ); + +VOID +UNDI_Initialize ( + IN PXE_CDB *CdbPtr, + NIC_DATA_INSTANCE *AdapterInfo + ); + +VOID +UNDI_Reset ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ); + +VOID +UNDI_Shutdown ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ); + +VOID +UNDI_Interrupt ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ); + +VOID +UNDI_RecFilter ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ); + +VOID +UNDI_StnAddr ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ); + +VOID +UNDI_Statistics ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ); + +VOID +UNDI_ip2mac ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ); + +VOID +UNDI_NVData ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ); + +VOID +UNDI_Status ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ); + +VOID +UNDI_FillHeader ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ); + +VOID +UNDI_Transmit ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ); + +VOID +UNDI_Receive ( + IN PXE_CDB *CdbPtr, + IN NIC_DATA_INSTANCE *AdapterInfo + ); + +VOID UNDI_APIEntry_new(UINT64); +VOID UNDI_APIEntry_Common(UINT64); + +PXE_IPV4 convert_mcip(PXE_MAC_ADDR *); +INT32 validate_mcip (PXE_MAC_ADDR *MCastAddr); + +VOID PxeStructInit (PXE_SW_UNDI *PxePtr); +VOID PxeUpdate (NIC_DATA_INSTANCE *NicPtr, PXE_SW_UNDI *PxePtr); + +#endif diff --git a/OptionRomPkg/UndiRuntimeDxe/UndiRuntimeDxe.inf b/OptionRomPkg/UndiRuntimeDxe/UndiRuntimeDxe.inf new file mode 100644 index 0000000000..2eee03efdf --- /dev/null +++ b/OptionRomPkg/UndiRuntimeDxe/UndiRuntimeDxe.inf @@ -0,0 +1,72 @@ +#/** @file +# Component description file for Undi module. +# +# This module provides support for Universal Network Driver Interface +# Copyright (c) 2006 - 2007, Intel Corporation +# +# All rights reserved. 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. +# +# +#**/ + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = UndiRuntimeDxe + FILE_GUID = A1f436EA-A127-4EF8-957C-8048606FF670 + MODULE_TYPE = DXE_RUNTIME_DRIVER + VERSION_STRING = 1.0 + + ENTRY_POINT = InitializeUndi + +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources.common] + Undi32.h + E100b.h + E100b.c + Decode.c + Init.c + + +[Packages] + MdePkg/MdePkg.dec + + +[LibraryClasses] + UefiLib + UefiBootServicesTableLib + BaseMemoryLib + DebugLib + UefiRuntimeLib + UefiDriverEntryPoint + BaseLib + +[Protocols] + gEfiNetworkInterfaceIdentifierProtocolGuid_31 + gEfiPciIoProtocolGuid + gEfiDevicePathProtocolGuid + +[Guids] + gEfiEventExitBootServicesGuid ## PRODUCES ## Event + gEfiEventVirtualAddressChangeGuid ## PRODUCES ## Event + +[Depex] + gEfiBdsArchProtocolGuid AND + gEfiCpuArchProtocolGuid AND + gEfiMetronomeArchProtocolGuid AND + gEfiMonotonicCounterArchProtocolGuid AND + gEfiRealTimeClockArchProtocolGuid AND + gEfiResetArchProtocolGuid AND + gEfiRuntimeArchProtocolGuid AND + gEfiSecurityArchProtocolGuid AND + gEfiTimerArchProtocolGuid AND + gEfiVariableWriteArchProtocolGuid AND + gEfiVariableArchProtocolGuid AND + gEfiWatchdogTimerArchProtocolGuid -- 2.39.2