| /** @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; | |
| } |