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