X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=blobdiff_plain;f=OptionRomPkg%2FUndiRuntimeDxe%2FE100b.c;fp=OptionRomPkg%2FUndiRuntimeDxe%2FE100b.c;h=c0600c5e5ae203502ecdc04a8d507dcfcf600855;hp=0000000000000000000000000000000000000000;hb=51ebae6b69f88c0e4b72202d7e97c5b280bd074a;hpb=e02a0e9245dd026e44c2f3158add4259df33d4a8 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; +}