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