[edk2-devel] [edk2-platforms: Patch 7/8] Drivers/OptionRomPkg: Import OptionRomPkg from edk2

Michael D Kinney michael.d.kinney at intel.com
Fri May 10 03:34:34 UTC 2019


https://bugzilla.tianocore.org/show_bug.cgi?id=1793

Import OptionRomPkg from edk2/master.

Cc: Ray Ni <ray.ni at intel.com>
Cc: Leif Lindholm <leif.lindholm at linaro.org>
Cc: Ard Biesheuvel <ard.biesheuvel at linaro.org>
Signed-off-by: Michael D Kinney <michael.d.kinney at intel.com>
---
 .../Application/BltLibSample/BltLibSample.c   |  279 ++
 .../Application/BltLibSample/BltLibSample.inf |   30 +
 .../AtapiPassThruDxe/AtapiPassThru.c          | 3410 ++++++++++++++++
 .../AtapiPassThruDxe/AtapiPassThru.h          | 1618 ++++++++
 .../AtapiPassThruDxe/AtapiPassThruDxe.inf     |   70 +
 .../AtapiPassThruDxe/ComponentName.c          |  169 +
 .../DriverSupportedEfiVersion.c               |   14 +
 .../FtdiUsbSerialDxe/CompatibleDevices.txt    |    5 +
 .../Bus/Usb/FtdiUsbSerialDxe/ComponentName.c  |  218 +
 .../FtdiUsbSerialDxe/FtdiUsbSerialDriver.c    | 2580 ++++++++++++
 .../FtdiUsbSerialDxe/FtdiUsbSerialDriver.h    |  589 +++
 .../Usb/FtdiUsbSerialDxe/FtdiUsbSerialDxe.inf |   55 +
 .../Bus/Usb/FtdiUsbSerialDxe/ReadMe.txt       |   32 +
 .../Bus/Usb/UsbNetworking/Ax88772/Ax88772.c   | 1318 ++++++
 .../Bus/Usb/UsbNetworking/Ax88772/Ax88772.h   |  969 +++++
 .../Bus/Usb/UsbNetworking/Ax88772/Ax88772.inf |   61 +
 .../Usb/UsbNetworking/Ax88772/ComponentName.c |  178 +
 .../Usb/UsbNetworking/Ax88772/DriverBinding.c |  507 +++
 .../Usb/UsbNetworking/Ax88772/SimpleNetwork.c | 1503 +++++++
 .../Bus/Usb/UsbNetworking/Ax88772b/Ax88772.c  |  875 ++++
 .../Bus/Usb/UsbNetworking/Ax88772b/Ax88772.h  | 1026 +++++
 .../Usb/UsbNetworking/Ax88772b/Ax88772b.inf   |   61 +
 .../UsbNetworking/Ax88772b/ComponentName.c    |  175 +
 .../UsbNetworking/Ax88772b/DriverBinding.c    |  696 ++++
 .../UsbNetworking/Ax88772b/SimpleNetwork.c    | 1657 ++++++++
 .../CirrusLogic5430Dxe/CirrusLogic5430.c      |  917 +++++
 .../CirrusLogic5430Dxe/CirrusLogic5430.h      |  432 ++
 .../CirrusLogic5430Dxe/CirrusLogic5430Dxe.inf |   84 +
 .../CirrusLogic5430GraphicsOutput.c           |  556 +++
 .../CirrusLogic5430Dxe/CirrusLogic5430I2c.c   |  427 ++
 .../CirrusLogic5430Dxe/CirrusLogic5430I2c.h   |   62 +
 .../CirrusLogic5430UgaDraw.c                  |  412 ++
 .../CirrusLogic5430Dxe/ComponentName.c        |  203 +
 .../DriverSupportedEfiVersion.c               |   14 +
 .../OptionRomPkg/CirrusLogic5430Dxe/Edid.c    |  525 +++
 Drivers/OptionRomPkg/Include/Library/BltLib.h |  253 ++
 .../FrameBufferBltLib/FrameBufferBltLib.c     |  744 ++++
 .../FrameBufferBltLib/FrameBufferBltLib.inf   |   29 +
 .../Library/GopBltLib/GopBltLib.c             |  449 +++
 .../Library/GopBltLib/GopBltLib.inf           |   31 +
 Drivers/OptionRomPkg/OptionRomPkg.dec         |   41 +
 Drivers/OptionRomPkg/OptionRomPkg.dsc         |  113 +
 Drivers/OptionRomPkg/ReadMe.txt               |   17 +
 .../UndiRuntimeDxe/ComponentName.c            |  359 ++
 Drivers/OptionRomPkg/UndiRuntimeDxe/Decode.c  | 1516 +++++++
 Drivers/OptionRomPkg/UndiRuntimeDxe/E100b.c   | 3541 +++++++++++++++++
 Drivers/OptionRomPkg/UndiRuntimeDxe/E100b.h   |  665 ++++
 Drivers/OptionRomPkg/UndiRuntimeDxe/Init.c    | 1051 +++++
 Drivers/OptionRomPkg/UndiRuntimeDxe/Undi32.h  |  439 ++
 .../OptionRomPkg/UndiRuntimeDxe/UndiAipImpl.c |  145 +
 .../UndiRuntimeDxe/UndiRuntimeDxe.inf         |   72 +
 51 files changed, 31192 insertions(+)
 create mode 100644 Drivers/OptionRomPkg/Application/BltLibSample/BltLibSample.c
 create mode 100644 Drivers/OptionRomPkg/Application/BltLibSample/BltLibSample.inf
 create mode 100644 Drivers/OptionRomPkg/AtapiPassThruDxe/AtapiPassThru.c
 create mode 100644 Drivers/OptionRomPkg/AtapiPassThruDxe/AtapiPassThru.h
 create mode 100644 Drivers/OptionRomPkg/AtapiPassThruDxe/AtapiPassThruDxe.inf
 create mode 100644 Drivers/OptionRomPkg/AtapiPassThruDxe/ComponentName.c
 create mode 100644 Drivers/OptionRomPkg/AtapiPassThruDxe/DriverSupportedEfiVersion.c
 create mode 100644 Drivers/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/CompatibleDevices.txt
 create mode 100644 Drivers/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/ComponentName.c
 create mode 100644 Drivers/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/FtdiUsbSerialDriver.c
 create mode 100644 Drivers/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/FtdiUsbSerialDriver.h
 create mode 100644 Drivers/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/FtdiUsbSerialDxe.inf
 create mode 100644 Drivers/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/ReadMe.txt
 create mode 100644 Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772/Ax88772.c
 create mode 100644 Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772/Ax88772.h
 create mode 100644 Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772/Ax88772.inf
 create mode 100644 Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772/ComponentName.c
 create mode 100644 Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772/DriverBinding.c
 create mode 100644 Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772/SimpleNetwork.c
 create mode 100644 Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/Ax88772.c
 create mode 100644 Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/Ax88772.h
 create mode 100644 Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/Ax88772b.inf
 create mode 100644 Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/ComponentName.c
 create mode 100644 Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/DriverBinding.c
 create mode 100644 Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/SimpleNetwork.c
 create mode 100644 Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430.c
 create mode 100644 Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430.h
 create mode 100644 Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430Dxe.inf
 create mode 100644 Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430GraphicsOutput.c
 create mode 100644 Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430I2c.c
 create mode 100644 Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430I2c.h
 create mode 100644 Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430UgaDraw.c
 create mode 100644 Drivers/OptionRomPkg/CirrusLogic5430Dxe/ComponentName.c
 create mode 100644 Drivers/OptionRomPkg/CirrusLogic5430Dxe/DriverSupportedEfiVersion.c
 create mode 100644 Drivers/OptionRomPkg/CirrusLogic5430Dxe/Edid.c
 create mode 100644 Drivers/OptionRomPkg/Include/Library/BltLib.h
 create mode 100644 Drivers/OptionRomPkg/Library/FrameBufferBltLib/FrameBufferBltLib.c
 create mode 100644 Drivers/OptionRomPkg/Library/FrameBufferBltLib/FrameBufferBltLib.inf
 create mode 100644 Drivers/OptionRomPkg/Library/GopBltLib/GopBltLib.c
 create mode 100644 Drivers/OptionRomPkg/Library/GopBltLib/GopBltLib.inf
 create mode 100644 Drivers/OptionRomPkg/OptionRomPkg.dec
 create mode 100644 Drivers/OptionRomPkg/OptionRomPkg.dsc
 create mode 100644 Drivers/OptionRomPkg/ReadMe.txt
 create mode 100644 Drivers/OptionRomPkg/UndiRuntimeDxe/ComponentName.c
 create mode 100644 Drivers/OptionRomPkg/UndiRuntimeDxe/Decode.c
 create mode 100644 Drivers/OptionRomPkg/UndiRuntimeDxe/E100b.c
 create mode 100644 Drivers/OptionRomPkg/UndiRuntimeDxe/E100b.h
 create mode 100644 Drivers/OptionRomPkg/UndiRuntimeDxe/Init.c
 create mode 100644 Drivers/OptionRomPkg/UndiRuntimeDxe/Undi32.h
 create mode 100644 Drivers/OptionRomPkg/UndiRuntimeDxe/UndiAipImpl.c
 create mode 100644 Drivers/OptionRomPkg/UndiRuntimeDxe/UndiRuntimeDxe.inf

diff --git a/Drivers/OptionRomPkg/Application/BltLibSample/BltLibSample.c b/Drivers/OptionRomPkg/Application/BltLibSample/BltLibSample.c
new file mode 100644
index 0000000000..6f901383b6
--- /dev/null
+++ b/Drivers/OptionRomPkg/Application/BltLibSample/BltLibSample.c
@@ -0,0 +1,279 @@
+/** @file
+  Example program using BltLib
+
+  Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Uefi.h>
+#include <Library/BltLib.h>
+#include <Library/DebugLib.h>
+#include <Library/UefiLib.h>
+#include <Library/UefiApplicationEntryPoint.h>
+#include <Library/UefiBootServicesTableLib.h>
+
+
+UINT64
+ReadTimestamp (
+  VOID
+  )
+{
+#if defined (MDE_CPU_IA32) || defined (MDE_CPU_X64)
+  return AsmReadTsc ();
+#else
+#error ReadTimestamp not supported for this architecture!
+#endif
+}
+
+UINT32
+Rand32 (
+  VOID
+  )
+{
+  UINTN    Found;
+  INTN     Bits;
+  UINT64   Tsc1;
+  UINT64   Tsc2;
+  UINT64   TscBits;
+  UINT32   R32;
+
+  R32 = 0;
+  Found = 0;
+  Tsc1 = ReadTimestamp ();
+  Tsc2 = ReadTimestamp ();
+  do {
+    Tsc2 = ReadTimestamp ();
+    TscBits = Tsc2 ^ Tsc1;
+    Bits = HighBitSet64 (TscBits);
+    if (Bits > 0) {
+      Bits = Bits - 1;
+    }
+    R32 = (UINT32)((R32 << Bits) |
+                   RShiftU64 (LShiftU64 (TscBits, (UINTN) (64 - Bits)), (UINTN) (64 - Bits)));
+    Found = Found + Bits;
+  } while (Found < 32);
+
+  return R32;
+}
+
+
+VOID
+TestFills (
+  VOID
+  )
+{
+  EFI_GRAPHICS_OUTPUT_BLT_PIXEL  Color;
+  UINTN                          Loop;
+  UINTN                          X;
+  UINTN                          Y;
+  UINTN                          W;
+  UINTN                          H;
+  UINTN                          Width;
+  UINTN                          Height;
+
+  BltLibGetSizes (&Width, &Height);
+  for (Loop = 0; Loop < 10000; Loop++) {
+    W = Width - (Rand32 () % Width);
+    H = Height - (Rand32 () % Height);
+    if (W != Width) {
+      X = Rand32 () % (Width - W);
+    } else {
+      X = 0;
+    }
+    if (H != Height) {
+      Y = Rand32 () % (Height - H);
+    } else {
+      Y = 0;
+    }
+    *(UINT32*) (&Color) = Rand32 () & 0xffffff;
+    BltLibVideoFill (&Color, X, Y, W, H);
+  }
+}
+
+
+VOID
+TestColor1 (
+  VOID
+  )
+{
+  EFI_GRAPHICS_OUTPUT_BLT_PIXEL  Color;
+  UINTN                          X;
+  UINTN                          Y;
+  UINTN                          Width;
+  UINTN                          Height;
+
+  BltLibGetSizes (&Width, &Height);
+  *(UINT32*) (&Color) = 0;
+
+  for (Y = 0; Y < Height; Y++) {
+    for (X = 0; X < Width; X++) {
+      Color.Red =   (UINT8) ((X * 0x100) / Width);
+      Color.Green = (UINT8) ((Y * 0x100) / Height);
+      Color.Blue =  (UINT8) ((Y * 0x100) / Height);
+      BltLibVideoFill (&Color, X, Y, 1, 1);
+    }
+  }
+}
+
+
+UINT32
+Uint32SqRt (
+  IN  UINT32  Uint32
+  )
+{
+  UINT32 Mask;
+  UINT32 SqRt;
+  UINT32 SqRtMask;
+  UINT32 Squared;
+
+  if (Uint32 == 0) {
+    return 0;
+  }
+
+  for (SqRt = 0, Mask = (UINT32) (1 << (HighBitSet32 (Uint32) / 2));
+       Mask != 0;
+       Mask = Mask >> 1
+      ) {
+    SqRtMask = SqRt | Mask;
+    //DEBUG ((EFI_D_INFO, "Uint32=0x%x SqRtMask=0x%x\n", Uint32, SqRtMask));
+    Squared = (UINT32) (SqRtMask * SqRtMask);
+    if (Squared > Uint32) {
+      continue;
+    } else if (Squared < Uint32) {
+      SqRt = SqRtMask;
+    } else {
+      return SqRtMask;
+    }
+  }
+
+  return SqRt;
+}
+
+
+UINT32
+Uint32Dist (
+  IN UINTN X,
+  IN UINTN Y
+  )
+{
+  return Uint32SqRt ((UINT32) ((X * X) + (Y * Y)));
+}
+
+UINT8
+GetTriColor (
+  IN UINTN ColorDist,
+  IN UINTN TriWidth
+  )
+{
+  return (UINT8) (((TriWidth - ColorDist) * 0x100) / TriWidth);
+  //return (((TriWidth * TriWidth - ColorDist * ColorDist) * 0x100) / (TriWidth * TriWidth));
+}
+
+VOID
+TestColor (
+  VOID
+  )
+{
+  EFI_GRAPHICS_OUTPUT_BLT_PIXEL  Color;
+  UINTN                          X, Y;
+  UINTN                          X1, X2, X3;
+  UINTN                          Y1, Y2;
+  UINTN                          LineWidth, TriWidth, ScreenWidth;
+  UINTN                          TriHeight, ScreenHeight;
+  UINT32                         ColorDist;
+
+  BltLibGetSizes (&ScreenWidth, &ScreenHeight);
+  *(UINT32*) (&Color) = 0;
+  BltLibVideoFill (&Color, 0, 0, ScreenWidth, ScreenHeight);
+
+  TriWidth = (UINTN) DivU64x32 (
+                       MultU64x32 (11547005, (UINT32) ScreenHeight),
+                       10000000
+                       );
+  TriHeight = (UINTN) DivU64x32 (
+                        MultU64x32 (8660254, (UINT32) ScreenWidth),
+                        10000000
+                        );
+  if (TriWidth > ScreenWidth) {
+    DEBUG ((EFI_D_INFO, "TriWidth at %d was too big\n", TriWidth));
+    TriWidth = ScreenWidth;
+  } else if (TriHeight > ScreenHeight) {
+    DEBUG ((EFI_D_INFO, "TriHeight at %d was too big\n", TriHeight));
+    TriHeight = ScreenHeight;
+  }
+
+  DEBUG ((EFI_D_INFO, "Triangle Width: %d; Height: %d\n", TriWidth, TriHeight));
+
+  X1 = (ScreenWidth - TriWidth) / 2;
+  X3 = X1 + TriWidth - 1;
+  X2 = (X1 + X3) / 2;
+  Y2 = (ScreenHeight - TriHeight) / 2;
+  Y1 = Y2 + TriHeight - 1;
+
+  for (Y = Y2; Y <= Y1; Y++) {
+    LineWidth =
+      (UINTN) DivU64x32 (
+                MultU64x32 (11547005, (UINT32) (Y - Y2)),
+                20000000
+                );
+    for (X = X2 - LineWidth; X < (X2 + LineWidth); X++) {
+      ColorDist = Uint32Dist(X - X1, Y1 - Y);
+      Color.Red = GetTriColor (ColorDist, TriWidth);
+
+      ColorDist = Uint32Dist((X < X2) ? X2 - X : X - X2, Y - Y2);
+      Color.Green = GetTriColor (ColorDist, TriWidth);
+
+      ColorDist = Uint32Dist(X3 - X, Y1 - Y);
+      Color.Blue = GetTriColor (ColorDist, TriWidth);
+
+      BltLibVideoFill (&Color, X, Y, 1, 1);
+    }
+  }
+}
+
+
+/**
+  The user Entry Point for Application. The user code starts with this function
+  as the real entry point for the application.
+
+  @param[in] ImageHandle    The firmware allocated handle for the EFI image.
+  @param[in] SystemTable    A pointer to the EFI System Table.
+
+  @retval EFI_SUCCESS       The entry point is executed successfully.
+  @retval other             Some error occurs when executing this entry point.
+
+**/
+EFI_STATUS
+EFIAPI
+UefiMain (
+  IN EFI_HANDLE        ImageHandle,
+  IN EFI_SYSTEM_TABLE  *SystemTable
+  )
+{
+  EFI_STATUS                     Status;
+  EFI_GRAPHICS_OUTPUT_PROTOCOL   *Gop;
+
+  Status = gBS->HandleProtocol (
+                  gST->ConsoleOutHandle,
+                  &gEfiGraphicsOutputProtocolGuid,
+                  (VOID **) &Gop
+                  );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = BltLibConfigure (
+             (VOID*)(UINTN) Gop->Mode->FrameBufferBase,
+             Gop->Mode->Info
+             );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  TestFills ();
+
+  TestColor ();
+
+  return EFI_SUCCESS;
+}
diff --git a/Drivers/OptionRomPkg/Application/BltLibSample/BltLibSample.inf b/Drivers/OptionRomPkg/Application/BltLibSample/BltLibSample.inf
new file mode 100644
index 0000000000..b544f960ab
--- /dev/null
+++ b/Drivers/OptionRomPkg/Application/BltLibSample/BltLibSample.inf
@@ -0,0 +1,30 @@
+## @file
+#  Test the BltLib interface
+#
+#  Copyright (c) 2008 - 2011, Intel Corporation. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = BltLibSample
+  FILE_GUID                      = f7763316-8c04-41d8-a87d-45b73c13c43c
+  MODULE_TYPE                    = UEFI_APPLICATION
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = UefiMain
+
+[Sources]
+  BltLibSample.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  OptionRomPkg/OptionRomPkg.dec
+
+[LibraryClasses]
+  BltLib
+  UefiApplicationEntryPoint
+  UefiLib
+
diff --git a/Drivers/OptionRomPkg/AtapiPassThruDxe/AtapiPassThru.c b/Drivers/OptionRomPkg/AtapiPassThruDxe/AtapiPassThru.c
new file mode 100644
index 0000000000..20de2bc392
--- /dev/null
+++ b/Drivers/OptionRomPkg/AtapiPassThruDxe/AtapiPassThru.c
@@ -0,0 +1,3410 @@
+/** @file
+  Copyright (c) 2006, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "AtapiPassThru.h"
+
+
+SCSI_COMMAND_SET     gEndTable = { 0xff, (DATA_DIRECTION) 0xff };
+
+///
+/// This table contains all the supported ATAPI commands.
+///
+SCSI_COMMAND_SET     gSupportedATAPICommands[] = {
+  { OP_INQUIRY,                     DataIn  },
+  { OP_LOAD_UNLOAD_CD,              NoData  },
+  { OP_MECHANISM_STATUS,            DataIn  },
+  { OP_MODE_SELECT_10,              DataOut },
+  { OP_MODE_SENSE_10,               DataIn  },
+  { OP_PAUSE_RESUME,                NoData  },
+  { OP_PLAY_AUDIO_10,               DataIn  },
+  { OP_PLAY_AUDIO_MSF,              DataIn  },
+  { OP_PLAY_CD,                     DataIn  },
+  { OP_PLAY_CD_MSF,                 DataIn  },
+  { OP_PREVENT_ALLOW_MEDIUM_REMOVAL,NoData  },
+  { OP_READ_10,                     DataIn  },
+  { OP_READ_12,                     DataIn  },
+  { OP_READ_CAPACITY,               DataIn  },
+  { OP_READ_CD,                     DataIn  },
+  { OP_READ_CD_MSF,                 DataIn  },
+  { OP_READ_HEADER,                 DataIn  },
+  { OP_READ_SUB_CHANNEL,            DataIn  },
+  { OP_READ_TOC,                    DataIn  },
+  { OP_REQUEST_SENSE,               DataIn  },
+  { OP_SCAN,                        NoData  },
+  { OP_SEEK_10,                     NoData  },
+  { OP_SET_CD_SPEED,                DataOut },
+  { OP_STOPPLAY_SCAN,               NoData  },
+  { OP_START_STOP_UNIT,             NoData  },
+  { OP_TEST_UNIT_READY,             NoData  },
+  { OP_FORMAT_UNIT,                 DataOut },
+  { OP_READ_FORMAT_CAPACITIES,      DataIn  },
+  { OP_VERIFY,                      DataOut },
+  { OP_WRITE_10,                    DataOut },
+  { OP_WRITE_12,                    DataOut },
+  { OP_WRITE_AND_VERIFY,            DataOut },
+  { 0xff,                           (DATA_DIRECTION) 0xff    }
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_SCSI_PASS_THRU_MODE gScsiPassThruMode = {
+  L"ATAPI Controller",
+  L"ATAPI Channel",
+  4,
+  EFI_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL | EFI_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL,
+  0
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_SCSI_PASS_THRU_PROTOCOL gScsiPassThruProtocolTemplate = {
+  &gScsiPassThruMode,
+  AtapiScsiPassThruFunction,
+  AtapiScsiPassThruGetNextDevice,
+  AtapiScsiPassThruBuildDevicePath,
+  AtapiScsiPassThruGetTargetLun,
+  AtapiScsiPassThruResetChannel,
+  AtapiScsiPassThruResetTarget
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_EXT_SCSI_PASS_THRU_MODE gExtScsiPassThruMode = {
+  4,
+  EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL | EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL,
+  0
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_EXT_SCSI_PASS_THRU_PROTOCOL gExtScsiPassThruProtocolTemplate = {
+  &gExtScsiPassThruMode,
+  AtapiExtScsiPassThruFunction,
+  AtapiExtScsiPassThruGetNextTargetLun,
+  AtapiExtScsiPassThruBuildDevicePath,
+  AtapiExtScsiPassThruGetTargetLun,
+  AtapiExtScsiPassThruResetChannel,
+  AtapiExtScsiPassThruResetTarget,
+  AtapiExtScsiPassThruGetNextTarget
+};
+
+EFI_DRIVER_BINDING_PROTOCOL gAtapiScsiPassThruDriverBinding = {
+  AtapiScsiPassThruDriverBindingSupported,
+  AtapiScsiPassThruDriverBindingStart,
+  AtapiScsiPassThruDriverBindingStop,
+  0x10,
+  NULL,
+  NULL
+};
+
+EFI_STATUS
+EFIAPI
+AtapiScsiPassThruDriverBindingSupported (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   Controller,
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
+  )
+/*++
+
+Routine Description:
+  Test to see if this driver supports ControllerHandle. Any ControllerHandle
+  that has gEfiPciIoProtocolGuid installed and is IDE Controller it will be supported.
+
+Arguments:
+
+  This                - Protocol instance pointer.
+  Controller          - Handle of device to test
+  RemainingDevicePath - Not used
+
+Returns:
+    EFI_STATUS
+
+--*/
+{
+  EFI_STATUS          Status;
+  EFI_PCI_IO_PROTOCOL *PciIo;
+  PCI_TYPE00          Pci;
+
+
+  //
+  // Open the IO Abstraction(s) needed to perform the supported test
+  //
+  Status = gBS->OpenProtocol (
+                  Controller,
+                  &gEfiPciIoProtocolGuid,
+                  (VOID **) &PciIo,
+                  This->DriverBindingHandle,
+                  Controller,
+                  EFI_OPEN_PROTOCOL_BY_DRIVER
+                  );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+  //
+  // Use the PCI I/O Protocol to see if Controller is a IDE Controller that
+  // can be managed by this driver.  Read the PCI Configuration Header
+  // for this device.
+  //
+  Status = PciIo->Pci.Read (
+                        PciIo,
+                        EfiPciIoWidthUint32,
+                        0,
+                        sizeof (Pci) / sizeof (UINT32),
+                        &Pci
+                        );
+  if (EFI_ERROR (Status)) {
+    gBS->CloseProtocol (
+           Controller,
+           &gEfiPciIoProtocolGuid,
+           This->DriverBindingHandle,
+           Controller
+           );
+    return EFI_UNSUPPORTED;
+  }
+
+  if (Pci.Hdr.ClassCode[2] != PCI_CLASS_MASS_STORAGE || Pci.Hdr.ClassCode[1] != PCI_CLASS_MASS_STORAGE_IDE) {
+
+    Status = EFI_UNSUPPORTED;
+  }
+
+  gBS->CloseProtocol (
+         Controller,
+         &gEfiPciIoProtocolGuid,
+         This->DriverBindingHandle,
+         Controller
+         );
+
+  return Status;
+}
+
+EFI_STATUS
+EFIAPI
+AtapiScsiPassThruDriverBindingStart (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   Controller,
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
+  )
+/*++
+
+Routine Description:
+  Create handles for IDE channels specified by RemainingDevicePath.
+  Install SCSI Pass Thru Protocol onto each created handle.
+
+Arguments:
+
+  This                - Protocol instance pointer.
+  Controller          - Handle of device to test
+  RemainingDevicePath - Not used
+
+Returns:
+    EFI_STATUS
+
+--*/
+{
+  EFI_STATUS          Status;
+  EFI_PCI_IO_PROTOCOL *PciIo;
+  UINT64              Supports;
+  UINT64              OriginalPciAttributes;
+  BOOLEAN             PciAttributesSaved;
+
+  PciIo = NULL;
+  Status = gBS->OpenProtocol (
+                  Controller,
+                  &gEfiPciIoProtocolGuid,
+                  (VOID **) &PciIo,
+                  This->DriverBindingHandle,
+                  Controller,
+                  EFI_OPEN_PROTOCOL_BY_DRIVER
+                  );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  PciAttributesSaved = FALSE;
+  //
+  // Save original PCI attributes
+  //
+  Status = PciIo->Attributes (
+                    PciIo,
+                    EfiPciIoAttributeOperationGet,
+                    0,
+                    &OriginalPciAttributes
+                    );
+
+  if (EFI_ERROR (Status)) {
+    goto Done;
+  }
+  PciAttributesSaved = TRUE;
+
+  Status = PciIo->Attributes (
+                    PciIo,
+                    EfiPciIoAttributeOperationSupported,
+                    0,
+                    &Supports
+                    );
+  if (!EFI_ERROR (Status)) {
+    Supports &= (EFI_PCI_DEVICE_ENABLE               |
+                 EFI_PCI_IO_ATTRIBUTE_IDE_PRIMARY_IO |
+                 EFI_PCI_IO_ATTRIBUTE_IDE_SECONDARY_IO);
+    Status = PciIo->Attributes (
+                      PciIo,
+                      EfiPciIoAttributeOperationEnable,
+                      Supports,
+                      NULL
+                      );
+  }
+  if (EFI_ERROR (Status)) {
+    goto Done;
+  }
+
+  //
+  // Create SCSI Pass Thru instance for the IDE channel.
+  //
+  Status = RegisterAtapiScsiPassThru (This, Controller, PciIo, OriginalPciAttributes);
+
+Done:
+  if (EFI_ERROR (Status)) {
+    if (PciAttributesSaved == TRUE) {
+      //
+      // Restore original PCI attributes
+      //
+      PciIo->Attributes (
+                      PciIo,
+                      EfiPciIoAttributeOperationSet,
+                      OriginalPciAttributes,
+                      NULL
+                      );
+    }
+
+    gBS->CloseProtocol (
+           Controller,
+           &gEfiPciIoProtocolGuid,
+           This->DriverBindingHandle,
+           Controller
+           );
+  }
+
+  return Status;
+}
+
+EFI_STATUS
+EFIAPI
+AtapiScsiPassThruDriverBindingStop (
+  IN  EFI_DRIVER_BINDING_PROTOCOL     *This,
+  IN  EFI_HANDLE                      Controller,
+  IN  UINTN                           NumberOfChildren,
+  IN  EFI_HANDLE                      *ChildHandleBuffer
+  )
+/*++
+
+Routine Description:
+
+  Stop this driver on ControllerHandle. Support stopping any child handles
+  created by this driver.
+
+Arguments:
+
+  This              - Protocol instance pointer.
+  Controller        - Handle of device to stop driver on
+  NumberOfChildren  - Number of Children in the ChildHandleBuffer
+  ChildHandleBuffer - List of handles for the children we need to stop.
+
+Returns:
+
+    EFI_STATUS
+
+--*/
+{
+  EFI_STATUS                      Status;
+  EFI_SCSI_PASS_THRU_PROTOCOL     *ScsiPassThru;
+  EFI_EXT_SCSI_PASS_THRU_PROTOCOL *ExtScsiPassThru;
+  ATAPI_SCSI_PASS_THRU_DEV        *AtapiScsiPrivate;
+
+  if (FeaturePcdGet (PcdSupportScsiPassThru)) {
+    Status = gBS->OpenProtocol (
+                    Controller,
+                    &gEfiScsiPassThruProtocolGuid,
+                    (VOID **) &ScsiPassThru,
+                    This->DriverBindingHandle,
+                    Controller,
+                    EFI_OPEN_PROTOCOL_GET_PROTOCOL
+                    );
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+    AtapiScsiPrivate = ATAPI_SCSI_PASS_THRU_DEV_FROM_THIS (ScsiPassThru);
+    if (FeaturePcdGet (PcdSupportExtScsiPassThru)) {
+      Status = gBS->UninstallMultipleProtocolInterfaces (
+                      Controller,
+                      &gEfiScsiPassThruProtocolGuid,
+                      &AtapiScsiPrivate->ScsiPassThru,
+                      &gEfiExtScsiPassThruProtocolGuid,
+                      &AtapiScsiPrivate->ExtScsiPassThru,
+                      NULL
+                      );
+    } else {
+      Status = gBS->UninstallMultipleProtocolInterfaces (
+                      Controller,
+                      &gEfiScsiPassThruProtocolGuid,
+                      &AtapiScsiPrivate->ScsiPassThru,
+                      NULL
+                      );
+    }
+  } else {
+    Status = gBS->OpenProtocol (
+                    Controller,
+                    &gEfiExtScsiPassThruProtocolGuid,
+                    (VOID **) &ExtScsiPassThru,
+                    This->DriverBindingHandle,
+                    Controller,
+                    EFI_OPEN_PROTOCOL_GET_PROTOCOL
+                    );
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+    AtapiScsiPrivate = ATAPI_EXT_SCSI_PASS_THRU_DEV_FROM_THIS (ExtScsiPassThru);
+    Status = gBS->UninstallMultipleProtocolInterfaces (
+                    Controller,
+                    &gEfiExtScsiPassThruProtocolGuid,
+                    &AtapiScsiPrivate->ExtScsiPassThru,
+                    NULL
+                    );
+  }
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Restore original PCI attributes
+  //
+  AtapiScsiPrivate->PciIo->Attributes (
+                  AtapiScsiPrivate->PciIo,
+                  EfiPciIoAttributeOperationSet,
+                  AtapiScsiPrivate->OriginalPciAttributes,
+                  NULL
+                  );
+
+  gBS->CloseProtocol (
+         Controller,
+         &gEfiPciIoProtocolGuid,
+         This->DriverBindingHandle,
+         Controller
+         );
+
+  gBS->FreePool (AtapiScsiPrivate);
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+RegisterAtapiScsiPassThru (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN  EFI_HANDLE                  Controller,
+  IN  EFI_PCI_IO_PROTOCOL         *PciIo,
+  IN  UINT64                      OriginalPciAttributes
+  )
+/*++
+
+Routine Description:
+  Attaches SCSI Pass Thru Protocol for specified IDE channel.
+
+Arguments:
+  This              - Protocol instance pointer.
+  Controller        - Parent device handle to the IDE channel.
+  PciIo             - PCI I/O protocol attached on the "Controller".
+
+Returns:
+  Always return EFI_SUCCESS unless installing SCSI Pass Thru Protocol failed.
+
+--*/
+{
+  EFI_STATUS                Status;
+  ATAPI_SCSI_PASS_THRU_DEV  *AtapiScsiPrivate;
+  IDE_REGISTERS_BASE_ADDR   IdeRegsBaseAddr[ATAPI_MAX_CHANNEL];
+
+  AtapiScsiPrivate = AllocateZeroPool (sizeof (ATAPI_SCSI_PASS_THRU_DEV));
+  if (AtapiScsiPrivate == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  AtapiScsiPrivate->Signature = ATAPI_SCSI_PASS_THRU_DEV_SIGNATURE;
+  AtapiScsiPrivate->Handle    = Controller;
+
+  //
+  // will reset the IoPort inside each API function.
+  //
+  AtapiScsiPrivate->IoPort                = NULL;
+  AtapiScsiPrivate->PciIo                 = PciIo;
+  AtapiScsiPrivate->OriginalPciAttributes = OriginalPciAttributes;
+
+  //
+  // Obtain IDE IO port registers' base addresses
+  //
+  Status = GetIdeRegistersBaseAddr (PciIo, IdeRegsBaseAddr);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  InitAtapiIoPortRegisters(AtapiScsiPrivate, IdeRegsBaseAddr);
+
+  //
+  // Initialize the LatestTargetId to MAX_TARGET_ID.
+  //
+  AtapiScsiPrivate->LatestTargetId  = MAX_TARGET_ID;
+  AtapiScsiPrivate->LatestLun       = 0;
+
+  Status = InstallScsiPassThruProtocols (&Controller, AtapiScsiPrivate);
+
+  return Status;
+}
+
+EFI_STATUS
+EFIAPI
+AtapiScsiPassThruFunction (
+  IN EFI_SCSI_PASS_THRU_PROTOCOL                        *This,
+  IN UINT32                                             Target,
+  IN UINT64                                             Lun,
+  IN OUT EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET         *Packet,
+  IN EFI_EVENT                                          Event OPTIONAL
+  )
+/*++
+
+Routine Description:
+
+  Implements EFI_SCSI_PASS_THRU_PROTOCOL.PassThru() function.
+
+Arguments:
+
+  This:     The EFI_SCSI_PASS_THRU_PROTOCOL instance.
+  Target:   The Target ID of the ATAPI device to send the SCSI
+            Request Packet. To ATAPI devices attached on an IDE
+            Channel, Target ID 0 indicates Master device;Target
+            ID 1 indicates Slave device.
+  Lun:      The LUN of the ATAPI device to send the SCSI Request
+            Packet. To the ATAPI device, Lun is always 0.
+  Packet:   The SCSI Request Packet to send to the ATAPI device
+            specified by Target and Lun.
+  Event:    If non-blocking I/O is not supported then Event is ignored,
+            and blocking I/O is performed.
+            If Event is NULL, then blocking I/O is performed.
+            If Event is not NULL and non blocking I/O is supported,
+            then non-blocking I/O is performed, and Event will be signaled
+            when the SCSI Request Packet completes.
+
+Returns:
+
+   EFI_STATUS
+
+--*/
+{
+  ATAPI_SCSI_PASS_THRU_DEV               *AtapiScsiPrivate;
+  EFI_STATUS                             Status;
+
+  AtapiScsiPrivate = ATAPI_SCSI_PASS_THRU_DEV_FROM_THIS (This);
+
+  //
+  // Target is not allowed beyond MAX_TARGET_ID
+  //
+  if ((Target > MAX_TARGET_ID) || (Lun != 0)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // check the data fields in Packet parameter.
+  //
+  Status = CheckSCSIRequestPacket (Packet);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // If Request Packet targets at the IDE channel itself,
+  // do nothing.
+  //
+  if (Target == This->Mode->AdapterId) {
+    Packet->TransferLength = 0;
+    return EFI_SUCCESS;
+  }
+
+  //
+  // According to Target ID, reset the Atapi I/O Register mapping
+  // (Target Id in [0,1] area, using AtapiIoPortRegisters[0],
+  //  Target Id in [2,3] area, using AtapiIoPortRegisters[1]
+  //
+  if ((Target / 2) == 0) {
+    Target = Target % 2;
+    AtapiScsiPrivate->IoPort = &AtapiScsiPrivate->AtapiIoPortRegisters[0];
+  } else {
+    Target = Target % 2;
+    AtapiScsiPrivate->IoPort = &AtapiScsiPrivate->AtapiIoPortRegisters[1];
+  }
+
+  //
+  // the ATAPI SCSI interface does not support non-blocking I/O
+  // ignore the Event parameter
+  //
+  // Performs blocking I/O.
+  //
+  Status = SubmitBlockingIoCommand (AtapiScsiPrivate, Target, Packet);
+  return Status;
+}
+
+EFI_STATUS
+EFIAPI
+AtapiScsiPassThruGetNextDevice (
+  IN  EFI_SCSI_PASS_THRU_PROTOCOL    *This,
+  IN OUT UINT32                      *Target,
+  IN OUT UINT64                      *Lun
+  )
+/*++
+
+Routine Description:
+
+  Used to retrieve the list of legal Target IDs for SCSI devices
+  on a SCSI channel.
+
+Arguments:
+
+  This                  - Protocol instance pointer.
+  Target                - On input, a pointer to the Target ID of a SCSI
+                          device present on the SCSI channel.  On output,
+                          a pointer to the Target ID of the next SCSI device
+                          present on a SCSI channel.  An input value of
+                          0xFFFFFFFF retrieves the Target ID of the first
+                          SCSI device present on a SCSI channel.
+  Lun                   - On input, a pointer to the LUN of a SCSI device
+                          present on the SCSI channel. On output, a pointer
+                          to the LUN of the next SCSI device present on
+                          a SCSI channel.
+Returns:
+
+  EFI_SUCCESS           - The Target ID and Lun of the next SCSI device
+                          on the SCSI channel was returned in Target and Lun.
+  EFI_NOT_FOUND         - There are no more SCSI devices on this SCSI channel.
+  EFI_INVALID_PARAMETER - Target is not 0xFFFFFFFF,and Target and Lun were not
+                           returned on a previous call to GetNextDevice().
+--*/
+{
+  ATAPI_SCSI_PASS_THRU_DEV  *AtapiScsiPrivate;
+
+  //
+  // Retrieve Device Private Data Structure.
+  //
+  AtapiScsiPrivate = ATAPI_SCSI_PASS_THRU_DEV_FROM_THIS (This);
+
+  //
+  // Check whether Target is valid.
+  //
+  if (Target == NULL || Lun == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if ((*Target != 0xFFFFFFFF) &&
+      ((*Target != AtapiScsiPrivate->LatestTargetId) ||
+      (*Lun != AtapiScsiPrivate->LatestLun))) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (*Target == MAX_TARGET_ID) {
+    return EFI_NOT_FOUND;
+  }
+
+  if (*Target == 0xFFFFFFFF) {
+    *Target = 0;
+  } else {
+    *Target = AtapiScsiPrivate->LatestTargetId + 1;
+  }
+
+  *Lun = 0;
+
+  //
+  // Update the LatestTargetId.
+  //
+  AtapiScsiPrivate->LatestTargetId  = *Target;
+  AtapiScsiPrivate->LatestLun       = *Lun;
+
+  return EFI_SUCCESS;
+
+}
+
+EFI_STATUS
+EFIAPI
+AtapiScsiPassThruBuildDevicePath (
+  IN     EFI_SCSI_PASS_THRU_PROTOCOL    *This,
+  IN     UINT32                         Target,
+  IN     UINT64                         Lun,
+  IN OUT EFI_DEVICE_PATH_PROTOCOL       **DevicePath
+  )
+/*++
+
+Routine Description:
+
+  Used to allocate and build a device path node for a SCSI device
+  on a SCSI channel. Would not build device path for a SCSI Host Controller.
+
+Arguments:
+
+  This                  - Protocol instance pointer.
+  Target                - The Target ID of the SCSI device for which
+                          a device path node is to be allocated and built.
+  Lun                   - The LUN of the SCSI device for which a device
+                          path node is to be allocated and built.
+  DevicePath            - A pointer to a single device path node that
+                          describes the SCSI device specified by
+                          Target and Lun. This function is responsible
+                          for allocating the buffer DevicePath with the boot
+                          service AllocatePool().  It is the caller's
+                          responsibility to free DevicePath when the caller
+                          is finished with DevicePath.
+  Returns:
+  EFI_SUCCESS           - The device path node that describes the SCSI device
+                          specified by Target and Lun was allocated and
+                          returned in DevicePath.
+  EFI_NOT_FOUND         - The SCSI devices specified by Target and Lun does
+                          not exist on the SCSI channel.
+  EFI_INVALID_PARAMETER - DevicePath is NULL.
+  EFI_OUT_OF_RESOURCES  - There are not enough resources to allocate
+                          DevicePath.
+--*/
+{
+  EFI_DEV_PATH              *Node;
+
+
+  //
+  // Validate parameters passed in.
+  //
+
+  if (DevicePath == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // can not build device path for the SCSI Host Controller.
+  //
+  if ((Target > (MAX_TARGET_ID - 1)) || (Lun != 0)) {
+    return EFI_NOT_FOUND;
+  }
+
+  Node = AllocateZeroPool (sizeof (EFI_DEV_PATH));
+  if (Node == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  Node->DevPath.Type    = MESSAGING_DEVICE_PATH;
+  Node->DevPath.SubType = MSG_ATAPI_DP;
+  SetDevicePathNodeLength (&Node->DevPath, sizeof (ATAPI_DEVICE_PATH));
+
+  Node->Atapi.PrimarySecondary  = (UINT8) (Target / 2);
+  Node->Atapi.SlaveMaster       = (UINT8) (Target % 2);
+  Node->Atapi.Lun               = (UINT16) Lun;
+
+  *DevicePath                   = (EFI_DEVICE_PATH_PROTOCOL *) Node;
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+AtapiScsiPassThruGetTargetLun (
+  IN  EFI_SCSI_PASS_THRU_PROTOCOL    *This,
+  IN  EFI_DEVICE_PATH_PROTOCOL       *DevicePath,
+  OUT UINT32                         *Target,
+  OUT UINT64                         *Lun
+  )
+/*++
+
+Routine Description:
+
+  Used to translate a device path node to a Target ID and LUN.
+
+Arguments:
+
+  This                  - Protocol instance pointer.
+  DevicePath            - A pointer to the device path node that
+                          describes a SCSI device on the SCSI channel.
+  Target                - A pointer to the Target ID of a SCSI device
+                          on the SCSI channel.
+  Lun                   - A pointer to the LUN of a SCSI device on
+                          the SCSI channel.
+Returns:
+
+  EFI_SUCCESS           - DevicePath was successfully translated to a
+                          Target ID and LUN, and they were returned
+                          in Target and Lun.
+  EFI_INVALID_PARAMETER - DevicePath/Target/Lun is NULL.
+  EFI_UNSUPPORTED       - This driver does not support the device path
+                          node type in DevicePath.
+  EFI_NOT_FOUND         - A valid translation from DevicePath to a
+                          Target ID and LUN does not exist.
+--*/
+{
+  EFI_DEV_PATH  *Node;
+
+  //
+  // Validate parameters passed in.
+  //
+  if (DevicePath == NULL || Target == NULL || Lun == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Check whether the DevicePath belongs to SCSI_DEVICE_PATH
+  //
+  if ((DevicePath->Type != MESSAGING_DEVICE_PATH) ||
+      (DevicePath->SubType != MSG_ATAPI_DP) ||
+      (DevicePathNodeLength(DevicePath) != sizeof(ATAPI_DEVICE_PATH))) {
+    return EFI_UNSUPPORTED;
+  }
+
+  Node    = (EFI_DEV_PATH *) DevicePath;
+
+  *Target = Node->Atapi.PrimarySecondary * 2 + Node->Atapi.SlaveMaster;
+  *Lun    = Node->Atapi.Lun;
+
+  if (*Target > (MAX_TARGET_ID - 1) || *Lun != 0) {
+    return EFI_NOT_FOUND;
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+AtapiScsiPassThruResetChannel (
+  IN  EFI_SCSI_PASS_THRU_PROTOCOL   *This
+  )
+/*++
+
+Routine Description:
+
+  Resets a SCSI channel.This operation resets all the
+  SCSI devices connected to the SCSI channel.
+
+Arguments:
+
+  This                  - Protocol instance pointer.
+
+Returns:
+
+  EFI_SUCCESS           - The SCSI channel was reset.
+  EFI_UNSUPPORTED       - The SCSI channel does not support
+                          a channel reset operation.
+  EFI_DEVICE_ERROR      - A device error occurred while
+                          attempting to reset the SCSI channel.
+  EFI_TIMEOUT           - A timeout occurred while attempting
+                          to reset the SCSI channel.
+--*/
+{
+  UINT8                     DeviceControlValue;
+  ATAPI_SCSI_PASS_THRU_DEV  *AtapiScsiPrivate;
+  UINT8                     Index;
+  BOOLEAN                   ResetFlag;
+
+  AtapiScsiPrivate = ATAPI_SCSI_PASS_THRU_DEV_FROM_THIS (This);
+  ResetFlag = FALSE;
+
+  //
+  // Reset both Primary channel and Secondary channel.
+  // so, the IoPort pointer must point to the right I/O Register group
+  //
+  for (Index = 0; Index < 2; Index++) {
+    //
+    // Reset
+    //
+    AtapiScsiPrivate->IoPort  = &AtapiScsiPrivate->AtapiIoPortRegisters[Index];
+
+    DeviceControlValue        = 0;
+    //
+    // set SRST bit to initiate soft reset
+    //
+    DeviceControlValue |= SRST;
+    //
+    // disable Interrupt
+    //
+    DeviceControlValue |= BIT1;
+    WritePortB (
+      AtapiScsiPrivate->PciIo,
+      AtapiScsiPrivate->IoPort->Alt.DeviceControl,
+      DeviceControlValue
+      );
+
+    //
+    // Wait 10us
+    //
+    gBS->Stall (10);
+
+    //
+    // Clear SRST bit
+    // 0xfb:1111,1011
+    //
+    DeviceControlValue &= 0xfb;
+
+    WritePortB (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Alt.DeviceControl, DeviceControlValue);
+
+    //
+    // slave device needs at most 31s to clear BSY
+    //
+    if (StatusWaitForBSYClear (AtapiScsiPrivate, 31000000) != EFI_TIMEOUT) {
+      ResetFlag = TRUE;
+    }
+  }
+
+  if (ResetFlag) {
+    return EFI_SUCCESS;
+  }
+
+  return EFI_TIMEOUT;
+}
+
+EFI_STATUS
+EFIAPI
+AtapiScsiPassThruResetTarget (
+  IN EFI_SCSI_PASS_THRU_PROTOCOL    *This,
+  IN UINT32                         Target,
+  IN UINT64                         Lun
+  )
+/*++
+
+Routine Description:
+
+  Resets a SCSI device that is connected to a SCSI channel.
+
+Arguments:
+
+  This                  - Protocol instance pointer.
+  Target                - The Target ID of the SCSI device to reset.
+  Lun                   - The LUN of the SCSI device to reset.
+
+Returns:
+
+  EFI_SUCCESS           - The SCSI device specified by Target and
+                          Lun was reset.
+  EFI_UNSUPPORTED       - The SCSI channel does not support a target
+                          reset operation.
+  EFI_INVALID_PARAMETER - Target or Lun are invalid.
+  EFI_DEVICE_ERROR      - A device error occurred while attempting
+                          to reset the SCSI device specified by Target
+                          and Lun.
+  EFI_TIMEOUT           - A timeout occurred while attempting to reset
+                          the SCSI device specified by Target and Lun.
+--*/
+{
+  ATAPI_SCSI_PASS_THRU_DEV  *AtapiScsiPrivate;
+  UINT8                     Command;
+  UINT8                     DeviceSelect;
+
+  AtapiScsiPrivate = ATAPI_SCSI_PASS_THRU_DEV_FROM_THIS (This);
+
+  if ((Target > MAX_TARGET_ID) || (Lun != 0)) {
+    return EFI_INVALID_PARAMETER;
+  }
+  //
+  // Directly return EFI_SUCCESS if want to reset the host controller
+  //
+  if (Target == This->Mode->AdapterId) {
+    return EFI_SUCCESS;
+  }
+
+  //
+  // According to Target ID, reset the Atapi I/O Register mapping
+  // (Target Id in [0,1] area, using AtapiIoPortRegisters[0],
+  //  Target Id in [2,3] area, using AtapiIoPortRegisters[1]
+  //
+  if ((Target / 2) == 0) {
+    AtapiScsiPrivate->IoPort = &AtapiScsiPrivate->AtapiIoPortRegisters[0];
+  } else {
+    AtapiScsiPrivate->IoPort = &AtapiScsiPrivate->AtapiIoPortRegisters[1];
+  }
+
+  //
+  // for ATAPI device, no need to wait DRDY ready after device selecting.
+  //
+  // bit7 and bit5 are both set to 1 for backward compatibility
+  //
+  DeviceSelect = (UINT8) (((BIT7 | BIT5) | (Target << 4)));
+  WritePortB (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Head, DeviceSelect);
+
+  Command = ATAPI_SOFT_RESET_CMD;
+  WritePortB (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Reg.Command, Command);
+
+  //
+  // BSY clear is the only status return to the host by the device
+  // when reset is complete.
+  // slave device needs at most 31s to clear BSY
+  //
+  if (EFI_ERROR (StatusWaitForBSYClear (AtapiScsiPrivate, 31000000))) {
+    return EFI_TIMEOUT;
+  }
+
+  //
+  // stall 5 seconds to make the device status stable
+  //
+  gBS->Stall (5000000);
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+AtapiExtScsiPassThruFunction (
+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL                    *This,
+  IN UINT8                                              *Target,
+  IN UINT64                                             Lun,
+  IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET     *Packet,
+  IN EFI_EVENT                                          Event OPTIONAL
+  )
+/*++
+
+Routine Description:
+
+  Implements EFI_EXT_SCSI_PASS_THRU_PROTOCOL.PassThru() function.
+
+Arguments:
+
+  This:     The EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance.
+  Target:   The Target ID of the ATAPI device to send the SCSI
+            Request Packet. To ATAPI devices attached on an IDE
+            Channel, Target ID 0 indicates Master device;Target
+            ID 1 indicates Slave device.
+  Lun:      The LUN of the ATAPI device to send the SCSI Request
+            Packet. To the ATAPI device, Lun is always 0.
+  Packet:   The SCSI Request Packet to send to the ATAPI device
+            specified by Target and Lun.
+  Event:    If non-blocking I/O is not supported then Event is ignored,
+            and blocking I/O is performed.
+            If Event is NULL, then blocking I/O is performed.
+            If Event is not NULL and non blocking I/O is supported,
+            then non-blocking I/O is performed, and Event will be signaled
+            when the SCSI Request Packet completes.
+
+Returns:
+
+   EFI_STATUS
+
+--*/
+{
+  EFI_STATUS                          Status;
+  ATAPI_SCSI_PASS_THRU_DEV            *AtapiScsiPrivate;
+  UINT8                                TargetId;
+
+  AtapiScsiPrivate = ATAPI_EXT_SCSI_PASS_THRU_DEV_FROM_THIS (This);
+
+  //
+  // For ATAPI device, UINT8 is enough to represent the SCSI ID on channel.
+  //
+  TargetId = Target[0];
+
+  //
+  // Target is not allowed beyond MAX_TARGET_ID
+  //
+  if ((TargetId > MAX_TARGET_ID) || (Lun != 0)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // check the data fields in Packet parameter.
+  //
+  Status = CheckExtSCSIRequestPacket (Packet);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // If Request Packet targets at the IDE channel itself,
+  // do nothing.
+  //
+  if (TargetId == (UINT8)This->Mode->AdapterId) {
+    Packet->InTransferLength = Packet->OutTransferLength = 0;
+    return EFI_SUCCESS;
+  }
+
+  //
+  // According to Target ID, reset the Atapi I/O Register mapping
+  // (Target Id in [0,1] area, using AtapiIoPortRegisters[0],
+  //  Target Id in [2,3] area, using AtapiIoPortRegisters[1]
+  //
+  if ((TargetId / 2) == 0) {
+    TargetId = (UINT8) (TargetId % 2);
+    AtapiScsiPrivate->IoPort = &AtapiScsiPrivate->AtapiIoPortRegisters[0];
+  } else {
+    TargetId = (UINT8) (TargetId % 2);
+    AtapiScsiPrivate->IoPort = &AtapiScsiPrivate->AtapiIoPortRegisters[1];
+  }
+
+  //
+  // the ATAPI SCSI interface does not support non-blocking I/O
+  // ignore the Event parameter
+  //
+  // Performs blocking I/O.
+  //
+  Status = SubmitExtBlockingIoCommand (AtapiScsiPrivate, TargetId, Packet);
+  return Status;
+}
+
+EFI_STATUS
+EFIAPI
+AtapiExtScsiPassThruGetNextTargetLun (
+  IN  EFI_EXT_SCSI_PASS_THRU_PROTOCOL    *This,
+  IN OUT UINT8                           **Target,
+  IN OUT UINT64                          *Lun
+  )
+/*++
+
+Routine Description:
+
+  Used to retrieve the list of legal Target IDs for SCSI devices
+  on a SCSI channel.
+
+Arguments:
+
+  This                  - Protocol instance pointer.
+  Target                - On input, a pointer to the Target ID of a SCSI
+                          device present on the SCSI channel.  On output,
+                          a pointer to the Target ID of the next SCSI device
+                          present on a SCSI channel.  An input value of
+                          0xFFFFFFFF retrieves the Target ID of the first
+                          SCSI device present on a SCSI channel.
+  Lun                   - On input, a pointer to the LUN of a SCSI device
+                          present on the SCSI channel. On output, a pointer
+                          to the LUN of the next SCSI device present on
+                          a SCSI channel.
+Returns:
+
+  EFI_SUCCESS           - The Target ID and Lun of the next SCSI device
+                          on the SCSI channel was returned in Target and Lun.
+  EFI_NOT_FOUND         - There are no more SCSI devices on this SCSI channel.
+  EFI_INVALID_PARAMETER - Target is not 0xFFFFFFFF,and Target and Lun were not
+                           returned on a previous call to GetNextDevice().
+--*/
+{
+  UINT8                          ByteIndex;
+  UINT8                          TargetId;
+  UINT8                          ScsiId[TARGET_MAX_BYTES];
+  ATAPI_SCSI_PASS_THRU_DEV       *AtapiScsiPrivate;
+
+  //
+  // Retrieve Device Private Data Structure.
+  //
+  AtapiScsiPrivate = ATAPI_EXT_SCSI_PASS_THRU_DEV_FROM_THIS (This);
+
+  //
+  // Check whether Target is valid.
+  //
+  if (*Target == NULL || Lun == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  SetMem (ScsiId, TARGET_MAX_BYTES, 0xFF);
+
+  TargetId = (*Target)[0];
+
+  //
+  // For ATAPI device, we use UINT8 to represent the SCSI ID on channel.
+  //
+  if (CompareMem(*Target, ScsiId, TARGET_MAX_BYTES) != 0) {
+    for (ByteIndex = 1; ByteIndex < TARGET_MAX_BYTES; ByteIndex++) {
+      if ((*Target)[ByteIndex] != 0) {
+        return EFI_INVALID_PARAMETER;
+      }
+    }
+  }
+
+  if ((CompareMem(*Target, ScsiId, TARGET_MAX_BYTES) != 0) &&
+      ((TargetId != AtapiScsiPrivate->LatestTargetId) ||
+      (*Lun != AtapiScsiPrivate->LatestLun))) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (TargetId == MAX_TARGET_ID) {
+    return EFI_NOT_FOUND;
+  }
+
+  if (CompareMem(*Target, ScsiId, TARGET_MAX_BYTES) == 0) {
+    SetMem (*Target, TARGET_MAX_BYTES,0);
+  } else {
+    (*Target)[0] = (UINT8) (AtapiScsiPrivate->LatestTargetId + 1);
+  }
+
+  *Lun = 0;
+
+  //
+  // Update the LatestTargetId.
+  //
+  AtapiScsiPrivate->LatestTargetId  = (*Target)[0];
+  AtapiScsiPrivate->LatestLun       = *Lun;
+
+  return EFI_SUCCESS;
+
+}
+
+EFI_STATUS
+EFIAPI
+AtapiExtScsiPassThruBuildDevicePath (
+  IN     EFI_EXT_SCSI_PASS_THRU_PROTOCOL    *This,
+  IN     UINT8                              *Target,
+  IN     UINT64                             Lun,
+  IN OUT EFI_DEVICE_PATH_PROTOCOL           **DevicePath
+  )
+/*++
+
+Routine Description:
+
+  Used to allocate and build a device path node for a SCSI device
+  on a SCSI channel. Would not build device path for a SCSI Host Controller.
+
+Arguments:
+
+  This                  - Protocol instance pointer.
+  Target                - The Target ID of the SCSI device for which
+                          a device path node is to be allocated and built.
+  Lun                   - The LUN of the SCSI device for which a device
+                          path node is to be allocated and built.
+  DevicePath            - A pointer to a single device path node that
+                          describes the SCSI device specified by
+                          Target and Lun. This function is responsible
+                          for allocating the buffer DevicePath with the boot
+                          service AllocatePool().  It is the caller's
+                          responsibility to free DevicePath when the caller
+                          is finished with DevicePath.
+  Returns:
+  EFI_SUCCESS           - The device path node that describes the SCSI device
+                          specified by Target and Lun was allocated and
+                          returned in DevicePath.
+  EFI_NOT_FOUND         - The SCSI devices specified by Target and Lun does
+                          not exist on the SCSI channel.
+  EFI_INVALID_PARAMETER - DevicePath is NULL.
+  EFI_OUT_OF_RESOURCES  - There are not enough resources to allocate
+                          DevicePath.
+--*/
+{
+  EFI_DEV_PATH                   *Node;
+  UINT8                          TargetId;
+
+  TargetId = Target[0];
+
+  //
+  // Validate parameters passed in.
+  //
+
+  if (DevicePath == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // can not build device path for the SCSI Host Controller.
+  //
+  if ((TargetId > (MAX_TARGET_ID - 1)) || (Lun != 0)) {
+    return EFI_NOT_FOUND;
+  }
+
+  Node = AllocateZeroPool (sizeof (EFI_DEV_PATH));
+  if (Node == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  Node->DevPath.Type    = MESSAGING_DEVICE_PATH;
+  Node->DevPath.SubType = MSG_ATAPI_DP;
+  SetDevicePathNodeLength (&Node->DevPath, sizeof (ATAPI_DEVICE_PATH));
+
+  Node->Atapi.PrimarySecondary  = (UINT8) (TargetId / 2);
+  Node->Atapi.SlaveMaster       = (UINT8) (TargetId % 2);
+  Node->Atapi.Lun               = (UINT16) Lun;
+
+  *DevicePath                   = (EFI_DEVICE_PATH_PROTOCOL *) Node;
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+AtapiExtScsiPassThruGetTargetLun (
+  IN  EFI_EXT_SCSI_PASS_THRU_PROTOCOL    *This,
+  IN  EFI_DEVICE_PATH_PROTOCOL           *DevicePath,
+  OUT UINT8                              **Target,
+  OUT UINT64                             *Lun
+  )
+/*++
+
+Routine Description:
+
+  Used to translate a device path node to a Target ID and LUN.
+
+Arguments:
+
+  This                  - Protocol instance pointer.
+  DevicePath            - A pointer to the device path node that
+                          describes a SCSI device on the SCSI channel.
+  Target                - A pointer to the Target ID of a SCSI device
+                          on the SCSI channel.
+  Lun                   - A pointer to the LUN of a SCSI device on
+                          the SCSI channel.
+Returns:
+
+  EFI_SUCCESS           - DevicePath was successfully translated to a
+                          Target ID and LUN, and they were returned
+                          in Target and Lun.
+  EFI_INVALID_PARAMETER - DevicePath/Target/Lun is NULL.
+  EFI_UNSUPPORTED       - This driver does not support the device path
+                          node type in DevicePath.
+  EFI_NOT_FOUND         - A valid translation from DevicePath to a
+                          Target ID and LUN does not exist.
+--*/
+{
+  EFI_DEV_PATH  *Node;
+
+  //
+  // Validate parameters passed in.
+  //
+  if (DevicePath == NULL || Target == NULL || Lun == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Check whether the DevicePath belongs to SCSI_DEVICE_PATH
+  //
+  if ((DevicePath->Type != MESSAGING_DEVICE_PATH) ||
+      (DevicePath->SubType != MSG_ATAPI_DP) ||
+      (DevicePathNodeLength(DevicePath) != sizeof(ATAPI_DEVICE_PATH))) {
+    return EFI_UNSUPPORTED;
+  }
+
+  ZeroMem (*Target, TARGET_MAX_BYTES);
+
+  Node    = (EFI_DEV_PATH *) DevicePath;
+
+  (*Target)[0] = (UINT8) (Node->Atapi.PrimarySecondary * 2 + Node->Atapi.SlaveMaster);
+  *Lun    = Node->Atapi.Lun;
+
+  if ((*Target)[0] > (MAX_TARGET_ID - 1) || *Lun != 0) {
+    return EFI_NOT_FOUND;
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+AtapiExtScsiPassThruResetChannel (
+  IN  EFI_EXT_SCSI_PASS_THRU_PROTOCOL   *This
+  )
+/*++
+
+Routine Description:
+
+  Resets a SCSI channel.This operation resets all the
+  SCSI devices connected to the SCSI channel.
+
+Arguments:
+
+  This                  - Protocol instance pointer.
+
+Returns:
+
+  EFI_SUCCESS           - The SCSI channel was reset.
+  EFI_UNSUPPORTED       - The SCSI channel does not support
+                          a channel reset operation.
+  EFI_DEVICE_ERROR      - A device error occurred while
+                          attempting to reset the SCSI channel.
+  EFI_TIMEOUT           - A timeout occurred while attempting
+                          to reset the SCSI channel.
+--*/
+{
+  UINT8                         DeviceControlValue;
+  UINT8                         Index;
+  ATAPI_SCSI_PASS_THRU_DEV      *AtapiScsiPrivate;
+  BOOLEAN                       ResetFlag;
+
+  AtapiScsiPrivate = ATAPI_EXT_SCSI_PASS_THRU_DEV_FROM_THIS (This);
+  ResetFlag = FALSE;
+  //
+  // Reset both Primary channel and Secondary channel.
+  // so, the IoPort pointer must point to the right I/O Register group
+  // And if there is a channel reset successfully, return EFI_SUCCESS.
+  //
+  for (Index = 0; Index < 2; Index++) {
+    //
+    // Reset
+    //
+    AtapiScsiPrivate->IoPort  = &AtapiScsiPrivate->AtapiIoPortRegisters[Index];
+
+    DeviceControlValue        = 0;
+    //
+    // set SRST bit to initiate soft reset
+    //
+    DeviceControlValue |= SRST;
+    //
+    // disable Interrupt
+    //
+    DeviceControlValue |= BIT1;
+    WritePortB (
+      AtapiScsiPrivate->PciIo,
+      AtapiScsiPrivate->IoPort->Alt.DeviceControl,
+      DeviceControlValue
+      );
+
+    //
+    // Wait 10us
+    //
+    gBS->Stall (10);
+
+    //
+    // Clear SRST bit
+    // 0xfb:1111,1011
+    //
+    DeviceControlValue &= 0xfb;
+
+    WritePortB (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Alt.DeviceControl, DeviceControlValue);
+
+    //
+    // slave device needs at most 31s to clear BSY
+    //
+    if (StatusWaitForBSYClear (AtapiScsiPrivate, 31000000) != EFI_TIMEOUT) {
+      ResetFlag = TRUE;
+    }
+  }
+
+  if (ResetFlag) {
+    return EFI_SUCCESS;
+  }
+
+  return EFI_TIMEOUT;
+}
+
+EFI_STATUS
+EFIAPI
+AtapiExtScsiPassThruResetTarget (
+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL    *This,
+  IN UINT8                              *Target,
+  IN UINT64                             Lun
+  )
+/*++
+
+Routine Description:
+
+  Resets a SCSI device that is connected to a SCSI channel.
+
+Arguments:
+
+  This                  - Protocol instance pointer.
+  Target                - The Target ID of the SCSI device to reset.
+  Lun                   - The LUN of the SCSI device to reset.
+
+Returns:
+
+  EFI_SUCCESS           - The SCSI device specified by Target and
+                          Lun was reset.
+  EFI_UNSUPPORTED       - The SCSI channel does not support a target
+                          reset operation.
+  EFI_INVALID_PARAMETER - Target or Lun are invalid.
+  EFI_DEVICE_ERROR      - A device error occurred while attempting
+                          to reset the SCSI device specified by Target
+                          and Lun.
+  EFI_TIMEOUT           - A timeout occurred while attempting to reset
+                          the SCSI device specified by Target and Lun.
+--*/
+{
+  UINT8                         Command;
+  UINT8                         DeviceSelect;
+  UINT8                         TargetId;
+  ATAPI_SCSI_PASS_THRU_DEV      *AtapiScsiPrivate;
+
+  AtapiScsiPrivate = ATAPI_EXT_SCSI_PASS_THRU_DEV_FROM_THIS (This);
+  TargetId = Target[0];
+
+  if ((TargetId > MAX_TARGET_ID) || (Lun != 0)) {
+    return EFI_INVALID_PARAMETER;
+  }
+  //
+  // Directly return EFI_SUCCESS if want to reset the host controller
+  //
+  if (TargetId == This->Mode->AdapterId) {
+    return EFI_SUCCESS;
+  }
+
+  //
+  // According to Target ID, reset the Atapi I/O Register mapping
+  // (Target Id in [0,1] area, using AtapiIoPortRegisters[0],
+  //  Target Id in [2,3] area, using AtapiIoPortRegisters[1]
+  //
+  if ((TargetId / 2) == 0) {
+    AtapiScsiPrivate->IoPort = &AtapiScsiPrivate->AtapiIoPortRegisters[0];
+  } else {
+    AtapiScsiPrivate->IoPort = &AtapiScsiPrivate->AtapiIoPortRegisters[1];
+  }
+
+  //
+  // for ATAPI device, no need to wait DRDY ready after device selecting.
+  //
+  // bit7 and bit5 are both set to 1 for backward compatibility
+  //
+  DeviceSelect = (UINT8) ((BIT7 | BIT5) | (TargetId << 4));
+  WritePortB (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Head, DeviceSelect);
+
+  Command = ATAPI_SOFT_RESET_CMD;
+  WritePortB (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Reg.Command, Command);
+
+  //
+  // BSY clear is the only status return to the host by the device
+  // when reset is complete.
+  // slave device needs at most 31s to clear BSY
+  //
+  if (EFI_ERROR (StatusWaitForBSYClear (AtapiScsiPrivate, 31000000))) {
+    return EFI_TIMEOUT;
+  }
+
+  //
+  // stall 5 seconds to make the device status stable
+  //
+  gBS->Stall (5000000);
+
+  return EFI_SUCCESS;
+}
+
+
+EFI_STATUS
+EFIAPI
+AtapiExtScsiPassThruGetNextTarget (
+  IN  EFI_EXT_SCSI_PASS_THRU_PROTOCOL    *This,
+  IN OUT UINT8                           **Target
+  )
+/*++
+
+Routine Description:
+  Used to retrieve the list of legal Target IDs for SCSI devices
+  on a SCSI channel.
+
+Arguments:
+  This                  - Protocol instance pointer.
+  Target                - On input, a pointer to the Target ID of a SCSI
+                          device present on the SCSI channel.  On output,
+                          a pointer to the Target ID of the next SCSI device
+                           present on a SCSI channel.  An input value of
+                           0xFFFFFFFF retrieves the Target ID of the first
+                           SCSI device present on a SCSI channel.
+  Lun                   - On input, a pointer to the LUN of a SCSI device
+                          present on the SCSI channel. On output, a pointer
+                          to the LUN of the next SCSI device present on
+                          a SCSI channel.
+
+Returns:
+  EFI_SUCCESS           - The Target ID and Lun of the next SCSI device
+                          on the SCSI channel was returned in Target and Lun.
+  EFI_NOT_FOUND         - There are no more SCSI devices on this SCSI channel.
+  EFI_INVALID_PARAMETER - Target is not 0xFFFFFFFF,and Target and Lun were not
+                          returned on a previous call to GetNextDevice().
+--*/
+{
+  UINT8                         TargetId;
+  UINT8                         ScsiId[TARGET_MAX_BYTES];
+  ATAPI_SCSI_PASS_THRU_DEV      *AtapiScsiPrivate;
+  UINT8                         ByteIndex;
+
+  //
+  // Retrieve Device Private Data Structure.
+  //
+  AtapiScsiPrivate = ATAPI_EXT_SCSI_PASS_THRU_DEV_FROM_THIS (This);
+
+  //
+  // Check whether Target is valid.
+  //
+  if (*Target == NULL ) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  TargetId = (*Target)[0];
+  SetMem (ScsiId, TARGET_MAX_BYTES, 0xFF);
+
+  //
+  // For ATAPI device, we use UINT8 to represent the SCSI ID on channel.
+  //
+  if (CompareMem(*Target, ScsiId, TARGET_MAX_BYTES) != 0) {
+    for (ByteIndex = 1; ByteIndex < TARGET_MAX_BYTES; ByteIndex++) {
+      if ((*Target)[ByteIndex] != 0) {
+        return EFI_INVALID_PARAMETER;
+      }
+    }
+  }
+
+  if ((CompareMem(*Target, ScsiId, TARGET_MAX_BYTES) != 0) &&(TargetId != AtapiScsiPrivate->LatestTargetId)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (TargetId == MAX_TARGET_ID) {
+    return EFI_NOT_FOUND;
+  }
+
+  if ((CompareMem(*Target, ScsiId, TARGET_MAX_BYTES) == 0)) {
+    SetMem (*Target, TARGET_MAX_BYTES, 0);
+  } else {
+    (*Target)[0] = (UINT8) (AtapiScsiPrivate->LatestTargetId + 1);
+  }
+
+  //
+  // Update the LatestTargetId.
+  //
+  AtapiScsiPrivate->LatestTargetId  = (*Target)[0];
+  AtapiScsiPrivate->LatestLun       = 0;
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+GetIdeRegistersBaseAddr (
+  IN  EFI_PCI_IO_PROTOCOL         *PciIo,
+  OUT IDE_REGISTERS_BASE_ADDR     *IdeRegsBaseAddr
+  )
+/*++
+
+Routine Description:
+  Get IDE IO port registers' base addresses by mode. In 'Compatibility' mode,
+  use fixed addresses. In Native-PCI mode, get base addresses from BARs in
+  the PCI IDE controller's Configuration Space.
+
+Arguments:
+  PciIo             - Pointer to the EFI_PCI_IO_PROTOCOL instance
+  IdeRegsBaseAddr   - Pointer to IDE_REGISTERS_BASE_ADDR to
+                      receive IDE IO port registers' base addresses
+
+Returns:
+
+  EFI_STATUS
+
+--*/
+{
+  EFI_STATUS  Status;
+  PCI_TYPE00  PciData;
+
+  Status = PciIo->Pci.Read (
+                        PciIo,
+                        EfiPciIoWidthUint8,
+                        0,
+                        sizeof (PciData),
+                        &PciData
+                        );
+
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  if ((PciData.Hdr.ClassCode[0] & IDE_PRIMARY_OPERATING_MODE) == 0) {
+    IdeRegsBaseAddr[IdePrimary].CommandBlockBaseAddr  = 0x1f0;
+    IdeRegsBaseAddr[IdePrimary].ControlBlockBaseAddr  = 0x3f6;
+  } else {
+    //
+    // The BARs should be of IO type
+    //
+    if ((PciData.Device.Bar[0] & BIT0) == 0 ||
+        (PciData.Device.Bar[1] & BIT0) == 0) {
+      return EFI_UNSUPPORTED;
+    }
+
+    IdeRegsBaseAddr[IdePrimary].CommandBlockBaseAddr  =
+    (UINT16) (PciData.Device.Bar[0] & 0x0000fff8);
+    IdeRegsBaseAddr[IdePrimary].ControlBlockBaseAddr  =
+    (UINT16) ((PciData.Device.Bar[1] & 0x0000fffc) + 2);
+  }
+
+  if ((PciData.Hdr.ClassCode[0] & IDE_SECONDARY_OPERATING_MODE) == 0) {
+    IdeRegsBaseAddr[IdeSecondary].CommandBlockBaseAddr  = 0x170;
+    IdeRegsBaseAddr[IdeSecondary].ControlBlockBaseAddr  = 0x376;
+  } else {
+    //
+    // The BARs should be of IO type
+    //
+    if ((PciData.Device.Bar[2] & BIT0) == 0 ||
+        (PciData.Device.Bar[3] & BIT0) == 0) {
+      return EFI_UNSUPPORTED;
+    }
+
+    IdeRegsBaseAddr[IdeSecondary].CommandBlockBaseAddr  =
+    (UINT16) (PciData.Device.Bar[2] & 0x0000fff8);
+    IdeRegsBaseAddr[IdeSecondary].ControlBlockBaseAddr  =
+    (UINT16) ((PciData.Device.Bar[3] & 0x0000fffc) + 2);
+  }
+
+  return EFI_SUCCESS;
+}
+
+VOID
+InitAtapiIoPortRegisters (
+  IN  ATAPI_SCSI_PASS_THRU_DEV     *AtapiScsiPrivate,
+  IN  IDE_REGISTERS_BASE_ADDR      *IdeRegsBaseAddr
+  )
+/*++
+
+Routine Description:
+
+  Initialize each Channel's Base Address of CommandBlock and ControlBlock.
+
+Arguments:
+
+  AtapiScsiPrivate            - The pointer of ATAPI_SCSI_PASS_THRU_DEV
+  IdeRegsBaseAddr             - The pointer of IDE_REGISTERS_BASE_ADDR
+
+Returns:
+
+  None
+
+--*/
+{
+
+  UINT8               IdeChannel;
+  UINT16              CommandBlockBaseAddr;
+  UINT16              ControlBlockBaseAddr;
+  IDE_BASE_REGISTERS  *RegisterPointer;
+
+
+  for (IdeChannel = 0; IdeChannel < ATAPI_MAX_CHANNEL; IdeChannel++) {
+
+    RegisterPointer =  &AtapiScsiPrivate->AtapiIoPortRegisters[IdeChannel];
+
+    //
+    // Initialize IDE IO port addresses, including Command Block registers
+    // and Control Block registers
+    //
+    CommandBlockBaseAddr = IdeRegsBaseAddr[IdeChannel].CommandBlockBaseAddr;
+    ControlBlockBaseAddr = IdeRegsBaseAddr[IdeChannel].ControlBlockBaseAddr;
+
+    RegisterPointer->Data = CommandBlockBaseAddr;
+    (*(UINT16 *) &RegisterPointer->Reg1) = (UINT16) (CommandBlockBaseAddr + 0x01);
+    RegisterPointer->SectorCount = (UINT16) (CommandBlockBaseAddr + 0x02);
+    RegisterPointer->SectorNumber = (UINT16) (CommandBlockBaseAddr + 0x03);
+    RegisterPointer->CylinderLsb = (UINT16) (CommandBlockBaseAddr + 0x04);
+    RegisterPointer->CylinderMsb = (UINT16) (CommandBlockBaseAddr + 0x05);
+    RegisterPointer->Head = (UINT16) (CommandBlockBaseAddr + 0x06);
+    (*(UINT16 *) &RegisterPointer->Reg) = (UINT16) (CommandBlockBaseAddr + 0x07);
+
+    (*(UINT16 *) &RegisterPointer->Alt) = ControlBlockBaseAddr;
+    RegisterPointer->DriveAddress = (UINT16) (ControlBlockBaseAddr + 0x01);
+  }
+
+}
+
+
+EFI_STATUS
+CheckSCSIRequestPacket (
+  EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET      *Packet
+  )
+/*++
+
+Routine Description:
+
+  Checks the parameters in the SCSI Request Packet to make sure
+  they are valid for a SCSI Pass Thru request.
+
+Arguments:
+
+  Packet         -  The pointer of EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
+
+Returns:
+
+  EFI_STATUS
+
+--*/
+{
+  if (Packet == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (!ValidCdbLength (Packet->CdbLength)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (Packet->Cdb == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Checks whether the request command is supported.
+  //
+  if (!IsCommandValid (Packet)) {
+    return EFI_UNSUPPORTED;
+  }
+
+  return EFI_SUCCESS;
+}
+
+BOOLEAN
+IsCommandValid (
+  EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET   *Packet
+  )
+/*++
+
+Routine Description:
+
+  Checks the requested SCSI command:
+  Is it supported by this driver?
+  Is the Data transfer direction reasonable?
+
+Arguments:
+
+  Packet         -  The pointer of EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
+
+Returns:
+
+  EFI_STATUS
+
+--*/
+{
+  UINT8 Index;
+  UINT8 *OpCode;
+  UINT8 ArrayLen;
+
+  OpCode = (UINT8 *) (Packet->Cdb);
+  ArrayLen = (UINT8) (ARRAY_SIZE (gSupportedATAPICommands));
+
+  for (Index = 0; (Index < ArrayLen) && (CompareMem (&gSupportedATAPICommands[Index], &gEndTable, sizeof (SCSI_COMMAND_SET)) != 0); Index++) {
+
+    if (*OpCode == gSupportedATAPICommands[Index].OpCode) {
+      //
+      // Check whether the requested Command is supported by this driver
+      //
+      if (Packet->DataDirection == DataIn) {
+        //
+        // Check whether the requested data direction conforms to
+        // what it should be.
+        //
+        if (gSupportedATAPICommands[Index].Direction == DataOut) {
+          return FALSE;
+        }
+      }
+
+      if (Packet->DataDirection == DataOut) {
+        //
+        // Check whether the requested data direction conforms to
+        // what it should be.
+        //
+        if (gSupportedATAPICommands[Index].Direction == DataIn) {
+          return FALSE;
+        }
+      }
+
+      return TRUE;
+    }
+  }
+
+  return FALSE;
+}
+
+EFI_STATUS
+SubmitBlockingIoCommand (
+  ATAPI_SCSI_PASS_THRU_DEV                  *AtapiScsiPrivate,
+  UINT32                                    Target,
+  EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET    *Packet
+  )
+/*++
+
+Routine Description:
+
+  Performs blocking I/O request.
+
+Arguments:
+
+  AtapiScsiPrivate:   Private data structure for the specified channel.
+  Target:             The Target ID of the ATAPI device to send the SCSI
+                      Request Packet. To ATAPI devices attached on an IDE
+                      Channel, Target ID 0 indicates Master device;Target
+                      ID 1 indicates Slave device.
+  Packet:             The SCSI Request Packet to send to the ATAPI device
+                      specified by Target.
+
+  Returns:            EFI_STATUS
+
+--*/
+{
+  UINT8       PacketCommand[12];
+  UINT64      TimeoutInMicroSeconds;
+  EFI_STATUS  PacketCommandStatus;
+
+  //
+  // Fill ATAPI Command Packet according to CDB
+  //
+  ZeroMem (&PacketCommand, 12);
+  CopyMem (&PacketCommand, Packet->Cdb, Packet->CdbLength);
+
+  //
+  // Timeout is 100ns unit, convert it to 1000ns (1us) unit.
+  //
+  TimeoutInMicroSeconds = DivU64x32 (Packet->Timeout, (UINT32) 10);
+
+  //
+  // Submit ATAPI Command Packet
+  //
+  PacketCommandStatus = AtapiPacketCommand (
+                          AtapiScsiPrivate,
+                          Target,
+                          PacketCommand,
+                          Packet->DataBuffer,
+                          &(Packet->TransferLength),
+                          (DATA_DIRECTION) Packet->DataDirection,
+                          TimeoutInMicroSeconds
+                          );
+  if (!EFI_ERROR (PacketCommandStatus) || (Packet->SenseData == NULL)) {
+    Packet->SenseDataLength = 0;
+    return PacketCommandStatus;
+  }
+
+  //
+  // Return SenseData if PacketCommandStatus matches
+  // the following return codes.
+  //
+  if ((PacketCommandStatus ==  EFI_BAD_BUFFER_SIZE) ||
+      (PacketCommandStatus == EFI_DEVICE_ERROR) ||
+      (PacketCommandStatus == EFI_TIMEOUT)) {
+
+    //
+    // avoid submit request sense command continuously.
+    //
+    if (PacketCommand[0] == OP_REQUEST_SENSE) {
+      Packet->SenseDataLength = 0;
+      return PacketCommandStatus;
+    }
+
+    RequestSenseCommand (
+      AtapiScsiPrivate,
+      Target,
+      Packet->Timeout,
+      Packet->SenseData,
+      &Packet->SenseDataLength
+      );
+  }
+
+  return PacketCommandStatus;
+}
+
+EFI_STATUS
+RequestSenseCommand (
+  ATAPI_SCSI_PASS_THRU_DEV    *AtapiScsiPrivate,
+  UINT32                      Target,
+  UINT64                      Timeout,
+  VOID                        *SenseData,
+  UINT8                       *SenseDataLength
+  )
+/*++
+
+Routine Description:
+
+  Submit request sense command
+
+Arguments:
+
+  AtapiScsiPrivate  - The pointer of ATAPI_SCSI_PASS_THRU_DEV
+  Target            - The target ID
+  Timeout           - The time to complete the command
+  SenseData         - The buffer to fill in sense data
+  SenseDataLength   - The length of buffer
+
+Returns:
+
+  EFI_STATUS
+
+--*/
+{
+  EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET  Packet;
+  UINT8                                   Cdb[12];
+  EFI_STATUS                              Status;
+
+  ZeroMem (&Packet, sizeof (EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET));
+  ZeroMem (Cdb, 12);
+
+  Cdb[0]                = OP_REQUEST_SENSE;
+  Cdb[4]                = (UINT8) (*SenseDataLength);
+
+  Packet.Timeout        = Timeout;
+  Packet.DataBuffer     = SenseData;
+  Packet.SenseData      = NULL;
+  Packet.Cdb            = Cdb;
+  Packet.TransferLength = *SenseDataLength;
+  Packet.CdbLength      = 12;
+  Packet.DataDirection  = DataIn;
+
+  Status                = SubmitBlockingIoCommand (AtapiScsiPrivate, Target, &Packet);
+  *SenseDataLength      = (UINT8) (Packet.TransferLength);
+  return Status;
+}
+
+EFI_STATUS
+CheckExtSCSIRequestPacket (
+  EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET      *Packet
+  )
+/*++
+
+Routine Description:
+
+  Checks the parameters in the SCSI Request Packet to make sure
+  they are valid for a SCSI Pass Thru request.
+
+Arguments:
+
+  Packet       - The pointer of EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
+
+Returns:
+
+  EFI_STATUS
+
+--*/
+{
+  if (Packet == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (!ValidCdbLength (Packet->CdbLength)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (Packet->Cdb == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Checks whether the request command is supported.
+  //
+  if (!IsExtCommandValid (Packet)) {
+    return EFI_UNSUPPORTED;
+  }
+
+  return EFI_SUCCESS;
+}
+
+
+BOOLEAN
+IsExtCommandValid (
+  EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET   *Packet
+  )
+/*++
+
+Routine Description:
+
+  Checks the requested SCSI command:
+  Is it supported by this driver?
+  Is the Data transfer direction reasonable?
+
+Arguments:
+
+  Packet         -  The pointer of EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
+
+Returns:
+
+  EFI_STATUS
+
+--*/
+{
+  UINT8 Index;
+  UINT8 *OpCode;
+  UINT8 ArrayLen;
+
+  OpCode = (UINT8 *) (Packet->Cdb);
+  ArrayLen = (UINT8) (ARRAY_SIZE (gSupportedATAPICommands));
+
+  for (Index = 0; (Index < ArrayLen) && (CompareMem (&gSupportedATAPICommands[Index], &gEndTable, sizeof (SCSI_COMMAND_SET)) != 0); Index++) {
+
+    if (*OpCode == gSupportedATAPICommands[Index].OpCode) {
+      //
+      // Check whether the requested Command is supported by this driver
+      //
+      if (Packet->DataDirection == DataIn) {
+        //
+        // Check whether the requested data direction conforms to
+        // what it should be.
+        //
+        if (gSupportedATAPICommands[Index].Direction == DataOut) {
+          return FALSE;
+        }
+      }
+
+      if (Packet->DataDirection == DataOut) {
+        //
+        // Check whether the requested data direction conforms to
+        // what it should be.
+        //
+        if (gSupportedATAPICommands[Index].Direction == DataIn) {
+          return FALSE;
+        }
+      }
+
+      return TRUE;
+    }
+  }
+
+  return FALSE;
+}
+
+EFI_STATUS
+SubmitExtBlockingIoCommand (
+  ATAPI_SCSI_PASS_THRU_DEV                      *AtapiScsiPrivate,
+  UINT8                                         Target,
+  EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET    *Packet
+  )
+/*++
+
+Routine Description:
+
+  Performs blocking I/O request.
+
+Arguments:
+
+  AtapiScsiPrivate:   Private data structure for the specified channel.
+  Target:             The Target ID of the ATAPI device to send the SCSI
+                      Request Packet. To ATAPI devices attached on an IDE
+                      Channel, Target ID 0 indicates Master device;Target
+                      ID 1 indicates Slave device.
+  Packet:             The SCSI Request Packet to send to the ATAPI device
+                      specified by Target.
+
+  Returns:            EFI_STATUS
+
+--*/
+{
+  UINT8       PacketCommand[12];
+  UINT64      TimeoutInMicroSeconds;
+  EFI_STATUS  PacketCommandStatus;
+
+  //
+  // Fill ATAPI Command Packet according to CDB
+  //
+  ZeroMem (&PacketCommand, 12);
+  CopyMem (&PacketCommand, Packet->Cdb, Packet->CdbLength);
+
+  //
+  // Timeout is 100ns unit, convert it to 1000ns (1us) unit.
+  //
+  TimeoutInMicroSeconds = DivU64x32 (Packet->Timeout, (UINT32) 10);
+
+  //
+  // Submit ATAPI Command Packet
+  //
+  if (Packet->DataDirection == DataIn) {
+    PacketCommandStatus = AtapiPacketCommand (
+                              AtapiScsiPrivate,
+                              Target,
+                              PacketCommand,
+                              Packet->InDataBuffer,
+                              &(Packet->InTransferLength),
+                              DataIn,
+                              TimeoutInMicroSeconds
+                              );
+  } else {
+
+    PacketCommandStatus = AtapiPacketCommand (
+                            AtapiScsiPrivate,
+                            Target,
+                            PacketCommand,
+                            Packet->OutDataBuffer,
+                            &(Packet->OutTransferLength),
+                            DataOut,
+                            TimeoutInMicroSeconds
+                            );
+  }
+
+  if (!EFI_ERROR (PacketCommandStatus) || (Packet->SenseData == NULL)) {
+    Packet->SenseDataLength = 0;
+    return PacketCommandStatus;
+  }
+
+  //
+  // Return SenseData if PacketCommandStatus matches
+  // the following return codes.
+  //
+  if ((PacketCommandStatus == EFI_BAD_BUFFER_SIZE) ||
+      (PacketCommandStatus == EFI_DEVICE_ERROR) ||
+      (PacketCommandStatus == EFI_TIMEOUT)) {
+
+    //
+    // avoid submit request sense command continuously.
+    //
+    if (PacketCommand[0] == OP_REQUEST_SENSE) {
+      Packet->SenseDataLength = 0;
+      return PacketCommandStatus;
+    }
+
+    RequestSenseCommand (
+      AtapiScsiPrivate,
+      Target,
+      Packet->Timeout,
+      Packet->SenseData,
+      &Packet->SenseDataLength
+      );
+  }
+
+  return PacketCommandStatus;
+}
+
+
+EFI_STATUS
+AtapiPacketCommand (
+  ATAPI_SCSI_PASS_THRU_DEV    *AtapiScsiPrivate,
+  UINT32                      Target,
+  UINT8                       *PacketCommand,
+  VOID                        *Buffer,
+  UINT32                      *ByteCount,
+  DATA_DIRECTION              Direction,
+  UINT64                      TimeoutInMicroSeconds
+  )
+/*++
+
+Routine Description:
+
+  Submits ATAPI command packet to the specified ATAPI device.
+
+Arguments:
+
+  AtapiScsiPrivate:   Private data structure for the specified channel.
+  Target:             The Target ID of the ATAPI device to send the SCSI
+                      Request Packet. To ATAPI devices attached on an IDE
+                      Channel, Target ID 0 indicates Master device;Target
+                      ID 1 indicates Slave device.
+  PacketCommand:      Points to the ATAPI command packet.
+  Buffer:             Points to the transferred data.
+  ByteCount:          When input,indicates the buffer size; when output,
+                      indicates the actually transferred data size.
+  Direction:          Indicates the data transfer direction.
+  TimeoutInMicroSeconds:
+                      The timeout, in micro second units, to use for the
+                      execution of this ATAPI command.
+                      A TimeoutInMicroSeconds value of 0 means that
+                      this function will wait indefinitely for the ATAPI
+                      command to execute.
+                      If TimeoutInMicroSeconds is greater than zero, then
+                      this function will return EFI_TIMEOUT if the time
+                      required to execute the ATAPI command is greater
+                      than TimeoutInMicroSeconds.
+
+Returns:
+
+  EFI_STATUS
+
+--*/
+{
+
+  UINT16      *CommandIndex;
+  UINT8       Count;
+  EFI_STATUS  Status;
+
+  //
+  // Set all the command parameters by fill related registers.
+  // Before write to all the following registers, BSY must be 0.
+  //
+  Status = StatusWaitForBSYClear (AtapiScsiPrivate, TimeoutInMicroSeconds);
+  if (EFI_ERROR (Status)) {
+    return EFI_DEVICE_ERROR;
+  }
+
+
+  //
+  // Select device via Device/Head Register.
+  // "Target = 0" indicates device 0; "Target = 1" indicates device 1
+  //
+  WritePortB (
+    AtapiScsiPrivate->PciIo,
+    AtapiScsiPrivate->IoPort->Head,
+    (UINT8) ((Target << 4) | DEFAULT_CMD) // DEFAULT_CMD: 0xa0 (1010,0000)
+    );
+
+  //
+  // Set all the command parameters by fill related registers.
+  // Before write to all the following registers, BSY DRQ must be 0.
+  //
+  Status =  StatusDRQClear(AtapiScsiPrivate,  TimeoutInMicroSeconds);
+
+  if (EFI_ERROR (Status)) {
+    if (Status == EFI_ABORTED) {
+      Status = EFI_DEVICE_ERROR;
+    }
+    *ByteCount = 0;
+    return Status;
+  }
+
+  //
+  // No OVL; No DMA (by setting feature register)
+  //
+  WritePortB (
+    AtapiScsiPrivate->PciIo,
+    AtapiScsiPrivate->IoPort->Reg1.Feature,
+    0x00
+    );
+
+  //
+  // set the transfersize to MAX_ATAPI_BYTE_COUNT to let the device
+  // determine how much data should be transfered.
+  //
+  WritePortB (
+    AtapiScsiPrivate->PciIo,
+    AtapiScsiPrivate->IoPort->CylinderLsb,
+    (UINT8) (MAX_ATAPI_BYTE_COUNT & 0x00ff)
+    );
+  WritePortB (
+    AtapiScsiPrivate->PciIo,
+    AtapiScsiPrivate->IoPort->CylinderMsb,
+    (UINT8) (MAX_ATAPI_BYTE_COUNT >> 8)
+    );
+
+  //
+  //  DEFAULT_CTL:0x0a (0000,1010)
+  //  Disable interrupt
+  //
+  WritePortB (
+    AtapiScsiPrivate->PciIo,
+    AtapiScsiPrivate->IoPort->Alt.DeviceControl,
+    DEFAULT_CTL
+    );
+
+  //
+  // Send Packet command to inform device
+  // that the following data bytes are command packet.
+  //
+  WritePortB (
+    AtapiScsiPrivate->PciIo,
+    AtapiScsiPrivate->IoPort->Reg.Command,
+    PACKET_CMD
+    );
+
+  //
+  // Before data transfer, BSY should be 0 and DRQ should be 1.
+  // if they are not in specified time frame,
+  // retrieve Sense Key from Error Register before return.
+  //
+  Status = StatusDRQReady (AtapiScsiPrivate, TimeoutInMicroSeconds);
+  if (EFI_ERROR (Status)) {
+    if (Status == EFI_ABORTED) {
+      Status = EFI_DEVICE_ERROR;
+    }
+
+    *ByteCount = 0;
+    return Status;
+  }
+
+  //
+  // Send out command packet
+  //
+  CommandIndex = (UINT16 *) PacketCommand;
+  for (Count = 0; Count < 6; Count++, CommandIndex++) {
+    WritePortW (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Data, *CommandIndex);
+  }
+
+  //
+  // call AtapiPassThruPioReadWriteData() function to get
+  // requested transfer data form device.
+  //
+  return AtapiPassThruPioReadWriteData (
+          AtapiScsiPrivate,
+          Buffer,
+          ByteCount,
+          Direction,
+          TimeoutInMicroSeconds
+          );
+}
+
+EFI_STATUS
+AtapiPassThruPioReadWriteData (
+  ATAPI_SCSI_PASS_THRU_DEV  *AtapiScsiPrivate,
+  UINT16                    *Buffer,
+  UINT32                    *ByteCount,
+  DATA_DIRECTION            Direction,
+  UINT64                    TimeoutInMicroSeconds
+  )
+/*++
+
+Routine Description:
+
+  Performs data transfer between ATAPI device and host after the
+  ATAPI command packet is sent.
+
+Arguments:
+
+  AtapiScsiPrivate:   Private data structure for the specified channel.
+  Buffer:             Points to the transferred data.
+  ByteCount:          When input,indicates the buffer size; when output,
+                      indicates the actually transferred data size.
+  Direction:          Indicates the data transfer direction.
+  TimeoutInMicroSeconds:
+                      The timeout, in micro second units, to use for the
+                      execution of this ATAPI command.
+                      A TimeoutInMicroSeconds value of 0 means that
+                      this function will wait indefinitely for the ATAPI
+                      command to execute.
+                      If TimeoutInMicroSeconds is greater than zero, then
+                      this function will return EFI_TIMEOUT if the time
+                      required to execute the ATAPI command is greater
+                      than TimeoutInMicroSeconds.
+ Returns:
+
+  EFI_STATUS
+
+--*/
+{
+  UINT32      Index;
+  UINT32      RequiredWordCount;
+  UINT32      ActualWordCount;
+  UINT32      WordCount;
+  EFI_STATUS  Status;
+  UINT16      *ptrBuffer;
+
+  Status = EFI_SUCCESS;
+
+  //
+  // Non Data transfer request is also supported.
+  //
+  if (*ByteCount == 0 || Buffer == NULL) {
+    *ByteCount = 0;
+    if (EFI_ERROR (StatusWaitForBSYClear (AtapiScsiPrivate, TimeoutInMicroSeconds))) {
+      return EFI_DEVICE_ERROR;
+    }
+  }
+
+  ptrBuffer         = Buffer;
+  RequiredWordCount = *ByteCount / 2;
+
+  //
+  // ActuralWordCount means the word count of data really transfered.
+  //
+  ActualWordCount = 0;
+
+  while (ActualWordCount < RequiredWordCount) {
+    //
+    // before each data transfer stream, the host should poll DRQ bit ready,
+    // which indicates device's ready for data transfer .
+    //
+    Status = StatusDRQReady (AtapiScsiPrivate, TimeoutInMicroSeconds);
+    if (EFI_ERROR (Status)) {
+      *ByteCount = ActualWordCount * 2;
+
+      AtapiPassThruCheckErrorStatus (AtapiScsiPrivate);
+
+      if (ActualWordCount == 0) {
+        return EFI_DEVICE_ERROR;
+      }
+      //
+      // ActualWordCount > 0
+      //
+      if (ActualWordCount < RequiredWordCount) {
+        return EFI_BAD_BUFFER_SIZE;
+      }
+    }
+    //
+    // get current data transfer size from Cylinder Registers.
+    //
+    WordCount = ReadPortB (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->CylinderMsb) << 8;
+    WordCount = WordCount | ReadPortB (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->CylinderLsb);
+    WordCount = WordCount & 0xffff;
+    WordCount /= 2;
+
+    //
+    // perform a series data In/Out.
+    //
+    for (Index = 0; (Index < WordCount) && (ActualWordCount < RequiredWordCount); Index++, ActualWordCount++) {
+
+      if (Direction == DataIn) {
+
+        *ptrBuffer = ReadPortW (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Data);
+      } else {
+
+        WritePortW (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Data, *ptrBuffer);
+      }
+
+      ptrBuffer++;
+
+    }
+  }
+  //
+  // After data transfer is completed, normally, DRQ bit should clear.
+  //
+  StatusDRQClear (AtapiScsiPrivate, TimeoutInMicroSeconds);
+
+  //
+  // read status register to check whether error happens.
+  //
+  Status      = AtapiPassThruCheckErrorStatus (AtapiScsiPrivate);
+
+  *ByteCount  = ActualWordCount * 2;
+
+  return Status;
+}
+
+
+UINT8
+ReadPortB (
+  IN  EFI_PCI_IO_PROTOCOL   *PciIo,
+  IN  UINT16                Port
+  )
+/*++
+
+Routine Description:
+
+  Read one byte from a specified I/O port.
+
+Arguments:
+
+  PciIo      - The pointer of EFI_PCI_IO_PROTOCOL
+  Port       - IO port
+
+Returns:
+
+  A byte read out
+
+--*/
+{
+  UINT8 Data;
+
+  Data = 0;
+  PciIo->Io.Read (
+              PciIo,
+              EfiPciIoWidthUint8,
+              EFI_PCI_IO_PASS_THROUGH_BAR,
+              (UINT64) Port,
+              1,
+              &Data
+              );
+  return Data;
+}
+
+
+UINT16
+ReadPortW (
+  IN  EFI_PCI_IO_PROTOCOL   *PciIo,
+  IN  UINT16                Port
+  )
+/*++
+
+Routine Description:
+
+  Read one word from a specified I/O port.
+
+Arguments:
+
+  PciIo      - The pointer of EFI_PCI_IO_PROTOCOL
+  Port       - IO port
+
+Returns:
+
+  A word read out
+--*/
+{
+  UINT16  Data;
+
+  Data = 0;
+  PciIo->Io.Read (
+              PciIo,
+              EfiPciIoWidthUint16,
+              EFI_PCI_IO_PASS_THROUGH_BAR,
+              (UINT64) Port,
+              1,
+              &Data
+              );
+  return Data;
+}
+
+
+VOID
+WritePortB (
+  IN  EFI_PCI_IO_PROTOCOL   *PciIo,
+  IN  UINT16                Port,
+  IN  UINT8                 Data
+  )
+/*++
+
+Routine Description:
+
+  Write one byte to a specified I/O port.
+
+Arguments:
+
+  PciIo      - The pointer of EFI_PCI_IO_PROTOCOL
+  Port       - IO port
+  Data       - The data to write
+
+Returns:
+
+   NONE
+
+--*/
+{
+  PciIo->Io.Write (
+              PciIo,
+              EfiPciIoWidthUint8,
+              EFI_PCI_IO_PASS_THROUGH_BAR,
+              (UINT64) Port,
+              1,
+              &Data
+              );
+}
+
+
+VOID
+WritePortW (
+  IN  EFI_PCI_IO_PROTOCOL   *PciIo,
+  IN  UINT16                Port,
+  IN  UINT16                Data
+  )
+/*++
+
+Routine Description:
+
+  Write one word to a specified I/O port.
+
+Arguments:
+
+  PciIo      - The pointer of EFI_PCI_IO_PROTOCOL
+  Port       - IO port
+  Data       - The data to write
+
+Returns:
+
+   NONE
+
+--*/
+{
+  PciIo->Io.Write (
+              PciIo,
+              EfiPciIoWidthUint16,
+              EFI_PCI_IO_PASS_THROUGH_BAR,
+              (UINT64) Port,
+              1,
+              &Data
+              );
+}
+
+EFI_STATUS
+StatusDRQClear (
+  ATAPI_SCSI_PASS_THRU_DEV        *AtapiScsiPrivate,
+  UINT64                          TimeoutInMicroSeconds
+  )
+/*++
+
+Routine Description:
+
+  Check whether DRQ is clear in the Status Register. (BSY must also be cleared)
+  If TimeoutInMicroSeconds is zero, this routine should wait infinitely for
+  DRQ clear. Otherwise, it will return EFI_TIMEOUT when specified time is
+  elapsed.
+
+Arguments:
+
+  AtapiScsiPrivate            - The pointer of ATAPI_SCSI_PASS_THRU_DEV
+  TimeoutInMicroSeconds       - The time to wait for
+
+Returns:
+
+  EFI_STATUS
+
+--*/
+{
+  UINT64  Delay;
+  UINT8   StatusRegister;
+  UINT8   ErrRegister;
+
+  if (TimeoutInMicroSeconds == 0) {
+    Delay = 2;
+  } else {
+    Delay = DivU64x32 (TimeoutInMicroSeconds, (UINT32) 30) + 1;
+  }
+
+  do {
+
+    StatusRegister = ReadPortB (
+                      AtapiScsiPrivate->PciIo,
+                      AtapiScsiPrivate->IoPort->Reg.Status
+                      );
+
+    //
+    // wait for BSY == 0 and DRQ == 0
+    //
+    if ((StatusRegister & (DRQ | BSY)) == 0) {
+      break;
+    }
+    //
+    // check whether the command is aborted by the device
+    //
+    if ((StatusRegister & (BSY | ERR)) == ERR) {
+
+      ErrRegister = ReadPortB (
+                      AtapiScsiPrivate->PciIo,
+                      AtapiScsiPrivate->IoPort->Reg1.Error
+                      );
+      if ((ErrRegister & ABRT_ERR) == ABRT_ERR) {
+
+        return EFI_ABORTED;
+      }
+    }
+    //
+    //  Stall for 30 us
+    //
+    gBS->Stall (30);
+
+    //
+    // Loop infinitely if not meeting expected condition
+    //
+    if (TimeoutInMicroSeconds == 0) {
+      Delay = 2;
+    }
+
+    Delay--;
+  } while (Delay);
+
+  if (Delay == 0) {
+    return EFI_TIMEOUT;
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+AltStatusDRQClear (
+  ATAPI_SCSI_PASS_THRU_DEV        *AtapiScsiPrivate,
+  UINT64                          TimeoutInMicroSeconds
+  )
+/*++
+
+Routine Description:
+
+  Check whether DRQ is clear in the Alternate Status Register.
+  (BSY must also be cleared).If TimeoutInMicroSeconds is zero, this routine should
+  wait infinitely for DRQ clear. Otherwise, it will return EFI_TIMEOUT when specified time is
+  elapsed.
+
+Arguments:
+
+  AtapiScsiPrivate            - The pointer of ATAPI_SCSI_PASS_THRU_DEV
+  TimeoutInMicroSeconds       - The time to wait for
+
+Returns:
+
+  EFI_STATUS
+
+--*/
+{
+  UINT64  Delay;
+  UINT8   AltStatusRegister;
+  UINT8   ErrRegister;
+
+  if (TimeoutInMicroSeconds == 0) {
+    Delay = 2;
+  } else {
+    Delay = DivU64x32 (TimeoutInMicroSeconds, (UINT32) 30) + 1;
+  }
+
+  do {
+
+    AltStatusRegister = ReadPortB (
+                          AtapiScsiPrivate->PciIo,
+                          AtapiScsiPrivate->IoPort->Alt.AltStatus
+                          );
+
+    //
+    // wait for BSY == 0 and DRQ == 0
+    //
+    if ((AltStatusRegister & (DRQ | BSY)) == 0) {
+      break;
+    }
+
+    if ((AltStatusRegister & (BSY | ERR)) == ERR) {
+
+      ErrRegister = ReadPortB (
+                      AtapiScsiPrivate->PciIo,
+                      AtapiScsiPrivate->IoPort->Reg1.Error
+                      );
+      if ((ErrRegister & ABRT_ERR) == ABRT_ERR) {
+
+        return EFI_ABORTED;
+      }
+    }
+    //
+    //  Stall for 30 us
+    //
+    gBS->Stall (30);
+
+    //
+    // Loop infinitely if not meeting expected condition
+    //
+    if (TimeoutInMicroSeconds == 0) {
+      Delay = 2;
+    }
+
+    Delay--;
+  } while (Delay);
+
+  if (Delay == 0) {
+    return EFI_TIMEOUT;
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+StatusDRQReady (
+  ATAPI_SCSI_PASS_THRU_DEV        *AtapiScsiPrivate,
+  UINT64                          TimeoutInMicroSeconds
+  )
+/*++
+
+Routine Description:
+
+  Check whether DRQ is ready in the Status Register. (BSY must also be cleared)
+  If TimeoutInMicroSeconds is zero, this routine should wait infinitely for
+  DRQ ready. Otherwise, it will return EFI_TIMEOUT when specified time is
+  elapsed.
+
+Arguments:
+
+  AtapiScsiPrivate            - The pointer of ATAPI_SCSI_PASS_THRU_DEV
+  TimeoutInMicroSeconds       - The time to wait for
+
+Returns:
+
+  EFI_STATUS
+
+--*/
+{
+  UINT64  Delay;
+  UINT8   StatusRegister;
+  UINT8   ErrRegister;
+
+  if (TimeoutInMicroSeconds == 0) {
+    Delay = 2;
+  } else {
+    Delay = DivU64x32 (TimeoutInMicroSeconds, (UINT32) 30) + 1;
+  }
+
+  do {
+    //
+    //  read Status Register will clear interrupt
+    //
+    StatusRegister = ReadPortB (
+                      AtapiScsiPrivate->PciIo,
+                      AtapiScsiPrivate->IoPort->Reg.Status
+                      );
+
+    //
+    //  BSY==0,DRQ==1
+    //
+    if ((StatusRegister & (BSY | DRQ)) == DRQ) {
+      break;
+    }
+
+    if ((StatusRegister & (BSY | ERR)) == ERR) {
+
+      ErrRegister = ReadPortB (
+                      AtapiScsiPrivate->PciIo,
+                      AtapiScsiPrivate->IoPort->Reg1.Error
+                      );
+      if ((ErrRegister & ABRT_ERR) == ABRT_ERR) {
+        return EFI_ABORTED;
+      }
+    }
+
+    //
+    // Stall for 30 us
+    //
+    gBS->Stall (30);
+
+    //
+    // Loop infinitely if not meeting expected condition
+    //
+    if (TimeoutInMicroSeconds == 0) {
+      Delay = 2;
+    }
+
+    Delay--;
+  } while (Delay);
+
+  if (Delay == 0) {
+    return EFI_TIMEOUT;
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+AltStatusDRQReady (
+  ATAPI_SCSI_PASS_THRU_DEV        *AtapiScsiPrivate,
+  UINT64                          TimeoutInMicroSeconds
+  )
+/*++
+
+Routine Description:
+
+  Check whether DRQ is ready in the Alternate Status Register.
+  (BSY must also be cleared)
+  If TimeoutInMicroSeconds is zero, this routine should wait infinitely for
+  DRQ ready. Otherwise, it will return EFI_TIMEOUT when specified time is
+  elapsed.
+
+Arguments:
+
+  AtapiScsiPrivate            - The pointer of ATAPI_SCSI_PASS_THRU_DEV
+  TimeoutInMicroSeconds       - The time to wait for
+
+Returns:
+
+  EFI_STATUS
+
+--*/
+{
+  UINT64  Delay;
+  UINT8   AltStatusRegister;
+  UINT8   ErrRegister;
+
+  if (TimeoutInMicroSeconds == 0) {
+    Delay = 2;
+  } else {
+    Delay = DivU64x32 (TimeoutInMicroSeconds, (UINT32) 30) + 1;
+  }
+
+  do {
+    //
+    //  read Status Register will clear interrupt
+    //
+    AltStatusRegister = ReadPortB (
+                          AtapiScsiPrivate->PciIo,
+                          AtapiScsiPrivate->IoPort->Alt.AltStatus
+                          );
+    //
+    //  BSY==0,DRQ==1
+    //
+    if ((AltStatusRegister & (BSY | DRQ)) == DRQ) {
+      break;
+    }
+
+    if ((AltStatusRegister & (BSY | ERR)) == ERR) {
+
+      ErrRegister = ReadPortB (
+                      AtapiScsiPrivate->PciIo,
+                      AtapiScsiPrivate->IoPort->Reg1.Error
+                      );
+      if ((ErrRegister & ABRT_ERR) == ABRT_ERR) {
+        return EFI_ABORTED;
+      }
+    }
+
+    //
+    // Stall for 30 us
+    //
+    gBS->Stall (30);
+
+    //
+    // Loop infinitely if not meeting expected condition
+    //
+    if (TimeoutInMicroSeconds == 0) {
+      Delay = 2;
+    }
+
+    Delay--;
+  } while (Delay);
+
+  if (Delay == 0) {
+    return EFI_TIMEOUT;
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+StatusWaitForBSYClear (
+  ATAPI_SCSI_PASS_THRU_DEV    *AtapiScsiPrivate,
+  UINT64                      TimeoutInMicroSeconds
+  )
+/*++
+
+Routine Description:
+
+  Check whether BSY is clear in the Status Register.
+  If TimeoutInMicroSeconds is zero, this routine should wait infinitely for
+  BSY clear. Otherwise, it will return EFI_TIMEOUT when specified time is
+  elapsed.
+
+Arguments:
+
+  AtapiScsiPrivate            - The pointer of ATAPI_SCSI_PASS_THRU_DEV
+  TimeoutInMicroSeconds       - The time to wait for
+
+Returns:
+
+  EFI_STATUS
+
+--*/
+{
+  UINT64  Delay;
+  UINT8   StatusRegister;
+
+  if (TimeoutInMicroSeconds == 0) {
+    Delay = 2;
+  } else {
+    Delay = DivU64x32 (TimeoutInMicroSeconds, (UINT32) 30) + 1;
+  }
+
+  do {
+
+    StatusRegister = ReadPortB (
+                      AtapiScsiPrivate->PciIo,
+                      AtapiScsiPrivate->IoPort->Reg.Status
+                      );
+    if ((StatusRegister & BSY) == 0x00) {
+      break;
+    }
+
+    //
+    // Stall for 30 us
+    //
+    gBS->Stall (30);
+
+    //
+    // Loop infinitely if not meeting expected condition
+    //
+    if (TimeoutInMicroSeconds == 0) {
+      Delay = 2;
+    }
+
+    Delay--;
+  } while (Delay);
+
+  if (Delay == 0) {
+    return EFI_TIMEOUT;
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+AltStatusWaitForBSYClear (
+  ATAPI_SCSI_PASS_THRU_DEV    *AtapiScsiPrivate,
+  UINT64                      TimeoutInMicroSeconds
+  )
+/*++
+
+Routine Description:
+
+  Check whether BSY is clear in the Alternate Status Register.
+  If TimeoutInMicroSeconds is zero, this routine should wait infinitely for
+  BSY clear. Otherwise, it will return EFI_TIMEOUT when specified time is
+  elapsed.
+
+Arguments:
+
+  AtapiScsiPrivate            - The pointer of ATAPI_SCSI_PASS_THRU_DEV
+  TimeoutInMicroSeconds       - The time to wait for
+
+Returns:
+
+  EFI_STATUS
+
+--*/
+{
+  UINT64  Delay;
+  UINT8   AltStatusRegister;
+
+  if (TimeoutInMicroSeconds == 0) {
+    Delay = 2;
+  } else {
+    Delay = DivU64x32 (TimeoutInMicroSeconds, (UINT32) 30) + 1;
+  }
+
+  do {
+
+    AltStatusRegister = ReadPortB (
+                          AtapiScsiPrivate->PciIo,
+                          AtapiScsiPrivate->IoPort->Alt.AltStatus
+                          );
+    if ((AltStatusRegister & BSY) == 0x00) {
+      break;
+    }
+
+    //
+    // Stall for 30 us
+    //
+    gBS->Stall (30);
+    //
+    // Loop infinitely if not meeting expected condition
+    //
+    if (TimeoutInMicroSeconds == 0) {
+      Delay = 2;
+    }
+
+    Delay--;
+  } while (Delay);
+
+  if (Delay == 0) {
+    return EFI_TIMEOUT;
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+StatusDRDYReady (
+  ATAPI_SCSI_PASS_THRU_DEV     *AtapiScsiPrivate,
+  UINT64                       TimeoutInMicroSeconds
+  )
+/*++
+
+Routine Description:
+
+  Check whether DRDY is ready in the Status Register.
+  (BSY must also be cleared)
+  If TimeoutInMicroSeconds is zero, this routine should wait infinitely for
+  DRDY ready. Otherwise, it will return EFI_TIMEOUT when specified time is
+  elapsed.
+
+Arguments:
+
+  AtapiScsiPrivate            - The pointer of ATAPI_SCSI_PASS_THRU_DEV
+  TimeoutInMicroSeconds       - The time to wait for
+
+Returns:
+
+  EFI_STATUS
+
+--*/
+{
+  UINT64  Delay;
+  UINT8   StatusRegister;
+  UINT8   ErrRegister;
+
+  if (TimeoutInMicroSeconds == 0) {
+    Delay = 2;
+  } else {
+    Delay = DivU64x32 (TimeoutInMicroSeconds, (UINT32) 30) + 1;
+  }
+
+  do {
+    StatusRegister = ReadPortB (
+                      AtapiScsiPrivate->PciIo,
+                      AtapiScsiPrivate->IoPort->Reg.Status
+                      );
+    //
+    //  BSY == 0 , DRDY == 1
+    //
+    if ((StatusRegister & (DRDY | BSY)) == DRDY) {
+      break;
+    }
+
+    if ((StatusRegister & (BSY | ERR)) == ERR) {
+
+      ErrRegister = ReadPortB (
+                      AtapiScsiPrivate->PciIo,
+                      AtapiScsiPrivate->IoPort->Reg1.Error
+                      );
+      if ((ErrRegister & ABRT_ERR) == ABRT_ERR) {
+        return EFI_ABORTED;
+      }
+    }
+
+    //
+    // Stall for 30 us
+    //
+    gBS->Stall (30);
+    //
+    // Loop infinitely if not meeting expected condition
+    //
+    if (TimeoutInMicroSeconds == 0) {
+      Delay = 2;
+    }
+
+    Delay--;
+  } while (Delay);
+
+  if (Delay == 0) {
+    return EFI_TIMEOUT;
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+AltStatusDRDYReady (
+  ATAPI_SCSI_PASS_THRU_DEV     *AtapiScsiPrivate,
+  UINT64                       TimeoutInMicroSeconds
+  )
+/*++
+
+Routine Description:
+
+  Check whether DRDY is ready in the Alternate Status Register.
+  (BSY must also be cleared)
+  If TimeoutInMicroSeconds is zero, this routine should wait infinitely for
+  DRDY ready. Otherwise, it will return EFI_TIMEOUT when specified time is
+  elapsed.
+
+Arguments:
+
+  AtapiScsiPrivate            - The pointer of ATAPI_SCSI_PASS_THRU_DEV
+  TimeoutInMicroSeconds       - The time to wait for
+
+Returns:
+
+  EFI_STATUS
+
+--*/
+{
+  UINT64  Delay;
+  UINT8   AltStatusRegister;
+  UINT8   ErrRegister;
+
+  if (TimeoutInMicroSeconds == 0) {
+    Delay = 2;
+  } else {
+    Delay = DivU64x32 (TimeoutInMicroSeconds, (UINT32) 30) + 1;
+  }
+
+  do {
+    AltStatusRegister = ReadPortB (
+                          AtapiScsiPrivate->PciIo,
+                          AtapiScsiPrivate->IoPort->Alt.AltStatus
+                          );
+    //
+    //  BSY == 0 , DRDY == 1
+    //
+    if ((AltStatusRegister & (DRDY | BSY)) == DRDY) {
+      break;
+    }
+
+    if ((AltStatusRegister & (BSY | ERR)) == ERR) {
+
+      ErrRegister = ReadPortB (
+                      AtapiScsiPrivate->PciIo,
+                      AtapiScsiPrivate->IoPort->Reg1.Error
+                      );
+      if ((ErrRegister & ABRT_ERR) == ABRT_ERR) {
+        return EFI_ABORTED;
+      }
+    }
+
+    //
+    // Stall for 30 us
+    //
+    gBS->Stall (30);
+    //
+    // Loop infinitely if not meeting expected condition
+    //
+    if (TimeoutInMicroSeconds == 0) {
+      Delay = 2;
+    }
+
+    Delay--;
+  } while (Delay);
+
+  if (Delay == 0) {
+    return EFI_TIMEOUT;
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+AtapiPassThruCheckErrorStatus (
+  ATAPI_SCSI_PASS_THRU_DEV        *AtapiScsiPrivate
+  )
+/*++
+
+Routine Description:
+
+  Check Error Register for Error Information.
+
+Arguments:
+
+  AtapiScsiPrivate            - The pointer of ATAPI_SCSI_PASS_THRU_DEV
+
+Returns:
+
+  EFI_STATUS
+
+--*/
+{
+  UINT8 StatusRegister;
+  UINT8 ErrorRegister;
+
+  StatusRegister = ReadPortB (
+                    AtapiScsiPrivate->PciIo,
+                    AtapiScsiPrivate->IoPort->Reg.Status
+                    );
+
+  DEBUG_CODE_BEGIN ();
+
+    if (StatusRegister & DWF) {
+      DEBUG (
+        (EFI_D_BLKIO,
+        "AtapiPassThruCheckErrorStatus()-- %02x : Error : Write Fault\n",
+        StatusRegister)
+        );
+    }
+
+    if (StatusRegister & CORR) {
+      DEBUG (
+        (EFI_D_BLKIO,
+        "AtapiPassThruCheckErrorStatus()-- %02x : Error : Corrected Data\n",
+        StatusRegister)
+        );
+    }
+
+    if (StatusRegister & ERR) {
+      ErrorRegister = ReadPortB (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Reg1.Error);
+
+
+      if (ErrorRegister & BBK_ERR) {
+        DEBUG (
+          (EFI_D_BLKIO,
+          "AtapiPassThruCheckErrorStatus()-- %02x : Error : Bad Block Detected\n",
+          ErrorRegister)
+          );
+      }
+
+      if (ErrorRegister & UNC_ERR) {
+        DEBUG (
+          (EFI_D_BLKIO,
+          "AtapiPassThruCheckErrorStatus()-- %02x : Error : Uncorrectable Data\n",
+          ErrorRegister)
+          );
+      }
+
+      if (ErrorRegister & MC_ERR) {
+        DEBUG (
+          (EFI_D_BLKIO,
+          "AtapiPassThruCheckErrorStatus()-- %02x : Error : Media Change\n",
+          ErrorRegister)
+          );
+      }
+
+      if (ErrorRegister & ABRT_ERR) {
+        DEBUG (
+          (EFI_D_BLKIO,
+          "AtapiPassThruCheckErrorStatus()-- %02x : Error : Abort\n",
+          ErrorRegister)
+          );
+      }
+
+      if (ErrorRegister & TK0NF_ERR) {
+        DEBUG (
+          (EFI_D_BLKIO,
+          "AtapiPassThruCheckErrorStatus()-- %02x : Error : Track 0 Not Found\n",
+          ErrorRegister)
+          );
+      }
+
+      if (ErrorRegister & AMNF_ERR) {
+        DEBUG (
+          (EFI_D_BLKIO,
+          "AtapiPassThruCheckErrorStatus()-- %02x : Error : Address Mark Not Found\n",
+          ErrorRegister)
+          );
+       }
+    }
+
+  DEBUG_CODE_END ();
+
+  if ((StatusRegister & (ERR | DWF | CORR)) == 0) {
+    return EFI_SUCCESS;
+  }
+
+
+  return EFI_DEVICE_ERROR;
+}
+
+
+/**
+  Installs Scsi Pass Thru and/or Ext Scsi Pass Thru
+  protocols based on feature flags.
+
+  @param Controller         The controller handle to
+                            install these protocols on.
+  @param AtapiScsiPrivate   A pointer to the protocol private
+                            data structure.
+
+  @retval EFI_SUCCESS       The installation succeeds.
+  @retval other             The installation fails.
+
+**/
+EFI_STATUS
+InstallScsiPassThruProtocols (
+  IN EFI_HANDLE                     *ControllerHandle,
+  IN ATAPI_SCSI_PASS_THRU_DEV       *AtapiScsiPrivate
+  )
+{
+  EFI_STATUS                        Status;
+  EFI_SCSI_PASS_THRU_PROTOCOL       *ScsiPassThru;
+  EFI_EXT_SCSI_PASS_THRU_PROTOCOL   *ExtScsiPassThru;
+
+  ScsiPassThru = &AtapiScsiPrivate->ScsiPassThru;
+  ExtScsiPassThru = &AtapiScsiPrivate->ExtScsiPassThru;
+
+  if (FeaturePcdGet (PcdSupportScsiPassThru)) {
+    ScsiPassThru = CopyMem (ScsiPassThru, &gScsiPassThruProtocolTemplate, sizeof (*ScsiPassThru));
+    if (FeaturePcdGet (PcdSupportExtScsiPassThru)) {
+      ExtScsiPassThru = CopyMem (ExtScsiPassThru, &gExtScsiPassThruProtocolTemplate, sizeof (*ExtScsiPassThru));
+      Status = gBS->InstallMultipleProtocolInterfaces (
+                      ControllerHandle,
+                      &gEfiScsiPassThruProtocolGuid,
+                      ScsiPassThru,
+                      &gEfiExtScsiPassThruProtocolGuid,
+                      ExtScsiPassThru,
+                      NULL
+                      );
+    } else {
+      Status = gBS->InstallMultipleProtocolInterfaces (
+                      ControllerHandle,
+                      &gEfiScsiPassThruProtocolGuid,
+                      ScsiPassThru,
+                      NULL
+                      );
+    }
+  } else {
+    if (FeaturePcdGet (PcdSupportExtScsiPassThru)) {
+      ExtScsiPassThru = CopyMem (ExtScsiPassThru, &gExtScsiPassThruProtocolTemplate, sizeof (*ExtScsiPassThru));
+      Status = gBS->InstallMultipleProtocolInterfaces (
+                      ControllerHandle,
+                      &gEfiExtScsiPassThruProtocolGuid,
+                      ExtScsiPassThru,
+                      NULL
+                      );
+    } else {
+      //
+      // This driver must support either ScsiPassThru or
+      // ExtScsiPassThru protocols
+      //
+      ASSERT (FALSE);
+      Status = EFI_UNSUPPORTED;
+    }
+  }
+
+  return Status;
+}
+
+/**
+  The user Entry Point for module AtapiPassThru. The user code starts with this function.
+
+  @param[in] ImageHandle    The firmware allocated handle for the EFI image.
+  @param[in] SystemTable    A pointer to the EFI System Table.
+
+  @retval EFI_SUCCESS       The entry point is executed successfully.
+  @retval other             Some error occurs when executing this entry point.
+
+**/
+EFI_STATUS
+EFIAPI
+InitializeAtapiPassThru(
+  IN EFI_HANDLE           ImageHandle,
+  IN EFI_SYSTEM_TABLE     *SystemTable
+  )
+{
+  EFI_STATUS              Status;
+
+  //
+  // Install driver model protocol(s).
+  //
+  Status = EfiLibInstallDriverBindingComponentName2 (
+             ImageHandle,
+             SystemTable,
+             &gAtapiScsiPassThruDriverBinding,
+             ImageHandle,
+             &gAtapiScsiPassThruComponentName,
+             &gAtapiScsiPassThruComponentName2
+             );
+  ASSERT_EFI_ERROR (Status);
+
+  //
+  // Install EFI Driver Supported EFI Version Protocol required for
+  // EFI drivers that are on PCI and other plug in cards.
+  //
+  gAtapiScsiPassThruDriverSupportedEfiVersion.FirmwareVersion = PcdGet32 (PcdDriverSupportedEfiVersion);
+  Status = gBS->InstallMultipleProtocolInterfaces (
+                  &ImageHandle,
+                  &gEfiDriverSupportedEfiVersionProtocolGuid,
+                  &gAtapiScsiPassThruDriverSupportedEfiVersion,
+                  NULL
+                  );
+  ASSERT_EFI_ERROR (Status);
+
+  return Status;
+}
diff --git a/Drivers/OptionRomPkg/AtapiPassThruDxe/AtapiPassThru.h b/Drivers/OptionRomPkg/AtapiPassThruDxe/AtapiPassThru.h
new file mode 100644
index 0000000000..9fca7b6ac2
--- /dev/null
+++ b/Drivers/OptionRomPkg/AtapiPassThruDxe/AtapiPassThru.h
@@ -0,0 +1,1618 @@
+/** @file
+  Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  Module Name:  AtapiPassThru.h
+
+**/
+
+#ifndef _APT_H
+#define _APT_H
+
+
+
+#include <Uefi.h>
+
+#include <Protocol/ScsiPassThru.h>
+#include <Protocol/ScsiPassThruExt.h>
+#include <Protocol/PciIo.h>
+#include <Protocol/DriverSupportedEfiVersion.h>
+
+#include <Library/DebugLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/BaseLib.h>
+#include <Library/UefiLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/PcdLib.h>
+#include <Library/DevicePathLib.h>
+
+#include <IndustryStandard/Pci.h>
+
+#define MAX_TARGET_ID 4
+
+//
+// IDE Registers
+//
+typedef union {
+  UINT16  Command;        /* when write */
+  UINT16  Status;         /* when read */
+} IDE_CMD_OR_STATUS;
+
+typedef union {
+  UINT16  Error;          /* when read */
+  UINT16  Feature;        /* when write */
+} IDE_ERROR_OR_FEATURE;
+
+typedef union {
+  UINT16  AltStatus;      /* when read */
+  UINT16  DeviceControl;  /* when write */
+} IDE_AltStatus_OR_DeviceControl;
+
+
+typedef enum {
+  IdePrimary    = 0,
+  IdeSecondary  = 1,
+  IdeMaxChannel = 2
+} EFI_IDE_CHANNEL;
+
+///
+
+
+//
+// Bit definitions in Programming Interface byte of the Class Code field
+// in PCI IDE controller's Configuration Space
+//
+#define IDE_PRIMARY_OPERATING_MODE            BIT0
+#define IDE_PRIMARY_PROGRAMMABLE_INDICATOR    BIT1
+#define IDE_SECONDARY_OPERATING_MODE          BIT2
+#define IDE_SECONDARY_PROGRAMMABLE_INDICATOR  BIT3
+
+
+#define ATAPI_MAX_CHANNEL 2
+
+///
+/// IDE registers set
+///
+typedef struct {
+  UINT16                          Data;
+  IDE_ERROR_OR_FEATURE            Reg1;
+  UINT16                          SectorCount;
+  UINT16                          SectorNumber;
+  UINT16                          CylinderLsb;
+  UINT16                          CylinderMsb;
+  UINT16                          Head;
+  IDE_CMD_OR_STATUS               Reg;
+  IDE_AltStatus_OR_DeviceControl  Alt;
+  UINT16                          DriveAddress;
+} IDE_BASE_REGISTERS;
+
+#define ATAPI_SCSI_PASS_THRU_DEV_SIGNATURE  SIGNATURE_32 ('a', 's', 'p', 't')
+
+typedef struct {
+  UINTN                            Signature;
+  EFI_HANDLE                       Handle;
+  EFI_SCSI_PASS_THRU_PROTOCOL      ScsiPassThru;
+  EFI_EXT_SCSI_PASS_THRU_PROTOCOL  ExtScsiPassThru;
+  EFI_PCI_IO_PROTOCOL              *PciIo;
+  UINT64                           OriginalPciAttributes;
+  //
+  // Local Data goes here
+  //
+  IDE_BASE_REGISTERS               *IoPort;
+  IDE_BASE_REGISTERS               AtapiIoPortRegisters[2];
+  UINT32                           LatestTargetId;
+  UINT64                           LatestLun;
+} ATAPI_SCSI_PASS_THRU_DEV;
+
+//
+// IDE registers' base addresses
+//
+typedef struct {
+  UINT16  CommandBlockBaseAddr;
+  UINT16  ControlBlockBaseAddr;
+} IDE_REGISTERS_BASE_ADDR;
+
+#define ATAPI_SCSI_PASS_THRU_DEV_FROM_THIS(a) \
+  CR (a, \
+      ATAPI_SCSI_PASS_THRU_DEV, \
+      ScsiPassThru, \
+      ATAPI_SCSI_PASS_THRU_DEV_SIGNATURE \
+      )
+
+#define ATAPI_EXT_SCSI_PASS_THRU_DEV_FROM_THIS(a) \
+  CR (a, \
+      ATAPI_SCSI_PASS_THRU_DEV, \
+      ExtScsiPassThru, \
+      ATAPI_SCSI_PASS_THRU_DEV_SIGNATURE \
+      )
+
+//
+// Global Variables
+//
+extern EFI_DRIVER_BINDING_PROTOCOL                gAtapiScsiPassThruDriverBinding;
+extern EFI_COMPONENT_NAME_PROTOCOL                gAtapiScsiPassThruComponentName;
+extern EFI_COMPONENT_NAME2_PROTOCOL               gAtapiScsiPassThruComponentName2;
+extern EFI_DRIVER_SUPPORTED_EFI_VERSION_PROTOCOL  gAtapiScsiPassThruDriverSupportedEfiVersion;
+
+//
+// ATAPI Command op code
+//
+#define OP_INQUIRY                      0x12
+#define OP_LOAD_UNLOAD_CD               0xa6
+#define OP_MECHANISM_STATUS             0xbd
+#define OP_MODE_SELECT_10               0x55
+#define OP_MODE_SENSE_10                0x5a
+#define OP_PAUSE_RESUME                 0x4b
+#define OP_PLAY_AUDIO_10                0x45
+#define OP_PLAY_AUDIO_MSF               0x47
+#define OP_PLAY_CD                      0xbc
+#define OP_PLAY_CD_MSF                  0xb4
+#define OP_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1e
+#define OP_READ_10                      0x28
+#define OP_READ_12                      0xa8
+#define OP_READ_CAPACITY                0x25
+#define OP_READ_CD                      0xbe
+#define OP_READ_CD_MSF                  0xb9
+#define OP_READ_HEADER                  0x44
+#define OP_READ_SUB_CHANNEL             0x42
+#define OP_READ_TOC                     0x43
+#define OP_REQUEST_SENSE                0x03
+#define OP_SCAN                         0xba
+#define OP_SEEK_10                      0x2b
+#define OP_SET_CD_SPEED                 0xbb
+#define OP_STOPPLAY_SCAN                0x4e
+#define OP_START_STOP_UNIT              0x1b
+#define OP_TEST_UNIT_READY              0x00
+
+#define OP_FORMAT_UNIT                  0x04
+#define OP_READ_FORMAT_CAPACITIES       0x23
+#define OP_VERIFY                       0x2f
+#define OP_WRITE_10                     0x2a
+#define OP_WRITE_12                     0xaa
+#define OP_WRITE_AND_VERIFY             0x2e
+
+//
+// ATA Command
+//
+#define ATAPI_SOFT_RESET_CMD  0x08
+
+typedef enum {
+  DataIn  = 0,
+  DataOut = 1,
+  DataBi  = 2,
+  NoData  = 3,
+  End     = 0xff
+} DATA_DIRECTION;
+
+typedef struct {
+  UINT8           OpCode;
+  DATA_DIRECTION  Direction;
+} SCSI_COMMAND_SET;
+
+#define MAX_CHANNEL         2
+
+#define ValidCdbLength(Len) ((Len) == 6 || (Len) == 10 || (Len) == 12) ? 1 : 0
+
+//
+// IDE registers bit definitions
+//
+// ATA Err Reg bitmap
+//
+#define BBK_ERR   BIT7 ///< Bad block detected
+#define UNC_ERR   BIT6 ///< Uncorrectable Data
+#define MC_ERR    BIT5 ///< Media Change
+#define IDNF_ERR  BIT4 ///< ID Not Found
+#define MCR_ERR   BIT3 ///< Media Change Requested
+#define ABRT_ERR  BIT2 ///< Aborted Command
+#define TK0NF_ERR BIT1 ///< Track 0 Not Found
+#define AMNF_ERR  BIT0 ///< Address Mark Not Found
+
+//
+// ATAPI Err Reg bitmap
+//
+#define SENSE_KEY_ERR (BIT7 | BIT6 | BIT5 | BIT4)
+#define EOM_ERR BIT1 ///< End of Media Detected
+#define ILI_ERR BIT0 ///< Illegal Length Indication
+
+//
+// Device/Head Reg
+//
+#define LBA_MODE  BIT6
+#define DEV       BIT4
+#define HS3       BIT3
+#define HS2       BIT2
+#define HS1       BIT1
+#define HS0       BIT0
+#define CHS_MODE  (0)
+#define DRV0      (0)
+#define DRV1      (1)
+#define MST_DRV   DRV0
+#define SLV_DRV   DRV1
+
+//
+// Status Reg
+//
+#define BSY   BIT7 ///< Controller Busy
+#define DRDY  BIT6 ///< Drive Ready
+#define DWF   BIT5 ///< Drive Write Fault
+#define DSC   BIT4 ///< Disk Seek Complete
+#define DRQ   BIT3 ///< Data Request
+#define CORR  BIT2 ///< Corrected Data
+#define IDX   BIT1 ///< Index
+#define ERR   BIT0 ///< Error
+#define CHECK BIT0 ///< Check bit for ATAPI Status Reg
+
+//
+// Device Control Reg
+//
+#define SRST  BIT2 ///< Software Reset
+#define IEN_L BIT1 ///< Interrupt Enable
+
+//
+// ATAPI Feature Register
+//
+#define OVERLAP BIT1
+#define DMA     BIT0
+
+//
+// ATAPI Interrupt Reason Reson Reg (ATA Sector Count Register)
+//
+#define RELEASE     BIT2
+#define IO          BIT1
+#define CoD         BIT0
+
+#define PACKET_CMD  0xA0
+
+#define DEFAULT_CMD (0xa0)
+//
+// default content of device control register, disable INT
+//
+#define DEFAULT_CTL           (0x0a)
+#define MAX_ATAPI_BYTE_COUNT  (0xfffe)
+
+//
+// function prototype
+//
+
+EFI_STATUS
+EFIAPI
+AtapiScsiPassThruDriverBindingSupported (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   Controller,
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
+  );
+
+EFI_STATUS
+EFIAPI
+AtapiScsiPassThruDriverBindingStart (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   Controller,
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
+  );
+
+EFI_STATUS
+EFIAPI
+AtapiScsiPassThruDriverBindingStop (
+  IN  EFI_DRIVER_BINDING_PROTOCOL     *This,
+  IN  EFI_HANDLE                      Controller,
+  IN  UINTN                           NumberOfChildren,
+  IN  EFI_HANDLE                      *ChildHandleBuffer
+  );
+
+//
+// EFI Component Name Functions
+//
+/**
+  Retrieves a Unicode string that is the user readable name of the driver.
+
+  This function retrieves the user readable name of a driver in the form of a
+  Unicode string. If the driver specified by This has a user readable name in
+  the language specified by Language, then a pointer to the driver name is
+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+  by This does not support the language specified by Language,
+  then EFI_UNSUPPORTED is returned.
+
+  @param  This[in]              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+                                EFI_COMPONENT_NAME_PROTOCOL instance.
+
+  @param  Language[in]          A pointer to a Null-terminated ASCII string
+                                array indicating the language. This is the
+                                language of the driver name that the caller is
+                                requesting, and it must match one of the
+                                languages specified in SupportedLanguages. The
+                                number of languages supported by a driver is up
+                                to the driver writer. Language is specified
+                                in RFC 4646 or ISO 639-2 language code format.
+
+  @param  DriverName[out]       A pointer to the Unicode string to return.
+                                This Unicode string is the name of the
+                                driver specified by This in the language
+                                specified by Language.
+
+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by
+                                This and the language specified by Language was
+                                returned in DriverName.
+
+  @retval EFI_INVALID_PARAMETER Language is NULL.
+
+  @retval EFI_INVALID_PARAMETER DriverName is NULL.
+
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support
+                                the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+AtapiScsiPassThruComponentNameGetDriverName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,
+  IN  CHAR8                        *Language,
+  OUT CHAR16                       **DriverName
+  );
+
+
+/**
+  Retrieves a Unicode string that is the user readable name of the controller
+  that is being managed by a driver.
+
+  This function retrieves the user readable name of the controller specified by
+  ControllerHandle and ChildHandle in the form of a Unicode string. If the
+  driver specified by This has a user readable name in the language specified by
+  Language, then a pointer to the controller name is returned in ControllerName,
+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently
+  managing the controller specified by ControllerHandle and ChildHandle,
+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not
+  support the language specified by Language, then EFI_UNSUPPORTED is returned.
+
+  @param  This[in]              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+                                EFI_COMPONENT_NAME_PROTOCOL instance.
+
+  @param  ControllerHandle[in]  The handle of a controller that the driver
+                                specified by This is managing.  This handle
+                                specifies the controller whose name is to be
+                                returned.
+
+  @param  ChildHandle[in]       The handle of the child controller to retrieve
+                                the name of.  This is an optional parameter that
+                                may be NULL.  It will be NULL for device
+                                drivers.  It will also be NULL for a bus drivers
+                                that wish to retrieve the name of the bus
+                                controller.  It will not be NULL for a bus
+                                driver that wishes to retrieve the name of a
+                                child controller.
+
+  @param  Language[in]          A pointer to a Null-terminated ASCII string
+                                array indicating the language.  This is the
+                                language of the driver name that the caller is
+                                requesting, and it must match one of the
+                                languages specified in SupportedLanguages. The
+                                number of languages supported by a driver is up
+                                to the driver writer. Language is specified in
+                                RFC 4646 or ISO 639-2 language code format.
+
+  @param  ControllerName[out]   A pointer to the Unicode string to return.
+                                This Unicode string is the name of the
+                                controller specified by ControllerHandle and
+                                ChildHandle in the language specified by
+                                Language from the point of view of the driver
+                                specified by This.
+
+  @retval EFI_SUCCESS           The Unicode string for the user readable name in
+                                the language specified by Language for the
+                                driver specified by This was returned in
+                                DriverName.
+
+  @retval EFI_INVALID_PARAMETER ControllerHandle is NULL.
+
+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid
+                                EFI_HANDLE.
+
+  @retval EFI_INVALID_PARAMETER Language is NULL.
+
+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+
+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently
+                                managing the controller specified by
+                                ControllerHandle and ChildHandle.
+
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support
+                                the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+AtapiScsiPassThruComponentNameGetControllerName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL                     *This,
+  IN  EFI_HANDLE                                      ControllerHandle,
+  IN  EFI_HANDLE                                      ChildHandle        OPTIONAL,
+  IN  CHAR8                                           *Language,
+  OUT CHAR16                                          **ControllerName
+  );
+
+
+EFI_STATUS
+EFIAPI
+AtapiScsiPassThruDriverEntryPoint (
+  IN EFI_HANDLE         ImageHandle,
+  IN EFI_SYSTEM_TABLE   *SystemTable
+  )
+ /*++
+
+Routine Description:
+
+  Entry point for EFI drivers.
+
+Arguments:
+
+  ImageHandle - EFI_HANDLE
+  SystemTable - EFI_SYSTEM_TABLE
+
+Returns:
+
+  EFI_SUCCESS
+  Others 
+
+--*/
+;
+
+EFI_STATUS
+RegisterAtapiScsiPassThru (
+  IN  EFI_DRIVER_BINDING_PROTOCOL *This,
+  IN  EFI_HANDLE                  Controller,
+  IN  EFI_PCI_IO_PROTOCOL         *PciIo,
+  IN  UINT64                      OriginalPciAttributes
+  )
+/*++
+
+Routine Description:
+  Attaches SCSI Pass Thru Protocol for specified IDE channel.
+    
+Arguments:
+  This              - Protocol instance pointer.
+  Controller        - Parent device handle to the IDE channel.    
+  PciIo             - PCI I/O protocol attached on the "Controller".                        
+  
+Returns:
+  Always return EFI_SUCCESS unless installing SCSI Pass Thru Protocol failed.
+
+--*/
+;
+
+EFI_STATUS
+EFIAPI
+AtapiScsiPassThruFunction (
+  IN EFI_SCSI_PASS_THRU_PROTOCOL                        *This,
+  IN UINT32                                             Target,
+  IN UINT64                                             Lun,
+  IN OUT EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET         *Packet,
+  IN EFI_EVENT                                          Event OPTIONAL
+  )
+/*++
+
+Routine Description:
+
+  Implements EFI_SCSI_PASS_THRU_PROTOCOL.PassThru() function.
+
+Arguments:
+
+  This:     The EFI_SCSI_PASS_THRU_PROTOCOL instance.
+  Target:   The Target ID of the ATAPI device to send the SCSI 
+            Request Packet. To ATAPI devices attached on an IDE
+            Channel, Target ID 0 indicates Master device;Target
+            ID 1 indicates Slave device.
+  Lun:      The LUN of the ATAPI device to send the SCSI Request
+            Packet. To the ATAPI device, Lun is always 0.
+  Packet:   The SCSI Request Packet to send to the ATAPI device 
+            specified by Target and Lun.
+  Event:    If non-blocking I/O is not supported then Event is ignored, 
+            and blocking I/O is performed.
+            If Event is NULL, then blocking I/O is performed.
+            If Event is not NULL and non blocking I/O is supported, 
+            then non-blocking I/O is performed, and Event will be signaled 
+            when the SCSI Request Packet completes.      
+
+Returns:  
+
+   EFI_STATUS
+
+--*/
+;
+
+EFI_STATUS
+EFIAPI
+AtapiScsiPassThruGetNextDevice (
+  IN  EFI_SCSI_PASS_THRU_PROTOCOL    *This,
+  IN OUT UINT32                      *Target,
+  IN OUT UINT64                      *Lun
+  )
+/*++
+
+Routine Description:
+
+  Used to retrieve the list of legal Target IDs for SCSI devices 
+  on a SCSI channel.
+
+Arguments:
+
+  This                  - Protocol instance pointer.
+  Target                - On input, a pointer to the Target ID of a SCSI 
+                          device present on the SCSI channel.  On output, 
+                          a pointer to the Target ID of the next SCSI device
+                          present on a SCSI channel.  An input value of 
+                          0xFFFFFFFF retrieves the Target ID of the first 
+                          SCSI device present on a SCSI channel.
+  Lun                   - On input, a pointer to the LUN of a SCSI device
+                          present on the SCSI channel. On output, a pointer
+                          to the LUN of the next SCSI device present on 
+                          a SCSI channel.
+Returns:
+
+  EFI_SUCCESS           - The Target ID and Lun of the next SCSI device 
+                          on the SCSI channel was returned in Target and Lun.
+  EFI_NOT_FOUND         - There are no more SCSI devices on this SCSI channel.
+  EFI_INVALID_PARAMETER - Target is not 0xFFFFFFFF,and Target and Lun were not
+                           returned on a previous call to GetNextDevice().
+
+--*/
+;
+
+EFI_STATUS
+EFIAPI
+AtapiScsiPassThruBuildDevicePath (
+  IN     EFI_SCSI_PASS_THRU_PROTOCOL    *This,
+  IN     UINT32                         Target,
+  IN     UINT64                         Lun,
+  IN OUT EFI_DEVICE_PATH_PROTOCOL       **DevicePath
+  )
+/*++
+
+Routine Description:
+
+  Used to allocate and build a device path node for a SCSI device 
+  on a SCSI channel. Would not build device path for a SCSI Host Controller.
+
+Arguments:
+
+  This                  - Protocol instance pointer.
+  Target                - The Target ID of the SCSI device for which
+                          a device path node is to be allocated and built.
+  Lun                   - The LUN of the SCSI device for which a device 
+                          path node is to be allocated and built.
+  DevicePath            - A pointer to a single device path node that 
+                          describes the SCSI device specified by 
+                          Target and Lun. This function is responsible 
+                          for allocating the buffer DevicePath with the boot
+                          service AllocatePool().  It is the caller's 
+                          responsibility to free DevicePath when the caller
+                          is finished with DevicePath.    
+  Returns:
+  EFI_SUCCESS           - The device path node that describes the SCSI device
+                          specified by Target and Lun was allocated and 
+                          returned in DevicePath.
+  EFI_NOT_FOUND         - The SCSI devices specified by Target and Lun does
+                          not exist on the SCSI channel.
+  EFI_INVALID_PARAMETER - DevicePath is NULL.
+  EFI_OUT_OF_RESOURCES  - There are not enough resources to allocate 
+                          DevicePath.
+
+--*/
+;
+
+EFI_STATUS
+EFIAPI
+AtapiScsiPassThruGetTargetLun (
+  IN  EFI_SCSI_PASS_THRU_PROTOCOL    *This,
+  IN  EFI_DEVICE_PATH_PROTOCOL       *DevicePath,
+  OUT UINT32                         *Target,
+  OUT UINT64                         *Lun
+  )
+/*++
+
+Routine Description:
+
+  Used to translate a device path node to a Target ID and LUN.
+
+Arguments:
+
+  This                  - Protocol instance pointer.
+  DevicePath            - A pointer to the device path node that 
+                          describes a SCSI device on the SCSI channel.
+  Target                - A pointer to the Target ID of a SCSI device 
+                          on the SCSI channel. 
+  Lun                   - A pointer to the LUN of a SCSI device on 
+                          the SCSI channel.    
+Returns:
+
+  EFI_SUCCESS           - DevicePath was successfully translated to a 
+                          Target ID and LUN, and they were returned 
+                          in Target and Lun.
+  EFI_INVALID_PARAMETER - DevicePath/Target/Lun is NULL.
+  EFI_UNSUPPORTED       - This driver does not support the device path 
+                          node type in DevicePath.
+  EFI_NOT_FOUND         - A valid translation from DevicePath to a 
+                          Target ID and LUN does not exist.
+
+--*/
+;
+
+EFI_STATUS
+EFIAPI
+AtapiScsiPassThruResetChannel (
+  IN  EFI_SCSI_PASS_THRU_PROTOCOL   *This
+  )
+/*++
+
+Routine Description:
+
+  Resets a SCSI channel.This operation resets all the 
+  SCSI devices connected to the SCSI channel.
+
+Arguments:
+
+  This                  - Protocol instance pointer.
+
+Returns:
+
+  EFI_SUCCESS           - The SCSI channel was reset.
+  EFI_UNSUPPORTED       - The SCSI channel does not support 
+                          a channel reset operation.
+  EFI_DEVICE_ERROR      - A device error occurred while 
+                          attempting to reset the SCSI channel.
+  EFI_TIMEOUT           - A timeout occurred while attempting 
+                          to reset the SCSI channel.
+
+--*/
+;
+
+EFI_STATUS
+EFIAPI
+AtapiScsiPassThruResetTarget (
+  IN EFI_SCSI_PASS_THRU_PROTOCOL    *This,
+  IN UINT32                         Target,
+  IN UINT64                         Lun
+  )
+/*++
+
+Routine Description:
+
+  Resets a SCSI device that is connected to a SCSI channel.
+
+Arguments:
+
+  This                  - Protocol instance pointer.
+  Target                - The Target ID of the SCSI device to reset. 
+  Lun                   - The LUN of the SCSI device to reset.
+    
+Returns:
+
+  EFI_SUCCESS           - The SCSI device specified by Target and 
+                          Lun was reset.
+  EFI_UNSUPPORTED       - The SCSI channel does not support a target
+                          reset operation.
+  EFI_INVALID_PARAMETER - Target or Lun are invalid.
+  EFI_DEVICE_ERROR      - A device error occurred while attempting 
+                          to reset the SCSI device specified by Target 
+                          and Lun.
+  EFI_TIMEOUT           - A timeout occurred while attempting to reset 
+                          the SCSI device specified by Target and Lun.
+
+--*/
+;
+
+EFI_STATUS
+EFIAPI
+AtapiExtScsiPassThruFunction (
+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL                    *This,
+  IN UINT8                                              *Target,
+  IN UINT64                                             Lun,
+  IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET     *Packet,
+  IN EFI_EVENT                                          Event OPTIONAL
+  )
+/*++
+
+Routine Description:
+
+  Implements EFI_EXT_SCSI_PASS_THRU_PROTOCOL.PassThru() function.
+
+Arguments:
+
+  This:     The EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance.
+  Target:   The Target ID of the ATAPI device to send the SCSI 
+            Request Packet. To ATAPI devices attached on an IDE
+            Channel, Target ID 0 indicates Master device;Target
+            ID 1 indicates Slave device.
+  Lun:      The LUN of the ATAPI device to send the SCSI Request
+            Packet. To the ATAPI device, Lun is always 0.
+  Packet:   The SCSI Request Packet to send to the ATAPI device 
+            specified by Target and Lun.
+  Event:    If non-blocking I/O is not supported then Event is ignored, 
+            and blocking I/O is performed.
+            If Event is NULL, then blocking I/O is performed.
+            If Event is not NULL and non blocking I/O is supported, 
+            then non-blocking I/O is performed, and Event will be signaled 
+            when the SCSI Request Packet completes.      
+
+Returns:  
+
+  EFI_STATUS
+
+--*/
+;
+
+EFI_STATUS
+EFIAPI
+AtapiExtScsiPassThruGetNextTargetLun (
+  IN  EFI_EXT_SCSI_PASS_THRU_PROTOCOL    *This,
+  IN OUT UINT8                           **Target,
+  IN OUT UINT64                          *Lun
+  )
+/*++
+
+Routine Description:
+
+  Used to retrieve the list of legal Target IDs for SCSI devices 
+  on a SCSI channel.
+
+Arguments:
+
+  This                  - Protocol instance pointer.
+  Target                - On input, a pointer to the Target ID of a SCSI 
+                          device present on the SCSI channel.  On output, 
+                          a pointer to the Target ID of the next SCSI device
+                          present on a SCSI channel.  An input value of 
+                          0xFFFFFFFF retrieves the Target ID of the first 
+                          SCSI device present on a SCSI channel.
+  Lun                   - On input, a pointer to the LUN of a SCSI device
+                          present on the SCSI channel. On output, a pointer
+                          to the LUN of the next SCSI device present on 
+                          a SCSI channel.
+Returns:
+
+  EFI_SUCCESS           - The Target ID and Lun of the next SCSI device 
+                          on the SCSI channel was returned in Target and Lun.
+  EFI_NOT_FOUND         - There are no more SCSI devices on this SCSI channel.
+  EFI_INVALID_PARAMETER - Target is not 0xFFFFFFFF,and Target and Lun were not
+                           returned on a previous call to GetNextDevice().
+
+--*/
+;
+
+EFI_STATUS
+EFIAPI
+AtapiExtScsiPassThruBuildDevicePath (
+  IN     EFI_EXT_SCSI_PASS_THRU_PROTOCOL    *This,
+  IN     UINT8                              *Target,
+  IN     UINT64                             Lun,
+  IN OUT EFI_DEVICE_PATH_PROTOCOL           **DevicePath
+  )
+/*++
+
+Routine Description:
+
+  Used to allocate and build a device path node for a SCSI device 
+  on a SCSI channel. Would not build device path for a SCSI Host Controller.
+
+Arguments:
+
+  This                  - Protocol instance pointer.
+  Target                - The Target ID of the SCSI device for which
+                          a device path node is to be allocated and built.
+  Lun                   - The LUN of the SCSI device for which a device 
+                          path node is to be allocated and built.
+  DevicePath            - A pointer to a single device path node that 
+                          describes the SCSI device specified by 
+                          Target and Lun. This function is responsible 
+                          for allocating the buffer DevicePath with the boot
+                          service AllocatePool().  It is the caller's 
+                          responsibility to free DevicePath when the caller
+                          is finished with DevicePath.    
+  Returns:
+  EFI_SUCCESS           - The device path node that describes the SCSI device
+                          specified by Target and Lun was allocated and 
+                          returned in DevicePath.
+  EFI_NOT_FOUND         - The SCSI devices specified by Target and Lun does
+                          not exist on the SCSI channel.
+  EFI_INVALID_PARAMETER - DevicePath is NULL.
+  EFI_OUT_OF_RESOURCES  - There are not enough resources to allocate 
+                          DevicePath.
+
+--*/
+;
+
+EFI_STATUS
+EFIAPI
+AtapiExtScsiPassThruGetTargetLun (
+  IN  EFI_EXT_SCSI_PASS_THRU_PROTOCOL    *This,
+  IN  EFI_DEVICE_PATH_PROTOCOL       *DevicePath,
+  OUT UINT8                          **Target,
+  OUT UINT64                         *Lun
+  )
+/*++
+
+Routine Description:
+
+  Used to translate a device path node to a Target ID and LUN.
+
+Arguments:
+
+  This                  - Protocol instance pointer.
+  DevicePath            - A pointer to the device path node that 
+                          describes a SCSI device on the SCSI channel.
+  Target                - A pointer to the Target ID of a SCSI device 
+                          on the SCSI channel. 
+  Lun                   - A pointer to the LUN of a SCSI device on 
+                          the SCSI channel.    
+Returns:
+
+  EFI_SUCCESS           - DevicePath was successfully translated to a 
+                          Target ID and LUN, and they were returned 
+                          in Target and Lun.
+  EFI_INVALID_PARAMETER - DevicePath/Target/Lun is NULL.
+  EFI_UNSUPPORTED       - This driver does not support the device path 
+                          node type in DevicePath.
+  EFI_NOT_FOUND         - A valid translation from DevicePath to a 
+                          Target ID and LUN does not exist.
+
+--*/
+;
+
+EFI_STATUS
+EFIAPI
+AtapiExtScsiPassThruResetChannel (
+  IN  EFI_EXT_SCSI_PASS_THRU_PROTOCOL   *This
+  )
+/*++
+
+Routine Description:
+
+  Resets a SCSI channel.This operation resets all the 
+  SCSI devices connected to the SCSI channel.
+
+Arguments:
+
+  This                  - Protocol instance pointer.
+
+Returns:
+
+  EFI_SUCCESS           - The SCSI channel was reset.
+  EFI_UNSUPPORTED       - The SCSI channel does not support 
+                          a channel reset operation.
+  EFI_DEVICE_ERROR      - A device error occurred while 
+                          attempting to reset the SCSI channel.
+  EFI_TIMEOUT           - A timeout occurred while attempting 
+                          to reset the SCSI channel.
+
+--*/
+;
+
+EFI_STATUS
+EFIAPI
+AtapiExtScsiPassThruResetTarget (
+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL    *This,
+  IN UINT8                              *Target,
+  IN UINT64                             Lun
+  )
+/*++
+
+Routine Description:
+
+  Resets a SCSI device that is connected to a SCSI channel.
+
+Arguments:
+
+  This                  - Protocol instance pointer.
+  Target                - The Target ID of the SCSI device to reset. 
+  Lun                   - The LUN of the SCSI device to reset.
+    
+Returns:
+
+  EFI_SUCCESS           - The SCSI device specified by Target and 
+                          Lun was reset.
+  EFI_UNSUPPORTED       - The SCSI channel does not support a target
+                          reset operation.
+  EFI_INVALID_PARAMETER - Target or Lun are invalid.
+  EFI_DEVICE_ERROR      - A device error occurred while attempting 
+                          to reset the SCSI device specified by Target 
+                          and Lun.
+  EFI_TIMEOUT           - A timeout occurred while attempting to reset 
+                          the SCSI device specified by Target and Lun.
+
+--*/
+;
+
+EFI_STATUS
+EFIAPI
+AtapiExtScsiPassThruGetNextTarget (
+  IN  EFI_EXT_SCSI_PASS_THRU_PROTOCOL    *This,
+  IN OUT UINT8                           **Target
+  )
+/*++
+
+Routine Description:
+  Used to retrieve the list of legal Target IDs for SCSI devices 
+  on a SCSI channel.
+
+Arguments:
+  This                  - Protocol instance pointer.
+  Target                - On input, a pointer to the Target ID of a SCSI 
+                          device present on the SCSI channel.  On output, 
+                          a pointer to the Target ID of the next SCSI device
+                           present on a SCSI channel.  An input value of 
+                           0xFFFFFFFF retrieves the Target ID of the first 
+                           SCSI device present on a SCSI channel.
+  Lun                   - On input, a pointer to the LUN of a SCSI device
+                          present on the SCSI channel. On output, a pointer
+                          to the LUN of the next SCSI device present on 
+                          a SCSI channel.
+    
+Returns:
+  EFI_SUCCESS           - The Target ID and Lun of the next SCSI device 
+                          on the SCSI channel was returned in Target and Lun.
+  EFI_NOT_FOUND         - There are no more SCSI devices on this SCSI channel.
+  EFI_INVALID_PARAMETER - Target is not 0xFFFFFFFF,and Target and Lun were not
+                          returned on a previous call to GetNextDevice().
+
+--*/
+;
+
+EFI_STATUS
+CheckSCSIRequestPacket (
+  EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET      *Packet
+  )
+/*++
+
+Routine Description:
+
+  Checks the parameters in the SCSI Request Packet to make sure
+  they are valid for a SCSI Pass Thru request.
+
+Arguments:
+
+  Packet         -  The pointer of EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET   
+
+Returns:
+
+  EFI_STATUS
+
+--*/
+;
+
+EFI_STATUS
+SubmitBlockingIoCommand (
+  ATAPI_SCSI_PASS_THRU_DEV                  *AtapiScsiPrivate,
+  UINT32                                    Target,
+  EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET    *Packet
+  )
+/*++
+
+Routine Description:
+
+  Performs blocking I/O request.
+    
+Arguments:
+
+  AtapiScsiPrivate:   Private data structure for the specified channel.
+  Target:             The Target ID of the ATAPI device to send the SCSI 
+                      Request Packet. To ATAPI devices attached on an IDE
+                      Channel, Target ID 0 indicates Master device;Target
+                      ID 1 indicates Slave device.
+  Packet:             The SCSI Request Packet to send to the ATAPI device 
+                      specified by Target.
+  
+  Returns:            EFI_STATUS  
+
+--*/
+;
+
+BOOLEAN
+IsCommandValid (
+  EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET   *Packet
+  )
+ /*++
+
+Routine Description:
+
+  Checks the requested SCSI command: 
+  Is it supported by this driver?
+  Is the Data transfer direction reasonable?
+
+Arguments:
+
+  Packet         -  The pointer of EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET   
+
+Returns:
+
+  EFI_STATUS
+
+--*/
+;
+
+EFI_STATUS
+CheckExtSCSIRequestPacket (
+  EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET      *Packet
+  )
+/*++
+
+Routine Description:
+
+  Checks the parameters in the SCSI Request Packet to make sure
+  they are valid for a SCSI Pass Thru request.
+
+Arguments:
+
+  Packet       - The pointer of EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
+  
+Returns:
+  
+  EFI_STATUS
+
+--*/
+;
+
+
+BOOLEAN
+IsExtCommandValid (
+  EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET   *Packet
+  )
+/*++
+  
+Routine Description:
+
+  Checks the requested SCSI command: 
+  Is it supported by this driver?
+  Is the Data transfer direction reasonable?
+
+Arguments:
+
+  Packet         -  The pointer of EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET   
+
+Returns:
+
+  EFI_STATUS
+
+--*/
+;
+
+EFI_STATUS
+SubmitExtBlockingIoCommand (
+  ATAPI_SCSI_PASS_THRU_DEV                      *AtapiScsiPrivate,
+  UINT8                                         Target,
+  EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET    *Packet
+  )
+/*++
+
+Routine Description:
+
+  Performs blocking I/O request.
+    
+Arguments:
+
+  AtapiScsiPrivate:   Private data structure for the specified channel.
+  Target:             The Target ID of the ATAPI device to send the SCSI 
+                      Request Packet. To ATAPI devices attached on an IDE
+                      Channel, Target ID 0 indicates Master device;Target
+                      ID 1 indicates Slave device.
+  Packet:             The SCSI Request Packet to send to the ATAPI device 
+                      specified by Target.
+  
+  Returns:            EFI_STATUS  
+  
+--*/
+;
+
+EFI_STATUS
+RequestSenseCommand (
+  ATAPI_SCSI_PASS_THRU_DEV    *AtapiScsiPrivate,
+  UINT32                      Target,
+  UINT64                      Timeout,
+  VOID                        *SenseData,
+  UINT8                       *SenseDataLength
+  )
+/*++
+
+Routine Description:
+
+  Submit request sense command
+
+Arguments:
+
+  AtapiScsiPrivate  - The pointer of ATAPI_SCSI_PASS_THRU_DEV
+  Target            - The target ID
+  Timeout           - The time to complete the command
+  SenseData         - The buffer to fill in sense data
+  SenseDataLength   - The length of buffer
+
+Returns:
+
+  EFI_STATUS
+
+--*/
+;
+
+EFI_STATUS
+AtapiPacketCommand (
+  ATAPI_SCSI_PASS_THRU_DEV                  *AtapiScsiPrivate,
+  UINT32                                    Target,
+  UINT8                                     *PacketCommand,
+  VOID                                      *Buffer,
+  UINT32                                    *ByteCount,
+  DATA_DIRECTION                            Direction,
+  UINT64                                    TimeOutInMicroSeconds
+  )
+/*++
+
+Routine Description:
+
+  Submits ATAPI command packet to the specified ATAPI device.
+    
+Arguments:
+
+  AtapiScsiPrivate:   Private data structure for the specified channel.
+  Target:             The Target ID of the ATAPI device to send the SCSI 
+                      Request Packet. To ATAPI devices attached on an IDE
+                      Channel, Target ID 0 indicates Master device;Target
+                      ID 1 indicates Slave device.
+  PacketCommand:      Points to the ATAPI command packet.
+  Buffer:             Points to the transferred data.
+  ByteCount:          When input,indicates the buffer size; when output,
+                      indicates the actually transferred data size.
+  Direction:          Indicates the data transfer direction. 
+  TimeoutInMicroSeconds:
+                      The timeout, in micro second units, to use for the 
+                      execution of this ATAPI command.
+                      A TimeoutInMicroSeconds value of 0 means that 
+                      this function will wait indefinitely for the ATAPI 
+                      command to execute.
+                      If TimeoutInMicroSeconds is greater than zero, then 
+                      this function will return EFI_TIMEOUT if the time 
+                      required to execute the ATAPI command is greater 
+                      than TimeoutInMicroSeconds.
+  
+Returns:
+
+  EFI_STATUS
+
+--*/
+;
+
+
+UINT8
+ReadPortB (
+  IN  EFI_PCI_IO_PROTOCOL   *PciIo,
+  IN  UINT16                Port
+  )
+/*++
+
+Routine Description:
+
+  Read one byte from a specified I/O port.
+
+Arguments:
+
+  PciIo      - The pointer of EFI_PCI_IO_PROTOCOL
+  Port       - IO port
+  
+Returns:
+
+  A byte read out
+
+--*/
+;
+
+
+UINT16
+ReadPortW (
+  IN  EFI_PCI_IO_PROTOCOL   *PciIo,
+  IN  UINT16                Port
+  )
+/*++
+
+Routine Description:
+
+  Read one word from a specified I/O port.
+
+Arguments:
+
+  PciIo      - The pointer of EFI_PCI_IO_PROTOCOL
+  Port       - IO port
+  
+Returns:     
+
+  A word read out
+
+--*/
+;
+
+
+VOID
+WritePortB (
+  IN  EFI_PCI_IO_PROTOCOL   *PciIo,
+  IN  UINT16                Port,
+  IN  UINT8                 Data
+  )
+/*++
+
+Routine Description:
+
+  Write one byte to a specified I/O port.
+
+Arguments:
+
+  PciIo      - The pointer of EFI_PCI_IO_PROTOCOL
+  Port       - IO port
+  Data       - The data to write
+  
+Returns:
+ 
+  NONE
+ 
+--*/
+;
+
+
+VOID
+WritePortW (
+  IN  EFI_PCI_IO_PROTOCOL   *PciIo,
+  IN  UINT16                Port,
+  IN  UINT16                Data
+  )
+/*++
+
+Routine Description:
+
+  Write one word to a specified I/O port.
+
+Arguments:
+
+  PciIo      - The pointer of EFI_PCI_IO_PROTOCOL
+  Port       - IO port
+  Data       - The data to write
+  
+Returns:
+
+  NONE
+  
+--*/
+;
+
+EFI_STATUS
+StatusDRQClear (
+  ATAPI_SCSI_PASS_THRU_DEV        *AtapiScsiPrivate,
+  UINT64                          TimeOutInMicroSeconds
+  )
+/*++
+
+Routine Description:
+
+  Check whether DRQ is clear in the Status Register. (BSY must also be cleared)
+  If TimeoutInMicroSeconds is zero, this routine should wait infinitely for
+  DRQ clear. Otherwise, it will return EFI_TIMEOUT when specified time is 
+  elapsed.
+
+Arguments:
+
+  AtapiScsiPrivate            - The pointer of ATAPI_SCSI_PASS_THRU_DEV
+  TimeoutInMicroSeconds       - The time to wait for
+   
+Returns:
+
+  EFI_STATUS
+
+--*/
+;
+
+EFI_STATUS
+AltStatusDRQClear (
+  ATAPI_SCSI_PASS_THRU_DEV        *AtapiScsiPrivate,
+  UINT64                          TimeOutInMicroSeconds
+  )
+/*++
+
+Routine Description:
+
+  Check whether DRQ is clear in the Alternate Status Register. 
+  (BSY must also be cleared).If TimeoutInMicroSeconds is zero, this routine should 
+  wait infinitely for DRQ clear. Otherwise, it will return EFI_TIMEOUT when specified time is 
+  elapsed.
+
+Arguments:
+
+  AtapiScsiPrivate            - The pointer of ATAPI_SCSI_PASS_THRU_DEV
+  TimeoutInMicroSeconds       - The time to wait for
+   
+Returns:
+
+  EFI_STATUS
+
+--*/
+;
+
+EFI_STATUS
+StatusDRQReady (
+  ATAPI_SCSI_PASS_THRU_DEV        *AtapiScsiPrivate,
+  UINT64                          TimeOutInMicroSeconds
+  )
+/*++
+
+Routine Description:
+
+  Check whether DRQ is ready in the Status Register. (BSY must also be cleared)
+  If TimeoutInMicroSeconds is zero, this routine should wait infinitely for
+  DRQ ready. Otherwise, it will return EFI_TIMEOUT when specified time is 
+  elapsed.
+
+Arguments:
+
+  AtapiScsiPrivate            - The pointer of ATAPI_SCSI_PASS_THRU_DEV
+  TimeoutInMicroSeconds       - The time to wait for
+   
+Returns:
+
+  EFI_STATUS
+
+--*/
+;
+
+EFI_STATUS
+AltStatusDRQReady (
+  ATAPI_SCSI_PASS_THRU_DEV        *AtapiScsiPrivate,
+  UINT64                          TimeOutInMicroSeconds
+  )
+/*++
+
+Routine Description:
+
+  Check whether DRQ is ready in the Alternate Status Register. 
+  (BSY must also be cleared)
+  If TimeoutInMicroSeconds is zero, this routine should wait infinitely for
+  DRQ ready. Otherwise, it will return EFI_TIMEOUT when specified time is 
+  elapsed.
+
+Arguments:
+
+  AtapiScsiPrivate            - The pointer of ATAPI_SCSI_PASS_THRU_DEV
+  TimeoutInMicroSeconds       - The time to wait for
+   
+Returns:
+
+  EFI_STATUS
+
+--*/
+;
+
+EFI_STATUS
+StatusWaitForBSYClear (
+  ATAPI_SCSI_PASS_THRU_DEV    *AtapiScsiPrivate,
+  UINT64                      TimeoutInMicroSeconds
+  )
+/*++
+
+Routine Description:
+
+  Check whether BSY is clear in the Status Register.
+  If TimeoutInMicroSeconds is zero, this routine should wait infinitely for
+  BSY clear. Otherwise, it will return EFI_TIMEOUT when specified time is 
+  elapsed.
+
+Arguments:
+
+  AtapiScsiPrivate            - The pointer of ATAPI_SCSI_PASS_THRU_DEV
+  TimeoutInMicroSeconds       - The time to wait for
+   
+Returns:
+
+  EFI_STATUS
+
+--*/
+;
+
+EFI_STATUS
+AltStatusWaitForBSYClear (
+  ATAPI_SCSI_PASS_THRU_DEV    *AtapiScsiPrivate,
+  UINT64                      TimeoutInMicroSeconds
+  )
+/*++
+
+Routine Description:
+
+  Check whether BSY is clear in the Alternate Status Register.
+  If TimeoutInMicroSeconds is zero, this routine should wait infinitely for
+  BSY clear. Otherwise, it will return EFI_TIMEOUT when specified time is 
+  elapsed.
+
+Arguments:
+
+  AtapiScsiPrivate            - The pointer of ATAPI_SCSI_PASS_THRU_DEV
+  TimeoutInMicroSeconds       - The time to wait for
+   
+Returns:
+
+  EFI_STATUS
+
+--*/
+;
+
+EFI_STATUS
+StatusDRDYReady (
+  ATAPI_SCSI_PASS_THRU_DEV    *AtapiScsiPrivate,
+  UINT64                      TimeoutInMicroSeconds
+  )
+/*++
+
+Routine Description:
+
+  Check whether DRDY is ready in the Status Register. 
+  (BSY must also be cleared)
+  If TimeoutInMicroSeconds is zero, this routine should wait infinitely for
+  DRDY ready. Otherwise, it will return EFI_TIMEOUT when specified time is 
+  elapsed.
+
+Arguments:
+
+  AtapiScsiPrivate            - The pointer of ATAPI_SCSI_PASS_THRU_DEV
+  TimeoutInMicroSeconds       - The time to wait for
+   
+Returns:
+
+  EFI_STATUS
+
+--*/
+;
+
+EFI_STATUS
+AltStatusDRDYReady (
+  ATAPI_SCSI_PASS_THRU_DEV    *AtapiScsiPrivate,
+  UINT64                      TimeoutInMicroSeconds
+  )
+/*++
+
+Routine Description:
+
+  Check whether DRDY is ready in the Alternate Status Register. 
+  (BSY must also be cleared)
+  If TimeoutInMicroSeconds is zero, this routine should wait infinitely for
+  DRDY ready. Otherwise, it will return EFI_TIMEOUT when specified time is 
+  elapsed.
+
+Arguments:
+
+  AtapiScsiPrivate            - The pointer of ATAPI_SCSI_PASS_THRU_DEV
+  TimeoutInMicroSeconds       - The time to wait for
+   
+Returns:
+
+  EFI_STATUS
+
+--*/
+;
+
+EFI_STATUS
+AtapiPassThruPioReadWriteData (
+  ATAPI_SCSI_PASS_THRU_DEV  *AtapiScsiPrivate,
+  UINT16                    *Buffer,
+  UINT32                    *ByteCount,
+  DATA_DIRECTION            Direction,
+  UINT64                    TimeOutInMicroSeconds
+  )
+/*++
+
+Routine Description:
+
+  Performs data transfer between ATAPI device and host after the
+  ATAPI command packet is sent.
+    
+Arguments:
+
+  AtapiScsiPrivate:   Private data structure for the specified channel.    
+  Buffer:             Points to the transferred data.
+  ByteCount:          When input,indicates the buffer size; when output,
+                      indicates the actually transferred data size.
+  Direction:          Indicates the data transfer direction. 
+  TimeoutInMicroSeconds:
+                      The timeout, in micro second units, to use for the 
+                      execution of this ATAPI command.
+                      A TimeoutInMicroSeconds value of 0 means that 
+                      this function will wait indefinitely for the ATAPI 
+                      command to execute.
+                      If TimeoutInMicroSeconds is greater than zero, then 
+                      this function will return EFI_TIMEOUT if the time 
+                      required to execute the ATAPI command is greater 
+                      than TimeoutInMicroSeconds.
+ Returns:
+
+  EFI_STATUS
+
+--*/
+;
+
+EFI_STATUS
+AtapiPassThruCheckErrorStatus (
+  ATAPI_SCSI_PASS_THRU_DEV        *AtapiScsiPrivate
+  )
+/*++
+
+Routine Description:
+
+  Check Error Register for Error Information. 
+  
+Arguments:
+
+  AtapiScsiPrivate            - The pointer of ATAPI_SCSI_PASS_THRU_DEV
+   
+Returns:
+
+  EFI_STATUS
+
+--*/
+;
+
+
+EFI_STATUS
+GetIdeRegistersBaseAddr (
+  IN  EFI_PCI_IO_PROTOCOL         *PciIo,
+  OUT IDE_REGISTERS_BASE_ADDR     *IdeRegsBaseAddr
+  )
+/*++
+
+Routine Description:
+  Get IDE IO port registers' base addresses by mode. In 'Compatibility' mode,
+  use fixed addresses. In Native-PCI mode, get base addresses from BARs in
+  the PCI IDE controller's Configuration Space.
+
+Arguments:
+  PciIo             - Pointer to the EFI_PCI_IO_PROTOCOL instance
+  IdeRegsBaseAddr   - Pointer to IDE_REGISTERS_BASE_ADDR to 
+                      receive IDE IO port registers' base addresses
+                      
+Returns:
+
+  EFI_STATUS
+    
+--*/
+;
+
+
+VOID
+InitAtapiIoPortRegisters (
+  IN  ATAPI_SCSI_PASS_THRU_DEV     *AtapiScsiPrivate,
+  IN  IDE_REGISTERS_BASE_ADDR      *IdeRegsBaseAddr
+  )
+/*++
+
+Routine Description:
+
+  Initialize each Channel's Base Address of CommandBlock and ControlBlock.
+
+Arguments:
+    
+  AtapiScsiPrivate            - The pointer of ATAPI_SCSI_PASS_THRU_DEV
+  IdeRegsBaseAddr             - The pointer of IDE_REGISTERS_BASE_ADDR
+  
+Returns:
+  
+  None
+
+--*/  
+;
+
+/**
+  Installs Scsi Pass Thru and/or Ext Scsi Pass Thru 
+  protocols based on feature flags. 
+
+  @param Controller         The controller handle to 
+                            install these protocols on.
+  @param AtapiScsiPrivate   A pointer to the protocol private
+                            data structure.
+
+  @retval EFI_SUCCESS       The installation succeeds. 
+  @retval other             The installation fails. 
+   
+**/
+EFI_STATUS
+InstallScsiPassThruProtocols (
+  IN EFI_HANDLE                 *ControllerHandle,
+  IN ATAPI_SCSI_PASS_THRU_DEV   *AtapiScsiPrivate
+  );
+
+#endif
diff --git a/Drivers/OptionRomPkg/AtapiPassThruDxe/AtapiPassThruDxe.inf b/Drivers/OptionRomPkg/AtapiPassThruDxe/AtapiPassThruDxe.inf
new file mode 100644
index 0000000000..750136275a
--- /dev/null
+++ b/Drivers/OptionRomPkg/AtapiPassThruDxe/AtapiPassThruDxe.inf
@@ -0,0 +1,70 @@
+## @file
+# Description file for the Atapi Pass Thru driver.
+#
+# This driver simulates SCSI devices with Atapi devices to test the SCSI io
+#  protocol.
+# Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = AtapiPassThruDxe
+  FILE_GUID                      = E49061CE-99A7-41d3-AB3A-36E5CFBAD63E
+  MODULE_TYPE                    = UEFI_DRIVER
+  VERSION_STRING                 = 1.0
+
+  ENTRY_POINT                    = InitializeAtapiPassThru
+
+  PCI_VENDOR_ID                  = 0x8086
+  PCI_DEVICE_ID                  = 0x2921
+  PCI_CLASS_CODE                 = 0x010100
+  PCI_REVISION                   = 0x0003
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+#  VALID_ARCHITECTURES           = IA32 X64 EBC
+#
+#  DRIVER_BINDING                =  gAtapiScsiPassThruDriverBinding              
+#  COMPONENT_NAME                =  gAtapiScsiPassThruComponentName              
+#
+
+[Sources]
+  DriverSupportedEfiVersion.c
+  ComponentName.c
+  AtapiPassThru.c
+  AtapiPassThru.h
+
+
+[Packages]
+  MdePkg/MdePkg.dec
+  OptionRomPkg/OptionRomPkg.dec
+
+[LibraryClasses]
+  UefiBootServicesTableLib
+  MemoryAllocationLib
+  BaseMemoryLib
+  UefiLib
+  BaseLib
+  UefiDriverEntryPoint
+  DebugLib
+  DevicePathLib
+
+
+[Protocols]
+  gEfiScsiPassThruProtocolGuid                  # PROTOCOL BY_START
+  gEfiExtScsiPassThruProtocolGuid               # PROTOCOL BY_START
+  gEfiPciIoProtocolGuid                         # PROTOCOL TO_START
+  gEfiDriverSupportedEfiVersionProtocolGuid     # PROTOCOL ALWAYS_PRODUCED
+
+[FeaturePcd]
+  gOptionRomPkgTokenSpaceGuid.PcdSupportScsiPassThru
+  gOptionRomPkgTokenSpaceGuid.PcdSupportExtScsiPassThru
+
+[Pcd]
+  gOptionRomPkgTokenSpaceGuid.PcdDriverSupportedEfiVersion
+
diff --git a/Drivers/OptionRomPkg/AtapiPassThruDxe/ComponentName.c b/Drivers/OptionRomPkg/AtapiPassThruDxe/ComponentName.c
new file mode 100644
index 0000000000..007cb5f195
--- /dev/null
+++ b/Drivers/OptionRomPkg/AtapiPassThruDxe/ComponentName.c
@@ -0,0 +1,169 @@
+/** @file
+  Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  Module Name:  ComponentName.c
+
+**/
+#include "AtapiPassThru.h"
+
+//
+// EFI Component Name Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL  gAtapiScsiPassThruComponentName = {
+  AtapiScsiPassThruComponentNameGetDriverName,
+  AtapiScsiPassThruComponentNameGetControllerName,
+  "eng"
+};
+
+//
+// EFI Component Name 2 Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gAtapiScsiPassThruComponentName2 = {
+  (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) AtapiScsiPassThruComponentNameGetDriverName,
+  (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) AtapiScsiPassThruComponentNameGetControllerName,
+  "en"
+};
+
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mAtapiScsiPassThruDriverNameTable[] = {
+  { "eng;en", (CHAR16 *) L"ATAPI SCSI Pass Thru Driver" },
+  { NULL , NULL }
+};
+
+/**
+  Retrieves a Unicode string that is the user readable name of the driver.
+
+  This function retrieves the user readable name of a driver in the form of a
+  Unicode string. If the driver specified by This has a user readable name in
+  the language specified by Language, then a pointer to the driver name is
+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+  by This does not support the language specified by Language,
+  then EFI_UNSUPPORTED is returned.
+
+  @param  This[in]              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+                                EFI_COMPONENT_NAME_PROTOCOL instance.
+
+  @param  Language[in]          A pointer to a Null-terminated ASCII string
+                                array indicating the language. This is the
+                                language of the driver name that the caller is
+                                requesting, and it must match one of the
+                                languages specified in SupportedLanguages. The
+                                number of languages supported by a driver is up
+                                to the driver writer. Language is specified
+                                in RFC 4646 or ISO 639-2 language code format.
+
+  @param  DriverName[out]       A pointer to the Unicode string to return.
+                                This Unicode string is the name of the
+                                driver specified by This in the language
+                                specified by Language.
+
+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by
+                                This and the language specified by Language was
+                                returned in DriverName.
+
+  @retval EFI_INVALID_PARAMETER Language is NULL.
+
+  @retval EFI_INVALID_PARAMETER DriverName is NULL.
+
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support
+                                the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+AtapiScsiPassThruComponentNameGetDriverName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,
+  IN  CHAR8                        *Language,
+  OUT CHAR16                       **DriverName
+  )
+{
+  return LookupUnicodeString2 (
+           Language,
+           This->SupportedLanguages,
+           mAtapiScsiPassThruDriverNameTable,
+           DriverName,
+           (BOOLEAN)(This == &gAtapiScsiPassThruComponentName)
+           );
+}
+
+/**
+  Retrieves a Unicode string that is the user readable name of the controller
+  that is being managed by a driver.
+
+  This function retrieves the user readable name of the controller specified by
+  ControllerHandle and ChildHandle in the form of a Unicode string. If the
+  driver specified by This has a user readable name in the language specified by
+  Language, then a pointer to the controller name is returned in ControllerName,
+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently
+  managing the controller specified by ControllerHandle and ChildHandle,
+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not
+  support the language specified by Language, then EFI_UNSUPPORTED is returned.
+
+  @param  This[in]              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+                                EFI_COMPONENT_NAME_PROTOCOL instance.
+
+  @param  ControllerHandle[in]  The handle of a controller that the driver
+                                specified by This is managing.  This handle
+                                specifies the controller whose name is to be
+                                returned.
+
+  @param  ChildHandle[in]       The handle of the child controller to retrieve
+                                the name of.  This is an optional parameter that
+                                may be NULL.  It will be NULL for device
+                                drivers.  It will also be NULL for a bus drivers
+                                that wish to retrieve the name of the bus
+                                controller.  It will not be NULL for a bus
+                                driver that wishes to retrieve the name of a
+                                child controller.
+
+  @param  Language[in]          A pointer to a Null-terminated ASCII string
+                                array indicating the language.  This is the
+                                language of the driver name that the caller is
+                                requesting, and it must match one of the
+                                languages specified in SupportedLanguages. The
+                                number of languages supported by a driver is up
+                                to the driver writer. Language is specified in
+                                RFC 4646 or ISO 639-2 language code format.
+
+  @param  ControllerName[out]   A pointer to the Unicode string to return.
+                                This Unicode string is the name of the
+                                controller specified by ControllerHandle and
+                                ChildHandle in the language specified by
+                                Language from the point of view of the driver
+                                specified by This.
+
+  @retval EFI_SUCCESS           The Unicode string for the user readable name in
+                                the language specified by Language for the
+                                driver specified by This was returned in
+                                DriverName.
+
+  @retval EFI_INVALID_PARAMETER ControllerHandle is NULL.
+
+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid
+                                EFI_HANDLE.
+
+  @retval EFI_INVALID_PARAMETER Language is NULL.
+
+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+
+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently
+                                managing the controller specified by
+                                ControllerHandle and ChildHandle.
+
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support
+                                the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+AtapiScsiPassThruComponentNameGetControllerName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL                     *This,
+  IN  EFI_HANDLE                                      ControllerHandle,
+  IN  EFI_HANDLE                                      ChildHandle        OPTIONAL,
+  IN  CHAR8                                           *Language,
+  OUT CHAR16                                          **ControllerName
+  )
+{
+  return EFI_UNSUPPORTED;
+}
diff --git a/Drivers/OptionRomPkg/AtapiPassThruDxe/DriverSupportedEfiVersion.c b/Drivers/OptionRomPkg/AtapiPassThruDxe/DriverSupportedEfiVersion.c
new file mode 100644
index 0000000000..84a9badad2
--- /dev/null
+++ b/Drivers/OptionRomPkg/AtapiPassThruDxe/DriverSupportedEfiVersion.c
@@ -0,0 +1,14 @@
+/** @file
+  Copyright (c) 2007, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  Module Name:  DriverSupportEfiVersion.c
+
+**/
+#include "AtapiPassThru.h"
+
+EFI_DRIVER_SUPPORTED_EFI_VERSION_PROTOCOL gAtapiScsiPassThruDriverSupportedEfiVersion = {
+  sizeof (EFI_DRIVER_SUPPORTED_EFI_VERSION_PROTOCOL), // Size of Protocol structure.
+  0                                                   // Version number to be filled at start up.
+};
+
diff --git a/Drivers/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/CompatibleDevices.txt b/Drivers/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/CompatibleDevices.txt
new file mode 100644
index 0000000000..1ec1cce0d1
--- /dev/null
+++ b/Drivers/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/CompatibleDevices.txt
@@ -0,0 +1,5 @@
+The following devices have been confirmed to work with the USB Serial Driver:
+
+Brand        Model Name        Product Name          Vendor ID    Device ID
+Gearmo       USA_FTDI-36       USB to RS-232         0x0403       0x6001
+Sabrent      CB-FTDI                                 0x0403       0x6001
\ No newline at end of file
diff --git a/Drivers/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/ComponentName.c b/Drivers/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/ComponentName.c
new file mode 100644
index 0000000000..d15abf5090
--- /dev/null
+++ b/Drivers/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/ComponentName.c
@@ -0,0 +1,218 @@
+/** @file
+  UEFI Component Name(2) protocol implementation for USB Serial driver.
+
+Copyright (c) 2004 - 2013, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "FtdiUsbSerialDriver.h"
+
+//
+// EFI Component Name Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gUsbSerialComponentName = {
+  (EFI_COMPONENT_NAME_GET_DRIVER_NAME) UsbSerialComponentNameGetDriverName,
+  (EFI_COMPONENT_NAME_GET_CONTROLLER_NAME) UsbSerialComponentNameGetControllerName,
+  "eng"
+};
+
+//
+// EFI Component Name 2 Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gUsbSerialComponentName2 = {
+  UsbSerialComponentNameGetDriverName,
+  UsbSerialComponentNameGetControllerName,
+  "en"
+};
+
+//
+// Driver name string table
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mUsbSerialDriverNameTable[] = {
+  { "eng;en", L"FTDI-232 USB Serial Driver" },
+  { NULL , NULL }
+};
+
+/**
+  Retrieves a Unicode string that is the user readable name of the driver.
+
+  This function retrieves the user readable name of a driver in the form of a
+  Unicode string. If the driver specified by This has a user readable name in
+  the language specified by Language, then a pointer to the driver name is
+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+  by This does not support the language specified by Language,
+  then EFI_UNSUPPORTED is returned.
+
+  @param  This                  A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+                                EFI_COMPONENT_NAME_PROTOCOL instance.
+  @param  Language              A pointer to a Null-terminated ASCII string
+                                array indicating the language. This is the
+                                language of the driver name that the caller is
+                                requesting, and it must match one of the
+                                languages specified in SupportedLanguages. The
+                                number of languages supported by a driver is up
+                                to the driver writer. Language is specified
+                                in RFC 4646 or ISO 639-2 language code format.
+  @param  DriverName            A pointer to the Unicode string to return.
+                                This Unicode string is the name of the
+                                driver specified by This in the language
+                                specified by Language.
+
+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by
+                                This and the language specified by Language was
+                                returned in DriverName.
+  @retval EFI_INVALID_PARAMETER Language is NULL.
+  @retval EFI_INVALID_PARAMETER DriverName is NULL.
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support
+                                the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+UsbSerialComponentNameGetDriverName (
+  IN  EFI_COMPONENT_NAME2_PROTOCOL  *This,
+  IN  CHAR8                         *Language,
+  OUT CHAR16                        **DriverName
+  )
+{
+  return LookupUnicodeString2 (
+           Language,
+           This->SupportedLanguages,
+           mUsbSerialDriverNameTable,
+           DriverName,
+           (BOOLEAN)(This == &gUsbSerialComponentName2)
+           );
+}
+
+
+/**
+  Retrieves a Unicode string that is the user readable name of the controller
+  that is being managed by a driver.
+
+  This function retrieves the user readable name of the controller specified by
+  ControllerHandle and ChildHandle in the form of a Unicode string. If the
+  driver specified by This has a user readable name in the language specified by
+  Language, then a pointer to the controller name is returned in ControllerName,
+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently
+  managing the controller specified by ControllerHandle and ChildHandle,
+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not
+  support the language specified by Language, then EFI_UNSUPPORTED is returned.
+
+  @param  This                  A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+                                EFI_COMPONENT_NAME_PROTOCOL instance.
+  @param  ControllerHandle      The handle of a controller that the driver
+                                specified by This is managing.  This handle
+                                specifies the controller whose name is to be
+                                returned.
+  @param  ChildHandle           The handle of the child controller to retrieve
+                                the name of.  This is an optional parameter that
+                                may be NULL.  It will be NULL for device
+                                drivers.  It will also be NULL for a bus drivers
+                                that wish to retrieve the name of the bus
+                                controller.  It will not be NULL for a bus
+                                driver that wishes to retrieve the name of a
+                                child controller.
+  @param  Language              A pointer to a Null-terminated ASCII string
+                                array indicating the language.  This is the
+                                language of the driver name that the caller is
+                                requesting, and it must match one of the
+                                languages specified in SupportedLanguages. The
+                                number of languages supported by a driver is up
+                                to the driver writer. Language is specified in
+                                RFC 4646 or ISO 639-2 language code format.
+  @param  ControllerName        A pointer to the Unicode string to return.
+                                This Unicode string is the name of the
+                                controller specified by ControllerHandle and
+                                ChildHandle in the language specified by
+                                Language from the point of view of the driver
+                                specified by This.
+
+  @retval EFI_SUCCESS           The Unicode string for the user readable name in
+                                the language specified by Language for the
+                                driver specified by This was returned in
+                                DriverName.
+  @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.
+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid
+                                EFI_HANDLE.
+  @retval EFI_INVALID_PARAMETER Language is NULL.
+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently
+                                managing the controller specified by
+                                ControllerHandle and ChildHandle.
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support
+                                the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+UsbSerialComponentNameGetControllerName (
+  IN  EFI_COMPONENT_NAME2_PROTOCOL  *This,
+  IN  EFI_HANDLE                    ControllerHandle,
+  IN  EFI_HANDLE                    ChildHandle      OPTIONAL,
+  IN  CHAR8                         *Language,
+  OUT CHAR16                        **ControllerName
+  )
+{
+  EFI_STATUS              Status;
+  USB_SER_DEV             *UsbSerDev;
+  EFI_SERIAL_IO_PROTOCOL  *SerialIo;
+  EFI_USB_IO_PROTOCOL     *UsbIoProtocol;
+  
+  //
+  // This is a device driver, so ChildHandle must be NULL.
+  //
+  if (ChildHandle != NULL) {
+    return EFI_UNSUPPORTED;
+  }
+
+  //
+  // Check Controller's handle
+  //
+  Status = gBS->OpenProtocol (
+                  ControllerHandle,
+                  &gEfiUsbIoProtocolGuid,
+                  (VOID **) &UsbIoProtocol,
+                  gUsbSerialDriverBinding.DriverBindingHandle,
+                  ControllerHandle,
+                  EFI_OPEN_PROTOCOL_BY_DRIVER
+                  );
+  if (!EFI_ERROR (Status)) {
+    gBS->CloseProtocol (
+           ControllerHandle,
+           &gEfiUsbIoProtocolGuid,
+           gUsbSerialDriverBinding.DriverBindingHandle,
+           ControllerHandle
+           );
+
+    return EFI_UNSUPPORTED;
+  }
+
+  if (Status != EFI_ALREADY_STARTED) {
+    return EFI_UNSUPPORTED;
+  }
+  //
+  // Get the device context
+  //
+  Status = gBS->OpenProtocol (
+                  ControllerHandle,
+                  &gEfiSerialIoProtocolGuid,
+                  (VOID **) &SerialIo,
+                  gUsbSerialDriverBinding.DriverBindingHandle,
+                  ControllerHandle,
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
+                  );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  UsbSerDev = USB_SER_DEV_FROM_THIS (SerialIo);
+
+  return LookupUnicodeString2 (
+           Language,
+           This->SupportedLanguages,
+           UsbSerDev->ControllerNameTable,
+           ControllerName,
+           (BOOLEAN)(This == &gUsbSerialComponentName2)
+           );
+}
diff --git a/Drivers/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/FtdiUsbSerialDriver.c b/Drivers/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/FtdiUsbSerialDriver.c
new file mode 100644
index 0000000000..ac09fae014
--- /dev/null
+++ b/Drivers/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/FtdiUsbSerialDriver.c
@@ -0,0 +1,2580 @@
+/** @file
+  USB Serial Driver that manages USB to Serial and produces Serial IO Protocol.
+
+Copyright (c) 2004 - 2013, Intel Corporation. All rights reserved.
+Portions Copyright 2012 Ashley DeSimone
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+//
+
+// Tested with VEND_ID 0x0403, DEVICE_ID 0x6001
+//
+// Driver starts the device with the following values:
+// 115200, No parity, 8 data bits, 1 stop bit, No Flow control
+//
+
+#include "FtdiUsbSerialDriver.h"
+
+//
+// Table of supported devices. This is the device information that this
+// driver was developed with. Add other FTDI devices as needed.
+//
+USB_DEVICE gUSBDeviceList[] = {
+  {VID_FTDI, DID_FTDI_FT232},
+  {0,0}
+};
+
+//
+// USB Serial Driver Global Variables
+//
+EFI_DRIVER_BINDING_PROTOCOL  gUsbSerialDriverBinding = {
+  UsbSerialDriverBindingSupported,
+  UsbSerialDriverBindingStart,
+  UsbSerialDriverBindingStop,
+  0xa,
+  NULL,
+  NULL
+};
+
+//
+// Table with the nearest power of 2 for the numbers 0-15
+//
+UINT8 gRoundedPowersOf2[16] = { 0, 2, 2, 4, 4, 4, 8, 8, 8, 8, 8, 8, 16, 16, 16, 16 };
+
+/**
+  Check to see if the device path node is the Flow control node
+
+  @param[in] FlowControl    The device path node to be checked
+
+  @retval    TRUE           It is the flow control node
+  @retval    FALSE          It is not the flow control node
+
+**/
+BOOLEAN
+IsUartFlowControlNode (
+  IN UART_FLOW_CONTROL_DEVICE_PATH *FlowControl
+  )
+{
+  return (BOOLEAN) (
+           (DevicePathType (FlowControl) == MESSAGING_DEVICE_PATH) &&
+           (DevicePathSubType (FlowControl) == MSG_VENDOR_DP) &&
+           (CompareGuid (&FlowControl->Guid, &gEfiUartDevicePathGuid))
+           );
+}
+
+/**
+  Checks the device path to see if it contains flow control.
+
+  @param[in] DevicePath    The device path to be checked
+
+  @retval    TRUE          It contains flow control
+  @retval    FALSE         It does not contain flow control
+
+**/
+BOOLEAN
+ContainsFlowControl (
+  IN EFI_DEVICE_PATH_PROTOCOL  *DevicePath
+  )
+{
+  while (!IsDevicePathEnd (DevicePath)) {
+    if (IsUartFlowControlNode ((UART_FLOW_CONTROL_DEVICE_PATH *) DevicePath)) {
+      return TRUE;
+    }
+    DevicePath = NextDevicePathNode (DevicePath);
+  }
+  return FALSE;
+}
+
+/**
+  Transfer the data between the device and host.
+
+  This function transfers the data between the device and host.
+  BOT transfer is composed of three phases: Command, Data, and Status.
+  This is the Data phase.
+
+  @param  UsbBot[in]                     The USB BOT device
+  @param  DataDir[in]                    The direction of the data
+  @param  Data[in, out]                  The buffer to hold data
+  @param  TransLen[in, out]              The expected length of the data
+  @param  Timeout[in]                    The time to wait the command to complete
+
+  @retval EFI_SUCCESS                    The data is transferred
+  @retval EFI_SUCCESS                    No data to transfer
+  @retval EFI_NOT_READY                  The device return NAK to the transfer
+  @retval Others                         Failed to transfer data
+
+**/
+EFI_STATUS
+UsbSerialDataTransfer (
+  IN USB_SER_DEV             *UsbBot,
+  IN EFI_USB_DATA_DIRECTION  DataDir,
+  IN OUT VOID                *Data,
+  IN OUT UINTN               *TransLen,
+  IN UINT32                  Timeout
+  )
+{
+  EFI_USB_ENDPOINT_DESCRIPTOR  *Endpoint;
+  EFI_STATUS                   Status;
+  UINT32                       Result;
+
+  //
+  // If no data to transfer, just return EFI_SUCCESS.
+  //
+  if ((DataDir == EfiUsbNoData) || (*TransLen == 0)) {
+    return EFI_SUCCESS;
+  }
+
+  //
+  // Select the endpoint then issue the transfer
+  //
+  if (DataDir == EfiUsbDataIn) {
+    Endpoint = &UsbBot->InEndpointDescriptor;
+  } else {
+    Endpoint = &UsbBot->OutEndpointDescriptor;
+  }
+
+  Result = 0;
+  Status = UsbBot->UsbIo->UsbBulkTransfer (
+                            UsbBot->UsbIo,
+                            Endpoint->EndpointAddress,
+                            Data,
+                            TransLen,
+                            Timeout,
+                            &Result
+                            );
+  if (EFI_ERROR (Status)) {
+    if (USB_IS_ERROR (Result, EFI_USB_ERR_NAK)) {
+      Status = EFI_NOT_READY;
+    } else {
+      UsbBot->Shutdown = TRUE; // Fixes infinite loop in older EFI
+    }
+    return Status;
+  }
+  return Status;
+}
+
+/**
+  Sets the status values of the Usb Serial Device.
+
+  @param  UsbSerialDevice[in]  Handle to the Usb Serial Device to set the status
+                               for
+  @param  StatusBuffer[in]     Buffer holding the status values
+
+  @retval EFI_SUCCESS          The status values were read and set correctly
+
+**/
+EFI_STATUS
+EFIAPI
+SetStatusInternal (
+  IN USB_SER_DEV  *UsbSerialDevice,
+  IN UINT8        *StatusBuffer
+  )
+{
+  UINT8  Msr;
+
+  Msr = (StatusBuffer[0] & MSR_MASK);
+
+  //
+  // set the Status values to disabled
+  //
+  UsbSerialDevice->StatusValues.CtsState = FALSE;
+  UsbSerialDevice->StatusValues.DsrState = FALSE;
+  UsbSerialDevice->StatusValues.RiState  = FALSE;
+  UsbSerialDevice->StatusValues.SdState  = FALSE;
+
+  //
+  // Check the values from the status buffer and set the appropriate status
+  // values to enabled
+  //
+  if ((Msr & CTS_MASK) == CTS_MASK) {
+    UsbSerialDevice->StatusValues.CtsState = TRUE;
+  }
+  if ((Msr & DSR_MASK) == DSR_MASK) {
+    UsbSerialDevice->StatusValues.DsrState = TRUE;
+  }
+  if ((Msr & RI_MASK) == RI_MASK) {
+    UsbSerialDevice->StatusValues.RiState = TRUE;
+  }
+  if ((Msr & SD_MASK) == SD_MASK) {
+    UsbSerialDevice->StatusValues.SdState = TRUE;
+  }
+  return EFI_SUCCESS;
+}
+
+/**
+  Initiates a read operation on the Usb Serial Device.
+
+  @param  UsbSerialDevice[in]        Handle to the USB device to read
+  @param  BufferSize[in, out]        On input, the size of the Buffer. On output,
+                                     the amount of data returned in Buffer.
+                                     Setting this to zero will initiate a read
+                                     and store all data returned in the internal
+                                     buffer.
+  @param  Buffer [out]               The buffer to return the data into.
+
+  @retval EFI_SUCCESS                The data was read.
+  @retval EFI_DEVICE_ERROR           The device reported an error.
+  @retval EFI_TIMEOUT                The data write was stopped due to a timeout.
+
+**/
+EFI_STATUS
+EFIAPI
+ReadDataFromUsb (
+  IN USB_SER_DEV  *UsbSerialDevice,
+  IN OUT UINTN    *BufferSize,
+  OUT VOID        *Buffer
+  )
+{
+  EFI_STATUS  Status;
+  UINTN       ReadBufferSize;
+  UINT8       *ReadBuffer;
+  UINTN       Index;
+  EFI_TPL     Tpl;
+  UINT8       StatusBuffer[2]; // buffer to store the status bytes
+
+  ReadBufferSize = 512;
+  ReadBuffer     = &(UsbSerialDevice->ReadBuffer[0]);
+
+  if (UsbSerialDevice->Shutdown) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  Tpl = gBS->RaiseTPL (TPL_NOTIFY);
+
+  Status = UsbSerialDataTransfer (
+             UsbSerialDevice,
+             EfiUsbDataIn,
+             ReadBuffer,
+             &ReadBufferSize,
+             FTDI_TIMEOUT*2  //Padded because timers won't be exactly aligned
+             );
+  if (EFI_ERROR (Status)) {
+    gBS->RestoreTPL (Tpl);
+    if (Status == EFI_TIMEOUT) {
+      return EFI_TIMEOUT;
+    } else {
+      return EFI_DEVICE_ERROR;
+    }
+  }
+
+  //
+  // Store the status bytes in the status buffer
+  //
+  for (Index = 0; Index < 2; Index++) {//only the first 2 bytes are status bytes
+    StatusBuffer[Index] = ReadBuffer[Index];
+  }
+  //
+  // update the statusvalue field of the usbserialdevice
+  //
+  Status = SetStatusInternal (UsbSerialDevice, StatusBuffer);
+  if (Status != EFI_SUCCESS) {
+  }
+
+  //
+  // Store the read data in the read buffer, start at 2 to ignore status bytes
+  //
+  for (Index = 2; Index < ReadBufferSize; Index++) {
+    if (((UsbSerialDevice->DataBufferTail + 1) % SW_FIFO_DEPTH) == UsbSerialDevice->DataBufferHead) {
+      break;
+    }
+    if (ReadBuffer[Index] == 0x00) {
+      //
+      // This is null, do not add
+      //
+    } else {
+      UsbSerialDevice->DataBuffer[UsbSerialDevice->DataBufferTail] = ReadBuffer[Index];
+      UsbSerialDevice->DataBufferTail = (UsbSerialDevice->DataBufferTail + 1) % SW_FIFO_DEPTH;
+    }
+  }
+
+  //
+  // Read characters out of the buffer to satisfy caller's request.
+  //
+  for (Index = 0; Index < *BufferSize; Index++) {
+    if (UsbSerialDevice->DataBufferHead == UsbSerialDevice->DataBufferTail) {
+      break;
+    }
+    //
+    // Still have characters in the buffer to return
+    //
+    ((UINT8 *)Buffer)[Index]        = UsbSerialDevice->DataBuffer[UsbSerialDevice->DataBufferHead];
+    UsbSerialDevice->DataBufferHead = (UsbSerialDevice->DataBufferHead + 1) % SW_FIFO_DEPTH;
+  }
+  //
+  // Return actual number of bytes returned.
+  //
+  *BufferSize = Index;
+  gBS->RestoreTPL (Tpl);
+  return EFI_SUCCESS;
+}
+
+/**
+  Sets the initial status values of the Usb Serial Device by reading the status
+  bytes from the device.
+
+  @param  UsbSerialDevice[in]  Handle to the Usb Serial Device that needs its
+                               initial status values set
+
+  @retval EFI_SUCCESS          The status bytes were read successfully and the
+                               initial status values were set correctly
+  @retval EFI_TIMEOUT          The read of the status bytes was stopped due to a
+                               timeout
+  @retval EFI_DEVICE_ERROR     The device reported an error during the read of
+                               the status bytes
+
+**/
+EFI_STATUS
+EFIAPI
+SetInitialStatus (
+  IN USB_SER_DEV          *UsbSerialDevice
+  )
+{
+  EFI_STATUS      Status;
+  UINTN           BufferSize;
+  EFI_TPL         Tpl;
+  UINT8           StatusBuffer[2];
+
+  Status          = EFI_UNSUPPORTED;
+  BufferSize      = sizeof (StatusBuffer);
+
+  if (UsbSerialDevice->Shutdown) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  Tpl = gBS->RaiseTPL (TPL_NOTIFY);
+
+  Status = UsbSerialDataTransfer (
+             UsbSerialDevice,
+             EfiUsbDataIn,
+             StatusBuffer,
+             &BufferSize,
+             40    //Slightly more than 2x the FTDI polling frequency to make sure that data will be returned
+             );
+
+  Status = SetStatusInternal (UsbSerialDevice, StatusBuffer);
+
+  gBS->RestoreTPL (Tpl);
+
+  return Status;
+}
+
+/**
+  UsbSerialDriverCheckInput.
+  attempts to read data in from the device periodically, stores any read data
+  and updates the control attributes.
+
+  @param  Event[in]
+  @param  Context[in]....The current instance of the USB serial device
+
+**/
+VOID
+EFIAPI
+UsbSerialDriverCheckInput (
+  IN  EFI_EVENT  Event,
+  IN  VOID       *Context
+  )
+{
+  UINTN        BufferSize;
+  USB_SER_DEV  *UsbSerialDevice;
+
+  UsbSerialDevice = (USB_SER_DEV*)Context;
+
+  if (UsbSerialDevice->DataBufferHead == UsbSerialDevice->DataBufferTail) {
+    //
+    // Data buffer is empty, try to read from device
+    //
+    BufferSize = 0;
+    ReadDataFromUsb (UsbSerialDevice, &BufferSize, NULL);
+    if (UsbSerialDevice->DataBufferHead == UsbSerialDevice->DataBufferTail) {
+      //
+      // Data buffer still has no data, set the EFI_SERIAL_INPUT_BUFFER_EMPTY
+      // flag
+      //
+      UsbSerialDevice->ControlBits |= EFI_SERIAL_INPUT_BUFFER_EMPTY;
+    } else {
+      //
+      // Read has returned some data, clear the EFI_SERIAL_INPUT_BUFFER_EMPTY
+      // flag
+      //
+      UsbSerialDevice->ControlBits &= ~(EFI_SERIAL_INPUT_BUFFER_EMPTY);
+    }
+  } else {
+    //
+    // Data buffer has data, no read attempt required
+    //
+    UsbSerialDevice->ControlBits &= ~(EFI_SERIAL_INPUT_BUFFER_EMPTY);
+  }
+}
+
+/**
+  Encodes the baud rate into the format expected by the Ftdi device.
+
+  @param  BaudRate[in]                The baudrate to be set on the device
+  @param  EncodedBaudRate[out]        The baud rate encoded in the format
+                                      expected by the Ftdi device
+
+  @return EFI_SUCCESS                 Baudrate encoding was calculated
+                                      successfully
+  @return EFI_INVALID_PARAMETER       An invalid value of BaudRate was received
+
+**/
+EFI_STATUS
+EFIAPI
+EncodeBaudRateForFtdi (
+  IN  UINT64  BaudRate,
+  OUT UINT16  *EncodedBaudRate
+  )
+{
+  UINT32 Divisor;
+  UINT32 AdjustedFrequency;
+  UINT16 Result;
+
+  //
+  // Check to make sure we won't get an integer overflow
+  //
+  if ((BaudRate < 178) || ( BaudRate > ((FTDI_UART_FREQUENCY * 100) / 97))) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Baud Rates of 2000000 and 3000000 are special cases
+  //
+  if ((BaudRate >= FTDI_SPECIAL_CASE_300_MIN) && (BaudRate <= FTDI_SPECIAL_CASE_300_MAX)) {
+    *EncodedBaudRate = 0;
+    return EFI_SUCCESS;
+  }
+  if ((BaudRate >= FTDI_SPECIAL_CASE_200_MIN) && (BaudRate <= FTDI_SPECIAL_CASE_200_MAX)) {
+    *EncodedBaudRate = 1;
+    return EFI_SUCCESS;
+  }
+
+  //
+  // Compute divisor
+  //
+  Divisor = (FTDI_UART_FREQUENCY << 4) / (UINT32)BaudRate;
+
+  //
+  // Round the last 4 bits to the nearest power of 2
+  //
+  Divisor = (Divisor & ~(0xF)) + (gRoundedPowersOf2[Divisor & 0xF]);
+
+  //
+  // Check to make sure computed divisor is within 
+  // the min and max that FTDI controller will accept
+  //
+  if (Divisor < FTDI_MIN_DIVISOR) {
+    Divisor = FTDI_MIN_DIVISOR;
+  } else if (Divisor > FTDI_MAX_DIVISOR) {
+    Divisor = FTDI_MAX_DIVISOR;
+  }
+
+  //
+  // Check to make sure the frequency that the FTDI chip will need to
+  // generate to attain the requested Baud Rate is within 3% of the
+  // 3MHz clock frequency that the FTDI chip runs at.
+  //
+  // (3MHz * 1600) / 103 = 46601941
+  // (3MHz * 1600) / 97  = 49484536
+  //
+  AdjustedFrequency = (((UINT32)BaudRate) * Divisor);
+  if ((AdjustedFrequency < FTDI_MIN_FREQUENCY) || (AdjustedFrequency > FTDI_MAX_FREQUENCY)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Encode the Divisor into the format FTDI expects
+  //
+  Result = (UINT16)(Divisor >> 4);
+  if ((Divisor & 0x8) != 0) {
+    Result |= 0x4000;
+  } else if ((Divisor & 0x4) != 0) {
+    Result |= 0x8000;
+  } else if ((Divisor & 0x2) != 0) {
+    Result |= 0xC000;
+  }
+
+  *EncodedBaudRate = Result;
+  return EFI_SUCCESS;
+}
+
+/**
+  Uses USB I/O to check whether the device is a USB Serial device.
+
+  @param  UsbIo[in]    Pointer to a USB I/O protocol instance.
+
+  @retval TRUE         Device is a USB Serial device.
+  @retval FALSE        Device is a not USB Serial device.
+
+**/
+BOOLEAN
+IsUsbSerial (
+  IN  EFI_USB_IO_PROTOCOL  *UsbIo
+  )
+{
+  EFI_STATUS                 Status;
+  EFI_USB_DEVICE_DESCRIPTOR  DeviceDescriptor;
+  CHAR16                     *StrMfg;
+  BOOLEAN                    Found;
+  UINT32                     Index;
+
+  //
+  // Get the default device descriptor
+  //
+  Status = UsbIo->UsbGetDeviceDescriptor (
+                    UsbIo,
+                    &DeviceDescriptor
+                    );
+  if (EFI_ERROR (Status)) {
+    return FALSE;
+  }
+
+  Found = FALSE;
+  Index = 0;
+  while (gUSBDeviceList[Index].VendorId != 0 &&
+         gUSBDeviceList[Index].DeviceId != 0 &&
+         !Found                                  ) {
+    if (DeviceDescriptor.IdProduct == gUSBDeviceList[Index].DeviceId &&
+        DeviceDescriptor.IdVendor  == gUSBDeviceList[Index].VendorId      ){
+        //
+        // Checks to see if a string descriptor can be pulled from the device in
+        // the selected language. If not False is returned indicating that this
+        // is not a Usb Serial Device that can be managegd by this driver
+        //
+        StrMfg = NULL;
+        Status = UsbIo->UsbGetStringDescriptor (
+                          UsbIo,
+                          USB_US_LANG_ID, // LANGID selector, should make this
+                                          // more robust to verify lang support
+                                          // for device
+                          DeviceDescriptor.StrManufacturer,
+                          &StrMfg
+                          );
+        if (StrMfg != NULL) {
+          FreePool (StrMfg);
+        }
+        if (EFI_ERROR (Status)) {
+          return FALSE;
+        }
+        return TRUE;
+    }
+    Index++;
+  }
+  return FALSE;
+}
+
+/**
+  Internal function that sets the Data Bits, Stop Bits and Parity values on the
+  Usb Serial Device with a single usb control transfer.
+
+  @param  UsbIo[in]                  Usb Io Protocol instance pointer
+  @param  DataBits[in]               The data bits value to be set on the Usb
+                                     Serial Device
+  @param  Parity[in]                 The parity type that will be set on the Usb
+                                     Serial Device
+  @param  StopBits[in]               The stop bits type that will be set on the
+                                     Usb Serial Device
+  @param  LastSettings[in]           A pointer to the Usb Serial Device's
+                                     PREVIOUS_ATTRIBUTES item
+
+  @retval EFI_SUCCESS                The data items were correctly set on the
+                                     USB Serial Device
+  @retval EFI_INVALID_PARAMETER      An invalid data parameter or an invalid
+                                     combination or parameters was used
+  @retval EFI_DEVICE_ERROR           The device is not functioning correctly and
+                                     the data values were unable to be set
+
+**/
+EFI_STATUS
+EFIAPI
+SetDataInternal (
+  IN EFI_USB_IO_PROTOCOL  *UsbIo,
+  IN UINT8                DataBits,
+  IN EFI_PARITY_TYPE      Parity,
+  IN EFI_STOP_BITS_TYPE   StopBits,
+  IN PREVIOUS_ATTRIBUTES  *LastSettings
+  )
+{
+  EFI_STATUS              Status;
+  EFI_USB_DEVICE_REQUEST  DevReq;
+  UINT32                  ReturnValue;
+  UINT8                   ConfigurationValue;
+
+  //
+  // Since data bits settings of 6,7,8 cannot be set with a stop bits setting of
+  // 1.5 check to see if this happens when the values of last settings are used
+  //
+  if ((DataBits == 0) && (StopBits == OneFiveStopBits)) {
+    if ((LastSettings->DataBits == 6) || (LastSettings->DataBits == 7) || (LastSettings->DataBits == 8)) {
+      return EFI_INVALID_PARAMETER;
+    }
+  } else if ((StopBits == DefaultStopBits) && ((DataBits == 6) || (DataBits == 7) || (DataBits == 8))) {
+    if (LastSettings->StopBits == OneFiveStopBits) {
+      return EFI_INVALID_PARAMETER;
+    }
+  } else if ((DataBits == 0) && (StopBits == DefaultStopBits)) {
+    if (LastSettings->StopBits == OneFiveStopBits) {
+      if ((LastSettings->DataBits == 6) || (LastSettings->DataBits == 7) || (LastSettings->DataBits == 8)) {
+        return EFI_INVALID_PARAMETER;
+      }
+    }
+  }
+
+  //
+  // set the DevReq.Value for the usb control transfer to the correct value
+  // based on the seleceted number of data bits if there is an invalid number of
+  // data bits requested return EFI_INVALID_PARAMETER
+  //
+  if (((DataBits < 5 ) || (DataBits > 8)) && (DataBits != 0)) {
+    return EFI_INVALID_PARAMETER;
+  }
+  if (DataBits == 0) {
+    //
+    // use the value of LastDataBits
+    //
+    DevReq.Value = SET_DATA_BITS (LastSettings->DataBits);
+  } else {
+    //
+    // use the value of DataBits
+    //
+    DevReq.Value = SET_DATA_BITS (DataBits);
+  }
+
+  //
+  // Set Parity
+  //
+  if (Parity == DefaultParity) {
+    Parity = LastSettings->Parity;
+  }
+
+  if (Parity == NoParity) {
+    DevReq.Value |= SET_PARITY_NONE;
+  } else if (Parity == EvenParity) {
+    DevReq.Value |= SET_PARITY_EVEN;
+  } else if (Parity == OddParity){
+    DevReq.Value |= SET_PARITY_ODD;
+  } else if (Parity == MarkParity) {
+    DevReq.Value |= SET_PARITY_MARK;
+  } else if (Parity == SpaceParity) {
+    DevReq.Value |= SET_PARITY_SPACE;
+  }
+
+  //
+  // Set Stop Bits
+  //
+  if (StopBits == DefaultStopBits) {
+    StopBits = LastSettings->StopBits;
+  }
+
+  if (StopBits == OneStopBit) {
+    DevReq.Value |= SET_STOP_BITS_1;
+  } else if (StopBits == OneFiveStopBits) {
+    DevReq.Value |= SET_STOP_BITS_15;
+  } else if (StopBits == TwoStopBits) {
+    DevReq.Value |= SET_STOP_BITS_2;
+  }
+
+  //
+  // set the rest of the DevReq parameters and perform the usb control transfer
+  // to set the data bits on the device
+  //
+  DevReq.Request     = FTDI_COMMAND_SET_DATA;
+  DevReq.RequestType = USB_REQ_TYPE_VENDOR;
+  DevReq.Index       = FTDI_PORT_IDENTIFIER;
+  DevReq.Length      = 0; // indicates that there is no data phase in this request
+
+  Status = UsbIo->UsbControlTransfer (
+                    UsbIo,
+                    &DevReq,
+                    EfiUsbDataOut,
+                    WDR_SHORT_TIMEOUT,
+                    &ConfigurationValue,
+                    1,
+                    &ReturnValue
+                    );
+  if (EFI_ERROR (Status)) {
+    goto StatusError;
+  }
+  return Status;
+
+StatusError:
+  if ((Status != EFI_INVALID_PARAMETER) || (Status != EFI_DEVICE_ERROR)) {
+    return EFI_DEVICE_ERROR;
+  } else {
+    return Status;
+  }
+}
+
+/**
+  Internal function that sets the baudrate on the Usb Serial Device.
+
+  @param  UsbIo[in]                  Usb Io Protocol instance pointer
+  @param  BaudRate[in]               The baudrate value to be set on the device.
+                                     If this value is 0 the value of LastBaudRate
+                                     will be used instead
+  @param  LastBaudRate[in]           The baud rate value that was previously set
+                                     on the Usb Serial Device
+
+  @retval EFI_SUCCESS                The baudrate was set succesfully
+  @retval EFI_INVALID_PARAMETER      An invalid baudrate was used
+  @retval EFI_DEVICE_ERROR           The device is not functioning correctly and
+                                     the baudrate was unable to be set
+
+**/
+EFI_STATUS
+EFIAPI
+SetBaudRateInternal (
+  IN EFI_USB_IO_PROTOCOL  *UsbIo,
+  IN UINT64               BaudRate,
+  IN UINT64               LastBaudRate
+  )
+{
+  EFI_STATUS              Status;
+  EFI_USB_DEVICE_REQUEST  DevReq;
+  UINT32                  ReturnValue;
+  UINT8                   ConfigurationValue;
+  UINT16                  EncodedBaudRate;
+  EFI_TPL                 Tpl;
+
+  Tpl    = gBS->RaiseTPL(TPL_NOTIFY);
+
+  //
+  // set the value of DevReq.Value based on the value of BaudRate
+  // if 0 is selected as baud rate use the value of LastBaudRate
+  //
+  if (BaudRate == 0) {
+    Status = EncodeBaudRateForFtdi (LastBaudRate, &EncodedBaudRate);
+    if (EFI_ERROR (Status)) {
+      gBS->RestoreTPL (Tpl);
+      //
+      // EncodeBaudRateForFtdi returns EFI_INVALID_PARAMETER when not
+      // succesfull
+      //
+      return Status;
+    }
+    DevReq.Value = EncodedBaudRate;
+  } else {
+    Status = EncodeBaudRateForFtdi (BaudRate, &EncodedBaudRate);
+    if (EFI_ERROR (Status)) {
+      gBS->RestoreTPL (Tpl);
+      //
+      // EncodeBaudRateForFtdi returns EFI_INVALID_PARAMETER when not
+      // successfull
+      //
+      return Status;
+    }
+    DevReq.Value = EncodedBaudRate;
+  }
+
+  //
+  // set the remaining parameters of DevReq and perform the usb control transfer
+  // to set the device
+  //
+  DevReq.Request     = FTDI_COMMAND_SET_BAUDRATE;
+  DevReq.RequestType = USB_REQ_TYPE_VENDOR;
+  DevReq.Index       = FTDI_PORT_IDENTIFIER;
+  DevReq.Length      = 0; // indicates that there is no data phase in this request
+
+  Status = UsbIo->UsbControlTransfer (
+                    UsbIo,
+                    &DevReq,
+                    EfiUsbDataOut,
+                    WDR_SHORT_TIMEOUT,
+                    &ConfigurationValue,
+                    1,
+                    &ReturnValue
+                    );
+  if (EFI_ERROR (Status)) {
+    goto StatusError;
+  }
+  gBS->RestoreTPL (Tpl);
+  return Status;
+
+StatusError:
+  gBS->RestoreTPL (Tpl);
+  if ((Status != EFI_INVALID_PARAMETER) || (Status != EFI_DEVICE_ERROR)) {
+    return EFI_DEVICE_ERROR;
+  } else {
+    return Status;
+  }
+}
+
+/**
+  Sets the baud rate, receive FIFO depth, transmit/receice time out, parity,
+  data bits, and stop bits on a serial device.
+
+  @param  UsbSerialDevice[in]  Pointer to the current instance of the USB Serial
+                               Device.
+  @param  BaudRate[in]         The requested baud rate. A BaudRate value of 0
+                               will use the device's default interface speed.
+  @param  ReveiveFifoDepth[in] The requested depth of the FIFO on the receive
+                               side of the serial interface. A ReceiveFifoDepth
+                               value of 0 will use the device's default FIFO
+                               depth.
+  @param  Timeout[in]          The requested time out for a single character in
+                               microseconds.This timeout applies to both the
+                               transmit and receive side of the interface.A
+                               Timeout value of 0 will use the device's default
+                               time out value.
+  @param  Parity[in]           The type of parity to use on this serial device.
+                               A Parity value of DefaultParity will use the
+                               device's default parity value.
+  @param  DataBits[in]         The number of data bits to use on the serial
+                               device. A DataBits value of 0 will use the
+                               device's default data bit setting.
+  @param  StopBits[in]         The number of stop bits to use on this serial
+                               device. A StopBits value of DefaultStopBits will
+                               use the device's default number of stop bits.
+
+  @retval EFI_SUCCESS          The attributes were set
+  @retval EFI_DEVICE_ERROR     The attributes were not able to be set
+
+**/
+EFI_STATUS
+EFIAPI
+SetAttributesInternal (
+  IN USB_SER_DEV         *UsbSerialDevice,
+  IN UINT64              BaudRate,
+  IN UINT32              ReceiveFifoDepth,
+  IN UINT32              Timeout,
+  IN EFI_PARITY_TYPE     Parity,
+  IN UINT8               DataBits,
+  IN EFI_STOP_BITS_TYPE  StopBits
+  )
+{
+  EFI_STATUS                Status;
+  EFI_TPL                   Tpl;
+  UART_DEVICE_PATH          *Uart;
+  EFI_DEVICE_PATH_PROTOCOL  *RemainingDevicePath;
+
+  Status = EFI_UNSUPPORTED;
+  Tpl    = gBS->RaiseTPL(TPL_NOTIFY);
+  Uart   = NULL;
+
+  //
+  // check for invalid combinations of parameters
+  //
+  if (((DataBits >= 6) && (DataBits <= 8)) && (StopBits == OneFiveStopBits)) {
+    return  EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // set data bits, parity and stop bits
+  //
+  Status = SetDataInternal (
+             UsbSerialDevice->UsbIo,
+             DataBits,
+             Parity,
+             StopBits,
+             &(UsbSerialDevice->LastSettings)
+             );
+  if (EFI_ERROR (Status)) {
+    goto StatusError;
+  }
+  //
+  // set baudrate
+  //
+  Status = SetBaudRateInternal (
+             UsbSerialDevice->UsbIo,
+             BaudRate,
+             UsbSerialDevice->LastSettings.BaudRate
+             );
+  if (EFI_ERROR (Status)){
+    goto StatusError;
+  }
+
+  //
+  // update the values of UsbSerialDevice->LastSettings and UsbSerialDevice->SerialIo.Mode
+  //
+  if (BaudRate == 0) {
+    UsbSerialDevice->LastSettings.BaudRate   = UsbSerialDevice->LastSettings.BaudRate;
+    UsbSerialDevice->SerialIo.Mode->BaudRate = UsbSerialDevice->LastSettings.BaudRate;
+  } else {
+    UsbSerialDevice->LastSettings.BaudRate   = BaudRate;
+    UsbSerialDevice->SerialIo.Mode->BaudRate = BaudRate;
+  }
+
+  UsbSerialDevice->LastSettings.Timeout          = FTDI_TIMEOUT;
+  UsbSerialDevice->LastSettings.ReceiveFifoDepth = FTDI_MAX_RECEIVE_FIFO_DEPTH;
+
+  if (Parity == DefaultParity) {
+    UsbSerialDevice->LastSettings.Parity   = UsbSerialDevice->LastSettings.Parity;
+    UsbSerialDevice->SerialIo.Mode->Parity = UsbSerialDevice->LastSettings.Parity;
+  } else {
+    UsbSerialDevice->LastSettings.Parity   = Parity;
+    UsbSerialDevice->SerialIo.Mode->Parity = Parity;
+  }
+  if (DataBits == 0) {
+    UsbSerialDevice->LastSettings.DataBits   = UsbSerialDevice->LastSettings.DataBits;
+    UsbSerialDevice->SerialIo.Mode->DataBits = UsbSerialDevice->LastSettings.DataBits;
+  } else {
+    UsbSerialDevice->LastSettings.DataBits   = DataBits;
+    UsbSerialDevice->SerialIo.Mode->DataBits = DataBits;
+  }
+  if (StopBits == DefaultStopBits) {
+    UsbSerialDevice->LastSettings.StopBits   = UsbSerialDevice->LastSettings.StopBits;
+    UsbSerialDevice->SerialIo.Mode->StopBits = UsbSerialDevice->LastSettings.StopBits;
+  } else {
+    UsbSerialDevice->LastSettings.StopBits   = StopBits;
+    UsbSerialDevice->SerialIo.Mode->StopBits = StopBits;
+  }
+
+  //
+  // See if the device path node has changed
+  //
+  if (UsbSerialDevice->UartDevicePath.BaudRate == BaudRate &&
+      UsbSerialDevice->UartDevicePath.DataBits == DataBits &&
+      UsbSerialDevice->UartDevicePath.StopBits == StopBits &&
+      UsbSerialDevice->UartDevicePath.Parity == Parity
+      ) {
+    gBS->RestoreTPL (Tpl);
+    return EFI_SUCCESS;
+  }
+
+  //
+  // Update the device path
+  //
+  UsbSerialDevice->UartDevicePath.BaudRate = BaudRate;
+  UsbSerialDevice->UartDevicePath.DataBits = DataBits;
+  UsbSerialDevice->UartDevicePath.StopBits = (UINT8) StopBits;
+  UsbSerialDevice->UartDevicePath.Parity   = (UINT8) Parity;
+
+  Status = EFI_SUCCESS;
+  if (UsbSerialDevice->ControllerHandle != NULL) {
+    RemainingDevicePath = UsbSerialDevice->DevicePath;
+    while (!IsDevicePathEnd (RemainingDevicePath)) {
+      Uart = (UART_DEVICE_PATH *) NextDevicePathNode (RemainingDevicePath);
+      if (Uart->Header.Type == MESSAGING_DEVICE_PATH &&
+          Uart->Header.SubType == MSG_UART_DP &&
+          sizeof (UART_DEVICE_PATH) == DevicePathNodeLength ((EFI_DEVICE_PATH *) Uart)) {
+        Uart->BaudRate = BaudRate;
+        Uart->DataBits = DataBits;
+        Uart->StopBits = (UINT8)StopBits;
+        Uart->Parity   = (UINT8) Parity;
+        break;
+        }
+        RemainingDevicePath = NextDevicePathNode (RemainingDevicePath);
+    }
+  }
+
+  gBS->RestoreTPL (Tpl);
+  return Status;
+
+StatusError:
+  gBS->RestoreTPL (Tpl);
+  if ((Status != EFI_INVALID_PARAMETER) || (Status != EFI_DEVICE_ERROR)) {
+    return EFI_DEVICE_ERROR;
+  } else {
+    return Status;
+  }
+}
+
+/**
+  Internal function that performs a Usb Control Transfer to set the flow control
+  on the Usb Serial Device.
+
+  @param  UsbIo[in]                  Usb Io Protocol instance pointer
+  @param  FlowControlEnable[in]      Data on the Enable/Disable status of Flow
+                                     Control on the Usb Serial Device
+
+  @retval EFI_SUCCESS                The flow control was set on the Usb Serial
+                                     device
+  @retval EFI_INVALID_PARAMETER      An invalid flow control value was used
+  @retval EFI_EFI_UNSUPPORTED        The operation is not supported
+  @retval EFI_DEVICE_ERROR           The device is not functioning correctly
+
+**/
+EFI_STATUS
+EFIAPI
+SetFlowControlInternal (
+  IN EFI_USB_IO_PROTOCOL  *UsbIo,
+  IN BOOLEAN              FlowControlEnable
+  )
+{
+  EFI_STATUS               Status;
+  EFI_USB_DEVICE_REQUEST   DevReq;
+  UINT32                   ReturnValue;
+  UINT8                    ConfigurationValue;
+
+  //
+  // set DevReq.Value based on the value of FlowControlEnable
+  //
+  if (!FlowControlEnable) {
+    DevReq.Value = NO_FLOW_CTRL;
+  }
+  if (FlowControlEnable) {
+    DevReq.Value = XON_XOFF_CTRL;
+  }
+  //
+  // set the remaining DevReq parameters and perform the usb control transfer to
+  // set the flow control on the device
+  //
+  DevReq.Request      = FTDI_COMMAND_SET_FLOW_CTRL;
+  DevReq.RequestType  = USB_REQ_TYPE_VENDOR;
+  DevReq.Index        = FTDI_PORT_IDENTIFIER;
+  DevReq.Length       = 0; // indicates that this transfer has no data phase
+  Status              = UsbIo->UsbControlTransfer (
+                                 UsbIo,
+                                 &DevReq,
+                                 EfiUsbDataOut,
+                                 WDR_TIMEOUT,
+                                 &ConfigurationValue,
+                                 1,
+                                 &ReturnValue
+                                 );
+  if (EFI_ERROR (Status)) {
+    goto StatusError;
+  }
+
+  return Status;
+
+StatusError:
+  if ((Status != EFI_INVALID_PARAMETER) ||
+      (Status != EFI_DEVICE_ERROR)      ||
+      (Status != EFI_UNSUPPORTED)          ) {
+    return EFI_DEVICE_ERROR;
+  } else {
+    return Status;
+  }
+}
+
+/**
+  Internal function that performs a Usb Control Transfer to set the Dtr value on
+  the Usb Serial Device.
+
+  @param  UsbIo[in]                  Usb Io Protocol instance pointer
+  @param  DtrEnable[in]              Data on the Enable/Disable status of the
+                                     Dtr for the Usb Serial Device
+
+  @retval EFI_SUCCESS                The Dtr value was set on the Usb Serial
+                                     Device
+  @retval EFI_INVALID_PARAMETER      An invalid Dtr value was used
+  @retval EFI_UNSUPPORTED            The operation is not supported
+  @retval EFI_DEVICE_ERROR           The device is not functioning correctly
+
+**/
+EFI_STATUS
+EFIAPI
+SetDtrInternal (
+  IN EFI_USB_IO_PROTOCOL  *UsbIo,
+  IN BOOLEAN              DtrEnable
+  )
+{
+  EFI_STATUS              Status;
+  EFI_USB_DEVICE_REQUEST  DevReq;
+  UINT32                  ReturnValue;
+  UINT8                   ConfigurationValue;
+
+  //
+  // set the value of DevReq.Value based on the value of DtrEnable
+  //
+  if (!DtrEnable) {
+    DevReq.Value = SET_DTR_LOW;
+  }
+  if (DtrEnable) {
+    DevReq.Value = SET_DTR_HIGH;
+  }
+  //
+  // set the remaining attributes of DevReq and perform the usb control transfer
+  // to set the device
+  //
+  DevReq.Request      = FTDI_COMMAND_MODEM_CTRL;
+  DevReq.RequestType  = USB_REQ_TYPE_VENDOR;
+  DevReq.Index        = FTDI_PORT_IDENTIFIER;
+  DevReq.Length       = 0; // indicates that there is no data phase in this transfer
+
+  Status = UsbIo->UsbControlTransfer (
+                    UsbIo,
+                    &DevReq,
+                    EfiUsbDataOut,
+                    WDR_TIMEOUT,
+                    &ConfigurationValue,
+                    1,
+                    &ReturnValue
+                    );
+  if (EFI_ERROR (Status)) {
+    goto StatusError;
+  }
+  return Status;
+
+StatusError:
+  if ((Status != EFI_INVALID_PARAMETER) ||
+      (Status != EFI_DEVICE_ERROR)      ||
+      (Status != EFI_UNSUPPORTED)          ) {
+    return EFI_DEVICE_ERROR;
+  } else {
+    return Status;
+  }
+}
+
+/**
+  Internal function that performs a Usb Control Transfer to set the Dtr value on
+  the Usb Serial Device.
+  
+  @param  UsbIo[in]                  Usb Io Protocol instance pointer
+  @param  RtsEnable[in]              Data on the Enable/Disable status of the
+                                     Rts for the Usb Serial Device
+
+  @retval EFI_SUCCESS                The Rts value was set on the Usb Serial
+                                     Device
+  @retval EFI_INVALID_PARAMETER      An invalid Rts value was used
+  @retval EFI_UNSUPPORTED            The operation is not supported
+  @retval EFI_DEVICE_ERROR           The device is not functioning correctly
+
+**/
+EFI_STATUS
+EFIAPI
+SetRtsInternal (
+  IN EFI_USB_IO_PROTOCOL  *UsbIo,
+  IN BOOLEAN              RtsEnable
+  )
+{
+  EFI_STATUS              Status;
+  EFI_USB_DEVICE_REQUEST  DevReq;
+  UINT32                  ReturnValue;
+  UINT8                   ConfigurationValue;
+
+  //
+  // set DevReq.Value based on the value of RtsEnable
+  //
+  if (!RtsEnable) {
+    DevReq.Value = SET_RTS_LOW;
+  }
+  if (RtsEnable) {
+    DevReq.Value = SET_RTS_HIGH;
+  }
+
+  //
+  // set the remaining parameters of DevReq and perform the usb control transfer
+  // to set the values on the device
+  //
+  DevReq.Request     = FTDI_COMMAND_MODEM_CTRL;
+  DevReq.RequestType = USB_REQ_TYPE_VENDOR;
+  DevReq.Index       = FTDI_PORT_IDENTIFIER;
+  DevReq.Length      = 0; // indicates that there is no data phase in this request
+
+  Status = UsbIo->UsbControlTransfer (
+                    UsbIo,
+                    &DevReq,
+                    EfiUsbDataOut,
+                    WDR_TIMEOUT,
+                    &ConfigurationValue,
+                    1,
+                    &ReturnValue
+                    );
+  if (EFI_ERROR (Status)) {
+    goto StatusError;
+  }
+
+  return Status;
+
+StatusError:
+  if ((Status != EFI_INVALID_PARAMETER) ||
+      (Status != EFI_DEVICE_ERROR)      ||
+      (Status != EFI_UNSUPPORTED)          ) {
+    return EFI_DEVICE_ERROR;
+  } else {
+    return Status;
+  }
+}
+
+/**
+  Internal function that checks for valid control values and sets the control
+  bits on the Usb Serial Device.
+
+  @param  UsbSerialDevice[in]        Handle to the Usb Serial Device whose
+                                     control bits are being set
+  @param  Control[in]                The control value passed to the function
+                                     that contains the values of the control
+                                     bits that are being set
+
+  @retval EFI_SUCCESS                The control bits were set on the Usb Serial
+                                     Device
+  @retval EFI_INVALID_PARAMETER      An invalid control value was encountered
+  @retval EFI_EFI_UNSUPPORTED        The operation is not supported
+  @retval EFI_DEVICE_ERROR           The device is not functioning correctly
+
+**/
+EFI_STATUS
+EFIAPI
+SetControlBitsInternal (
+  IN USB_SER_DEV   *UsbSerialDevice,
+  IN CONTROL_BITS  *Control
+  )
+{
+  EFI_STATUS                    Status;
+  UART_FLOW_CONTROL_DEVICE_PATH *FlowControl;
+  EFI_DEVICE_PATH_PROTOCOL      *RemainingDevicePath;
+
+  //
+  // check for invalid control parameters hardware and software loopback enabled
+  // must always be set to FALSE
+  //
+  Control->HardwareLoopBack = FALSE;
+  Control->SoftwareLoopBack = FALSE;
+
+  //
+  // set hardware flow control
+  //
+  Status  = SetFlowControlInternal (
+              UsbSerialDevice->UsbIo,
+              Control->HardwareFlowControl
+              );
+  if (EFI_ERROR (Status)) {
+    goto StatusError;
+  }
+
+  //
+  // set Dtr state
+  //
+  Status = SetDtrInternal (UsbSerialDevice->UsbIo, Control->DtrState);
+  if (EFI_ERROR (Status)) {
+    goto StatusError;
+  }
+
+  //
+  // set Rts state
+  //
+  Status = SetRtsInternal (UsbSerialDevice->UsbIo, Control->RtsState);
+  if (EFI_ERROR (Status)){
+    goto StatusError;
+  }
+
+  //
+  // update the remaining control values for UsbSerialDevice->ControlValues
+  //
+  UsbSerialDevice->ControlValues.DtrState            = Control->DtrState;
+  UsbSerialDevice->ControlValues.RtsState            = Control->RtsState;
+  UsbSerialDevice->ControlValues.HardwareFlowControl = Control->HardwareFlowControl;
+  UsbSerialDevice->ControlValues.HardwareLoopBack    = FALSE;
+  UsbSerialDevice->ControlValues.SoftwareLoopBack    = FALSE;
+
+  Status = EFI_SUCCESS;
+  //
+  // Update the device path to have the correct flow control values
+  //
+  if (UsbSerialDevice->ControllerHandle != NULL) {
+    RemainingDevicePath = UsbSerialDevice->DevicePath;
+    while (!IsDevicePathEnd (RemainingDevicePath)) {
+      FlowControl = (UART_FLOW_CONTROL_DEVICE_PATH *) NextDevicePathNode (RemainingDevicePath);
+      if (FlowControl->Header.Type == MESSAGING_DEVICE_PATH &&
+          FlowControl->Header.SubType == MSG_VENDOR_DP &&
+          sizeof (UART_FLOW_CONTROL_DEVICE_PATH) == DevicePathNodeLength ((EFI_DEVICE_PATH *) FlowControl)){
+        if (UsbSerialDevice->ControlValues.HardwareFlowControl == TRUE) {
+          FlowControl->FlowControlMap = UART_FLOW_CONTROL_HARDWARE;
+        } else if (UsbSerialDevice->ControlValues.HardwareFlowControl == FALSE) {
+          FlowControl->FlowControlMap = 0;
+        }
+        break;
+      }
+      RemainingDevicePath = NextDevicePathNode (RemainingDevicePath);
+    }
+  }
+
+  return Status;
+
+StatusError:
+  if ((Status != EFI_INVALID_PARAMETER) ||
+      (Status != EFI_DEVICE_ERROR)      ||
+      (Status != EFI_UNSUPPORTED)          ) {
+    return EFI_DEVICE_ERROR;
+  } else {
+    return Status;
+  }
+}
+
+/**
+  Internal function that calculates the Control value used by GetControlBits()
+  based on the status and control values of the Usb Serial Device.
+
+  @param  UsbSerialDevice[in]        Handle to the Usb Serial Devie whose status
+                                     and control values are being used to set
+                                     Control
+  @param  Control[out]               On output the formated value of Control
+                                     that has been calculated based on the
+                                     control and status values of the Usb Serial
+                                     Device
+
+  @retval EFI_SUCCESS                The value of Control was successfully
+                                     calculated
+
+**/
+EFI_STATUS
+EFIAPI
+GetControlBitsInternal (
+  IN USB_SER_DEV  *UsbSerialDevice,
+  OUT UINT32      *Control
+  )
+{
+  *Control = 0;
+
+  //
+  // Check the values of UsbSerialDevice->Status Values and modify control
+  // accordingly these values correspond to the modem status register
+  //
+  if (UsbSerialDevice->StatusValues.CtsState) {
+    *Control |= EFI_SERIAL_CLEAR_TO_SEND;
+  }
+  if (UsbSerialDevice->StatusValues.DsrState) {
+    *Control |= EFI_SERIAL_DATA_SET_READY;
+  }
+  if (UsbSerialDevice->StatusValues.RiState) {
+    *Control |= EFI_SERIAL_RING_INDICATE;
+  }
+  if (UsbSerialDevice->StatusValues.SdState) {
+    *Control |= EFI_SERIAL_CARRIER_DETECT;
+  }
+
+  //
+  // check the values of UsbSerialDevice->ControlValues and modify control
+  // accordingly these values correspond to the values of the Modem Control
+  // Register
+  //
+  if (UsbSerialDevice->ControlValues.DtrState) {
+    *Control |= EFI_SERIAL_DATA_TERMINAL_READY;
+  }
+  if (UsbSerialDevice->ControlValues.RtsState) {
+    *Control |= EFI_SERIAL_REQUEST_TO_SEND;
+  }
+  if (UsbSerialDevice->ControlValues.HardwareLoopBack) {
+    *Control |= EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE;
+  }
+  if (UsbSerialDevice->ControlValues.HardwareFlowControl) {
+    *Control |= EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE;
+  }
+  //
+  // check if the buffer is empty since only one is being used if it is empty
+  // set both the receive and transmit buffers to empty
+  //
+  if (UsbSerialDevice->DataBufferHead == UsbSerialDevice->DataBufferTail) {
+    *Control |= EFI_SERIAL_OUTPUT_BUFFER_EMPTY;
+    *Control |= EFI_SERIAL_INPUT_BUFFER_EMPTY;
+  }
+  //
+  // check for software loopback enable in UsbSerialDevice->ControlValues
+  //
+  if (UsbSerialDevice->ControlValues.SoftwareLoopBack) {
+    *Control |= EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE;
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Resets the USB Serial Device
+
+  This function is the internal method for resetting the device and is called by
+  SerialReset()
+
+  @param  UsbSerialDevice[in]  A pointer to the USB Serial device
+
+  @retval EFI_SUCCESS          The device was reset
+  @retval EFI_DEVICE_ERROR     The device could not be reset
+
+**/
+EFI_STATUS
+EFIAPI
+ResetInternal (
+  IN USB_SER_DEV  *UsbSerialDevice
+  )
+{
+  EFI_STATUS              Status;
+  EFI_USB_DEVICE_REQUEST  DevReq;
+  UINT8                   ConfigurationValue;
+  UINT32                  ReturnValue;
+
+  DevReq.Request     = FTDI_COMMAND_RESET_PORT;
+  DevReq.RequestType = USB_REQ_TYPE_VENDOR;
+  DevReq.Value       = RESET_PORT_PURGE_RX;
+  DevReq.Index       = FTDI_PORT_IDENTIFIER;
+  DevReq.Length      = 0; //indicates that there is not data phase in this request
+
+  Status = UsbSerialDevice->UsbIo->UsbControlTransfer (
+                                     UsbSerialDevice->UsbIo,
+                                     &DevReq,
+                                     EfiUsbDataIn,
+                                     WDR_TIMEOUT,
+                                     &ConfigurationValue,
+                                     1,
+                                     &ReturnValue
+                                     );
+  if (EFI_ERROR (Status)) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  DevReq.Request     = FTDI_COMMAND_RESET_PORT;
+  DevReq.RequestType = USB_REQ_TYPE_VENDOR;
+  DevReq.Value       = RESET_PORT_PURGE_TX;
+  DevReq.Index       = FTDI_PORT_IDENTIFIER;
+  DevReq.Length      = 0; //indicates that there is no data phase in this request
+
+  Status = UsbSerialDevice->UsbIo->UsbControlTransfer (
+                                     UsbSerialDevice->UsbIo,
+                                     &DevReq,
+                                     EfiUsbDataIn,
+                                     WDR_TIMEOUT,
+                                     &ConfigurationValue,
+                                     1,
+                                     &ReturnValue
+                                     );
+  if (EFI_ERROR (Status)) {
+    return EFI_DEVICE_ERROR;
+  }
+  return Status;
+}
+
+/**
+  Entrypoint of USB Serial Driver.
+
+  This function is the entrypoint of USB Serial Driver. It installs
+  Driver Binding Protocols together with Component Name Protocols.
+
+  @param  ImageHandle[in]       The firmware allocated handle for the EFI image.
+  @param  SystemTable[in]       A pointer to the EFI System Table.
+
+  @retval EFI_SUCCESS           The entry point is executed successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+FtdiUsbSerialEntryPoint (
+  IN EFI_HANDLE        ImageHandle,
+  IN EFI_SYSTEM_TABLE  *SystemTable
+  )
+{
+  EFI_STATUS  Status;
+
+  Status = EfiLibInstallDriverBindingComponentName2 (
+             ImageHandle,
+             SystemTable,
+             &gUsbSerialDriverBinding,
+             ImageHandle,
+             &gUsbSerialComponentName,
+             &gUsbSerialComponentName2
+             );
+  ASSERT_EFI_ERROR (Status);
+  return EFI_SUCCESS;
+}
+
+/**
+  Unload function for the Usb Serial Driver.
+
+  @param  ImageHandle[in]    The allocated handle for the EFI image
+
+  @retval EFI_SUCCESS        The driver was unloaded successfully
+**/
+EFI_STATUS
+EFIAPI
+FtdiUsbSerialUnload (
+  IN EFI_HANDLE  ImageHandle
+  )
+{
+  EFI_STATUS  Status;
+  EFI_HANDLE  *HandleBuffer;
+  UINTN       HandleCount;
+  UINTN       Index;
+
+  //
+  // Retrieve all handles in the handle database
+  //
+  Status = gBS->LocateHandleBuffer (
+                  AllHandles,
+                  NULL,
+                  NULL,
+                  &HandleCount,
+                  &HandleBuffer
+                  );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Disconnect the driver from the handles in the handle database
+  //
+  for (Index = 0; Index < HandleCount; Index++) {
+    Status = gBS->DisconnectController (
+                    HandleBuffer[Index],
+                    gImageHandle,
+                    NULL
+                    );
+  }
+
+  //
+  // Free the handle array
+  //
+  FreePool (HandleBuffer);
+
+  //
+  // Uninstall protocols installed by the driver in its entrypoint
+  //
+  Status = gBS->UninstallMultipleProtocolInterfaces (
+                  ImageHandle,
+                  &gEfiDriverBindingProtocolGuid,
+                  &gUsbSerialDriverBinding,
+                  &gEfiComponentNameProtocolGuid,
+                  &gUsbSerialComponentName,
+                  &gEfiComponentName2ProtocolGuid,
+                  &gUsbSerialComponentName2,
+                  NULL
+                  );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Check whether USB Serial driver supports this device.
+
+  @param  This[in]                   The USB Serial driver binding protocol.
+  @param  Controller[in]             The controller handle to check.
+  @param  RemainingDevicePath[in]    The remaining device path.
+
+  @retval EFI_SUCCESS                The driver supports this controller.
+  @retval other                      This device isn't supported.
+
+**/
+EFI_STATUS
+EFIAPI
+UsbSerialDriverBindingSupported (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   Controller,
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
+  )
+{
+  EFI_STATUS           Status;
+  EFI_USB_IO_PROTOCOL  *UsbIo;
+  UART_DEVICE_PATH     *UartNode;
+  UART_FLOW_CONTROL_DEVICE_PATH        *FlowControlNode;
+  UINTN                                Index;
+  UINTN                                EntryCount;
+  EFI_OPEN_PROTOCOL_INFORMATION_ENTRY  *OpenInfoBuffer;
+  BOOLEAN                              HasFlowControl;
+  EFI_DEVICE_PATH_PROTOCOL             *DevicePath;
+  EFI_DEVICE_PATH_PROTOCOL             *ParentDevicePath;
+
+  if (RemainingDevicePath != NULL) {
+    if (!IsDevicePathEnd (RemainingDevicePath)) {
+      Status = EFI_UNSUPPORTED;
+      UartNode = (UART_DEVICE_PATH *) NextDevicePathNode (RemainingDevicePath);
+      if (UartNode->Header.Type != MESSAGING_DEVICE_PATH ||
+          UartNode->Header.SubType != MSG_UART_DP ||
+          sizeof (UART_DEVICE_PATH) != DevicePathNodeLength ((EFI_DEVICE_PATH *) UartNode)) {
+        goto Error;
+      }
+      FlowControlNode = (UART_FLOW_CONTROL_DEVICE_PATH *) NextDevicePathNode (UartNode);
+      if ((ReadUnaligned32 (&FlowControlNode->FlowControlMap) & ~UART_FLOW_CONTROL_HARDWARE) != 0) {
+        goto Error;
+      }
+    }
+  }
+
+  //
+  // Check if USB I/O Protocol is attached on the controller handle.
+  //
+  Status = gBS->OpenProtocol (
+                  Controller,
+                  &gEfiUsbIoProtocolGuid,
+                  (VOID **) &UsbIo,
+                  This->DriverBindingHandle,
+                  Controller,
+                  EFI_OPEN_PROTOCOL_BY_DRIVER
+                  );
+  if (Status == EFI_ALREADY_STARTED) {
+    if (RemainingDevicePath == NULL || IsDevicePathEnd (RemainingDevicePath)) {
+      return EFI_SUCCESS;
+    }
+    Status = gBS->OpenProtocolInformation (
+                    Controller,
+                    &gEfiUsbIoProtocolGuid,
+                    &OpenInfoBuffer,
+                    &EntryCount
+                    );
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+    for (Index = 0; Index < EntryCount; Index++) {
+      if ((OpenInfoBuffer[Index].Attributes & EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) != 0) {
+        Status = gBS->OpenProtocol (
+                        OpenInfoBuffer[Index].ControllerHandle,
+                        &gEfiDevicePathProtocolGuid,
+                        (VOID **) &DevicePath,
+                        This->DriverBindingHandle,
+                        Controller,
+                        EFI_OPEN_PROTOCOL_GET_PROTOCOL
+                        );
+        if (!EFI_ERROR (Status)) {
+          HasFlowControl = ContainsFlowControl (RemainingDevicePath);
+          if (HasFlowControl ^ ContainsFlowControl (DevicePath)) {
+            Status = EFI_UNSUPPORTED;
+          }
+        }
+        break;
+      }
+    }
+    FreePool (OpenInfoBuffer);
+    return Status;
+  }
+
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  gBS->CloseProtocol (
+         Controller,
+         &gEfiUsbIoProtocolGuid,
+         This->DriverBindingHandle,
+         Controller
+         );
+
+  Status = gBS->OpenProtocol (
+                  Controller,
+                  &gEfiDevicePathProtocolGuid,
+                  (VOID **) &ParentDevicePath,
+                  This->DriverBindingHandle,
+                  Controller,
+                  EFI_OPEN_PROTOCOL_BY_DRIVER
+                  );
+  if (Status == EFI_ALREADY_STARTED) {
+    return EFI_SUCCESS;
+  }
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Use the USB I/O Protocol interface to check whether Controller is
+  // a USB Serial device that can be managed by this driver.
+  //
+  Status = EFI_SUCCESS;
+
+  if (!IsUsbSerial (UsbIo)) {
+    Status = EFI_UNSUPPORTED;
+    goto Error;
+  }
+
+Error:
+  gBS->CloseProtocol (
+         Controller,
+         &gEfiDevicePathProtocolGuid,
+         This->DriverBindingHandle,
+         Controller
+         );
+  return Status;
+}
+
+/**
+  Starts the USB Serial device with this driver.
+
+  This function produces initializes the USB Serial device and
+  produces the Serial IO Protocol.
+
+  @param  This[in]                   The USB Serial driver binding instance.
+  @param  Controller[in]             Handle of device to bind driver to.
+  @param  RemainingDevicePath[in]    Optional parameter use to pick a specific
+                                     child device to start.
+
+  @retval EFI_SUCCESS                The controller is controlled by the usb USB
+                                     Serial driver.
+  @retval EFI_UNSUPPORTED            No interrupt endpoint can be found.
+  @retval Other                      This controller cannot be started.
+
+**/
+EFI_STATUS
+EFIAPI
+UsbSerialDriverBindingStart (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   Controller,
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
+  )
+{
+  EFI_STATUS                          Status;
+  EFI_USB_IO_PROTOCOL                 *UsbIo;
+  USB_SER_DEV                         *UsbSerialDevice;
+  UINT8                               EndpointNumber;
+  EFI_USB_ENDPOINT_DESCRIPTOR         EndpointDescriptor;
+  UINT8                               Index;
+  BOOLEAN                             FoundIn;
+  BOOLEAN                             FoundOut;
+  EFI_DEVICE_PATH_PROTOCOL            *ParentDevicePath;
+  EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *OpenInfoBuffer;
+  UINTN                               EntryCount;
+  EFI_SERIAL_IO_PROTOCOL              *SerialIo;
+  UART_DEVICE_PATH                    *Uart;
+  UART_FLOW_CONTROL_DEVICE_PATH       *FlowControl;
+  UINT32                              Control;
+  EFI_DEVICE_PATH_PROTOCOL            *TempDevicePath;
+
+  UsbSerialDevice = AllocateZeroPool (sizeof (USB_SER_DEV));
+  ASSERT (UsbSerialDevice != NULL);
+
+  //
+  // Get the Parent Device path
+  //
+  Status = gBS->OpenProtocol (
+                  Controller,
+                  &gEfiDevicePathProtocolGuid,
+                  (VOID **) &ParentDevicePath,
+                  This->DriverBindingHandle,
+                  Controller,
+                  EFI_OPEN_PROTOCOL_BY_DRIVER
+                  );
+  if (EFI_ERROR (Status) && Status != EFI_ALREADY_STARTED) {
+    goto ErrorExit1;
+  }
+
+  //
+  // Open USB I/O Protocol
+  //
+  Status = gBS->OpenProtocol (
+                  Controller,
+                  &gEfiUsbIoProtocolGuid,
+                  (VOID **) &UsbIo,
+                  This->DriverBindingHandle,
+                  Controller,
+                  EFI_OPEN_PROTOCOL_BY_DRIVER
+                  );
+  if (EFI_ERROR (Status) && Status != EFI_ALREADY_STARTED) {
+    goto ErrorExit1;
+  }
+
+  if (Status == EFI_ALREADY_STARTED) {
+    if (RemainingDevicePath == NULL || IsDevicePathEnd (RemainingDevicePath)) {
+      FreePool (UsbSerialDevice);
+      return EFI_SUCCESS;
+    }
+
+    //
+    // Check to see if a child handle exists
+    //
+    Status = gBS->OpenProtocolInformation (
+                    Controller,
+                    &gEfiSerialIoProtocolGuid,
+                    &OpenInfoBuffer,
+                    &EntryCount
+                    );
+    if (EFI_ERROR (Status)) {
+      goto ErrorExit1;
+    }
+
+    Status = EFI_ALREADY_STARTED;
+    for (Index = 0; Index < EntryCount; Index++) {
+      if ((OpenInfoBuffer[Index].Attributes & EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) != 0) {
+        Status = gBS->OpenProtocol (
+                        OpenInfoBuffer[Index].ControllerHandle,
+                        &gEfiSerialIoProtocolGuid,
+                        (VOID **) &SerialIo,
+                        This->DriverBindingHandle,
+                        Controller,
+                        EFI_OPEN_PROTOCOL_GET_PROTOCOL
+                        );
+        if (EFI_ERROR (Status)) {
+        }
+        if (!EFI_ERROR (Status)) {
+          Uart = (UART_DEVICE_PATH *) RemainingDevicePath;
+          Status = SerialIo->SetAttributes (
+                               SerialIo,
+                               Uart->BaudRate,
+                               SerialIo->Mode->ReceiveFifoDepth,
+                               SerialIo->Mode->Timeout,
+                               (EFI_PARITY_TYPE) Uart->Parity,
+                               Uart->DataBits,
+                               (EFI_STOP_BITS_TYPE) Uart->StopBits
+                               );
+          FlowControl = (UART_FLOW_CONTROL_DEVICE_PATH *) NextDevicePathNode (Uart);
+          if (!EFI_ERROR (Status) && IsUartFlowControlNode (FlowControl)) {
+            Status = SerialIo->GetControl (
+                                 SerialIo,
+                                 &Control
+                                 );
+            if (!EFI_ERROR (Status)) {
+              if (ReadUnaligned32 (&FlowControl->FlowControlMap) == UART_FLOW_CONTROL_HARDWARE) {
+                Control |= EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE;
+              } else {
+                Control &= ~EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE;
+              }
+              //
+              // Clear bits that are not allowed to be passed to SetControl
+              //
+              Control &= (EFI_SERIAL_REQUEST_TO_SEND | 
+                          EFI_SERIAL_DATA_TERMINAL_READY |
+                          EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE |
+                          EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE |
+                          EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE);
+              Status = SerialIo->SetControl (SerialIo, Control);
+            }
+          }
+        }
+        break;
+      }
+    }
+    FreePool (OpenInfoBuffer);
+    return Status;
+  }
+
+  if (RemainingDevicePath != NULL) {
+    if (IsDevicePathEnd (RemainingDevicePath)) {
+      return EFI_SUCCESS;
+    }
+  }
+
+  UsbSerialDevice->UsbIo = UsbIo;
+
+  //
+  // Get interface & endpoint descriptor
+  //
+  UsbIo->UsbGetInterfaceDescriptor (
+           UsbIo,
+           &UsbSerialDevice->InterfaceDescriptor
+           );
+
+  EndpointNumber = UsbSerialDevice->InterfaceDescriptor.NumEndpoints;
+
+  //
+  // Traverse endpoints to find the IN and OUT endpoints that will send and
+  // receive data.
+  //
+  FoundIn = FALSE;
+  FoundOut = FALSE;
+  for (Index = 0; Index < EndpointNumber; Index++) {
+
+    Status = UsbIo->UsbGetEndpointDescriptor (
+                      UsbIo,
+                      Index,
+                      &EndpointDescriptor
+                      );
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+
+    if (EndpointDescriptor.EndpointAddress == FTDI_ENDPOINT_ADDRESS_OUT) {
+      //
+      // Set the Out endpoint device
+      //
+      CopyMem (
+        &UsbSerialDevice->OutEndpointDescriptor,
+        &EndpointDescriptor,
+        sizeof(EndpointDescriptor)
+        );
+      FoundOut = TRUE;
+    }
+
+    if (EndpointDescriptor.EndpointAddress == FTDI_ENDPOINT_ADDRESS_IN) {
+      //
+      // Set the In endpoint device
+      //
+      CopyMem (
+        &UsbSerialDevice->InEndpointDescriptor,
+        &EndpointDescriptor,
+        sizeof(EndpointDescriptor)
+        );
+      FoundIn = TRUE;
+    }
+  }
+
+  if (!FoundIn || !FoundOut) {
+    //
+    // No interrupt endpoint found, then return unsupported.
+    //
+    Status = EFI_UNSUPPORTED;
+    goto ErrorExit;
+  }
+  //
+  // set the initial values of UsbSerialDevice->LastSettings to the default
+  // values
+  //
+  UsbSerialDevice->LastSettings.BaudRate         = 115200;
+  UsbSerialDevice->LastSettings.DataBits         = 8;
+  UsbSerialDevice->LastSettings.Parity           = NoParity;
+  UsbSerialDevice->LastSettings.ReceiveFifoDepth = FTDI_MAX_RECEIVE_FIFO_DEPTH;
+  UsbSerialDevice->LastSettings.StopBits         = OneStopBit;
+  UsbSerialDevice->LastSettings.Timeout          = FTDI_TIMEOUT;
+
+  //
+  // set the initial values of UsbSerialDevice->ControlValues
+  //
+  UsbSerialDevice->ControlValues.DtrState            = FALSE;
+  UsbSerialDevice->ControlValues.RtsState            = FALSE;
+  UsbSerialDevice->ControlValues.HardwareFlowControl = FALSE;
+  UsbSerialDevice->ControlValues.HardwareLoopBack    = FALSE;
+  UsbSerialDevice->ControlValues.SoftwareLoopBack    = FALSE;
+
+  //
+  // set the values of UsbSerialDevice->UartDevicePath
+  //
+  UsbSerialDevice->UartDevicePath.Header.Type    = MESSAGING_DEVICE_PATH;
+  UsbSerialDevice->UartDevicePath.Header.SubType = MSG_UART_DP;
+  UsbSerialDevice->UartDevicePath.Header.Length[0] = (UINT8) (sizeof (UART_DEVICE_PATH));
+  UsbSerialDevice->UartDevicePath.Header.Length[1] = (UINT8) ((sizeof (UART_DEVICE_PATH)) >> 8);
+
+  //
+  // set the values of UsbSerialDevice->FlowControlDevicePath
+  UsbSerialDevice->FlowControlDevicePath.Header.Type = MESSAGING_DEVICE_PATH;
+  UsbSerialDevice->FlowControlDevicePath.Header.SubType = MSG_VENDOR_DP;
+  UsbSerialDevice->FlowControlDevicePath.Header.Length[0] = (UINT8) (sizeof (UART_FLOW_CONTROL_DEVICE_PATH));
+  UsbSerialDevice->FlowControlDevicePath.Header.Length[1] = (UINT8) ((sizeof (UART_FLOW_CONTROL_DEVICE_PATH)) >> 8);
+  UsbSerialDevice->FlowControlDevicePath.FlowControlMap = 0;
+
+  Status = SetAttributesInternal (
+             UsbSerialDevice, 
+             UsbSerialDevice->LastSettings.BaudRate,
+             UsbSerialDevice->LastSettings.ReceiveFifoDepth, 
+             UsbSerialDevice->LastSettings.Timeout,
+             UsbSerialDevice->LastSettings.Parity, 
+             UsbSerialDevice->LastSettings.DataBits,
+             UsbSerialDevice->LastSettings.StopBits
+             );
+
+  ASSERT_EFI_ERROR (Status);
+
+  Status = SetControlBitsInternal (
+             UsbSerialDevice,
+             &(UsbSerialDevice->ControlValues)
+             );
+
+  ASSERT_EFI_ERROR (Status);
+
+  //
+  // Publish Serial GUID and protocol
+  //
+
+  UsbSerialDevice->Signature              = USB_SER_DEV_SIGNATURE;
+  UsbSerialDevice->SerialIo.Reset         = SerialReset;
+  UsbSerialDevice->SerialIo.SetControl    = SetControlBits;
+  UsbSerialDevice->SerialIo.SetAttributes = SetAttributes;
+  UsbSerialDevice->SerialIo.GetControl    = GetControlBits;
+  UsbSerialDevice->SerialIo.Read          = ReadSerialIo;
+  UsbSerialDevice->SerialIo.Write         = WriteSerialIo;
+
+  //
+  // Set the static Serial IO modes that will display when running
+  // "sermode" within the UEFI shell.
+  //
+
+  UsbSerialDevice->SerialIo.Mode->Timeout  = 0;
+  UsbSerialDevice->SerialIo.Mode->BaudRate = 115200;
+  UsbSerialDevice->SerialIo.Mode->DataBits = 8;
+  UsbSerialDevice->SerialIo.Mode->Parity   = 1;
+  UsbSerialDevice->SerialIo.Mode->StopBits = 1;
+
+  UsbSerialDevice->ParentDevicePath = ParentDevicePath;
+  UsbSerialDevice->ControllerHandle = NULL;
+  FlowControl                       = NULL;
+
+  //
+  // Allocate space for the receive buffer
+  //
+  UsbSerialDevice->DataBuffer = AllocateZeroPool (SW_FIFO_DEPTH);
+
+  //
+  // Initialize data buffer pointers.
+  // Head==Tail = true means buffer is empty.
+  //
+  UsbSerialDevice->DataBufferHead = 0;
+  UsbSerialDevice->DataBufferTail = 0;
+
+  UsbSerialDevice->ControllerNameTable = NULL;
+  AddUnicodeString2 (
+    "eng",
+    gUsbSerialComponentName.SupportedLanguages,
+    &UsbSerialDevice->ControllerNameTable,
+    L"FTDI USB Serial Adapter",
+    TRUE
+    );
+  AddUnicodeString2 (
+    "en",
+    gUsbSerialComponentName2.SupportedLanguages,
+    &UsbSerialDevice->ControllerNameTable,
+    L"FTDI USB Serial Adapter",
+    FALSE
+    );
+
+  Status = SetInitialStatus (UsbSerialDevice);
+  ASSERT_EFI_ERROR (Status);
+
+  //
+  // Create a polling loop to check for input
+  //
+
+  gBS->CreateEvent (
+         EVT_TIMER | EVT_NOTIFY_SIGNAL,
+         TPL_CALLBACK,
+         UsbSerialDriverCheckInput,
+         UsbSerialDevice,
+         &(UsbSerialDevice->PollingLoop)
+         );
+  //
+  // add code to set trigger time based on baud rate
+  // setting to 0.5s for now
+  //
+  gBS->SetTimer (
+         UsbSerialDevice->PollingLoop,
+         TimerPeriodic,
+         EFI_TIMER_PERIOD_MILLISECONDS (500)
+         );
+
+  //
+  // Check if the remaining device path is null. If it is not null change the settings
+  // of the device to match those on the device path
+  //
+  if (RemainingDevicePath != NULL) {
+    CopyMem (
+      &UsbSerialDevice->UartDevicePath,
+      RemainingDevicePath,
+      sizeof (UART_DEVICE_PATH)
+      );
+    FlowControl = (UART_FLOW_CONTROL_DEVICE_PATH *) NextDevicePathNode (RemainingDevicePath);
+    if (IsUartFlowControlNode (FlowControl)) {
+      UsbSerialDevice->FlowControlDevicePath.FlowControlMap = ReadUnaligned32 (&FlowControl->FlowControlMap);
+    } else {
+      FlowControl = NULL;
+    }
+  }
+
+  //
+  // Build the device path by appending the UART node to the parent device path
+  //
+  UsbSerialDevice->DevicePath = AppendDevicePathNode (
+                                  ParentDevicePath,
+                                  (EFI_DEVICE_PATH_PROTOCOL *) &UsbSerialDevice->UartDevicePath
+                                  );
+  //
+  // Continue building the device path by appending the flow control node
+  //
+  TempDevicePath = UsbSerialDevice->DevicePath;
+  UsbSerialDevice->DevicePath = AppendDevicePathNode (
+                                  TempDevicePath,
+                                  (EFI_DEVICE_PATH_PROTOCOL *) &UsbSerialDevice->FlowControlDevicePath
+                                  );
+  FreePool (TempDevicePath);
+
+  if (UsbSerialDevice->DevicePath == NULL) {
+    Status = EFI_OUT_OF_RESOURCES;
+    goto ErrorExit;
+  }
+
+  //
+  // Install protocol interfaces for the device
+  //
+  Status = gBS->InstallMultipleProtocolInterfaces (
+                  &UsbSerialDevice->ControllerHandle,
+                  &gEfiDevicePathProtocolGuid,
+                  UsbSerialDevice->DevicePath,
+                  &gEfiSerialIoProtocolGuid,
+                  &UsbSerialDevice->SerialIo,
+                  NULL
+                  );
+  if (EFI_ERROR (Status)){
+    goto ErrorExit;
+  }
+
+  //
+  // Open for child device
+  //
+  Status = gBS->OpenProtocol (
+                 Controller,
+                 &gEfiUsbIoProtocolGuid,
+                 (VOID **) &UsbIo,
+                 This->DriverBindingHandle,
+                 UsbSerialDevice->ControllerHandle,
+                 EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+                 );
+
+  UsbSerialDevice->Shutdown = FALSE;
+
+  return EFI_SUCCESS;
+
+ErrorExit:
+  //
+  // Error handler
+  //
+
+  Status = gBS->UninstallMultipleProtocolInterfaces (
+                  Controller,
+                  &gEfiSerialIoProtocolGuid,
+                  &UsbSerialDevice->SerialIo,
+                  NULL
+                  );
+  if (EFI_ERROR (Status)) {
+    goto ErrorExit1;
+  }
+
+  FreePool (UsbSerialDevice->DataBuffer);
+  FreePool (UsbSerialDevice);
+
+  UsbSerialDevice = NULL;
+  gBS->CloseProtocol (
+         Controller,
+         &gEfiUsbIoProtocolGuid,
+         This->DriverBindingHandle,
+         Controller
+         );
+
+ErrorExit1:
+  return Status;
+}
+
+/**
+  Stop the USB Serial device handled by this driver.
+
+  @param  This[in]                   The USB Serial driver binding protocol.
+  @param  Controller[in]             The controller to release.
+  @param  NumberOfChildren[in]       The number of handles in ChildHandleBuffer.
+  @param  ChildHandleBuffer[in]      The array of child handle.
+
+  @retval EFI_SUCCESS                The device was stopped.
+  @retval EFI_UNSUPPORTED            Serial IO Protocol is not installed on
+                                     Controller.
+  @retval EFI_DEVICE_ERROR           The device could not be stopped due to a
+                                     device error.
+  @retval Others                     Fail to uninstall protocols attached on the
+                                     device.
+
+**/
+EFI_STATUS
+EFIAPI
+UsbSerialDriverBindingStop (
+  IN  EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN  EFI_HANDLE                   Controller,
+  IN  UINTN                        NumberOfChildren,
+  IN  EFI_HANDLE                   *ChildHandleBuffer
+  )
+{
+  EFI_STATUS                Status;
+  EFI_SERIAL_IO_PROTOCOL    *SerialIo;
+  EFI_USB_IO_PROTOCOL       *UsbIo;
+  USB_SER_DEV               *UsbSerialDevice;
+  UINTN                     Index;
+  BOOLEAN                   AllChildrenStopped;
+
+  Status = EFI_SUCCESS;
+  UsbSerialDevice = NULL;
+
+  if (NumberOfChildren == 0) {
+    //
+    // Close the driver
+    //
+    Status = gBS->CloseProtocol (
+                    Controller,
+                    &gEfiUsbIoProtocolGuid,
+                    This->DriverBindingHandle,
+                    Controller
+                    );
+    Status = gBS->CloseProtocol (
+                    Controller,
+                    &gEfiDevicePathProtocolGuid,
+                    This->DriverBindingHandle,
+                    Controller
+                    );
+    return Status;
+  }
+
+  AllChildrenStopped = TRUE;
+
+  for (Index = 0; Index < NumberOfChildren ;Index++) {
+    Status = gBS->OpenProtocol (
+                    ChildHandleBuffer[Index],
+                    &gEfiSerialIoProtocolGuid,
+                    (VOID **) &SerialIo,
+                    This->DriverBindingHandle,
+                    Controller,
+                    EFI_OPEN_PROTOCOL_GET_PROTOCOL
+                    );
+    if (Status == EFI_SUCCESS) {//!EFI_ERROR (Status)) {
+      UsbSerialDevice = USB_SER_DEV_FROM_THIS (SerialIo);
+      Status = gBS->CloseProtocol (
+                      Controller,
+                      &gEfiUsbIoProtocolGuid,
+                      This->DriverBindingHandle,
+                      ChildHandleBuffer[Index]
+                      );
+      Status = gBS->UninstallMultipleProtocolInterfaces (
+                      ChildHandleBuffer[Index],
+                      &gEfiDevicePathProtocolGuid,
+                      UsbSerialDevice->DevicePath,
+                      &gEfiSerialIoProtocolGuid,
+                      &UsbSerialDevice->SerialIo,
+                      NULL
+                      );
+
+      if (EFI_ERROR (Status)) {
+        gBS->OpenProtocol (
+               Controller,
+               &gEfiUsbIoProtocolGuid,
+               (VOID **) &UsbIo,
+               This->DriverBindingHandle,
+               ChildHandleBuffer[Index],
+               EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+               );
+      } else {
+        if (UsbSerialDevice->DevicePath != NULL) {
+          gBS->FreePool (UsbSerialDevice->DevicePath);
+        }
+        gBS->SetTimer (
+               UsbSerialDevice->PollingLoop,
+               TimerCancel,
+               0
+               );
+        gBS->CloseEvent (UsbSerialDevice->PollingLoop);
+        UsbSerialDevice->Shutdown = TRUE;
+        FreeUnicodeStringTable (UsbSerialDevice->ControllerNameTable);
+        FreePool (UsbSerialDevice->DataBuffer);
+        FreePool (UsbSerialDevice);
+      }
+    }
+    if (EFI_ERROR (Status)) {
+      AllChildrenStopped = FALSE;
+    }
+  }
+
+  if (!AllChildrenStopped) {
+    return EFI_DEVICE_ERROR;
+  }
+  return EFI_SUCCESS;
+}
+
+//
+// Serial IO Member Functions
+//
+
+/**
+  Reset the serial device.
+
+  @param  This[in]              Protocol instance pointer.
+
+  @retval EFI_SUCCESS           The device was reset.
+  @retval EFI_DEVICE_ERROR      The serial device could not be reset.
+
+**/
+EFI_STATUS
+EFIAPI
+SerialReset (
+  IN EFI_SERIAL_IO_PROTOCOL  *This
+  )
+{
+  EFI_STATUS    Status;
+  USB_SER_DEV  *UsbSerialDevice;
+
+  UsbSerialDevice = USB_SER_DEV_FROM_THIS (This);
+  Status          = ResetInternal (UsbSerialDevice);
+  if (EFI_ERROR (Status)){
+    return EFI_DEVICE_ERROR;
+  }
+  return Status;
+}
+
+/**
+  Set the control bits on a serial device.
+
+  @param  This[in]             Protocol instance pointer.
+  @param  Control[in]          Set the bits of Control that are settable.
+
+  @retval EFI_SUCCESS          The new control bits were set on the serial device.
+  @retval EFI_UNSUPPORTED      The serial device does not support this operation.
+  @retval EFI_DEVICE_ERROR     The serial device is not functioning correctly.
+
+**/
+EFI_STATUS
+EFIAPI
+SetControlBits (
+  IN EFI_SERIAL_IO_PROTOCOL  *This,
+  IN UINT32                  Control
+  )
+{
+  EFI_STATUS    Status;
+  USB_SER_DEV   *UsbSerialDevice;
+  CONTROL_BITS  ControlBits;
+  
+  UsbSerialDevice = USB_SER_DEV_FROM_THIS (This);
+  
+  //
+  // check for invalid control parameters 
+  //
+  if ((Control & (~(EFI_SERIAL_REQUEST_TO_SEND          |
+                    EFI_SERIAL_DATA_TERMINAL_READY      |
+                    EFI_SERIAL_HARDWARE_LOOPBACK_ENABLE |
+                    EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE |
+                    EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE))) != 0 ) {
+    return EFI_UNSUPPORTED;
+  }
+
+  //
+  // check the control parameters and set the correct setting for
+  // the paramerts of ControlBits
+  // both loopback enables are always set to FALSE
+  //
+  ControlBits.HardwareLoopBack = FALSE;
+  ControlBits.SoftwareLoopBack = FALSE;
+  //
+  // check for hardware flow control
+  //
+  if ((Control & EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE) == EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE) {
+    ControlBits.HardwareFlowControl = TRUE;
+  } else {
+    ControlBits.HardwareFlowControl = FALSE;
+  }
+  //
+  // check for DTR enabled
+  //
+  if ((Control & EFI_SERIAL_DATA_TERMINAL_READY) == EFI_SERIAL_DATA_TERMINAL_READY) {
+    ControlBits.DtrState = TRUE;
+  } else {
+    ControlBits.DtrState = FALSE;
+  }
+  //
+  // check for RTS enabled
+  //
+  if ((Control & EFI_SERIAL_REQUEST_TO_SEND) == EFI_SERIAL_REQUEST_TO_SEND) {
+    ControlBits.RtsState = TRUE;
+  } else {
+    ControlBits.RtsState = FALSE;
+  }
+
+  //
+  // set the control values with a call to SetControlBitsInternal()
+  //
+  Status = SetControlBitsInternal (UsbSerialDevice, &ControlBits);
+
+  return Status;
+}
+
+/**
+  calls SetAttributesInternal() to set the baud rate, receive FIFO depth,
+  transmit/receive time out, parity, data buts, and stop bits on a serial
+  device.
+
+  @param  This[in]             Protocol instance pointer.
+  @param  BaudRate[in]         The requested baud rate. A BaudRate value of 0
+                               will use the device's default interface speed.
+  @param  ReveiveFifoDepth[in] The requested depth of the FIFO on the receive
+                               side of the serial interface. A ReceiveFifoDepth
+                               value of 0 will use the device's default FIFO
+                               depth.
+  @param  Timeout[in]          The requested time out for a single character in
+                               microseconds.This timeout applies to both the
+                               transmit and receive side of the interface. A
+                               Timeout value of 0 will use the device's default
+                               time out value.
+  @param  Parity[in]           The type of parity to use on this serial device.
+                               A Parity value of DefaultParity will use the
+                               device's default parity value.
+  @param  DataBits[in]         The number of data bits to use on the serial
+                               device. A DataBit vaule of 0 will use the
+                               device's default data bit setting.
+  @param  StopBits[in]         The number of stop bits to use on this serial
+                               device. A StopBits value of DefaultStopBits will
+                               use the device's default number of stop bits.
+
+  @retval EFI_SUCCESS          The attributes were set
+  @retval EFI_DEVICE_ERROR     The attributes were not able to be
+
+**/
+EFI_STATUS
+EFIAPI
+SetAttributes (
+  IN EFI_SERIAL_IO_PROTOCOL  *This,
+  IN UINT64                  BaudRate,
+  IN UINT32                  ReceiveFifoDepth,
+  IN UINT32                  Timeout,
+  IN EFI_PARITY_TYPE         Parity,
+  IN UINT8                   DataBits,
+  IN EFI_STOP_BITS_TYPE      StopBits
+  )
+{
+
+  EFI_STATUS   Status;
+  USB_SER_DEV  *UsbSerialDevice;
+
+  UsbSerialDevice = USB_SER_DEV_FROM_THIS (This);
+
+  Status = SetAttributesInternal (
+             UsbSerialDevice,
+             BaudRate,
+             ReceiveFifoDepth,
+             Timeout,
+             Parity,
+             DataBits,
+             StopBits
+             );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  return Status;
+}
+
+
+/**
+  Retrieves the status of the control bits on a serial device.
+
+  @param  This[in]               Protocol instance pointer.
+  @param  Control[out]           A pointer to return the current Control signals
+                                 from the serial device.
+
+  @retval EFI_SUCCESS            The control bits were read from the serial
+                                 device.
+  @retval EFI_DEVICE_ERROR       The serial device is not functioning correctly.
+
+**/
+EFI_STATUS
+EFIAPI
+GetControlBits (
+  IN EFI_SERIAL_IO_PROTOCOL  *This,
+  OUT UINT32                 *Control
+  )
+{
+  USB_SER_DEV  *UsbSerialDevice;
+  EFI_STATUS   Status;
+
+  UsbSerialDevice = USB_SER_DEV_FROM_THIS (This);
+
+  *Control        = 0;
+
+  Status = GetControlBitsInternal (UsbSerialDevice, Control);
+
+  if (EFI_ERROR (Status)) {
+    return EFI_DEVICE_ERROR;
+  }
+  return Status;
+}
+
+/**
+  Reads data from a serial device.
+
+  @param  This[in]                   Protocol instance pointer.
+  @param  BufferSize[in, out]        On input, the size of the Buffer. On output,
+                                     the amount of data returned in Buffer.
+  @param  Buffer[out]                The buffer to return the data into.
+
+  @retval EFI_SUCCESS                The data was read.
+  @retval EFI_DEVICE_ERROR           The device reported an error.
+  @retval EFI_TIMEOUT                The data write was stopped due to a timeout.
+
+**/
+EFI_STATUS
+EFIAPI
+ReadSerialIo (
+  IN EFI_SERIAL_IO_PROTOCOL  *This,
+  IN OUT UINTN               *BufferSize,
+  OUT VOID                   *Buffer
+  )
+{
+  UINTN        Index;
+  UINTN        RemainingCallerBufferSize;
+  USB_SER_DEV  *UsbSerialDevice;
+  EFI_STATUS   Status;
+
+
+  if (*BufferSize == 0) {
+    return EFI_SUCCESS;
+  }
+
+  if (Buffer == NULL) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  Status          = EFI_SUCCESS;
+  UsbSerialDevice = USB_SER_DEV_FROM_THIS (This);
+
+  //
+  // Clear out any data that we already have in our internal buffer
+  //
+  for (Index = 0; Index < *BufferSize; Index++) {
+    if (UsbSerialDevice->DataBufferHead == UsbSerialDevice->DataBufferTail) {
+      break;
+    }
+
+    //
+    // Still have characters in the buffer to return
+    //
+    ((UINT8 *)Buffer)[Index] = UsbSerialDevice->DataBuffer[UsbSerialDevice->DataBufferHead];
+    UsbSerialDevice->DataBufferHead = (UsbSerialDevice->DataBufferHead + 1) % SW_FIFO_DEPTH;
+  }
+
+  //
+  // If we haven't filled the caller's buffer using data that we already had on
+  // hand We need to generate an additional USB request to try and fill the
+  // caller's buffer
+  //
+  if (Index != *BufferSize) {
+    RemainingCallerBufferSize = *BufferSize - Index;
+    Status = ReadDataFromUsb (
+               UsbSerialDevice,
+               &RemainingCallerBufferSize,
+               (VOID *)(((CHAR8 *)Buffer) + Index)
+               );
+    if (!EFI_ERROR (Status)) {
+      *BufferSize = RemainingCallerBufferSize + Index;
+    } else {
+      *BufferSize = Index;
+    }
+  }
+
+  if (UsbSerialDevice->DataBufferHead == UsbSerialDevice->DataBufferTail) {
+    //
+    // Data buffer has no data, set the EFI_SERIAL_INPUT_BUFFER_EMPTY flag
+    //
+    UsbSerialDevice->ControlBits |= EFI_SERIAL_INPUT_BUFFER_EMPTY;
+  } else {
+    //
+    // There is some leftover data, clear EFI_SERIAL_INPUT_BUFFER_EMPTY flag
+    //
+    UsbSerialDevice->ControlBits &= ~(EFI_SERIAL_INPUT_BUFFER_EMPTY);
+  }
+  return Status;
+}
+
+/**
+  Writes data to a serial device.
+
+  @param  This[in]                   Protocol instance pointer.
+  @param  BufferSize[in, out]        On input, the size of the Buffer. On output,
+                                     the amount of data actually written.
+  @param  Buffer[in]                 The buffer of data to write
+
+  @retval EFI_SUCCESS                The data was written.
+  @retval EFI_DEVICE_ERROR           The device reported an error.
+  @retval EFI_TIMEOUT                The data write was stopped due to a timeout.
+
+**/
+EFI_STATUS
+EFIAPI
+WriteSerialIo (
+  IN EFI_SERIAL_IO_PROTOCOL  *This,
+  IN OUT UINTN               *BufferSize,
+  IN VOID                    *Buffer
+  )
+{
+  EFI_STATUS   Status;
+  USB_SER_DEV  *UsbSerialDevice;
+  EFI_TPL      Tpl;
+
+  UsbSerialDevice = USB_SER_DEV_FROM_THIS (This);
+
+  if (UsbSerialDevice->Shutdown) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  Tpl = gBS->RaiseTPL (TPL_NOTIFY);
+
+  Status = UsbSerialDataTransfer (
+             UsbSerialDevice,
+             EfiUsbDataOut,
+             Buffer,
+             BufferSize,
+             FTDI_TIMEOUT
+             );
+
+  gBS->RestoreTPL (Tpl);
+  if (EFI_ERROR (Status)) {
+    if (Status == EFI_TIMEOUT){
+      return Status;
+    } else {
+      return EFI_DEVICE_ERROR;
+    }
+  }
+
+  return EFI_SUCCESS;
+}
diff --git a/Drivers/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/FtdiUsbSerialDriver.h b/Drivers/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/FtdiUsbSerialDriver.h
new file mode 100644
index 0000000000..6048923d6f
--- /dev/null
+++ b/Drivers/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/FtdiUsbSerialDriver.h
@@ -0,0 +1,589 @@
+/** @file
+  Header file for USB Serial Driver's Data Structures.
+
+Copyright (c) 2004 - 2013, Intel Corporation. All rights reserved.
+Portions Copyright 2012 Ashley DeSimone
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef _FTDI_USB_SERIAL_DRIVER_H_
+#define _FTDI_USB_SERIAL_DRIVER_H_
+
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/DevicePathLib.h>
+
+#include <Protocol/DevicePath.h>
+#include <Protocol/UsbIo.h>
+#include <Protocol/SerialIo.h>
+
+//
+// US English LangID
+//
+#define USB_US_LANG_ID  0x0409
+
+//
+// Supported Vendor Ids
+//
+#define VID_FTDI    0x0403
+
+//
+// Supported product ids
+//
+#define DID_FTDI_FT232    0x6001
+
+//
+// FTDI Commands
+//
+#define FTDI_COMMAND_RESET_PORT          0
+#define FTDI_COMMAND_MODEM_CTRL          1
+#define FTDI_COMMAND_SET_FLOW_CTRL       2
+#define FTDI_COMMAND_SET_BAUDRATE        3
+#define FTDI_COMMAND_SET_DATA            4
+#define FTDI_COMMAND_GET_MODEM_STATUS    5
+#define FTDI_COMMAND_SET_EVENT_CHAR      6
+#define FTDI_COMMAND_SET_ERROR_CHAR      7
+#define FTDI_COMMAND_SET_LATENCY_TIMER   9
+#define FTDI_COMMAND_GET_LATENCY_TIMER   10
+
+//
+// FTDI_PORT_IDENTIFIER
+// Used in the usb control transfers that issue FTDI commands as the index value.
+//
+#define FTDI_PORT_IDENTIFIER    0x1 // For FTDI USB serial adapter the port
+                                    // identifier is always 1.
+
+//
+// RESET_PORT
+//
+#define RESET_PORT_RESET        0x0 // Purges RX and TX, clears DTR and RTS sets
+                                    // flow control to none, disables event
+                                    // trigger, sets the event char to 0x0d and
+                                    // does nothing to baudrate or data settings
+#define RESET_PORT_PURGE_RX     0x1
+#define RESET_PORT_PURGE_TX     0x2
+
+//
+// SET_FLOW_CONTROL
+//
+#define NO_FLOW_CTRL                     0x0
+#define XON_XOFF_CTRL                    0x4
+
+//
+// SET_BAUD_RATE
+// To set baud rate, one must calculate an encoding of the baud rate from
+// UINT32 to UINT16.See EncodeBaudRateForFtdi() for details
+//
+#define FTDI_UART_FREQUENCY              3000000
+#define FTDI_MIN_DIVISOR                 0x20
+#define FTDI_MAX_DIVISOR                 0x3FFF8
+//
+// Special case baudrate values
+// 300,000 and 200,000 are special cases for calculating the encoded baudrate
+//
+#define FTDI_SPECIAL_CASE_300_MIN        (3000000 * 100) / 103 // minimum adjusted
+                                                               // value for 300,000
+#define FTDI_SPECIAL_CASE_300_MAX        (3000000 * 100) / 97  // maximum adjusted
+                                                               // value for 300,000
+#define FTDI_SPECIAL_CASE_200_MIN        (2000000 * 100) / 103 // minimum adjusted
+                                                               // value for 200,000
+#define FTDI_SPECIAL_CASE_200_MAX        (2000000 * 100) / 97  // maximum adjusted
+                                                               // value for 200,000
+//
+// Min and max frequency values that the FTDI chip can attain
+//.all generated frequencies must be between these values
+//
+#define FTDI_MIN_FREQUENCY              46601941 // (3MHz * 1600) / 103 = 46601941
+#define FTDI_MAX_FREQUENCY              49484536 // (3MHz * 1600) / 97 = 49484536
+
+//
+// SET_DATA_BITS
+//
+#define SET_DATA_BITS(n)                 (n)
+
+//
+// SET_PARITY
+//
+#define SET_PARITY_NONE                   0x0
+#define SET_PARITY_ODD                    BIT8 // (0x1 << 8)
+#define SET_PARITY_EVEN                   BIT9 // (0x2 << 8)
+#define SET_PARITY_MARK                   BIT9 | BIT8 // (0x3 << 8)
+#define SET_PARITY_SPACE                  BIT10 // (0x4 << 8)
+
+//
+// SET_STOP_BITS
+//
+#define SET_STOP_BITS_1                   0x0
+#define SET_STOP_BITS_15                  BIT11 // (0x1 << 11)
+#define SET_STOP_BITS_2                   BIT12 // (0x2 << 11)
+
+//
+// SET_MODEM_CTRL
+// SET_DTR_HIGH = (1 | (1 << 8)), SET_DTR_LOW = (0 | (1 << 8)
+// SET_RTS_HIGH = (2 | (2 << 8)), SET_RTS_LOW = (0 | (2 << 8)
+//
+#define SET_DTR_HIGH                     (BIT8 | BIT0)
+#define SET_DTR_LOW                      (BIT8)
+#define SET_RTS_HIGH                     (BIT9 | BIT1)
+#define SET_RTS_LOW                      (BIT9)
+
+//
+// MODEM_STATUS
+//
+#define CTS_MASK                         BIT4
+#define DSR_MASK                         BIT5
+#define RI_MASK                          BIT6
+#define SD_MASK                          BIT7
+#define MSR_MASK                         (CTS_MASK | DSR_MASK | RI_MASK | SD_MASK)
+
+//
+// Macro used to check for USB transfer errors
+//
+#define USB_IS_ERROR(Result, Error)           (((Result) & (Error)) != 0)
+
+//
+// USB request timeouts
+//
+#define WDR_TIMEOUT        5000  // default urb timeout in ms
+#define WDR_SHORT_TIMEOUT  1000  // shorter urb timeout in ms
+
+//
+// FTDI timeout
+//
+#define FTDI_TIMEOUT       16
+
+//
+// FTDI FIFO depth
+//
+#define FTDI_MAX_RECEIVE_FIFO_DEPTH  384
+
+//
+// FTDI Endpoint Descriptors
+//
+#define FTDI_ENDPOINT_ADDRESS_IN   0x81 //the endpoint address for the in enpoint generated by the device
+#define FTDI_ENDPOINT_ADDRESS_OUT  0x02 //the endpoint address for the out endpoint generated by the device
+
+//
+// Max buffer size for USB transfers
+//
+#define SW_FIFO_DEPTH 1024
+
+//
+// struct to define a usb device as a vendor and product id pair
+//
+typedef struct {
+  UINTN     VendorId;
+  UINTN     DeviceId;
+} USB_DEVICE;
+
+//
+//struct to describe the control bits of the device
+//true indicates enabled
+//false indicates disabled
+// 
+typedef struct {
+  BOOLEAN    HardwareFlowControl;
+  BOOLEAN    DtrState;
+  BOOLEAN    RtsState;
+  BOOLEAN    HardwareLoopBack;
+  BOOLEAN    SoftwareLoopBack;
+} CONTROL_BITS;
+
+//
+//struct to describe the status bits of the device 
+//true indicates enabled
+//false indicated disabled
+//
+typedef struct {
+  BOOLEAN    CtsState;
+  BOOLEAN    DsrState;
+  BOOLEAN    RiState;
+  BOOLEAN    SdState;
+} STATUS_BITS;
+
+//
+// Structure to describe the last attributes of the Usb Serial device
+//
+typedef struct {
+  UINT64              BaudRate;
+  UINT32              ReceiveFifoDepth;
+  UINT32              Timeout;
+  EFI_PARITY_TYPE     Parity;
+  UINT8               DataBits;
+  EFI_STOP_BITS_TYPE  StopBits;
+} PREVIOUS_ATTRIBUTES;
+
+//
+// Structure to describe USB serial device
+//
+#define USB_SER_DEV_SIGNATURE  SIGNATURE_32 ('u', 's', 'b', 's')
+
+typedef struct {
+  UINTN                         Signature;
+  EFI_HANDLE                    ControllerHandle;
+  EFI_DEVICE_PATH_PROTOCOL      *DevicePath;
+  EFI_DEVICE_PATH_PROTOCOL      *ParentDevicePath;
+  UART_DEVICE_PATH              UartDevicePath;
+  UART_FLOW_CONTROL_DEVICE_PATH FlowControlDevicePath;
+  EFI_USB_IO_PROTOCOL           *UsbIo;
+  EFI_USB_INTERFACE_DESCRIPTOR  InterfaceDescriptor;
+  EFI_USB_ENDPOINT_DESCRIPTOR   InEndpointDescriptor;
+  EFI_USB_ENDPOINT_DESCRIPTOR   OutEndpointDescriptor;
+  EFI_UNICODE_STRING_TABLE      *ControllerNameTable;
+  UINT32                        DataBufferHead;
+  UINT32                        DataBufferTail;
+  UINT8                         *DataBuffer;
+  EFI_SERIAL_IO_PROTOCOL        SerialIo;
+  BOOLEAN                       Shutdown;
+  EFI_EVENT                     PollingLoop;
+  UINT32                        ControlBits;
+  PREVIOUS_ATTRIBUTES           LastSettings;
+  CONTROL_BITS                  ControlValues;
+  STATUS_BITS                   StatusValues;
+  UINT8                         ReadBuffer[512];
+} USB_SER_DEV;
+
+#define USB_SER_DEV_FROM_THIS(a) \
+  CR(a, USB_SER_DEV, SerialIo, USB_SER_DEV_SIGNATURE)
+
+//
+// Global Variables
+//
+extern EFI_DRIVER_BINDING_PROTOCOL   gUsbSerialDriverBinding;
+extern EFI_COMPONENT_NAME_PROTOCOL   gUsbSerialComponentName;
+extern EFI_COMPONENT_NAME2_PROTOCOL  gUsbSerialComponentName2;
+
+//
+// Functions of Driver Binding Protocol
+//
+/**
+  Check whether USB Serial driver supports this device.
+
+  @param  This[in]                   The USB Serial driver binding protocol.
+  @param  Controller[in]             The controller handle to check.
+  @param  RemainingDevicePath[in]    The remaining device path.
+
+  @retval EFI_SUCCESS                The driver supports this controller.
+  @retval other                      This device isn't supported.
+
+**/
+EFI_STATUS
+EFIAPI
+UsbSerialDriverBindingSupported (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   Controller,
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
+  );
+
+/**
+  Starts the Serial device with this driver.
+
+  This function produces Serial IO Protocol and initializes the USB
+  Serial device to manage this USB Serial device.
+
+  @param  This[in]                   The USB Serial driver binding instance.
+  @param  Controller[in]             Handle of device to bind driver to.
+  @param  RemainingDevicePath[in]    Optional parameter use to pick a specific
+                                     child device to start.
+
+  @retval EFI_SUCCESS                The controller is controlled by the USB
+                                     Serial driver.
+  @retval EFI_UNSUPPORTED            No interrupt endpoint can be found.
+  @retval Other                      This controller cannot be started.
+
+**/
+EFI_STATUS
+EFIAPI
+UsbSerialDriverBindingStart (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   Controller,
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
+  );
+
+/**
+  Stop the USB Serial device handled by this driver.
+
+  @param  This[in]                   The USB Serial driver binding protocol.
+  @param  Controller[in]             The controller to release.
+  @param  NumberOfChildren[in]       The number of handles in ChildHandleBuffer.
+  @param  ChildHandleBuffer[in]      The array of child handle.
+
+  @retval EFI_SUCCESS                The device was stopped.
+  @retval EFI_UNSUPPORTED            Simple Text In Protocol or Simple Text In Ex
+                                     Protocol is not installed on Controller.
+  @retval EFI_DEVICE_ERROR           The device could not be stopped due to a
+                                     device error.
+  @retval Others                     Fail to uninstall protocols attached on the
+                                     device.
+
+**/
+EFI_STATUS
+EFIAPI
+UsbSerialDriverBindingStop (
+  IN  EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN  EFI_HANDLE                   Controller,
+  IN  UINTN                        NumberOfChildren,
+  IN  EFI_HANDLE                   *ChildHandleBuffer
+  );
+
+//
+// Serial IO Member Functions
+//
+
+/**
+  Writes data to a serial device.
+
+  @param  This[in]                   Protocol instance pointer.
+  @param  BufferSize[in, out]        On input, the size of the Buffer. On output,
+                                     the amount of data actually written.
+  @param  Buffer[in]                 The buffer of data to write
+
+  @retval EFI_SUCCESS                The data was written.
+  @retval EFI_DEVICE_ERROR           The device reported an error.
+  @retval EFI_TIMEOUT                The data write was stopped due to a timeout.
+
+**/
+EFI_STATUS
+EFIAPI
+WriteSerialIo (
+  IN EFI_SERIAL_IO_PROTOCOL  *This,
+  IN OUT UINTN               *BufferSize,
+  IN VOID                    *Buffer
+  );
+
+/**
+  Reads data from a serial device.
+
+  @param  This[in]                   Protocol instance pointer.
+  @param  BufferSize[in, out]        On input, the size of the Buffer. On output,
+                                     the amount of data returned in Buffer.
+  @param  Buffer[out]                The buffer to return the data into.
+
+  @retval EFI_SUCCESS                The data was read.
+  @retval EFI_DEVICE_ERROR           The device reported an error.
+  @retval EFI_TIMEOUT                The data write was stopped due to a timeout.
+
+**/
+EFI_STATUS
+EFIAPI
+ReadSerialIo (
+  IN EFI_SERIAL_IO_PROTOCOL  *This,
+  IN OUT UINTN               *BufferSize,
+  OUT VOID                   *Buffer
+  );
+
+/**
+  Retrieves the status of the control bits on a serial device.
+
+  @param  This[in]               Protocol instance pointer.
+  @param  Control[out]           A pointer to return the current Control signals
+                                 from the serial device.
+
+  @retval EFI_SUCCESS            The control bits were read from the serial
+                                 device.
+  @retval EFI_DEVICE_ERROR       The serial device is not functioning correctly.
+
+**/
+EFI_STATUS
+EFIAPI
+GetControlBits (
+  IN EFI_SERIAL_IO_PROTOCOL  *This,
+  OUT UINT32                 *Control
+  );
+
+/**
+  Set the control bits on a serial device.
+
+  @param  This[in]             Protocol instance pointer.
+  @param  Control[in]          Set the bits of Control that are settable.
+
+  @retval EFI_SUCCESS          The new control bits were set on the serial device.
+  @retval EFI_UNSUPPORTED      The serial device does not support this operation.
+  @retval EFI_DEVICE_ERROR     The serial device is not functioning correctly.
+
+**/
+EFI_STATUS
+EFIAPI
+SetControlBits (
+  IN EFI_SERIAL_IO_PROTOCOL  *This,
+  IN UINT32                  Control
+  );
+
+/**
+  Calls SetAttributesInternal() to set the baud rate, receive FIFO depth,
+  transmit/receice time out, parity, data buts, and stop bits on a serial device.
+
+  @param  This[in]             Protocol instance pointer.
+  @param  BaudRate[in]         The requested baud rate. A BaudRate value of 0
+                               will use the device's default interface speed.
+  @param  ReveiveFifoDepth[in] The requested depth of the FIFO on the receive
+                               side of the serial interface. A ReceiveFifoDepth
+                               value of 0 will use the device's default FIFO
+                               depth.
+  @param  Timeout[in]          The requested time out for a single character in
+                               microseconds.This timeout applies to both the
+                               transmit and receive side of the interface.A
+                               Timeout value of 0 will use the device's default
+                               time out value.
+  @param  Parity[in]           The type of parity to use on this serial device.A
+                               Parity value of DefaultParity will use the
+                               device's default parity value.
+  @param  DataBits[in]         The number of data bits to use on the serial
+                               device. A DataBits value of 0 will use the
+                               device's default data bit setting.
+  @param  StopBits[in]         The number of stop bits to use on this serial
+                               device. A StopBits value of DefaultStopBits will
+                               use the device's default number of stop bits.
+
+  @retval EFI_SUCCESS          The attributes were set
+  @retval EFI_DEVICE_ERROR     The attributes were not able to be
+
+**/
+EFI_STATUS
+EFIAPI
+SetAttributes (
+  IN EFI_SERIAL_IO_PROTOCOL  *This,
+  IN UINT64                  BaudRate,
+  IN UINT32                  ReceiveFifoDepth,
+  IN UINT32                  Timeout,
+  IN EFI_PARITY_TYPE         Parity,
+  IN UINT8                   DataBits,
+  IN EFI_STOP_BITS_TYPE      StopBits
+  );
+
+/**
+  Reset the serial device.
+
+  @param  This              Protocol instance pointer.
+
+  @retval EFI_SUCCESS       The device was reset.
+  @retval EFI_DEVICE_ERROR  The serial device could not be reset.
+
+**/
+EFI_STATUS
+EFIAPI
+SerialReset (
+  IN EFI_SERIAL_IO_PROTOCOL  *This
+  );
+
+//
+// EFI Component Name Functions
+//
+
+/**
+  Retrieves a Unicode string that is the user readable name of the driver.
+
+  This function retrieves the user readable name of a driver in the form of a
+  Unicode string. If the driver specified by This has a user readable name in
+  the language specified by Language, then a pointer to the driver name is
+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+  by This does not support the language specified by Language,
+  then EFI_UNSUPPORTED is returned.
+
+  @param  This[in]                   A pointer to the EFI_COMPONENT_NAME2_PROTOCOL
+                                     or EFI_COMPONENT_NAME_PROTOCOL instance.
+  @param  Language[in]               A pointer to a Null-terminated ASCII string
+                                     array indicating the language. This is the
+                                     language of the driver name that the caller
+                                     is requesting, and it must match one of the
+                                     languages specified in SupportedLanguages.
+                                     The number of languages supported by a
+                                     driver is up to the driver writer. Language
+                                     is specified in RFC 4646 or ISO 639-2
+                                     language code format.
+  @param  DriverName[out]            A pointer to the Unicode string to return.
+                                     This Unicode string is the name of the
+                                     driver specified by This in the language
+                                     specified by Language.
+
+  @retval EFI_SUCCESS                The Unicode string for the Driver specified
+                                     by This and the language specified by
+                                     Language was returned in DriverName.
+  @retval EFI_INVALID_PARAMETER      Language is NULL.
+  @retval EFI_INVALID_PARAMETER      DriverName is NULL.
+  @retval EFI_UNSUPPORTED            The driver specified by This does not
+                                     support the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+UsbSerialComponentNameGetDriverName (
+  IN  EFI_COMPONENT_NAME2_PROTOCOL  *This,
+  IN  CHAR8                         *Language,
+  OUT CHAR16                        **DriverName
+  );
+
+/**
+  Retrieves a Unicode string that is the user readable name of the controller
+  that is being managed by a driver.
+
+  This function retrieves the user readable name of the controller specified by
+  ControllerHandle and ChildHandle in the form of a Unicode string. If the
+  driver specified by This has a user readable name in the language specified by
+  Language, then a pointer to the controller name is returned in ControllerName,
+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently
+  managing the controller specified by ControllerHandle and ChildHandle,
+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not
+  support the language specified by Language, then EFI_UNSUPPORTED is returned.
+
+  @param  This[in]                   A pointer to the EFI_COMPONENT_NAME2_PROTOCOL
+                                     or EFI_COMPONENT_NAME_PROTOCOL instance.
+  @param  ControllerHandle[in]       The handle of a controller that the driver
+                                     specified by This is managing.  This handle
+                                     specifies the controller whose name is to
+                                     be returned.
+  @param  ChildHandle[in]            The handle of the child controller to
+                                     retrieve the name of. This is an optional
+                                     parameter that may be NULL. It will be NULL
+                                     for device drivers. It will also be NULL
+                                     for a bus drivers that wish to retrieve the
+                                     name of the bus controller. It will not be
+                                     NULL for a bus driver that wishes to
+                                     retrieve the name of a child controller.
+  @param  Language[in]               A pointer to a Null-terminated ASCII string
+                                     array indicating the language.  This is the
+                                     language of the driver name that the caller
+                                     is requesting, and it must match one of the
+                                     languages specified in SupportedLanguages.
+                                     The number of languages supported by a
+                                     driver is up to the driver writer. Language
+                                     is specified in RFC 4646 or ISO 639-2
+                                     language code format.
+  @param  ControllerName[out]        A pointer to the Unicode string to return.
+                                     This Unicode string is the name of the
+                                     controller specified by ControllerHandle
+                                     and ChildHandle in the language specified
+                                     by Language from the point of view of the
+                                     driver specified by This.
+
+  @retval EFI_SUCCESS                The Unicode string for the user readable
+                                     name in the language specified by Language
+                                     for the driver specified by This was
+                                     returned in DriverName.
+  @retval EFI_INVALID_PARAMETER      ControllerHandle is not a valid EFI_HANDLE.
+  @retval EFI_INVALID_PARAMETER      ChildHandle is not NULL and it is not a
+                                     valid EFI_HANDLE.
+  @retval EFI_INVALID_PARAMETER      Language is NULL.
+  @retval EFI_INVALID_PARAMETER      ControllerName is NULL.
+  @retval EFI_UNSUPPORTED            The driver specified by This is not
+                                     currently managing the controller specified
+                                     by ControllerHandle and ChildHandle.
+  @retval EFI_UNSUPPORTED            The driver specified by This does not
+                                     support the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+UsbSerialComponentNameGetControllerName (
+  IN  EFI_COMPONENT_NAME2_PROTOCOL  *This,
+  IN  EFI_HANDLE                    ControllerHandle,
+  IN  EFI_HANDLE                    ChildHandle      OPTIONAL,
+  IN  CHAR8                         *Language,
+  OUT CHAR16                        **ControllerName
+  );
+
+#endif
diff --git a/Drivers/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/FtdiUsbSerialDxe.inf b/Drivers/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/FtdiUsbSerialDxe.inf
new file mode 100644
index 0000000000..67c1d36470
--- /dev/null
+++ b/Drivers/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/FtdiUsbSerialDxe.inf
@@ -0,0 +1,55 @@
+## @file
+#  USB Serial Driver that manages USB Serial device and produces Serial IO
+#  Protocol.
+#
+#  USB Serial Driver consumes USB I/O Protocol and Device Path Protocol, and
+#  produces Serial IO Protocol on USB Serial devices.
+#  It manages the USB Serial device via USB Bulk Transfer of USB I/O Protocol.
+#  This module refers to following specifications:
+#  1. UEFI Specification, v2.1
+#
+# Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = FtdiUsbSerialDxe
+  FILE_GUID                      = A8154B55-2021-4D40-AE81-2E23A02dCC46
+  MODULE_TYPE                    = UEFI_DRIVER
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = FtdiUsbSerialEntryPoint
+  UNLOAD_IMAGE                   = FtdiUsbSerialUnload
+
+#
+#  VALID_ARCHITECTURES           = IA32 X64 EBC
+#
+
+[Sources]
+  FtdiUsbSerialDriver.c
+  FtdiUsbSerialDriver.h
+  ComponentName.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+
+[LibraryClasses]
+  UefiDriverEntryPoint
+  BaseMemoryLib
+  DebugLib
+  MemoryAllocationLib
+  UefiBootServicesTableLib
+  UefiLib
+  DevicePathLib
+
+[Guids]
+  gEfiUartDevicePathGuid
+
+[Protocols]
+  ## TO_START
+  ## BY_START
+  gEfiDevicePathProtocolGuid
+  gEfiUsbIoProtocolGuid                         ## TO_START
+  gEfiSerialIoProtocolGuid                      ## BY_START
diff --git a/Drivers/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/ReadMe.txt b/Drivers/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/ReadMe.txt
new file mode 100644
index 0000000000..d8ca227a41
--- /dev/null
+++ b/Drivers/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/ReadMe.txt
@@ -0,0 +1,32 @@
+
+=== FTDI USB SERIAL OVERVIEW ===
+
+This is a bus driver that enables the EfiSerialIoProtocol interface
+for FTDI8U232AM based USB-to-Serial adapters.
+
+=== STATUS ===
+
+Serial Input: Functional on real hardware.
+Serial Output: Functional on real hardware.
+
+Operating Modes: Currently the user is able to change all operating modes
+except timeout and FIFO depth.
+The default operating mode is:
+	Baudrate:     115200
+	Parity:       None
+	Flow Control: None
+	Data Bits:    8
+	Stop Bits:    1
+Notes: 
+	Data Bits setting of 6,7,8 can not be combined with a Stop Bits setting of 1.5
+
+        At baudrates less than 9600 some of the characters may be transmitted incorrectly.
+
+=== COMPATIBILITY ===
+
+Tested with:
+An FTDI8U232AM based USB-To-Serial adapter, the UEFI Shell, and the SerialTest application 
+using a PuTTY Terminal
+
+See CompatibleDevices.txt for a list of devices which have been confirmed to work with this 
+driver.
\ No newline at end of file
diff --git a/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772/Ax88772.c b/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772/Ax88772.c
new file mode 100644
index 0000000000..c9329f506d
--- /dev/null
+++ b/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772/Ax88772.c
@@ -0,0 +1,1318 @@
+/** @file
+  Implement the interface to the AX88772 Ethernet controller.
+
+  This module implements the interface to the ASIX AX88772
+  USB to Ethernet MAC with integrated 10/100 PHY.  Note that this implementation
+  only supports the integrated PHY since no other test cases were available.
+
+  Copyright (c) 2011, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "Ax88772.h"
+
+
+/**
+  Compute the CRC
+
+  @param [in] pMacAddress      Address of a six byte buffer to containing the MAC address.
+
+  @returns The CRC-32 value associated with this MAC address
+
+**/
+UINT32
+Ax88772Crc (
+  IN UINT8 * pMacAddress
+  )
+{
+  UINT32 BitNumber;
+  INT32 Carry;
+  INT32 Crc;
+  UINT32 Data;
+  UINT8 * pEnd;
+
+  DBG_ENTER ( );
+
+  //
+  //  Walk the MAC address
+  //
+  Crc = -1;
+  pEnd = &pMacAddress[ PXE_HWADDR_LEN_ETHER ];
+  while ( pEnd > pMacAddress ) {
+    Data = *pMacAddress++;
+    
+    
+    //
+    //  CRC32: x32 + x26 + x23 + x22 + x16 + x12 + x11 + x10 + x8 + x7 + x5 + x4 + x2 + x + 1
+    //
+    //          1 0000 0100 1100 0001 0001 1101 1011 0111
+    //
+    for ( BitNumber = 0; 8 > BitNumber; BitNumber++ ) {
+      Carry = (( Crc >> 31 ) & 1 ) ^ ( Data & 1 );
+      Crc <<= 1;
+      if ( 0 != Carry ) {
+        Crc ^= 0x04c11db7;
+      }
+      Data >>= 1;
+    }
+  }
+
+  //
+  //  Return the CRC value
+  //
+  DBG_EXIT_HEX ( Crc );
+  return (UINT32) Crc;
+}
+
+
+/**
+  Get the MAC address
+
+  This routine calls ::Ax88772UsbCommand to request the MAC
+  address from the network adapter.
+
+  @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
+  @param [out] pMacAddress      Address of a six byte buffer to receive the MAC address.
+
+  @retval EFI_SUCCESS          The MAC address is available.
+  @retval other                The MAC address is not valid.
+
+**/
+EFI_STATUS
+Ax88772MacAddressGet (
+  IN NIC_DEVICE * pNicDevice,
+  OUT UINT8 * pMacAddress
+  )
+{
+  USB_DEVICE_REQUEST SetupMsg;
+  EFI_STATUS Status;
+  
+  DBG_ENTER ( );
+  
+  //
+  //  Set the register address.
+  //
+  SetupMsg.RequestType = USB_ENDPOINT_DIR_IN
+                       | USB_REQ_TYPE_VENDOR
+                       | USB_TARGET_DEVICE;
+  SetupMsg.Request = CMD_MAC_ADDRESS_READ;
+  SetupMsg.Value = 0;
+  SetupMsg.Index = 0;
+  SetupMsg.Length = PXE_HWADDR_LEN_ETHER;
+
+  //
+  //  Read the PHY register
+  //
+  Status = Ax88772UsbCommand ( pNicDevice,
+                               &SetupMsg,
+                               pMacAddress );
+
+  //
+  // Return the operation status
+  //
+  DBG_EXIT_STATUS ( Status );
+  return Status;
+}
+
+
+/**
+  Set the MAC address
+
+  This routine calls ::Ax88772UsbCommand to set the MAC address
+  in the network adapter.
+
+  @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
+  @param [in] pMacAddress      Address of a six byte buffer to containing the new MAC address.
+
+  @retval EFI_SUCCESS          The MAC address was set.
+  @retval other                The MAC address was not set.
+
+**/
+EFI_STATUS
+Ax88772MacAddressSet (
+  IN NIC_DEVICE * pNicDevice,
+  IN UINT8 * pMacAddress
+  )
+{
+  USB_DEVICE_REQUEST SetupMsg;
+  EFI_STATUS Status;
+  
+  DBG_ENTER ( );
+  
+  //
+  //  Set the register address.
+  //
+  SetupMsg.RequestType = USB_REQ_TYPE_VENDOR
+                       | USB_TARGET_DEVICE;
+  SetupMsg.Request = CMD_MAC_ADDRESS_WRITE;
+  SetupMsg.Value = 0;
+  SetupMsg.Index = 0;
+  SetupMsg.Length = PXE_HWADDR_LEN_ETHER;
+  
+  //
+  //  Read the PHY register
+  //
+  Status = Ax88772UsbCommand ( pNicDevice,
+                               &SetupMsg,
+                               pMacAddress );
+  
+  //
+  // Return the operation status
+  //
+  DBG_EXIT_STATUS ( Status );
+  return Status;
+}
+
+
+/**
+  Clear the multicast hash table
+
+  @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
+
+**/
+VOID
+Ax88772MulticastClear (
+  IN NIC_DEVICE * pNicDevice
+  )
+{
+  DBG_ENTER ( );
+
+  //
+  // Clear the multicast hash table
+  //
+  pNicDevice->MulticastHash[0] = 0;
+  pNicDevice->MulticastHash[1] = 0;
+
+  DBG_EXIT ( );
+}
+
+
+/**
+  Enable a multicast address in the multicast hash table
+
+  This routine calls ::Ax88772Crc to compute the hash bit for
+  this MAC address.
+
+  @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
+  @param [in] pMacAddress      Address of a six byte buffer to containing the MAC address.
+
+**/
+VOID
+Ax88772MulticastSet (
+  IN NIC_DEVICE * pNicDevice,
+  IN UINT8 * pMacAddress
+  )
+{
+  UINT32 BitNumber;
+  UINT32 Crc;
+  UINT32 Mask;
+
+  DBG_ENTER ( );
+
+  //
+  //  Compute the CRC on the destination address
+  //
+  Crc = Ax88772Crc ( pMacAddress );
+
+  //
+  //  Set the bit corresponding to the destination address
+  //
+  BitNumber = Crc >> 26;
+  if ( 32 > BitNumber ) {
+    Mask = 1 << BitNumber;
+    pNicDevice->MulticastHash[0] |= Mask;
+  }
+  else {
+    Mask = 1 << ( BitNumber - 32 );
+    pNicDevice->MulticastHash[1] |= Mask;
+  }
+
+  //
+  //  Display the multicast address
+  //
+  DEBUG (( DEBUG_RX_MULTICAST | DEBUG_INFO,
+            "Enable multicast: 0x%02x-%02x-%02x-%02x-%02x-%02x, CRC: 0x%08x, Bit number: 0x%02x\r\n",
+            pMacAddress[0],
+            pMacAddress[1],
+            pMacAddress[2],
+            pMacAddress[3],
+            pMacAddress[4],
+            pMacAddress[5],
+            Crc,
+            BitNumber ));
+
+  DBG_EXIT ( );
+}
+
+
+/**
+  Start the link negotiation
+
+  This routine calls ::Ax88772PhyWrite to start the PHY's link
+  negotiation.
+
+  @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
+
+  @retval EFI_SUCCESS          The link negotiation was started.
+  @retval other                Failed to start the link negotiation.
+
+**/
+EFI_STATUS
+Ax88772NegotiateLinkStart (
+  IN NIC_DEVICE * pNicDevice
+  )
+{
+  UINT16 Control;
+  EFI_STATUS Status;
+
+  DBG_ENTER ( );
+
+  //
+  // Set the supported capabilities.
+  //
+  Status = Ax88772PhyWrite ( pNicDevice,
+                             PHY_ANAR,
+                             AN_CSMA_CD
+                             | AN_TX_FDX | AN_TX_HDX
+                             | AN_10_FDX | AN_10_HDX );
+  if ( !EFI_ERROR ( Status )) {
+    //
+    // Set the link speed and duplex
+    //
+    Control = BMCR_AUTONEGOTIATION_ENABLE
+            | BMCR_RESTART_AUTONEGOTIATION;
+    if ( pNicDevice->b100Mbps ) {
+      Control |= BMCR_100MBPS;
+    }
+    if ( pNicDevice->bFullDuplex ) {
+      Control |= BMCR_FULL_DUPLEX;
+    }
+    Status = Ax88772PhyWrite ( pNicDevice, PHY_BMCR, Control );
+  }
+
+  //
+  // Return the operation status
+  //
+  DBG_EXIT_STATUS ( Status );
+  return Status;
+}
+
+
+/**
+  Complete the negotiation of the PHY link
+
+  This routine calls ::Ax88772PhyRead to determine if the
+  link negotiation is complete.
+
+  @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
+  @param [in, out] pPollCount  Address of number of times this routine was polled
+  @param [out] pbComplete      Address of boolean to receive complate status.
+  @param [out] pbLinkUp        Address of boolean to receive link status, TRUE=up.
+  @param [out] pbHiSpeed       Address of boolean to receive link speed, TRUE=100Mbps.
+  @param [out] pbFullDuplex    Address of boolean to receive link duplex, TRUE=full.
+
+  @retval EFI_SUCCESS          The MAC address is available.
+  @retval other                The MAC address is not valid.
+
+**/
+EFI_STATUS
+Ax88772NegotiateLinkComplete (
+  IN NIC_DEVICE * pNicDevice,
+  IN OUT UINTN * pPollCount,
+  OUT BOOLEAN * pbComplete,
+  OUT BOOLEAN * pbLinkUp,
+  OUT BOOLEAN * pbHiSpeed,
+  OUT BOOLEAN * pbFullDuplex
+  )
+{
+  UINT16 Mask;
+  UINT16 PhyData;
+  EFI_STATUS  Status;
+
+  DBG_ENTER ( );
+  
+  //
+  //  Determine if the link is up.
+  //
+  *pbComplete = FALSE;
+
+  //
+  //  Get the link status
+  //
+  Status = Ax88772PhyRead ( pNicDevice,
+                            PHY_BMSR,
+                            &PhyData );
+  if ( !EFI_ERROR ( Status )) {
+    //
+    //  Determine if the autonegotiation is complete.
+    //
+    *pbLinkUp = (BOOLEAN)( 0 != ( PhyData & BMSR_LINKST ));
+    *pbComplete = *pbLinkUp;
+    if ( 0 != *pbComplete ) {
+      //
+      //  Get the partners capabilities.
+      //
+      Status = Ax88772PhyRead ( pNicDevice,
+                                PHY_ANLPAR,
+                                &PhyData );
+      if ( !EFI_ERROR ( Status )) {
+        //
+        //  Autonegotiation is complete
+        //  Determine the link speed.
+        //
+        *pbHiSpeed = (BOOLEAN)( 0 != ( PhyData & ( AN_TX_FDX | AN_TX_HDX )));
+
+        //
+        //  Determine the link duplex.
+        //
+        Mask = ( *pbHiSpeed ) ? AN_TX_FDX : AN_10_FDX;
+        *pbFullDuplex = (BOOLEAN)( 0 != ( PhyData & Mask ));
+      }
+    }
+  }
+
+  //
+  // Return the operation status
+  //
+  DBG_EXIT_STATUS ( Status );
+  return Status;
+}
+
+
+/**
+  Read a register from the PHY
+
+  This routine calls ::Ax88772UsbCommand to read a PHY register.
+
+  @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
+  @param [in] RegisterAddress  Number of the register to read.
+  @param [in, out] pPhyData    Address of a buffer to receive the PHY register value
+
+  @retval EFI_SUCCESS          The PHY data is available.
+  @retval other                The PHY data is not valid.
+
+**/
+EFI_STATUS
+Ax88772PhyRead (
+  IN NIC_DEVICE * pNicDevice,
+  IN UINT8 RegisterAddress,
+  IN OUT UINT16 * pPhyData
+  )
+{
+  USB_DEVICE_REQUEST SetupMsg;
+  EFI_STATUS Status;
+
+  DBG_ENTER ( );
+
+  //
+  //  Request access to the PHY
+  //
+  SetupMsg.RequestType = USB_REQ_TYPE_VENDOR
+                       | USB_TARGET_DEVICE;
+  SetupMsg.Request = CMD_PHY_ACCESS_SOFTWARE;
+  SetupMsg.Value = 0;
+  SetupMsg.Index = 0;
+  SetupMsg.Length = 0;
+  Status = Ax88772UsbCommand ( pNicDevice,
+                               &SetupMsg,
+                               NULL );
+  if ( !EFI_ERROR ( Status )) {
+    //
+    //  Read the PHY register address.
+    //
+    SetupMsg.RequestType = USB_ENDPOINT_DIR_IN
+                         | USB_REQ_TYPE_VENDOR
+                         | USB_TARGET_DEVICE;
+    SetupMsg.Request = CMD_PHY_REG_READ;
+    SetupMsg.Value = pNicDevice->PhyId;
+    SetupMsg.Index = RegisterAddress;
+    SetupMsg.Length = sizeof ( *pPhyData );
+    Status = Ax88772UsbCommand ( pNicDevice,
+                                 &SetupMsg,
+                                 pPhyData );
+    if ( !EFI_ERROR ( Status )) {
+      DEBUG (( DEBUG_PHY | DEBUG_INFO,
+                "PHY %d: 0x%02x --> 0x%04x\r\n",
+                pNicDevice->PhyId,
+                RegisterAddress,
+                *pPhyData ));
+
+      //
+      //  Release the PHY to the hardware
+      //
+      SetupMsg.RequestType = USB_REQ_TYPE_VENDOR
+                           | USB_TARGET_DEVICE;
+      SetupMsg.Request = CMD_PHY_ACCESS_HARDWARE;
+      SetupMsg.Value = 0;
+      SetupMsg.Index = 0;
+      SetupMsg.Length = 0;
+      Status = Ax88772UsbCommand ( pNicDevice,
+                                   &SetupMsg,
+                                   NULL );
+    }
+  }
+
+  //
+  //  Return the operation status.
+  //
+  DBG_EXIT_STATUS ( Status );
+  return Status;
+}
+
+
+/**
+  Write to a PHY register
+
+  This routine calls ::Ax88772UsbCommand to write a PHY register.
+
+  @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
+  @param [in] RegisterAddress  Number of the register to read.
+  @param [in] PhyData          Address of a buffer to receive the PHY register value
+
+  @retval EFI_SUCCESS          The PHY data was written.
+  @retval other                Failed to wwrite the PHY register.
+
+**/
+EFI_STATUS
+Ax88772PhyWrite (
+  IN NIC_DEVICE * pNicDevice,
+  IN UINT8 RegisterAddress,
+  IN UINT16 PhyData
+  )
+{
+  USB_DEVICE_REQUEST SetupMsg;
+  EFI_STATUS Status;
+  
+  DBG_ENTER ( );
+  
+  //
+  //  Request access to the PHY
+  //
+  SetupMsg.RequestType = USB_REQ_TYPE_VENDOR
+                       | USB_TARGET_DEVICE;
+  SetupMsg.Request = CMD_PHY_ACCESS_SOFTWARE;
+  SetupMsg.Value = 0;
+  SetupMsg.Index = 0;
+  SetupMsg.Length = 0;
+  Status = Ax88772UsbCommand ( pNicDevice,
+                               &SetupMsg,
+                               NULL );
+  if ( !EFI_ERROR ( Status )) {
+    //
+    //  Write the PHY register
+    //
+    SetupMsg.RequestType = USB_REQ_TYPE_VENDOR
+                         | USB_TARGET_DEVICE;
+    SetupMsg.Request = CMD_PHY_REG_WRITE;
+    SetupMsg.Value = pNicDevice->PhyId;
+    SetupMsg.Index = RegisterAddress;
+    SetupMsg.Length = sizeof ( PhyData );
+    Status = Ax88772UsbCommand ( pNicDevice,
+                                 &SetupMsg,
+                                 &PhyData );
+    if ( !EFI_ERROR ( Status )) {
+      DEBUG (( DEBUG_PHY | DEBUG_INFO,
+                "PHY %d: 0x%02x <-- 0x%04x\r\n",
+                pNicDevice->PhyId,
+                RegisterAddress,
+                PhyData ));
+
+      //
+      //  Release the PHY to the hardware
+      //
+      SetupMsg.RequestType = USB_REQ_TYPE_VENDOR
+                           | USB_TARGET_DEVICE;
+      SetupMsg.Request = CMD_PHY_ACCESS_HARDWARE;
+      SetupMsg.Value = 0;
+      SetupMsg.Index = 0;
+      SetupMsg.Length = 0;
+      Status = Ax88772UsbCommand ( pNicDevice,
+                                   &SetupMsg,
+                                   NULL );
+    }
+  }
+
+  //
+  //  Return the operation status.
+  //
+  DBG_EXIT_STATUS ( Status );
+  return Status;
+}
+
+
+/**
+  Reset the AX88772
+
+  This routine uses ::Ax88772UsbCommand to reset the network
+  adapter.  This routine also uses ::Ax88772PhyWrite to reset
+  the PHY.
+
+  @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
+
+  @retval EFI_SUCCESS          The MAC address is available.
+  @retval other                The MAC address is not valid.
+
+**/
+EFI_STATUS
+Ax88772Reset (
+  IN NIC_DEVICE * pNicDevice
+  )
+{
+  USB_DEVICE_REQUEST SetupMsg;
+  EFI_STATUS Status;
+  
+  DBG_ENTER ( );
+
+  //
+  //  Turn off the MAC
+  //
+  SetupMsg.RequestType = USB_REQ_TYPE_VENDOR
+                       | USB_TARGET_DEVICE;
+  SetupMsg.Request = CMD_RX_CONTROL_WRITE;
+  SetupMsg.Value = 0;
+  SetupMsg.Index = 0;
+  SetupMsg.Length = 0;
+  Status = Ax88772UsbCommand ( pNicDevice,
+                               &SetupMsg,
+                               NULL );
+  if ( !EFI_ERROR ( Status )) {
+    DEBUG (( DEBUG_PHY | DEBUG_RX_BROADCAST | DEBUG_RX_MULTICAST
+              | DEBUG_RX_UNICAST | DEBUG_TX | DEBUG_INFO,
+              "MAC reset\r\n" ));
+
+    //
+    //  The link is now idle
+    //
+    pNicDevice->bLinkIdle = TRUE;
+
+    //
+    //  Delay for a bit
+    //
+    gBS->Stall ( RESET_MSEC );
+
+    //
+    //  Select the internal PHY
+    //
+    SetupMsg.RequestType = USB_REQ_TYPE_VENDOR
+                         | USB_TARGET_DEVICE;
+    SetupMsg.Request = CMD_PHY_SELECT;
+    SetupMsg.Value = SPHY_PSEL;
+    SetupMsg.Index = 0;
+    SetupMsg.Length = 0;
+    Status = Ax88772UsbCommand ( pNicDevice,
+                                 &SetupMsg,
+                                 NULL );
+    if ( !EFI_ERROR ( Status )) {
+      //
+      //  Delay for a bit
+      //
+      gBS->Stall ( PHY_RESET_MSEC );
+
+      //
+      //  Clear the internal PHY reset
+      //
+      SetupMsg.Request = CMD_RESET;
+      SetupMsg.Value = SRR_IPRL | SRR_PRL;
+      Status = Ax88772UsbCommand ( pNicDevice,
+                                   &SetupMsg,
+                                   NULL );
+      if ( !EFI_ERROR ( Status )) {
+        //
+        //  Reset the PHY
+        //
+        Status = Ax88772PhyWrite ( pNicDevice,
+                                   PHY_BMCR,
+                                   BMCR_RESET );
+        if ( !EFI_ERROR ( Status )) {
+          //
+          //  Set the gaps
+          //
+          SetupMsg.RequestType = USB_REQ_TYPE_VENDOR
+                               | USB_TARGET_DEVICE;
+          SetupMsg.Request = CMD_GAPS_WRITE;
+          SetupMsg.Value = 0x0c15;
+          SetupMsg.Index = 0x0e;
+          SetupMsg.Length = 0;
+          Status = Ax88772UsbCommand ( pNicDevice,
+                                       &SetupMsg,
+                                       NULL );
+        }
+      }
+    }
+  }
+
+  //
+  //  Return the operation status.
+  //
+  DBG_EXIT_STATUS ( Status );
+  return Status;
+}
+
+
+VOID 
+FillPkt2Queue (
+  IN NIC_DEVICE * pNicDevice,
+  IN UINTN BufLength)
+{
+
+  UINT16 * pLength;
+  UINT16 * pLengthBar;
+  UINT8* pData;
+  UINT32 offset;
+  RX_TX_PACKET * pRxPacket;
+  EFI_STATUS Status;
+  
+  for ( offset = 0; offset < BufLength; ){
+    pLength = (UINT16*) (pNicDevice->pBulkInBuff + offset);
+    pLengthBar = (UINT16*) (pNicDevice->pBulkInBuff + offset +2);
+    
+    *pLength &= 0x7ff;
+    *pLengthBar &= 0x7ff;
+    *pLengthBar |= 0xf800;
+      
+    if ((*pLength ^ *pLengthBar ) != 0xFFFF) {
+      DEBUG (( EFI_D_ERROR , "Pkt length error. BufLength = %d\n", BufLength));
+      return;
+    }
+      
+    pRxPacket = pNicDevice->pRxFree;
+    if ( NULL == pRxPacket ) {
+      Status = gBS->AllocatePool ( EfiRuntimeServicesData,
+                                   sizeof( RX_TX_PACKET ),
+                                   (VOID **) &pRxPacket );
+      if ( !EFI_ERROR ( Status )) {
+        //
+        //  Add this packet to the free packet list
+        //
+        pNicDevice->pRxFree = pRxPacket;
+        pRxPacket->pNext = NULL;
+      }
+      else {
+        //
+        //  Use the discard packet buffer
+        //
+        //pRxPacket = &Packet;
+      }
+    }
+      
+
+    pData = pNicDevice->pBulkInBuff + offset + 4;
+    pRxPacket->Length = *pLength;
+    pRxPacket->LengthBar = *(UINT16*) (pNicDevice->pBulkInBuff + offset +2);
+    CopyMem (&pRxPacket->Data[0], pData, *pLength);
+    //DEBUG((DEBUG_INFO, "Packet [%d]\n", *pLength));
+    
+    pNicDevice->pRxFree = pRxPacket->pNext;
+    pRxPacket->pNext = NULL;
+    
+    if ( NULL == pNicDevice->pRxTail ) {
+      pNicDevice->pRxHead = pRxPacket;
+    }
+    else {
+      pNicDevice->pRxTail->pNext = pRxPacket;
+    }
+    pNicDevice->pRxTail = pRxPacket;
+    offset += (*pLength + 4);
+              
+  }
+}
+
+
+
+/**
+  Receive a frame from the network.
+
+  This routine polls the USB receive interface for a packet.  If a packet
+  is available, this routine adds the receive packet to the list of
+  pending receive packets.
+
+  This routine calls ::Ax88772NegotiateLinkComplete to verify
+  that the link is up.  This routine also calls ::SN_Reset to
+  reset the network adapter when necessary.  Finally this
+  routine attempts to receive one or more packets from the
+  network adapter.
+
+  @param [in] pNicDevice  Pointer to the NIC_DEVICE structure
+  @param [in] bUpdateLink TRUE = Update link status
+
+**/
+VOID
+Ax88772Rx (
+  IN NIC_DEVICE * pNicDevice,
+  IN BOOLEAN bUpdateLink
+  )
+{
+  BOOLEAN bFullDuplex;
+  BOOLEAN bLinkUp;
+  BOOLEAN bRxPacket;
+  BOOLEAN bSpeed100;
+  UINTN LengthInBytes;
+  RX_TX_PACKET Packet;
+  RX_TX_PACKET * pRxPacket;
+  EFI_USB_IO_PROTOCOL *pUsbIo;
+  EFI_STATUS Status;
+  EFI_TPL TplPrevious;
+  UINT32 TransferStatus;
+
+  //
+  //  Synchronize with Ax88772Timer
+  //
+  VERIFY_TPL ( TPL_AX88772 );
+  TplPrevious = gBS->RaiseTPL ( TPL_AX88772 );
+  DEBUG (( DEBUG_TPL | DEBUG_INFO,
+            "%d: TPL\r\n",
+            TPL_AX88772 ));
+
+  //
+  //  Get the link status
+  //
+  if ( bUpdateLink ) {
+    bLinkUp = pNicDevice->bLinkUp;
+    bSpeed100 = pNicDevice->b100Mbps;
+    bFullDuplex = pNicDevice->bFullDuplex;
+    Status = Ax88772NegotiateLinkComplete ( pNicDevice,
+                                            &pNicDevice->PollCount,
+                                            &pNicDevice->bComplete,
+                                            &pNicDevice->bLinkUp,
+                                            &pNicDevice->b100Mbps,
+                                            &pNicDevice->bFullDuplex );
+
+    //
+    // Determine if the autonegotiation is complete
+    //
+    if ( pNicDevice->bComplete ) {
+      if ( pNicDevice->bLinkUp ) {
+        if (( bSpeed100 && ( !pNicDevice->b100Mbps ))
+          || (( !bSpeed100 ) && pNicDevice->b100Mbps )
+          || ( bFullDuplex && ( !pNicDevice->bFullDuplex ))
+          || (( !bFullDuplex ) && pNicDevice->bFullDuplex )) {
+          pNicDevice->PollCount = 0;
+          DEBUG (( DEBUG_LINK | DEBUG_INFO,
+                    "Reset to establish proper link setup: %d Mbps, %s duplex\r\n",
+                    pNicDevice->b100Mbps ? 100 : 10,
+                    pNicDevice->bFullDuplex ? L"Full" : L"Half" ));
+          Status = SN_Reset ( &pNicDevice->SimpleNetwork, FALSE );
+        }
+        if (( !bLinkUp ) && pNicDevice->bLinkUp ) {
+          //
+          // Display the autonegotiation status
+          //
+          DEBUG (( DEBUG_LINK | DEBUG_INFO,
+                    "Link: Up, %d Mbps, %s duplex\r\n",
+                    pNicDevice->b100Mbps ? 100 : 10,
+                    pNicDevice->bFullDuplex ? L"Full" : L"Half" ));
+        }
+      }
+    }
+
+    //
+    //  Update the link status
+    //
+    if ( bLinkUp && ( !pNicDevice->bLinkUp )) {
+      DEBUG (( DEBUG_LINK | DEBUG_INFO, "Link: Down\r\n" ));
+    }
+  }
+
+  //
+  //  Loop until all the packets are emptied from the receiver
+  //
+  do {
+    bRxPacket = FALSE;
+
+    //
+    //  Locate a packet for use
+    //
+    pRxPacket = pNicDevice->pRxFree;
+    LengthInBytes = MAX_BULKIN_SIZE;
+    if ( NULL == pRxPacket ) {
+      Status = gBS->AllocatePool ( EfiRuntimeServicesData,
+                                   sizeof ( *pRxPacket ),
+                                   (VOID **) &pRxPacket );
+      if ( !EFI_ERROR ( Status )) {
+        //
+        //  Add this packet to the free packet list
+        //
+        pNicDevice->pRxFree = pRxPacket;
+        pRxPacket->pNext = NULL;
+      }
+      else {
+        //
+        //  Use the discard packet buffer
+        //
+        pRxPacket = &Packet;
+      }
+    }
+
+    //
+    //  Attempt to receive a packet
+    //
+    SetMem (&pNicDevice->pBulkInBuff[0], MAX_BULKIN_SIZE, 0);
+    pUsbIo = pNicDevice->pUsbIo;
+    Status = pUsbIo->UsbBulkTransfer ( pUsbIo,
+                                       USB_ENDPOINT_DIR_IN | BULK_IN_ENDPOINT,
+                                       &pNicDevice->pBulkInBuff[0],
+                                       &LengthInBytes,
+                                       2,
+                                       &TransferStatus );
+    if ( LengthInBytes > 0 ) {
+      FillPkt2Queue(pNicDevice, LengthInBytes);
+    }
+    pRxPacket = pNicDevice->pRxHead;
+    if (( !EFI_ERROR ( Status ))
+      && ( 0 < pRxPacket->Length )
+      && ( pRxPacket->Length <= sizeof ( pRxPacket->Data ))
+      && ( LengthInBytes > 0)) {
+
+      //
+      //  Determine if the packet should be received
+      //
+      bRxPacket = TRUE;
+      LengthInBytes = pRxPacket->Length;
+      pNicDevice->bLinkIdle = FALSE;
+      if ( pNicDevice->pRxFree == pRxPacket ) {
+        //
+        //  Display the received packet
+        //
+        if ( 0 != ( pRxPacket->Data[0] & 1 )) {
+          if (( 0xff == pRxPacket->Data[0])
+            && ( 0xff == pRxPacket->Data[1])
+            && ( 0xff == pRxPacket->Data[2])
+            && ( 0xff == pRxPacket->Data[3])
+            && ( 0xff == pRxPacket->Data[4])
+            && ( 0xff == pRxPacket->Data[5])) {
+            DEBUG (( DEBUG_RX_BROADCAST | DEBUG_INFO,
+                      "RX: %02x-%02x-%02x-%02x-%02x-%02x  %02x-%02x-%02x-%02x-%02x-%02x  %02x-%02x  %d bytes\r\n",
+                      pRxPacket->Data[0],
+                      pRxPacket->Data[1],
+                      pRxPacket->Data[2],
+                      pRxPacket->Data[3],
+                      pRxPacket->Data[4],
+                      pRxPacket->Data[5],
+                      pRxPacket->Data[6],
+                      pRxPacket->Data[7],
+                      pRxPacket->Data[8],
+                      pRxPacket->Data[9],
+                      pRxPacket->Data[10],
+                      pRxPacket->Data[11],
+                      pRxPacket->Data[12],
+                      pRxPacket->Data[13],
+                      LengthInBytes ));
+          }
+          else {
+            DEBUG (( DEBUG_RX_MULTICAST | DEBUG_INFO,
+                      "RX: %02x-%02x-%02x-%02x-%02x-%02x  %02x-%02x-%02x-%02x-%02x-%02x  %02x-%02x  %d bytes\r\n",
+                      pRxPacket->Data[0],
+                      pRxPacket->Data[1],
+                      pRxPacket->Data[2],
+                      pRxPacket->Data[3],
+                      pRxPacket->Data[4],
+                      pRxPacket->Data[5],
+                      pRxPacket->Data[6],
+                      pRxPacket->Data[7],
+                      pRxPacket->Data[8],
+                      pRxPacket->Data[9],
+                      pRxPacket->Data[10],
+                      pRxPacket->Data[11],
+                      pRxPacket->Data[12],
+                      pRxPacket->Data[13],
+                      LengthInBytes ));
+          }
+        }
+        else {
+          DEBUG (( DEBUG_RX_UNICAST | DEBUG_INFO,
+                    "RX: %02x-%02x-%02x-%02x-%02x-%02x  %02x-%02x-%02x-%02x-%02x-%02x  %02x-%02x  %d bytes\r\n",
+                    pRxPacket->Data[0],
+                    pRxPacket->Data[1],
+                    pRxPacket->Data[2],
+                    pRxPacket->Data[3],
+                    pRxPacket->Data[4],
+                    pRxPacket->Data[5],
+                    pRxPacket->Data[6],
+                    pRxPacket->Data[7],
+                    pRxPacket->Data[8],
+                    pRxPacket->Data[9],
+                    pRxPacket->Data[10],
+                    pRxPacket->Data[11],
+                    pRxPacket->Data[12],
+                    pRxPacket->Data[13],
+                    LengthInBytes ));
+        }
+        
+      }
+      else {
+        //
+        //  Error, not enough buffers for this packet, discard packet
+        //
+        DEBUG (( DEBUG_WARN | DEBUG_INFO,
+                  "WARNING - No buffer, discarding RX packet: %02x-%02x-%02x-%02x-%02x-%02x  %02x-%02x-%02x-%02x-%02x-%02x  %02x-%02x  %d bytes\r\n",
+                  pRxPacket->Data[0],
+                  pRxPacket->Data[1],
+                  pRxPacket->Data[2],
+                  pRxPacket->Data[3],
+                  pRxPacket->Data[4],
+                  pRxPacket->Data[5],
+                  pRxPacket->Data[6],
+                  pRxPacket->Data[7],
+                  pRxPacket->Data[8],
+                  pRxPacket->Data[9],
+                  pRxPacket->Data[10],
+                  pRxPacket->Data[11],
+                  pRxPacket->Data[12],
+                  pRxPacket->Data[13],
+                  LengthInBytes ));
+      }
+    }
+  }while ( bRxPacket );
+
+  //
+  //  Release the synchronization withhe Ax88772Timer
+  //
+  gBS->RestoreTPL ( TplPrevious );
+  DEBUG (( DEBUG_TPL | DEBUG_INFO,
+            "%d: TPL\r\n",
+            TplPrevious ));
+}
+
+
+/**
+  Enable or disable the receiver
+
+  This routine calls ::Ax88772UsbCommand to update the
+  receiver state.  This routine also calls ::Ax88772MacAddressSet
+  to establish the MAC address for the network adapter.
+
+  @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
+  @param [in] RxFilter         Simple network RX filter mask value
+
+  @retval EFI_SUCCESS          The MAC address was set.
+  @retval other                The MAC address was not set.
+
+**/
+EFI_STATUS
+Ax88772RxControl (
+  IN NIC_DEVICE * pNicDevice,
+  IN UINT32 RxFilter
+  )
+{
+  UINT16 MediumStatus;
+  INT32 MulticastHash[2];
+  UINT16 RxControl;
+  USB_DEVICE_REQUEST SetupMsg;
+  EFI_STATUS Status;
+
+  DBG_ENTER ( );
+
+  //
+  //  Disable all multicast
+  //
+  MulticastHash[0] = 0;
+  MulticastHash[1] = 0;
+
+  //
+  // Enable the receiver if something is to be received
+  //
+  Status = EFI_SUCCESS;
+  RxControl = RXC_SO | RXC_MFB_16384;
+  if ( 0 != RxFilter ) {
+    //
+    //  Enable the receiver
+    //
+    SetupMsg.RequestType = USB_ENDPOINT_DIR_IN
+                         | USB_REQ_TYPE_VENDOR
+                         | USB_TARGET_DEVICE;
+    SetupMsg.Request = CMD_MEDIUM_STATUS_READ;
+    SetupMsg.Value = 0;
+    SetupMsg.Index = 0;
+    SetupMsg.Length = sizeof ( MediumStatus );
+    Status = Ax88772UsbCommand ( pNicDevice,
+                                 &SetupMsg,
+                                 &MediumStatus );
+    if ( !EFI_ERROR ( Status )) {
+      if ( 0 == ( MediumStatus & MS_RE )) {
+        MediumStatus |= MS_RE | MS_ONE;
+        if ( pNicDevice->bFullDuplex ) {
+          MediumStatus |= MS_TFC | MS_RFC;
+        }
+        SetupMsg.RequestType = USB_REQ_TYPE_VENDOR
+                             | USB_TARGET_DEVICE;
+        SetupMsg.Request = CMD_MEDIUM_STATUS_WRITE;
+        SetupMsg.Value = MediumStatus;
+        SetupMsg.Index = 0;
+        SetupMsg.Length = 0;
+        Status = Ax88772UsbCommand ( pNicDevice,
+                                     &SetupMsg,
+                                     NULL );
+        if ( EFI_ERROR ( Status )) {
+          DEBUG (( DEBUG_ERROR | DEBUG_INFO,
+                    "ERROR - Failed to enable receiver, Status: %r\r\n",
+                    Status ));
+        }
+      }
+    }
+    else {
+      DEBUG (( DEBUG_ERROR | DEBUG_INFO,
+                "ERROR - Failed to read receiver status, Status: %r\r\n",
+                Status ));
+    }
+
+    //
+    //  Enable multicast if requested
+    //
+    if ( 0 != ( RxFilter & EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST )) {
+      RxControl |= RXC_AM;
+      MulticastHash[0] = pNicDevice->MulticastHash[0];
+      MulticastHash[1] = pNicDevice->MulticastHash[1];
+    }
+
+    //
+    //  Enable all multicast if requested
+    //
+    if ( 0 != ( RxFilter & EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST )) {
+      RxControl |= RXC_AMALL;
+      MulticastHash[0] = -1;
+      MulticastHash[1] = -1;
+    }
+
+    //
+    //  Enable broadcast if requested
+    //
+    if ( 0 != ( RxFilter & EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST )) {
+      RxControl |= RXC_AB;
+    }
+
+    //
+    //  Enable promiscuous mode if requested
+    //
+    if ( 0 != ( RxFilter & EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS )) {
+      RxControl |= RXC_PRO;
+      MulticastHash[0] = -1;
+      MulticastHash[1] = -1;
+    }
+  }
+
+  //
+  //  Update the MAC address
+  //
+  if ( !EFI_ERROR ( Status )) {
+    Status = Ax88772MacAddressSet ( pNicDevice, &pNicDevice->SimpleNetworkData.CurrentAddress.Addr[0]);
+  }
+
+  //
+  //  Update the receiver control
+  //
+  if ( !EFI_ERROR ( Status )) {
+    SetupMsg.RequestType = USB_REQ_TYPE_VENDOR
+                         | USB_TARGET_DEVICE;
+    SetupMsg.Request = CMD_RX_CONTROL_WRITE;
+    SetupMsg.Value = RxControl;
+    SetupMsg.Index = 0;
+    SetupMsg.Length = 0;
+    Status = Ax88772UsbCommand ( pNicDevice,
+                                 &SetupMsg,
+                                 NULL );
+    if ( !EFI_ERROR ( Status )) {
+      DEBUG (( DEBUG_RX_BROADCAST | DEBUG_RX_MULTICAST | DEBUG_RX_UNICAST | DEBUG_INFO,
+                "RxControl: 0x%04x\r\n",
+                RxControl ));
+
+      //
+      //  Update the multicast hash table
+      //
+      SetupMsg.RequestType = USB_REQ_TYPE_VENDOR
+                           | USB_TARGET_DEVICE;
+      SetupMsg.Request = CMD_MULTICAST_HASH_WRITE;
+      SetupMsg.Value = 0;
+      SetupMsg.Index = 0;
+      SetupMsg.Length = sizeof ( pNicDevice ->MulticastHash );
+      Status = Ax88772UsbCommand ( pNicDevice,
+                                   &SetupMsg,
+                                   &pNicDevice->MulticastHash );
+      if ( !EFI_ERROR ( Status )) {
+        DEBUG (( DEBUG_RX_MULTICAST | DEBUG_INFO,
+                  "Multicast Hash: 0x%02x %02x %02x %02x %02x %02x %02x %02x\r\n",
+                  (UINT8) MulticastHash[0],
+                  (UINT8)( MulticastHash[0] >> 8 ),
+                  (UINT8)( MulticastHash[0] >> 16 ),
+                  (UINT8)( MulticastHash[0] >> 24 ),
+                  (UINT8) MulticastHash[1],
+                  (UINT8)( MulticastHash[1] >> 8 ),
+                  (UINT8)( MulticastHash[1] >> 16 ),
+                  (UINT8)( MulticastHash[1] >> 24 )));
+      }
+      else {
+        DEBUG (( DEBUG_ERROR | DEBUG_INFO,
+                  "ERROR - Failed to update multicast hash table, Status: %r\r\n",
+                  Status ));
+      }
+    }
+    else {
+      DEBUG (( DEBUG_ERROR | DEBUG_INFO,
+                "ERROR - Failed to set receiver control, Status: %r\r\n",
+                Status ));
+    }
+  }
+
+  //
+  // Return the operation status
+  //
+  DBG_EXIT_STATUS ( Status );
+  return Status;
+}
+
+
+/**
+  Read an SROM location
+
+  This routine calls ::Ax88772UsbCommand to read data from the
+  SROM.
+
+  @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
+  @param [in] Address          SROM address
+  @param [out] pData           Buffer to receive the data
+
+  @retval EFI_SUCCESS          The read was successful
+  @retval other                The read failed
+
+**/
+EFI_STATUS
+Ax88772SromRead (
+  IN NIC_DEVICE * pNicDevice,
+  IN UINT32 Address,
+  OUT UINT16 * pData
+  )
+{
+  USB_DEVICE_REQUEST SetupMsg;
+  EFI_STATUS Status;
+
+  DBG_ENTER ( );
+
+  //
+  //  Read a value from the SROM
+  //
+  SetupMsg.RequestType = USB_ENDPOINT_DIR_IN
+                       | USB_REQ_TYPE_VENDOR
+                       | USB_TARGET_DEVICE;
+  SetupMsg.Request = CMD_SROM_READ;
+  SetupMsg.Value = (UINT16) Address;
+  SetupMsg.Index = 0;
+  SetupMsg.Length = sizeof ( *pData );
+  Status = Ax88772UsbCommand ( pNicDevice,
+                               &SetupMsg,
+                               pData );
+
+  //
+  // Return the operation status
+  //
+  DBG_EXIT_STATUS ( Status );
+  return Status;
+}
+
+
+/**
+  This routine is called at a regular interval to poll for
+  receive packets.
+
+  This routine polls the link state and gets any receive packets
+  by calling ::Ax88772Rx.
+
+  @param [in] Event            Timer event
+  @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
+
+**/
+VOID
+Ax88772Timer (
+  IN EFI_EVENT Event,
+  IN NIC_DEVICE * pNicDevice
+  )
+{
+  //
+  //  Use explicit DEBUG messages since the output frequency is too
+  //  high for DEBUG_INFO to keep up and have spare cycles for the
+  //  shell
+  //
+  DEBUG (( DEBUG_TIMER, "Entering Ax88772Timer\r\n" ));
+
+  //
+  //  Poll the link state and get any receive packets
+  //
+  Ax88772Rx ( pNicDevice, FALSE );
+
+  DEBUG (( DEBUG_TIMER, "Exiting Ax88772Timer\r\n" ));
+}
+
+
+/**
+  Send a command to the USB device.
+
+  @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
+  @param [in] pRequest         Pointer to the request structure
+  @param [in, out] pBuffer     Data buffer address
+
+  @retval EFI_SUCCESS          The USB transfer was successful
+  @retval other                The USB transfer failed
+
+**/
+EFI_STATUS
+Ax88772UsbCommand (
+  IN NIC_DEVICE * pNicDevice,
+  IN USB_DEVICE_REQUEST * pRequest,
+  IN OUT VOID * pBuffer
+  )
+{
+  UINT32 CmdStatus;
+  EFI_USB_DATA_DIRECTION Direction;
+  EFI_USB_IO_PROTOCOL * pUsbIo;
+  EFI_STATUS Status;
+
+  DBG_ENTER ( );
+
+  //
+  // Determine the transfer direction
+  //
+  Direction = EfiUsbNoData;
+  if ( 0 != pRequest->Length ) {
+    Direction = ( 0 != ( pRequest->RequestType & USB_ENDPOINT_DIR_IN ))
+              ? EfiUsbDataIn : EfiUsbDataOut;
+  }
+
+  //
+  // Issue the command
+  //
+  pUsbIo = pNicDevice->pUsbIo;
+  Status = pUsbIo->UsbControlTransfer ( pUsbIo,
+                                        pRequest,
+                                        Direction,
+                                        USB_BUS_TIMEOUT,
+                                        pBuffer,
+                                        pRequest->Length,
+                                        &CmdStatus );
+
+  //
+  // Determine the operation status
+  //
+  if ( !EFI_ERROR ( Status )) {
+    Status = CmdStatus;
+  }
+  else {
+    //
+    // Display any errors
+    //
+    DEBUG (( DEBUG_INFO,
+              "Ax88772UsbCommand - Status: %r\n",
+              Status ));
+
+    //
+    // Only use status values associated with the Simple Network protocol
+    //
+    if ( EFI_TIMEOUT == Status ) {
+      Status = EFI_DEVICE_ERROR;
+    }
+  }
+
+  //
+  // Return the operation status
+  //
+  DBG_EXIT_STATUS ( Status );
+  return Status;
+}
diff --git a/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772/Ax88772.h b/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772/Ax88772.h
new file mode 100644
index 0000000000..8840a4f464
--- /dev/null
+++ b/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772/Ax88772.h
@@ -0,0 +1,969 @@
+/** @file
+  Definitions for ASIX AX88772 Ethernet adapter.
+
+  Copyright (c) 2011 - 2015, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef _AX88772_H_
+#define _AX88772_H_
+
+#include <Uefi.h>
+
+#include <Guid/EventGroup.h>
+
+#include <IndustryStandard/Pci.h>
+
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/UefiLib.h>
+#include <Library/UefiRuntimeLib.h>
+
+#include <Protocol/DevicePath.h>
+#include <Protocol/LoadedImage.h>
+#include <Protocol/NetworkInterfaceIdentifier.h>
+#include <Protocol/SimpleNetwork.h>
+#include <Protocol/UsbIo.h>
+
+//------------------------------------------------------------------------------
+//  Macros
+//------------------------------------------------------------------------------
+//
+//Too many output debug info hangs system in Debug tip
+//
+//#if defined(_MSC_VER)           /* Handle Microsoft VC++ compiler specifics. */
+//#define DBG_ENTER()             DEBUG (( DEBUG_INFO, "Entering " __FUNCTION__ "\n" )) ///<  Display routine entry
+//#define DBG_EXIT()              DEBUG (( DEBUG_INFO, "Exiting " __FUNCTION__ "\n" ))  ///<  Display routine exit
+//#define DBG_EXIT_DEC(Status)    DEBUG (( DEBUG_INFO, "Exiting " __FUNCTION__ ", Status: %d\n", Status ))      ///<  Display routine exit with decimal value
+//#define DBG_EXIT_HEX(Status)    DEBUG (( DEBUG_INFO, "Exiting " __FUNCTION__ ", Status: 0x%08x\n", Status ))  ///<  Display routine exit with hex value
+//#define DBG_EXIT_STATUS(Status) DEBUG (( DEBUG_INFO, "Exiting " __FUNCTION__ ", Status: %r\n", Status ))      ///<  Display routine exit with status value
+//#define DBG_EXIT_TF(Status)     DEBUG (( DEBUG_INFO, "Exiting " __FUNCTION__ ", returning %s\n", (FALSE == Status) ? L"FALSE" : L"TRUE" ))  ///<  Display routine with TRUE/FALSE value
+//#else   //  _MSC_VER
+#define DBG_ENTER()               ///<  Display routine entry
+#define DBG_EXIT()                ///<  Display routine exit
+#define DBG_EXIT_DEC(Status)      ///<  Display routine exit with decimal value
+#define DBG_EXIT_HEX(Status)      ///<  Display routine exit with hex value
+#define DBG_EXIT_STATUS(Status)   ///<  Display routine exit with status value
+#define DBG_EXIT_TF(Status)       ///<  Display routine with TRUE/FALSE value
+//#endif  //  _MSC_VER
+
+#define USB_IS_IN_ENDPOINT(EndPointAddr)      (((EndPointAddr) & BIT7) != 0)  ///<  Return TRUE/FALSE for IN direction
+#define USB_IS_OUT_ENDPOINT(EndPointAddr)     (((EndPointAddr) & BIT7) == 0)  ///<  Return TRUE/FALSE for OUT direction
+#define USB_IS_BULK_ENDPOINT(Attribute)       (((Attribute) & (BIT0 | BIT1)) == USB_ENDPOINT_BULK)      ///<  Return TRUE/FALSE for BULK type
+#define USB_IS_INTERRUPT_ENDPOINT(Attribute)  (((Attribute) & (BIT0 | BIT1)) == USB_ENDPOINT_INTERRUPT) ///<  Return TRUE/FALSE for INTERRUPT type
+
+//------------------------------------------------------------------------------
+//  Constants
+//------------------------------------------------------------------------------
+
+#define DEBUG_RX_BROADCAST  0x40000000  ///<  Display RX broadcast messages
+#define DEBUG_RX_MULTICAST  0x20000000  ///<  Display RX multicast messages
+#define DEBUG_RX_UNICAST    0x10000000  ///<  Display RX unicast messages
+#define DEBUG_MAC_ADDRESS   0x08000000  ///<  Display the MAC address
+#define DEBUG_LINK          0x04000000  ///<  Display the link status
+#define DEBUG_TX            0x02000000  ///<  Display the TX messages
+#define DEBUG_PHY           0x01000000  ///<  Display the PHY register values
+#define DEBUG_SROM          0x00800000  ///<  Display the SROM contents
+#define DEBUG_TIMER         0x00400000  ///<  Display the timer routine entry/exit
+#define DEBUG_TPL           0x00200000  ///<  Display the timer routine entry/exit
+
+#define AX88772_MAX_PKT_SIZE  ( 2048 - 4 )  ///< Maximum packet size
+#define ETHERNET_HEADER_SIZE  sizeof ( ETHERNET_HEADER )  ///<  Size in bytes of the Ethernet header
+#define MIN_ETHERNET_PKT_SIZE 60    ///<  Minimum packet size including Ethernet header
+#define MAX_ETHERNET_PKT_SIZE 1500  ///<  Ethernet spec 3.1.1: Minimum packet size
+#define MAX_BULKIN_SIZE       2048  ///<  Maximum size of one UsbBulk 
+
+
+#define USB_NETWORK_CLASS   0x09    ///<  USB Network class code
+#define USB_BUS_TIMEOUT     1000    ///<  USB timeout in milliseconds
+
+#define TIMER_MSEC          20              ///<  Polling interval for the NIC
+#define TPL_AX88772         TPL_CALLBACK    ///<  TPL for routine synchronization
+
+/**
+  Verify new TPL value
+
+  This macro which is enabled when debug is enabled verifies that
+  the new TPL value is >= the current TPL value.
+**/
+#ifdef VERIFY_TPL
+#undef VERIFY_TPL
+#endif  //  VERIFY_TPL
+
+#if !defined(MDEPKG_NDEBUG)
+
+#define VERIFY_TPL(tpl)                           \
+{                                                 \
+  EFI_TPL PreviousTpl;                            \
+                                                  \
+  PreviousTpl = gBS->RaiseTPL ( TPL_HIGH_LEVEL ); \
+  gBS->RestoreTPL ( PreviousTpl );                \
+  if ( PreviousTpl > tpl ) {                      \
+    DEBUG (( DEBUG_ERROR, "Current TPL: %d, New TPL: %d\r\n", PreviousTpl, tpl ));  \
+    ASSERT ( PreviousTpl <= tpl );                \
+  }                                               \
+}
+
+#else   //  MDEPKG_NDEBUG
+
+#define VERIFY_TPL(tpl)
+
+#endif  //  MDEPKG_NDEBUG
+
+//------------------------------------------------------------------------------
+//  Hardware Definition
+//------------------------------------------------------------------------------
+
+#define DEV_SIGNATURE     SIGNATURE_32 ('A','X','8','8')  ///<  Signature of data structures in memory
+
+#define VENDOR_ID         0x0b95  ///<  Vendor ID for Asix
+#define PRODUCT_ID        0x7720  ///<  Product ID for the AX88772 USB 10/100 Ethernet controller
+
+#define RESET_MSEC        1000    ///<  Reset duration
+#define PHY_RESET_MSEC     500    ///<  PHY reset duration
+
+//
+//  RX Control register
+//
+
+#define RXC_PRO           0x0001  ///<  Receive all packets
+#define RXC_AMALL         0x0002  ///<  Receive all multicast packets
+#define RXC_SEP           0x0004  ///<  Save error packets
+#define RXC_AB            0x0008  ///<  Receive broadcast packets
+#define RXC_AM            0x0010  ///<  Use multicast destination address hash table
+#define RXC_AP            0x0020  ///<  Accept physical address from Multicast Filter
+#define RXC_SO            0x0080  ///<  Start operation
+#define RXC_MFB           0x0300  ///<  Maximum frame burst
+#define RXC_MFB_2048      0       ///<  Maximum frame size:  2048 bytes
+#define RXC_MFB_4096      0x0100  ///<  Maximum frame size:  4096 bytes
+#define RXC_MFB_8192      0x0200  ///<  Maximum frame size:  8192 bytes
+#define RXC_MFB_16384     0x0300  ///<  Maximum frame size: 16384 bytes
+
+//
+//  Medium Status register
+//
+
+#define MS_FD             0x0002  ///<  Full duplex
+#define MS_ONE            0x0004  ///<  Must be one
+#define MS_RFC            0x0010  ///<  RX flow control enable
+#define MS_TFC            0x0020  ///<  TX flow control enable
+#define MS_PF             0x0080  ///<  Pause frame enable
+#define MS_RE             0x0100  ///<  Receive enable
+#define MS_PS             0x0200  ///<  Port speed 1=100, 0=10 Mbps
+#define MS_SBP            0x0800  ///<  Stop back pressure
+#define MS_SM             0x1000  ///<  Super MAC support
+
+//
+//  Software PHY Select register
+//
+
+#define SPHY_PSEL         0x01    ///<  Select internal PHY
+#define SPHY_ASEL         0x02    ///<  1=Auto select, 0=Manual select
+
+//
+//  Software Reset register
+//
+
+#define SRR_RR            0x01    ///<  Clear receive frame length error
+#define SRR_RT            0x02    ///<  Clear transmit frame length error
+#define SRR_PRTE          0x04    ///<  External PHY reset pin tri-state enable
+#define SRR_PRL           0x08    ///<  External PHY reset pin level
+#define SRR_BZ            0x10    ///<  Force Bulk to return zero length packet
+#define SRR_IPRL          0x20    ///<  Internal PHY reset control
+#define SRR_IPPD          0x40    ///<  Internal PHY power down
+
+//
+//  PHY ID values
+//
+
+#define PHY_ID_INTERNAL   0x0010  ///<  Internal PHY
+
+//
+//  USB Commands
+//
+
+#define CMD_PHY_ACCESS_SOFTWARE   0x06  ///<  Software in control of PHY
+#define CMD_PHY_REG_READ          0x07  ///<  Read PHY register, Value: PHY, Index: Register, Data: Register value
+#define CMD_PHY_REG_WRITE         0x08  ///<  Write PHY register, Value: PHY, Index: Register, Data: New 16-bit value
+#define CMD_PHY_ACCESS_HARDWARE   0x0a  ///<  Hardware in control of PHY
+#define CMD_SROM_READ             0x0b  ///<  Read SROM register: Value: Address, Data: Value
+#define CMD_RX_CONTROL_WRITE      0x10  ///<  Set the RX control register, Value: New value
+#define CMD_GAPS_WRITE            0x12  ///<  Write the gaps register, Value: New value
+#define CMD_MAC_ADDRESS_READ      0x13  ///<  Read the MAC address, Data: 6 byte MAC address
+#define CMD_MAC_ADDRESS_WRITE     0x14  ///<  Set the MAC address, Data: New 6 byte MAC address
+#define CMD_MULTICAST_HASH_WRITE  0x16  ///<  Write the multicast hash table, Data: New 8 byte value
+#define CMD_MEDIUM_STATUS_READ    0x1a  ///<  Read medium status register, Data: Register value
+#define CMD_MEDIUM_STATUS_WRITE   0x1b  ///<  Write medium status register, Value: New value
+#define CMD_RESET                 0x20  ///<  Reset register, Value: New value
+#define CMD_PHY_SELECT            0x22  ///<  PHY select register, Value: New value
+
+//------------------------------
+//  USB Endpoints
+//------------------------------
+
+#define CONTROL_ENDPOINT                0       ///<  Control endpoint
+#define INTERRUPT_ENDPOINT              1       ///<  Interrupt endpoint
+#define BULK_IN_ENDPOINT                2       ///<  Receive endpoint
+#define BULK_OUT_ENDPOINT               3       ///<  Transmit endpoint
+
+//------------------------------
+//  PHY Registers
+//------------------------------
+
+#define PHY_BMCR                        0       ///<  Control register
+#define PHY_BMSR                        1       ///<  Status register
+#define PHY_ANAR                        4       ///<  Autonegotiation advertisement register
+#define PHY_ANLPAR                      5       ///<  Autonegotiation link parter ability register
+#define PHY_ANER                        6       ///<  Autonegotiation expansion register
+
+//  BMCR - Register 0
+
+#define BMCR_RESET                      0x8000  ///<  1 = Reset the PHY, bit clears after reset
+#define BMCR_LOOPBACK                   0x4000  ///<  1 = Loopback enabled
+#define BMCR_100MBPS                    0x2000  ///<  100 Mbits/Sec
+#define BMCR_10MBPS                     0       ///<  10 Mbits/Sec
+#define BMCR_AUTONEGOTIATION_ENABLE     0x1000  ///<  1 = Enable autonegotiation
+#define BMCR_POWER_DOWN                 0x0800  ///<  1 = Power down
+#define BMCR_ISOLATE                    0x0400  ///<  0 = Isolate PHY
+#define BMCR_RESTART_AUTONEGOTIATION    0x0200  ///<  1 = Restart autonegotiation
+#define BMCR_FULL_DUPLEX                0x0100  ///<  Full duplex operation
+#define BMCR_HALF_DUPLEX                0       ///<  Half duplex operation
+#define BMCR_COLLISION_TEST             0x0080  ///<  1 = Collision test enabled
+
+//  BSMR - Register 1
+
+#define BMSR_100BASET4                  0x8000  ///<  1 = 100BASE-T4 mode
+#define BMSR_100BASETX_FDX              0x4000  ///<  1 = 100BASE-TX full duplex
+#define BMSR_100BASETX_HDX              0x2000  ///<  1 = 100BASE-TX half duplex
+#define BMSR_10BASET_FDX                0x1000  ///<  1 = 10BASE-T full duplex
+#define BMSR_10BASET_HDX                0x0800  ///<  1 = 10BASE-T half duplex
+#define BMSR_MF                         0x0040  ///<  1 = PHY accepts frames with preamble suppressed
+#define BMSR_AUTONEG_CMPLT              0x0020  ///<  1 = Autonegotiation complete
+#define BMSR_RF                         0x0010  ///<  1 = Remote fault
+#define BMSR_AUTONEG                    0x0008  ///<  1 = Able to perform autonegotiation
+#define BMSR_LINKST                     0x0004  ///<  1 = Link up
+#define BMSR_JABBER_DETECT              0x0002  ///<  1 = jabber condition detected
+#define BMSR_EXTENDED_CAPABILITY        0x0001  ///<  1 = Extended register capable
+
+//  ANAR and ANLPAR Registers 4, 5
+
+#define AN_NP                           0x8000  ///<  1 = Next page available
+#define AN_ACK                          0x4000  ///<  1 = Link partner acknowledged
+#define AN_RF                           0x2000  ///<  1 = Remote fault indicated by link partner
+#define AN_FCS                          0x0400  ///<  1 = Flow control ability
+#define AN_T4                           0x0200  ///<  1 = 100BASE-T4 support
+#define AN_TX_FDX                       0x0100  ///<  1 = 100BASE-TX Full duplex
+#define AN_TX_HDX                       0x0080  ///<  1 = 100BASE-TX support
+#define AN_10_FDX                       0x0040  ///<  1 = 10BASE-T Full duplex
+#define AN_10_HDX                       0x0020  ///<  1 = 10BASE-T support
+#define AN_CSMA_CD                      0x0001  ///<  1 = IEEE 802.3 CSMA/CD support
+
+//------------------------------------------------------------------------------
+//  Data Types
+//------------------------------------------------------------------------------
+
+/**
+  Ethernet header layout
+
+  IEEE 802.3-2002 Part 3 specification, section 3.1.1.
+**/
+#pragma pack(1)
+typedef struct {
+  UINT8 dest_addr[PXE_HWADDR_LEN_ETHER];  ///<  Destination LAN address
+  UINT8 src_addr[PXE_HWADDR_LEN_ETHER];   ///<  Source LAN address
+  UINT16 type;                            ///<  Protocol or length
+} ETHERNET_HEADER;
+#pragma pack()
+
+/**
+  Receive and Transmit packet structure
+**/
+#pragma pack(1)
+typedef struct _RX_TX_PACKET {
+  struct _RX_TX_PACKET * pNext;       ///<  Next receive packet
+  UINT16 Length;                      ///<  Packet length
+  UINT16 LengthBar;                   ///<  Complement of the length
+  UINT8 Data[ AX88772_MAX_PKT_SIZE ]; ///<  Received packet data
+} RX_TX_PACKET;
+#pragma pack()
+
+/**
+  AX88772 control structure
+
+  The driver uses this structure to manage the Asix AX88772 10/100
+  Ethernet controller.
+**/
+typedef struct {
+  UINTN Signature;          ///<  Structure identification
+
+  //
+  //  USB data
+  //
+  EFI_HANDLE Controller;        ///<  Controller handle
+  EFI_USB_IO_PROTOCOL * pUsbIo; ///<  USB driver interface
+
+  //
+  //  Simple network protocol data
+  //
+  EFI_SIMPLE_NETWORK_PROTOCOL SimpleNetwork;  ///<  Driver's network stack interface
+  EFI_SIMPLE_NETWORK_MODE SimpleNetworkData;  ///<  Data for simple network
+
+  //
+  // Ethernet controller data
+  //
+  BOOLEAN bInitialized;     ///<  Controller initialized
+  VOID * pTxBuffer;         ///<  Last transmit buffer
+  UINT16 PhyId;             ///<  PHY ID
+
+  //
+  //  Link state
+  //
+  BOOLEAN b100Mbps;         ///<  Current link speed, FALSE = 10 Mbps
+  BOOLEAN bComplete;        ///<  Current state of auto-negotiation
+  BOOLEAN bFullDuplex;      ///<  Current duplex
+  BOOLEAN bLinkUp;          ///<  Current link state
+  BOOLEAN bLinkIdle;        ///<  TRUE = No received traffic
+  EFI_EVENT Timer;          ///<  Timer to monitor link state and receive packets
+  UINTN PollCount;          ///<  Number of times the autonegotiation status was polled
+
+  //
+  //  Receive buffer list
+  //
+  RX_TX_PACKET * pRxHead;   ///<  Head of receive packet list
+  RX_TX_PACKET * pRxTail;   ///<  Tail of receive packet list
+  RX_TX_PACKET * pRxFree;   ///<  Free packet list
+  INT32 MulticastHash[2];   ///<  Hash table for multicast destination addresses
+  UINT8 * pBulkInBuff;      ///<  Buffer for Usb Bulk
+} NIC_DEVICE;
+
+#define DEV_FROM_SIMPLE_NETWORK(a)  CR (a, NIC_DEVICE, SimpleNetwork, DEV_SIGNATURE)  ///< Locate NIC_DEVICE from Simple Network Protocol
+
+//------------------------------------------------------------------------------
+// Simple Network Protocol
+//------------------------------------------------------------------------------
+
+/**
+  Reset the network adapter.
+
+  Resets a network adapter and reinitializes it with the parameters that
+  were provided in the previous call to Initialize ().  The transmit and
+  receive queues are cleared.  Receive filters, the station address, the
+  statistics, and the multicast-IP-to-HW MAC addresses are not reset by
+  this call.
+
+  This routine calls ::Ax88772Reset to perform the adapter specific
+  reset operation.  This routine also starts the link negotiation
+  by calling ::Ax88772NegotiateLinkStart.
+
+  @param [in] pSimpleNetwork    Protocol instance pointer
+  @param [in] bExtendedVerification  Indicates that the driver may perform a more
+                                exhaustive verification operation of the device
+                                during reset.
+
+  @retval EFI_SUCCESS           This operation was successful.
+  @retval EFI_NOT_STARTED       The network interface was not started.
+  @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid
+                                EFI_SIMPLE_NETWORK_PROTOCOL structure.
+  @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
+  @retval EFI_UNSUPPORTED       The increased buffer size feature is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+SN_Reset (
+  IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork,
+  IN BOOLEAN bExtendedVerification
+  );
+
+/**
+  Initialize the simple network protocol.
+
+  This routine calls ::Ax88772MacAddressGet to obtain the
+  MAC address.
+
+  @param [in] pNicDevice       NIC_DEVICE_INSTANCE pointer
+
+  @retval EFI_SUCCESS     Setup was successful
+
+**/
+EFI_STATUS
+SN_Setup (
+  IN NIC_DEVICE * pNicDevice
+  );
+
+/**
+  This routine starts the network interface.
+
+  @param [in] pSimpleNetwork    Protocol instance pointer
+
+  @retval EFI_SUCCESS           This operation was successful.
+  @retval EFI_ALREADY_STARTED   The network interface was already started.
+  @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid
+                                EFI_SIMPLE_NETWORK_PROTOCOL structure.
+  @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
+  @retval EFI_UNSUPPORTED       The increased buffer size feature is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+SN_Start (
+  IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork
+  );
+
+/**
+  Set the MAC address.
+  
+  This function modifies or resets the current station address of a
+  network interface.  If Reset is TRUE, then the current station address
+  is set ot the network interface's permanent address.  If Reset if FALSE
+  then the current station address is changed to the address specified by
+  pNew.
+
+  This routine calls ::Ax88772MacAddressSet to update the MAC address
+  in the network adapter.
+
+  @param [in] pSimpleNetwork    Protocol instance pointer
+  @param [in] bReset            Flag used to reset the station address to the
+                                network interface's permanent address.
+  @param [in] pNew              New station address to be used for the network
+                                interface.
+
+  @retval EFI_SUCCESS           This operation was successful.
+  @retval EFI_NOT_STARTED       The network interface was not started.
+  @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid
+                                EFI_SIMPLE_NETWORK_PROTOCOL structure.
+  @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
+  @retval EFI_UNSUPPORTED       The increased buffer size feature is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+SN_StationAddress (
+  IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork,
+  IN BOOLEAN bReset,
+  IN EFI_MAC_ADDRESS * pNew
+  );
+
+/**
+  This function resets or collects the statistics on a network interface.
+  If the size of the statistics table specified by StatisticsSize is not
+  big enough for all of the statistics that are collected by the network
+  interface, then a partial buffer of statistics is returned in
+  StatisticsTable.
+
+  @param [in] pSimpleNetwork    Protocol instance pointer
+  @param [in] bReset            Set to TRUE to reset the statistics for the network interface.
+  @param [in, out] pStatisticsSize  On input the size, in bytes, of StatisticsTable.  On output
+                                the size, in bytes, of the resulting table of statistics.
+  @param [out] pStatisticsTable A pointer to the EFI_NETWORK_STATISTICS structure that
+                                conains the statistics.
+
+  @retval EFI_SUCCESS           This operation was successful.
+  @retval EFI_NOT_STARTED       The network interface was not started.
+  @retval EFI_BUFFER_TOO_SMALL  The pStatisticsTable is NULL or the buffer is too small.
+  @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid
+                                EFI_SIMPLE_NETWORK_PROTOCOL structure.
+  @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
+  @retval EFI_UNSUPPORTED       The increased buffer size feature is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+SN_Statistics (
+  IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork,
+  IN BOOLEAN bReset,
+  IN OUT UINTN * pStatisticsSize,
+  OUT EFI_NETWORK_STATISTICS * pStatisticsTable
+  );
+
+/**
+  This function stops a network interface.  This call is only valid
+  if the network interface is in the started state.
+
+  @param [in] pSimpleNetwork    Protocol instance pointer
+
+  @retval EFI_SUCCESS           This operation was successful.
+  @retval EFI_NOT_STARTED       The network interface was not started.
+  @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid
+                                EFI_SIMPLE_NETWORK_PROTOCOL structure.
+  @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
+  @retval EFI_UNSUPPORTED       The increased buffer size feature is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+SN_Stop (
+  IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork
+  );
+
+/**
+  This function releases the memory buffers assigned in the Initialize() call.
+  Pending transmits and receives are lost, and interrupts are cleared and disabled.
+  After this call, only Initialize() and Stop() calls may be used.
+
+  @param [in] pSimpleNetwork    Protocol instance pointer
+
+  @retval EFI_SUCCESS           This operation was successful.
+  @retval EFI_NOT_STARTED       The network interface was not started.
+  @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid
+                                EFI_SIMPLE_NETWORK_PROTOCOL structure.
+  @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
+  @retval EFI_UNSUPPORTED       The increased buffer size feature is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+SN_Shutdown (
+  IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork
+  );
+
+/**
+  Send a packet over the network.
+
+  This function places the packet specified by Header and Buffer on
+  the transmit queue.  This function performs a non-blocking transmit
+  operation.  When the transmit is complete, the buffer is returned
+  via the GetStatus() call.
+
+  This routine calls ::Ax88772Rx to empty the network adapter of
+  receive packets.  The routine then passes the transmit packet
+  to the network adapter.
+
+  @param [in] pSimpleNetwork    Protocol instance pointer
+  @param [in] HeaderSize        The size, in bytes, of the media header to be filled in by
+                                the Transmit() function.  If HeaderSize is non-zero, then
+                                it must be equal to SimpleNetwork->Mode->MediaHeaderSize
+                                and DestAddr and Protocol parameters must not be NULL.
+  @param [in] BufferSize        The size, in bytes, of the entire packet (media header and
+                                data) to be transmitted through the network interface.
+  @param [in] pBuffer           A pointer to the packet (media header followed by data) to
+                                to be transmitted.  This parameter can not be NULL.  If
+                                HeaderSize is zero, then the media header is Buffer must
+                                already be filled in by the caller.  If HeaderSize is nonzero,
+                                then the media header will be filled in by the Transmit()
+                                function.
+  @param [in] pSrcAddr          The source HW MAC address.  If HeaderSize is zero, then
+                                this parameter is ignored.  If HeaderSize is nonzero and
+                                SrcAddr is NULL, then SimpleNetwork->Mode->CurrentAddress
+                                is used for the source HW MAC address.
+  @param [in] pDestAddr         The destination HW MAC address.  If HeaderSize is zero, then
+                                this parameter is ignored.
+  @param [in] pProtocol         The type of header to build.  If HeaderSize is zero, then
+                                this parameter is ignored.
+
+  @retval EFI_SUCCESS           This operation was successful.
+  @retval EFI_NOT_STARTED       The network interface was not started.
+  @retval EFI_NOT_READY         The network interface is too busy to accept this transmit request.
+  @retval EFI_BUFFER_TOO_SMALL  The BufferSize parameter is too small.
+  @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid
+                                EFI_SIMPLE_NETWORK_PROTOCOL structure.
+  @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
+
+**/
+EFI_STATUS
+EFIAPI
+SN_Transmit (
+  IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork,
+  IN UINTN HeaderSize,
+  IN UINTN BufferSize,
+  IN VOID * pBuffer,
+  IN EFI_MAC_ADDRESS * pSrcAddr,
+  IN EFI_MAC_ADDRESS * pDestAddr,
+  IN UINT16 * pProtocol
+  );
+
+//------------------------------------------------------------------------------
+// Support Routines
+//------------------------------------------------------------------------------
+
+/**
+  Get the MAC address
+
+  This routine calls ::Ax88772UsbCommand to request the MAC
+  address from the network adapter.
+
+  @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
+  @param [out] pMacAddress      Address of a six byte buffer to receive the MAC address.
+
+  @retval EFI_SUCCESS          The MAC address is available.
+  @retval other                The MAC address is not valid.
+
+**/
+EFI_STATUS
+Ax88772MacAddressGet (
+  IN NIC_DEVICE * pNicDevice,
+  OUT UINT8 * pMacAddress
+  );
+
+/**
+  Set the MAC address
+
+  This routine calls ::Ax88772UsbCommand to set the MAC address
+  in the network adapter.
+
+  @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
+  @param [in] pMacAddress      Address of a six byte buffer to containing the new MAC address.
+
+  @retval EFI_SUCCESS          The MAC address was set.
+  @retval other                The MAC address was not set.
+
+**/
+EFI_STATUS
+Ax88772MacAddressSet (
+  IN NIC_DEVICE * pNicDevice,
+  IN UINT8 * pMacAddress
+  );
+
+/**
+  Clear the multicast hash table
+
+  @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
+
+**/
+VOID
+Ax88772MulticastClear (
+  IN NIC_DEVICE * pNicDevice
+  );
+
+/**
+  Enable a multicast address in the multicast hash table
+
+  This routine calls ::Ax88772Crc to compute the hash bit for
+  this MAC address.
+
+  @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
+  @param [in] pMacAddress      Address of a six byte buffer to containing the MAC address.
+
+**/
+VOID
+Ax88772MulticastSet (
+  IN NIC_DEVICE * pNicDevice,
+  IN UINT8 * pMacAddress
+  );
+
+/**
+  Start the link negotiation
+
+  This routine calls ::Ax88772PhyWrite to start the PHY's link
+  negotiation.
+
+  @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
+
+  @retval EFI_SUCCESS          The link negotiation was started.
+  @retval other                Failed to start the link negotiation.
+
+**/
+EFI_STATUS
+Ax88772NegotiateLinkStart (
+  IN NIC_DEVICE * pNicDevice
+  );
+
+/**
+  Complete the negotiation of the PHY link
+
+  This routine calls ::Ax88772PhyRead to determine if the
+  link negotiation is complete.
+
+  @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
+  @param [in, out] pPollCount  Address of number of times this routine was polled
+  @param [out] pbComplete      Address of boolean to receive complate status.
+  @param [out] pbLinkUp        Address of boolean to receive link status, TRUE=up.
+  @param [out] pbHiSpeed       Address of boolean to receive link speed, TRUE=100Mbps.
+  @param [out] pbFullDuplex    Address of boolean to receive link duplex, TRUE=full.
+
+  @retval EFI_SUCCESS          The MAC address is available.
+  @retval other                The MAC address is not valid.
+
+**/
+EFI_STATUS
+Ax88772NegotiateLinkComplete (
+  IN NIC_DEVICE * pNicDevice,
+  IN OUT UINTN * pPollCount,
+  OUT BOOLEAN * pbComplete,
+  OUT BOOLEAN * pbLinkUp,
+  OUT BOOLEAN * pbHiSpeed,
+  OUT BOOLEAN * pbFullDuplex
+  );
+
+/**
+  Read a register from the PHY
+
+  This routine calls ::Ax88772UsbCommand to read a PHY register.
+
+  @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
+  @param [in] RegisterAddress  Number of the register to read.
+  @param [in, out] pPhyData    Address of a buffer to receive the PHY register value
+
+  @retval EFI_SUCCESS          The PHY data is available.
+  @retval other                The PHY data is not valid.
+
+**/
+EFI_STATUS
+Ax88772PhyRead (
+  IN NIC_DEVICE * pNicDevice,
+  IN UINT8 RegisterAddress,
+  IN OUT UINT16 * pPhyData
+  );
+
+/**
+  Write to a PHY register
+
+  This routine calls ::Ax88772UsbCommand to write a PHY register.
+
+  @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
+  @param [in] RegisterAddress  Number of the register to read.
+  @param [in] PhyData          Address of a buffer to receive the PHY register value
+
+  @retval EFI_SUCCESS          The PHY data was written.
+  @retval other                Failed to wwrite the PHY register.
+
+**/
+EFI_STATUS
+Ax88772PhyWrite (
+  IN NIC_DEVICE * pNicDevice,
+  IN UINT8 RegisterAddress,
+  IN UINT16 PhyData
+  );
+
+/**
+  Reset the AX88772
+
+  This routine uses ::Ax88772UsbCommand to reset the network
+  adapter.  This routine also uses ::Ax88772PhyWrite to reset
+  the PHY.
+
+  @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
+
+  @retval EFI_SUCCESS          The MAC address is available.
+  @retval other                The MAC address is not valid.
+
+**/
+EFI_STATUS
+Ax88772Reset (
+  IN NIC_DEVICE * pNicDevice
+  );
+
+/**
+  Receive a frame from the network.
+
+  This routine polls the USB receive interface for a packet.  If a packet
+  is available, this routine adds the receive packet to the list of
+  pending receive packets.
+
+  This routine calls ::Ax88772NegotiateLinkComplete to verify
+  that the link is up.  This routine also calls ::SN_Reset to
+  reset the network adapter when necessary.  Finally this
+  routine attempts to receive one or more packets from the
+  network adapter.
+
+  @param [in] pNicDevice  Pointer to the NIC_DEVICE structure
+  @param [in] bUpdateLink TRUE = Update link status
+
+**/
+VOID
+Ax88772Rx (
+  IN NIC_DEVICE * pNicDevice,
+  IN BOOLEAN bUpdateLink
+  );
+
+/**
+  Enable or disable the receiver
+
+  This routine calls ::Ax88772UsbCommand to update the
+  receiver state.  This routine also calls ::Ax88772MacAddressSet
+  to establish the MAC address for the network adapter.
+
+  @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
+  @param [in] RxFilter         Simple network RX filter mask value
+
+  @retval EFI_SUCCESS          The MAC address was set.
+  @retval other                The MAC address was not set.
+
+**/
+EFI_STATUS
+Ax88772RxControl (
+  IN NIC_DEVICE * pNicDevice,
+  IN UINT32 RxFilter
+  );
+
+/**
+  Read an SROM location
+
+  This routine calls ::Ax88772UsbCommand to read data from the
+  SROM.
+
+  @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
+  @param [in] Address          SROM address
+  @param [out] pData           Buffer to receive the data
+
+  @retval EFI_SUCCESS          The read was successful
+  @retval other                The read failed
+
+**/
+EFI_STATUS
+Ax88772SromRead (
+  IN NIC_DEVICE * pNicDevice,
+  IN UINT32 Address,
+  OUT UINT16 * pData
+  );
+
+/**
+  This routine is called at a regular interval to poll for
+  receive packets.
+
+  This routine polls the link state and gets any receive packets
+  by calling ::Ax88772Rx.
+
+  @param [in] Event            Timer event
+  @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
+
+**/
+VOID
+Ax88772Timer (
+  IN EFI_EVENT Event,
+  IN NIC_DEVICE * pNicDevice
+  );
+
+/**
+  Send a command to the USB device.
+
+  @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
+  @param [in] pRequest         Pointer to the request structure
+  @param [in, out] pBuffer     Data buffer address
+
+  @retval EFI_SUCCESS          The USB transfer was successful
+  @retval other                The USB transfer failed
+
+**/
+EFI_STATUS
+Ax88772UsbCommand (
+  IN NIC_DEVICE * pNicDevice,
+  IN USB_DEVICE_REQUEST * pRequest,
+  IN OUT VOID * pBuffer
+  );
+
+//------------------------------------------------------------------------------
+// EFI Component Name Protocol Support
+//------------------------------------------------------------------------------
+
+extern EFI_COMPONENT_NAME_PROTOCOL   gComponentName;  ///<  Component name protocol declaration
+extern EFI_COMPONENT_NAME2_PROTOCOL  gComponentName2; ///<  Component name 2 protocol declaration
+
+/**
+  Retrieves a Unicode string that is the user readable name of the driver.
+
+  This function retrieves the user readable name of a driver in the form of a
+  Unicode string. If the driver specified by This has a user readable name in
+  the language specified by Language, then a pointer to the driver name is
+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+  by This does not support the language specified by Language,
+  then EFI_UNSUPPORTED is returned.
+
+  @param [in] pThis             A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+                                EFI_COMPONENT_NAME_PROTOCOL instance.
+  @param [in] pLanguage         A pointer to a Null-terminated ASCII string
+                                array indicating the language. This is the
+                                language of the driver name that the caller is
+                                requesting, and it must match one of the
+                                languages specified in SupportedLanguages. The
+                                number of languages supported by a driver is up
+                                to the driver writer. Language is specified
+                                in RFC 3066 or ISO 639-2 language code format.
+  @param [out] ppDriverName     A pointer to the Unicode string to return.
+                                This Unicode string is the name of the
+                                driver specified by This in the language
+                                specified by Language.
+
+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by
+                                This and the language specified by Language was
+                                returned in DriverName.
+  @retval EFI_INVALID_PARAMETER Language is NULL.
+  @retval EFI_INVALID_PARAMETER DriverName is NULL.
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support
+                                the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+GetDriverName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL * pThis,
+  IN  CHAR8 * pLanguage,
+  OUT CHAR16 ** ppDriverName
+  );
+
+
+/**
+  Retrieves a Unicode string that is the user readable name of the controller
+  that is being managed by a driver.
+
+  This function retrieves the user readable name of the controller specified by
+  ControllerHandle and ChildHandle in the form of a Unicode string. If the
+  driver specified by This has a user readable name in the language specified by
+  Language, then a pointer to the controller name is returned in ControllerName,
+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently
+  managing the controller specified by ControllerHandle and ChildHandle,
+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not
+  support the language specified by Language, then EFI_UNSUPPORTED is returned.
+
+  @param [in] pThis             A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+                                EFI_COMPONENT_NAME_PROTOCOL instance.
+  @param [in] ControllerHandle  The handle of a controller that the driver
+                                specified by This is managing.  This handle
+                                specifies the controller whose name is to be
+                                returned.
+  @param [in] ChildHandle       The handle of the child controller to retrieve
+                                the name of.  This is an optional parameter that
+                                may be NULL.  It will be NULL for device
+                                drivers.  It will also be NULL for a bus drivers
+                                that wish to retrieve the name of the bus
+                                controller.  It will not be NULL for a bus
+                                driver that wishes to retrieve the name of a
+                                child controller.
+  @param [in] pLanguage         A pointer to a Null-terminated ASCII string
+                                array indicating the language.  This is the
+                                language of the driver name that the caller is
+                                requesting, and it must match one of the
+                                languages specified in SupportedLanguages. The
+                                number of languages supported by a driver is up
+                                to the driver writer. Language is specified in
+                                RFC 3066 or ISO 639-2 language code format.
+  @param [out] ppControllerName A pointer to the Unicode string to return.
+                                This Unicode string is the name of the
+                                controller specified by ControllerHandle and
+                                ChildHandle in the language specified by
+                                Language from the point of view of the driver
+                                specified by This.
+
+  @retval EFI_SUCCESS           The Unicode string for the user readable name in
+                                the language specified by Language for the
+                                driver specified by This was returned in
+                                DriverName.
+  @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.
+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid
+                                EFI_HANDLE.
+  @retval EFI_INVALID_PARAMETER Language is NULL.
+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently
+                                managing the controller specified by
+                                ControllerHandle and ChildHandle.
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support
+                                the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+GetControllerName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL * pThis,
+  IN  EFI_HANDLE ControllerHandle,
+  IN OPTIONAL EFI_HANDLE ChildHandle,
+  IN  CHAR8 * pLanguage,
+  OUT CHAR16 ** ppControllerName
+  );
+
+//------------------------------------------------------------------------------
+
+#endif  //  _AX88772_H_
diff --git a/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772/Ax88772.inf b/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772/Ax88772.inf
new file mode 100644
index 0000000000..12e7ebc5a2
--- /dev/null
+++ b/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772/Ax88772.inf
@@ -0,0 +1,61 @@
+## @file
+# Component description file for ASIX AX88772 USB/Ethernet driver.
+#
+# This module provides support for the ASIX AX88772 USB/Ethernet adapter.
+# Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010018
+  BASE_NAME                      = Ax88772
+  FILE_GUID                      = B15239D6-6A01-4808-A0F7-B7F20F073555
+  MODULE_TYPE                    = DXE_RUNTIME_DRIVER
+  VERSION_STRING                 = 1.0
+
+  ENTRY_POINT                    = EntryPoint
+
+#
+#  VALID_ARCHITECTURES           = IA32 X64 EBC
+#
+
+[Sources.common]
+  Ax88772.h
+  Ax88772.c
+  ComponentName.c
+  DriverBinding.c
+  SimpleNetwork.c
+
+
+[Packages]
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+
+[LibraryClasses]
+  UefiLib
+  UefiBootServicesTableLib
+  BaseMemoryLib
+  DebugLib
+  UefiRuntimeLib
+  UefiDriverEntryPoint
+
+[Protocols]
+  gEfiDevicePathProtocolGuid           ## BY_START
+  gEfiSimpleNetworkProtocolGuid        ## BY_START
+  gEfiUsbIoProtocolGuid                ## TO_START
+
+[Depex]
+  gEfiBdsArchProtocolGuid AND
+  gEfiCpuArchProtocolGuid AND
+  gEfiMetronomeArchProtocolGuid AND
+  gEfiMonotonicCounterArchProtocolGuid AND
+  gEfiRealTimeClockArchProtocolGuid AND
+  gEfiResetArchProtocolGuid AND
+  gEfiRuntimeArchProtocolGuid AND
+  gEfiSecurityArchProtocolGuid AND
+  gEfiTimerArchProtocolGuid AND
+  gEfiVariableWriteArchProtocolGuid AND
+  gEfiVariableArchProtocolGuid AND
+  gEfiWatchdogTimerArchProtocolGuid
diff --git a/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772/ComponentName.c b/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772/ComponentName.c
new file mode 100644
index 0000000000..b6dce7e7cb
--- /dev/null
+++ b/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772/ComponentName.c
@@ -0,0 +1,178 @@
+/** @file
+  UEFI Component Name(2) protocol implementation.
+
+  Copyright (c) 2011, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "Ax88772.h"
+
+/**
+  EFI Component Name Protocol declaration
+**/
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL  gComponentName = {
+  GetDriverName,
+  GetControllerName,
+  "eng"
+};
+
+/**
+  EFI Component Name 2 Protocol declaration
+**/
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gComponentName2 = {
+  (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) GetDriverName,
+  (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) GetControllerName,
+  "en"
+};
+
+
+/**
+  Driver name table declaration
+**/
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE
+mDriverNameTable[] = {
+  {"eng;en", L"AX88772 Ethernet Driver"},
+  {NULL,  NULL}
+};
+
+/**
+  Retrieves a Unicode string that is the user readable name of the driver.
+
+  This function retrieves the user readable name of a driver in the form of a
+  Unicode string. If the driver specified by This has a user readable name in
+  the language specified by Language, then a pointer to the driver name is
+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+  by This does not support the language specified by Language,
+  then EFI_UNSUPPORTED is returned.
+
+  @param [in] pThis             A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+                                EFI_COMPONENT_NAME_PROTOCOL instance.
+  @param [in] pLanguage         A pointer to a Null-terminated ASCII string
+                                array indicating the language. This is the
+                                language of the driver name that the caller is
+                                requesting, and it must match one of the
+                                languages specified in SupportedLanguages. The
+                                number of languages supported by a driver is up
+                                to the driver writer. Language is specified
+                                in RFC 3066 or ISO 639-2 language code format.
+  @param [out] ppDriverName     A pointer to the Unicode string to return.
+                                This Unicode string is the name of the
+                                driver specified by This in the language
+                                specified by Language.
+
+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by
+                                This and the language specified by Language was
+                                returned in DriverName.
+  @retval EFI_INVALID_PARAMETER Language is NULL.
+  @retval EFI_INVALID_PARAMETER DriverName is NULL.
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support
+                                the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+GetDriverName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL * pThis,
+  IN  CHAR8 * pLanguage,
+  OUT CHAR16 ** ppDriverName
+  )
+{
+  EFI_STATUS Status;
+
+  DBG_ENTER ( );
+  Status = LookupUnicodeString2 (
+             pLanguage,
+             pThis->SupportedLanguages,
+             mDriverNameTable,
+             ppDriverName,
+             (BOOLEAN)(pThis == &gComponentName)
+             );
+  DBG_EXIT_HEX ( Status );
+  return Status;
+}
+
+/**
+  Retrieves a Unicode string that is the user readable name of the controller
+  that is being managed by a driver.
+
+  This function retrieves the user readable name of the controller specified by
+  ControllerHandle and ChildHandle in the form of a Unicode string. If the
+  driver specified by This has a user readable name in the language specified by
+  Language, then a pointer to the controller name is returned in ControllerName,
+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently
+  managing the controller specified by ControllerHandle and ChildHandle,
+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not
+  support the language specified by Language, then EFI_UNSUPPORTED is returned.
+
+  @param [in] pThis             A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+                                EFI_COMPONENT_NAME_PROTOCOL instance.
+  @param [in] ControllerHandle  The handle of a controller that the driver
+                                specified by This is managing.  This handle
+                                specifies the controller whose name is to be
+                                returned.
+  @param [in] ChildHandle       The handle of the child controller to retrieve
+                                the name of.  This is an optional parameter that
+                                may be NULL.  It will be NULL for device
+                                drivers.  It will also be NULL for a bus drivers
+                                that wish to retrieve the name of the bus
+                                controller.  It will not be NULL for a bus
+                                driver that wishes to retrieve the name of a
+                                child controller.
+  @param [in] pLanguage         A pointer to a Null-terminated ASCII string
+                                array indicating the language.  This is the
+                                language of the driver name that the caller is
+                                requesting, and it must match one of the
+                                languages specified in SupportedLanguages. The
+                                number of languages supported by a driver is up
+                                to the driver writer. Language is specified in
+                                RFC 3066 or ISO 639-2 language code format.
+  @param [out] ppControllerName A pointer to the Unicode string to return.
+                                This Unicode string is the name of the
+                                controller specified by ControllerHandle and
+                                ChildHandle in the language specified by
+                                Language from the point of view of the driver
+                                specified by This.
+
+  @retval EFI_SUCCESS           The Unicode string for the user readable name in
+                                the language specified by Language for the
+                                driver specified by This was returned in
+                                DriverName.
+  @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.
+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid
+                                EFI_HANDLE.
+  @retval EFI_INVALID_PARAMETER Language is NULL.
+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently
+                                managing the controller specified by
+                                ControllerHandle and ChildHandle.
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support
+                                the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+GetControllerName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL * pThis,
+  IN  EFI_HANDLE ControllerHandle,
+  IN OPTIONAL EFI_HANDLE ChildHandle,
+  IN  CHAR8 * pLanguage,
+  OUT CHAR16 ** ppControllerName
+  )
+{
+  EFI_STATUS Status;
+
+  DBG_ENTER ( );
+
+  //
+  // Set the controller name
+  //
+  *ppControllerName = L"AX88772 10/100 Ethernet";
+  Status = EFI_SUCCESS;
+
+  //
+  // Return the operation status
+  //
+  DBG_EXIT_HEX ( Status );
+  return Status;
+}
diff --git a/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772/DriverBinding.c b/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772/DriverBinding.c
new file mode 100644
index 0000000000..5bcde4b211
--- /dev/null
+++ b/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772/DriverBinding.c
@@ -0,0 +1,507 @@
+/** @file
+  Implement the driver binding protocol for Asix AX88772 Ethernet driver.
+
+  Copyright (c) 2011-2013, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "Ax88772.h"
+
+/**
+  Verify the controller type
+
+  @param [in] pThis                Protocol instance pointer.
+  @param [in] Controller           Handle of device to test.
+  @param [in] pRemainingDevicePath Not used.
+
+  @retval EFI_SUCCESS          This driver supports this device.
+  @retval other                This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+DriverSupported (
+  IN EFI_DRIVER_BINDING_PROTOCOL * pThis,
+  IN EFI_HANDLE Controller,
+  IN EFI_DEVICE_PATH_PROTOCOL * pRemainingDevicePath
+  )
+{
+  EFI_USB_DEVICE_DESCRIPTOR Device;
+  EFI_USB_IO_PROTOCOL * pUsbIo;
+  EFI_STATUS Status;
+
+  //
+  //  Connect to the USB stack
+  //
+  Status = gBS->OpenProtocol (
+                  Controller,
+                  &gEfiUsbIoProtocolGuid,
+                  (VOID **) &pUsbIo,
+                  pThis->DriverBindingHandle,
+                  Controller,
+                  EFI_OPEN_PROTOCOL_BY_DRIVER
+                  );
+  if (!EFI_ERROR ( Status )) {
+
+    //
+    //  Get the interface descriptor to check the USB class and find a transport
+    //  protocol handler.
+    //
+    Status = pUsbIo->UsbGetDeviceDescriptor ( pUsbIo, &Device );
+    if (!EFI_ERROR ( Status )) {
+
+      //
+      //  Validate the adapter
+      //
+      if (( VENDOR_ID != Device.IdVendor )
+        || ( PRODUCT_ID != Device.IdProduct )) {
+        Status = EFI_UNSUPPORTED;
+      }
+    }
+
+    //
+    //  Done with the USB stack
+    //
+    gBS->CloseProtocol (
+           Controller,
+           &gEfiUsbIoProtocolGuid,
+           pThis->DriverBindingHandle,
+           Controller
+           );
+  }
+
+  //
+  //  Return the device supported status
+  //
+  return Status;
+}
+
+
+/**
+  Start this driver on Controller by opening UsbIo and DevicePath protocols.
+  Initialize PXE structures, create a copy of the Controller Device Path with the
+  NIC's MAC address appended to it, install the NetworkInterfaceIdentifier protocol
+  on the newly created Device Path.
+
+  @param [in] pThis                Protocol instance pointer.
+  @param [in] Controller           Handle of device to work with.
+  @param [in] pRemainingDevicePath Not used, always produce all possible children.
+
+  @retval EFI_SUCCESS          This driver is added to Controller.
+  @retval other                This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+DriverStart (
+  IN EFI_DRIVER_BINDING_PROTOCOL * pThis,
+  IN EFI_HANDLE Controller,
+  IN EFI_DEVICE_PATH_PROTOCOL * pRemainingDevicePath
+  )
+{
+  EFI_STATUS Status;
+  NIC_DEVICE * pNicDevice;
+  UINTN LengthInBytes;
+
+  DBG_ENTER ( );
+
+  //
+  //  Allocate the device structure
+  //
+  LengthInBytes = sizeof ( *pNicDevice );
+  Status = gBS->AllocatePool (
+                  EfiRuntimeServicesData,
+                  LengthInBytes,
+                  (VOID **) &pNicDevice
+                  );
+  if ( !EFI_ERROR ( Status )) {
+    DEBUG (( DEBUG_POOL | DEBUG_INIT,
+              "0x%08x: Allocate pNicDevice, %d bytes\r\n",
+              pNicDevice,
+              sizeof ( *pNicDevice )));
+
+    //
+    //  Set the structure signature
+    //
+    ZeroMem ( pNicDevice, LengthInBytes );
+    pNicDevice->Signature = DEV_SIGNATURE;
+
+    //
+    //  Connect to the USB I/O protocol
+    //
+    Status = gBS->OpenProtocol (
+                    Controller,
+                    &gEfiUsbIoProtocolGuid,
+                    (VOID **) &pNicDevice->pUsbIo,
+                    pThis->DriverBindingHandle,
+                    Controller,
+                    EFI_OPEN_PROTOCOL_BY_DRIVER
+                    );
+
+    if ( !EFI_ERROR ( Status )) {
+      //
+      //  Allocate the necessary events
+      //
+      Status = gBS->CreateEvent ( EVT_TIMER,
+                                  TPL_AX88772,
+                                  (EFI_EVENT_NOTIFY)Ax88772Timer,
+                                  pNicDevice,
+                                  (VOID **)&pNicDevice->Timer );
+      if ( !EFI_ERROR ( Status )) {
+        DEBUG (( DEBUG_POOL | DEBUG_INIT | DEBUG_INFO,
+                  "0x%08x: Allocated timer\r\n",
+                  pNicDevice->Timer ));
+
+        //
+        //  Initialize the simple network protocol
+        //
+        pNicDevice->Controller = Controller;
+        SN_Setup ( pNicDevice );
+
+        //
+        //  Start the timer
+        //
+        Status = gBS->SetTimer ( pNicDevice->Timer,
+                                 TimerPeriodic,
+                                 TIMER_MSEC );
+        if ( !EFI_ERROR ( Status )) {
+          //
+          //  Install both the simple network and device path protocols.
+          //
+          Status = gBS->InstallMultipleProtocolInterfaces (
+                          &Controller,
+                          &gEfiCallerIdGuid,
+                          pNicDevice,
+                          &gEfiSimpleNetworkProtocolGuid,
+                          &pNicDevice->SimpleNetwork,
+                          NULL
+                          );
+
+          if ( !EFI_ERROR ( Status )) {
+            DEBUG (( DEBUG_POOL | DEBUG_INIT | DEBUG_INFO,
+                      "Installed: gEfiCallerIdGuid on   0x%08x\r\n",
+                      Controller ));
+            DEBUG (( DEBUG_POOL | DEBUG_INIT | DEBUG_INFO,
+                      "Installed: gEfiSimpleNetworkProtocolGuid on   0x%08x\r\n",
+                      Controller ));
+            DBG_EXIT_STATUS ( Status );
+            return Status;
+          }
+          DEBUG (( DEBUG_ERROR | DEBUG_INIT | DEBUG_INFO,
+                    "ERROR - Failed to install gEfiSimpleNetworkProtocol on 0x%08x\r\n",
+                    Controller ));
+        }
+        else {
+          DEBUG (( DEBUG_ERROR | DEBUG_INIT | DEBUG_INFO,
+                    "ERROR - Failed to start the timer, Status: %r\r\n",
+                    Status ));
+        }
+      }
+      else {
+        DEBUG (( DEBUG_ERROR | DEBUG_INIT | DEBUG_INFO,
+                  "ERROR - Failed to create timer event, Status: %r\r\n",
+                  Status ));
+      }
+
+      //
+      //  Done with the USB stack
+      //
+      gBS->CloseProtocol (
+             Controller,
+             &gEfiUsbIoProtocolGuid,
+             pThis->DriverBindingHandle,
+             Controller
+             );
+    }
+
+    //
+    //  Done with the device
+    //
+    gBS->FreePool ( pNicDevice );
+  }
+
+  //
+  //  Display the driver start status
+  //
+  DBG_EXIT_STATUS ( Status );
+  return Status;
+}
+
+
+/**
+  Stop this driver on Controller by removing NetworkInterfaceIdentifier protocol and
+  closing the DevicePath and PciIo protocols on Controller.
+
+  @param [in] pThis                Protocol instance pointer.
+  @param [in] Controller           Handle of device to stop driver on.
+  @param [in] NumberOfChildren     How many children need to be stopped.
+  @param [in] pChildHandleBuffer   Not used.
+
+  @retval EFI_SUCCESS          This driver is removed Controller.
+  @retval EFI_DEVICE_ERROR     The device could not be stopped due to a device error.
+  @retval other                This driver was not removed from this device.
+
+**/
+EFI_STATUS
+EFIAPI
+DriverStop (
+  IN  EFI_DRIVER_BINDING_PROTOCOL * pThis,
+  IN  EFI_HANDLE Controller,
+  IN  UINTN NumberOfChildren,
+  IN  EFI_HANDLE * pChildHandleBuffer
+  )
+{
+  NIC_DEVICE * pNicDevice;
+  EFI_STATUS Status;
+
+  DBG_ENTER ( );
+
+  //
+  //  Determine if this driver is already attached
+  //
+  Status = gBS->OpenProtocol (
+                  Controller,
+                  &gEfiCallerIdGuid,
+                  (VOID **) &pNicDevice,
+                  pThis->DriverBindingHandle,
+                  Controller,
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
+                  );
+  if ( !EFI_ERROR ( Status )) {
+    //
+    //  AX88772 driver is no longer running on this device
+    //
+    gBS->UninstallMultipleProtocolInterfaces (
+              Controller,
+              &gEfiSimpleNetworkProtocolGuid,
+              &pNicDevice->SimpleNetwork,
+              &gEfiCallerIdGuid,
+              pNicDevice,
+              NULL );
+    DEBUG (( DEBUG_POOL | DEBUG_INIT,
+                "Removed:   gEfiSimpleNetworkProtocolGuid from 0x%08x\r\n",
+                Controller ));
+    DEBUG (( DEBUG_POOL | DEBUG_INIT,
+                "Removed:   gEfiCallerIdGuid from 0x%08x\r\n",
+                Controller ));
+
+    //
+    //  Stop the timer
+    //
+    if ( NULL != pNicDevice->Timer ) {
+      gBS->SetTimer ( pNicDevice->Timer, TimerCancel, 0 );
+      gBS->CloseEvent ( pNicDevice->Timer );
+      DEBUG (( DEBUG_POOL | DEBUG_INIT | DEBUG_INFO,
+                "0x%08x: Released timer\r\n",
+                pNicDevice->Timer ));
+    }
+
+    //
+    //  Done with the device context
+    //
+    DEBUG (( DEBUG_POOL | DEBUG_INIT,
+              "0x%08x: Free pNicDevice, %d bytes\r\n",
+              pNicDevice,
+              sizeof ( *pNicDevice )));
+    gBS->FreePool ( pNicDevice );
+  }
+
+  //
+  //  Return the shutdown status
+  //
+  DBG_EXIT_STATUS ( Status );
+  return Status;
+}
+
+
+/**
+  Driver binding protocol declaration
+**/
+EFI_DRIVER_BINDING_PROTOCOL  gDriverBinding = {
+  DriverSupported,
+  DriverStart,
+  DriverStop,
+  0xa,
+  NULL,
+  NULL
+};
+
+
+/**
+  Ax88772 driver unload routine.
+
+  @param [in] ImageHandle       Handle for the image.
+
+  @retval EFI_SUCCESS           Image may be unloaded
+
+**/
+EFI_STATUS
+EFIAPI
+DriverUnload (
+  IN EFI_HANDLE ImageHandle
+  )
+{
+  UINTN BufferSize;
+  UINTN Index;
+  UINTN Max;
+  EFI_HANDLE * pHandle;
+  EFI_STATUS Status;
+
+  //
+  //  Determine which devices are using this driver
+  //
+  BufferSize = 0;
+  pHandle = NULL;
+  Status = gBS->LocateHandle (
+                  ByProtocol,
+                  &gEfiCallerIdGuid,
+                  NULL,
+                  &BufferSize,
+                  NULL );
+  if ( EFI_BUFFER_TOO_SMALL == Status ) {
+    for ( ; ; ) {
+      //
+      //  One or more block IO devices are present
+      //
+      Status = gBS->AllocatePool (
+                      EfiRuntimeServicesData,
+                      BufferSize,
+                      (VOID **) &pHandle
+                      );
+      if ( EFI_ERROR ( Status )) {
+        DEBUG (( DEBUG_ERROR | DEBUG_POOL | DEBUG_INIT | DEBUG_INFO,
+                  "Insufficient memory, failed handle buffer allocation\r\n" ));
+        break;
+      }
+
+      //
+      //  Locate the block IO devices
+      //
+      Status = gBS->LocateHandle (
+                      ByProtocol,
+                      &gEfiCallerIdGuid,
+                      NULL,
+                      &BufferSize,
+                      pHandle );
+      if ( EFI_ERROR ( Status )) {
+        //
+        //  Error getting handles
+        //
+        DEBUG (( DEBUG_ERROR | DEBUG_INIT | DEBUG_INFO,
+                "Failure getting Telnet handles\r\n" ));
+        break;
+      }
+      
+      //
+      //  Remove any use of the driver
+      //
+      Max = BufferSize / sizeof ( pHandle[ 0 ]);
+      for ( Index = 0; Max > Index; Index++ ) {
+        Status = DriverStop ( &gDriverBinding,
+                              pHandle[ Index ],
+                              0,
+                              NULL );
+        if ( EFI_ERROR ( Status )) {
+          DEBUG (( DEBUG_WARN | DEBUG_INIT | DEBUG_INFO,
+                    "WARNING - Failed to shutdown the driver on handle %08x\r\n", pHandle[ Index ]));
+          break;
+        }
+      }
+      break;
+    }
+  }
+  else {
+    if ( EFI_NOT_FOUND == Status ) {
+      //
+      //  No devices were found
+      //
+      Status = EFI_SUCCESS;
+    }
+  }
+
+  //
+  //  Free the handle array
+  //
+  if ( NULL != pHandle ) {
+    gBS->FreePool ( pHandle );
+  }
+
+  //
+  //  Remove the protocols installed by the EntryPoint routine.
+  //
+  if ( !EFI_ERROR ( Status )) {
+    gBS->UninstallMultipleProtocolInterfaces (
+            ImageHandle,
+            &gEfiDriverBindingProtocolGuid,
+            &gDriverBinding,
+            &gEfiComponentNameProtocolGuid,
+            &gComponentName,
+            &gEfiComponentName2ProtocolGuid,
+            &gComponentName2,
+            NULL
+            );
+    DEBUG (( DEBUG_POOL | DEBUG_INIT | DEBUG_INFO,
+            "Removed:   gEfiComponentName2ProtocolGuid from 0x%08x\r\n",
+            ImageHandle ));
+    DEBUG (( DEBUG_POOL | DEBUG_INIT | DEBUG_INFO,
+              "Removed:   gEfiComponentNameProtocolGuid from 0x%08x\r\n",
+              ImageHandle ));
+    DEBUG (( DEBUG_POOL | DEBUG_INIT | DEBUG_INFO,
+              "Removed:   gEfiDriverBindingProtocolGuid from 0x%08x\r\n",
+              ImageHandle ));
+  }
+
+  //
+  //  Return the unload status
+  //
+  return Status;
+}
+
+
+/**
+Ax88772 driver entry point.
+
+ at param [in] ImageHandle       Handle for the image.
+ at param [in] pSystemTable      Address of the system table.
+
+ at retval EFI_SUCCESS           Image successfully loaded.
+
+**/
+EFI_STATUS
+EFIAPI
+EntryPoint (
+  IN EFI_HANDLE ImageHandle,
+  IN EFI_SYSTEM_TABLE * pSystemTable
+  )
+{
+  EFI_STATUS    Status;
+
+  DBG_ENTER ( );
+
+  //
+  //  Add the driver to the list of drivers
+  //
+  Status = EfiLibInstallDriverBindingComponentName2 (
+             ImageHandle,
+             pSystemTable,
+             &gDriverBinding,
+             ImageHandle,
+             &gComponentName,
+             &gComponentName2
+             );
+  ASSERT_EFI_ERROR (Status);
+  if ( !EFI_ERROR ( Status )) {
+    DEBUG (( DEBUG_POOL | DEBUG_INIT | DEBUG_INFO,
+              "Installed: gEfiDriverBindingProtocolGuid on   0x%08x\r\n",
+              ImageHandle ));
+    DEBUG (( DEBUG_POOL | DEBUG_INIT | DEBUG_INFO,
+              "Installed: gEfiComponentNameProtocolGuid on   0x%08x\r\n",
+              ImageHandle ));
+    DEBUG (( DEBUG_POOL | DEBUG_INIT | DEBUG_INFO,
+              "Installed: gEfiComponentName2ProtocolGuid on   0x%08x\r\n",
+              ImageHandle ));
+  }
+  DBG_EXIT_STATUS ( Status );
+  return Status;
+}
diff --git a/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772/SimpleNetwork.c b/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772/SimpleNetwork.c
new file mode 100644
index 0000000000..0105d04f5d
--- /dev/null
+++ b/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772/SimpleNetwork.c
@@ -0,0 +1,1503 @@
+/** @file
+  Provides the Simple Network functions.
+
+  Copyright (c) 2011 - 2016, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "Ax88772.h"
+
+/**
+  This function updates the filtering on the receiver.
+
+  This support routine calls ::Ax88772MacAddressSet to update
+  the MAC address.  This routine then rebuilds the multicast
+  hash by calling ::Ax88772MulticastClear and ::Ax88772MulticastSet.
+  Finally this routine enables the receiver by calling
+  ::Ax88772RxControl.
+
+  @param [in] pSimpleNetwork    Simple network mode pointer
+
+  @retval EFI_SUCCESS           This operation was successful.
+  @retval EFI_NOT_STARTED       The network interface was not started.
+  @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid
+                                EFI_SIMPLE_NETWORK_PROTOCOL structure.
+  @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
+  @retval EFI_UNSUPPORTED       The increased buffer size feature is not supported.
+
+**/
+EFI_STATUS
+ReceiveFilterUpdate (
+  IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork
+  )
+{
+  EFI_SIMPLE_NETWORK_MODE * pMode;
+  NIC_DEVICE * pNicDevice;
+  EFI_STATUS Status;
+  UINT32 Index;
+
+  DBG_ENTER ( );
+
+  //
+  // Set the MAC address
+  //
+  pNicDevice = DEV_FROM_SIMPLE_NETWORK ( pSimpleNetwork );
+  pMode = pSimpleNetwork->Mode;
+  Status = Ax88772MacAddressSet ( pNicDevice,
+                                  &pMode->CurrentAddress.Addr[0]);
+  if ( !EFI_ERROR ( Status )) {
+    //
+    // Clear the multicast hash table
+    //
+    Ax88772MulticastClear ( pNicDevice );
+
+    //
+    // Load the multicast hash table
+    //
+    if ( 0 != ( pMode->ReceiveFilterSetting & EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST )) {
+      for ( Index = 0;
+            ( !EFI_ERROR ( Status )) && ( Index < pMode->MCastFilterCount );
+            Index++ ) {
+        //
+        // Enable the next multicast address
+        //
+        Ax88772MulticastSet ( pNicDevice,
+                              &pMode->MCastFilter[ Index ].Addr[0]);
+      }
+    }
+
+    //
+    // Enable the receiver
+    //
+    if ( !EFI_ERROR ( Status )) {
+      Status = Ax88772RxControl ( pNicDevice, pMode->ReceiveFilterSetting );
+    }
+  }
+
+  //
+  // Return the operation status
+  //
+  DBG_EXIT_STATUS ( Status );
+  return Status;
+}
+
+
+/**
+  This function updates the SNP driver status.
+  
+  This function gets the current interrupt and recycled transmit
+  buffer status from the network interface.  The interrupt status
+  and the media status are returned as a bit mask in InterruptStatus.
+  If InterruptStatus is NULL, the interrupt status will not be read.
+  Upon successful return of the media status, the MediaPresent field
+  of EFI_SIMPLE_NETWORK_MODE will be updated to reflect any change
+  of media status.  If TxBuf is not NULL, a recycled transmit buffer
+  address will be retrived.  If a recycled transmit buffer address
+  is returned in TxBuf, then the buffer has been successfully
+  transmitted, and the status for that buffer is cleared.
+
+  This function calls ::Ax88772Rx to update the media status and
+  queue any receive packets.
+
+  @param [in] pSimpleNetwork    Protocol instance pointer
+  @param [in] pInterruptStatus  A pointer to the bit mask of the current active interrupts.
+                                If this is NULL, the interrupt status will not be read from
+                                the device.  If this is not NULL, the interrupt status will
+                                be read from teh device.  When the interrupt status is read,
+                                it will also be cleared.  Clearing the transmit interrupt
+                                does not empty the recycled transmit buffer array.
+  @param [out] ppTxBuf          Recycled transmit buffer address.  The network interface will
+                                not transmit if its internal recycled transmit buffer array is
+                                full.  Reading the transmit buffer does not clear the transmit
+                                interrupt.  If this is NULL, then the transmit buffer status
+                                will not be read.  If there are not transmit buffers to recycle
+                                and TxBuf is not NULL, *TxBuf will be set to NULL.
+
+  @retval EFI_SUCCESS           This operation was successful.
+  @retval EFI_NOT_STARTED       The network interface was not started.
+  @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid
+                                EFI_SIMPLE_NETWORK_PROTOCOL structure.
+  @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
+
+**/
+EFI_STATUS
+EFIAPI
+SN_GetStatus (
+  IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork,
+  OUT UINT32 * pInterruptStatus,
+  OUT VOID ** ppTxBuf
+  )
+{
+  BOOLEAN bLinkIdle;
+  EFI_SIMPLE_NETWORK_MODE * pMode;
+  NIC_DEVICE * pNicDevice;
+  EFI_STATUS Status;
+  EFI_TPL TplPrevious;
+
+  DBG_ENTER ( );
+
+  //
+  // Verify the parameters
+  //
+  if (( NULL != pSimpleNetwork ) && ( NULL != pSimpleNetwork->Mode )) {
+    //
+    // Return the transmit buffer
+    //
+    pNicDevice = DEV_FROM_SIMPLE_NETWORK ( pSimpleNetwork );
+    if (( NULL != ppTxBuf ) && ( NULL != pNicDevice->pTxBuffer )) {
+      *ppTxBuf = pNicDevice->pTxBuffer;
+      pNicDevice->pTxBuffer = NULL;
+    }
+
+    //
+    // Determine if interface is running
+    //
+    pMode = pSimpleNetwork->Mode;
+    if ( EfiSimpleNetworkStopped != pMode->State ) {
+      //
+      //  Synchronize with Ax88772Timer
+      //
+      VERIFY_TPL ( TPL_AX88772 );
+      TplPrevious = gBS->RaiseTPL ( TPL_AX88772 );
+
+      //
+      // Update the link status
+      //
+      bLinkIdle = pNicDevice->bLinkIdle;
+      pNicDevice->bLinkIdle = TRUE;
+      Ax88772Rx ( pNicDevice, bLinkIdle );
+      pMode->MediaPresent = pNicDevice->bLinkUp;
+
+      //
+      //  Release the synchronization with Ax88772Timer
+      //
+      gBS->RestoreTPL ( TplPrevious );
+
+      //
+      // Return the interrupt status
+      //
+      if ( NULL != pInterruptStatus ) {
+        *pInterruptStatus = 0;
+      }
+      Status = EFI_SUCCESS;
+    }
+    else {
+      Status = EFI_NOT_STARTED;
+    }
+  }
+  else {
+    Status = EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Return the operation status
+  //
+  DBG_EXIT_STATUS ( Status );
+  return Status;
+}
+
+
+/**
+  Resets the network adapter and allocates the transmit and receive buffers
+  required by the network interface; optionally, also requests allocation of
+  additional transmit and receive buffers.  This routine must be called before
+  any other routine in the Simple Network protocol is called.
+
+  @param [in] pSimpleNetwork    Protocol instance pointer
+  @param [in] ExtraRxBufferSize Size in bytes to add to the receive buffer allocation
+  @param [in] ExtraTxBufferSize Size in bytes to add to the transmit buffer allocation
+
+  @retval EFI_SUCCESS           This operation was successful.
+  @retval EFI_NOT_STARTED       The network interface was not started.
+  @retval EFI_OUT_OF_RESOURCES  There was not enough memory for the transmit and receive buffers
+  @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid
+                                EFI_SIMPLE_NETWORK_PROTOCOL structure.
+  @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
+  @retval EFI_UNSUPPORTED       The increased buffer size feature is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+SN_Initialize (
+  IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork,
+  IN UINTN ExtraRxBufferSize,
+  IN UINTN ExtraTxBufferSize
+  )
+{
+  EFI_SIMPLE_NETWORK_MODE * pMode;
+  EFI_STATUS Status;
+
+  DBG_ENTER ( );
+  
+  //
+  // Verify the parameters
+  //
+  if (( NULL != pSimpleNetwork ) && ( NULL != pSimpleNetwork->Mode )) {
+    //
+    // Determine if the interface is already started
+    //
+    pMode = pSimpleNetwork->Mode;
+    if ( EfiSimpleNetworkStarted == pMode->State ) {
+      if (( 0 == ExtraRxBufferSize ) && ( 0 == ExtraTxBufferSize )) {
+        //
+        // Start the adapter
+        //
+        Status = SN_Reset ( pSimpleNetwork, FALSE );
+        if ( !EFI_ERROR ( Status )) {
+          //
+          // Update the network state
+          //
+          pMode->State = EfiSimpleNetworkInitialized;
+        }
+      }
+      else {
+        Status = EFI_UNSUPPORTED;
+      }
+    }
+    else {
+      Status = EFI_NOT_STARTED;
+    }
+  }
+  else {
+    Status = EFI_INVALID_PARAMETER;
+  }
+  
+  //
+  // Return the operation status
+  //
+  DBG_EXIT_STATUS ( Status );
+  return Status;
+}
+
+
+/**
+  This function converts a multicast IP address to a multicast HW MAC address
+  for all packet transactions.
+
+  @param [in] pSimpleNetwork    Protocol instance pointer
+  @param [in] bIPv6             Set to TRUE if the multicast IP address is IPv6 [RFC2460].
+                                Set to FALSE if the multicast IP address is IPv4 [RFC 791].
+  @param [in] pIP               The multicast IP address that is to be converted to a
+                                multicast HW MAC address.
+  @param [in] pMAC              The multicast HW MAC address that is to be generated from IP.
+
+  @retval EFI_SUCCESS           This operation was successful.
+  @retval EFI_NOT_STARTED       The network interface was not started.
+  @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid
+                                EFI_SIMPLE_NETWORK_PROTOCOL structure.
+  @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
+  @retval EFI_UNSUPPORTED       The increased buffer size feature is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+SN_MCastIPtoMAC (
+  IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork,
+  IN BOOLEAN bIPv6,
+  IN EFI_IP_ADDRESS * pIP,
+  IN EFI_MAC_ADDRESS * pMAC
+  )
+{
+  EFI_STATUS Status;
+
+  DBG_ENTER ( );
+
+  //
+  // This is not currently supported
+  //
+  Status = EFI_UNSUPPORTED;
+
+  //
+  // Return the operation status
+  //
+  DBG_EXIT_STATUS ( Status );
+  return Status;
+}
+
+
+/**
+  This function performs read and write operations on the NVRAM device
+  attached to a network interface.
+
+  @param [in] pSimpleNetwork    Protocol instance pointer
+  @param [in] ReadWrite         TRUE for read operations, FALSE for write operations.
+  @param [in] Offset            Byte offset in the NVRAM device at which to start the
+                                read or write operation.  This must be a multiple of
+                                NvRamAccessSize and less than NvRamSize.
+  @param [in] BufferSize        The number of bytes to read or write from the NVRAM device.
+                                This must also be a multiple of NvramAccessSize.
+  @param [in, out] pBuffer      A pointer to the data buffer.
+
+  @retval EFI_SUCCESS           This operation was successful.
+  @retval EFI_NOT_STARTED       The network interface was not started.
+  @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid
+                                EFI_SIMPLE_NETWORK_PROTOCOL structure.
+  @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
+  @retval EFI_UNSUPPORTED       The increased buffer size feature is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+SN_NvData (
+  IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork,
+  IN BOOLEAN ReadWrite,
+  IN UINTN Offset,
+  IN UINTN BufferSize,
+  IN OUT VOID * pBuffer
+  )
+{
+  EFI_STATUS Status;
+
+  DBG_ENTER ( );
+
+  //
+  // This is not currently supported
+  //
+  Status = EFI_UNSUPPORTED;
+
+  //
+  // Return the operation status
+  //
+  DBG_EXIT_STATUS ( Status );
+  return Status;
+}
+
+
+/**
+  Attempt to receive a packet from the network adapter.
+
+  This function retrieves one packet from the receive queue of the network
+  interface.  If there are no packets on the receive queue, then EFI_NOT_READY
+  will be returned.  If there is a packet on the receive queue, and the size
+  of the packet is smaller than BufferSize, then the contents of the packet
+  will be placed in Buffer, and BufferSize will be udpated with the actual
+  size of the packet.  In addition, if SrcAddr, DestAddr, and Protocol are
+  not NULL, then these values will be extracted from the media header and
+  returned.  If BufferSize is smaller than the received packet, then the
+  size of the receive packet will be placed in BufferSize and
+  EFI_BUFFER_TOO_SMALL will be returned.
+
+  This routine calls ::Ax88772Rx to update the media status and
+  empty the network adapter of receive packets.
+
+  @param [in] pSimpleNetwork    Protocol instance pointer
+  @param [out] pHeaderSize      The size, in bytes, of the media header to be filled in by
+                                the Transmit() function.  If HeaderSize is non-zero, then
+                                it must be equal to SimpleNetwork->Mode->MediaHeaderSize
+                                and DestAddr and Protocol parameters must not be NULL.
+  @param [out] pBufferSize      The size, in bytes, of the entire packet (media header and
+                                data) to be transmitted through the network interface.
+  @param [out] pBuffer          A pointer to the packet (media header followed by data) to
+                                to be transmitted.  This parameter can not be NULL.  If
+                                HeaderSize is zero, then the media header is Buffer must
+                                already be filled in by the caller.  If HeaderSize is nonzero,
+                                then the media header will be filled in by the Transmit()
+                                function.
+  @param [out] pSrcAddr         The source HW MAC address.  If HeaderSize is zero, then
+                                this parameter is ignored.  If HeaderSize is nonzero and
+                                SrcAddr is NULL, then SimpleNetwork->Mode->CurrentAddress
+                                is used for the source HW MAC address.
+  @param [out] pDestAddr        The destination HW MAC address.  If HeaderSize is zero, then
+                                this parameter is ignored.
+  @param [out] pProtocol        The type of header to build.  If HeaderSize is zero, then
+                                this parameter is ignored.
+
+  @retval EFI_SUCCESS           This operation was successful.
+  @retval EFI_NOT_STARTED       The network interface was not started.
+  @retval EFI_NOT_READY         No packets have been received on the network interface.
+  @retval EFI_BUFFER_TOO_SMALL  The packet is larger than BufferSize bytes.
+  @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid
+                                EFI_SIMPLE_NETWORK_PROTOCOL structure.
+  @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
+
+**/
+EFI_STATUS
+EFIAPI
+SN_Receive (
+  IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork,
+  OUT UINTN                      * pHeaderSize,
+  OUT UINTN                      * pBufferSize,
+  OUT VOID                       * pBuffer,
+  OUT EFI_MAC_ADDRESS            * pSrcAddr,
+  OUT EFI_MAC_ADDRESS            * pDestAddr,
+  OUT UINT16                     * pProtocol
+  )
+{
+  ETHERNET_HEADER * pHeader;
+  EFI_SIMPLE_NETWORK_MODE * pMode;
+  NIC_DEVICE * pNicDevice;
+  RX_TX_PACKET * pRxPacket;
+  EFI_STATUS Status;
+  EFI_TPL TplPrevious;
+  UINT16 Type;
+
+  DBG_ENTER ( );
+
+  //
+  // Verify the parameters
+  //
+  if (( NULL != pSimpleNetwork ) && ( NULL != pSimpleNetwork->Mode )) {
+    //
+    // The interface must be running
+    //
+    pMode = pSimpleNetwork->Mode;
+    if ( EfiSimpleNetworkInitialized == pMode->State ) {
+      //
+      //  Synchronize with Ax88772Timer
+      //
+      VERIFY_TPL ( TPL_AX88772 );
+      TplPrevious = gBS->RaiseTPL ( TPL_AX88772 );
+
+      //
+      // Update the link status
+      //
+      pNicDevice = DEV_FROM_SIMPLE_NETWORK ( pSimpleNetwork );
+      Ax88772Rx ( pNicDevice, FALSE );
+      pMode->MediaPresent = pNicDevice->bLinkUp;
+      if ( pMode->MediaPresent ) {
+        //
+        //  Attempt to receive a packet
+        //
+        pRxPacket = pNicDevice->pRxHead;
+        if ( NULL != pRxPacket ) {
+          pNicDevice->pRxHead = pRxPacket->pNext;
+          if ( NULL == pNicDevice->pRxHead ) {
+            pNicDevice->pRxTail = NULL;
+          }
+
+          //
+          // Copy the received packet into the receive buffer
+          //
+          *pBufferSize = pRxPacket->Length;
+          CopyMem ( pBuffer, &pRxPacket->Data[0], pRxPacket->Length );
+          pHeader = (ETHERNET_HEADER *) &pRxPacket->Data[0];
+          if ( NULL != pHeaderSize ) {
+            *pHeaderSize = sizeof ( *pHeader );
+          }
+          if ( NULL != pDestAddr ) {
+            CopyMem ( pDestAddr, &pHeader->dest_addr, PXE_HWADDR_LEN_ETHER );
+          }
+          if ( NULL != pSrcAddr ) {
+            CopyMem ( pSrcAddr, &pHeader->src_addr, PXE_HWADDR_LEN_ETHER );
+          }
+          if ( NULL != pProtocol ) {
+            Type = pHeader->type;
+            Type = (UINT16)(( Type >> 8 ) | ( Type << 8 ));
+            *pProtocol = Type;
+          }
+          Status = EFI_SUCCESS;
+        }
+        else {
+          //
+          //  No receive packets available
+          //
+          Status = EFI_NOT_READY;
+        }
+      }
+      else {
+        //
+        //  Link no up
+        //
+        Status = EFI_NOT_READY;
+      }
+
+      //
+      //  Release the synchronization with Ax88772Timer
+      //
+      gBS->RestoreTPL ( TplPrevious );
+    }
+    else {
+      Status = EFI_NOT_STARTED;
+    }
+  }
+  else {
+    Status = EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Return the operation status
+  //
+  DBG_EXIT_STATUS ( Status );
+  return Status;
+}
+
+
+/**
+  This function is used to enable and disable the hardware and software receive
+  filters for the underlying network device.
+
+  The receive filter change is broken down into three steps:
+
+    1.  The filter mask bits that are set (ON) in the Enable parameter
+        are added to the current receive filter settings.
+
+    2.  The filter mask bits that are set (ON) in the Disable parameter
+        are subtracted from the updated receive filter settins.
+
+    3.  If the resulting filter settigns is not supported by the hardware
+        a more liberal setting is selected.
+
+  If the same bits are set in the Enable and Disable parameters, then the bits
+  in the Disable parameter takes precedence.
+
+  If the ResetMCastFilter parameter is TRUE, then the multicast address list
+  filter is disabled (irregardless of what other multicast bits are set in
+  the enable and Disable parameters).  The SNP->Mode->MCastFilterCount field
+  is set to zero.  The SNP->Mode->MCastFilter contents are undefined.
+
+  After enableing or disabling receive filter settings, software should
+  verify the new settings by checking the SNP->Mode->ReceeiveFilterSettings,
+  SNP->Mode->MCastFilterCount and SNP->Mode->MCastFilter fields.
+
+  Note: Some network drivers and/or devices will automatically promote
+  receive filter settings if the requested setting can not be honored.
+  For example, if a request for four multicast addresses is made and
+  the underlying hardware only supports two multicast addresses the
+  driver might set the promiscuous or promiscuous multicast receive filters
+  instead.  The receiving software is responsible for discarding any extra
+  packets that get through the hardware receive filters.
+
+  If ResetMCastFilter is TRUE, then the multicast receive filter list
+  on the network interface will be reset to the default multicast receive
+  filter list.  If ResetMCastFilter is FALSE, and this network interface
+  allows the multicast receive filter list to be modified, then the
+  MCastFilterCnt and MCastFilter are used to update the current multicast
+  receive filter list.  The modified receive filter list settings can be
+  found in the MCastFilter field of EFI_SIMPLE_NETWORK_MODE.
+
+  This routine calls ::ReceiveFilterUpdate to update the receive
+  state in the network adapter.
+
+  @param [in] pSimpleNetwork    Protocol instance pointer
+  @param [in] Enable            A bit mask of receive filters to enable on the network interface.
+  @param [in] Disable           A bit mask of receive filters to disable on the network interface.
+                                For backward compatibility with EFI 1.1 platforms, the
+                                EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST bit must be set
+                                when the ResetMCastFilter parameter is TRUE.
+  @param [in] bResetMCastFilter Set to TRUE to reset the contents of the multicast receive
+                                filters on the network interface to their default values.
+  @param [in] MCastFilterCnt    Number of multicast HW MAC address in the new MCastFilter list.
+                                This value must be less than or equal to the MaxMCastFilterCnt
+                                field of EFI_SIMPLE_NETWORK_MODE.  This field is optional if
+                                ResetMCastFilter is TRUE.
+  @param [in] pMCastFilter      A pointer to a list of new multicast receive filter HW MAC
+                                addresses.  This list will replace any existing multicast
+                                HW MAC address list.  This field is optional if ResetMCastFilter
+                                is TRUE.
+
+  @retval EFI_SUCCESS           This operation was successful.
+  @retval EFI_NOT_STARTED       The network interface was not started.
+  @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid
+                                EFI_SIMPLE_NETWORK_PROTOCOL structure.
+  @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
+  @retval EFI_UNSUPPORTED       The increased buffer size feature is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+SN_ReceiveFilters (
+  IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork,
+  IN UINT32 Enable,
+  IN UINT32 Disable,
+  IN BOOLEAN bResetMCastFilter,
+  IN UINTN MCastFilterCnt,
+  IN EFI_MAC_ADDRESS * pMCastFilter
+  )
+{
+  EFI_SIMPLE_NETWORK_MODE * pMode;
+  EFI_MAC_ADDRESS * pMulticastAddress;
+  EFI_MAC_ADDRESS * pTableEnd;
+  EFI_STATUS Status;
+
+  DBG_ENTER ( );
+
+  //
+  // Verify the parameters
+  //
+  Status = EFI_INVALID_PARAMETER;
+  if (( NULL != pSimpleNetwork ) && ( NULL != pSimpleNetwork->Mode )) {
+    pMode = pSimpleNetwork->Mode;
+
+    //
+    //  Update the multicast list if necessary
+    //
+    if ( !bResetMCastFilter ) {
+      if ( 0 != MCastFilterCnt ) {
+        if (( MAX_MCAST_FILTER_CNT >= MCastFilterCnt )
+          && ( NULL != pMCastFilter )) {
+          //
+          // Verify the multicast addresses
+          //
+          pMulticastAddress = pMCastFilter;
+          pTableEnd = pMulticastAddress + MCastFilterCnt;
+          while ( pTableEnd > pMulticastAddress ) {
+            //
+            // The first digit of the multicast address must have the LSB set
+            //
+            if ( 0 == ( pMulticastAddress->Addr[0] & 1 )) {
+              //
+              // Invalid multicast address
+              //
+              break;
+            }
+            pMulticastAddress += 1;
+          }
+          if ( pTableEnd == pMulticastAddress ) {
+            //
+            // Update the multicast filter list.
+            //
+            CopyMem (&pMode->MCastFilter[0],
+                     pMCastFilter,
+                     MCastFilterCnt * sizeof ( *pMCastFilter ));
+            Status = EFI_SUCCESS;
+          }
+        }
+      }
+      else {
+        Status = EFI_SUCCESS;
+      }
+    }
+    else {
+      //
+      // No multicast address list is specified
+      //
+      MCastFilterCnt = 0;
+      Status = EFI_SUCCESS;
+    }
+    if ( !EFI_ERROR ( Status )) {
+      //
+      // The parameters are valid!
+      //
+      pMode->ReceiveFilterSetting |= Enable;
+      pMode->ReceiveFilterSetting &= ~Disable;
+      pMode->MCastFilterCount = (UINT32)MCastFilterCnt;
+
+      //
+      // Update the receive filters in the adapter
+      //
+      Status = ReceiveFilterUpdate ( pSimpleNetwork );
+    }
+  }
+
+  //
+  // Return the operation status
+  //
+  DBG_EXIT_STATUS ( Status );
+  return Status;
+}
+
+
+/**
+  Reset the network adapter.
+
+  Resets a network adapter and reinitializes it with the parameters that
+  were provided in the previous call to Initialize ().  The transmit and
+  receive queues are cleared.  Receive filters, the station address, the
+  statistics, and the multicast-IP-to-HW MAC addresses are not reset by
+  this call.
+
+  This routine calls ::Ax88772Reset to perform the adapter specific
+  reset operation.  This routine also starts the link negotiation
+  by calling ::Ax88772NegotiateLinkStart.
+
+  @param [in] pSimpleNetwork    Protocol instance pointer
+  @param [in] bExtendedVerification  Indicates that the driver may perform a more
+                                exhaustive verification operation of the device
+                                during reset.
+
+  @retval EFI_SUCCESS           This operation was successful.
+  @retval EFI_NOT_STARTED       The network interface was not started.
+  @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid
+                                EFI_SIMPLE_NETWORK_PROTOCOL structure.
+  @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
+  @retval EFI_UNSUPPORTED       The increased buffer size feature is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+SN_Reset (
+  IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork,
+  IN BOOLEAN bExtendedVerification
+  )
+{
+  EFI_SIMPLE_NETWORK_MODE * pMode;
+  NIC_DEVICE * pNicDevice;
+  RX_TX_PACKET * pRxPacket;
+  EFI_STATUS Status;
+  EFI_TPL TplPrevious;
+
+  DBG_ENTER ( );
+
+  //
+  //  Verify the parameters
+  //
+  if (( NULL != pSimpleNetwork ) && ( NULL != pSimpleNetwork->Mode )) {
+    //
+    //  Synchronize with Ax88772Timer
+    //
+    VERIFY_TPL ( TPL_AX88772 );
+    TplPrevious = gBS->RaiseTPL ( TPL_AX88772 );
+
+    //
+    //  Update the device state
+    //
+    pNicDevice = DEV_FROM_SIMPLE_NETWORK ( pSimpleNetwork );
+    pNicDevice->bComplete = FALSE;
+    pNicDevice->bLinkUp = FALSE;
+
+    pMode = pSimpleNetwork->Mode;
+    pMode->MediaPresent = FALSE;
+
+    //
+    //  Discard any received packets
+    //
+    while ( NULL != pNicDevice->pRxHead ) {
+      //
+      //  Remove the packet from the received packet list
+      //
+      pRxPacket = pNicDevice->pRxHead;
+      pNicDevice->pRxHead = pRxPacket->pNext;
+
+      //
+      //  Queue the packet to the free list
+      //
+      pRxPacket->pNext = pNicDevice->pRxFree;
+      pNicDevice->pRxFree = pRxPacket;
+    }
+    pNicDevice->pRxTail = NULL;
+
+    //
+    //  Reset the device
+    //
+    Status = Ax88772Reset ( pNicDevice );
+    if ( !EFI_ERROR ( Status )) {
+      //
+      //  Update the receive filters in the adapter
+      //
+      Status = ReceiveFilterUpdate ( pSimpleNetwork );
+
+      //
+      //  Try to get a connection to the network
+      //
+      if ( !EFI_ERROR ( Status )) {
+        //
+        //  Start the autonegotiation
+        //
+        Status = Ax88772NegotiateLinkStart ( pNicDevice );
+      }
+    }
+
+    //
+    //  Release the synchronization with Ax88772Timer
+    //
+    gBS->RestoreTPL ( TplPrevious );
+  }
+  else {
+    Status = EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Return the operation status
+  //
+  DBG_EXIT_STATUS ( Status );
+  return Status;
+}
+
+
+/**
+  Initialize the simple network protocol.
+
+  This routine calls ::Ax88772MacAddressGet to obtain the
+  MAC address.
+
+  @param [in] pNicDevice       NIC_DEVICE_INSTANCE pointer
+
+  @retval EFI_SUCCESS     Setup was successful
+
+**/
+EFI_STATUS
+SN_Setup (
+  IN NIC_DEVICE * pNicDevice
+  )
+{
+  EFI_SIMPLE_NETWORK_MODE * pMode;
+  EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork;
+  EFI_STATUS Status;
+
+  DBG_ENTER ( );
+
+  //
+  // Initialize the simple network protocol
+  //
+  pSimpleNetwork = &pNicDevice->SimpleNetwork;
+  pSimpleNetwork->Revision = EFI_SIMPLE_NETWORK_PROTOCOL_REVISION;
+  pSimpleNetwork->Start = (EFI_SIMPLE_NETWORK_START)SN_Start;
+  pSimpleNetwork->Stop = (EFI_SIMPLE_NETWORK_STOP)SN_Stop;
+  pSimpleNetwork->Initialize = (EFI_SIMPLE_NETWORK_INITIALIZE)SN_Initialize;
+  pSimpleNetwork->Reset = (EFI_SIMPLE_NETWORK_RESET)SN_Reset;
+  pSimpleNetwork->Shutdown = (EFI_SIMPLE_NETWORK_SHUTDOWN)SN_Shutdown;
+  pSimpleNetwork->ReceiveFilters = (EFI_SIMPLE_NETWORK_RECEIVE_FILTERS)SN_ReceiveFilters;
+  pSimpleNetwork->StationAddress = (EFI_SIMPLE_NETWORK_STATION_ADDRESS)SN_StationAddress;
+  pSimpleNetwork->Statistics = (EFI_SIMPLE_NETWORK_STATISTICS)SN_Statistics;
+  pSimpleNetwork->MCastIpToMac = (EFI_SIMPLE_NETWORK_MCAST_IP_TO_MAC)SN_MCastIPtoMAC;
+  pSimpleNetwork->NvData = (EFI_SIMPLE_NETWORK_NVDATA)SN_NvData;
+  pSimpleNetwork->GetStatus = (EFI_SIMPLE_NETWORK_GET_STATUS)SN_GetStatus;
+  pSimpleNetwork->Transmit = (EFI_SIMPLE_NETWORK_TRANSMIT)SN_Transmit;
+  pSimpleNetwork->Receive = (EFI_SIMPLE_NETWORK_RECEIVE)SN_Receive;
+  pSimpleNetwork->WaitForPacket = NULL;
+  pMode = &pNicDevice->SimpleNetworkData;
+  pSimpleNetwork->Mode = pMode;
+
+  pMode->State = EfiSimpleNetworkStopped;
+  pMode->HwAddressSize = PXE_HWADDR_LEN_ETHER;
+  pMode->MediaHeaderSize = sizeof ( ETHERNET_HEADER );
+  pMode->MaxPacketSize = MAX_ETHERNET_PKT_SIZE;
+  pMode->NvRamSize = 0;
+  pMode->NvRamAccessSize = 0;
+  pMode->ReceiveFilterMask = EFI_SIMPLE_NETWORK_RECEIVE_UNICAST
+                           | EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST
+                           | EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST
+                           | EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS
+                           | EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST;
+  pMode->ReceiveFilterSetting = EFI_SIMPLE_NETWORK_RECEIVE_UNICAST
+                              | EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST;
+  pMode->MaxMCastFilterCount = MAX_MCAST_FILTER_CNT;
+  pMode->MCastFilterCount = 0;
+  SetMem ( &pMode->BroadcastAddress,
+           PXE_HWADDR_LEN_ETHER,
+           0xff );
+  pMode->IfType = EfiNetworkInterfaceUndi;
+  pMode->MacAddressChangeable = TRUE;
+  pMode->MultipleTxSupported = TRUE;
+  pMode->MediaPresentSupported = TRUE;
+  pMode->MediaPresent = FALSE;
+
+  //
+  //  Read the MAC address
+  //
+  pNicDevice->PhyId = PHY_ID_INTERNAL;
+  pNicDevice->b100Mbps = TRUE;
+  pNicDevice->bFullDuplex = TRUE;
+
+  Status = gBS->AllocatePool ( EfiRuntimeServicesData, 
+                               MAX_BULKIN_SIZE,
+                               (VOID **) &pNicDevice->pBulkInBuff);
+  if ( EFI_ERROR(Status)) {
+    DEBUG (( EFI_D_ERROR, "Memory are not enough\n"));
+    return Status;
+  }
+        
+  Status = Ax88772MacAddressGet (
+                pNicDevice,
+                &pMode->PermanentAddress.Addr[0]);
+  if ( !EFI_ERROR ( Status )) {
+    //
+    //  Display the MAC address
+    //
+    DEBUG (( DEBUG_MAC_ADDRESS | DEBUG_INFO,
+              "MAC: %02x-%02x-%02x-%02x-%02x-%02x\n",
+              pMode->PermanentAddress.Addr[0],
+              pMode->PermanentAddress.Addr[1],
+              pMode->PermanentAddress.Addr[2],
+              pMode->PermanentAddress.Addr[3],
+              pMode->PermanentAddress.Addr[4],
+              pMode->PermanentAddress.Addr[5]));
+
+    //
+    //  Use the hardware address as the current address
+    //
+    CopyMem ( &pMode->CurrentAddress,
+              &pMode->PermanentAddress,
+              PXE_HWADDR_LEN_ETHER );
+  }
+
+  //
+  //  Return the setup status
+  //
+  DBG_EXIT_STATUS ( Status );
+  return Status;
+}
+
+
+/**
+  This routine starts the network interface.
+
+  @param [in] pSimpleNetwork    Protocol instance pointer
+
+  @retval EFI_SUCCESS           This operation was successful.
+  @retval EFI_ALREADY_STARTED   The network interface was already started.
+  @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid
+                                EFI_SIMPLE_NETWORK_PROTOCOL structure.
+  @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
+  @retval EFI_UNSUPPORTED       The increased buffer size feature is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+SN_Start (
+  IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork
+  )
+{
+  NIC_DEVICE * pNicDevice;
+  EFI_SIMPLE_NETWORK_MODE * pMode;
+  EFI_STATUS Status;
+  
+  DBG_ENTER ( );
+  
+  //
+  // Verify the parameters
+  //
+  Status = EFI_INVALID_PARAMETER;
+  if (( NULL != pSimpleNetwork ) && ( NULL != pSimpleNetwork->Mode )) {
+    pMode = pSimpleNetwork->Mode;
+    if ( EfiSimpleNetworkStopped == pMode->State ) {
+      //
+      // Initialize the mode structure
+      // NVRAM access is not supported
+      //
+      ZeroMem ( pMode, sizeof ( *pMode ));
+  
+      pMode->State = EfiSimpleNetworkStarted;
+      pMode->HwAddressSize = PXE_HWADDR_LEN_ETHER;
+      pMode->MediaHeaderSize = sizeof ( ETHERNET_HEADER );
+      pMode->MaxPacketSize = MAX_ETHERNET_PKT_SIZE;
+      pMode->ReceiveFilterMask = EFI_SIMPLE_NETWORK_RECEIVE_UNICAST
+                               | EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST
+                               | EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST
+                               | EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS
+                               | EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST;
+      pMode->ReceiveFilterSetting = EFI_SIMPLE_NETWORK_RECEIVE_UNICAST;
+      pMode->MaxMCastFilterCount = MAX_MCAST_FILTER_CNT;
+      pNicDevice = DEV_FROM_SIMPLE_NETWORK ( pSimpleNetwork );
+      Status = Ax88772MacAddressGet ( pNicDevice, &pMode->PermanentAddress.Addr[0]);
+      CopyMem ( &pMode->CurrentAddress,
+                &pMode->PermanentAddress,
+                sizeof ( pMode->CurrentAddress ));
+      pMode->BroadcastAddress.Addr[0] = 0xff;
+      pMode->BroadcastAddress.Addr[1] = 0xff;
+      pMode->BroadcastAddress.Addr[2] = 0xff;
+      pMode->BroadcastAddress.Addr[3] = 0xff;
+      pMode->BroadcastAddress.Addr[4] = 0xff;
+      pMode->BroadcastAddress.Addr[5] = 0xff;
+      pMode->IfType = 1;
+      pMode->MacAddressChangeable = TRUE;
+      pMode->MultipleTxSupported = TRUE;
+      pMode->MediaPresentSupported = TRUE;
+      pMode->MediaPresent = FALSE;
+    }
+    else {
+      Status = EFI_ALREADY_STARTED;
+    }
+  }
+  
+  //
+  // Return the operation status
+  //
+  DBG_EXIT_STATUS ( Status );
+  return Status;
+}
+
+
+/**
+  Set the MAC address.
+  
+  This function modifies or resets the current station address of a
+  network interface.  If Reset is TRUE, then the current station address
+  is set ot the network interface's permanent address.  If Reset if FALSE
+  then the current station address is changed to the address specified by
+  pNew.
+
+  This routine calls ::Ax88772MacAddressSet to update the MAC address
+  in the network adapter.
+
+  @param [in] pSimpleNetwork    Protocol instance pointer
+  @param [in] bReset            Flag used to reset the station address to the
+                                network interface's permanent address.
+  @param [in] pNew              New station address to be used for the network
+                                interface.
+
+  @retval EFI_SUCCESS           This operation was successful.
+  @retval EFI_NOT_STARTED       The network interface was not started.
+  @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid
+                                EFI_SIMPLE_NETWORK_PROTOCOL structure.
+  @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
+  @retval EFI_UNSUPPORTED       The increased buffer size feature is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+SN_StationAddress (
+  IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork,
+  IN BOOLEAN bReset,
+  IN EFI_MAC_ADDRESS * pNew
+  )
+{
+  NIC_DEVICE * pNicDevice;
+  EFI_SIMPLE_NETWORK_MODE * pMode;
+  EFI_STATUS Status;
+
+  DBG_ENTER ( );
+
+  //
+  // Verify the parameters
+  //
+  if (( NULL != pSimpleNetwork )
+    && ( NULL != pSimpleNetwork->Mode )
+    && (( !bReset ) || ( bReset && ( NULL != pNew )))) {
+    //
+    // Verify that the adapter is already started
+    //
+    pNicDevice = DEV_FROM_SIMPLE_NETWORK ( pSimpleNetwork );
+    pMode = pSimpleNetwork->Mode;
+    if ( EfiSimpleNetworkStarted == pMode->State ) {
+      //
+      // Determine the adapter MAC address
+      //
+      if ( bReset ) {
+        //
+        // Use the permanent address
+        //
+        CopyMem ( &pMode->CurrentAddress,
+                  &pMode->PermanentAddress,
+                  sizeof ( pMode->CurrentAddress ));
+      }
+      else {
+        //
+        // Use the specified address
+        //
+        CopyMem ( &pMode->CurrentAddress,
+                  pNew,
+                  sizeof ( pMode->CurrentAddress ));
+      }
+
+      //
+      // Update the address on the adapter
+      //
+      Status = Ax88772MacAddressSet ( pNicDevice, &pMode->CurrentAddress.Addr[0]);
+    }
+    else {
+      Status = EFI_NOT_STARTED;
+    }
+  }
+  else {
+    Status = EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Return the operation status
+  //
+  DBG_EXIT_STATUS ( Status );
+  return Status;
+}
+
+
+/**
+  This function resets or collects the statistics on a network interface.
+  If the size of the statistics table specified by StatisticsSize is not
+  big enough for all of the statistics that are collected by the network
+  interface, then a partial buffer of statistics is returned in
+  StatisticsTable.
+
+  @param [in] pSimpleNetwork    Protocol instance pointer
+  @param [in] bReset            Set to TRUE to reset the statistics for the network interface.
+  @param [in, out] pStatisticsSize  On input the size, in bytes, of StatisticsTable.  On output
+                                the size, in bytes, of the resulting table of statistics.
+  @param [out] pStatisticsTable A pointer to the EFI_NETWORK_STATISTICS structure that
+                                conains the statistics.
+
+  @retval EFI_SUCCESS           This operation was successful.
+  @retval EFI_NOT_STARTED       The network interface was not started.
+  @retval EFI_BUFFER_TOO_SMALL  The pStatisticsTable is NULL or the buffer is too small.
+  @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid
+                                EFI_SIMPLE_NETWORK_PROTOCOL structure.
+  @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
+  @retval EFI_UNSUPPORTED       The increased buffer size feature is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+SN_Statistics (
+  IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork,
+  IN BOOLEAN bReset,
+  IN OUT UINTN * pStatisticsSize,
+  OUT EFI_NETWORK_STATISTICS * pStatisticsTable
+  )
+{
+  EFI_STATUS Status;
+
+  DBG_ENTER ( );
+
+  //
+  // This is not currently supported
+  //
+  Status = EFI_UNSUPPORTED;
+
+  //
+  // Return the operation status
+  //
+  DBG_EXIT_STATUS ( Status );
+  return Status;
+}
+
+
+/**
+  This function stops a network interface.  This call is only valid
+  if the network interface is in the started state.
+
+  @param [in] pSimpleNetwork    Protocol instance pointer
+
+  @retval EFI_SUCCESS           This operation was successful.
+  @retval EFI_NOT_STARTED       The network interface was not started.
+  @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid
+                                EFI_SIMPLE_NETWORK_PROTOCOL structure.
+  @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
+  @retval EFI_UNSUPPORTED       The increased buffer size feature is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+SN_Stop (
+  IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork
+  )
+{
+  EFI_SIMPLE_NETWORK_MODE * pMode;
+  EFI_STATUS Status;
+  
+  DBG_ENTER ( );
+  
+  //
+  // Verify the parameters
+  //
+  if (( NULL != pSimpleNetwork ) && ( NULL != pSimpleNetwork->Mode )) {
+    //
+    // Determine if the interface is started
+    //
+    pMode = pSimpleNetwork->Mode;
+    if ( EfiSimpleNetworkStopped != pMode->State ) {
+      if ( EfiSimpleNetworkStarted == pMode->State ) {
+        //
+        //  Release the resources acquired in SN_Start
+        //
+
+        //
+        //  Mark the adapter as stopped
+        //
+        pMode->State = EfiSimpleNetworkStopped;
+        Status = EFI_SUCCESS;
+      }
+      else {
+        Status = EFI_UNSUPPORTED;
+      }
+    }
+    else {
+      Status = EFI_NOT_STARTED;
+    }
+  }
+  else {
+    Status = EFI_INVALID_PARAMETER;
+  }
+  
+  //
+  // Return the operation status
+  //
+  DBG_EXIT_STATUS ( Status );
+  return Status;
+}
+
+
+/**
+  This function releases the memory buffers assigned in the Initialize() call.
+  Pending transmits and receives are lost, and interrupts are cleared and disabled.
+  After this call, only Initialize() and Stop() calls may be used.
+
+  @param [in] pSimpleNetwork    Protocol instance pointer
+
+  @retval EFI_SUCCESS           This operation was successful.
+  @retval EFI_NOT_STARTED       The network interface was not started.
+  @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid
+                                EFI_SIMPLE_NETWORK_PROTOCOL structure.
+  @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
+  @retval EFI_UNSUPPORTED       The increased buffer size feature is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+SN_Shutdown (
+  IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork
+  )
+{
+  EFI_SIMPLE_NETWORK_MODE * pMode;
+  UINT32 RxFilter;
+  EFI_STATUS Status;
+  
+  DBG_ENTER ( );
+  
+  //
+  // Verify the parameters
+  //
+  if (( NULL != pSimpleNetwork ) && ( NULL != pSimpleNetwork->Mode )) {
+    //
+    // Determine if the interface is already started
+    //
+    pMode = pSimpleNetwork->Mode;
+    if ( EfiSimpleNetworkInitialized == pMode->State ) {
+      //
+      // Stop the adapter
+      //
+      RxFilter = pMode->ReceiveFilterSetting;
+      pMode->ReceiveFilterSetting = 0;
+      Status = SN_Reset ( pSimpleNetwork, FALSE );
+      pMode->ReceiveFilterSetting = RxFilter;
+      if ( !EFI_ERROR ( Status )) {
+        //
+        // Release the resources acquired by SN_Initialize
+        //
+
+        //
+        // Update the network state
+        //
+        pMode->State = EfiSimpleNetworkStarted;
+      }
+    }
+    else {
+      Status = EFI_NOT_STARTED;
+    }
+  }
+  else {
+    Status = EFI_INVALID_PARAMETER;
+  }
+  
+  //
+  // Return the operation status
+  //
+  DBG_EXIT_STATUS ( Status );
+  return Status;
+}
+
+
+/**
+  Send a packet over the network.
+
+  This function places the packet specified by Header and Buffer on
+  the transmit queue.  This function performs a non-blocking transmit
+  operation.  When the transmit is complete, the buffer is returned
+  via the GetStatus() call.
+
+  This routine calls ::Ax88772Rx to empty the network adapter of
+  receive packets.  The routine then passes the transmit packet
+  to the network adapter.
+
+  @param [in] pSimpleNetwork    Protocol instance pointer
+  @param [in] HeaderSize        The size, in bytes, of the media header to be filled in by
+                                the Transmit() function.  If HeaderSize is non-zero, then
+                                it must be equal to SimpleNetwork->Mode->MediaHeaderSize
+                                and DestAddr and Protocol parameters must not be NULL.
+  @param [in] BufferSize        The size, in bytes, of the entire packet (media header and
+                                data) to be transmitted through the network interface.
+  @param [in] pBuffer           A pointer to the packet (media header followed by data) to
+                                to be transmitted.  This parameter can not be NULL.  If
+                                HeaderSize is zero, then the media header is Buffer must
+                                already be filled in by the caller.  If HeaderSize is nonzero,
+                                then the media header will be filled in by the Transmit()
+                                function.
+  @param [in] pSrcAddr          The source HW MAC address.  If HeaderSize is zero, then
+                                this parameter is ignored.  If HeaderSize is nonzero and
+                                SrcAddr is NULL, then SimpleNetwork->Mode->CurrentAddress
+                                is used for the source HW MAC address.
+  @param [in] pDestAddr         The destination HW MAC address.  If HeaderSize is zero, then
+                                this parameter is ignored.
+  @param [in] pProtocol         The type of header to build.  If HeaderSize is zero, then
+                                this parameter is ignored.
+
+  @retval EFI_SUCCESS           This operation was successful.
+  @retval EFI_NOT_STARTED       The network interface was not started.
+  @retval EFI_NOT_READY         The network interface is too busy to accept this transmit request.
+  @retval EFI_BUFFER_TOO_SMALL  The BufferSize parameter is too small.
+  @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid
+                                EFI_SIMPLE_NETWORK_PROTOCOL structure.
+  @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
+
+**/
+EFI_STATUS
+EFIAPI
+SN_Transmit (
+  IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork,
+  IN UINTN HeaderSize,
+  IN UINTN BufferSize,
+  IN VOID * pBuffer,
+  IN EFI_MAC_ADDRESS * pSrcAddr,
+  IN EFI_MAC_ADDRESS * pDestAddr,
+  IN UINT16 * pProtocol
+  )
+{
+  RX_TX_PACKET Packet;
+  ETHERNET_HEADER * pHeader;
+  EFI_SIMPLE_NETWORK_MODE * pMode;
+  NIC_DEVICE * pNicDevice;
+  EFI_USB_IO_PROTOCOL * pUsbIo;
+  EFI_STATUS Status;
+  EFI_TPL TplPrevious;
+  UINTN TransferLength;
+  UINT32 TransferStatus;
+  UINT16 Type;
+
+  DBG_ENTER ( );
+
+  //
+  // Verify the parameters
+  //
+  if (( NULL != pSimpleNetwork ) && ( NULL != pSimpleNetwork->Mode )) {
+    //
+    // The interface must be running
+    //
+    pMode = pSimpleNetwork->Mode;
+    if ( EfiSimpleNetworkInitialized == pMode->State ) {
+      //
+      //  Synchronize with Ax88772Timer
+      //
+      VERIFY_TPL ( TPL_AX88772 );
+      TplPrevious = gBS->RaiseTPL ( TPL_AX88772 );
+
+      //
+      // Update the link status
+      //
+      pNicDevice = DEV_FROM_SIMPLE_NETWORK ( pSimpleNetwork );
+
+      //
+      //No need to call receive to receive packet
+      //
+      //Ax88772Rx ( pNicDevice, FALSE );
+      pMode->MediaPresent = pNicDevice->bLinkUp;
+
+      //
+      //  Release the synchronization with Ax88772Timer
+      //
+      gBS->RestoreTPL ( TplPrevious );
+      if ( pMode->MediaPresent ) {
+        //
+        //  Copy the packet into the USB buffer
+        //
+        CopyMem ( &Packet.Data[0], pBuffer, BufferSize );
+        Packet.Length = (UINT16) BufferSize;
+
+        //
+        //  Transmit the packet
+        //
+        pHeader = (ETHERNET_HEADER *) &Packet.Data[0];
+        if ( 0 != HeaderSize ) {
+          if ( NULL != pDestAddr ) {
+            CopyMem ( &pHeader->dest_addr, pDestAddr, PXE_HWADDR_LEN_ETHER );
+          }
+          if ( NULL != pSrcAddr ) {
+            CopyMem ( &pHeader->src_addr, pSrcAddr, PXE_HWADDR_LEN_ETHER );
+          }
+          else {
+            CopyMem ( &pHeader->src_addr, &pMode->CurrentAddress.Addr[0], PXE_HWADDR_LEN_ETHER );
+          }
+          if ( NULL != pProtocol ) {
+            Type = *pProtocol;
+          }
+          else {
+            Type = Packet.Length;
+          }
+          Type = (UINT16)(( Type >> 8 ) | ( Type << 8 ));
+          pHeader->type = Type;
+        }
+        if ( Packet.Length < MIN_ETHERNET_PKT_SIZE ) {
+          Packet.Length = MIN_ETHERNET_PKT_SIZE;
+          ZeroMem ( &Packet.Data[ BufferSize ],
+                    Packet.Length - BufferSize );
+        }
+        DEBUG (( DEBUG_TX | DEBUG_INFO,
+                  "TX: %02x-%02x-%02x-%02x-%02x-%02x  %02x-%02x-%02x-%02x-%02x-%02x  %02x-%02x  %d bytes\r\n",
+                  Packet.Data[0],
+                  Packet.Data[1],
+                  Packet.Data[2],
+                  Packet.Data[3],
+                  Packet.Data[4],
+                  Packet.Data[5],
+                  Packet.Data[6],
+                  Packet.Data[7],
+                  Packet.Data[8],
+                  Packet.Data[9],
+                  Packet.Data[10],
+                  Packet.Data[11],
+                  Packet.Data[12],
+                  Packet.Data[13],
+                  Packet.Length ));
+        Packet.LengthBar = ~Packet.Length;
+        TransferLength = sizeof ( Packet.Length )
+                       + sizeof ( Packet.LengthBar )
+                       + Packet.Length;
+
+        //
+        //  Work around USB bus driver bug where a timeout set by receive
+        //  succeeds but the timeout expires immediately after, causing the
+        //  transmit operation to timeout.
+        //
+        pUsbIo = pNicDevice->pUsbIo;
+        Status = pUsbIo->UsbBulkTransfer ( pUsbIo,
+                                           BULK_OUT_ENDPOINT,
+                                           &Packet.Length,
+                                           &TransferLength,
+                                           0xfffffffe,
+                                           &TransferStatus );
+        if ( !EFI_ERROR ( Status )) {
+          Status = TransferStatus;
+        }
+        if (( !EFI_ERROR ( Status ))
+          && ( TransferLength != (UINTN)( Packet.Length + 4 ))) {
+          Status = EFI_WARN_WRITE_FAILURE;
+        }
+        if ( EFI_SUCCESS == Status ) {
+          pNicDevice->pTxBuffer = pBuffer;
+        }
+        else {
+          DEBUG (( DEBUG_ERROR | DEBUG_INFO,
+                    "Ax88772 USB transmit error, TransferLength: %d, Status: %r\r\n",
+                    sizeof ( Packet.Length ) + Packet.Length,
+                    Status ));
+          //
+          //  Reset the controller to fix the error
+          //
+          if ( EFI_DEVICE_ERROR == Status ) {
+            SN_Reset ( pSimpleNetwork, FALSE );
+          }
+        }
+      }
+      else {
+        //
+        // No packets available.
+        //
+        Status = EFI_NOT_READY;
+      }
+    }
+    else {
+      Status = EFI_NOT_STARTED;
+    }
+  }
+  else {
+    DEBUG (( DEBUG_ERROR | DEBUG_INFO,
+              "Ax88772 invalid transmit parameter\r\n"
+              "  0x%08x: HeaderSize\r\n"
+              "  0x%08x: BufferSize\r\n"
+              "  0x%08x: Buffer\r\n"
+              "  0x%08x: SrcAddr\r\n"
+              "  0x%08x: DestAddr\r\n"
+              "  0x%04x:     Protocol\r\n",
+              HeaderSize,
+              BufferSize,
+              pBuffer,
+              pSrcAddr,
+              pDestAddr,
+              pProtocol ));
+    Status = EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Return the operation status
+  //
+  DBG_EXIT_STATUS ( Status );
+  return Status;
+}
diff --git a/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/Ax88772.c b/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/Ax88772.c
new file mode 100644
index 0000000000..12684a6bd1
--- /dev/null
+++ b/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/Ax88772.c
@@ -0,0 +1,875 @@
+/** @file
+  Implement the interface to the AX88772 Ethernet controller.
+
+  This module implements the interface to the ASIX AX88772
+  USB to Ethernet MAC with integrated 10/100 PHY.  Note that this implementation
+  only supports the integrated PHY since no other test cases were available.
+
+  Copyright (c) 2011, Intel Corporation. All rights reserved.
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "Ax88772.h"
+
+
+/**
+  Compute the CRC 
+
+  @param [in] pMacAddress      Address of a six byte buffer to containing the MAC address.
+
+  @returns The CRC-32 value associated with this MAC address
+
+**/
+UINT32
+Ax88772Crc (
+  IN UINT8 * pMacAddress
+  )
+{
+  UINT32 BitNumber;
+  INT32 Carry;
+  INT32 Crc;
+  UINT32 Data;
+  UINT8 * pEnd;
+
+  //
+  //  Walk the MAC address
+  //
+  Crc = -1;
+  pEnd = &pMacAddress[ PXE_HWADDR_LEN_ETHER ];
+  while ( pEnd > pMacAddress ) {
+    Data = *pMacAddress++;
+    //
+    //  CRC32: x32 + x26 + x23 + x22 + x16 + x12 + x11 + x10 + x8 + x7 + x5 + x4 + x2 + x + 1
+    //
+    //          1 0000 0100 1100 0001 0001 1101 1011 0111
+    //
+    for ( BitNumber = 0; 8 > BitNumber; BitNumber++ ) {
+      Carry = (( Crc >> 31 ) & 1 ) ^ ( Data & 1 );
+      Crc <<= 1;
+      if ( 0 != Carry ) {
+        Crc ^= 0x04c11db7;
+      }
+      Data >>= 1;
+    }
+  }
+  //
+  //  Return the CRC value
+  //
+  return (UINT32) Crc;
+}
+
+
+/**
+  Get the MAC address
+
+  This routine calls ::Ax88772UsbCommand to request the MAC
+  address from the network adapter.
+
+  @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
+  @param [out] pMacAddress      Address of a six byte buffer to receive the MAC address.
+
+  @retval EFI_SUCCESS          The MAC address is available.
+  @retval other                The MAC address is not valid.
+
+**/
+EFI_STATUS
+Ax88772MacAddressGet (
+  IN NIC_DEVICE * pNicDevice,
+  OUT UINT8 * pMacAddress
+  )
+{
+  USB_DEVICE_REQUEST SetupMsg;
+  EFI_STATUS Status;
+
+  //
+  //  Set the register address.
+  //
+  SetupMsg.RequestType = USB_ENDPOINT_DIR_IN
+                       | USB_REQ_TYPE_VENDOR
+                       | USB_TARGET_DEVICE;
+  SetupMsg.Request = CMD_MAC_ADDRESS_READ;
+  SetupMsg.Value = 0;
+  SetupMsg.Index = 0;
+  SetupMsg.Length = PXE_HWADDR_LEN_ETHER;
+
+  //
+  //  Read the PHY register
+  //
+  Status = Ax88772UsbCommand ( pNicDevice,
+                               &SetupMsg,
+                               pMacAddress );
+  return Status;
+}
+
+
+/**
+  Set the MAC address
+
+  This routine calls ::Ax88772UsbCommand to set the MAC address
+  in the network adapter.
+
+  @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
+  @param [in] pMacAddress      Address of a six byte buffer to containing the new MAC address.
+
+  @retval EFI_SUCCESS          The MAC address was set.
+  @retval other                The MAC address was not set.
+
+**/
+EFI_STATUS
+Ax88772MacAddressSet (
+  IN NIC_DEVICE * pNicDevice,
+  IN UINT8 * pMacAddress
+  )
+{
+  USB_DEVICE_REQUEST SetupMsg;
+  EFI_STATUS Status;
+
+  //
+  //  Set the register address.
+  //
+  SetupMsg.RequestType = USB_REQ_TYPE_VENDOR
+                       | USB_TARGET_DEVICE;
+  SetupMsg.Request = CMD_MAC_ADDRESS_WRITE;
+  SetupMsg.Value = 0;
+  SetupMsg.Index = 0;
+  SetupMsg.Length = PXE_HWADDR_LEN_ETHER;
+  
+  //
+  //  Read the PHY register
+  //
+  Status = Ax88772UsbCommand ( pNicDevice,
+                               &SetupMsg,
+                               pMacAddress );
+  return Status;
+}
+
+/**
+  Clear the multicast hash table
+
+  @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
+
+**/
+VOID
+Ax88772MulticastClear (
+  IN NIC_DEVICE * pNicDevice
+  )
+{
+  int i = 0;
+  //
+  // Clear the multicast hash table
+  //
+  for ( i = 0 ; i < 8 ; i ++ )
+     pNicDevice->MulticastHash[0] = 0;
+}
+
+/**
+  Enable a multicast address in the multicast hash table
+
+  This routine calls ::Ax88772Crc to compute the hash bit for
+  this MAC address.
+
+  @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
+  @param [in] pMacAddress      Address of a six byte buffer to containing the MAC address.
+
+**/
+VOID
+Ax88772MulticastSet (
+  IN NIC_DEVICE * pNicDevice,
+  IN UINT8 * pMacAddress
+  )
+{
+  UINT32 Crc;
+
+  //
+  //  Compute the CRC on the destination address
+  //
+  Crc = Ax88772Crc ( pMacAddress ) >> 26;
+
+  //
+  //  Set the bit corresponding to the destination address
+  //
+   pNicDevice->MulticastHash [ Crc >> 3 ] |= ( 1<< (Crc& 7));
+}
+
+/**
+  Start the link negotiation
+
+  This routine calls ::Ax88772PhyWrite to start the PHY's link
+  negotiation.
+
+  @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
+
+  @retval EFI_SUCCESS          The link negotiation was started.
+  @retval other                Failed to start the link negotiation.
+
+**/
+EFI_STATUS
+Ax88772NegotiateLinkStart (
+  IN NIC_DEVICE * pNicDevice
+  )
+{
+  UINT16 Control;
+  EFI_STATUS Status;
+  int i; 
+  //
+  // Set the supported capabilities.
+  //
+  Status = Ax88772PhyWrite ( pNicDevice,
+                             PHY_ANAR,
+                             AN_CSMA_CD
+                             | AN_TX_FDX | AN_TX_HDX
+                             | AN_10_FDX | AN_10_HDX );
+  if ( !EFI_ERROR ( Status )) {
+    //
+    // Set the link speed and duplex
+    //
+    Control = BMCR_AUTONEGOTIATION_ENABLE
+            | BMCR_RESTART_AUTONEGOTIATION;
+    if ( pNicDevice->b100Mbps ) {
+      Control |= BMCR_100MBPS;
+    }
+    if ( pNicDevice->bFullDuplex ) {
+      Control |= BMCR_FULL_DUPLEX;
+    }
+    Status = Ax88772PhyWrite ( pNicDevice, PHY_BMCR, Control );
+  }
+  
+  if (!EFI_ERROR(Status)) {
+    i = 0;
+    do {
+      
+        if (pNicDevice->bComplete && pNicDevice->bLinkUp) {
+            pNicDevice->SimpleNetwork.Mode->MediaPresent 
+               = pNicDevice->bLinkUp & pNicDevice->bComplete;
+           break;
+       }
+       else {
+            gBS->Stall(AUTONEG_DELAY);
+            Status = Ax88772NegotiateLinkComplete ( pNicDevice,
+                                            &pNicDevice->PollCount,
+                                            &pNicDevice->bComplete,
+                                            &pNicDevice->bLinkUp,
+                                            &pNicDevice->b100Mbps,
+                                            &pNicDevice->bFullDuplex );
+            i++;
+        }
+    }while(!pNicDevice->bLinkUp && i < AUTONEG_POLLCNT);
+  }
+  return Status;
+}
+
+
+/**
+  Complete the negotiation of the PHY link
+
+  This routine calls ::Ax88772PhyRead to determine if the
+  link negotiation is complete.
+
+  @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
+  @param [in, out] pPollCount  Address of number of times this routine was polled
+  @param [out] pbComplete      Address of boolean to receive complate status.
+  @param [out] pbLinkUp        Address of boolean to receive link status, TRUE=up.
+  @param [out] pbHiSpeed       Address of boolean to receive link speed, TRUE=100Mbps.
+  @param [out] pbFullDuplex    Address of boolean to receive link duplex, TRUE=full.
+
+  @retval EFI_SUCCESS          The MAC address is available.
+  @retval other                The MAC address is not valid.
+
+**/
+EFI_STATUS
+Ax88772NegotiateLinkComplete (
+  IN NIC_DEVICE * pNicDevice,
+  IN OUT UINTN * pPollCount,
+  OUT BOOLEAN * pbComplete,
+  OUT BOOLEAN * pbLinkUp,
+  OUT BOOLEAN * pbHiSpeed,
+  OUT BOOLEAN * pbFullDuplex
+  )
+{
+  UINT16 Mask;
+  UINT16 PhyData;
+  EFI_STATUS  Status;
+ 
+  //
+  //  Determine if the link is up.
+  //
+  *pbComplete = FALSE;  
+
+  //
+  //  Get the link status
+  //
+  Status = Ax88772PhyRead ( pNicDevice,
+                            PHY_BMSR,
+                            &PhyData );
+
+  if ( !EFI_ERROR ( Status )) {
+      *pbLinkUp = (BOOLEAN)( 0 != ( PhyData & BMSR_LINKST ));
+      if ( 0 == *pbLinkUp ) {
+        DEBUG (( EFI_D_INFO, "Link Down\n" ));
+      }      
+      else {
+         *pbComplete = (BOOLEAN)( 0 != ( PhyData & 0x20 ));
+         if ( 0 == *pbComplete ) {
+              DEBUG (( EFI_D_INFO, "Autoneg is not yet Complete\n" ));
+        }
+        else {
+          Status = Ax88772PhyRead ( pNicDevice,
+                                PHY_ANLPAR,
+                                &PhyData );
+          if ( !EFI_ERROR ( Status )) {
+            //
+            //  Autonegotiation is complete
+            //  Determine the link speed.
+            //
+            *pbHiSpeed = (BOOLEAN)( 0 != ( PhyData & ( AN_TX_FDX | AN_TX_HDX )));
+
+            //
+            //  Determine the link duplex.
+            //
+            Mask = ( *pbHiSpeed ) ? AN_TX_FDX : AN_10_FDX;
+            *pbFullDuplex = (BOOLEAN)( 0 != ( PhyData & Mask ));
+          }
+        } 
+      }
+  } 
+  else {
+      DEBUG (( EFI_D_ERROR, "Failed to read BMCR\n" ));
+  }
+  return Status;
+}
+
+
+/**
+  Read a register from the PHY
+
+  This routine calls ::Ax88772UsbCommand to read a PHY register.
+
+  @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
+  @param [in] RegisterAddress  Number of the register to read.
+  @param [in, out] pPhyData    Address of a buffer to receive the PHY register value
+
+  @retval EFI_SUCCESS          The PHY data is available.
+  @retval other                The PHY data is not valid.
+
+**/
+EFI_STATUS
+Ax88772PhyRead (
+  IN NIC_DEVICE * pNicDevice,
+  IN UINT8 RegisterAddress,
+  IN OUT UINT16 * pPhyData
+  )
+{
+  USB_DEVICE_REQUEST SetupMsg;
+  EFI_STATUS Status;
+
+  //
+  //  Request access to the PHY
+  //
+  SetupMsg.RequestType = USB_REQ_TYPE_VENDOR
+                       | USB_TARGET_DEVICE;
+  SetupMsg.Request = CMD_PHY_ACCESS_SOFTWARE;   
+  SetupMsg.Value = 0;
+  SetupMsg.Index = 0;
+  SetupMsg.Length = 0;
+  Status = Ax88772UsbCommand ( pNicDevice,
+                               &SetupMsg,
+                               NULL );
+  if ( !EFI_ERROR ( Status )) {
+    //
+    //  Read the PHY register address.
+    //
+    SetupMsg.RequestType = USB_ENDPOINT_DIR_IN
+                         | USB_REQ_TYPE_VENDOR
+                         | USB_TARGET_DEVICE;
+    SetupMsg.Request = CMD_PHY_REG_READ;
+    SetupMsg.Value = pNicDevice->PhyId;
+    SetupMsg.Index = RegisterAddress;
+    SetupMsg.Length = sizeof ( *pPhyData );
+    Status = Ax88772UsbCommand ( pNicDevice,
+                                 &SetupMsg,
+                                 pPhyData );
+    if ( !EFI_ERROR ( Status )) {
+
+      //
+      //  Release the PHY to the hardware
+      //
+      SetupMsg.RequestType = USB_REQ_TYPE_VENDOR
+                           | USB_TARGET_DEVICE;
+      SetupMsg.Request = CMD_PHY_ACCESS_HARDWARE;
+      SetupMsg.Value = 0;
+      SetupMsg.Index = 0;
+      SetupMsg.Length = 0;
+      Status = Ax88772UsbCommand ( pNicDevice,
+                                   &SetupMsg,
+                                   NULL );
+    }
+  }
+  return Status;
+}
+
+
+/**
+  Write to a PHY register
+
+  This routine calls ::Ax88772UsbCommand to write a PHY register.
+
+  @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
+  @param [in] RegisterAddress  Number of the register to read.
+  @param [in] PhyData          Address of a buffer to receive the PHY register value
+
+  @retval EFI_SUCCESS          The PHY data was written.
+  @retval other                Failed to wwrite the PHY register.
+
+**/
+EFI_STATUS
+Ax88772PhyWrite (
+  IN NIC_DEVICE * pNicDevice,
+  IN UINT8 RegisterAddress,
+  IN UINT16 PhyData
+  )
+{
+  USB_DEVICE_REQUEST SetupMsg;
+  EFI_STATUS Status;
+
+  //
+  //  Request access to the PHY
+  //
+  SetupMsg.RequestType = USB_REQ_TYPE_VENDOR
+                       | USB_TARGET_DEVICE;
+  SetupMsg.Request = CMD_PHY_ACCESS_SOFTWARE;
+  SetupMsg.Value = 0;
+  SetupMsg.Index = 0;
+  SetupMsg.Length = 0;
+  Status = Ax88772UsbCommand ( pNicDevice,
+                               &SetupMsg,
+                               NULL );
+  if ( !EFI_ERROR ( Status )) {
+    //
+    //  Write the PHY register
+    //
+    SetupMsg.RequestType = USB_REQ_TYPE_VENDOR
+                         | USB_TARGET_DEVICE;
+    SetupMsg.Request = CMD_PHY_REG_WRITE;
+    SetupMsg.Value = pNicDevice->PhyId;
+    SetupMsg.Index = RegisterAddress;
+    SetupMsg.Length = sizeof ( PhyData );
+    Status = Ax88772UsbCommand ( pNicDevice,
+                                 &SetupMsg,
+                                 &PhyData );
+    if ( !EFI_ERROR ( Status )) {
+
+      //
+      //  Release the PHY to the hardware
+      //
+      SetupMsg.RequestType = USB_REQ_TYPE_VENDOR
+                           | USB_TARGET_DEVICE;
+      SetupMsg.Request = CMD_PHY_ACCESS_HARDWARE;
+      SetupMsg.Value = 0;
+      SetupMsg.Index = 0;
+      SetupMsg.Length = 0;
+      Status = Ax88772UsbCommand ( pNicDevice,
+                                   &SetupMsg,
+                                   NULL );
+    }
+  }
+
+  return Status;
+}
+
+
+/**
+  Reset the AX88772
+
+  This routine uses ::Ax88772UsbCommand to reset the network
+  adapter.  This routine also uses ::Ax88772PhyWrite to reset
+  the PHY.
+
+  @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
+
+  @retval EFI_SUCCESS          The MAC address is available.
+  @retval other                The MAC address is not valid.
+
+**/
+EFI_STATUS
+Ax88772Reset (
+  IN NIC_DEVICE * pNicDevice
+  )
+{
+  USB_DEVICE_REQUEST SetupMsg;
+  EFI_STATUS Status;
+  
+  EFI_USB_IO_PROTOCOL *pUsbIo;
+  EFI_USB_DEVICE_DESCRIPTOR Device;
+  
+  pUsbIo = pNicDevice->pUsbIo;
+  Status = pUsbIo->UsbGetDeviceDescriptor ( pUsbIo, &Device );
+
+	if (EFI_ERROR(Status)) goto err; 
+
+  SetupMsg.RequestType = USB_REQ_TYPE_VENDOR
+                           | USB_TARGET_DEVICE;
+  SetupMsg.Request = CMD_PHY_ACCESS_HARDWARE;
+  SetupMsg.Value = 0;
+  SetupMsg.Index = 0;
+  SetupMsg.Length = 0;
+  Status = Ax88772UsbCommand ( pNicDevice,
+                                &SetupMsg,
+                                NULL );
+                                   
+  if (EFI_ERROR(Status)) goto err;                                 
+                                   
+  SetupMsg.RequestType = USB_REQ_TYPE_VENDOR
+                          | USB_TARGET_DEVICE;
+      SetupMsg.Request = CMD_PHY_SELECT;
+      SetupMsg.Value = SPHY_PSEL;
+      SetupMsg.Index = 0;
+      SetupMsg.Length = 0;
+      Status = Ax88772UsbCommand ( pNicDevice,
+                                    &SetupMsg,
+                                    NULL );
+                                    
+  if (EFI_ERROR(Status)) goto err;  
+                                     
+  SetupMsg.RequestType = USB_REQ_TYPE_VENDOR
+                          | USB_TARGET_DEVICE;                                
+  SetupMsg.Request = CMD_RESET;
+      SetupMsg.Value = SRR_IPRL ;
+      SetupMsg.Index = 0;
+      SetupMsg.Length = 0;
+      Status = Ax88772UsbCommand ( pNicDevice,
+                                   &SetupMsg,
+                                   NULL );  
+                                   
+  if (EFI_ERROR(Status)) goto err;  
+                                   
+  SetupMsg.RequestType = USB_REQ_TYPE_VENDOR
+                          | USB_TARGET_DEVICE;                                
+  SetupMsg.Request = CMD_RESET;
+        SetupMsg.Value = SRR_IPPD | SRR_IPRL ;
+        SetupMsg.Index = 0;
+        SetupMsg.Length = 0;
+        Status = Ax88772UsbCommand ( pNicDevice,
+                                    &SetupMsg,
+                                    NULL );
+                                   
+  gBS->Stall ( 200000 );
+    
+  if (EFI_ERROR(Status)) goto err;  
+    
+  SetupMsg.RequestType = USB_REQ_TYPE_VENDOR
+                          | USB_TARGET_DEVICE;
+  SetupMsg.Request = CMD_RESET;
+  SetupMsg.Value =  SRR_IPRL  ;
+  SetupMsg.Index = 0;
+  SetupMsg.Length = 0;
+  Status = Ax88772UsbCommand ( pNicDevice,
+                                &SetupMsg,
+                                NULL );   
+                                    
+  gBS->Stall ( 200000 ); 
+     
+  if (EFI_ERROR(Status)) goto err;  
+     
+  SetupMsg.RequestType = USB_REQ_TYPE_VENDOR
+                          | USB_TARGET_DEVICE;
+  SetupMsg.Request = CMD_RESET;
+  SetupMsg.Value = 0;
+  SetupMsg.Index = 0;
+  SetupMsg.Length = 0;
+  Status = Ax88772UsbCommand ( pNicDevice,
+                                    &SetupMsg,
+                                    NULL );
+                                    
+  if (EFI_ERROR(Status)) goto err;                                
+                                    
+  SetupMsg.RequestType = USB_REQ_TYPE_VENDOR
+                          | USB_TARGET_DEVICE;
+  SetupMsg.Request = CMD_PHY_SELECT;
+  SetupMsg.Value = SPHY_PSEL;
+  SetupMsg.Index = 0;
+  SetupMsg.Length = 0;
+  Status = Ax88772UsbCommand ( pNicDevice,
+                                    &SetupMsg,
+                                    NULL ); 
+                                    
+  if (EFI_ERROR(Status)) goto err;                                
+                                    
+  SetupMsg.RequestType = USB_REQ_TYPE_VENDOR
+                          | USB_TARGET_DEVICE;
+  SetupMsg.Request = CMD_RESET;
+  SetupMsg.Value =  SRR_IPRL | SRR_BZ | SRR_BZTYPE;
+  SetupMsg.Index = 0;
+  SetupMsg.Length = 0;
+  Status = Ax88772UsbCommand ( pNicDevice,
+                                    &SetupMsg,
+                                    NULL );
+                                    
+  if (EFI_ERROR(Status)) goto err;                                
+                                    
+  SetupMsg.RequestType = USB_REQ_TYPE_VENDOR
+                        | USB_TARGET_DEVICE;
+  SetupMsg.Request = CMD_RX_CONTROL_WRITE;
+  SetupMsg.Value = 0;
+  SetupMsg.Index = 0;
+  SetupMsg.Length = 0;
+  Status = Ax88772UsbCommand ( pNicDevice,
+                                  &SetupMsg,
+                                  NULL );
+                                  
+  if (EFI_ERROR(Status)) goto err;  
+
+  if (pNicDevice->Flags != FLAG_TYPE_AX88772) {
+        SetupMsg.RequestType = USB_REQ_TYPE_VENDOR
+                        | USB_TARGET_DEVICE;
+        SetupMsg.Request = CMD_RXQTC;
+        SetupMsg.Value = 0x8000;
+        SetupMsg.Index = 0x8001;
+        SetupMsg.Length = 0;
+        Status = Ax88772UsbCommand ( pNicDevice,
+                                  &SetupMsg,
+                                  NULL );
+  }
+
+err:
+  return Status;
+}
+
+/**
+  Enable or disable the receiver
+
+  This routine calls ::Ax88772UsbCommand to update the
+  receiver state.  This routine also calls ::Ax88772MacAddressSet
+  to establish the MAC address for the network adapter.
+
+  @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
+  @param [in] RxFilter         Simple network RX filter mask value
+
+  @retval EFI_SUCCESS          The MAC address was set.
+  @retval other                The MAC address was not set.
+
+**/
+EFI_STATUS
+Ax88772RxControl (
+  IN NIC_DEVICE * pNicDevice,
+  IN UINT32 RxFilter
+  )
+{
+  UINT16 MediumStatus;
+  UINT16 RxControl;
+  USB_DEVICE_REQUEST SetupMsg;
+  EFI_STATUS Status;
+  EFI_USB_IO_PROTOCOL *pUsbIo;
+  EFI_USB_DEVICE_DESCRIPTOR Device;
+  
+  pUsbIo = pNicDevice->pUsbIo;
+  Status = pUsbIo->UsbGetDeviceDescriptor ( pUsbIo, &Device );
+  
+  if (EFI_ERROR(Status)) {
+    DEBUG (( EFI_D_ERROR, "Failed to get device descriptor\n" ));
+    return Status;
+  }
+
+  //
+  // Enable the receiver if something is to be received
+  //
+  
+  if ( 0 != RxFilter ) {
+    //
+    //  Enable the receiver
+    //
+    SetupMsg.RequestType = USB_ENDPOINT_DIR_IN
+                         | USB_REQ_TYPE_VENDOR
+                         | USB_TARGET_DEVICE;
+    SetupMsg.Request = CMD_MEDIUM_STATUS_READ;    
+    SetupMsg.Value = 0;
+    SetupMsg.Index = 0;
+    SetupMsg.Length = sizeof ( MediumStatus );
+    Status = Ax88772UsbCommand ( pNicDevice,
+                                 &SetupMsg,
+                                 &MediumStatus );
+    if ( !EFI_ERROR ( Status )) {
+      if ( 0 == ( MediumStatus & MS_RE )) {
+        MediumStatus |= MS_RE | MS_ONE;
+        
+        if ( pNicDevice->bFullDuplex )
+          MediumStatus |= MS_TFC | MS_RFC | MS_FD;
+        else
+          MediumStatus &= ~(MS_TFC | MS_RFC | MS_FD);
+        
+        if ( pNicDevice->b100Mbps )
+          MediumStatus |= MS_PS;
+        else
+          MediumStatus &= ~MS_PS;
+        
+        SetupMsg.RequestType = USB_REQ_TYPE_VENDOR
+                             | USB_TARGET_DEVICE;
+        SetupMsg.Request = CMD_MEDIUM_STATUS_WRITE;
+        SetupMsg.Value = MediumStatus;
+        SetupMsg.Index = 0;
+        SetupMsg.Length = 0;
+        Status = Ax88772UsbCommand ( pNicDevice,
+                                     &SetupMsg,
+                                     NULL );
+        if ( EFI_ERROR ( Status )) {
+            DEBUG (( EFI_D_ERROR, "Failed to enable receiver, Status: %r\r\n",
+              Status ));
+        }
+      }
+    }
+    else {
+        DEBUG (( EFI_D_ERROR, "Failed to read receiver status, Status: %r\r\n",
+              Status ));
+    }
+  }
+  
+  RxControl = RXC_SO | RXC_RH1M;  
+  //
+  //  Enable multicast if requested
+  //
+  if ( 0 != ( RxFilter & EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST )) {
+      RxControl |= RXC_AM;
+      //
+      //  Update the multicast hash table
+      //
+      SetupMsg.RequestType = USB_REQ_TYPE_VENDOR
+                           | USB_TARGET_DEVICE;
+      SetupMsg.Request = CMD_MULTICAST_HASH_WRITE;
+      SetupMsg.Value = 0;
+      SetupMsg.Index = 0;
+      SetupMsg.Length = sizeof ( pNicDevice ->MulticastHash );
+      Status = Ax88772UsbCommand ( pNicDevice,
+                                   &SetupMsg,
+                                   &pNicDevice->MulticastHash );
+  }
+  //
+  //  Enable all multicast if requested
+  //
+  if ( 0 != ( RxFilter & EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST )) {
+      RxControl |= RXC_AMALL;
+  }
+
+  //
+  //  Enable broadcast if requested
+  //
+  if ( 0 != ( RxFilter & EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST )) {
+      RxControl |= RXC_AB;
+  }
+
+  //
+  //  Enable promiscuous mode if requested
+  //
+  if ( 0 != ( RxFilter & EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS )) {
+      RxControl |= RXC_PRO;
+  }
+    
+  //
+  //  Update the receiver control
+  //
+  if (pNicDevice->CurRxControl != RxControl) {
+    SetupMsg.RequestType = USB_REQ_TYPE_VENDOR
+                         | USB_TARGET_DEVICE;
+    SetupMsg.Request = CMD_RX_CONTROL_WRITE;
+    SetupMsg.Value = RxControl;
+    SetupMsg.Index = 0;
+    SetupMsg.Length = 0;
+    Status = Ax88772UsbCommand ( pNicDevice,
+                                 &SetupMsg,
+                                 NULL );
+    if ( !EFI_ERROR ( Status )) {
+      pNicDevice->CurRxControl = RxControl;
+      
+    }
+    else {
+        DEBUG (( EFI_D_ERROR, "ERROR - Failed to set receiver control, Status: %r\r\n",
+            Status ));
+    }
+  }
+  return Status;
+}
+
+
+/**
+  Read an SROM location
+
+  This routine calls ::Ax88772UsbCommand to read data from the
+  SROM.
+
+  @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
+  @param [in] Address          SROM address
+  @param [out] pData           Buffer to receive the data
+
+  @retval EFI_SUCCESS          The read was successful
+  @retval other                The read failed
+
+**/
+EFI_STATUS
+Ax88772SromRead (
+  IN NIC_DEVICE * pNicDevice,
+  IN UINT32 Address,
+  OUT UINT16 * pData
+  )
+{ 
+  return EFI_UNSUPPORTED;
+}
+
+/**
+  Send a command to the USB device.
+
+  @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
+  @param [in] pRequest         Pointer to the request structure
+  @param [in, out] pBuffer     Data buffer address
+
+  @retval EFI_SUCCESS          The USB transfer was successful
+  @retval other                The USB transfer failed
+
+**/
+EFI_STATUS
+Ax88772UsbCommand (
+  IN NIC_DEVICE * pNicDevice,
+  IN USB_DEVICE_REQUEST * pRequest,
+  IN OUT VOID * pBuffer
+  )
+{
+  UINT32 CmdStatus;
+  EFI_USB_DATA_DIRECTION Direction;
+  EFI_USB_IO_PROTOCOL * pUsbIo;
+  EFI_STATUS Status;
+
+  //
+  // Determine the transfer direction
+  //
+  Direction = EfiUsbNoData;
+  if ( 0 != pRequest->Length ) {
+    Direction = ( 0 != ( pRequest->RequestType & USB_ENDPOINT_DIR_IN ))
+              ? EfiUsbDataIn : EfiUsbDataOut;
+  }
+
+  //
+  // Issue the command
+  //
+  pUsbIo = pNicDevice->pUsbIo;
+  Status = pUsbIo->UsbControlTransfer ( pUsbIo,
+                                        pRequest,
+                                        Direction,
+                                        USB_BUS_TIMEOUT,
+                                        pBuffer,
+                                        pRequest->Length,
+                                        &CmdStatus );
+  //
+  // Determine the operation status
+  //
+  if ( !EFI_ERROR ( Status )) {
+    Status = CmdStatus;
+  }
+  else {
+    //
+    // Only use status values associated with the Simple Network protocol
+    //
+    if ( EFI_TIMEOUT == Status ) {
+      Status = EFI_DEVICE_ERROR;
+    }
+  }
+  return Status;
+}
+
diff --git a/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/Ax88772.h b/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/Ax88772.h
new file mode 100644
index 0000000000..365929489b
--- /dev/null
+++ b/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/Ax88772.h
@@ -0,0 +1,1026 @@
+/** @file
+  Definitions for ASIX AX88772 Ethernet adapter.
+
+  Copyright (c) 2011 - 2015, Intel Corporation. All rights reserved.
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef _AX88772_H_
+#define _AX88772_H_
+
+#include <Uefi.h>
+
+#include <Guid/EventGroup.h>
+
+#include <IndustryStandard/Pci.h>
+
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/UefiLib.h>
+#include <Library/UefiRuntimeLib.h>
+
+#include <Protocol/DevicePath.h>
+#include <Protocol/LoadedImage.h>
+#include <Protocol/NetworkInterfaceIdentifier.h>
+#include <Protocol/SimpleNetwork.h>
+#include <Protocol/UsbIo.h>
+
+#define MAX_QUEUE_SIZE 50
+#define MAX_BULKIN_SIZE 16384
+#define HW_HDR_LENGTH 8
+
+
+#define MAX_LINKIDLE_THRESHOLD  20000
+
+
+
+//------------------------------------------------------------------------------
+//  Macros
+//------------------------------------------------------------------------------
+
+#if defined(_MSC_VER)           /* Handle Microsoft VC++ compiler specifics. */
+#define DBG_ENTER()             DEBUG (( 0xffffffff, "Entering " __FUNCTION__ "\n" )) ///<  Display routine entry
+#define DBG_EXIT()              DEBUG (( 0xffffffff, "Exiting " __FUNCTION__ "\n" ))  ///<  Display routine exit
+#define DBG_EXIT_DEC(Status)    DEBUG (( 0xffffffff, "Exiting " __FUNCTION__ ", Status: %d\n", Status ))      ///<  Display routine exit with decimal value
+#define DBG_EXIT_HEX(Status)    DEBUG (( 0xffffffff, "Exiting " __FUNCTION__ ", Status: 0x%08x\n", Status ))  ///<  Display routine exit with hex value
+#define DBG_EXIT_STATUS(Status) DEBUG (( 0xffffffff, "Exiting " __FUNCTION__ ", Status: %r\n", Status ))      ///<  Display routine exit with status value
+#define DBG_EXIT_TF(Status)     DEBUG (( 0xffffffff, "Exiting " __FUNCTION__ ", returning %s\n", (FALSE == Status) ? L"FALSE" : L"TRUE" ))  ///<  Display routine with TRUE/FALSE value
+#else   //  _MSC_VER
+#define DBG_ENTER()               ///<  Display routine entry
+#define DBG_EXIT()                ///<  Display routine exit
+#define DBG_EXIT_DEC(Status)      ///<  Display routine exit with decimal value
+#define DBG_EXIT_HEX(Status)      ///<  Display routine exit with hex value
+#define DBG_EXIT_STATUS(Status)   ///<  Display routine exit with status value
+#define DBG_EXIT_TF(Status)       ///<  Display routine with TRUE/FALSE value
+#endif  //  _MSC_VER
+
+#define USB_IS_IN_ENDPOINT(EndPointAddr)      (((EndPointAddr) & BIT7) != 0)  ///<  Return TRUE/FALSE for IN direction
+#define USB_IS_OUT_ENDPOINT(EndPointAddr)     (((EndPointAddr) & BIT7) == 0)  ///<  Return TRUE/FALSE for OUT direction
+#define USB_IS_BULK_ENDPOINT(Attribute)       (((Attribute) & (BIT0 | BIT1)) == USB_ENDPOINT_BULK)      ///<  Return TRUE/FALSE for BULK type
+#define USB_IS_INTERRUPT_ENDPOINT(Attribute)  (((Attribute) & (BIT0 | BIT1)) == USB_ENDPOINT_INTERRUPT) ///<  Return TRUE/FALSE for INTERRUPT type
+
+
+#define PRINT(_L_STR) (gST->ConOut->OutputString(gST->ConOut,(_L_STR)))
+//------------------------------------------------------------------------------
+//  Constants
+//------------------------------------------------------------------------------
+
+#define DEBUG_RX_BROADCAST  0x40000000  ///<  Display RX broadcast messages
+#define DEBUG_RX_MULTICAST  0x20000000  ///<  Display RX multicast messages
+#define DEBUG_RX_UNICAST    0x10000000  ///<  Display RX unicast messages
+#define DEBUG_MAC_ADDRESS   0x08000000  ///<  Display the MAC address
+#define DEBUG_LINK          0x04000000  ///<  Display the link status
+#define DEBUG_TX            0x02000000  ///<  Display the TX messages
+#define DEBUG_PHY           0x01000000  ///<  Display the PHY register values
+#define DEBUG_SROM          0x00800000  ///<  Display the SROM contents
+#define DEBUG_TIMER         0x00400000  ///<  Display the timer routine entry/exit
+#define DEBUG_TPL           0x00200000  ///<  Display the timer routine entry/exit
+
+#define AX88772_MAX_PKT_SIZE  2048  ///< Maximum packet size
+
+#define ETHERNET_HEADER_SIZE  sizeof ( ETHERNET_HEADER )  ///<  Size in bytes of the Ethernet header
+#define MIN_ETHERNET_PKT_SIZE 60    ///<  Minimum packet size including Ethernet header
+#define MAX_ETHERNET_PKT_SIZE 1500  ///<  Ethernet spec 3.1.1: Minimum packet size
+
+#define USB_NETWORK_CLASS   0x09    ///<  USB Network class code
+#define USB_BUS_TIMEOUT     1000    ///<  USB timeout in milliseconds
+
+#define TIMER_MSEC          20              ///<  Polling interval for the NIC
+//#define TPL_AX88772         TPL_CALLBACK    ///<  TPL for routine synchronization
+
+#define HC_DEBUG  0
+#define BULKIN_TIMEOUT  20
+#define AUTONEG_DELAY   500000
+#define AUTONEG_POLLCNT 20
+
+/**
+  Verify new TPL value
+
+  This macro which is enabled when debug is enabled verifies that
+  the new TPL value is >= the current TPL value.
+**/
+#ifdef VERIFY_TPL
+#undef VERIFY_TPL
+#endif  //  VERIFY_TPL
+
+#if !defined(MDEPKG_NDEBUG)
+
+#define VERIFY_TPL(tpl)                           \
+{                                                 \
+  EFI_TPL PreviousTpl;                            \
+                                                  \
+  PreviousTpl = gBS->RaiseTPL ( TPL_HIGH_LEVEL ); \
+  gBS->RestoreTPL ( PreviousTpl );                \
+  if ( PreviousTpl > tpl ) {                      \
+    DEBUG (( DEBUG_ERROR, "Current TPL: %d, New TPL: %d\r\n", PreviousTpl, tpl ));  \
+    ASSERT ( PreviousTpl <= tpl );                \
+  }                                               \
+}
+
+#else   //  MDEPKG_NDEBUG
+
+#define VERIFY_TPL(tpl)
+
+#endif  //  MDEPKG_NDEBUG
+
+//------------------------------------------------------------------------------
+//  Hardware Definition
+//------------------------------------------------------------------------------
+
+#define FreeQueueSize     10
+
+#define DEV_SIGNATURE     SIGNATURE_32 ('A','X','8','8')  ///<  Signature of data structures in memory
+
+#define RESET_MSEC        1000    ///<  Reset duration
+#define PHY_RESET_MSEC     500    ///<  PHY reset duration
+
+//
+//  RX Control register
+//
+
+#define RXC_PRO           0x0001  ///<  Receive all packets
+#define RXC_AMALL         0x0002  ///<  Receive all multicast packets
+#define RXC_SEP           0x0004  ///<  Save error packets
+#define RXC_AB            0x0008  ///<  Receive broadcast packets
+#define RXC_AM            0x0010  ///<  Use multicast destination address hash table
+#define RXC_AP            0x0020  ///<  Accept physical address from Multicast Filter
+#define RXC_SO            0x0080  ///<  Start operation
+#define RXC_MFB           0x0300  ///<  Maximum frame burst
+#define RXC_MFB_2048      0       ///<  Maximum frame size:  2048 bytes
+#define RXC_MFB_4096      0x0100  ///<  Maximum frame size:  4096 bytes
+#define RXC_MFB_8192      0x0200  ///<  Maximum frame size:  8192 bytes
+#define RXC_MFB_16384     0x0300  ///<  Maximum frame size: 16384 bytes
+
+/*Freddy*/
+#define RXC_RH1M          0x0100  ///<  Rx header 1
+#define RXC_RH2M          0x0200  ///<  Rx header 2
+#define RXC_RH3M          0x0400  ///<  Rx header 3
+/*Freddy*/
+
+//
+//  Medium Status register
+//
+
+#define MS_FD             0x0002  ///<  Full duplex
+#define MS_ONE            0x0004  ///<  Must be one
+#define MS_RFC            0x0010  ///<  RX flow control enable
+#define MS_TFC            0x0020  ///<  TX flow control enable
+#define MS_PF             0x0080  ///<  Pause frame enable
+#define MS_RE             0x0100  ///<  Receive enable
+#define MS_PS             0x0200  ///<  Port speed 1=100, 0=10 Mbps
+#define MS_SBP            0x0800  ///<  Stop back pressure
+#define MS_SM             0x1000  ///<  Super MAC support
+
+//
+//  Software PHY Select register
+//
+
+#define SPHY_PSEL         (1 << 0)    ///<  Select internal PHY
+#define SPHY_SSMII        (1 << 2)
+#define SPHY_SSEN         (1 << 4)
+#define SPHY_ASEL         0x02    ///<  1=Auto select, 0=Manual select
+
+//
+//  Software Reset register
+//
+
+#define SRR_RR            0x01    ///<  Clear receive frame length error
+#define SRR_RT            0x02    ///<  Clear transmit frame length error
+#define SRR_BZTYPE        0x04    ///<  External PHY reset pin tri-state enable
+#define SRR_PRL           0x08    ///<  External PHY reset pin level
+#define SRR_BZ            0x10    ///<  Force Bulk to return zero length packet
+#define SRR_IPRL          0x20    ///<  Internal PHY reset control
+#define SRR_IPPD          0x40    ///<  Internal PHY power down
+
+//
+//  PHY ID values
+//
+
+#define PHY_ID_INTERNAL   0x0010  ///<  Internal PHY
+
+//
+//  USB Commands
+//
+
+#define CMD_PHY_ACCESS_SOFTWARE   0x06  ///<  Software in control of PHY
+#define CMD_PHY_REG_READ          0x07  ///<  Read PHY register, Value: PHY, Index: Register, Data: Register value
+#define CMD_PHY_REG_WRITE         0x08  ///<  Write PHY register, Value: PHY, Index: Register, Data: New 16-bit value
+#define CMD_PHY_ACCESS_HARDWARE   0x0a  ///<  Hardware in control of PHY
+#define CMD_SROM_READ             0x0b  ///<  Read SROM register: Value: Address, Data: Value
+#define CMD_RX_CONTROL_WRITE      0x10  ///<  Set the RX control register, Value: New value
+#define CMD_GAPS_WRITE            0x12  ///<  Write the gaps register, Value: New value
+#define CMD_MAC_ADDRESS_READ      0x13  ///<  Read the MAC address, Data: 6 byte MAC address
+#define CMD_MAC_ADDRESS_WRITE     0x14  ///<  Set the MAC address, Data: New 6 byte MAC address
+#define CMD_MULTICAST_HASH_WRITE  0x16  ///<  Write the multicast hash table, Data: New 8 byte value
+#define CMD_MULTICAST_HASH_READ  0x16  ///<  Read the multicast hash table
+#define CMD_MEDIUM_STATUS_READ    0x1a  ///<  Read medium status register, Data: Register value
+#define CMD_MEDIUM_STATUS_WRITE   0x1b  ///<  Write medium status register, Value: New value
+#define CMD_WRITE_GPIOS           0x1f
+#define CMD_RESET                 0x20  ///<  Reset register, Value: New value
+#define CMD_PHY_SELECT            0x22  ///<  PHY select register, Value: New value
+
+/*Freddy*/
+#define CMD_RXQTC                 0x2a  ///<  RX Queue Cascade Threshold Control Register
+/*Freddy*/
+
+//------------------------------
+//  USB Endpoints
+//------------------------------
+
+#define CONTROL_ENDPOINT                0       ///<  Control endpoint
+#define INTERRUPT_ENDPOINT              1       ///<  Interrupt endpoint
+#define BULK_IN_ENDPOINT                2       ///<  Receive endpoint
+#define BULK_OUT_ENDPOINT               3       ///<  Transmit endpoint
+
+//------------------------------
+//  PHY Registers
+//------------------------------
+
+#define PHY_BMCR                        0       ///<  Control register
+#define PHY_BMSR                        1       ///<  Status register
+#define PHY_ANAR                        4       ///<  Autonegotiation advertisement register
+#define PHY_ANLPAR                      5       ///<  Autonegotiation link parter ability register
+#define PHY_ANER                        6       ///<  Autonegotiation expansion register
+
+//  BMCR - Register 0
+
+#define BMCR_RESET                      0x8000  ///<  1 = Reset the PHY, bit clears after reset
+#define BMCR_LOOPBACK                   0x4000  ///<  1 = Loopback enabled
+#define BMCR_100MBPS                    0x2000  ///<  100 Mbits/Sec
+#define BMCR_10MBPS                     0       ///<  10 Mbits/Sec
+#define BMCR_AUTONEGOTIATION_ENABLE     0x1000  ///<  1 = Enable autonegotiation
+#define BMCR_POWER_DOWN                 0x0800  ///<  1 = Power down
+#define BMCR_ISOLATE                    0x0400  ///<  0 = Isolate PHY
+#define BMCR_RESTART_AUTONEGOTIATION    0x0200  ///<  1 = Restart autonegotiation
+#define BMCR_FULL_DUPLEX                0x0100  ///<  Full duplex operation
+#define BMCR_HALF_DUPLEX                0       ///<  Half duplex operation
+#define BMCR_COLLISION_TEST             0x0080  ///<  1 = Collision test enabled
+
+//  BSMR - Register 1
+
+#define BMSR_100BASET4                  0x8000  ///<  1 = 100BASE-T4 mode
+#define BMSR_100BASETX_FDX              0x4000  ///<  1 = 100BASE-TX full duplex
+#define BMSR_100BASETX_HDX              0x2000  ///<  1 = 100BASE-TX half duplex
+#define BMSR_10BASET_FDX                0x1000  ///<  1 = 10BASE-T full duplex
+#define BMSR_10BASET_HDX                0x0800  ///<  1 = 10BASE-T half duplex
+#define BMSR_MF                         0x0040  ///<  1 = PHY accepts frames with preamble suppressed
+#define BMSR_AUTONEG_CMPLT              0x0020  ///<  1 = Autonegotiation complete
+#define BMSR_RF                         0x0010  ///<  1 = Remote fault
+#define BMSR_AUTONEG                    0x0008  ///<  1 = Able to perform autonegotiation
+#define BMSR_LINKST                     0x0004  ///<  1 = Link up
+#define BMSR_JABBER_DETECT              0x0002  ///<  1 = jabber condition detected
+#define BMSR_EXTENDED_CAPABILITY        0x0001  ///<  1 = Extended register capable
+
+//  ANAR and ANLPAR Registers 4, 5
+
+#define AN_NP                           0x8000  ///<  1 = Next page available
+#define AN_ACK                          0x4000  ///<  1 = Link partner acknowledged
+#define AN_RF                           0x2000  ///<  1 = Remote fault indicated by link partner
+#define AN_FCS                          0x0400  ///<  1 = Flow control ability
+#define AN_T4                           0x0200  ///<  1 = 100BASE-T4 support
+#define AN_TX_FDX                       0x0100  ///<  1 = 100BASE-TX Full duplex
+#define AN_TX_HDX                       0x0080  ///<  1 = 100BASE-TX support
+#define AN_10_FDX                       0x0040  ///<  1 = 10BASE-T Full duplex
+#define AN_10_HDX                       0x0020  ///<  1 = 10BASE-T support
+#define AN_CSMA_CD                      0x0001  ///<  1 = IEEE 802.3 CSMA/CD support
+
+// asix_flags defines
+#define FLAG_NONE               0
+#define FLAG_TYPE_AX88172       BIT0
+#define FLAG_TYPE_AX88772       BIT1
+#define FLAG_TYPE_AX88772B      BIT2
+#define FLAG_EEPROM_MAC         BIT3  // initial mac address in eeprom
+
+//------------------------------------------------------------------------------
+//  Data Types
+//------------------------------------------------------------------------------
+
+typedef struct {
+   UINT16  VendorId;
+   UINT16  ProductId;
+   INT32   Flags;
+}ASIX_DONGLE;
+
+/**
+  Ethernet header layout
+
+  IEEE 802.3-2002 Part 3 specification, section 3.1.1.
+**/
+#pragma pack(1)
+typedef struct {
+  UINT8 dest_addr[PXE_HWADDR_LEN_ETHER];  ///<  Destination LAN address
+  UINT8 src_addr[PXE_HWADDR_LEN_ETHER];   ///<  Source LAN address
+  UINT16 type;                            ///<  Protocol or length
+} ETHERNET_HEADER;
+#pragma pack()
+
+/**
+  Receive and Transmit packet structure
+**/
+#pragma pack(1)
+typedef struct _RX_TX_PACKET {
+  struct _RX_TX_PACKET * pNext;       ///<  Next receive packet
+  UINT16 Length;                      ///<  Packet length
+  UINT16 LengthBar;                   ///<  Complement of the length
+  UINT8 Data[ AX88772_MAX_PKT_SIZE ]; ///<  Received packet data
+} RX_TX_PACKET;
+#pragma pack()
+
+
+#pragma pack(1)
+typedef struct _RX_PKT {
+  struct _RX_PKT *pNext;
+  BOOLEAN f_Used;
+  UINT16 Length;
+  UINT8 Data [AX88772_MAX_PKT_SIZE] ;
+} RX_PKT;
+#pragma pack()
+
+/**
+  AX88772 control structure
+
+  The driver uses this structure to manage the Asix AX88772 10/100
+  Ethernet controller.
+**/
+typedef struct {
+  UINTN Signature;          ///<  Structure identification
+
+  //
+  //  USB data
+  //
+  EFI_HANDLE Controller;        ///<  Controller handle
+  EFI_USB_IO_PROTOCOL * pUsbIo;  ///<  USB driver interface
+
+  //
+  //  Simple network protocol data
+  //
+  EFI_SIMPLE_NETWORK_PROTOCOL SimpleNetwork;  ///<  Driver's network stack interface
+  EFI_SIMPLE_NETWORK_PROTOCOL SimpleNetwork_Backup;
+  EFI_SIMPLE_NETWORK_MODE SimpleNetworkData;  ///<  Data for simple network
+
+  //
+  // Ethernet controller data
+  //
+  BOOLEAN bInitialized;     ///<  Controller initialized
+  VOID * pTxBuffer;         ///<  Last transmit buffer
+  UINT16 PhyId;             ///<  PHY ID
+
+  //
+  //  Link state
+  //
+  BOOLEAN b100Mbps;         ///<  Current link speed, FALSE = 10 Mbps
+  BOOLEAN bComplete;        ///<  Current state of auto-negotiation
+  BOOLEAN bFullDuplex;      ///<  Current duplex
+  BOOLEAN bLinkUp;          ///<  Current link state
+  UINTN  LinkIdleCnt;
+  UINTN PollCount;          ///<  Number of times the autonegotiation status was polled
+  UINT16 CurRxControl;
+  //
+  //  Receive buffer list
+  //
+  RX_TX_PACKET * pRxTest;
+  RX_TX_PACKET * pTxTest;
+
+  INT8 MulticastHash[8];
+  EFI_MAC_ADDRESS MAC;
+  BOOLEAN bHavePkt;
+ 
+  EFI_DEVICE_PATH_PROTOCOL                  *MyDevPath;
+  
+  EFI_DRIVER_BINDING_PROTOCOL * DrvBind;
+  
+  RX_PKT * QueueHead;
+  RX_PKT * pNextFill;
+  RX_PKT * pFirstFill;
+  UINTN   PktCntInQueue;
+  UINT8 * pBulkInBuff;
+
+  INT32 Flags;
+
+} NIC_DEVICE;
+
+#define DEV_FROM_SIMPLE_NETWORK(a)  CR (a, NIC_DEVICE, SimpleNetwork, DEV_SIGNATURE)  ///< Locate NIC_DEVICE from Simple Network Protocol
+
+//------------------------------------------------------------------------------
+// Simple Network Protocol
+//------------------------------------------------------------------------------
+
+/**
+  Reset the network adapter.
+
+  Resets a network adapter and reinitializes it with the parameters that
+  were provided in the previous call to Initialize ().  The transmit and
+  receive queues are cleared.  Receive filters, the station address, the
+  statistics, and the multicast-IP-to-HW MAC addresses are not reset by
+  this call.
+
+  This routine calls ::Ax88772Reset to perform the adapter specific
+  reset operation.  This routine also starts the link negotiation
+  by calling ::Ax88772NegotiateLinkStart.
+
+  @param [in] pSimpleNetwork    Protocol instance pointer
+  @param [in] bExtendedVerification  Indicates that the driver may perform a more
+                                exhaustive verification operation of the device
+                                during reset.
+
+  @retval EFI_SUCCESS           This operation was successful.
+  @retval EFI_NOT_STARTED       The network interface was not started.
+  @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid
+                                EFI_SIMPLE_NETWORK_PROTOCOL structure.
+  @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
+  @retval EFI_UNSUPPORTED       The increased buffer size feature is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+SN_Reset (
+  IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork,
+  IN BOOLEAN bExtendedVerification
+  );
+
+/**
+  Initialize the simple network protocol.
+
+  This routine calls ::Ax88772MacAddressGet to obtain the
+  MAC address.
+
+  @param [in] pNicDevice       NIC_DEVICE_INSTANCE pointer
+
+  @retval EFI_SUCCESS     Setup was successful
+
+**/
+EFI_STATUS
+SN_Setup (
+  IN NIC_DEVICE * pNicDevice
+  );
+
+/**
+  This routine starts the network interface.
+
+  @param [in] pSimpleNetwork    Protocol instance pointer
+
+  @retval EFI_SUCCESS           This operation was successful.
+  @retval EFI_ALREADY_STARTED   The network interface was already started.
+  @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid
+                                EFI_SIMPLE_NETWORK_PROTOCOL structure.
+  @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
+  @retval EFI_UNSUPPORTED       The increased buffer size feature is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+SN_Start (
+  IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork
+  );
+
+/**
+  Set the MAC address.
+  
+  This function modifies or resets the current station address of a
+  network interface.  If Reset is TRUE, then the current station address
+  is set ot the network interface's permanent address.  If Reset if FALSE
+  then the current station address is changed to the address specified by
+  pNew.
+
+  This routine calls ::Ax88772MacAddressSet to update the MAC address
+  in the network adapter.
+
+  @param [in] pSimpleNetwork    Protocol instance pointer
+  @param [in] bReset            Flag used to reset the station address to the
+                                network interface's permanent address.
+  @param [in] pNew              New station address to be used for the network
+                                interface.
+
+  @retval EFI_SUCCESS           This operation was successful.
+  @retval EFI_NOT_STARTED       The network interface was not started.
+  @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid
+                                EFI_SIMPLE_NETWORK_PROTOCOL structure.
+  @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
+  @retval EFI_UNSUPPORTED       The increased buffer size feature is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+SN_StationAddress (
+  IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork,
+  IN BOOLEAN bReset,
+  IN EFI_MAC_ADDRESS * pNew
+  );
+
+/**
+  This function resets or collects the statistics on a network interface.
+  If the size of the statistics table specified by StatisticsSize is not
+  big enough for all of the statistics that are collected by the network
+  interface, then a partial buffer of statistics is returned in
+  StatisticsTable.
+
+  @param [in] pSimpleNetwork    Protocol instance pointer
+  @param [in] bReset            Set to TRUE to reset the statistics for the network interface.
+  @param [in, out] pStatisticsSize  On input the size, in bytes, of StatisticsTable.  On output
+                                the size, in bytes, of the resulting table of statistics.
+  @param [out] pStatisticsTable A pointer to the EFI_NETWORK_STATISTICS structure that
+                                conains the statistics.
+
+  @retval EFI_SUCCESS           This operation was successful.
+  @retval EFI_NOT_STARTED       The network interface was not started.
+  @retval EFI_BUFFER_TOO_SMALL  The pStatisticsTable is NULL or the buffer is too small.
+  @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid
+                                EFI_SIMPLE_NETWORK_PROTOCOL structure.
+  @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
+  @retval EFI_UNSUPPORTED       The increased buffer size feature is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+SN_Statistics (
+  IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork,
+  IN BOOLEAN bReset,
+  IN OUT UINTN * pStatisticsSize,
+  OUT EFI_NETWORK_STATISTICS * pStatisticsTable
+  );
+
+/**
+  This function stops a network interface.  This call is only valid
+  if the network interface is in the started state.
+
+  @param [in] pSimpleNetwork    Protocol instance pointer
+
+  @retval EFI_SUCCESS           This operation was successful.
+  @retval EFI_NOT_STARTED       The network interface was not started.
+  @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid
+                                EFI_SIMPLE_NETWORK_PROTOCOL structure.
+  @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
+  @retval EFI_UNSUPPORTED       The increased buffer size feature is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+SN_Stop (
+  IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork
+  );
+
+/**
+  This function releases the memory buffers assigned in the Initialize() call.
+  Pending transmits and receives are lost, and interrupts are cleared and disabled.
+  After this call, only Initialize() and Stop() calls may be used.
+
+  @param [in] pSimpleNetwork    Protocol instance pointer
+
+  @retval EFI_SUCCESS           This operation was successful.
+  @retval EFI_NOT_STARTED       The network interface was not started.
+  @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid
+                                EFI_SIMPLE_NETWORK_PROTOCOL structure.
+  @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
+  @retval EFI_UNSUPPORTED       The increased buffer size feature is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+SN_Shutdown (
+  IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork
+  );
+
+/**
+  Send a packet over the network.
+
+  This function places the packet specified by Header and Buffer on
+  the transmit queue.  This function performs a non-blocking transmit
+  operation.  When the transmit is complete, the buffer is returned
+  via the GetStatus() call.
+
+  This routine calls ::Ax88772Rx to empty the network adapter of
+  receive packets.  The routine then passes the transmit packet
+  to the network adapter.
+
+  @param [in] pSimpleNetwork    Protocol instance pointer
+  @param [in] HeaderSize        The size, in bytes, of the media header to be filled in by
+                                the Transmit() function.  If HeaderSize is non-zero, then
+                                it must be equal to SimpleNetwork->Mode->MediaHeaderSize
+                                and DestAddr and Protocol parameters must not be NULL.
+  @param [in] BufferSize        The size, in bytes, of the entire packet (media header and
+                                data) to be transmitted through the network interface.
+  @param [in] pBuffer           A pointer to the packet (media header followed by data) to
+                                to be transmitted.  This parameter can not be NULL.  If
+                                HeaderSize is zero, then the media header is Buffer must
+                                already be filled in by the caller.  If HeaderSize is nonzero,
+                                then the media header will be filled in by the Transmit()
+                                function.
+  @param [in] pSrcAddr          The source HW MAC address.  If HeaderSize is zero, then
+                                this parameter is ignored.  If HeaderSize is nonzero and
+                                SrcAddr is NULL, then SimpleNetwork->Mode->CurrentAddress
+                                is used for the source HW MAC address.
+  @param [in] pDestAddr         The destination HW MAC address.  If HeaderSize is zero, then
+                                this parameter is ignored.
+  @param [in] pProtocol         The type of header to build.  If HeaderSize is zero, then
+                                this parameter is ignored.
+
+  @retval EFI_SUCCESS           This operation was successful.
+  @retval EFI_NOT_STARTED       The network interface was not started.
+  @retval EFI_NOT_READY         The network interface is too busy to accept this transmit request.
+  @retval EFI_BUFFER_TOO_SMALL  The BufferSize parameter is too small.
+  @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid
+                                EFI_SIMPLE_NETWORK_PROTOCOL structure.
+  @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
+
+**/
+EFI_STATUS
+EFIAPI
+SN_Transmit (
+  IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork,
+  IN UINTN HeaderSize,
+  IN UINTN BufferSize,
+  IN VOID * pBuffer,
+  IN EFI_MAC_ADDRESS * pSrcAddr,
+  IN EFI_MAC_ADDRESS * pDestAddr,
+  IN UINT16 * pProtocol
+  );
+
+//------------------------------------------------------------------------------
+// Support Routines
+//------------------------------------------------------------------------------
+
+/**
+  Get the MAC address
+
+  This routine calls ::Ax88772UsbCommand to request the MAC
+  address from the network adapter.
+
+  @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
+  @param [out] pMacAddress      Address of a six byte buffer to receive the MAC address.
+
+  @retval EFI_SUCCESS          The MAC address is available.
+  @retval other                The MAC address is not valid.
+
+**/
+EFI_STATUS
+Ax88772MacAddressGet (
+  IN NIC_DEVICE * pNicDevice,
+  OUT UINT8 * pMacAddress
+  );
+
+/**
+  Set the MAC address
+
+  This routine calls ::Ax88772UsbCommand to set the MAC address
+  in the network adapter.
+
+  @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
+  @param [in] pMacAddress      Address of a six byte buffer to containing the new MAC address.
+
+  @retval EFI_SUCCESS          The MAC address was set.
+  @retval other                The MAC address was not set.
+
+**/
+EFI_STATUS
+Ax88772MacAddressSet (
+  IN NIC_DEVICE * pNicDevice,
+  IN UINT8 * pMacAddress
+  );
+
+/**
+  Clear the multicast hash table
+
+  @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
+
+**/
+VOID
+Ax88772MulticastClear (
+  IN NIC_DEVICE * pNicDevice
+  );
+
+/**
+  Enable a multicast address in the multicast hash table
+
+  This routine calls ::Ax88772Crc to compute the hash bit for
+  this MAC address.
+
+  @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
+  @param [in] pMacAddress      Address of a six byte buffer to containing the MAC address.
+
+**/
+VOID
+Ax88772MulticastSet (
+  IN NIC_DEVICE * pNicDevice,
+  IN UINT8 * pMacAddress
+  );
+
+/**
+  Start the link negotiation
+
+  This routine calls ::Ax88772PhyWrite to start the PHY's link
+  negotiation.
+
+  @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
+
+  @retval EFI_SUCCESS          The link negotiation was started.
+  @retval other                Failed to start the link negotiation.
+
+**/
+EFI_STATUS
+Ax88772NegotiateLinkStart (
+  IN NIC_DEVICE * pNicDevice
+  );
+
+/**
+  Complete the negotiation of the PHY link
+
+  This routine calls ::Ax88772PhyRead to determine if the
+  link negotiation is complete.
+
+  @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
+  @param [in, out] pPollCount  Address of number of times this routine was polled
+  @param [out] pbComplete      Address of boolean to receive complate status.
+  @param [out] pbLinkUp        Address of boolean to receive link status, TRUE=up.
+  @param [out] pbHiSpeed       Address of boolean to receive link speed, TRUE=100Mbps.
+  @param [out] pbFullDuplex    Address of boolean to receive link duplex, TRUE=full.
+
+  @retval EFI_SUCCESS          The MAC address is available.
+  @retval other                The MAC address is not valid.
+
+**/
+EFI_STATUS
+Ax88772NegotiateLinkComplete (
+  IN NIC_DEVICE * pNicDevice,
+  IN OUT UINTN * pPollCount,
+  OUT BOOLEAN * pbComplete,
+  OUT BOOLEAN * pbLinkUp,
+  OUT BOOLEAN * pbHiSpeed,
+  OUT BOOLEAN * pbFullDuplex
+  );
+
+/**
+  Read a register from the PHY
+
+  This routine calls ::Ax88772UsbCommand to read a PHY register.
+
+  @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
+  @param [in] RegisterAddress  Number of the register to read.
+  @param [in, out] pPhyData    Address of a buffer to receive the PHY register value
+
+  @retval EFI_SUCCESS          The PHY data is available.
+  @retval other                The PHY data is not valid.
+
+**/
+EFI_STATUS
+Ax88772PhyRead (
+  IN NIC_DEVICE * pNicDevice,
+  IN UINT8 RegisterAddress,
+  IN OUT UINT16 * pPhyData
+  );
+
+/**
+  Write to a PHY register
+
+  This routine calls ::Ax88772UsbCommand to write a PHY register.
+
+  @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
+  @param [in] RegisterAddress  Number of the register to read.
+  @param [in] PhyData          Address of a buffer to receive the PHY register value
+
+  @retval EFI_SUCCESS          The PHY data was written.
+  @retval other                Failed to wwrite the PHY register.
+
+**/
+EFI_STATUS
+Ax88772PhyWrite (
+  IN NIC_DEVICE * pNicDevice,
+  IN UINT8 RegisterAddress,
+  IN UINT16 PhyData
+  );
+
+/**
+  Reset the AX88772
+
+  This routine uses ::Ax88772UsbCommand to reset the network
+  adapter.  This routine also uses ::Ax88772PhyWrite to reset
+  the PHY.
+
+  @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
+
+  @retval EFI_SUCCESS          The MAC address is available.
+  @retval other                The MAC address is not valid.
+
+**/
+EFI_STATUS
+Ax88772Reset (
+  IN NIC_DEVICE * pNicDevice
+  );
+
+VOID
+Ax88772ChkLink (
+  IN NIC_DEVICE * pNicDevice,
+  IN BOOLEAN bUpdateLink
+  );
+
+/**
+  Receive a frame from the network.
+
+  This routine polls the USB receive interface for a packet.  If a packet
+  is available, this routine adds the receive packet to the list of
+  pending receive packets.
+
+  This routine calls ::Ax88772NegotiateLinkComplete to verify
+  that the link is up.  This routine also calls ::SN_Reset to
+  reset the network adapter when necessary.  Finally this
+  routine attempts to receive one or more packets from the
+  network adapter.
+
+  @param [in] pNicDevice  Pointer to the NIC_DEVICE structure
+  @param [in] bUpdateLink TRUE = Update link status
+
+**/
+VOID
+Ax88772Rx (
+  IN NIC_DEVICE * pNicDevice,
+  IN BOOLEAN bUpdateLink
+  );
+
+/**
+  Enable or disable the receiver
+
+  This routine calls ::Ax88772UsbCommand to update the
+  receiver state.  This routine also calls ::Ax88772MacAddressSet
+  to establish the MAC address for the network adapter.
+
+  @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
+  @param [in] RxFilter         Simple network RX filter mask value
+
+  @retval EFI_SUCCESS          The MAC address was set.
+  @retval other                The MAC address was not set.
+
+**/
+EFI_STATUS
+Ax88772RxControl (
+  IN NIC_DEVICE * pNicDevice,
+  IN UINT32 RxFilter
+  );
+
+/**
+  Read an SROM location
+
+  This routine calls ::Ax88772UsbCommand to read data from the
+  SROM.
+
+  @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
+  @param [in] Address          SROM address
+  @param [out] pData           Buffer to receive the data
+
+  @retval EFI_SUCCESS          The read was successful
+  @retval other                The read failed
+
+**/
+EFI_STATUS
+Ax88772SromRead (
+  IN NIC_DEVICE * pNicDevice,
+  IN UINT32 Address,
+  OUT UINT16 * pData
+  );
+
+/**
+  Send a command to the USB device.
+
+  @param [in] pNicDevice       Pointer to the NIC_DEVICE structure
+  @param [in] pRequest         Pointer to the request structure
+  @param [in, out] pBuffer     Data buffer address
+
+  @retval EFI_SUCCESS          The USB transfer was successful
+  @retval other                The USB transfer failed
+
+**/
+EFI_STATUS
+Ax88772UsbCommand (
+  IN NIC_DEVICE * pNicDevice,
+  IN USB_DEVICE_REQUEST * pRequest,
+  IN OUT VOID * pBuffer
+  );
+
+//------------------------------------------------------------------------------
+// EFI Component Name Protocol Support
+//------------------------------------------------------------------------------
+
+extern EFI_COMPONENT_NAME_PROTOCOL   gComponentName;  ///<  Component name protocol declaration
+extern EFI_COMPONENT_NAME2_PROTOCOL  gComponentName2; ///<  Component name 2 protocol declaration
+
+/**
+  Retrieves a Unicode string that is the user readable name of the driver.
+
+  This function retrieves the user readable name of a driver in the form of a
+  Unicode string. If the driver specified by This has a user readable name in
+  the language specified by Language, then a pointer to the driver name is
+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+  by This does not support the language specified by Language,
+  then EFI_UNSUPPORTED is returned.
+
+  @param [in] pThis             A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+                                EFI_COMPONENT_NAME_PROTOCOL instance.
+  @param [in] pLanguage         A pointer to a Null-terminated ASCII string
+                                array indicating the language. This is the
+                                language of the driver name that the caller is
+                                requesting, and it must match one of the
+                                languages specified in SupportedLanguages. The
+                                number of languages supported by a driver is up
+                                to the driver writer. Language is specified
+                                in RFC 3066 or ISO 639-2 language code format.
+  @param [out] ppDriverName     A pointer to the Unicode string to return.
+                                This Unicode string is the name of the
+                                driver specified by This in the language
+                                specified by Language.
+
+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by
+                                This and the language specified by Language was
+                                returned in DriverName.
+  @retval EFI_INVALID_PARAMETER Language is NULL.
+  @retval EFI_INVALID_PARAMETER DriverName is NULL.
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support
+                                the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+GetDriverName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL * pThis,
+  IN  CHAR8 * pLanguage,
+  OUT CHAR16 ** ppDriverName
+  );
+
+
+/**
+  Retrieves a Unicode string that is the user readable name of the controller
+  that is being managed by a driver.
+
+  This function retrieves the user readable name of the controller specified by
+  ControllerHandle and ChildHandle in the form of a Unicode string. If the
+  driver specified by This has a user readable name in the language specified by
+  Language, then a pointer to the controller name is returned in ControllerName,
+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently
+  managing the controller specified by ControllerHandle and ChildHandle,
+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not
+  support the language specified by Language, then EFI_UNSUPPORTED is returned.
+
+  @param [in] pThis             A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+                                EFI_COMPONENT_NAME_PROTOCOL instance.
+  @param [in] ControllerHandle  The handle of a controller that the driver
+                                specified by This is managing.  This handle
+                                specifies the controller whose name is to be
+                                returned.
+  @param [in] ChildHandle       The handle of the child controller to retrieve
+                                the name of.  This is an optional parameter that
+                                may be NULL.  It will be NULL for device
+                                drivers.  It will also be NULL for a bus drivers
+                                that wish to retrieve the name of the bus
+                                controller.  It will not be NULL for a bus
+                                driver that wishes to retrieve the name of a
+                                child controller.
+  @param [in] pLanguage         A pointer to a Null-terminated ASCII string
+                                array indicating the language.  This is the
+                                language of the driver name that the caller is
+                                requesting, and it must match one of the
+                                languages specified in SupportedLanguages. The
+                                number of languages supported by a driver is up
+                                to the driver writer. Language is specified in
+                                RFC 3066 or ISO 639-2 language code format.
+  @param [out] ppControllerName A pointer to the Unicode string to return.
+                                This Unicode string is the name of the
+                                controller specified by ControllerHandle and
+                                ChildHandle in the language specified by
+                                Language from the point of view of the driver
+                                specified by This.
+
+  @retval EFI_SUCCESS           The Unicode string for the user readable name in
+                                the language specified by Language for the
+                                driver specified by This was returned in
+                                DriverName.
+  @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.
+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid
+                                EFI_HANDLE.
+  @retval EFI_INVALID_PARAMETER Language is NULL.
+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently
+                                managing the controller specified by
+                                ControllerHandle and ChildHandle.
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support
+                                the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+GetControllerName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL * pThis,
+  IN  EFI_HANDLE ControllerHandle,
+  IN OPTIONAL EFI_HANDLE ChildHandle,
+  IN  CHAR8 * pLanguage,
+  OUT CHAR16 ** ppControllerName
+  );
+  
+VOID 
+FillPkt2Queue (
+  IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork,
+  IN UINTN BufLength);
+
+//------------------------------------------------------------------------------
+
+#endif  //  _AX88772_H_
diff --git a/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/Ax88772b.inf b/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/Ax88772b.inf
new file mode 100644
index 0000000000..60e43fd275
--- /dev/null
+++ b/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/Ax88772b.inf
@@ -0,0 +1,61 @@
+## @file
+# Component description file for ASIX AX88772 USB/Ethernet driver.
+#
+# This module provides support for the ASIX AX88772 USB/Ethernet adapter.
+# Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010018
+  BASE_NAME                      = Ax88772b
+  FILE_GUID                      = 95C8D770-E1A4-4422-B263-E32F14FD8186
+  MODULE_TYPE                    = DXE_RUNTIME_DRIVER
+  VERSION_STRING                 = 1.0
+
+  ENTRY_POINT                    = EntryPoint
+
+#
+#  VALID_ARCHITECTURES           = IA32 X64 EBC
+#
+
+[Sources.common]
+  Ax88772.h
+  Ax88772.c
+  ComponentName.c
+  DriverBinding.c
+  SimpleNetwork.c
+
+
+[Packages]
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+
+[LibraryClasses]
+  UefiLib
+  UefiBootServicesTableLib
+  BaseMemoryLib
+  DebugLib
+  UefiRuntimeLib
+  UefiDriverEntryPoint
+
+[Protocols]  
+  gEfiDevicePathProtocolGuid           ## BY_START
+  gEfiSimpleNetworkProtocolGuid        ## BY_START
+  gEfiUsbIoProtocolGuid                ## TO_START
+
+[Depex]
+  gEfiBdsArchProtocolGuid AND
+  gEfiCpuArchProtocolGuid AND
+  gEfiMetronomeArchProtocolGuid AND
+  gEfiMonotonicCounterArchProtocolGuid AND
+  gEfiRealTimeClockArchProtocolGuid AND
+  gEfiResetArchProtocolGuid AND
+  gEfiRuntimeArchProtocolGuid AND
+  gEfiSecurityArchProtocolGuid AND
+  gEfiTimerArchProtocolGuid AND
+  gEfiVariableWriteArchProtocolGuid AND
+  gEfiVariableArchProtocolGuid AND
+  gEfiWatchdogTimerArchProtocolGuid
diff --git a/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/ComponentName.c b/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/ComponentName.c
new file mode 100644
index 0000000000..76a732a7b0
--- /dev/null
+++ b/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/ComponentName.c
@@ -0,0 +1,175 @@
+/** @file
+  UEFI Component Name(2) protocol implementation.
+
+  Copyright (c) 2011, Intel Corporation. All rights reserved.
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "Ax88772.h"
+
+/**
+  EFI Component Name Protocol declaration
+**/
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL  gComponentName = {
+  GetDriverName,
+  GetControllerName,
+  "eng"
+};
+
+/**
+  EFI Component Name 2 Protocol declaration
+**/
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gComponentName2 = {
+  (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) GetDriverName,
+  (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) GetControllerName,
+  "en"
+};
+
+
+/**
+  Driver name table declaration
+**/
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE
+mDriverNameTable[] = {
+  {"eng;en", L"ASIX AX88772B Ethernet Driver 1.0"},
+  {NULL,  NULL}
+};
+
+/**
+  Retrieves a Unicode string that is the user readable name of the driver.
+
+  This function retrieves the user readable name of a driver in the form of a
+  Unicode string. If the driver specified by This has a user readable name in
+  the language specified by Language, then a pointer to the driver name is
+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+  by This does not support the language specified by Language,
+  then EFI_UNSUPPORTED is returned.
+
+  @param [in] pThis             A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+                                EFI_COMPONENT_NAME_PROTOCOL instance.
+  @param [in] pLanguage         A pointer to a Null-terminated ASCII string
+                                array indicating the language. This is the
+                                language of the driver name that the caller is
+                                requesting, and it must match one of the
+                                languages specified in SupportedLanguages. The
+                                number of languages supported by a driver is up
+                                to the driver writer. Language is specified
+                                in RFC 3066 or ISO 639-2 language code format.
+  @param [out] ppDriverName     A pointer to the Unicode string to return.
+                                This Unicode string is the name of the
+                                driver specified by This in the language
+                                specified by Language.
+
+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by
+                                This and the language specified by Language was
+                                returned in DriverName.
+  @retval EFI_INVALID_PARAMETER Language is NULL.
+  @retval EFI_INVALID_PARAMETER DriverName is NULL.
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support
+                                the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+GetDriverName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL * pThis,
+  IN  CHAR8 * pLanguage,
+  OUT CHAR16 ** ppDriverName
+  )
+{
+  EFI_STATUS Status;
+
+  Status = LookupUnicodeString2 (
+             pLanguage,
+             pThis->SupportedLanguages,
+             mDriverNameTable,
+             ppDriverName,
+             (BOOLEAN)(pThis == &gComponentName)
+             );
+
+  return Status;
+}
+
+/**
+  Retrieves a Unicode string that is the user readable name of the controller
+  that is being managed by a driver.
+
+  This function retrieves the user readable name of the controller specified by
+  ControllerHandle and ChildHandle in the form of a Unicode string. If the
+  driver specified by This has a user readable name in the language specified by
+  Language, then a pointer to the controller name is returned in ControllerName,
+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently
+  managing the controller specified by ControllerHandle and ChildHandle,
+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not
+  support the language specified by Language, then EFI_UNSUPPORTED is returned.
+
+  @param [in] pThis             A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+                                EFI_COMPONENT_NAME_PROTOCOL instance.
+  @param [in] ControllerHandle  The handle of a controller that the driver
+                                specified by This is managing.  This handle
+                                specifies the controller whose name is to be
+                                returned.
+  @param [in] ChildHandle       The handle of the child controller to retrieve
+                                the name of.  This is an optional parameter that
+                                may be NULL.  It will be NULL for device
+                                drivers.  It will also be NULL for a bus drivers
+                                that wish to retrieve the name of the bus
+                                controller.  It will not be NULL for a bus
+                                driver that wishes to retrieve the name of a
+                                child controller.
+  @param [in] pLanguage         A pointer to a Null-terminated ASCII string
+                                array indicating the language.  This is the
+                                language of the driver name that the caller is
+                                requesting, and it must match one of the
+                                languages specified in SupportedLanguages. The
+                                number of languages supported by a driver is up
+                                to the driver writer. Language is specified in
+                                RFC 3066 or ISO 639-2 language code format.
+  @param [out] ppControllerName A pointer to the Unicode string to return.
+                                This Unicode string is the name of the
+                                controller specified by ControllerHandle and
+                                ChildHandle in the language specified by
+                                Language from the point of view of the driver
+                                specified by This.
+
+  @retval EFI_SUCCESS           The Unicode string for the user readable name in
+                                the language specified by Language for the
+                                driver specified by This was returned in
+                                DriverName.
+  @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.
+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid
+                                EFI_HANDLE.
+  @retval EFI_INVALID_PARAMETER Language is NULL.
+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently
+                                managing the controller specified by
+                                ControllerHandle and ChildHandle.
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support
+                                the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+GetControllerName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL * pThis,
+  IN  EFI_HANDLE ControllerHandle,
+  IN OPTIONAL EFI_HANDLE ChildHandle,
+  IN  CHAR8 * pLanguage,
+  OUT CHAR16 ** ppControllerName
+  )
+{
+  EFI_STATUS Status;
+
+  //
+  // Set the controller name
+  //
+  *ppControllerName = L"ASIX AX88772B USB Fast Ethernet Controller";
+  Status = EFI_SUCCESS;
+
+  //
+  // Return the operation status
+  //
+
+  return Status;
+}
diff --git a/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/DriverBinding.c b/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/DriverBinding.c
new file mode 100644
index 0000000000..2bec944140
--- /dev/null
+++ b/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/DriverBinding.c
@@ -0,0 +1,696 @@
+/** @file
+  Implement the driver binding protocol for Asix AX88772 Ethernet driver.
+                     
+  Copyright (c) 2011-2013, Intel Corporation. All rights reserved.
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "Ax88772.h"
+
+ASIX_DONGLE ASIX_DONGLES[] = {
+  { 0x05AC, 0x1402, FLAG_TYPE_AX88772 }, // Apple USB Ethernet Adapter
+  // ASIX 88772B
+  { 0x0B95, 0x772B, FLAG_TYPE_AX88772B | FLAG_EEPROM_MAC },
+  { 0x0000, 0x0000, FLAG_NONE }   // END - Do not remove
+};
+
+/**
+  Verify the controller type
+
+  @param [in] pThis                Protocol instance pointer.
+  @param [in] Controller           Handle of device to test.
+  @param [in] pRemainingDevicePath Not used.
+
+  @retval EFI_SUCCESS          This driver supports this device.
+  @retval other                This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+DriverSupported (
+  IN EFI_DRIVER_BINDING_PROTOCOL * pThis,
+  IN EFI_HANDLE Controller,
+  IN EFI_DEVICE_PATH_PROTOCOL * pRemainingDevicePath
+  )
+{
+  EFI_USB_DEVICE_DESCRIPTOR Device;
+  EFI_USB_IO_PROTOCOL * pUsbIo;
+  EFI_STATUS Status;
+  UINT32 Index;
+
+  //
+  //  Connect to the USB stack
+  //
+  Status = gBS->OpenProtocol (
+                  Controller,
+                  &gEfiUsbIoProtocolGuid,
+                  (VOID **) &pUsbIo,
+                  pThis->DriverBindingHandle,         
+                  Controller,
+                  EFI_OPEN_PROTOCOL_BY_DRIVER
+                  );
+  if (!EFI_ERROR ( Status )) {
+
+    //
+    //  Get the interface descriptor to check the USB class and find a transport
+    //  protocol handler.
+    //
+    Status = pUsbIo->UsbGetDeviceDescriptor ( pUsbIo, &Device );
+    if (EFI_ERROR ( Status )) {
+    	Status = EFI_UNSUPPORTED;
+    }
+    else {
+      //
+      //  Validate the adapter
+      //
+      for (Index = 0; ASIX_DONGLES[Index].VendorId != 0; Index++) {
+        if (ASIX_DONGLES[Index].VendorId == Device.IdVendor &&
+            ASIX_DONGLES[Index].ProductId == Device.IdProduct) {
+              DEBUG ((EFI_D_INFO, "Found the AX88772B\r\n"));
+              break;
+        }
+      }
+
+      if (ASIX_DONGLES[Index].VendorId == 0)
+         Status = EFI_UNSUPPORTED;
+    }
+   
+    //
+    //  Done with the USB stack
+    //
+    gBS->CloseProtocol (
+           Controller,
+           &gEfiUsbIoProtocolGuid,
+           pThis->DriverBindingHandle,
+           Controller
+           );
+  }
+  return Status;
+}
+
+
+/**
+  Start this driver on Controller by opening UsbIo and DevicePath protocols.
+  Initialize PXE structures, create a copy of the Controller Device Path with the
+  NIC's MAC address appended to it, install the NetworkInterfaceIdentifier protocol
+  on the newly created Device Path.
+
+  @param [in] pThis                Protocol instance pointer.
+  @param [in] Controller           Handle of device to work with.
+  @param [in] pRemainingDevicePath Not used, always produce all possible children.
+
+  @retval EFI_SUCCESS          This driver is added to Controller.
+  @retval other                This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+DriverStart (
+  IN EFI_DRIVER_BINDING_PROTOCOL * pThis,
+  IN EFI_HANDLE Controller,
+  IN EFI_DEVICE_PATH_PROTOCOL * pRemainingDevicePath
+  )
+{
+
+	EFI_STATUS						Status;
+	NIC_DEVICE						*pNicDevice;
+	UINTN							LengthInBytes;
+	EFI_DEVICE_PATH_PROTOCOL        *ParentDevicePath = NULL;
+	MAC_ADDR_DEVICE_PATH            MacDeviceNode;
+        EFI_USB_DEVICE_DESCRIPTOR       Device;
+        UINT32                          Index;
+
+  //
+	//  Allocate the device structure
+	//
+	LengthInBytes = sizeof ( *pNicDevice );
+	Status = gBS->AllocatePool (
+                  EfiRuntimeServicesData,
+                  LengthInBytes,
+                  (VOID **) &pNicDevice
+                  );
+
+	if (EFI_ERROR (Status)) {
+		DEBUG ((EFI_D_ERROR, "gBS->AllocatePool:pNicDevice ERROR Status = %r\n", Status));
+		goto EXIT;
+	}
+	
+	//
+  //  Set the structure signature
+  //
+  ZeroMem ( pNicDevice, LengthInBytes );
+  pNicDevice->Signature = DEV_SIGNATURE;
+
+	Status = gBS->OpenProtocol (
+                    Controller,
+                    &gEfiUsbIoProtocolGuid,
+                    (VOID **) &pNicDevice->pUsbIo,
+                    pThis->DriverBindingHandle,
+                    Controller,
+                    EFI_OPEN_PROTOCOL_BY_DRIVER
+                    );
+
+	if (EFI_ERROR (Status)) {
+		DEBUG ((EFI_D_ERROR, "gBS->OpenProtocol:EFI_USB_IO_PROTOCOL ERROR Status = %r\n", Status));
+		gBS->FreePool ( pNicDevice );
+		goto EXIT;
+	}
+
+	//
+  //  Initialize the simple network protocol
+  //
+	Status = SN_Setup ( pNicDevice );
+
+	if (EFI_ERROR(Status)){
+	   DEBUG ((EFI_D_ERROR, "SN_Setup ERROR Status = %r\n", Status));
+	   gBS->CloseProtocol (
+					Controller,
+					&gEfiUsbIoProtocolGuid,
+					pThis->DriverBindingHandle,
+					Controller
+					);
+		  gBS->FreePool ( pNicDevice );
+		  goto EXIT;
+  }
+
+  Status = pNicDevice->pUsbIo->UsbGetDeviceDescriptor ( pNicDevice->pUsbIo, &Device );
+  if (EFI_ERROR ( Status )) {
+     gBS->CloseProtocol (
+               Controller,
+               &gEfiUsbIoProtocolGuid,
+               pThis->DriverBindingHandle,
+               Controller
+               );
+     gBS->FreePool ( pNicDevice );
+              goto EXIT;
+  } else {
+      //
+      //  Validate the adapter
+      //
+      for (Index = 0; ASIX_DONGLES[Index].VendorId != 0; Index++) {
+          if (ASIX_DONGLES[Index].VendorId == Device.IdVendor &&
+              ASIX_DONGLES[Index].ProductId == Device.IdProduct) {
+                break;
+          }
+      }
+
+      if (ASIX_DONGLES[Index].VendorId == 0) {
+         gBS->CloseProtocol (
+                   Controller,
+                   &gEfiUsbIoProtocolGuid,
+                   pThis->DriverBindingHandle,
+                   Controller
+                   );
+          gBS->FreePool ( pNicDevice );
+                   goto EXIT;
+      }
+
+      pNicDevice->Flags = ASIX_DONGLES[Index].Flags;
+  }
+
+	//
+  // Set Device Path
+  //  			
+  Status = gBS->OpenProtocol (
+                  Controller,
+                  &gEfiDevicePathProtocolGuid,
+                  (VOID **) &ParentDevicePath,
+				          pThis->DriverBindingHandle,
+                  Controller,
+                  EFI_OPEN_PROTOCOL_BY_DRIVER
+                  );
+	if (EFI_ERROR(Status)) {
+        DEBUG ((EFI_D_ERROR, "gBS->OpenProtocol:EFI_DEVICE_PATH_PROTOCOL error. Status = %r\n",
+            Status));        
+		    gBS->CloseProtocol (
+					Controller,
+					&gEfiUsbIoProtocolGuid,
+					pThis->DriverBindingHandle,
+					Controller
+					);
+		  gBS->FreePool ( pNicDevice );
+		  goto EXIT;
+	}
+
+  ZeroMem (&MacDeviceNode, sizeof (MAC_ADDR_DEVICE_PATH));
+  MacDeviceNode.Header.Type = MESSAGING_DEVICE_PATH;
+  MacDeviceNode.Header.SubType = MSG_MAC_ADDR_DP;
+
+  SetDevicePathNodeLength (&MacDeviceNode.Header, sizeof (MAC_ADDR_DEVICE_PATH));
+      			
+  CopyMem (&MacDeviceNode.MacAddress,
+      								&pNicDevice->SimpleNetworkData.CurrentAddress,
+      								PXE_HWADDR_LEN_ETHER);
+      								
+  MacDeviceNode.IfType = pNicDevice->SimpleNetworkData.IfType;
+
+  pNicDevice->MyDevPath = AppendDevicePathNode (
+                                          ParentDevicePath,
+                                          (EFI_DEVICE_PATH_PROTOCOL *) &MacDeviceNode
+                                          );
+
+	pNicDevice->Controller = NULL;
+
+	//
+  //  Install both the simple network and device path protocols.
+  //
+  Status = gBS->InstallMultipleProtocolInterfaces (
+                          &pNicDevice->Controller,
+                          &gEfiCallerIdGuid,
+                          pNicDevice,
+                          &gEfiSimpleNetworkProtocolGuid,            
+                          &pNicDevice->SimpleNetwork,
+						              &gEfiDevicePathProtocolGuid,
+						              pNicDevice->MyDevPath,
+                          NULL
+                          );
+
+	if (EFI_ERROR(Status)){
+		DEBUG ((EFI_D_ERROR, "gBS->InstallMultipleProtocolInterfaces error. Status = %r\n",
+            Status)); 
+		gBS->CloseProtocol (
+					               Controller,
+					               &gEfiDevicePathProtocolGuid,
+					               pThis->DriverBindingHandle,
+					               Controller);
+	   gBS->CloseProtocol (
+					Controller,
+					&gEfiUsbIoProtocolGuid,
+					pThis->DriverBindingHandle,
+					Controller
+					);
+		  gBS->FreePool ( pNicDevice );
+		  goto EXIT;
+	}
+
+	//
+	// Open For Child Device
+	//
+	Status = gBS->OpenProtocol (                                                                         
+                  Controller,
+                  &gEfiUsbIoProtocolGuid,
+                  (VOID **) &pNicDevice->pUsbIo,
+                  pThis->DriverBindingHandle,
+                  pNicDevice->Controller,
+                  EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+                  );
+
+	if (EFI_ERROR(Status)){
+	   gBS->UninstallMultipleProtocolInterfaces (
+              &pNicDevice->Controller,
+                          &gEfiCallerIdGuid,
+                          pNicDevice,
+                          &gEfiSimpleNetworkProtocolGuid,            
+                          &pNicDevice->SimpleNetwork,
+						              &gEfiDevicePathProtocolGuid,
+						              pNicDevice->MyDevPath,
+                          NULL
+                          );
+		gBS->CloseProtocol (
+					               Controller,
+					               &gEfiDevicePathProtocolGuid,
+					               pThis->DriverBindingHandle,
+					               Controller);
+	   gBS->CloseProtocol (
+					Controller,
+					&gEfiUsbIoProtocolGuid,
+					pThis->DriverBindingHandle,
+					Controller
+					);
+		  gBS->FreePool ( pNicDevice );
+	}
+
+EXIT:
+	return Status;
+
+}
+
+/**
+  Stop this driver on Controller by removing NetworkInterfaceIdentifier protocol and
+  closing the DevicePath and PciIo protocols on Controller.
+
+  @param [in] pThis                Protocol instance pointer.
+  @param [in] Controller           Handle of device to stop driver on.
+  @param [in] NumberOfChildren     How many children need to be stopped.
+  @param [in] pChildHandleBuffer   Not used.
+
+  @retval EFI_SUCCESS          This driver is removed Controller.
+  @retval EFI_DEVICE_ERROR     The device could not be stopped due to a device error.
+  @retval other                This driver was not removed from this device.
+
+**/
+EFI_STATUS
+EFIAPI
+DriverStop (
+  IN  EFI_DRIVER_BINDING_PROTOCOL * pThis,
+  IN  EFI_HANDLE Controller,
+  IN  UINTN NumberOfChildren,
+  IN  EFI_HANDLE * ChildHandleBuffer
+  )
+{
+		BOOLEAN                                   AllChildrenStopped;
+		UINTN                                     Index;
+		EFI_SIMPLE_NETWORK_PROTOCOL				  *SimpleNetwork;
+		EFI_STATUS                                Status = EFI_SUCCESS;
+		NIC_DEVICE								  *pNicDevice;
+		
+		//
+		// Complete all outstanding transactions to Controller.
+		// Don't allow any new transaction to Controller to be started.
+		//
+		if (NumberOfChildren == 0) {
+		
+		  Status = gBS->OpenProtocol (
+				                Controller,
+				                &gEfiSimpleNetworkProtocolGuid,
+				                (VOID **) &SimpleNetwork,
+				                pThis->DriverBindingHandle,
+				                Controller,
+				                EFI_OPEN_PROTOCOL_GET_PROTOCOL
+				                );
+				                
+			if (EFI_ERROR(Status)) {
+        //
+        // This is a 2nd type handle(multi-lun root), it needs to close devicepath
+        // and usbio protocol.
+        //
+        gBS->CloseProtocol (
+            Controller,
+            &gEfiDevicePathProtocolGuid,
+            pThis->DriverBindingHandle,
+            Controller
+            );
+        gBS->CloseProtocol (
+            Controller,
+            &gEfiUsbIoProtocolGuid,
+            pThis->DriverBindingHandle,
+            Controller
+            );
+        return EFI_SUCCESS;
+      }
+      
+      pNicDevice = DEV_FROM_SIMPLE_NETWORK ( SimpleNetwork );
+      
+      Status = gBS->UninstallMultipleProtocolInterfaces (
+				                  Controller,				                  
+				                  &gEfiCallerIdGuid,
+                          pNicDevice,
+                          &gEfiSimpleNetworkProtocolGuid,            
+                          &pNicDevice->SimpleNetwork,
+						              &gEfiDevicePathProtocolGuid,
+						              pNicDevice->MyDevPath,
+                          NULL
+                          );
+                          
+      if (EFI_ERROR (Status)) {
+        return Status;
+      }
+		  //
+		  // Close the bus driver
+		  //
+		  Status = gBS->CloseProtocol (
+		                  Controller,
+		                  &gEfiDevicePathProtocolGuid,
+		                  pThis->DriverBindingHandle,
+		                  Controller
+		                  );
+
+		  if (EFI_ERROR(Status)){
+          DEBUG ((EFI_D_ERROR, "driver stop: gBS->CloseProtocol:EfiDevicePathProtocol error. Status %r\n", Status));
+		  }
+
+		  Status = gBS->CloseProtocol (
+		                  Controller,
+		                  &gEfiUsbIoProtocolGuid,
+		                  pThis->DriverBindingHandle,
+		                  Controller
+		                  );
+
+		  if (EFI_ERROR(Status)){
+          DEBUG ((EFI_D_ERROR, "driver stop: gBS->CloseProtocol:EfiUsbIoProtocol error. Status %r\n", Status));
+		  }
+      return EFI_SUCCESS;
+		} 
+		AllChildrenStopped = TRUE;
+
+		for (Index = 0; Index < NumberOfChildren; Index++) {
+
+				Status = gBS->OpenProtocol (
+				                ChildHandleBuffer[Index],
+				                &gEfiSimpleNetworkProtocolGuid,
+				                (VOID **) &SimpleNetwork,
+				                pThis->DriverBindingHandle,
+				                Controller,
+				                EFI_OPEN_PROTOCOL_GET_PROTOCOL
+				                );
+				                
+				if (EFI_ERROR (Status)) {
+          AllChildrenStopped = FALSE;
+          DEBUG ((EFI_D_ERROR, "Fail to stop No.%d multi-lun child handle when opening SimpleNetwork\n", (UINT32)Index));
+          continue;
+        } 
+        
+        pNicDevice = DEV_FROM_SIMPLE_NETWORK ( SimpleNetwork );
+        
+        gBS->CloseProtocol (
+				                    Controller,
+				                    &gEfiUsbIoProtocolGuid,
+				                    pThis->DriverBindingHandle,
+				                    ChildHandleBuffer[Index]
+				                    ); 
+				                    
+				Status = gBS->UninstallMultipleProtocolInterfaces (
+				                  ChildHandleBuffer[Index],				                  
+				                  &gEfiCallerIdGuid,
+                          pNicDevice,
+                          &gEfiSimpleNetworkProtocolGuid,            
+                          &pNicDevice->SimpleNetwork,
+						              &gEfiDevicePathProtocolGuid,
+						              pNicDevice->MyDevPath,
+                          NULL
+                          );
+                          
+        if (EFI_ERROR (Status)) {
+            Status = gBS->OpenProtocol (                                                                         
+                  Controller,
+                  &gEfiUsbIoProtocolGuid,
+                  (VOID **) &pNicDevice->pUsbIo,
+                  pThis->DriverBindingHandle,
+                  ChildHandleBuffer[Index],
+                  EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+                  );
+        }
+        else {
+            int i;
+            RX_PKT * pCurr = pNicDevice->QueueHead;
+            RX_PKT * pFree;
+            
+            for ( i = 0 ; i < MAX_QUEUE_SIZE ; i++) {
+                 if ( NULL != pCurr ) {
+                    pFree = pCurr;
+                    pCurr = pCurr->pNext;
+                    gBS->FreePool (pFree);
+                 }
+            }
+            
+            if ( NULL != pNicDevice->pRxTest)
+						    gBS->FreePool (pNicDevice->pRxTest);
+
+					 if ( NULL != pNicDevice->pTxTest)
+						    gBS->FreePool (pNicDevice->pTxTest);
+
+           if ( NULL != pNicDevice->MyDevPath)
+					       gBS->FreePool (pNicDevice->MyDevPath);
+		  
+				    if ( NULL != pNicDevice)
+                  gBS->FreePool (pNicDevice);
+        }
+    }
+        
+        if (!AllChildrenStopped) {
+                return EFI_DEVICE_ERROR;
+        }
+        return EFI_SUCCESS;
+}
+
+
+/**
+  Driver binding protocol declaration
+**/
+EFI_DRIVER_BINDING_PROTOCOL  gDriverBinding = {
+  DriverSupported,
+  DriverStart,
+  DriverStop,
+  0xa,
+  NULL,
+  NULL
+};
+
+
+/**
+  Ax88772 driver unload routine.
+
+  @param [in] ImageHandle       Handle for the image.
+
+  @retval EFI_SUCCESS           Image may be unloaded
+
+**/
+EFI_STATUS
+EFIAPI
+DriverUnload (
+  IN EFI_HANDLE ImageHandle
+  )
+{
+  UINTN BufferSize;
+  UINTN Index;
+  UINTN Max;
+  EFI_HANDLE * pHandle;
+  EFI_STATUS Status;
+
+  //
+  //  Determine which devices are using this driver
+  //
+  BufferSize = 0;
+  pHandle = NULL;
+  Status = gBS->LocateHandle (
+                  ByProtocol,
+                  &gEfiCallerIdGuid,
+                  NULL,
+                  &BufferSize,
+                  NULL );
+  if ( EFI_BUFFER_TOO_SMALL == Status ) {
+    for ( ; ; ) {
+      //
+      //  One or more block IO devices are present
+      //
+      Status = gBS->AllocatePool (
+                      EfiRuntimeServicesData,
+                      BufferSize,
+                      (VOID **) &pHandle
+                      );
+      if ( EFI_ERROR ( Status )) {
+        DEBUG ((EFI_D_ERROR, "Insufficient memory, failed handle buffer allocation\r\n"));
+        break;
+      }
+
+      //
+      //  Locate the block IO devices
+      //
+      Status = gBS->LocateHandle (
+                      ByProtocol,
+                      &gEfiCallerIdGuid,
+                      NULL,
+                      &BufferSize,
+                      pHandle );
+      if ( EFI_ERROR ( Status )) {
+        //
+        //  Error getting handles
+        //
+        break;
+      }
+      
+      //
+      //  Remove any use of the driver
+      //
+      Max = BufferSize / sizeof ( pHandle[ 0 ]);
+      for ( Index = 0; Max > Index; Index++ ) {
+        Status = DriverStop ( &gDriverBinding,
+                              pHandle[ Index ],
+                              0,
+                              NULL );
+        if ( EFI_ERROR ( Status )) {
+          DEBUG ((EFI_D_ERROR, "WARNING - Failed to shutdown the driver on handle %08x\r\n", pHandle[ Index ]));
+          break;
+        }
+      }
+      break;
+    }
+  }
+  else {
+    if ( EFI_NOT_FOUND == Status ) {
+      //
+      //  No devices were found
+      //
+      Status = EFI_SUCCESS;
+    }
+  }
+
+  //
+  //  Free the handle array          
+  //
+  if ( NULL != pHandle ) {
+    gBS->FreePool ( pHandle );
+  }
+
+  //
+  //  Remove the protocols installed by the EntryPoint routine.
+  //
+  if ( !EFI_ERROR ( Status )) {
+    gBS->UninstallMultipleProtocolInterfaces (
+            ImageHandle,
+            &gEfiDriverBindingProtocolGuid,
+            &gDriverBinding,                              
+            &gEfiComponentNameProtocolGuid,
+            &gComponentName,
+            &gEfiComponentName2ProtocolGuid,
+            &gComponentName2,
+            NULL
+            );
+
+    DEBUG (( DEBUG_POOL | DEBUG_INIT | DEBUG_INFO,
+            "Removed:   gEfiComponentName2ProtocolGuid from 0x%08x\r\n",
+            ImageHandle ));
+    DEBUG (( DEBUG_POOL | DEBUG_INIT | DEBUG_INFO,
+              "Removed:   gEfiComponentNameProtocolGuid from 0x%08x\r\n",
+              ImageHandle ));
+    DEBUG (( DEBUG_POOL | DEBUG_INIT | DEBUG_INFO,
+              "Removed:   gEfiDriverBindingProtocolGuid from 0x%08x\r\n",
+              ImageHandle ));
+
+  }
+
+  return Status;
+}
+
+
+/**
+Ax88772 driver entry point.
+
+ at param [in] ImageHandle       Handle for the image.
+ at param [in] pSystemTable      Address of the system table.
+
+ at retval EFI_SUCCESS           Image successfully loaded.
+
+**/
+EFI_STATUS
+EFIAPI
+EntryPoint (
+  IN EFI_HANDLE ImageHandle,
+  IN EFI_SYSTEM_TABLE * pSystemTable
+  )
+{
+  EFI_STATUS    Status;
+
+  //
+  //  Add the driver to the list of drivers
+  //
+  Status = EfiLibInstallDriverBindingComponentName2 (
+             ImageHandle,
+             pSystemTable,
+             &gDriverBinding,
+             ImageHandle,
+             &gComponentName,
+             &gComponentName2
+             );
+  if ( !EFI_ERROR ( Status )) {
+    DEBUG ((EFI_D_INFO, "Installed: gEfiDriverBindingProtocolGuid on   0x%08x\r\n",
+              ImageHandle));
+    DEBUG ((EFI_D_INFO, "Installed: gEfiComponentNameProtocolGuid on   0x%08x\r\n",
+              ImageHandle));
+    DEBUG ((EFI_D_INFO,"Installed: gEfiComponentName2ProtocolGuid on   0x%08x\r\n",
+              ImageHandle ));
+
+  }
+  return Status;
+}
diff --git a/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/SimpleNetwork.c b/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/SimpleNetwork.c
new file mode 100644
index 0000000000..76babedb20
--- /dev/null
+++ b/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/SimpleNetwork.c
@@ -0,0 +1,1657 @@
+/** @file
+  Provides the Simple Network functions.
+
+  Copyright (c) 2011 - 2016, Intel Corporation. All rights reserved.
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "Ax88772.h"
+
+/**
+  This function updates the filtering on the receiver.
+
+  This support routine calls ::Ax88772MacAddressSet to update
+  the MAC address.  This routine then rebuilds the multicast
+  hash by calling ::Ax88772MulticastClear and ::Ax88772MulticastSet.
+  Finally this routine enables the receiver by calling
+  ::Ax88772RxControl.
+                                                                                  
+  @param [in] pSimpleNetwork    Simple network mode pointer  
+
+  @retval EFI_SUCCESS           This operation was successful.
+  @retval EFI_NOT_STARTED       The network interface was not started.
+  @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid
+                                EFI_SIMPLE_NETWORK_PROTOCOL structure.
+  @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
+  @retval EFI_UNSUPPORTED       The increased buffer size feature is not supported.
+
+**/
+EFI_STATUS
+ReceiveFilterUpdate (
+  IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork
+  )
+{
+  EFI_SIMPLE_NETWORK_MODE * pMode;
+  NIC_DEVICE * pNicDevice;
+  EFI_STATUS Status;
+  UINT32 Index;
+
+  //
+  // Set the MAC address
+  //
+  pNicDevice = DEV_FROM_SIMPLE_NETWORK ( pSimpleNetwork );
+  pMode = pSimpleNetwork->Mode;
+
+  //
+  // Clear the multicast hash table
+  //
+  Ax88772MulticastClear ( pNicDevice );
+
+  //
+  // Load the multicast hash table
+  //
+  if ( 0 != ( pMode->ReceiveFilterSetting & EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST )) {
+      for ( Index = 0; Index < pMode->MCastFilterCount; Index++ ) {
+        //
+        // Enable the next multicast address
+        //
+        Ax88772MulticastSet ( pNicDevice,
+                              &pMode->MCastFilter[ Index ].Addr[0]);
+      }
+  }
+
+  Status = Ax88772RxControl ( pNicDevice, pMode->ReceiveFilterSetting );
+
+  return Status;
+}
+
+
+/**
+  This function updates the SNP driver status.
+  
+  This function gets the current interrupt and recycled transmit
+  buffer status from the network interface.  The interrupt status
+  and the media status are returned as a bit mask in InterruptStatus.
+  If InterruptStatus is NULL, the interrupt status will not be read.
+  Upon successful return of the media status, the MediaPresent field
+  of EFI_SIMPLE_NETWORK_MODE will be updated to reflect any change
+  of media status.  If TxBuf is not NULL, a recycled transmit buffer
+  address will be retrived.  If a recycled transmit buffer address
+  is returned in TxBuf, then the buffer has been successfully
+  transmitted, and the status for that buffer is cleared.
+
+  This function calls ::Ax88772Rx to update the media status and
+  queue any receive packets.
+
+  @param [in] pSimpleNetwork    Protocol instance pointer
+  @param [in] pInterruptStatus  A pointer to the bit mask of the current active interrupts.
+                                If this is NULL, the interrupt status will not be read from
+                                the device.  If this is not NULL, the interrupt status will
+                                be read from teh device.  When the interrupt status is read,
+                                it will also be cleared.  Clearing the transmit interrupt
+                                does not empty the recycled transmit buffer array.
+  @param [out] ppTxBuf          Recycled transmit buffer address.  The network interface will
+                                not transmit if its internal recycled transmit buffer array is
+                                full.  Reading the transmit buffer does not clear the transmit
+                                interrupt.  If this is NULL, then the transmit buffer status
+                                will not be read.  If there are not transmit buffers to recycle
+                                and TxBuf is not NULL, *TxBuf will be set to NULL.
+
+  @retval EFI_SUCCESS           This operation was successful.
+  @retval EFI_NOT_STARTED       The network interface was not started.
+  @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid
+                                EFI_SIMPLE_NETWORK_PROTOCOL structure.
+  @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
+
+**/
+EFI_STATUS
+EFIAPI
+SN_GetStatus (
+  IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork,
+  OUT UINT32 * pInterruptStatus,
+  OUT VOID ** ppTxBuf
+  )
+{
+  EFI_SIMPLE_NETWORK_MODE * pMode;
+  NIC_DEVICE * pNicDevice;
+  EFI_STATUS Status;
+  BOOLEAN bFullDuplex;
+  BOOLEAN bLinkUp;
+  BOOLEAN bSpeed100;
+  EFI_TPL TplPrevious;
+ 
+  TplPrevious = gBS->RaiseTPL(TPL_CALLBACK);
+  //
+  // Verify the parameters
+  //
+  if (( NULL != pSimpleNetwork ) && ( NULL != pSimpleNetwork->Mode )) {
+    //
+    // Return the transmit buffer
+    //
+    
+    pNicDevice = DEV_FROM_SIMPLE_NETWORK ( pSimpleNetwork );
+    if (( NULL != ppTxBuf ) && ( NULL != pNicDevice->pTxBuffer )) {
+     		 *ppTxBuf = pNicDevice->pTxBuffer;
+     		 pNicDevice->pTxBuffer = NULL;
+   	}
+    
+    //
+    // Determine if interface is running
+    //
+    pMode = pSimpleNetwork->Mode;
+    if ( EfiSimpleNetworkInitialized == pMode->State ) {
+
+      if ( pNicDevice->LinkIdleCnt > MAX_LINKIDLE_THRESHOLD) {
+
+          bLinkUp = pNicDevice->bLinkUp;
+          bSpeed100 = pNicDevice->b100Mbps;
+          bFullDuplex = pNicDevice->bFullDuplex;
+          Status = Ax88772NegotiateLinkComplete ( pNicDevice,
+                                            &pNicDevice->PollCount,
+                                            &pNicDevice->bComplete,
+                                            &pNicDevice->bLinkUp,
+                                            &pNicDevice->b100Mbps,
+                                            &pNicDevice->bFullDuplex );
+
+          //
+          // Determine if the autonegotiation is complete
+          //
+          if ( pNicDevice->bComplete ) {
+              if ( pNicDevice->bLinkUp ) {
+                  if (( bSpeed100 && ( !pNicDevice->b100Mbps ))
+                      || (( !bSpeed100 ) && pNicDevice->b100Mbps )
+                      || ( bFullDuplex && ( !pNicDevice->bFullDuplex ))
+                      || (( !bFullDuplex ) && pNicDevice->bFullDuplex )) {
+                          pNicDevice->PollCount = 0;
+                          DEBUG (( EFI_D_INFO , "Reset to establish proper link setup: %d Mbps, %a duplex\r\n",
+                                    pNicDevice->b100Mbps ? 100 : 10, pNicDevice->bFullDuplex ? "Full" : "Half"));
+                          Status = SN_Reset ( &pNicDevice->SimpleNetwork, FALSE );
+                  }
+                  if (( !bLinkUp ) && pNicDevice->bLinkUp ) {
+                      //
+                      // Display the autonegotiation status
+                      //
+                      DEBUG (( EFI_D_INFO , "Link: Up, %d Mbps, %a duplex\r\n",
+                                pNicDevice->b100Mbps ? 100 : 10, pNicDevice->bFullDuplex ? "Full" : "Half"));
+
+                  }
+                  pNicDevice->LinkIdleCnt = 0;
+            }
+        }
+        //
+        //  Update the link status
+        //
+        if ( bLinkUp && ( !pNicDevice->bLinkUp )) {
+            DEBUG (( EFI_D_INFO , "Link: Down\r\n"));
+        }
+      }
+
+      pMode->MediaPresent = pNicDevice->bLinkUp;
+      //
+      // Return the interrupt status
+      //
+      if ( NULL != pInterruptStatus ) {
+        *pInterruptStatus = 0;
+      }   
+      Status = EFI_SUCCESS;
+    }
+    else {
+      if ( EfiSimpleNetworkStarted == pMode->State ) {
+        Status = EFI_DEVICE_ERROR;
+      }
+      else {
+        Status = EFI_NOT_STARTED;
+      }
+    }
+      
+  }
+  else {
+    Status = EFI_INVALID_PARAMETER;
+  }
+  gBS->RestoreTPL(TplPrevious) ;
+
+  return Status;
+}
+
+
+/**
+  Resets the network adapter and allocates the transmit and receive buffers
+  required by the network interface; optionally, also requests allocation of
+  additional transmit and receive buffers.  This routine must be called before
+  any other routine in the Simple Network protocol is called.
+
+  @param [in] pSimpleNetwork    Protocol instance pointer
+  @param [in] ExtraRxBufferSize Size in bytes to add to the receive buffer allocation
+  @param [in] ExtraTxBufferSize Size in bytes to add to the transmit buffer allocation
+
+  @retval EFI_SUCCESS           This operation was successful.
+  @retval EFI_NOT_STARTED       The network interface was not started.
+  @retval EFI_OUT_OF_RESOURCES  There was not enough memory for the transmit and receive buffers
+  @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid
+                                EFI_SIMPLE_NETWORK_PROTOCOL structure.
+  @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
+  @retval EFI_UNSUPPORTED       The increased buffer size feature is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+SN_Initialize (                                              
+  IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork,
+  IN UINTN ExtraRxBufferSize,
+  IN UINTN ExtraTxBufferSize
+  )
+{
+  EFI_SIMPLE_NETWORK_MODE * pMode;
+  EFI_STATUS Status;
+  UINT32  TmpState;
+   EFI_TPL TplPrevious;
+   
+   TplPrevious = gBS->RaiseTPL (TPL_CALLBACK);
+  //
+  // Verify the parameters
+  //
+  if (( NULL != pSimpleNetwork ) && ( NULL != pSimpleNetwork->Mode )) {
+    //
+    // Determine if the interface is already started
+    //
+    pMode = pSimpleNetwork->Mode;
+    if ( EfiSimpleNetworkStarted == pMode->State ) {
+      if (( 0 == ExtraRxBufferSize ) && ( 0 == ExtraTxBufferSize )) {
+        //
+        // Start the adapter
+        //
+        TmpState = pMode->State;
+        pMode->State = EfiSimpleNetworkInitialized;
+        Status = SN_Reset ( pSimpleNetwork, FALSE );
+        if ( EFI_ERROR ( Status )) {
+          //
+          // Update the network state
+          //
+          pMode->State = TmpState;
+          DEBUG (( EFI_D_ERROR , "SN_reset failed\n"));
+        }
+      }
+      else {
+        DEBUG (( EFI_D_ERROR , "Increase ExtraRxBufferSize = %d ExtraTxBufferSize=%d\n", 
+              ExtraRxBufferSize, ExtraTxBufferSize));
+        Status = EFI_UNSUPPORTED;
+      }
+    }
+    else {
+      Status = EFI_NOT_STARTED;
+    }
+  }
+  else {
+    Status = EFI_INVALID_PARAMETER;
+  }
+  gBS->RestoreTPL (TplPrevious);
+
+  return Status;
+}
+
+
+/**
+  This function converts a multicast IP address to a multicast HW MAC address
+  for all packet transactions.
+
+  @param [in] pSimpleNetwork    Protocol instance pointer
+  @param [in] bIPv6             Set to TRUE if the multicast IP address is IPv6 [RFC2460].
+                                Set to FALSE if the multicast IP address is IPv4 [RFC 791].
+  @param [in] pIP               The multicast IP address that is to be converted to a
+                                multicast HW MAC address.
+  @param [in] pMAC              The multicast HW MAC address that is to be generated from IP.
+
+  @retval EFI_SUCCESS           This operation was successful.
+  @retval EFI_NOT_STARTED       The network interface was not started.
+  @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid
+                                EFI_SIMPLE_NETWORK_PROTOCOL structure.
+  @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
+  @retval EFI_UNSUPPORTED       The increased buffer size feature is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+SN_MCastIPtoMAC (
+  IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork,
+  IN BOOLEAN bIPv6,
+  IN EFI_IP_ADDRESS * pIP,
+  OUT EFI_MAC_ADDRESS * pMAC
+  )
+{
+  EFI_STATUS Status;
+  EFI_TPL TplPrevious;
+
+  TplPrevious = gBS->RaiseTPL(TPL_CALLBACK);
+  //
+  // Get pointer to SNP driver instance for *this.
+  //
+  if (pSimpleNetwork == NULL) {
+    gBS->RestoreTPL(TplPrevious);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (pIP == NULL || pMAC == NULL) {
+    gBS->RestoreTPL(TplPrevious);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (bIPv6){
+    Status = EFI_UNSUPPORTED;
+  }  
+  else {
+      //
+      // check if the ip given is a mcast IP
+      //
+      if ((pIP->v4.Addr[0] & 0xF0) != 0xE0) {
+        gBS->RestoreTPL(TplPrevious);
+        return EFI_INVALID_PARAMETER;
+      }
+      else {
+        if (pSimpleNetwork->Mode->State == EfiSimpleNetworkInitialized)
+        {
+          pMAC->Addr[0] = 0x01;
+          pMAC->Addr[1] = 0x00;
+          pMAC->Addr[2] = 0x5e;
+          pMAC->Addr[3] = (UINT8) (pIP->v4.Addr[1] & 0x7f);
+          pMAC->Addr[4] = (UINT8) pIP->v4.Addr[2];
+          pMAC->Addr[5] = (UINT8) pIP->v4.Addr[3];
+          Status = EFI_SUCCESS;
+        }
+        else if (pSimpleNetwork->Mode->State == EfiSimpleNetworkStarted) {
+          Status = EFI_DEVICE_ERROR;
+        }
+        else {
+          Status = EFI_NOT_STARTED;
+        }
+        gBS->RestoreTPL(TplPrevious);
+      }
+  }
+  return Status;
+}
+
+
+/**
+  This function performs read and write operations on the NVRAM device
+  attached to a network interface.
+
+  @param [in] pSimpleNetwork    Protocol instance pointer
+  @param [in] ReadWrite         TRUE for read operations, FALSE for write operations.
+  @param [in] Offset            Byte offset in the NVRAM device at which to start the
+                                read or write operation.  This must be a multiple of
+                                NvRamAccessSize and less than NvRamSize.
+  @param [in] BufferSize        The number of bytes to read or write from the NVRAM device.
+                                This must also be a multiple of NvramAccessSize.
+  @param [in, out] pBuffer      A pointer to the data buffer.
+
+  @retval EFI_SUCCESS           This operation was successful.
+  @retval EFI_NOT_STARTED       The network interface was not started.
+  @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid
+                                EFI_SIMPLE_NETWORK_PROTOCOL structure.
+  @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
+  @retval EFI_UNSUPPORTED       The increased buffer size feature is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+SN_NvData (
+  IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork,
+  IN BOOLEAN ReadWrite,
+  IN UINTN Offset,
+  IN UINTN BufferSize,
+  IN OUT VOID * pBuffer
+  )
+{
+  EFI_STATUS Status;
+  //
+  // This is not currently supported
+  //
+  Status = EFI_UNSUPPORTED;
+  return Status;
+}
+
+VOID 
+FillPkt2Queue (
+  IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork,
+  IN UINTN BufLength)
+{
+
+  UINT16 * pLength;
+  UINT16 * pLengthBar;
+  UINT8* pData;
+  UINT32 offset;
+  NIC_DEVICE * pNicDevice;
+  
+  pNicDevice = DEV_FROM_SIMPLE_NETWORK ( pSimpleNetwork);
+  for ( offset = 0; offset < BufLength; ){
+      pLength = (UINT16*) (pNicDevice->pBulkInBuff + offset);
+      pLengthBar = (UINT16*) (pNicDevice->pBulkInBuff + offset +2);
+      
+      *pLength &= 0x7ff;
+      *pLengthBar &= 0x7ff;
+      *pLengthBar |= 0xf800;
+      
+      if ((*pLength ^ *pLengthBar ) != 0xFFFF) {
+          DEBUG (( EFI_D_ERROR , "Pkt length error. BufLength = %d\n", BufLength));
+          return;
+      }          
+      
+      if (TRUE == pNicDevice->pNextFill->f_Used) {
+        return;
+      }
+      else {
+          pData = pNicDevice->pBulkInBuff + offset + 4;
+          pNicDevice->pNextFill->f_Used = TRUE;
+          pNicDevice->pNextFill->Length = *pLength;
+          CopyMem (&pNicDevice->pNextFill->Data[0], pData, *pLength);
+          
+          pNicDevice->pNextFill = pNicDevice->pNextFill->pNext;
+          offset += ((*pLength + HW_HDR_LENGTH - 1) &~3) + 1;
+          pNicDevice->PktCntInQueue++;
+      }
+              
+  }
+}
+
+EFI_STATUS
+EFIAPI
+SN_Receive (
+  IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork,
+  OUT UINTN                      * pHeaderSize,
+  OUT UINTN                      * pBufferSize,
+  OUT VOID                       * pBuffer,
+  OUT EFI_MAC_ADDRESS            * pSrcAddr,
+  OUT EFI_MAC_ADDRESS            * pDestAddr,
+  OUT UINT16                     * pProtocol
+  )
+{
+  EFI_SIMPLE_NETWORK_MODE * pMode;
+  NIC_DEVICE * pNicDevice;
+  EFI_STATUS Status;
+  EFI_TPL TplPrevious;
+  UINT16 Type;
+  EFI_USB_IO_PROTOCOL *pUsbIo;
+  UINTN LengthInBytes;
+  UINT32 TransferStatus;
+  RX_PKT * pFirstFill;
+  TplPrevious = gBS->RaiseTPL (TPL_CALLBACK);
+  
+  //
+  // Verify the parameters
+  //
+  if (( NULL != pSimpleNetwork ) && 
+    ( NULL != pSimpleNetwork->Mode ) && 
+    (NULL != pBufferSize) && 
+    (NULL != pBuffer)) {
+    //
+    // The interface must be running
+    //
+    pMode = pSimpleNetwork->Mode;
+    if ( EfiSimpleNetworkInitialized == pMode->State ) {
+      //
+      // Update the link status
+      //
+      pNicDevice = DEV_FROM_SIMPLE_NETWORK ( pSimpleNetwork );
+      pNicDevice->LinkIdleCnt++;
+      pMode->MediaPresent = pNicDevice->bLinkUp;
+      
+      if ( pMode->MediaPresent && pNicDevice->bComplete) {
+      
+      
+        if (pNicDevice->PktCntInQueue != 0 ) {
+            DEBUG (( EFI_D_INFO, "pNicDevice->PktCntInQueue = %d\n",
+                pNicDevice->PktCntInQueue));
+        }
+        
+        LengthInBytes = MAX_BULKIN_SIZE;
+        if (pNicDevice->PktCntInQueue == 0 ){
+            //
+            // Attempt to do bulk in
+            //
+            SetMem (&pNicDevice->pBulkInBuff[0], 4, 0);
+            pUsbIo = pNicDevice->pUsbIo;
+            Status = pUsbIo->UsbBulkTransfer ( pUsbIo,
+                                       USB_ENDPOINT_DIR_IN | BULK_IN_ENDPOINT,
+                                       &pNicDevice->pBulkInBuff[0],
+                                       &LengthInBytes,
+                                       BULKIN_TIMEOUT,
+                                       &TransferStatus );
+                                       
+            if (LengthInBytes != 0 && !EFI_ERROR(Status) && !EFI_ERROR(TransferStatus) ){
+                FillPkt2Queue(pSimpleNetwork, LengthInBytes);
+            }
+        }
+        
+        pFirstFill = pNicDevice->pFirstFill;
+         
+        if (TRUE == pFirstFill->f_Used) {
+            ETHERNET_HEADER * pHeader;
+            pNicDevice->LinkIdleCnt = 0;
+            CopyMem (pBuffer,  &pFirstFill->Data[0], pFirstFill->Length);
+            pHeader = (ETHERNET_HEADER *) &pFirstFill->Data[0];
+                     
+            DEBUG (( EFI_D_INFO, "RX: %02x-%02x-%02x-%02x-%02x-%02x " 
+                      "%02x-%02x-%02x-%02x-%02x-%02x  %02x-%02x  %d bytes\r\n",
+                      pFirstFill->Data[0],
+                      pFirstFill->Data[1],
+                      pFirstFill->Data[2],
+                      pFirstFill->Data[3],
+                      pFirstFill->Data[4],
+                      pFirstFill->Data[5],
+                      pFirstFill->Data[6],
+                      pFirstFill->Data[7],
+                      pFirstFill->Data[8],
+                      pFirstFill->Data[9],
+                      pFirstFill->Data[10],
+                      pFirstFill->Data[11],
+                      pFirstFill->Data[12],
+                      pFirstFill->Data[13],
+                      pFirstFill->Length));   
+            
+            if ( NULL != pHeaderSize ) {
+              *pHeaderSize = sizeof ( *pHeader );
+            }
+            if ( NULL != pDestAddr ) {
+               CopyMem ( pDestAddr, &pHeader->dest_addr, PXE_HWADDR_LEN_ETHER );
+            }
+            if ( NULL != pSrcAddr ) {
+             CopyMem ( pSrcAddr, &pHeader->src_addr, PXE_HWADDR_LEN_ETHER );
+            }
+            if ( NULL != pProtocol ) {
+              Type = pHeader->type;
+              Type = (UINT16)(( Type >> 8 ) | ( Type << 8 ));
+              *pProtocol = Type;
+            }
+            Status = EFI_SUCCESS;
+            if (*pBufferSize < pFirstFill->Length) {
+                  DEBUG (( EFI_D_ERROR, "RX: Buffer was too small"));
+                  Status = EFI_BUFFER_TOO_SMALL;
+            }
+            *pBufferSize =  pFirstFill->Length;
+            pFirstFill->f_Used = FALSE;
+            pNicDevice->pFirstFill = pFirstFill->pNext;
+            pNicDevice->PktCntInQueue--;
+        }
+        else {
+            pNicDevice->LinkIdleCnt++;
+            Status = EFI_NOT_READY;
+        }
+      }
+      else {
+        //
+        //  Link no up
+        //
+        pNicDevice->LinkIdleCnt++;
+        Status = EFI_NOT_READY; 
+      }
+      
+    }
+    else {
+      if (EfiSimpleNetworkStarted == pMode->State) {
+        Status = EFI_DEVICE_ERROR;
+      }
+      else {
+        Status = EFI_NOT_STARTED;
+      }
+    }
+  }
+  else {
+    Status = EFI_INVALID_PARAMETER;
+  }                              
+  gBS->RestoreTPL (TplPrevious);
+  return Status;
+}
+
+/**
+  This function is used to enable and disable the hardware and software receive
+  filters for the underlying network device.
+
+  The receive filter change is broken down into three steps:
+
+    1.  The filter mask bits that are set (ON) in the Enable parameter
+        are added to the current receive filter settings.
+
+    2.  The filter mask bits that are set (ON) in the Disable parameter
+        are subtracted from the updated receive filter settins.
+
+    3.  If the resulting filter settigns is not supported by the hardware
+        a more liberal setting is selected.
+
+  If the same bits are set in the Enable and Disable parameters, then the bits
+  in the Disable parameter takes precedence.
+
+  If the ResetMCastFilter parameter is TRUE, then the multicast address list
+  filter is disabled (irregardless of what other multicast bits are set in
+  the enable and Disable parameters).  The SNP->Mode->MCastFilterCount field
+  is set to zero.  The SNP->Mode->MCastFilter contents are undefined.
+
+  After enableing or disabling receive filter settings, software should
+  verify the new settings by checking the SNP->Mode->ReceeiveFilterSettings,
+  SNP->Mode->MCastFilterCount and SNP->Mode->MCastFilter fields.
+
+  Note: Some network drivers and/or devices will automatically promote
+  receive filter settings if the requested setting can not be honored.
+  For example, if a request for four multicast addresses is made and
+  the underlying hardware only supports two multicast addresses the
+  driver might set the promiscuous or promiscuous multicast receive filters
+  instead.  The receiving software is responsible for discarding any extra
+  packets that get through the hardware receive filters.
+
+  If ResetMCastFilter is TRUE, then the multicast receive filter list
+  on the network interface will be reset to the default multicast receive
+  filter list.  If ResetMCastFilter is FALSE, and this network interface
+  allows the multicast receive filter list to be modified, then the
+  MCastFilterCnt and MCastFilter are used to update the current multicast
+  receive filter list.  The modified receive filter list settings can be
+  found in the MCastFilter field of EFI_SIMPLE_NETWORK_MODE.
+
+  This routine calls ::ReceiveFilterUpdate to update the receive
+  state in the network adapter.
+
+  @param [in] pSimpleNetwork    Protocol instance pointer
+  @param [in] Enable            A bit mask of receive filters to enable on the network interface.
+  @param [in] Disable           A bit mask of receive filters to disable on the network interface.
+                                For backward compatibility with EFI 1.1 platforms, the
+                                EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST bit must be set
+                                when the ResetMCastFilter parameter is TRUE.
+  @param [in] bResetMCastFilter Set to TRUE to reset the contents of the multicast receive
+                                filters on the network interface to their default values.
+  @param [in] MCastFilterCnt    Number of multicast HW MAC address in the new MCastFilter list.
+                                This value must be less than or equal to the MaxMCastFilterCnt
+                                field of EFI_SIMPLE_NETWORK_MODE.  This field is optional if
+                                ResetMCastFilter is TRUE.
+  @param [in] pMCastFilter      A pointer to a list of new multicast receive filter HW MAC
+                                addresses.  This list will replace any existing multicast
+                                HW MAC address list.  This field is optional if ResetMCastFilter
+                                is TRUE.
+
+  @retval EFI_SUCCESS           This operation was successful.
+  @retval EFI_NOT_STARTED       The network interface was not started.
+  @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid
+                                EFI_SIMPLE_NETWORK_PROTOCOL structure.
+  @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
+  @retval EFI_UNSUPPORTED       The increased buffer size feature is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+SN_ReceiveFilters (
+  IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork,
+  IN UINT32 Enable,
+  IN UINT32 Disable,
+/*
+#define EFI_SIMPLE_NETWORK_RECEIVE_UNICAST               0x01
+#define EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST             0x02
+#define EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST             0x04
+#define EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS           0x08
+#define EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST 0x10
+*/
+  IN BOOLEAN bResetMCastFilter,
+  IN UINTN MCastFilterCnt,
+  IN EFI_MAC_ADDRESS * pMCastFilter
+  )
+{
+  EFI_SIMPLE_NETWORK_MODE * pMode;
+  EFI_STATUS Status = EFI_SUCCESS;   
+  EFI_TPL TplPrevious; 
+
+  TplPrevious = gBS->RaiseTPL(TPL_CALLBACK);
+  pMode = pSimpleNetwork->Mode;
+
+  if (pSimpleNetwork == NULL) {
+    gBS->RestoreTPL(TplPrevious);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  switch (pMode->State) {
+    case EfiSimpleNetworkInitialized:
+      break;
+    case EfiSimpleNetworkStopped:
+      Status = EFI_NOT_STARTED;
+      gBS->RestoreTPL(TplPrevious);
+      return Status;
+    default:
+      Status = EFI_DEVICE_ERROR;
+      gBS->RestoreTPL(TplPrevious);
+      return Status;
+  }
+
+  //
+  // check if we are asked to enable or disable something that the UNDI
+  // does not even support!
+  //
+  if (((Enable &~pMode->ReceiveFilterMask) != 0) ||
+    ((Disable &~pMode->ReceiveFilterMask) != 0)) {
+    Status = EFI_INVALID_PARAMETER;
+    gBS->RestoreTPL(TplPrevious);
+    return Status;
+  }
+  
+  if (bResetMCastFilter) {
+    Disable |= (EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST & pMode->ReceiveFilterMask);
+      pMode->MCastFilterCount = 0;
+      if ( (0 == (pMode->ReceiveFilterSetting & EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST)) 
+            && Enable == 0 && Disable == 2) {
+            gBS->RestoreTPL(TplPrevious);
+            return EFI_SUCCESS;
+      }
+  } 
+  else {
+    if (MCastFilterCnt != 0) {
+      UINTN i; 
+      EFI_MAC_ADDRESS * pMulticastAddress;
+      pMulticastAddress =  pMCastFilter;
+      
+      if ((MCastFilterCnt > pMode->MaxMCastFilterCount) ||
+          (pMCastFilter == NULL)) {
+        Status = EFI_INVALID_PARAMETER;
+        gBS->RestoreTPL(TplPrevious);
+        return Status;
+      }
+      
+      for ( i = 0 ; i < MCastFilterCnt ; i++ ) {
+          UINT8  tmp;
+          tmp = pMulticastAddress->Addr[0];
+          if ( (tmp & 0x01) != 0x01 ) {
+            gBS->RestoreTPL(TplPrevious);
+            return EFI_INVALID_PARAMETER;
+          }
+          pMulticastAddress++;
+      }
+      
+      pMode->MCastFilterCount = (UINT32)MCastFilterCnt;
+      CopyMem (&pMode->MCastFilter[0],
+                     pMCastFilter,
+                     MCastFilterCnt * sizeof ( EFI_MAC_ADDRESS));
+    }
+  }
+  
+  if (Enable == 0 && Disable == 0 && !bResetMCastFilter && MCastFilterCnt == 0) {
+    Status = EFI_SUCCESS;
+    gBS->RestoreTPL(TplPrevious);
+    return Status;
+  }
+
+  if ((Enable & EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST) != 0 && MCastFilterCnt == 0) {
+    Status = EFI_INVALID_PARAMETER;
+    gBS->RestoreTPL(TplPrevious);
+    return Status;
+  }
+  
+  pMode->ReceiveFilterSetting |= Enable;
+  pMode->ReceiveFilterSetting &= ~Disable;
+  Status = ReceiveFilterUpdate (pSimpleNetwork);
+  
+  if (EFI_DEVICE_ERROR == Status || EFI_INVALID_PARAMETER == Status)
+      Status = EFI_SUCCESS;
+
+  gBS->RestoreTPL(TplPrevious);
+  return Status;
+}
+
+/**
+  Reset the network adapter.
+
+  Resets a network adapter and reinitializes it with the parameters that
+  were provided in the previous call to Initialize ().  The transmit and
+  receive queues are cleared.  Receive filters, the station address, the
+  statistics, and the multicast-IP-to-HW MAC addresses are not reset by
+  this call.
+
+  This routine calls ::Ax88772Reset to perform the adapter specific
+  reset operation.  This routine also starts the link negotiation
+  by calling ::Ax88772NegotiateLinkStart.
+
+  @param [in] pSimpleNetwork    Protocol instance pointer
+  @param [in] bExtendedVerification  Indicates that the driver may perform a more
+                                exhaustive verification operation of the device
+                                during reset.
+
+  @retval EFI_SUCCESS           This operation was successful.
+  @retval EFI_NOT_STARTED       The network interface was not started.
+  @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid
+                                EFI_SIMPLE_NETWORK_PROTOCOL structure.
+  @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
+  @retval EFI_UNSUPPORTED       The increased buffer size feature is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+SN_Reset (
+  IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork,
+  IN BOOLEAN bExtendedVerification
+  )
+{
+  EFI_SIMPLE_NETWORK_MODE * pMode;
+  NIC_DEVICE * pNicDevice;
+  EFI_STATUS Status;
+  EFI_TPL TplPrevious;
+
+  TplPrevious = gBS->RaiseTPL(TPL_CALLBACK);
+  //
+  //  Verify the parameters
+  //
+  if (( NULL != pSimpleNetwork ) && ( NULL != pSimpleNetwork->Mode )) {
+		pMode = pSimpleNetwork->Mode;
+		if ( EfiSimpleNetworkInitialized == pMode->State ) {
+    	//
+    	//  Update the device state
+    	//
+    	pNicDevice = DEV_FROM_SIMPLE_NETWORK ( pSimpleNetwork );
+    	pNicDevice->bComplete = FALSE;
+    	pNicDevice->bLinkUp = FALSE; 
+    	pNicDevice->bHavePkt = FALSE;
+    	pMode = pSimpleNetwork->Mode;
+    	pMode->MediaPresent = FALSE;
+
+    	//
+   		//  Reset the device
+    	//
+    	Status = Ax88772Reset ( pNicDevice );
+    	if ( !EFI_ERROR ( Status )) {
+     	 	//
+     	 	//  Update the receive filters in the adapter
+     	 	//
+     	 	Status = ReceiveFilterUpdate ( pSimpleNetwork );
+
+     	 	//
+     		 //  Try to get a connection to the network
+     	 	//
+     	 	if ( !EFI_ERROR ( Status )) {
+        	//
+        	//  Start the autonegotiation
+       		//
+        	Status = Ax88772NegotiateLinkStart ( pNicDevice );
+     		}
+   	 	}
+   	}
+   	else {
+      if (EfiSimpleNetworkStarted == pMode->State) {
+        Status = EFI_DEVICE_ERROR;
+      }
+      else {
+        Status = EFI_NOT_STARTED;
+      }
+   	}  
+  }
+  else {
+    Status = EFI_INVALID_PARAMETER;
+  }
+  gBS->RestoreTPL ( TplPrevious );
+  return Status;
+}
+
+/**
+  Initialize the simple network protocol.
+
+  This routine calls ::Ax88772MacAddressGet to obtain the
+  MAC address.
+
+  @param [in] pNicDevice       NIC_DEVICE_INSTANCE pointer
+
+  @retval EFI_SUCCESS     Setup was successful
+
+**/
+EFI_STATUS
+SN_Setup (
+  IN NIC_DEVICE * pNicDevice
+  )
+{
+  
+
+  EFI_SIMPLE_NETWORK_MODE * pMode;
+  EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork;
+  EFI_STATUS Status;
+  RX_PKT * pCurr = NULL;
+  RX_PKT * pPrev = NULL;
+
+	pSimpleNetwork = &pNicDevice->SimpleNetwork;  
+  pSimpleNetwork->Revision = EFI_SIMPLE_NETWORK_PROTOCOL_REVISION;
+  pSimpleNetwork->Start = (EFI_SIMPLE_NETWORK_START)SN_Start;
+  pSimpleNetwork->Stop = (EFI_SIMPLE_NETWORK_STOP)SN_Stop;
+  pSimpleNetwork->Initialize = (EFI_SIMPLE_NETWORK_INITIALIZE)SN_Initialize;
+  pSimpleNetwork->Reset = (EFI_SIMPLE_NETWORK_RESET)SN_Reset;
+  pSimpleNetwork->Shutdown = (EFI_SIMPLE_NETWORK_SHUTDOWN)SN_Shutdown;
+  pSimpleNetwork->ReceiveFilters = (EFI_SIMPLE_NETWORK_RECEIVE_FILTERS)SN_ReceiveFilters;
+  pSimpleNetwork->StationAddress = (EFI_SIMPLE_NETWORK_STATION_ADDRESS)SN_StationAddress;
+  pSimpleNetwork->Statistics = (EFI_SIMPLE_NETWORK_STATISTICS)SN_Statistics;
+  pSimpleNetwork->MCastIpToMac = (EFI_SIMPLE_NETWORK_MCAST_IP_TO_MAC)SN_MCastIPtoMAC;
+  pSimpleNetwork->NvData = (EFI_SIMPLE_NETWORK_NVDATA)SN_NvData;
+  pSimpleNetwork->GetStatus = (EFI_SIMPLE_NETWORK_GET_STATUS)SN_GetStatus;
+  pSimpleNetwork->Transmit = (EFI_SIMPLE_NETWORK_TRANSMIT)SN_Transmit;
+  pSimpleNetwork->Receive = (EFI_SIMPLE_NETWORK_RECEIVE)SN_Receive;
+  pSimpleNetwork->WaitForPacket = NULL;
+  pMode = &pNicDevice->SimpleNetworkData;
+  pSimpleNetwork->Mode = pMode;
+  pMode->State = EfiSimpleNetworkStopped;
+  pMode->HwAddressSize = PXE_HWADDR_LEN_ETHER;
+  pMode->MediaHeaderSize = sizeof ( ETHERNET_HEADER );
+  pMode->MaxPacketSize = MAX_ETHERNET_PKT_SIZE;
+  pMode->NvRamSize = 0;
+  pMode->NvRamAccessSize = 0;
+  pMode->ReceiveFilterMask = EFI_SIMPLE_NETWORK_RECEIVE_UNICAST
+                           | EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST
+                           | EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST
+                           | EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS
+                           | EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST;
+  pMode->ReceiveFilterSetting = EFI_SIMPLE_NETWORK_RECEIVE_UNICAST
+                              | EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST;
+  pMode->MaxMCastFilterCount = MAX_MCAST_FILTER_CNT;
+  pMode->MCastFilterCount = 0;
+  SetMem ( &pMode->BroadcastAddress,
+           PXE_HWADDR_LEN_ETHER,
+           0xff );
+  pMode->IfType = EfiNetworkInterfaceUndi;
+  pMode->MacAddressChangeable = TRUE;
+  pMode->MultipleTxSupported = FALSE;
+  pMode->MediaPresentSupported = TRUE;
+  pMode->MediaPresent = FALSE;
+  pNicDevice->LinkIdleCnt = 0;
+  //
+  //  Read the MAC address
+  //
+  pNicDevice->PhyId = PHY_ID_INTERNAL;
+  pNicDevice->b100Mbps = TRUE;
+  pNicDevice->bFullDuplex = TRUE;
+  
+  Status = Ax88772MacAddressGet (
+                pNicDevice,
+                &pMode->PermanentAddress.Addr[0]);
+
+  if ( !EFI_ERROR ( Status )) {
+    int i; 
+    //
+    //  Use the hardware address as the current address
+    //
+
+    CopyMem ( &pMode->CurrentAddress,
+              &pMode->PermanentAddress,
+              PXE_HWADDR_LEN_ETHER );
+              
+    CopyMem ( &pNicDevice->MAC,
+              &pMode->PermanentAddress,
+              PXE_HWADDR_LEN_ETHER );
+              
+    pNicDevice->PktCntInQueue = 0;
+    
+    for ( i = 0 ; i < MAX_QUEUE_SIZE ; i++) {
+        Status = gBS->AllocatePool ( EfiRuntimeServicesData, 
+                                      sizeof (RX_PKT),
+                                      (VOID **) &pCurr);
+        if ( EFI_ERROR(Status)) {
+            DEBUG (( EFI_D_ERROR, "Memory are not enough\n"));
+            return Status;
+        }                              
+        pCurr->f_Used = FALSE;
+        
+        if ( i ) {
+            pPrev->pNext = pCurr;
+        }
+        else {
+            pNicDevice->QueueHead = pCurr;
+        }
+        
+        if (MAX_QUEUE_SIZE - 1 == i) {
+            pCurr->pNext = pNicDevice->QueueHead;
+        }
+        
+        pPrev = pCurr;
+    }
+    
+    pNicDevice->pNextFill = pNicDevice->QueueHead;
+    pNicDevice->pFirstFill = pNicDevice->QueueHead;
+    
+    Status = gBS->AllocatePool (EfiRuntimeServicesData,
+                                MAX_BULKIN_SIZE,
+                                (VOID **) &pNicDevice->pBulkInBuff);
+                                
+    if (EFI_ERROR(Status)) {
+        DEBUG (( EFI_D_ERROR, "gBS->AllocatePool for pBulkInBuff error. Status = %r\n",
+              Status));
+        return Status;
+    }
+  }
+  else {
+    DEBUG (( EFI_D_ERROR, "Ax88772MacAddressGet error. Status = %r\n", Status));
+		return Status;
+  }
+  
+  Status = gBS->AllocatePool ( EfiRuntimeServicesData,
+                                   sizeof ( RX_TX_PACKET ),
+                                   (VOID **) &pNicDevice->pRxTest );
+
+  if (EFI_ERROR (Status)) {
+    DEBUG (( EFI_D_ERROR, "gBS->AllocatePool:pNicDevice->pRxTest error. Status = %r\n",
+              Status));
+	  return Status;
+  }
+                                   
+  Status = gBS->AllocatePool ( EfiRuntimeServicesData,
+                                   sizeof ( RX_TX_PACKET ),
+                                   (VOID **) &pNicDevice->pTxTest );
+
+  if (EFI_ERROR (Status)) {
+    DEBUG (( EFI_D_ERROR, "gBS->AllocatePool:pNicDevice->pTxTest error. Status = %r\n",
+              Status));
+	  gBS->FreePool (pNicDevice->pRxTest);
+  }
+
+  return Status;
+}
+
+
+/**
+  This routine starts the network interface.
+
+  @param [in] pSimpleNetwork    Protocol instance pointer
+
+  @retval EFI_SUCCESS           This operation was successful.
+  @retval EFI_ALREADY_STARTED   The network interface was already started.
+  @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid
+                                EFI_SIMPLE_NETWORK_PROTOCOL structure.
+  @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
+  @retval EFI_UNSUPPORTED       The increased buffer size feature is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+SN_Start (
+  IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork
+  )
+{
+  NIC_DEVICE * pNicDevice;
+  EFI_SIMPLE_NETWORK_MODE * pMode;
+  EFI_STATUS Status;
+  EFI_TPL TplPrevious;
+  int i = 0;
+  RX_PKT * pCurr = NULL;
+
+  TplPrevious = gBS->RaiseTPL(TPL_CALLBACK);
+  //
+  // Verify the parameters
+  //
+  Status = EFI_INVALID_PARAMETER;
+  if (( NULL != pSimpleNetwork ) && ( NULL != pSimpleNetwork->Mode )) {
+    pMode = pSimpleNetwork->Mode;
+    if ( EfiSimpleNetworkStopped == pMode->State ) {
+      //
+      // Initialize the mode structuref
+      // NVRAM access is not supported
+      //
+      ZeroMem ( pMode, sizeof ( *pMode ));
+  
+      pMode->State = EfiSimpleNetworkStarted;
+      pMode->HwAddressSize = PXE_HWADDR_LEN_ETHER;
+      pMode->MediaHeaderSize = sizeof ( ETHERNET_HEADER );
+      pMode->MaxPacketSize = MAX_ETHERNET_PKT_SIZE;
+      pMode->ReceiveFilterMask = EFI_SIMPLE_NETWORK_RECEIVE_UNICAST
+                               | EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST
+                               | EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST
+                               | EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS
+                               | EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST;
+      pMode->ReceiveFilterSetting = EFI_SIMPLE_NETWORK_RECEIVE_UNICAST;
+      pMode->MaxMCastFilterCount = MAX_MCAST_FILTER_CNT;
+      pNicDevice = DEV_FROM_SIMPLE_NETWORK ( pSimpleNetwork );
+      Status = Ax88772MacAddressGet ( pNicDevice, &pMode->PermanentAddress.Addr[0]);
+      CopyMem ( &pMode->CurrentAddress,
+                &pMode->PermanentAddress,
+                sizeof ( pMode->CurrentAddress ));
+      SetMem(&pMode->BroadcastAddress, PXE_HWADDR_LEN_ETHER, 0xff);
+      pMode->IfType = EfiNetworkInterfaceUndi;
+      pMode->MacAddressChangeable = TRUE;
+      pMode->MultipleTxSupported = FALSE;
+      pMode->MediaPresentSupported = TRUE;
+      pMode->MediaPresent = FALSE; 
+      pNicDevice->PktCntInQueue = 0;
+      pNicDevice->pNextFill = pNicDevice->QueueHead;
+      pNicDevice->pFirstFill = pNicDevice->QueueHead;
+      pCurr = pNicDevice->QueueHead;
+      
+      for ( i = 0 ; i < MAX_QUEUE_SIZE ; i++) { 
+        pCurr->f_Used = FALSE;
+        pCurr = pCurr->pNext;
+      }
+      
+    }
+    else {
+      Status = EFI_ALREADY_STARTED;
+    }
+  }
+  gBS->RestoreTPL ( TplPrevious );
+  return Status;
+}
+
+
+/**
+  Set the MAC address.
+  
+  This function modifies or resets the current station address of a
+  network interface.  If Reset is TRUE, then the current station address
+  is set ot the network interface's permanent address.  If Reset if FALSE
+  then the current station address is changed to the address specified by
+  pNew.
+
+  This routine calls ::Ax88772MacAddressSet to update the MAC address
+  in the network adapter.
+
+  @param [in] pSimpleNetwork    Protocol instance pointer
+  @param [in] bReset            Flag used to reset the station address to the
+                                network interface's permanent address.
+  @param [in] pNew              New station address to be used for the network
+                                interface.
+
+  @retval EFI_SUCCESS           This operation was successful.
+  @retval EFI_NOT_STARTED       The network interface was not started.
+  @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid
+                                EFI_SIMPLE_NETWORK_PROTOCOL structure.
+  @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
+  @retval EFI_UNSUPPORTED       The increased buffer size feature is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+SN_StationAddress (
+  IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork,
+  IN BOOLEAN bReset,
+  IN EFI_MAC_ADDRESS * pNew
+  )
+{
+  NIC_DEVICE * pNicDevice;
+  EFI_SIMPLE_NETWORK_MODE * pMode;
+  EFI_STATUS Status;
+  EFI_TPL TplPrevious;
+  
+  TplPrevious = gBS->RaiseTPL(TPL_CALLBACK);
+  //
+  // Verify the parameters
+  //
+  if (( NULL != pSimpleNetwork )
+    && ( NULL != pSimpleNetwork->Mode )
+    && (( bReset ) || ( ( !bReset) && ( NULL != pNew )))) {
+    //
+    // Verify that the adapter is already started
+    //
+    pNicDevice = DEV_FROM_SIMPLE_NETWORK ( pSimpleNetwork );
+    pMode = pSimpleNetwork->Mode;
+    if ( EfiSimpleNetworkInitialized == pMode->State ) {
+      //
+      // Determine the adapter MAC address
+      //
+      if ( bReset ) {
+        //
+        // Use the permanent address
+        //
+        CopyMem ( &pMode->CurrentAddress,
+                  &pMode->PermanentAddress,
+                  sizeof ( pMode->CurrentAddress ));
+      }
+      else {
+        //
+        // Use the specified address
+        //
+        CopyMem ( &pMode->CurrentAddress,
+                  pNew,
+                  sizeof ( pMode->CurrentAddress ));
+      }
+
+      //
+      // Update the address on the adapter
+      //
+      Status = Ax88772MacAddressSet ( pNicDevice, &pMode->CurrentAddress.Addr[0]);
+    }
+    else {
+      if (EfiSimpleNetworkStarted == pMode->State) {
+        Status = EFI_DEVICE_ERROR;
+      }
+      else {
+        Status = EFI_NOT_STARTED;
+      }
+    }
+  }
+  else {
+    Status = EFI_INVALID_PARAMETER;
+  }
+  gBS->RestoreTPL ( TplPrevious );
+  return Status;
+}
+
+
+/**
+  This function resets or collects the statistics on a network interface.
+  If the size of the statistics table specified by StatisticsSize is not
+  big enough for all of the statistics that are collected by the network
+  interface, then a partial buffer of statistics is returned in
+  StatisticsTable.
+
+  @param [in] pSimpleNetwork    Protocol instance pointer
+  @param [in] bReset            Set to TRUE to reset the statistics for the network interface.
+  @param [in, out] pStatisticsSize  On input the size, in bytes, of StatisticsTable.  On output
+                                the size, in bytes, of the resulting table of statistics.
+  @param [out] pStatisticsTable A pointer to the EFI_NETWORK_STATISTICS structure that
+                                conains the statistics.
+
+  @retval EFI_SUCCESS           This operation was successful.
+  @retval EFI_NOT_STARTED       The network interface was not started.
+  @retval EFI_BUFFER_TOO_SMALL  The pStatisticsTable is NULL or the buffer is too small.
+  @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid
+                                EFI_SIMPLE_NETWORK_PROTOCOL structure.
+  @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
+  @retval EFI_UNSUPPORTED       The increased buffer size feature is not supported.
+
+  typedef struct {
+  UINT64 RxTotalFrames;
+  UINT64 RxGoodFrames;
+  UINT64 RxUndersizeFrames;
+  UINT64 RxOversizeFrames;
+  UINT64 RxDroppedFrames;
+  UINT64 RxUnicastFrames;
+  UINT64 RxBroadcastFrames;
+  UINT64 RxMulticastFrames;
+  UINT64 RxCrcErrorFrames;
+  UINT64 RxTotalBytes;
+  UINT64 TxTotalFrames;
+  UINT64 TxGoodFrames;
+  UINT64 TxUndersizeFrames;
+  UINT64 TxOversizeFrames;
+  UINT64 TxDroppedFrames;
+  UINT64 TxUnicastFrames;
+  UINT64 TxBroadcastFrames;
+  UINT64 TxMulticastFrames;
+  UINT64 TxCrcErrorFrames;
+  UINT64 TxTotalBytes;
+  UINT64 Collisions;
+  UINT64 UnsupportedProtocol;
+  } EFI_NETWORK_STATISTICS;
+**/
+EFI_STATUS
+EFIAPI
+SN_Statistics (
+  IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork,
+  IN BOOLEAN bReset,
+  IN OUT UINTN * pStatisticsSize,
+  OUT EFI_NETWORK_STATISTICS * pStatisticsTable
+  )
+{
+  EFI_STATUS Status;
+  EFI_SIMPLE_NETWORK_MODE * pMode;
+  //
+  // Verify the prarameters
+  //
+  if (( NULL != pSimpleNetwork ) && ( NULL != pSimpleNetwork->Mode )) {
+    pMode = pSimpleNetwork->Mode;
+    //
+    // Determine if the interface is started 
+    //
+    if (EfiSimpleNetworkInitialized == pMode->State){
+      //
+      // Determine if the StatisticsSize is big enough
+      //
+      if (sizeof (EFI_NETWORK_STATISTICS) <= *pStatisticsSize){
+        if (bReset) {
+          Status = EFI_SUCCESS;
+        } 
+        else {
+          Status = EFI_UNSUPPORTED;
+        }
+      }
+      else {
+        Status = EFI_BUFFER_TOO_SMALL;
+      }
+    }
+    else{
+      if (EfiSimpleNetworkStarted == pMode->State) {
+        Status = EFI_DEVICE_ERROR;
+      }
+      else {
+        Status = EFI_NOT_STARTED;
+      }
+    }
+  }
+  else {
+  	Status = EFI_INVALID_PARAMETER;
+  }
+
+  return Status;
+}
+
+
+/**
+  This function stops a network interface.  This call is only valid
+  if the network interface is in the started state.
+
+  @param [in] pSimpleNetwork    Protocol instance pointer
+
+  @retval EFI_SUCCESS           This operation was successful.
+  @retval EFI_NOT_STARTED       The network interface was not started.
+  @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid
+                                EFI_SIMPLE_NETWORK_PROTOCOL structure.
+  @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
+  @retval EFI_UNSUPPORTED       The increased buffer size feature is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+SN_Stop (
+  IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork
+  )
+{
+  EFI_SIMPLE_NETWORK_MODE * pMode;
+  EFI_STATUS Status;
+  EFI_TPL TplPrevious;
+  
+  TplPrevious = gBS->RaiseTPL(TPL_CALLBACK);
+  //
+  // Verify the parameters
+  //
+  if (( NULL != pSimpleNetwork ) && ( NULL != pSimpleNetwork->Mode )) {
+    //
+    // Determine if the interface is started
+    //
+    pMode = pSimpleNetwork->Mode;   
+    if ( EfiSimpleNetworkStarted == pMode->State ) {
+        pMode->State = EfiSimpleNetworkStopped;
+        Status = EFI_SUCCESS; 
+    }
+    else {
+        Status = EFI_NOT_STARTED;
+    }
+  } 
+  else {
+    Status = EFI_INVALID_PARAMETER;
+  }
+  
+  gBS->RestoreTPL ( TplPrevious );
+  return Status;
+}
+
+
+/**
+  This function releases the memory buffers assigned in the Initialize() call.
+  Pending transmits and receives are lost, and interrupts are cleared and disabled.
+  After this call, only Initialize() and Stop() calls may be used.
+
+  @param [in] pSimpleNetwork    Protocol instance pointer
+
+  @retval EFI_SUCCESS           This operation was successful.
+  @retval EFI_NOT_STARTED       The network interface was not started.
+  @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid
+                                EFI_SIMPLE_NETWORK_PROTOCOL structure.
+  @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
+  @retval EFI_UNSUPPORTED       The increased buffer size feature is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+SN_Shutdown (
+  IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork
+  )
+{
+  EFI_SIMPLE_NETWORK_MODE * pMode;
+  UINT32 RxFilter;
+  EFI_STATUS Status;
+  EFI_TPL TplPrevious;
+  
+  TplPrevious = gBS->RaiseTPL(TPL_CALLBACK);
+  //
+  // Verify the parameters
+  //
+  if (( NULL != pSimpleNetwork ) && ( NULL != pSimpleNetwork->Mode )) {
+    //
+    // Determine if the interface is already started
+    //
+    pMode = pSimpleNetwork->Mode;
+    if ( EfiSimpleNetworkInitialized == pMode->State ) {
+      //
+      // Stop the adapter
+      //
+      RxFilter = pMode->ReceiveFilterSetting;
+      pMode->ReceiveFilterSetting = 0;
+      Status = SN_Reset ( pSimpleNetwork, FALSE );
+      pMode->ReceiveFilterSetting = RxFilter;
+      if ( !EFI_ERROR ( Status )) {
+
+        //
+        // Update the network state
+        //
+        pMode->State = EfiSimpleNetworkStarted;
+      }
+      else if ( EFI_DEVICE_ERROR == Status ) {
+      	pMode->State = EfiSimpleNetworkStopped;
+      }
+    }
+    else {
+      Status = EFI_NOT_STARTED;
+    }
+  }
+  else {
+    Status = EFI_INVALID_PARAMETER;
+  }
+  gBS->RestoreTPL ( TplPrevious );
+  return Status;
+}
+
+
+/**
+  Send a packet over the network.
+
+  This function places the packet specified by Header and Buffer on
+  the transmit queue.  This function performs a non-blocking transmit
+  operation.  When the transmit is complete, the buffer is returned
+  via the GetStatus() call.
+
+  This routine calls ::Ax88772Rx to empty the network adapter of
+  receive packets.  The routine then passes the transmit packet
+  to the network adapter.
+
+  @param [in] pSimpleNetwork    Protocol instance pointer
+  @param [in] HeaderSize        The size, in bytes, of the media header to be filled in by
+                                the Transmit() function.  If HeaderSize is non-zero, then
+                                it must be equal to SimpleNetwork->Mode->MediaHeaderSize
+                                and DestAddr and Protocol parameters must not be NULL.
+  @param [in] BufferSize        The size, in bytes, of the entire packet (media header and
+                                data) to be transmitted through the network interface.
+  @param [in] pBuffer           A pointer to the packet (media header followed by data) to
+                                to be transmitted.  This parameter can not be NULL.  If
+                                HeaderSize is zero, then the media header is Buffer must
+                                already be filled in by the caller.  If HeaderSize is nonzero,
+                                then the media header will be filled in by the Transmit()
+                                function.
+  @param [in] pSrcAddr          The source HW MAC address.  If HeaderSize is zero, then
+                                this parameter is ignored.  If HeaderSize is nonzero and
+                                SrcAddr is NULL, then SimpleNetwork->Mode->CurrentAddress
+                                is used for the source HW MAC address.
+  @param [in] pDestAddr         The destination HW MAC address.  If HeaderSize is zero, then
+                                this parameter is ignored.
+  @param [in] pProtocol         The type of header to build.  If HeaderSize is zero, then
+                                this parameter is ignored.
+
+  @retval EFI_SUCCESS           This operation was successful.
+  @retval EFI_NOT_STARTED       The network interface was not started.
+  @retval EFI_NOT_READY         The network interface is too busy to accept this transmit request.
+  @retval EFI_BUFFER_TOO_SMALL  The BufferSize parameter is too small.
+  @retval EFI_INVALID_PARAMETER pSimpleNetwork parameter was NULL or did not point to a valid
+                                EFI_SIMPLE_NETWORK_PROTOCOL structure.
+  @retval EFI_DEVICE_ERROR      The command could not be sent to the network interface.
+
+**/
+EFI_STATUS
+EFIAPI
+SN_Transmit (
+  IN EFI_SIMPLE_NETWORK_PROTOCOL * pSimpleNetwork,
+  IN UINTN HeaderSize,
+  IN UINTN BufferSize,
+  IN VOID * pBuffer,
+  IN EFI_MAC_ADDRESS * pSrcAddr,
+  IN EFI_MAC_ADDRESS * pDestAddr,
+  IN UINT16 * pProtocol
+  )
+{
+  ETHERNET_HEADER * pHeader;
+  EFI_SIMPLE_NETWORK_MODE * pMode;
+  NIC_DEVICE * pNicDevice;
+  EFI_USB_IO_PROTOCOL * pUsbIo;
+  EFI_STATUS Status;
+  UINTN TransferLength;
+  UINT32 TransferStatus;
+  UINT16 Type;
+  EFI_TPL TplPrevious;
+
+  TplPrevious = gBS->RaiseTPL(TPL_CALLBACK);
+
+  // Verify the parameters
+  //
+  if (( NULL != pSimpleNetwork ) && 
+      ( NULL != pSimpleNetwork->Mode ) && 
+      ( NULL != pBuffer) && 
+      ( (HeaderSize == 0) || ( (NULL != pDestAddr) && (NULL != pProtocol) ))) {
+    //
+    // The interface must be running
+    //
+    pMode = pSimpleNetwork->Mode;
+    //
+    // Verify parameter of HeaderSize
+    //
+    if ((HeaderSize == 0) || (HeaderSize == pMode->MediaHeaderSize)){
+      //
+      // Determine if BufferSize is big enough
+      //
+      if (BufferSize >= pMode->MediaHeaderSize){
+        if ( EfiSimpleNetworkInitialized == pMode->State ) {
+          //
+          // Update the link status
+          //
+          pNicDevice = DEV_FROM_SIMPLE_NETWORK ( pSimpleNetwork );
+          pMode->MediaPresent = pNicDevice->bLinkUp;
+
+          //
+          //  Release the synchronization with Ax88772Timer
+          //      
+          if ( pMode->MediaPresent && pNicDevice->bComplete) {
+            //
+            //  Copy the packet into the USB buffer
+            //
+
+            CopyMem ( &pNicDevice->pTxTest->Data[0], pBuffer, BufferSize ); 
+            pNicDevice->pTxTest->Length = (UINT16) BufferSize;
+
+            //
+            //  Transmit the packet
+            //
+            pHeader = (ETHERNET_HEADER *) &pNicDevice->pTxTest->Data[0];
+            if ( 0 != HeaderSize ) {
+              if ( NULL != pDestAddr ) {
+                CopyMem ( &pHeader->dest_addr, pDestAddr, PXE_HWADDR_LEN_ETHER );
+              }
+              if ( NULL != pSrcAddr ) {
+                CopyMem ( &pHeader->src_addr, pSrcAddr, PXE_HWADDR_LEN_ETHER );
+              }
+              else {
+                CopyMem ( &pHeader->src_addr, &pMode->CurrentAddress.Addr[0], PXE_HWADDR_LEN_ETHER );
+              }
+              if ( NULL != pProtocol ) {
+                Type = *pProtocol;
+              }
+              else {
+                Type = pNicDevice->pTxTest->Length;
+              }
+              Type = (UINT16)(( Type >> 8 ) | ( Type << 8 ));
+              pHeader->type = Type;
+            }
+            if ( pNicDevice->pTxTest->Length < MIN_ETHERNET_PKT_SIZE ) {
+              pNicDevice->pTxTest->Length = MIN_ETHERNET_PKT_SIZE;
+              ZeroMem ( &pNicDevice->pTxTest->Data[ BufferSize ],
+                        pNicDevice->pTxTest->Length - BufferSize );
+            }
+        
+            DEBUG ((EFI_D_INFO, "TX: %02x-%02x-%02x-%02x-%02x-%02x  %02x-%02x-%02x-%02x-%02x-%02x"
+                      "  %02x-%02x  %d bytes\r\n",
+                      pNicDevice->pTxTest->Data[0],
+                      pNicDevice->pTxTest->Data[1],
+                      pNicDevice->pTxTest->Data[2],
+                      pNicDevice->pTxTest->Data[3],
+                      pNicDevice->pTxTest->Data[4],
+                      pNicDevice->pTxTest->Data[5],
+                      pNicDevice->pTxTest->Data[6],
+                      pNicDevice->pTxTest->Data[7],
+                      pNicDevice->pTxTest->Data[8],
+                      pNicDevice->pTxTest->Data[9],
+                      pNicDevice->pTxTest->Data[10],
+                      pNicDevice->pTxTest->Data[11],
+                      pNicDevice->pTxTest->Data[12],
+                      pNicDevice->pTxTest->Data[13],
+                      pNicDevice->pTxTest->Length ));
+
+            pNicDevice->pTxTest->LengthBar = ~(pNicDevice->pTxTest->Length);
+            TransferLength = sizeof ( pNicDevice->pTxTest->Length )
+                           + sizeof ( pNicDevice->pTxTest->LengthBar )
+                           + pNicDevice->pTxTest->Length;
+                           
+            if (TransferLength % 512 == 0 || TransferLength % 1024 == 0)
+                TransferLength +=4;
+
+            //
+            //  Work around USB bus driver bug where a timeout set by receive
+            //  succeeds but the timeout expires immediately after, causing the
+            //  transmit operation to timeout.
+            //
+            pUsbIo = pNicDevice->pUsbIo;
+            Status = pUsbIo->UsbBulkTransfer ( pUsbIo,
+                                               BULK_OUT_ENDPOINT,
+                                               &pNicDevice->pTxTest->Length,
+                                               &TransferLength,
+                                               0xfffffffe, 
+                                               &TransferStatus );
+            if ( !EFI_ERROR ( Status )) {
+              Status = TransferStatus;
+            }
+
+            if ( !EFI_ERROR ( Status )) {
+              pNicDevice->pTxBuffer = pBuffer;
+            }
+            else {
+              if ((TransferLength != (UINTN)( pNicDevice->pTxTest->Length + 4 )) &&
+                   (TransferLength != (UINTN)(( pNicDevice->pTxTest->Length + 4 ) + 4))) {
+                DEBUG ((EFI_D_INFO, "TransferLength didn't match Packet Length\n"));
+              }
+              //
+              //  Reset the controller to fix the error
+              //
+              if ( EFI_DEVICE_ERROR == Status ) {
+                SN_Reset ( pSimpleNetwork, FALSE );
+              }
+              Status = EFI_NOT_READY;
+            }
+          }
+          else {
+            //
+            // No packets available.
+            //
+            Status = EFI_NOT_READY;
+          }
+          
+        }
+        else {
+          if (EfiSimpleNetworkStarted == pMode->State) {
+            Status = EFI_DEVICE_ERROR;
+          }
+          else {
+            Status = EFI_NOT_STARTED ;
+          }
+        }
+      }
+      else {
+        Status = EFI_BUFFER_TOO_SMALL;
+      }
+    }
+    else {
+      Status = EFI_INVALID_PARAMETER;
+    }
+  }
+  else {
+    Status = EFI_INVALID_PARAMETER;
+  }
+  
+  gBS->RestoreTPL (TplPrevious);
+
+  return Status;
+}
diff --git a/Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430.c b/Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430.c
new file mode 100644
index 0000000000..4e7830ea94
--- /dev/null
+++ b/Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430.c
@@ -0,0 +1,917 @@
+/** @file
+  Cirrus Logic 5430 Controller Driver.
+  This driver is a sample implementation of the UGA Draw and Graphics Output
+  Protocols for the Cirrus Logic 5430 family of PCI video controllers.
+  This driver is only usable in the EFI pre-boot environment.
+  This sample is intended to show how the UGA Draw and Graphics output Protocol
+  is able to function.
+  The UGA I/O Protocol is not implemented in this sample.
+  A fully compliant EFI UGA driver requires both
+  the UGA Draw and the UGA I/O Protocol.  Please refer to Microsoft's
+  documentation on UGA for details on how to write a UGA driver that is able
+  to function both in the EFI pre-boot environment and from the OS runtime.
+
+  Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+//
+// Cirrus Logic 5430 Controller Driver
+//
+#include "CirrusLogic5430.h"
+
+EFI_DRIVER_BINDING_PROTOCOL gCirrusLogic5430DriverBinding = {
+  CirrusLogic5430ControllerDriverSupported,
+  CirrusLogic5430ControllerDriverStart,
+  CirrusLogic5430ControllerDriverStop,
+  0x10,
+  NULL,
+  NULL
+};
+
+///
+/// Generic Attribute Controller Register Settings
+///
+UINT8  AttributeController[21] = {
+  0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+  0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+  0x41, 0x00, 0x0F, 0x00, 0x00
+};
+
+///
+/// Generic Graphics Controller Register Settings
+///
+UINT8 GraphicsController[9] = {
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F, 0xFF
+};
+
+//
+// 640 x 480 x 256 color @ 60 Hertz
+//
+UINT8 Crtc_640_480_256_60[28] = {
+  0x5d, 0x4f, 0x50, 0x82, 0x53, 0x9f, 0x00, 0x3e,
+  0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0xe1, 0x83, 0xdf, 0x50, 0x00, 0xe7, 0x04, 0xe3,
+  0xff, 0x00, 0x00, 0x22
+};
+
+UINT16 Seq_640_480_256_60[15] = {
+  0x0100, 0x0101, 0x0f02, 0x0003, 0x0e04, 0x1107, 0x0008, 0x4a0b,
+  0x5b0c, 0x450d, 0x7e0e, 0x2b1b, 0x2f1c, 0x301d, 0x331e
+};
+
+//
+// 800 x 600 x 256 color @ 60 Hertz
+//
+UINT8 Crtc_800_600_256_60[28] = {
+  0x7F, 0x63, 0x64, 0x80, 0x6B, 0x1B, 0x72, 0xF0,
+  0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x58, 0x8C, 0x57, 0x64, 0x00, 0x5F, 0x91, 0xE3,
+  0xFF, 0x00, 0x00, 0x22
+};
+
+UINT16 Seq_800_600_256_60[15] = {
+  0x0100, 0x0101, 0x0f02, 0x0003, 0x0e04, 0x1107, 0x0008, 0x4a0b,
+  0x5b0c, 0x450d, 0x510e, 0x2b1b, 0x2f1c, 0x301d, 0x3a1e
+};
+
+//
+// 1024 x 768 x 256 color @ 60 Hertz
+//
+UINT8 Crtc_1024_768_256_60[28] = {
+  0xA3, 0x7F, 0x80, 0x86, 0x85, 0x96, 0x24, 0xFD,
+  0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x02, 0x88, 0xFF, 0x80, 0x00, 0x00, 0x24, 0xE3,
+  0xFF, 0x4A, 0x00, 0x22
+};
+
+UINT16 Seq_1024_768_256_60[15] = {
+  0x0100, 0x0101, 0x0f02, 0x0003, 0x0e04, 0x1107, 0x0008, 0x4a0b,
+  0x5b0c, 0x450d, 0x760e, 0x2b1b, 0x2f1c, 0x301d, 0x341e
+};
+
+///
+/// Table of supported video modes
+///
+CIRRUS_LOGIC_5430_VIDEO_MODES  CirrusLogic5430VideoModes[] = {
+  {  640, 480, 8, 60, Crtc_640_480_256_60,  Seq_640_480_256_60,  0xe3 },
+  {  800, 600, 8, 60, Crtc_800_600_256_60,  Seq_800_600_256_60,  0xef },
+  { 1024, 768, 8, 60, Crtc_1024_768_256_60, Seq_1024_768_256_60, 0xef }
+};
+
+
+/**
+  CirrusLogic5430ControllerDriverSupported
+
+  TODO:    This - add argument and description to function comment
+  TODO:    Controller - add argument and description to function comment
+  TODO:    RemainingDevicePath - add argument and description to function comment
+**/
+EFI_STATUS
+EFIAPI
+CirrusLogic5430ControllerDriverSupported (
+  IN EFI_DRIVER_BINDING_PROTOCOL    *This,
+  IN EFI_HANDLE                     Controller,
+  IN EFI_DEVICE_PATH_PROTOCOL       *RemainingDevicePath
+  )
+{
+  EFI_STATUS          Status;
+  EFI_PCI_IO_PROTOCOL *PciIo;
+  PCI_TYPE00          Pci;
+  EFI_DEV_PATH        *Node;
+
+  //
+  // Open the PCI I/O Protocol
+  //
+  Status = gBS->OpenProtocol (
+                  Controller,
+                  &gEfiPciIoProtocolGuid,
+                  (VOID **) &PciIo,
+                  This->DriverBindingHandle,
+                  Controller,
+                  EFI_OPEN_PROTOCOL_BY_DRIVER
+                  );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Read the PCI Configuration Header from the PCI Device
+  //
+  Status = PciIo->Pci.Read (
+                        PciIo,
+                        EfiPciIoWidthUint32,
+                        0,
+                        sizeof (Pci) / sizeof (UINT32),
+                        &Pci
+                        );
+  if (EFI_ERROR (Status)) {
+    goto Done;
+  }
+
+  Status = EFI_UNSUPPORTED;
+  //
+  // See if the I/O enable is on.  Most systems only allow one VGA device to be turned on
+  // at a time, so see if this is one that is turned on.
+  //
+  //  if (((Pci.Hdr.Command & 0x01) == 0x01)) {
+  //
+  // See if this is a Cirrus Logic PCI controller
+  //
+  if (Pci.Hdr.VendorId == CIRRUS_LOGIC_VENDOR_ID) {
+    //
+    // See if this is a 5430 or a 5446 PCI controller
+    //
+    if (Pci.Hdr.DeviceId == CIRRUS_LOGIC_5430_DEVICE_ID || 
+        Pci.Hdr.DeviceId == CIRRUS_LOGIC_5430_ALTERNATE_DEVICE_ID ||
+        Pci.Hdr.DeviceId == CIRRUS_LOGIC_5446_DEVICE_ID) {
+        
+      Status = EFI_SUCCESS;
+      //
+      // If this is an Intel 945 graphics controller,
+      // go further check RemainingDevicePath validation
+      //
+      if (RemainingDevicePath != NULL) {
+        Node = (EFI_DEV_PATH *) RemainingDevicePath;
+        //
+        // Check if RemainingDevicePath is the End of Device Path Node, 
+        // if yes, return EFI_SUCCESS
+        //
+        if (!IsDevicePathEnd (Node)) {
+          //
+          // If RemainingDevicePath isn't the End of Device Path Node,
+          // check its validation
+          //
+          if (Node->DevPath.Type != ACPI_DEVICE_PATH ||
+              Node->DevPath.SubType != ACPI_ADR_DP ||
+              DevicePathNodeLength(&Node->DevPath) != sizeof(ACPI_ADR_DEVICE_PATH)) {
+            Status = EFI_UNSUPPORTED;
+          }
+        }
+      }
+    }
+  }
+
+Done:
+  //
+  // Close the PCI I/O Protocol
+  //
+  gBS->CloseProtocol (
+        Controller,
+        &gEfiPciIoProtocolGuid,
+        This->DriverBindingHandle,
+        Controller
+        );
+
+  return Status;
+}
+
+/**
+  CirrusLogic5430ControllerDriverStart
+
+  TODO:    This - add argument and description to function comment
+  TODO:    Controller - add argument and description to function comment
+  TODO:    RemainingDevicePath - add argument and description to function comment
+**/
+EFI_STATUS
+EFIAPI
+CirrusLogic5430ControllerDriverStart (
+  IN EFI_DRIVER_BINDING_PROTOCOL    *This,
+  IN EFI_HANDLE                     Controller,
+  IN EFI_DEVICE_PATH_PROTOCOL       *RemainingDevicePath
+  )
+{
+  EFI_STATUS                      Status;
+  CIRRUS_LOGIC_5430_PRIVATE_DATA  *Private;
+  BOOLEAN                         PciAttributesSaved;
+  EFI_DEVICE_PATH_PROTOCOL        *ParentDevicePath;
+  ACPI_ADR_DEVICE_PATH            AcpiDeviceNode;
+  UINT64                          Supports;
+
+  PciAttributesSaved = FALSE;
+  //
+  // Allocate Private context data for UGA Draw inteface.
+  //
+  Private = AllocateZeroPool (sizeof (CIRRUS_LOGIC_5430_PRIVATE_DATA));
+  if (Private == NULL) {
+    Status = EFI_OUT_OF_RESOURCES;
+    goto Error;
+  }
+
+  //
+  // Set up context record
+  //
+  Private->Signature  = CIRRUS_LOGIC_5430_PRIVATE_DATA_SIGNATURE;
+  Private->Handle     = NULL;
+
+  //
+  // Open PCI I/O Protocol
+  //
+  Status = gBS->OpenProtocol (
+                  Controller,
+                  &gEfiPciIoProtocolGuid,
+                  (VOID **) &Private->PciIo,
+                  This->DriverBindingHandle,
+                  Controller,
+                  EFI_OPEN_PROTOCOL_BY_DRIVER
+                  );
+  if (EFI_ERROR (Status)) {
+    goto Error;
+  }
+
+  //
+  // Get supported PCI attributes
+  //
+  Status = Private->PciIo->Attributes (
+                             Private->PciIo,
+                             EfiPciIoAttributeOperationSupported,
+                             0,
+                             &Supports
+                             );
+  if (EFI_ERROR (Status)) {
+    goto Error;
+  }
+
+  Supports &= (EFI_PCI_IO_ATTRIBUTE_VGA_IO | EFI_PCI_IO_ATTRIBUTE_VGA_IO_16);
+  if (Supports == 0 || Supports == (EFI_PCI_IO_ATTRIBUTE_VGA_IO | EFI_PCI_IO_ATTRIBUTE_VGA_IO_16)) {
+    Status = EFI_UNSUPPORTED;
+    goto Error;
+  }  
+
+  //
+  // Save original PCI attributes
+  //
+  Status = Private->PciIo->Attributes (
+                    Private->PciIo,
+                    EfiPciIoAttributeOperationGet,
+                    0,
+                    &Private->OriginalPciAttributes
+                    );
+
+  if (EFI_ERROR (Status)) {
+    goto Error;
+  }
+  PciAttributesSaved = TRUE;
+
+  Status = Private->PciIo->Attributes (
+                             Private->PciIo,
+                             EfiPciIoAttributeOperationEnable,
+                             EFI_PCI_DEVICE_ENABLE | EFI_PCI_IO_ATTRIBUTE_VGA_MEMORY | Supports,
+                             NULL
+                             );
+  if (EFI_ERROR (Status)) {
+    goto Error;
+  }
+
+  //
+  // Get ParentDevicePath
+  //
+  Status = gBS->HandleProtocol (
+                  Controller,
+                  &gEfiDevicePathProtocolGuid,
+                  (VOID **) &ParentDevicePath
+                  );
+  if (EFI_ERROR (Status)) {
+    goto Error;
+  }
+
+  if (FeaturePcdGet (PcdSupportGop)) {
+    //
+    // Set Gop Device Path
+    //
+    if (RemainingDevicePath == NULL) {
+      ZeroMem (&AcpiDeviceNode, sizeof (ACPI_ADR_DEVICE_PATH));
+      AcpiDeviceNode.Header.Type = ACPI_DEVICE_PATH;
+      AcpiDeviceNode.Header.SubType = ACPI_ADR_DP;
+      AcpiDeviceNode.ADR = ACPI_DISPLAY_ADR (1, 0, 0, 1, 0, ACPI_ADR_DISPLAY_TYPE_VGA, 0, 0);
+      SetDevicePathNodeLength (&AcpiDeviceNode.Header, sizeof (ACPI_ADR_DEVICE_PATH));
+
+      Private->GopDevicePath = AppendDevicePathNode (
+                                          ParentDevicePath,
+                                          (EFI_DEVICE_PATH_PROTOCOL *) &AcpiDeviceNode
+                                          );
+    } else if (!IsDevicePathEnd (RemainingDevicePath)) {
+      //
+      // If RemainingDevicePath isn't the End of Device Path Node, 
+      // only scan the specified device by RemainingDevicePath
+      //
+      Private->GopDevicePath = AppendDevicePathNode (ParentDevicePath, RemainingDevicePath);
+    } else {
+      //
+      // If RemainingDevicePath is the End of Device Path Node, 
+      // don't create child device and return EFI_SUCCESS
+      //
+      Private->GopDevicePath = NULL;
+    }
+      
+    if (Private->GopDevicePath != NULL) {
+      //
+      // Creat child handle and device path protocol firstly
+      //
+      Private->Handle = NULL;
+      Status = gBS->InstallMultipleProtocolInterfaces (
+                      &Private->Handle,
+                      &gEfiDevicePathProtocolGuid,
+                      Private->GopDevicePath,
+                      NULL
+                      );
+    }
+  }
+
+  //
+  // Construct video mode buffer
+  //
+  Status = CirrusLogic5430VideoModeSetup (Private);
+  if (EFI_ERROR (Status)) {
+    goto Error;
+  }
+
+  if (FeaturePcdGet (PcdSupportUga)) {
+    //
+    // Start the UGA Draw software stack.
+    //
+    Status = CirrusLogic5430UgaDrawConstructor (Private);
+    ASSERT_EFI_ERROR (Status);
+
+    Private->UgaDevicePath = ParentDevicePath;
+    Status = gBS->InstallMultipleProtocolInterfaces (
+                    &Controller,
+                    &gEfiUgaDrawProtocolGuid,
+                    &Private->UgaDraw,
+                    &gEfiDevicePathProtocolGuid,
+                    Private->UgaDevicePath,
+                    NULL
+                    );
+
+  } else if (FeaturePcdGet (PcdSupportGop)) {
+    if (Private->GopDevicePath == NULL) {
+      //
+      // If RemainingDevicePath is the End of Device Path Node, 
+      // don't create child device and return EFI_SUCCESS
+      //
+      Status = EFI_SUCCESS;
+    } else {
+  
+      //
+      // Start the GOP software stack.
+      //
+      Status = CirrusLogic5430GraphicsOutputConstructor (Private);
+      ASSERT_EFI_ERROR (Status);
+  
+      Status = gBS->InstallMultipleProtocolInterfaces (
+                      &Private->Handle,
+                      &gEfiGraphicsOutputProtocolGuid,
+                      &Private->GraphicsOutput,
+                      &gEfiEdidDiscoveredProtocolGuid,
+                      &Private->EdidDiscovered,
+                      &gEfiEdidActiveProtocolGuid,
+                      &Private->EdidActive,
+                      NULL
+                      );
+    }
+  } else {
+    //
+    // This driver must support eithor GOP or UGA or both.
+    //
+    ASSERT (FALSE);
+    Status = EFI_UNSUPPORTED;
+  }
+
+
+Error:
+  if (EFI_ERROR (Status)) {
+    if (Private) {
+      if (Private->PciIo) {
+        if (PciAttributesSaved == TRUE) {
+          //
+          // Restore original PCI attributes
+          //
+          Private->PciIo->Attributes (
+                          Private->PciIo,
+                          EfiPciIoAttributeOperationSet,
+                          Private->OriginalPciAttributes,
+                          NULL
+                          );
+        }
+        //
+        // Close the PCI I/O Protocol
+        //
+        gBS->CloseProtocol (
+              Private->Handle,
+              &gEfiPciIoProtocolGuid,
+              This->DriverBindingHandle,
+              Private->Handle
+              );
+      }
+
+      gBS->FreePool (Private);
+    }
+  }
+
+  return Status;
+}
+
+/**
+  CirrusLogic5430ControllerDriverStop
+
+  TODO:    This - add argument and description to function comment
+  TODO:    Controller - add argument and description to function comment
+  TODO:    NumberOfChildren - add argument and description to function comment
+  TODO:    ChildHandleBuffer - add argument and description to function comment
+  TODO:    EFI_SUCCESS - add return value to function comment
+**/
+EFI_STATUS
+EFIAPI
+CirrusLogic5430ControllerDriverStop (
+  IN EFI_DRIVER_BINDING_PROTOCOL    *This,
+  IN EFI_HANDLE                     Controller,
+  IN UINTN                          NumberOfChildren,
+  IN EFI_HANDLE                     *ChildHandleBuffer
+  )
+{
+  EFI_UGA_DRAW_PROTOCOL           *UgaDraw;
+  EFI_GRAPHICS_OUTPUT_PROTOCOL    *GraphicsOutput;
+
+  EFI_STATUS                      Status;
+  CIRRUS_LOGIC_5430_PRIVATE_DATA  *Private;
+
+  if (FeaturePcdGet (PcdSupportUga)) {
+    Status = gBS->OpenProtocol (
+                    Controller,
+                    &gEfiUgaDrawProtocolGuid,
+                    (VOID **) &UgaDraw,
+                    This->DriverBindingHandle,
+                    Controller,
+                    EFI_OPEN_PROTOCOL_GET_PROTOCOL
+                    );
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+    //
+    // Get our private context information
+    //
+    Private = CIRRUS_LOGIC_5430_PRIVATE_DATA_FROM_UGA_DRAW_THIS (UgaDraw);
+    CirrusLogic5430UgaDrawDestructor (Private);
+
+    if (FeaturePcdGet (PcdSupportGop)) {
+      CirrusLogic5430GraphicsOutputDestructor (Private);
+      //
+      // Remove the UGA and GOP protocol interface from the system
+      //
+      Status = gBS->UninstallMultipleProtocolInterfaces (
+                      Private->Handle,
+                      &gEfiUgaDrawProtocolGuid,
+                      &Private->UgaDraw,
+                      &gEfiGraphicsOutputProtocolGuid,
+                      &Private->GraphicsOutput,
+                      NULL
+                      );
+    } else {
+      //
+      // Remove the UGA Draw interface from the system
+      //
+      Status = gBS->UninstallMultipleProtocolInterfaces (
+                      Private->Handle,
+                      &gEfiUgaDrawProtocolGuid,
+                      &Private->UgaDraw,
+                      NULL
+                      );
+    }
+  } else {
+    Status = gBS->OpenProtocol (
+                    Controller,
+                    &gEfiGraphicsOutputProtocolGuid,
+                    (VOID **) &GraphicsOutput,
+                    This->DriverBindingHandle,
+                    Controller,
+                    EFI_OPEN_PROTOCOL_GET_PROTOCOL
+                    );
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+
+    //
+    // Get our private context information
+    //
+    Private = CIRRUS_LOGIC_5430_PRIVATE_DATA_FROM_GRAPHICS_OUTPUT_THIS (GraphicsOutput);
+
+    CirrusLogic5430GraphicsOutputDestructor (Private);
+    //
+    // Remove the GOP protocol interface from the system
+    //
+    Status = gBS->UninstallMultipleProtocolInterfaces (
+                    Private->Handle,
+                    &gEfiUgaDrawProtocolGuid,
+                    &Private->UgaDraw,
+                    &gEfiGraphicsOutputProtocolGuid,
+                    &Private->GraphicsOutput,
+                    NULL
+                    );
+  }
+
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Restore original PCI attributes
+  //
+  Private->PciIo->Attributes (
+                  Private->PciIo,
+                  EfiPciIoAttributeOperationSet,
+                  Private->OriginalPciAttributes,
+                  NULL
+                  );
+
+  //
+  // Close the PCI I/O Protocol
+  //
+  gBS->CloseProtocol (
+        Controller,
+        &gEfiPciIoProtocolGuid,
+        This->DriverBindingHandle,
+        Controller
+        );
+
+  //
+  // Free our instance data
+  //
+  gBS->FreePool (Private);
+
+  return EFI_SUCCESS;
+}
+
+/**
+  CirrusLogic5430UgaDrawDestructor
+
+  TODO:    Private - add argument and description to function comment
+  TODO:    EFI_SUCCESS - add return value to function comment
+**/
+EFI_STATUS
+CirrusLogic5430UgaDrawDestructor (
+  CIRRUS_LOGIC_5430_PRIVATE_DATA  *Private
+  )
+{
+  return EFI_SUCCESS;
+}
+
+/**
+  TODO: Add function description
+
+  @param  Private TODO: add argument description
+  @param  Address TODO: add argument description
+  @param  Data TODO: add argument description
+
+  TODO: add return values
+
+**/
+VOID
+outb (
+  CIRRUS_LOGIC_5430_PRIVATE_DATA  *Private,
+  UINTN                           Address,
+  UINT8                           Data
+  )
+{
+  Private->PciIo->Io.Write (
+                      Private->PciIo,
+                      EfiPciIoWidthUint8,
+                      EFI_PCI_IO_PASS_THROUGH_BAR,
+                      Address,
+                      1,
+                      &Data
+                      );
+}
+
+/**
+  TODO: Add function description
+
+  @param  Private TODO: add argument description
+  @param  Address TODO: add argument description
+  @param  Data TODO: add argument description
+
+  TODO: add return values
+
+**/
+VOID
+outw (
+  CIRRUS_LOGIC_5430_PRIVATE_DATA  *Private,
+  UINTN                           Address,
+  UINT16                          Data
+  )
+{
+  Private->PciIo->Io.Write (
+                      Private->PciIo,
+                      EfiPciIoWidthUint16,
+                      EFI_PCI_IO_PASS_THROUGH_BAR,
+                      Address,
+                      1,
+                      &Data
+                      );
+}
+
+/**
+  TODO: Add function description
+
+  @param  Private TODO: add argument description
+  @param  Address TODO: add argument description
+
+  TODO: add return values
+
+**/
+UINT8
+inb (
+  CIRRUS_LOGIC_5430_PRIVATE_DATA  *Private,
+  UINTN                           Address
+  )
+{
+  UINT8 Data;
+
+  Private->PciIo->Io.Read (
+                      Private->PciIo,
+                      EfiPciIoWidthUint8,
+                      EFI_PCI_IO_PASS_THROUGH_BAR,
+                      Address,
+                      1,
+                      &Data
+                      );
+  return Data;
+}
+
+/**
+  TODO: Add function description
+
+  @param  Private TODO: add argument description
+  @param  Address TODO: add argument description
+
+  TODO: add return values
+
+**/
+UINT16
+inw (
+  CIRRUS_LOGIC_5430_PRIVATE_DATA  *Private,
+  UINTN                           Address
+  )
+{
+  UINT16  Data;
+
+  Private->PciIo->Io.Read (
+                      Private->PciIo,
+                      EfiPciIoWidthUint16,
+                      EFI_PCI_IO_PASS_THROUGH_BAR,
+                      Address,
+                      1,
+                      &Data
+                      );
+  return Data;
+}
+
+/**
+  TODO: Add function description
+
+  @param  Private TODO: add argument description
+  @param  Index TODO: add argument description
+  @param  Red TODO: add argument description
+  @param  Green TODO: add argument description
+  @param  Blue TODO: add argument description
+
+  TODO: add return values
+
+**/
+VOID
+SetPaletteColor (
+  CIRRUS_LOGIC_5430_PRIVATE_DATA  *Private,
+  UINTN                           Index,
+  UINT8                           Red,
+  UINT8                           Green,
+  UINT8                           Blue
+  )
+{
+  outb (Private, PALETTE_INDEX_REGISTER, (UINT8) Index);
+  outb (Private, PALETTE_DATA_REGISTER, (UINT8) (Red >> 2));
+  outb (Private, PALETTE_DATA_REGISTER, (UINT8) (Green >> 2));
+  outb (Private, PALETTE_DATA_REGISTER, (UINT8) (Blue >> 2));
+}
+
+/**
+  TODO: Add function description
+
+  @param  Private TODO: add argument description
+
+  TODO: add return values
+
+**/
+VOID
+SetDefaultPalette (
+  CIRRUS_LOGIC_5430_PRIVATE_DATA  *Private
+  )
+{
+  UINTN Index;
+  UINTN RedIndex;
+  UINTN GreenIndex;
+  UINTN BlueIndex;
+
+  Index = 0;
+  for (RedIndex = 0; RedIndex < 8; RedIndex++) {
+    for (GreenIndex = 0; GreenIndex < 8; GreenIndex++) {
+      for (BlueIndex = 0; BlueIndex < 4; BlueIndex++) {
+        SetPaletteColor (Private, Index, (UINT8) (RedIndex << 5), (UINT8) (GreenIndex << 5), (UINT8) (BlueIndex << 6));
+        Index++;
+      }
+    }
+  }
+}
+
+/**
+  TODO: Add function description
+
+  @param  Private TODO: add argument description
+
+  TODO: add return values
+
+**/
+VOID
+ClearScreen (
+  CIRRUS_LOGIC_5430_PRIVATE_DATA  *Private
+  )
+{
+  UINT32  Color;
+
+  Color = 0;
+  Private->PciIo->Mem.Write (
+                        Private->PciIo,
+                        EfiPciIoWidthFillUint32,
+                        0,
+                        0,
+                        0x100000 >> 2,
+                        &Color
+                        );
+}
+
+/**
+  TODO: Add function description
+
+  @param  Private TODO: add argument description
+
+  TODO: add return values
+
+**/
+VOID
+DrawLogo (
+  CIRRUS_LOGIC_5430_PRIVATE_DATA  *Private,
+  UINTN                           ScreenWidth,
+  UINTN                           ScreenHeight
+  )
+{
+}
+
+/**
+  TODO: Add function description
+
+  @param  Private TODO: add argument description
+  @param  ModeData TODO: add argument description
+
+  TODO: add return values
+
+**/
+VOID
+InitializeGraphicsMode (
+  CIRRUS_LOGIC_5430_PRIVATE_DATA  *Private,
+  CIRRUS_LOGIC_5430_VIDEO_MODES   *ModeData
+  )
+{
+  UINT8 Byte;
+  UINTN Index;
+  UINT16 DeviceId;
+  EFI_STATUS Status;
+
+  Status = Private->PciIo->Pci.Read (
+             Private->PciIo,
+             EfiPciIoWidthUint16,
+             PCI_DEVICE_ID_OFFSET,
+             1,
+             &DeviceId
+             );
+  //
+  // Read the PCI Configuration Header from the PCI Device
+  //
+  ASSERT_EFI_ERROR (Status);
+
+  outw (Private, SEQ_ADDRESS_REGISTER, 0x1206);
+  outw (Private, SEQ_ADDRESS_REGISTER, 0x0012);
+
+  for (Index = 0; Index < 15; Index++) {
+    outw (Private, SEQ_ADDRESS_REGISTER, ModeData->SeqSettings[Index]);
+  }
+
+  if (DeviceId != CIRRUS_LOGIC_5446_DEVICE_ID) {
+    outb (Private, SEQ_ADDRESS_REGISTER, 0x0f);
+    Byte = (UINT8) ((inb (Private, SEQ_DATA_REGISTER) & 0xc7) ^ 0x30);
+    outb (Private, SEQ_DATA_REGISTER, Byte);
+  }
+
+  outb (Private, MISC_OUTPUT_REGISTER, ModeData->MiscSetting);
+  outw (Private, GRAPH_ADDRESS_REGISTER, 0x0506);
+  outw (Private, SEQ_ADDRESS_REGISTER, 0x0300);
+  outw (Private, CRTC_ADDRESS_REGISTER, 0x2011);
+
+  for (Index = 0; Index < 28; Index++) {
+    outw (Private, CRTC_ADDRESS_REGISTER, (UINT16) ((ModeData->CrtcSettings[Index] << 8) | Index));
+  }
+
+  for (Index = 0; Index < 9; Index++) {
+    outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) ((GraphicsController[Index] << 8) | Index));
+  }
+
+  inb (Private, INPUT_STATUS_1_REGISTER);
+
+  for (Index = 0; Index < 21; Index++) {
+    outb (Private, ATT_ADDRESS_REGISTER, (UINT8) Index);
+    outb (Private, ATT_ADDRESS_REGISTER, AttributeController[Index]);
+  }
+
+  outb (Private, ATT_ADDRESS_REGISTER, 0x20);
+
+  outw (Private, GRAPH_ADDRESS_REGISTER, 0x0009);
+  outw (Private, GRAPH_ADDRESS_REGISTER, 0x000a);
+  outw (Private, GRAPH_ADDRESS_REGISTER, 0x000b);
+  outb (Private, DAC_PIXEL_MASK_REGISTER, 0xff);
+
+  SetDefaultPalette (Private);
+  ClearScreen (Private);
+}
+
+EFI_STATUS
+EFIAPI
+InitializeCirrusLogic5430 (
+  IN EFI_HANDLE           ImageHandle,
+  IN EFI_SYSTEM_TABLE     *SystemTable
+  )
+{
+  EFI_STATUS              Status;
+
+  Status = EfiLibInstallDriverBindingComponentName2 (
+             ImageHandle,
+             SystemTable,
+             &gCirrusLogic5430DriverBinding,
+             ImageHandle,
+             &gCirrusLogic5430ComponentName,
+             &gCirrusLogic5430ComponentName2
+             );
+  ASSERT_EFI_ERROR (Status);
+
+  //
+  // Install EFI Driver Supported EFI Version Protocol required for
+  // EFI drivers that are on PCI and other plug in cards.
+  //
+  gCirrusLogic5430DriverSupportedEfiVersion.FirmwareVersion = PcdGet32 (PcdDriverSupportedEfiVersion);
+  Status = gBS->InstallMultipleProtocolInterfaces (
+                  &ImageHandle,
+                  &gEfiDriverSupportedEfiVersionProtocolGuid,
+                  &gCirrusLogic5430DriverSupportedEfiVersion,
+                  NULL
+                  );
+  ASSERT_EFI_ERROR (Status);
+
+  return Status;
+}
diff --git a/Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430.h b/Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430.h
new file mode 100644
index 0000000000..355f0418b3
--- /dev/null
+++ b/Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430.h
@@ -0,0 +1,432 @@
+/** @file
+  Cirrus Logic 5430 Controller Driver
+
+  Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+//
+// Cirrus Logic 5430 Controller Driver
+//
+
+#ifndef _CIRRUS_LOGIC_5430_H_
+#define _CIRRUS_LOGIC_5430_H_
+
+
+#include <Uefi.h>
+#include <Protocol/UgaDraw.h>
+#include <Protocol/GraphicsOutput.h>
+#include <Protocol/PciIo.h>
+#include <Protocol/DriverSupportedEfiVersion.h>
+#include <Protocol/EdidOverride.h>
+#include <Protocol/EdidDiscovered.h>
+#include <Protocol/EdidActive.h>
+#include <Protocol/DevicePath.h>
+
+#include <Library/DebugLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/UefiLib.h>
+#include <Library/PcdLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/TimerLib.h>
+
+#include <IndustryStandard/Pci.h>
+//
+// Cirrus Logic 5430 PCI Configuration Header values
+//
+#define CIRRUS_LOGIC_VENDOR_ID                0x1013
+#define CIRRUS_LOGIC_5430_DEVICE_ID           0x00a8
+#define CIRRUS_LOGIC_5430_ALTERNATE_DEVICE_ID 0x00a0
+#define CIRRUS_LOGIC_5446_DEVICE_ID           0x00b8
+
+//
+// Cirrus Logic Graphical Mode Data
+//
+#define CIRRUS_LOGIC_5430_MODE_COUNT         3
+
+typedef struct {
+  UINT32  ModeNumber;
+  UINT32  HorizontalResolution;
+  UINT32  VerticalResolution;
+  UINT32  ColorDepth;
+  UINT32  RefreshRate;
+} CIRRUS_LOGIC_5430_MODE_DATA;
+
+#define PIXEL_RED_SHIFT   0
+#define PIXEL_GREEN_SHIFT 3
+#define PIXEL_BLUE_SHIFT  6
+
+#define PIXEL_RED_MASK    (BIT7 | BIT6 | BIT5)
+#define PIXEL_GREEN_MASK  (BIT4 | BIT3 | BIT2)
+#define PIXEL_BLUE_MASK   (BIT1 | BIT0)
+
+#define PIXEL_TO_COLOR_BYTE(pixel, mask, shift) ((UINT8) ((pixel & mask) << shift))
+#define PIXEL_TO_RED_BYTE(pixel) PIXEL_TO_COLOR_BYTE(pixel, PIXEL_RED_MASK, PIXEL_RED_SHIFT)
+#define PIXEL_TO_GREEN_BYTE(pixel) PIXEL_TO_COLOR_BYTE(pixel, PIXEL_GREEN_MASK, PIXEL_GREEN_SHIFT)
+#define PIXEL_TO_BLUE_BYTE(pixel) PIXEL_TO_COLOR_BYTE(pixel, PIXEL_BLUE_MASK, PIXEL_BLUE_SHIFT)
+
+#define RGB_BYTES_TO_PIXEL(Red, Green, Blue) \
+  (UINT8) ( (((Red) >> PIXEL_RED_SHIFT) & PIXEL_RED_MASK) | \
+            (((Green) >> PIXEL_GREEN_SHIFT) & PIXEL_GREEN_MASK) | \
+            (((Blue) >> PIXEL_BLUE_SHIFT) & PIXEL_BLUE_MASK) )
+
+#define GRAPHICS_OUTPUT_INVALIDE_MODE_NUMBER  0xffff
+
+//
+// Cirrus Logic 5440 Private Data Structure
+//
+#define CIRRUS_LOGIC_5430_PRIVATE_DATA_SIGNATURE  SIGNATURE_32 ('C', 'L', '5', '4')
+
+typedef struct {
+  UINT64                                Signature;
+  EFI_HANDLE                            Handle;
+  EFI_PCI_IO_PROTOCOL                   *PciIo;
+  UINT64                                OriginalPciAttributes;
+  EFI_UGA_DRAW_PROTOCOL                 UgaDraw;
+  EFI_GRAPHICS_OUTPUT_PROTOCOL          GraphicsOutput;
+  EFI_EDID_DISCOVERED_PROTOCOL          EdidDiscovered;
+  EFI_EDID_ACTIVE_PROTOCOL              EdidActive;
+  EFI_DEVICE_PATH_PROTOCOL              *GopDevicePath;
+  EFI_DEVICE_PATH_PROTOCOL              *UgaDevicePath;
+  UINTN                                 CurrentMode;
+  UINTN                                 MaxMode;
+  CIRRUS_LOGIC_5430_MODE_DATA           ModeData[CIRRUS_LOGIC_5430_MODE_COUNT];
+  UINT8                                 *LineBuffer;
+  BOOLEAN                               HardwareNeedsStarting;
+} CIRRUS_LOGIC_5430_PRIVATE_DATA;
+
+///
+/// Video Mode structure
+///
+typedef struct {
+  UINT32  Width;
+  UINT32  Height;
+  UINT32  ColorDepth;
+  UINT32  RefreshRate;
+  UINT8   *CrtcSettings;
+  UINT16  *SeqSettings;
+  UINT8   MiscSetting;
+} CIRRUS_LOGIC_5430_VIDEO_MODES;
+
+#define CIRRUS_LOGIC_5430_PRIVATE_DATA_FROM_UGA_DRAW_THIS(a) \
+  CR(a, CIRRUS_LOGIC_5430_PRIVATE_DATA, UgaDraw, CIRRUS_LOGIC_5430_PRIVATE_DATA_SIGNATURE)
+
+#define CIRRUS_LOGIC_5430_PRIVATE_DATA_FROM_GRAPHICS_OUTPUT_THIS(a) \
+  CR(a, CIRRUS_LOGIC_5430_PRIVATE_DATA, GraphicsOutput, CIRRUS_LOGIC_5430_PRIVATE_DATA_SIGNATURE)
+
+
+//
+// Global Variables
+//
+extern UINT8                                      AttributeController[];
+extern UINT8                                      GraphicsController[];
+extern UINT8                                      Crtc_640_480_256_60[];
+extern UINT16                                     Seq_640_480_256_60[];
+extern UINT8                                      Crtc_800_600_256_60[];
+extern UINT16                                     Seq_800_600_256_60[];
+extern UINT8                                      Crtc_1024_768_256_60[];
+extern UINT16                                     Seq_1024_768_256_60[];
+extern CIRRUS_LOGIC_5430_VIDEO_MODES              CirrusLogic5430VideoModes[];
+extern EFI_DRIVER_BINDING_PROTOCOL                gCirrusLogic5430DriverBinding;
+extern EFI_COMPONENT_NAME_PROTOCOL                gCirrusLogic5430ComponentName;
+extern EFI_COMPONENT_NAME2_PROTOCOL               gCirrusLogic5430ComponentName2;
+extern EFI_DRIVER_SUPPORTED_EFI_VERSION_PROTOCOL  gCirrusLogic5430DriverSupportedEfiVersion;
+
+//
+// Io Registers defined by VGA
+//
+#define CRTC_ADDRESS_REGISTER   0x3d4
+#define CRTC_DATA_REGISTER      0x3d5
+#define SEQ_ADDRESS_REGISTER    0x3c4
+#define SEQ_DATA_REGISTER       0x3c5
+#define GRAPH_ADDRESS_REGISTER  0x3ce
+#define GRAPH_DATA_REGISTER     0x3cf
+#define ATT_ADDRESS_REGISTER    0x3c0
+#define MISC_OUTPUT_REGISTER    0x3c2
+#define INPUT_STATUS_1_REGISTER 0x3da
+#define DAC_PIXEL_MASK_REGISTER 0x3c6
+#define PALETTE_INDEX_REGISTER  0x3c8
+#define PALETTE_DATA_REGISTER   0x3c9
+
+//
+// UGA Draw Hardware abstraction internal worker functions
+//
+EFI_STATUS
+CirrusLogic5430UgaDrawConstructor (
+  CIRRUS_LOGIC_5430_PRIVATE_DATA  *Private
+  );
+
+EFI_STATUS
+CirrusLogic5430UgaDrawDestructor (
+  CIRRUS_LOGIC_5430_PRIVATE_DATA  *Private
+  );
+
+//
+// Graphics Output Hardware abstraction internal worker functions
+//
+EFI_STATUS
+CirrusLogic5430GraphicsOutputConstructor (
+  CIRRUS_LOGIC_5430_PRIVATE_DATA  *Private
+  );
+
+EFI_STATUS
+CirrusLogic5430GraphicsOutputDestructor (
+  CIRRUS_LOGIC_5430_PRIVATE_DATA  *Private
+  );
+
+
+//
+// EFI_DRIVER_BINDING_PROTOCOL Protocol Interface
+//
+/**
+  TODO: Add function description
+
+  @param  This TODO: add argument description
+  @param  Controller TODO: add argument description
+  @param  RemainingDevicePath TODO: add argument description
+
+  TODO: add return values
+
+**/
+EFI_STATUS
+EFIAPI
+CirrusLogic5430ControllerDriverSupported (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   Controller,
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
+  );
+
+/**
+  TODO: Add function description
+
+  @param  This TODO: add argument description
+  @param  Controller TODO: add argument description
+  @param  RemainingDevicePath TODO: add argument description
+
+  TODO: add return values
+
+**/
+EFI_STATUS
+EFIAPI
+CirrusLogic5430ControllerDriverStart (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   Controller,
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
+  );
+
+/**
+  TODO: Add function description
+
+  @param  This TODO: add argument description
+  @param  Controller TODO: add argument description
+  @param  NumberOfChildren TODO: add argument description
+  @param  ChildHandleBuffer TODO: add argument description
+
+  TODO: add return values
+
+**/
+EFI_STATUS
+EFIAPI
+CirrusLogic5430ControllerDriverStop (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   Controller,
+  IN UINTN                        NumberOfChildren,
+  IN EFI_HANDLE                   *ChildHandleBuffer
+  );
+
+//
+// EFI Component Name Functions
+//
+/**
+  Retrieves a Unicode string that is the user readable name of the driver.
+
+  This function retrieves the user readable name of a driver in the form of a
+  Unicode string. If the driver specified by This has a user readable name in
+  the language specified by Language, then a pointer to the driver name is
+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+  by This does not support the language specified by Language,
+  then EFI_UNSUPPORTED is returned.
+
+  @param  This[in]              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+                                EFI_COMPONENT_NAME_PROTOCOL instance.
+
+  @param  Language[in]          A pointer to a Null-terminated ASCII string
+                                array indicating the language. This is the
+                                language of the driver name that the caller is
+                                requesting, and it must match one of the
+                                languages specified in SupportedLanguages. The
+                                number of languages supported by a driver is up
+                                to the driver writer. Language is specified
+                                in RFC 4646 or ISO 639-2 language code format.
+
+  @param  DriverName[out]       A pointer to the Unicode string to return.
+                                This Unicode string is the name of the
+                                driver specified by This in the language
+                                specified by Language.
+
+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by
+                                This and the language specified by Language was
+                                returned in DriverName.
+
+  @retval EFI_INVALID_PARAMETER Language is NULL.
+
+  @retval EFI_INVALID_PARAMETER DriverName is NULL.
+
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support
+                                the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+CirrusLogic5430ComponentNameGetDriverName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,
+  IN  CHAR8                        *Language,
+  OUT CHAR16                       **DriverName
+  );
+
+
+/**
+  Retrieves a Unicode string that is the user readable name of the controller
+  that is being managed by a driver.
+
+  This function retrieves the user readable name of the controller specified by
+  ControllerHandle and ChildHandle in the form of a Unicode string. If the
+  driver specified by This has a user readable name in the language specified by
+  Language, then a pointer to the controller name is returned in ControllerName,
+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently
+  managing the controller specified by ControllerHandle and ChildHandle,
+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not
+  support the language specified by Language, then EFI_UNSUPPORTED is returned.
+
+  @param  This[in]              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+                                EFI_COMPONENT_NAME_PROTOCOL instance.
+
+  @param  ControllerHandle[in]  The handle of a controller that the driver
+                                specified by This is managing.  This handle
+                                specifies the controller whose name is to be
+                                returned.
+
+  @param  ChildHandle[in]       The handle of the child controller to retrieve
+                                the name of.  This is an optional parameter that
+                                may be NULL.  It will be NULL for device
+                                drivers.  It will also be NULL for a bus drivers
+                                that wish to retrieve the name of the bus
+                                controller.  It will not be NULL for a bus
+                                driver that wishes to retrieve the name of a
+                                child controller.
+
+  @param  Language[in]          A pointer to a Null-terminated ASCII string
+                                array indicating the language.  This is the
+                                language of the driver name that the caller is
+                                requesting, and it must match one of the
+                                languages specified in SupportedLanguages. The
+                                number of languages supported by a driver is up
+                                to the driver writer. Language is specified in
+                                RFC 4646 or ISO 639-2 language code format.
+
+  @param  ControllerName[out]   A pointer to the Unicode string to return.
+                                This Unicode string is the name of the
+                                controller specified by ControllerHandle and
+                                ChildHandle in the language specified by
+                                Language from the point of view of the driver
+                                specified by This.
+
+  @retval EFI_SUCCESS           The Unicode string for the user readable name in
+                                the language specified by Language for the
+                                driver specified by This was returned in
+                                DriverName.
+
+  @retval EFI_INVALID_PARAMETER ControllerHandle is NULL.
+
+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid
+                                EFI_HANDLE.
+
+  @retval EFI_INVALID_PARAMETER Language is NULL.
+
+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+
+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently
+                                managing the controller specified by
+                                ControllerHandle and ChildHandle.
+
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support
+                                the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+CirrusLogic5430ComponentNameGetControllerName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL                     *This,
+  IN  EFI_HANDLE                                      ControllerHandle,
+  IN  EFI_HANDLE                                      ChildHandle        OPTIONAL,
+  IN  CHAR8                                           *Language,
+  OUT CHAR16                                          **ControllerName
+  );
+
+
+//
+// Local Function Prototypes
+//
+VOID
+InitializeGraphicsMode (
+  CIRRUS_LOGIC_5430_PRIVATE_DATA  *Private,
+  CIRRUS_LOGIC_5430_VIDEO_MODES   *ModeData
+  );
+
+VOID
+SetPaletteColor (
+  CIRRUS_LOGIC_5430_PRIVATE_DATA  *Private,
+  UINTN                           Index,
+  UINT8                           Red,
+  UINT8                           Green,
+  UINT8                           Blue
+  );
+
+VOID
+SetDefaultPalette (
+  CIRRUS_LOGIC_5430_PRIVATE_DATA  *Private
+  );
+
+VOID
+DrawLogo (
+  CIRRUS_LOGIC_5430_PRIVATE_DATA  *Private,
+  UINTN                           ScreenWidth,
+  UINTN                           ScreenHeight
+  );
+
+VOID
+outb (
+  CIRRUS_LOGIC_5430_PRIVATE_DATA  *Private,
+  UINTN                           Address,
+  UINT8                           Data
+  );
+
+VOID
+outw (
+  CIRRUS_LOGIC_5430_PRIVATE_DATA  *Private,
+  UINTN                           Address,
+  UINT16                          Data
+  );
+
+UINT8
+inb (
+  CIRRUS_LOGIC_5430_PRIVATE_DATA  *Private,
+  UINTN                           Address
+  );
+
+UINT16
+inw (
+  CIRRUS_LOGIC_5430_PRIVATE_DATA  *Private,
+  UINTN                           Address
+  );
+
+EFI_STATUS
+CirrusLogic5430VideoModeSetup (
+  CIRRUS_LOGIC_5430_PRIVATE_DATA  *Private
+  );
+
+#endif
diff --git a/Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430Dxe.inf b/Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430Dxe.inf
new file mode 100644
index 0000000000..3e8b7b087f
--- /dev/null
+++ b/Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430Dxe.inf
@@ -0,0 +1,84 @@
+## @file
+# Component description file for CirrusLogic5430 module
+#
+# Cirrus Logic 5430 Controller Driver.This driver is a sample implementation
+#  of the UGA Draw Protocol for the Cirrus Logic 5430 family of PCI video controllers.
+#  This driver is only usable in the EFI pre-boot environment. This sample is
+#  intended to show how the UGA Draw Protocol is able to function. The UGA I/O
+#  Protocol is not implemented in this sample. A fully compliant EFI UGA driver
+#  requires both the UGA Draw and the UGA I/O Protocol. Please refer to Microsoft's
+#  documentation on UGA for details on how to write a UGA driver that is able
+#  to function both in the EFI pre-boot environment and from the OS runtime.
+# Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = CirrusLogic5430Dxe
+  FILE_GUID                      = 555F76EA-785F-40d7-9174-153C43636C68
+  MODULE_TYPE                    = UEFI_DRIVER
+  VERSION_STRING                 = 1.0
+
+  ENTRY_POINT                    = InitializeCirrusLogic5430
+
+  PCI_VENDOR_ID  = 0x1013
+  PCI_DEVICE_ID  = 0x00A8
+  PCI_CLASS_CODE = 0x030000
+  PCI_REVISION   = 0x00
+  PCI_COMPRESS   = TRUE
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+#  VALID_ARCHITECTURES           = IA32 X64 EBC
+#
+#  DRIVER_BINDING                =  gCirrusLogic5430DriverBinding
+#  COMPONENT_NAME                =  gCirrusLogic5430ComponentName
+#
+
+[Sources]
+  ComponentName.c
+  DriverSupportedEfiVersion.c
+  CirrusLogic5430UgaDraw.c
+  CirrusLogic5430GraphicsOutput.c
+  CirrusLogic5430.c
+  CirrusLogic5430.h
+  Edid.c
+  CirrusLogic5430I2c.h
+  CirrusLogic5430I2c.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  OptionRomPkg/OptionRomPkg.dec
+
+[LibraryClasses]
+  UefiBootServicesTableLib
+  MemoryAllocationLib
+  UefiLib
+  UefiDriverEntryPoint
+  DebugLib
+  BaseMemoryLib
+  DevicePathLib
+  TimerLib
+
+[Protocols]
+  gEfiDriverSupportedEfiVersionProtocolGuid     # PROTOCOL ALWAYS_PRODUCED
+  gEfiUgaDrawProtocolGuid                       # PROTOCOL BY_START
+  gEfiGraphicsOutputProtocolGuid                # PROTOCOL BY_START
+  gEfiEdidDiscoveredProtocolGuid                # PROTOCOL BY_START
+  gEfiEdidActiveProtocolGuid                    # PROTOCOL BY_START
+  gEfiDevicePathProtocolGuid                    # PROTOCOL BY_START
+  gEfiPciIoProtocolGuid                         # PROTOCOL TO_START
+  gEfiEdidOverrideProtocolGuid                  # PROTOCOL TO_START
+
+
+[FeaturePcd]
+  gOptionRomPkgTokenSpaceGuid.PcdSupportGop
+  gOptionRomPkgTokenSpaceGuid.PcdSupportUga
+
+[Pcd]
+  gOptionRomPkgTokenSpaceGuid.PcdDriverSupportedEfiVersion
diff --git a/Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430GraphicsOutput.c b/Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430GraphicsOutput.c
new file mode 100644
index 0000000000..b74d84b131
--- /dev/null
+++ b/Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430GraphicsOutput.c
@@ -0,0 +1,556 @@
+/** @file
+Copyright (c) 2007 - 2012, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+Module Name:
+
+  UefiCirrusLogic5430GraphicsOutput.c
+
+Abstract:
+
+  This file produces the graphics abstration of Graphics Output Protocol. It is called by
+  CirrusLogic5430.c file which deals with the EFI 1.1 driver model.
+  This file just does graphics.
+
+**/
+#include "CirrusLogic5430.h"
+#include <IndustryStandard/Acpi.h>
+
+
+STATIC
+VOID
+CirrusLogic5430CompleteModeInfo (
+  OUT EFI_GRAPHICS_OUTPUT_MODE_INFORMATION  *Info
+  )
+{
+  Info->Version = 0;
+  Info->PixelFormat = PixelBitMask;
+  Info->PixelInformation.RedMask = PIXEL_RED_MASK;
+  Info->PixelInformation.GreenMask = PIXEL_GREEN_MASK;
+  Info->PixelInformation.BlueMask = PIXEL_BLUE_MASK;
+  Info->PixelInformation.ReservedMask = 0;
+  Info->PixelsPerScanLine = Info->HorizontalResolution;
+}
+
+
+STATIC
+EFI_STATUS
+CirrusLogic5430CompleteModeData (
+  IN  CIRRUS_LOGIC_5430_PRIVATE_DATA    *Private,
+  OUT EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE *Mode
+  )
+{
+  EFI_GRAPHICS_OUTPUT_MODE_INFORMATION  *Info;
+  EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR     *FrameBufDesc;
+
+  Info = Mode->Info;
+  CirrusLogic5430CompleteModeInfo (Info);
+
+  Private->PciIo->GetBarAttributes (
+                        Private->PciIo,
+                        0,
+                        NULL,
+                        (VOID**) &FrameBufDesc
+                        );
+
+  Mode->FrameBufferBase = FrameBufDesc->AddrRangeMin;
+  Mode->FrameBufferSize = Info->HorizontalResolution * Info->VerticalResolution;
+
+  return EFI_SUCCESS;
+}
+
+
+//
+// Graphics Output Protocol Member Functions
+//
+EFI_STATUS
+EFIAPI
+CirrusLogic5430GraphicsOutputQueryMode (
+  IN  EFI_GRAPHICS_OUTPUT_PROTOCOL          *This,
+  IN  UINT32                                ModeNumber,
+  OUT UINTN                                 *SizeOfInfo,
+  OUT EFI_GRAPHICS_OUTPUT_MODE_INFORMATION  **Info
+  )
+/*++
+
+Routine Description:
+
+  Graphics Output protocol interface to query video mode
+
+  Arguments:
+    This                  - Protocol instance pointer.
+    ModeNumber            - The mode number to return information on.
+    Info                  - Caller allocated buffer that returns information about ModeNumber.
+    SizeOfInfo            - A pointer to the size, in bytes, of the Info buffer.
+
+  Returns:
+    EFI_SUCCESS           - Mode information returned.
+    EFI_BUFFER_TOO_SMALL  - The Info buffer was too small.
+    EFI_DEVICE_ERROR      - A hardware error occurred trying to retrieve the video mode.
+    EFI_NOT_STARTED       - Video display is not initialized. Call SetMode ()
+    EFI_INVALID_PARAMETER - One of the input args was NULL.
+
+--*/
+{
+  CIRRUS_LOGIC_5430_PRIVATE_DATA  *Private;
+
+  Private = CIRRUS_LOGIC_5430_PRIVATE_DATA_FROM_GRAPHICS_OUTPUT_THIS (This);
+
+  if (Private->HardwareNeedsStarting) {
+    return EFI_NOT_STARTED;
+  }
+
+  if (Info == NULL || SizeOfInfo == NULL || ModeNumber >= This->Mode->MaxMode) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  *Info = AllocatePool (sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION));
+  if (*Info == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  *SizeOfInfo = sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION);
+
+  (*Info)->HorizontalResolution = Private->ModeData[ModeNumber].HorizontalResolution;
+  (*Info)->VerticalResolution   = Private->ModeData[ModeNumber].VerticalResolution;
+  CirrusLogic5430CompleteModeInfo (*Info);
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+CirrusLogic5430GraphicsOutputSetMode (
+  IN  EFI_GRAPHICS_OUTPUT_PROTOCOL *This,
+  IN  UINT32                       ModeNumber
+  )
+/*++
+
+Routine Description:
+
+  Graphics Output protocol interface to set video mode
+
+  Arguments:
+    This             - Protocol instance pointer.
+    ModeNumber       - The mode number to be set.
+
+  Returns:
+    EFI_SUCCESS      - Graphics mode was changed.
+    EFI_DEVICE_ERROR - The device had an error and could not complete the request.
+    EFI_UNSUPPORTED  - ModeNumber is not supported by this device.
+
+--*/
+{
+  CIRRUS_LOGIC_5430_PRIVATE_DATA    *Private;
+  CIRRUS_LOGIC_5430_MODE_DATA       *ModeData;
+
+  Private = CIRRUS_LOGIC_5430_PRIVATE_DATA_FROM_GRAPHICS_OUTPUT_THIS (This);
+
+  if (ModeNumber >= This->Mode->MaxMode) {
+    return EFI_UNSUPPORTED;
+  }
+
+  ModeData = &Private->ModeData[ModeNumber];
+
+  if (Private->LineBuffer) {
+    gBS->FreePool (Private->LineBuffer);
+  }
+
+  Private->LineBuffer = NULL;
+  Private->LineBuffer = AllocatePool (ModeData->HorizontalResolution);
+  if (Private->LineBuffer == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  InitializeGraphicsMode (Private, &CirrusLogic5430VideoModes[ModeData->ModeNumber]);
+
+  This->Mode->Mode = ModeNumber;
+  This->Mode->Info->HorizontalResolution = ModeData->HorizontalResolution;
+  This->Mode->Info->VerticalResolution = ModeData->VerticalResolution;
+  This->Mode->SizeOfInfo = sizeof(EFI_GRAPHICS_OUTPUT_MODE_INFORMATION);
+
+  CirrusLogic5430CompleteModeData (Private, This->Mode);
+
+  Private->HardwareNeedsStarting  = FALSE;
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+CirrusLogic5430GraphicsOutputBlt (
+  IN  EFI_GRAPHICS_OUTPUT_PROTOCOL          *This,
+  IN  EFI_GRAPHICS_OUTPUT_BLT_PIXEL         *BltBuffer, OPTIONAL
+  IN  EFI_GRAPHICS_OUTPUT_BLT_OPERATION     BltOperation,
+  IN  UINTN                                 SourceX,
+  IN  UINTN                                 SourceY,
+  IN  UINTN                                 DestinationX,
+  IN  UINTN                                 DestinationY,
+  IN  UINTN                                 Width,
+  IN  UINTN                                 Height,
+  IN  UINTN                                 Delta
+  )
+/*++
+
+Routine Description:
+
+  Graphics Output protocol instance to block transfer for CirrusLogic device
+
+Arguments:
+
+  This          - Pointer to Graphics Output protocol instance
+  BltBuffer     - The data to transfer to screen
+  BltOperation  - The operation to perform
+  SourceX       - The X coordinate of the source for BltOperation
+  SourceY       - The Y coordinate of the source for BltOperation
+  DestinationX  - The X coordinate of the destination for BltOperation
+  DestinationY  - The Y coordinate of the destination for BltOperation
+  Width         - The width of a rectangle in the blt rectangle in pixels
+  Height        - The height of a rectangle in the blt rectangle in pixels
+  Delta         - Not used for EfiBltVideoFill and EfiBltVideoToVideo operation.
+                  If a Delta of 0 is used, the entire BltBuffer will be operated on.
+                  If a subrectangle of the BltBuffer is used, then Delta represents
+                  the number of bytes in a row of the BltBuffer.
+
+Returns:
+
+  EFI_INVALID_PARAMETER - Invalid parameter passed in
+  EFI_SUCCESS - Blt operation success
+
+--*/
+{
+  CIRRUS_LOGIC_5430_PRIVATE_DATA  *Private;
+  EFI_TPL                         OriginalTPL;
+  UINTN                           DstY;
+  UINTN                           SrcY;
+  EFI_GRAPHICS_OUTPUT_BLT_PIXEL   *Blt;
+  UINTN                           X;
+  UINT8                           Pixel;
+  UINT32                          WidePixel;
+  UINTN                           ScreenWidth;
+  UINTN                           Offset;
+  UINTN                           SourceOffset;
+  UINT32                          CurrentMode;
+
+  Private = CIRRUS_LOGIC_5430_PRIVATE_DATA_FROM_GRAPHICS_OUTPUT_THIS (This);
+
+  if ((UINT32)BltOperation >= EfiGraphicsOutputBltOperationMax) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (Width == 0 || Height == 0) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // If Delta is zero, then the entire BltBuffer is being used, so Delta
+  // is the number of bytes in each row of BltBuffer.  Since BltBuffer is Width pixels size,
+  // the number of bytes in each row can be computed.
+  //
+  if (Delta == 0) {
+    Delta = Width * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL);
+  }
+
+  //
+  // We need to fill the Virtual Screen buffer with the blt data.
+  // The virtual screen is upside down, as the first row is the bootom row of
+  // the image.
+  //
+
+  CurrentMode = This->Mode->Mode;
+  //
+  // Make sure the SourceX, SourceY, DestinationX, DestinationY, Width, and Height parameters
+  // are valid for the operation and the current screen geometry.
+  //
+  if (BltOperation == EfiBltVideoToBltBuffer) {
+    //
+    // Video to BltBuffer: Source is Video, destination is BltBuffer
+    //
+    if (SourceY + Height > Private->ModeData[CurrentMode].VerticalResolution) {
+      return EFI_INVALID_PARAMETER;
+    }
+
+    if (SourceX + Width > Private->ModeData[CurrentMode].HorizontalResolution) {
+      return EFI_INVALID_PARAMETER;
+    }
+  } else {
+    //
+    // BltBuffer to Video: Source is BltBuffer, destination is Video
+    //
+    if (DestinationY + Height > Private->ModeData[CurrentMode].VerticalResolution) {
+      return EFI_INVALID_PARAMETER;
+    }
+
+    if (DestinationX + Width > Private->ModeData[CurrentMode].HorizontalResolution) {
+      return EFI_INVALID_PARAMETER;
+    }
+  }
+  //
+  // We have to raise to TPL Notify, so we make an atomic write the frame buffer.
+  // We would not want a timer based event (Cursor, ...) to come in while we are
+  // doing this operation.
+  //
+  OriginalTPL = gBS->RaiseTPL (TPL_NOTIFY);
+
+  switch (BltOperation) {
+  case EfiBltVideoToBltBuffer:
+    //
+    // Video to BltBuffer: Source is Video, destination is BltBuffer
+    //
+    for (SrcY = SourceY, DstY = DestinationY; DstY < (Height + DestinationY); SrcY++, DstY++) {
+
+      Offset = (SrcY * Private->ModeData[CurrentMode].HorizontalResolution) + SourceX;
+      if (((Offset & 0x03) == 0) && ((Width & 0x03) == 0)) {
+        Private->PciIo->Mem.Read (
+                              Private->PciIo,
+                              EfiPciIoWidthUint32,
+                              0,
+                              Offset,
+                              Width >> 2,
+                              Private->LineBuffer
+                              );
+      } else {
+        Private->PciIo->Mem.Read (
+                              Private->PciIo,
+                              EfiPciIoWidthUint8,
+                              0,
+                              Offset,
+                              Width,
+                              Private->LineBuffer
+                              );
+      }
+
+      for (X = 0; X < Width; X++) {
+        Blt         = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *) ((UINT8 *) BltBuffer + (DstY * Delta) + (DestinationX + X) * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL));
+
+        Blt->Red    = PIXEL_TO_RED_BYTE (Private->LineBuffer[X]);
+        Blt->Green  = PIXEL_TO_GREEN_BYTE (Private->LineBuffer[X]);
+        Blt->Blue   = PIXEL_TO_BLUE_BYTE (Private->LineBuffer[X]);
+      }
+    }
+    break;
+
+  case EfiBltVideoToVideo:
+    //
+    // Perform hardware acceleration for Video to Video operations
+    //
+    ScreenWidth   = Private->ModeData[CurrentMode].HorizontalResolution;
+    SourceOffset  = (SourceY * Private->ModeData[CurrentMode].HorizontalResolution) + (SourceX);
+    Offset        = (DestinationY * Private->ModeData[CurrentMode].HorizontalResolution) + (DestinationX);
+
+    outw (Private, GRAPH_ADDRESS_REGISTER, 0x0000);
+    outw (Private, GRAPH_ADDRESS_REGISTER, 0x0010);
+    outw (Private, GRAPH_ADDRESS_REGISTER, 0x0012);
+    outw (Private, GRAPH_ADDRESS_REGISTER, 0x0014);
+
+    outw (Private, GRAPH_ADDRESS_REGISTER, 0x0001);
+    outw (Private, GRAPH_ADDRESS_REGISTER, 0x0011);
+    outw (Private, GRAPH_ADDRESS_REGISTER, 0x0013);
+    outw (Private, GRAPH_ADDRESS_REGISTER, 0x0015);
+
+    outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) (((Width << 8) & 0xff00) | 0x20));
+    outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) ((Width & 0xff00) | 0x21));
+    outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) (((Height << 8) & 0xff00) | 0x22));
+    outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) ((Height & 0xff00) | 0x23));
+    outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) (((ScreenWidth << 8) & 0xff00) | 0x24));
+    outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) ((ScreenWidth & 0xff00) | 0x25));
+    outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) (((ScreenWidth << 8) & 0xff00) | 0x26));
+    outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) ((ScreenWidth & 0xff00) | 0x27));
+    outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) ((((Offset) << 8) & 0xff00) | 0x28));
+    outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) ((((Offset) >> 0) & 0xff00) | 0x29));
+    outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) ((((Offset) >> 8) & 0xff00) | 0x2a));
+    outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) ((((SourceOffset) << 8) & 0xff00) | 0x2c));
+    outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) ((((SourceOffset) >> 0) & 0xff00) | 0x2d));
+    outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) ((((SourceOffset) >> 8) & 0xff00) | 0x2e));
+    outw (Private, GRAPH_ADDRESS_REGISTER, 0x002f);
+    outw (Private, GRAPH_ADDRESS_REGISTER, 0x0030);
+    outw (Private, GRAPH_ADDRESS_REGISTER, 0x0d32);
+    outw (Private, GRAPH_ADDRESS_REGISTER, 0x0033);
+    outw (Private, GRAPH_ADDRESS_REGISTER, 0x0034);
+    outw (Private, GRAPH_ADDRESS_REGISTER, 0x0035);
+
+    outw (Private, GRAPH_ADDRESS_REGISTER, 0x0231);
+
+    outb (Private, GRAPH_ADDRESS_REGISTER, 0x31);
+    while ((inb (Private, GRAPH_DATA_REGISTER) & 0x01) == 0x01)
+      ;
+    break;
+
+  case EfiBltVideoFill:
+    Blt       = BltBuffer;
+    Pixel     = RGB_BYTES_TO_PIXEL (Blt->Red, Blt->Green, Blt->Blue);
+    WidePixel = (Pixel << 8) | Pixel;
+    WidePixel = (WidePixel << 16) | WidePixel;
+
+    if (DestinationX == 0 && Width == Private->ModeData[CurrentMode].HorizontalResolution) {
+      Offset = DestinationY * Private->ModeData[CurrentMode].HorizontalResolution;
+      if (((Offset & 0x03) == 0) && (((Width * Height) & 0x03) == 0)) {
+        Private->PciIo->Mem.Write (
+                              Private->PciIo,
+                              EfiPciIoWidthFillUint32,
+                              0,
+                              Offset,
+                              (Width * Height) >> 2,
+                              &WidePixel
+                              );
+      } else {
+        Private->PciIo->Mem.Write (
+                              Private->PciIo,
+                              EfiPciIoWidthFillUint8,
+                              0,
+                              Offset,
+                              Width * Height,
+                              &Pixel
+                              );
+      }
+    } else {
+      for (SrcY = SourceY, DstY = DestinationY; SrcY < (Height + SourceY); SrcY++, DstY++) {
+        Offset = (DstY * Private->ModeData[CurrentMode].HorizontalResolution) + DestinationX;
+        if (((Offset & 0x03) == 0) && ((Width & 0x03) == 0)) {
+          Private->PciIo->Mem.Write (
+                                Private->PciIo,
+                                EfiPciIoWidthFillUint32,
+                                0,
+                                Offset,
+                                Width >> 2,
+                                &WidePixel
+                                );
+        } else {
+          Private->PciIo->Mem.Write (
+                                Private->PciIo,
+                                EfiPciIoWidthFillUint8,
+                                0,
+                                Offset,
+                                Width,
+                                &Pixel
+                                );
+        }
+      }
+    }
+    break;
+
+  case EfiBltBufferToVideo:
+    for (SrcY = SourceY, DstY = DestinationY; SrcY < (Height + SourceY); SrcY++, DstY++) {
+
+      for (X = 0; X < Width; X++) {
+        Blt =
+          (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *) (
+              (UINT8 *) BltBuffer +
+              (SrcY * Delta) +
+              ((SourceX + X) * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL))
+            );
+        Private->LineBuffer[X]  =
+          RGB_BYTES_TO_PIXEL (Blt->Red, Blt->Green, Blt->Blue);
+      }
+
+      Offset = (DstY * Private->ModeData[CurrentMode].HorizontalResolution) + DestinationX;
+
+      if (((Offset & 0x03) == 0) && ((Width & 0x03) == 0)) {
+        Private->PciIo->Mem.Write (
+                              Private->PciIo,
+                              EfiPciIoWidthUint32,
+                              0,
+                              Offset,
+                              Width >> 2,
+                              Private->LineBuffer
+                              );
+      } else {
+        Private->PciIo->Mem.Write (
+                              Private->PciIo,
+                              EfiPciIoWidthUint8,
+                              0,
+                              Offset,
+                              Width,
+                              Private->LineBuffer
+                              );
+      }
+    }
+    break;
+  default:
+    ASSERT (FALSE);
+  }
+
+  gBS->RestoreTPL (OriginalTPL);
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+CirrusLogic5430GraphicsOutputConstructor (
+  CIRRUS_LOGIC_5430_PRIVATE_DATA  *Private
+  )
+{
+  EFI_STATUS                   Status;
+  EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput;
+
+
+  GraphicsOutput            = &Private->GraphicsOutput;
+  GraphicsOutput->QueryMode = CirrusLogic5430GraphicsOutputQueryMode;
+  GraphicsOutput->SetMode   = CirrusLogic5430GraphicsOutputSetMode;
+  GraphicsOutput->Blt       = CirrusLogic5430GraphicsOutputBlt;
+
+  //
+  // Initialize the private data
+  //
+  Status = gBS->AllocatePool (
+                  EfiBootServicesData,
+                  sizeof (EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE),
+                  (VOID **) &Private->GraphicsOutput.Mode
+                  );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+  Status = gBS->AllocatePool (
+                  EfiBootServicesData,
+                  sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION),
+                  (VOID **) &Private->GraphicsOutput.Mode->Info
+                  );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+  Private->GraphicsOutput.Mode->MaxMode = (UINT32) Private->MaxMode;
+  Private->GraphicsOutput.Mode->Mode    = GRAPHICS_OUTPUT_INVALIDE_MODE_NUMBER;
+  Private->HardwareNeedsStarting        = TRUE;
+  Private->LineBuffer                   = NULL;
+
+  //
+  // Initialize the hardware
+  //
+  GraphicsOutput->SetMode (GraphicsOutput, 0);
+  ASSERT (Private->GraphicsOutput.Mode->Mode < CIRRUS_LOGIC_5430_MODE_COUNT);
+  DrawLogo (
+    Private,
+    Private->ModeData[Private->GraphicsOutput.Mode->Mode].HorizontalResolution,
+    Private->ModeData[Private->GraphicsOutput.Mode->Mode].VerticalResolution
+    );
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+CirrusLogic5430GraphicsOutputDestructor (
+  CIRRUS_LOGIC_5430_PRIVATE_DATA  *Private
+  )
+/*++
+
+Routine Description:
+
+Arguments:
+
+Returns:
+
+  None
+
+--*/
+{
+  if (Private->GraphicsOutput.Mode != NULL) {
+    if (Private->GraphicsOutput.Mode->Info != NULL) {
+      gBS->FreePool (Private->GraphicsOutput.Mode->Info);
+    }
+    gBS->FreePool (Private->GraphicsOutput.Mode);
+  }
+
+  return EFI_SUCCESS;
+}
+
+
diff --git a/Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430I2c.c b/Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430I2c.c
new file mode 100644
index 0000000000..0cec8670b7
--- /dev/null
+++ b/Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430I2c.c
@@ -0,0 +1,427 @@
+/** @file
+  I2C Bus implementation upon CirrusLogic.
+
+  Copyright (c) 2008 - 2009, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "CirrusLogic5430.h"
+#include "CirrusLogic5430I2c.h"
+
+#define SEQ_ADDRESS_REGISTER    0x3c4
+#define SEQ_DATA_REGISTER       0x3c5
+
+#define I2C_CONTROL             0x08
+#define I2CDAT_IN               7
+#define I2CCLK_IN               2
+#define I2CDAT_OUT              1
+#define I2CCLK_OUT              0
+
+#define I2C_BUS_SPEED           100  //100kbps
+
+/**
+  PCI I/O byte write function.
+
+  @param  PciIo        The pointer to PCI_IO_PROTOCOL.
+  @param  Address      The bit map of I2C Data or I2C Clock pins.
+  @param  Data         The date to write.
+
+**/
+VOID
+I2cOutb (
+  EFI_PCI_IO_PROTOCOL    *PciIo,
+  UINTN                  Address,
+  UINT8                  Data
+  )
+{
+  PciIo->Io.Write (
+             PciIo,
+             EfiPciIoWidthUint8,
+             EFI_PCI_IO_PASS_THROUGH_BAR,
+             Address,
+             1,
+             &Data
+             );
+}
+/**
+  PCI I/O byte read function.
+
+  @param  PciIo        The pointer to PCI_IO_PROTOCOL.
+  @param  Address      The bit map of I2C Data or I2C Clock pins.
+
+  return byte value read from PCI I/O space.
+
+**/
+UINT8
+I2cInb (
+  EFI_PCI_IO_PROTOCOL    *PciIo,
+  UINTN                  Address
+  )
+{
+  UINT8 Data;
+
+  PciIo->Io.Read (
+             PciIo,
+             EfiPciIoWidthUint8,
+             EFI_PCI_IO_PASS_THROUGH_BAR,
+             Address,
+             1,
+             &Data
+             );
+  return Data;
+}
+
+/**
+  Read status of I2C Data and I2C Clock Pins.
+
+  @param  PciIo        The pointer to PCI_IO_PROTOCOL.
+  @param  Blt          The bit map of I2C Data or I2C Clock pins.
+
+  @retval 0            Low on I2C Data or I2C Clock Pin.
+  @retval 1            High on I2C Data or I2C Clock Pin.
+
+**/
+UINT8
+I2cPinRead (
+  EFI_PCI_IO_PROTOCOL    *PciIo,
+  UINT8                  Bit
+  )
+{
+  I2cOutb (PciIo, SEQ_ADDRESS_REGISTER, I2C_CONTROL);
+  return (UINT8) ((I2cInb (PciIo, SEQ_DATA_REGISTER) >> Bit ) & 0xfe);
+}
+
+
+/**
+  Set/Clear I2C Data and I2C Clock Pins.
+
+  @param  PciIo              The pointer to PCI_IO_PROTOCOL.
+  @param  Blt                The bit map to controller I2C Data or I2C Clock pins.
+  @param  Value              1 or 0 stands for Set or Clear I2C Data and I2C Clock Pins.
+
+**/
+VOID
+I2cPinWrite (
+  EFI_PCI_IO_PROTOCOL    *PciIo,
+  UINT8                  Bit,
+  UINT8                  Value
+  )
+{
+  UINT8        Byte;
+  I2cOutb (PciIo, SEQ_ADDRESS_REGISTER, I2C_CONTROL);
+  Byte = (UINT8) (I2cInb (PciIo, SEQ_DATA_REGISTER) & (UINT8) ~(1 << Bit)) ;
+  Byte = (UINT8) (Byte | ((Value & 0x01) << Bit));
+  I2cOutb (PciIo, SEQ_DATA_REGISTER, (UINT8) (Byte | 0x40));
+  return;
+}
+
+/**
+  Read/write delay acoording to I2C Bus Speed.
+
+**/
+VOID
+I2cDelay (
+  VOID
+  )
+{
+  MicroSecondDelay (1000 / I2C_BUS_SPEED);
+}
+
+/**
+  Write a 8-bit data onto I2C Data Pin.
+
+  @param  PciIo              The pointer to PCI_IO_PROTOCOL.
+  @param  Data               The byte data to write.
+
+**/
+VOID
+I2cSendByte (
+  EFI_PCI_IO_PROTOCOL    *PciIo,
+  UINT8                  Data
+  )
+{
+  UINTN                  Index;
+  //
+  // Send byte data onto I2C Bus
+  //
+  for (Index = 0; Index < 8; Index --) {
+    I2cPinWrite (PciIo, I2CDAT_OUT, (UINT8) (Data >> (7 - Index)));
+    I2cPinWrite (PciIo, I2CCLK_OUT, 1);
+    I2cDelay ();
+    I2cPinWrite (PciIo, I2CCLK_OUT, 0);
+  }
+}
+
+/**
+  Read a 8-bit data from I2C Data Pin.
+
+  @param  PciIo              The pointer to PCI_IO_PROTOCOL.
+
+  Return the byte data read from I2C Data Pin.
+**/
+UINT8
+I2cReceiveByte (
+  EFI_PCI_IO_PROTOCOL    *PciIo
+  )
+{
+  UINT8          Data;
+  UINTN          Index;
+
+  Data = 0;
+  //
+  // Read byte data from I2C Bus
+  //
+  for (Index = 0; Index < 8; Index --) {
+    I2cPinWrite (PciIo, I2CCLK_OUT, 1);
+    I2cDelay ();
+    Data = (UINT8) (Data << 1);
+    Data = (UINT8) (Data | I2cPinRead (PciIo, I2CDAT_IN));
+    I2cPinWrite (PciIo, I2CCLK_OUT, 0);
+  }
+
+  return Data;
+}
+
+/**
+  Receive an ACK signal from I2C Bus.
+
+  @param  PciIo              The pointer to PCI_IO_PROTOCOL.
+
+**/
+BOOLEAN
+I2cWaitAck (
+  EFI_PCI_IO_PROTOCOL    *PciIo
+  )
+{
+  //
+  // Wait for ACK signal
+  //
+  I2cPinWrite (PciIo, I2CDAT_OUT, 1);
+  I2cPinWrite (PciIo, I2CCLK_OUT, 1);
+  I2cDelay ();
+  if (I2cPinRead (PciIo, I2CDAT_IN) == 0) {
+    I2cPinWrite (PciIo, I2CDAT_OUT, 1);
+    return TRUE;
+  } else {
+    return FALSE;
+  }
+}
+
+/**
+  Send an ACK signal onto I2C Bus.
+
+  @param  PciIo              The pointer to PCI_IO_PROTOCOL.
+
+**/
+VOID
+I2cSendAck (
+  EFI_PCI_IO_PROTOCOL    *PciIo
+  )
+{
+  I2cPinWrite (PciIo, I2CCLK_OUT, 1);
+  I2cPinWrite (PciIo, I2CDAT_OUT, 1);
+  I2cPinWrite (PciIo, I2CDAT_OUT, 0);
+  I2cPinWrite (PciIo, I2CCLK_OUT, 0);
+}
+
+/**
+  Start a I2C transfer on I2C Bus.
+
+  @param  PciIo              The pointer to PCI_IO_PROTOCOL.
+
+**/
+VOID
+I2cStart (
+  EFI_PCI_IO_PROTOCOL    *PciIo
+  )
+{
+  //
+  // Init CLK and DAT pins
+  //
+  I2cPinWrite (PciIo, I2CCLK_OUT, 1);
+  I2cPinWrite (PciIo, I2CDAT_OUT, 1);
+  //
+  // Start a I2C transfer, set SDA low from high, when SCL is high
+  //
+  I2cPinWrite (PciIo, I2CDAT_OUT, 0);
+  I2cPinWrite (PciIo, I2CCLK_OUT, 0);
+}
+
+/**
+  Stop a I2C transfer on I2C Bus.
+
+  @param  PciIo              The pointer to PCI_IO_PROTOCOL.
+
+**/
+VOID
+I2cStop (
+  EFI_PCI_IO_PROTOCOL    *PciIo
+  )
+{
+  //
+  // Stop a I2C transfer, set SDA high from low, when SCL is high
+  //
+  I2cPinWrite (PciIo, I2CDAT_OUT, 0);
+  I2cPinWrite (PciIo, I2CCLK_OUT, 1);
+  I2cPinWrite (PciIo, I2CDAT_OUT, 1);
+}
+
+/**
+  Read one byte data on I2C Bus.
+
+  Read one byte data from the slave device connectet to I2C Bus.
+  If Data is NULL, then ASSERT().
+
+  @param  PciIo              The pointer to PCI_IO_PROTOCOL.
+  @param  DeviceAddress      Slave device's address.
+  @param  RegisterAddress    The register address on slave device.
+  @param  Data               The pointer to returned data if EFI_SUCCESS returned.
+
+  @retval EFI_DEVICE_ERROR
+  @retval EFI_SUCCESS
+
+**/
+EFI_STATUS
+EFIAPI
+I2cReadByte (
+  EFI_PCI_IO_PROTOCOL    *PciIo,
+  UINT8                  DeviceAddress,
+  UINT8                  RegisterAddress,
+  UINT8                  *Data
+  )
+{
+  ASSERT (Data != NULL);
+
+  //
+  // Start I2C transfer
+  //
+  I2cStart (PciIo);
+
+  //
+  // Send slave address with enabling write flag
+  //
+  I2cSendByte (PciIo, (UINT8) (DeviceAddress & 0xfe));
+
+  //
+  // Wait for ACK signal
+  //
+  if (I2cWaitAck (PciIo) == FALSE) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  //
+  // Send register address
+  //
+  I2cSendByte (PciIo, RegisterAddress);
+
+  //
+  // Wait for ACK signal
+  //
+  if (I2cWaitAck (PciIo) == FALSE) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  //
+  // Send slave address with enabling read flag
+  //
+  I2cSendByte (PciIo, (UINT8) (DeviceAddress | 0x01));
+
+  //
+  // Wait for ACK signal
+  //
+  if (I2cWaitAck (PciIo) == FALSE) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  //
+  // Read byte data from I2C Bus
+  //
+  *Data = I2cReceiveByte (PciIo);
+
+  //
+  // Send ACK signal onto I2C Bus
+  //
+  I2cSendAck (PciIo);
+
+  //
+  // Stop a I2C transfer
+  //
+  I2cStop (PciIo);
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Write one byte data onto I2C Bus.
+
+  Write one byte data to the slave device connectet to I2C Bus.
+  If Data is NULL, then ASSERT().
+
+  @param  PciIo              The pointer to PCI_IO_PROTOCOL.
+  @param  DeviceAddress      Slave device's address.
+  @param  RegisterAddress    The register address on slave device.
+  @param  Data               The pointer to write data.
+
+  @retval EFI_DEVICE_ERROR
+  @retval EFI_SUCCESS
+
+**/
+EFI_STATUS
+EFIAPI
+I2cWriteByte (
+  EFI_PCI_IO_PROTOCOL    *PciIo,
+  UINT8                  DeviceAddress,
+  UINT8                  RegisterAddress,
+  UINT8                  *Data
+  )
+{
+  ASSERT (Data != NULL);
+
+  I2cStart (PciIo);
+  //
+  // Send slave address with enabling write flag
+  //
+  I2cSendByte (PciIo, (UINT8) (DeviceAddress & 0xfe));
+
+  //
+  // Wait for ACK signal
+  //
+  if (I2cWaitAck (PciIo) == FALSE) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  //
+  // Send register address
+  //
+  I2cSendByte (PciIo, RegisterAddress);
+
+  //
+  // Wait for ACK signal
+  //
+  if (I2cWaitAck (PciIo) == FALSE) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  //
+  // Send byte data onto I2C Bus
+  //
+  I2cSendByte (PciIo, *Data);
+
+  //
+  // Wait for ACK signal
+  //
+  if (I2cWaitAck (PciIo) == FALSE) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  //
+  // Stop a I2C transfer
+  //
+  I2cStop (PciIo);
+
+  return EFI_SUCCESS;
+}
+
+
+
diff --git a/Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430I2c.h b/Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430I2c.h
new file mode 100644
index 0000000000..505575cc99
--- /dev/null
+++ b/Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430I2c.h
@@ -0,0 +1,62 @@
+/** @file
+  I2c Bus byte read/write functions.
+
+  Copyright (c) 2008 - 2009, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef _CIRRUS_LOGIC_I2C_H_
+#define _CIRRUS_LOGIC_I2C_H_
+
+#include <Protocol/PciIo.h>
+
+/**
+  Read one byte data on I2C Bus.
+
+  Read one byte data from the slave device connectet to I2C Bus.
+  If Data is NULL, then ASSERT().
+
+  @param  PciIo              The pointer to PCI_IO_PROTOCOL.
+  @param  DeviceAddress      Slave device's address.
+  @param  RegisterAddress    The register address on slave device.
+  @param  Data               The pointer to returned data if EFI_SUCCESS returned.
+
+  @retval EFI_DEVICE_ERROR
+  @retval EFI_SUCCESS
+
+**/
+EFI_STATUS
+EFIAPI
+I2cReadByte (
+  EFI_PCI_IO_PROTOCOL    *PciIo,
+  UINT8                  DeviceAddress,
+  UINT8                  RegisterAddress,
+  UINT8                  *Data
+  );
+
+/**
+  Write one byte data onto I2C Bus.
+
+  Write one byte data to the slave device connectet to I2C Bus.
+  If Data is NULL, then ASSERT().
+
+  @param  PciIo              The pointer to PCI_IO_PROTOCOL.
+  @param  DeviceAddress      Slave device's address.
+  @param  RegisterAddress    The register address on slave device.
+  @param  Data               The pointer to write data.
+
+  @retval EFI_DEVICE_ERROR
+  @retval EFI_SUCCESS
+
+**/
+EFI_STATUS
+EFIAPI
+I2cWriteByte (
+  EFI_PCI_IO_PROTOCOL    *PciIo,
+  UINT8                  DeviceAddress,
+  UINT8                  RegisterAddress,
+  UINT8                  *Data
+  );
+
+#endif
diff --git a/Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430UgaDraw.c b/Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430UgaDraw.c
new file mode 100644
index 0000000000..bdcbd3450c
--- /dev/null
+++ b/Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430UgaDraw.c
@@ -0,0 +1,412 @@
+/** @file
+  This file produces the graphics abstration of UGA Draw. It is called by
+  CirrusLogic5430.c file which deals with the EFI 1.1 driver model.
+  This file just does graphics.
+
+  Copyright (c) 2006 - 2012, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "CirrusLogic5430.h"
+
+//
+// UGA Draw Protocol Member Functions
+//
+EFI_STATUS
+EFIAPI
+CirrusLogic5430UgaDrawGetMode (
+  IN  EFI_UGA_DRAW_PROTOCOL *This,
+  OUT UINT32                *HorizontalResolution,
+  OUT UINT32                *VerticalResolution,
+  OUT UINT32                *ColorDepth,
+  OUT UINT32                *RefreshRate
+  )
+{
+  CIRRUS_LOGIC_5430_PRIVATE_DATA  *Private;
+
+  Private = CIRRUS_LOGIC_5430_PRIVATE_DATA_FROM_UGA_DRAW_THIS (This);
+
+  if (Private->HardwareNeedsStarting) {
+    return EFI_NOT_STARTED;
+  }
+
+  if ((HorizontalResolution == NULL) ||
+      (VerticalResolution == NULL)   ||
+      (ColorDepth == NULL)           ||
+      (RefreshRate == NULL)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  *HorizontalResolution = Private->ModeData[Private->CurrentMode].HorizontalResolution;
+  *VerticalResolution   = Private->ModeData[Private->CurrentMode].VerticalResolution;
+  *ColorDepth           = Private->ModeData[Private->CurrentMode].ColorDepth;
+  *RefreshRate          = Private->ModeData[Private->CurrentMode].RefreshRate;
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+CirrusLogic5430UgaDrawSetMode (
+  IN  EFI_UGA_DRAW_PROTOCOL *This,
+  IN  UINT32                HorizontalResolution,
+  IN  UINT32                VerticalResolution,
+  IN  UINT32                ColorDepth,
+  IN  UINT32                RefreshRate
+  )
+{
+  CIRRUS_LOGIC_5430_PRIVATE_DATA  *Private;
+  UINTN                           Index;
+
+  Private = CIRRUS_LOGIC_5430_PRIVATE_DATA_FROM_UGA_DRAW_THIS (This);
+
+  for (Index = 0; Index < Private->MaxMode; Index++) {
+
+    if (HorizontalResolution != Private->ModeData[Index].HorizontalResolution) {
+      continue;
+    }
+
+    if (VerticalResolution != Private->ModeData[Index].VerticalResolution) {
+      continue;
+    }
+
+    if (ColorDepth != Private->ModeData[Index].ColorDepth) {
+      continue;
+    }
+
+    if (RefreshRate != Private->ModeData[Index].RefreshRate) {
+      continue;
+    }
+
+    if (Private->LineBuffer) {
+      gBS->FreePool (Private->LineBuffer);
+    }
+
+    Private->LineBuffer = NULL;
+    Private->LineBuffer = AllocatePool (HorizontalResolution);
+    if (Private->LineBuffer == NULL) {
+      return EFI_OUT_OF_RESOURCES;
+    }
+
+    InitializeGraphicsMode (Private, &CirrusLogic5430VideoModes[Private->ModeData[Index].ModeNumber]);
+
+    Private->CurrentMode            = Index;
+
+    Private->HardwareNeedsStarting  = FALSE;
+
+    return EFI_SUCCESS;
+  }
+
+  return EFI_NOT_FOUND;
+}
+
+EFI_STATUS
+EFIAPI
+CirrusLogic5430UgaDrawBlt (
+  IN  EFI_UGA_DRAW_PROTOCOL     *This,
+  IN  EFI_UGA_PIXEL             *BltBuffer, OPTIONAL
+  IN  EFI_UGA_BLT_OPERATION     BltOperation,
+  IN  UINTN                     SourceX,
+  IN  UINTN                     SourceY,
+  IN  UINTN                     DestinationX,
+  IN  UINTN                     DestinationY,
+  IN  UINTN                     Width,
+  IN  UINTN                     Height,
+  IN  UINTN                     Delta
+  )
+{
+  CIRRUS_LOGIC_5430_PRIVATE_DATA  *Private;
+  EFI_TPL                         OriginalTPL;
+  UINTN                           DstY;
+  UINTN                           SrcY;
+  EFI_UGA_PIXEL                   *Blt;
+  UINTN                           X;
+  UINT8                           Pixel;
+  UINT32                          WidePixel;
+  UINTN                           ScreenWidth;
+  UINTN                           Offset;
+  UINTN                           SourceOffset;
+
+  Private = CIRRUS_LOGIC_5430_PRIVATE_DATA_FROM_UGA_DRAW_THIS (This);
+
+  if ((UINT32)BltOperation >= EfiUgaBltMax) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (Width == 0 || Height == 0) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // If Delta is zero, then the entire BltBuffer is being used, so Delta
+  // is the number of bytes in each row of BltBuffer.  Since BltBuffer is Width pixels size,
+  // the number of bytes in each row can be computed.
+  //
+  if (Delta == 0) {
+    Delta = Width * sizeof (EFI_UGA_PIXEL);
+  }
+
+  //
+  // We need to fill the Virtual Screen buffer with the blt data.
+  // The virtual screen is upside down, as the first row is the bootom row of
+  // the image.
+  //
+
+  //
+  // Make sure the SourceX, SourceY, DestinationX, DestinationY, Width, and Height parameters
+  // are valid for the operation and the current screen geometry.
+  //
+  if (BltOperation == EfiUgaVideoToBltBuffer) {
+    //
+    // Video to BltBuffer: Source is Video, destination is BltBuffer
+    //
+    if (SourceY + Height > Private->ModeData[Private->CurrentMode].VerticalResolution) {
+      return EFI_INVALID_PARAMETER;
+    }
+
+    if (SourceX + Width > Private->ModeData[Private->CurrentMode].HorizontalResolution) {
+      return EFI_INVALID_PARAMETER;
+    }
+  } else {
+    //
+    // BltBuffer to Video: Source is BltBuffer, destination is Video
+    //
+    if (DestinationY + Height > Private->ModeData[Private->CurrentMode].VerticalResolution) {
+      return EFI_INVALID_PARAMETER;
+    }
+
+    if (DestinationX + Width > Private->ModeData[Private->CurrentMode].HorizontalResolution) {
+      return EFI_INVALID_PARAMETER;
+    }
+  }
+  //
+  // We have to raise to TPL Notify, so we make an atomic write the frame buffer.
+  // We would not want a timer based event (Cursor, ...) to come in while we are
+  // doing this operation.
+  //
+  OriginalTPL = gBS->RaiseTPL (TPL_NOTIFY);
+
+  switch (BltOperation) {
+  case EfiUgaVideoToBltBuffer:
+    //
+    // Video to BltBuffer: Source is Video, destination is BltBuffer
+    //
+    for (SrcY = SourceY, DstY = DestinationY; DstY < (Height + DestinationY); SrcY++, DstY++) {
+
+      Offset = (SrcY * Private->ModeData[Private->CurrentMode].HorizontalResolution) + SourceX;
+      if (((Offset & 0x03) == 0) && ((Width & 0x03) == 0)) {
+        Private->PciIo->Mem.Read (
+                              Private->PciIo,
+                              EfiPciIoWidthUint32,
+                              0,
+                              Offset,
+                              Width >> 2,
+                              Private->LineBuffer
+                              );
+      } else {
+        Private->PciIo->Mem.Read (
+                              Private->PciIo,
+                              EfiPciIoWidthUint8,
+                              0,
+                              Offset,
+                              Width,
+                              Private->LineBuffer
+                              );
+      }
+
+      for (X = 0; X < Width; X++) {
+        Blt         = (EFI_UGA_PIXEL *) ((UINT8 *) BltBuffer + (DstY * Delta) + (DestinationX + X) * sizeof (EFI_UGA_PIXEL));
+
+        Blt->Red    = (UINT8) (Private->LineBuffer[X] & 0xe0);
+        Blt->Green  = (UINT8) ((Private->LineBuffer[X] & 0x1c) << 3);
+        Blt->Blue   = (UINT8) ((Private->LineBuffer[X] & 0x03) << 6);
+      }
+    }
+    break;
+
+  case EfiUgaVideoToVideo:
+    //
+    // Perform hardware acceleration for Video to Video operations
+    //
+    ScreenWidth   = Private->ModeData[Private->CurrentMode].HorizontalResolution;
+    SourceOffset  = (SourceY * Private->ModeData[Private->CurrentMode].HorizontalResolution) + (SourceX);
+    Offset        = (DestinationY * Private->ModeData[Private->CurrentMode].HorizontalResolution) + (DestinationX);
+
+    outw (Private, GRAPH_ADDRESS_REGISTER, 0x0000);
+    outw (Private, GRAPH_ADDRESS_REGISTER, 0x0010);
+    outw (Private, GRAPH_ADDRESS_REGISTER, 0x0012);
+    outw (Private, GRAPH_ADDRESS_REGISTER, 0x0014);
+
+    outw (Private, GRAPH_ADDRESS_REGISTER, 0x0001);
+    outw (Private, GRAPH_ADDRESS_REGISTER, 0x0011);
+    outw (Private, GRAPH_ADDRESS_REGISTER, 0x0013);
+    outw (Private, GRAPH_ADDRESS_REGISTER, 0x0015);
+
+    outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) (((Width << 8) & 0xff00) | 0x20));
+    outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) ((Width & 0xff00) | 0x21));
+    outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) (((Height << 8) & 0xff00) | 0x22));
+    outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) ((Height & 0xff00) | 0x23));
+    outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) (((ScreenWidth << 8) & 0xff00) | 0x24));
+    outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) ((ScreenWidth & 0xff00) | 0x25));
+    outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) (((ScreenWidth << 8) & 0xff00) | 0x26));
+    outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) ((ScreenWidth & 0xff00) | 0x27));
+    outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) ((((Offset) << 8) & 0xff00) | 0x28));
+    outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) ((((Offset) >> 0) & 0xff00) | 0x29));
+    outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) ((((Offset) >> 8) & 0xff00) | 0x2a));
+    outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) ((((SourceOffset) << 8) & 0xff00) | 0x2c));
+    outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) ((((SourceOffset) >> 0) & 0xff00) | 0x2d));
+    outw (Private, GRAPH_ADDRESS_REGISTER, (UINT16) ((((SourceOffset) >> 8) & 0xff00) | 0x2e));
+    outw (Private, GRAPH_ADDRESS_REGISTER, 0x002f);
+    outw (Private, GRAPH_ADDRESS_REGISTER, 0x0030);
+    outw (Private, GRAPH_ADDRESS_REGISTER, 0x0d32);
+    outw (Private, GRAPH_ADDRESS_REGISTER, 0x0033);
+    outw (Private, GRAPH_ADDRESS_REGISTER, 0x0034);
+    outw (Private, GRAPH_ADDRESS_REGISTER, 0x0035);
+
+    outw (Private, GRAPH_ADDRESS_REGISTER, 0x0231);
+
+    outb (Private, GRAPH_ADDRESS_REGISTER, 0x31);
+    while ((inb (Private, GRAPH_DATA_REGISTER) & 0x01) == 0x01)
+      ;
+    break;
+
+  case EfiUgaVideoFill:
+    Blt       = BltBuffer;
+    Pixel     = (UINT8) ((Blt->Red & 0xe0) | ((Blt->Green >> 3) & 0x1c) | ((Blt->Blue >> 6) & 0x03));
+    WidePixel = (Pixel << 8) | Pixel;
+    WidePixel = (WidePixel << 16) | WidePixel;
+
+    if (DestinationX == 0 && Width == Private->ModeData[Private->CurrentMode].HorizontalResolution) {
+      Offset = DestinationY * Private->ModeData[Private->CurrentMode].HorizontalResolution;
+      if (((Offset & 0x03) == 0) && (((Width * Height) & 0x03) == 0)) {
+        Private->PciIo->Mem.Write (
+                              Private->PciIo,
+                              EfiPciIoWidthFillUint32,
+                              0,
+                              Offset,
+                              (Width * Height) >> 2,
+                              &WidePixel
+                              );
+      } else {
+        Private->PciIo->Mem.Write (
+                              Private->PciIo,
+                              EfiPciIoWidthFillUint8,
+                              0,
+                              Offset,
+                              Width * Height,
+                              &Pixel
+                              );
+      }
+    } else {
+      for (SrcY = SourceY, DstY = DestinationY; SrcY < (Height + SourceY); SrcY++, DstY++) {
+        Offset = (DstY * Private->ModeData[Private->CurrentMode].HorizontalResolution) + DestinationX;
+        if (((Offset & 0x03) == 0) && ((Width & 0x03) == 0)) {
+          Private->PciIo->Mem.Write (
+                                Private->PciIo,
+                                EfiPciIoWidthFillUint32,
+                                0,
+                                Offset,
+                                Width >> 2,
+                                &WidePixel
+                                );
+        } else {
+          Private->PciIo->Mem.Write (
+                                Private->PciIo,
+                                EfiPciIoWidthFillUint8,
+                                0,
+                                Offset,
+                                Width,
+                                &Pixel
+                                );
+        }
+      }
+    }
+    break;
+
+  case EfiUgaBltBufferToVideo:
+    for (SrcY = SourceY, DstY = DestinationY; SrcY < (Height + SourceY); SrcY++, DstY++) {
+
+      for (X = 0; X < Width; X++) {
+        Blt                     = (EFI_UGA_PIXEL *) ((UINT8 *) BltBuffer + (SrcY * Delta) + (SourceX + X) * sizeof (EFI_UGA_PIXEL));
+        Private->LineBuffer[X]  = (UINT8) ((Blt->Red & 0xe0) | ((Blt->Green >> 3) & 0x1c) | ((Blt->Blue >> 6) & 0x03));
+      }
+
+      Offset = (DstY * Private->ModeData[Private->CurrentMode].HorizontalResolution) + DestinationX;
+
+      if (((Offset & 0x03) == 0) && ((Width & 0x03) == 0)) {
+        Private->PciIo->Mem.Write (
+                              Private->PciIo,
+                              EfiPciIoWidthUint32,
+                              0,
+                              Offset,
+                              Width >> 2,
+                              Private->LineBuffer
+                              );
+      } else {
+        Private->PciIo->Mem.Write (
+                              Private->PciIo,
+                              EfiPciIoWidthUint8,
+                              0,
+                              Offset,
+                              Width,
+                              Private->LineBuffer
+                              );
+      }
+    }
+    break;
+
+  default:
+    break;
+  }
+
+  gBS->RestoreTPL (OriginalTPL);
+
+  return EFI_SUCCESS;
+}
+
+//
+// Construction and Destruction functions
+//
+EFI_STATUS
+CirrusLogic5430UgaDrawConstructor (
+  CIRRUS_LOGIC_5430_PRIVATE_DATA  *Private
+  )
+{
+  EFI_UGA_DRAW_PROTOCOL *UgaDraw;
+
+  //
+  // Fill in Private->UgaDraw protocol
+  //
+  UgaDraw           = &Private->UgaDraw;
+
+  UgaDraw->GetMode  = CirrusLogic5430UgaDrawGetMode;
+  UgaDraw->SetMode  = CirrusLogic5430UgaDrawSetMode;
+  UgaDraw->Blt      = CirrusLogic5430UgaDrawBlt;
+
+  //
+  // Initialize the private data
+  //
+  Private->CurrentMode            = 0;
+  Private->HardwareNeedsStarting  = TRUE;
+  Private->LineBuffer             = NULL;
+
+  //
+  // Initialize the hardware
+  //
+  UgaDraw->SetMode (
+            UgaDraw,
+            Private->ModeData[Private->CurrentMode].HorizontalResolution,
+            Private->ModeData[Private->CurrentMode].VerticalResolution,
+            Private->ModeData[Private->CurrentMode].ColorDepth,
+            Private->ModeData[Private->CurrentMode].RefreshRate
+            );
+  DrawLogo (
+    Private,
+    Private->ModeData[Private->CurrentMode].HorizontalResolution,
+    Private->ModeData[Private->CurrentMode].VerticalResolution
+    );
+
+  return EFI_SUCCESS;
+}
+
diff --git a/Drivers/OptionRomPkg/CirrusLogic5430Dxe/ComponentName.c b/Drivers/OptionRomPkg/CirrusLogic5430Dxe/ComponentName.c
new file mode 100644
index 0000000000..3792937bed
--- /dev/null
+++ b/Drivers/OptionRomPkg/CirrusLogic5430Dxe/ComponentName.c
@@ -0,0 +1,203 @@
+/** @file
+  Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "CirrusLogic5430.h"
+
+//
+// EFI Component Name Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL  gCirrusLogic5430ComponentName = {
+  CirrusLogic5430ComponentNameGetDriverName,
+  CirrusLogic5430ComponentNameGetControllerName,
+  "eng"
+};
+
+//
+// EFI Component Name 2 Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gCirrusLogic5430ComponentName2 = {
+  (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) CirrusLogic5430ComponentNameGetDriverName,
+  (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) CirrusLogic5430ComponentNameGetControllerName,
+  "en"
+};
+
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mCirrusLogic5430DriverNameTable[] = {
+  { "eng;en", L"Cirrus Logic 5430 Driver" },
+  { NULL , NULL }
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mCirrusLogic5430ControllerNameTable[] = {
+  { "eng;en", L"Cirrus Logic 5430 PCI Adapter" },
+  { NULL , NULL }
+};
+
+/**
+  Retrieves a Unicode string that is the user readable name of the driver.
+
+  This function retrieves the user readable name of a driver in the form of a
+  Unicode string. If the driver specified by This has a user readable name in
+  the language specified by Language, then a pointer to the driver name is
+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+  by This does not support the language specified by Language,
+  then EFI_UNSUPPORTED is returned.
+
+  @param  This[in]              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+                                EFI_COMPONENT_NAME_PROTOCOL instance.
+
+  @param  Language[in]          A pointer to a Null-terminated ASCII string
+                                array indicating the language. This is the
+                                language of the driver name that the caller is
+                                requesting, and it must match one of the
+                                languages specified in SupportedLanguages. The
+                                number of languages supported by a driver is up
+                                to the driver writer. Language is specified
+                                in RFC 4646 or ISO 639-2 language code format.
+
+  @param  DriverName[out]       A pointer to the Unicode string to return.
+                                This Unicode string is the name of the
+                                driver specified by This in the language
+                                specified by Language.
+
+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by
+                                This and the language specified by Language was
+                                returned in DriverName.
+
+  @retval EFI_INVALID_PARAMETER Language is NULL.
+
+  @retval EFI_INVALID_PARAMETER DriverName is NULL.
+
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support
+                                the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+CirrusLogic5430ComponentNameGetDriverName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,
+  IN  CHAR8                        *Language,
+  OUT CHAR16                       **DriverName
+  )
+{
+  return LookupUnicodeString2 (
+           Language,
+           This->SupportedLanguages,
+           mCirrusLogic5430DriverNameTable,
+           DriverName,
+           (BOOLEAN)(This == &gCirrusLogic5430ComponentName)
+           );
+}
+
+/**
+  Retrieves a Unicode string that is the user readable name of the controller
+  that is being managed by a driver.
+
+  This function retrieves the user readable name of the controller specified by
+  ControllerHandle and ChildHandle in the form of a Unicode string. If the
+  driver specified by This has a user readable name in the language specified by
+  Language, then a pointer to the controller name is returned in ControllerName,
+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently
+  managing the controller specified by ControllerHandle and ChildHandle,
+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not
+  support the language specified by Language, then EFI_UNSUPPORTED is returned.
+
+  @param  This[in]              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+                                EFI_COMPONENT_NAME_PROTOCOL instance.
+
+  @param  ControllerHandle[in]  The handle of a controller that the driver
+                                specified by This is managing.  This handle
+                                specifies the controller whose name is to be
+                                returned.
+
+  @param  ChildHandle[in]       The handle of the child controller to retrieve
+                                the name of.  This is an optional parameter that
+                                may be NULL.  It will be NULL for device
+                                drivers.  It will also be NULL for a bus drivers
+                                that wish to retrieve the name of the bus
+                                controller.  It will not be NULL for a bus
+                                driver that wishes to retrieve the name of a
+                                child controller.
+
+  @param  Language[in]          A pointer to a Null-terminated ASCII string
+                                array indicating the language.  This is the
+                                language of the driver name that the caller is
+                                requesting, and it must match one of the
+                                languages specified in SupportedLanguages. The
+                                number of languages supported by a driver is up
+                                to the driver writer. Language is specified in
+                                RFC 4646 or ISO 639-2 language code format.
+
+  @param  ControllerName[out]   A pointer to the Unicode string to return.
+                                This Unicode string is the name of the
+                                controller specified by ControllerHandle and
+                                ChildHandle in the language specified by
+                                Language from the point of view of the driver
+                                specified by This.
+
+  @retval EFI_SUCCESS           The Unicode string for the user readable name in
+                                the language specified by Language for the
+                                driver specified by This was returned in
+                                DriverName.
+
+  @retval EFI_INVALID_PARAMETER ControllerHandle is NULL.
+
+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid
+                                EFI_HANDLE.
+
+  @retval EFI_INVALID_PARAMETER Language is NULL.
+
+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+
+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently
+                                managing the controller specified by
+                                ControllerHandle and ChildHandle.
+
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support
+                                the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+CirrusLogic5430ComponentNameGetControllerName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL                     *This,
+  IN  EFI_HANDLE                                      ControllerHandle,
+  IN  EFI_HANDLE                                      ChildHandle        OPTIONAL,
+  IN  CHAR8                                           *Language,
+  OUT CHAR16                                          **ControllerName
+  )
+{
+  EFI_STATUS                      Status;
+
+  //
+  // This is a device driver, so ChildHandle must be NULL.
+  //
+  if (ChildHandle != NULL) {
+    return EFI_UNSUPPORTED;
+  }
+
+  //
+  // Make sure this driver is currently managing ControllHandle
+  //
+  Status = EfiTestManagedDevice (
+             ControllerHandle,
+             gCirrusLogic5430DriverBinding.DriverBindingHandle,
+             &gEfiPciIoProtocolGuid
+             );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Get the Cirrus Logic 5430's Device structure
+  //
+  return LookupUnicodeString2 (
+           Language,
+           This->SupportedLanguages,
+           mCirrusLogic5430ControllerNameTable,
+           ControllerName,
+           (BOOLEAN)(This == &gCirrusLogic5430ComponentName)
+           );
+}
diff --git a/Drivers/OptionRomPkg/CirrusLogic5430Dxe/DriverSupportedEfiVersion.c b/Drivers/OptionRomPkg/CirrusLogic5430Dxe/DriverSupportedEfiVersion.c
new file mode 100644
index 0000000000..2d8412bf53
--- /dev/null
+++ b/Drivers/OptionRomPkg/CirrusLogic5430Dxe/DriverSupportedEfiVersion.c
@@ -0,0 +1,14 @@
+/** @file
+  Copyright (c) 2007, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  Module Name:  DriverSupportEfiVersion.c
+
+**/
+#include "CirrusLogic5430.h"
+
+EFI_DRIVER_SUPPORTED_EFI_VERSION_PROTOCOL gCirrusLogic5430DriverSupportedEfiVersion = {
+  sizeof (EFI_DRIVER_SUPPORTED_EFI_VERSION_PROTOCOL), // Size of Protocol structure.
+  0                                                   // Version number to be filled at start up.
+};
+
diff --git a/Drivers/OptionRomPkg/CirrusLogic5430Dxe/Edid.c b/Drivers/OptionRomPkg/CirrusLogic5430Dxe/Edid.c
new file mode 100644
index 0000000000..5f288d219e
--- /dev/null
+++ b/Drivers/OptionRomPkg/CirrusLogic5430Dxe/Edid.c
@@ -0,0 +1,525 @@
+/** @file
+  Read EDID information and parse EDID information.
+
+  Copyright (c) 2008 - 2014, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "CirrusLogic5430.h"
+#include "CirrusLogic5430I2c.h"
+
+//
+// EDID block
+//
+typedef struct {
+  UINT8   Header[8];                        //EDID header "00 FF FF FF FF FF FF 00"
+  UINT16  ManufactureName;                  //EISA 3-character ID
+  UINT16  ProductCode;                      //Vendor assigned code
+  UINT32  SerialNumber;                     //32-bit serial number
+  UINT8   WeekOfManufacture;                //Week number
+  UINT8   YearOfManufacture;                //Year
+  UINT8   EdidVersion;                      //EDID Structure Version
+  UINT8   EdidRevision;                     //EDID Structure Revision
+  UINT8   VideoInputDefinition;
+  UINT8   MaxHorizontalImageSize;           //cm
+  UINT8   MaxVerticalImageSize;             //cm
+  UINT8   DisplayTransferCharacteristic;
+  UINT8   FeatureSupport;
+  UINT8   RedGreenLowBits;                  //Rx1 Rx0 Ry1 Ry0 Gx1 Gx0 Gy1Gy0
+  UINT8   BlueWhiteLowBits;                 //Bx1 Bx0 By1 By0 Wx1 Wx0 Wy1 Wy0
+  UINT8   RedX;                             //Red-x Bits 9 - 2
+  UINT8   RedY;                             //Red-y Bits 9 - 2
+  UINT8   GreenX;                           //Green-x Bits 9 - 2
+  UINT8   GreenY;                           //Green-y Bits 9 - 2
+  UINT8   BlueX;                            //Blue-x Bits 9 - 2
+  UINT8   BlueY;                            //Blue-y Bits 9 - 2
+  UINT8   WhiteX;                           //White-x Bits 9 - 2
+  UINT8   WhiteY;                           //White-x Bits 9 - 2
+  UINT8   EstablishedTimings[3];
+  UINT8   StandardTimingIdentification[16];
+  UINT8   DetailedTimingDescriptions[72];
+  UINT8   ExtensionFlag;                    //Number of (optional) 128-byte EDID extension blocks to follow
+  UINT8   Checksum;
+} EDID_BLOCK;
+
+#define EDID_BLOCK_SIZE                        128
+#define VBE_EDID_ESTABLISHED_TIMING_MAX_NUMBER 17
+
+typedef struct {
+  UINT16  HorizontalResolution;
+  UINT16  VerticalResolution;
+  UINT16  RefreshRate;
+} EDID_TIMING;
+
+typedef struct {
+  UINT32  ValidNumber;
+  UINT32  Key[VBE_EDID_ESTABLISHED_TIMING_MAX_NUMBER];
+} VALID_EDID_TIMING;
+
+//
+// Standard timing defined by VESA EDID
+//
+EDID_TIMING mVbeEstablishedEdidTiming[] = {
+  //
+  // Established Timing I
+  //
+  {800, 600, 60},
+  {800, 600, 56},
+  {640, 480, 75},
+  {640, 480, 72},
+  {640, 480, 67},
+  {640, 480, 60},
+  {720, 400, 88},
+  {720, 400, 70},
+  //
+  // Established Timing II
+  //
+  {1280, 1024, 75},
+  {1024,  768, 75},
+  {1024,  768, 70},
+  {1024,  768, 60},
+  {1024,  768, 87},
+  {832,   624, 75},
+  {800,   600, 75},
+  {800,   600, 72},
+  //
+  // Established Timing III
+  //
+  {1152, 870, 75}
+};
+
+/**
+  Read EDID information from I2C Bus on CirrusLogic.
+
+  @param  Private             Pointer to CIRRUS_LOGIC_5430_PRIVATE_DATA.
+  @param  EdidDataBlock       Pointer to EDID data block.
+  @param  EdidSize            Returned EDID block size.
+
+  @retval EFI_UNSUPPORTED
+  @retval EFI_SUCCESS
+
+**/
+EFI_STATUS
+ReadEdidData (
+  CIRRUS_LOGIC_5430_PRIVATE_DATA     *Private,
+  UINT8                              **EdidDataBlock,
+  UINTN                              *EdidSize
+  )
+{
+  UINTN             Index;
+  UINT8             EdidData[EDID_BLOCK_SIZE * 2];
+  UINT8             *ValidEdid;
+  UINT64            Signature;
+
+  for (Index = 0; Index < EDID_BLOCK_SIZE * 2; Index ++) {
+    I2cReadByte (Private->PciIo, 0xa0, (UINT8)Index, &EdidData[Index]);
+  }
+
+  //
+  // Search for the EDID signature
+  //
+  ValidEdid = &EdidData[0];
+  Signature = 0x00ffffffffffff00ull;
+  for (Index = 0; Index < EDID_BLOCK_SIZE * 2; Index ++, ValidEdid ++) {
+    if (CompareMem (ValidEdid, &Signature, 8) == 0) {
+      break;
+    }
+  }
+
+  if (Index == 256) {
+    //
+    // No EDID signature found
+    //
+    return EFI_UNSUPPORTED;
+  }
+
+  *EdidDataBlock = AllocateCopyPool (
+                     EDID_BLOCK_SIZE,
+                     ValidEdid
+                     );
+  if (*EdidDataBlock == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  //
+  // Currently only support EDID 1.x
+  //
+  *EdidSize = EDID_BLOCK_SIZE;
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Generate a search key for a specified timing data.
+
+  @param  EdidTiming             Pointer to EDID timing
+
+  @return The 32 bit unique key for search.
+
+**/
+UINT32
+CalculateEdidKey (
+  EDID_TIMING       *EdidTiming
+  )
+{
+  UINT32 Key;
+
+  //
+  // Be sure no conflicts for all standard timing defined by VESA.
+  //
+  Key = (EdidTiming->HorizontalResolution * 2) + EdidTiming->VerticalResolution;
+  return Key;
+}
+
+/**
+  Search a specified Timing in all the valid EDID timings.
+
+  @param  ValidEdidTiming        All valid EDID timing information.
+  @param  EdidTiming             The Timing to search for.
+
+  @retval TRUE                   Found.
+  @retval FALSE                  Not found.
+
+**/
+BOOLEAN
+SearchEdidTiming (
+  VALID_EDID_TIMING *ValidEdidTiming,
+  EDID_TIMING       *EdidTiming
+  )
+{
+  UINT32 Index;
+  UINT32 Key;
+
+  Key = CalculateEdidKey (EdidTiming);
+
+  for (Index = 0; Index < ValidEdidTiming->ValidNumber; Index ++) {
+    if (Key == ValidEdidTiming->Key[Index]) {
+      return TRUE;
+    }
+  }
+
+  return FALSE;
+}
+
+/**
+  Parse the Established Timing and Standard Timing in EDID data block.
+
+  @param  EdidBuffer             Pointer to EDID data block
+  @param  ValidEdidTiming        Valid EDID timing information
+
+  @retval TRUE                   The EDID data is valid.
+  @retval FALSE                  The EDID data is invalid.
+
+**/
+BOOLEAN
+ParseEdidData (
+  UINT8                         *EdidBuffer,
+  VALID_EDID_TIMING             *ValidEdidTiming
+  )
+{
+  UINT8        CheckSum;
+  UINT32       Index;
+  UINT32       ValidNumber;
+  UINT32       TimingBits;
+  UINT8        *BufferIndex;
+  UINT16       HorizontalResolution;
+  UINT16       VerticalResolution;
+  UINT8        AspectRatio;
+  UINT8        RefreshRate;
+  EDID_TIMING  TempTiming;
+  EDID_BLOCK   *EdidDataBlock;
+
+  EdidDataBlock = (EDID_BLOCK *) EdidBuffer;
+
+  //
+  // Check the checksum of EDID data
+  //
+  CheckSum = 0;
+  for (Index = 0; Index < EDID_BLOCK_SIZE; Index ++) {
+    CheckSum = (UINT8) (CheckSum + EdidBuffer[Index]);
+  }
+  if (CheckSum != 0) {
+    return FALSE;
+  }
+
+  ValidNumber = 0;
+  SetMem (ValidEdidTiming, sizeof (VALID_EDID_TIMING), 0);
+
+  if ((EdidDataBlock->EstablishedTimings[0] != 0) ||
+      (EdidDataBlock->EstablishedTimings[1] != 0) ||
+      (EdidDataBlock->EstablishedTimings[2] != 0)
+      ) {
+    //
+    // Established timing data
+    //
+    TimingBits = EdidDataBlock->EstablishedTimings[0] |
+                 (EdidDataBlock->EstablishedTimings[1] << 8) |
+                 ((EdidDataBlock->EstablishedTimings[2] & 0x80) << 9) ;
+    for (Index = 0; Index < VBE_EDID_ESTABLISHED_TIMING_MAX_NUMBER; Index ++) {
+      if (TimingBits & 0x1) {
+        ValidEdidTiming->Key[ValidNumber] = CalculateEdidKey (&mVbeEstablishedEdidTiming[Index]);
+        ValidNumber ++;
+      }
+      TimingBits = TimingBits >> 1;
+    }
+  } else {
+    //
+    // If no Established timing data, read the standard timing data
+    //
+    BufferIndex = &EdidDataBlock->StandardTimingIdentification[0];
+    for (Index = 0; Index < 8; Index ++) {
+      if ((BufferIndex[0] != 0x1) && (BufferIndex[1] != 0x1)){
+        //
+        // A valid Standard Timing
+        //
+        HorizontalResolution = (UINT16) (BufferIndex[0] * 8 + 248);
+        AspectRatio = (UINT8) (BufferIndex[1] >> 6);
+        switch (AspectRatio) {
+          case 0:
+            VerticalResolution = (UINT16) (HorizontalResolution / 16 * 10);
+            break;
+          case 1:
+            VerticalResolution = (UINT16) (HorizontalResolution / 4 * 3);
+            break;
+          case 2:
+            VerticalResolution = (UINT16) (HorizontalResolution / 5 * 4);
+            break;
+          case 3:
+            VerticalResolution = (UINT16) (HorizontalResolution / 16 * 9);
+            break;
+          default:
+            VerticalResolution = (UINT16) (HorizontalResolution / 4 * 3);
+            break;
+        }
+        RefreshRate = (UINT8) ((BufferIndex[1] & 0x1f) + 60);
+        TempTiming.HorizontalResolution = HorizontalResolution;
+        TempTiming.VerticalResolution = VerticalResolution;
+        TempTiming.RefreshRate = RefreshRate;
+        ValidEdidTiming->Key[ValidNumber] = CalculateEdidKey (&TempTiming);
+        ValidNumber ++;
+      }
+      BufferIndex += 2;
+    }
+  }
+
+  ValidEdidTiming->ValidNumber = ValidNumber;
+  return TRUE;
+}
+
+/**
+  Construct the valid video modes for CirrusLogic5430.
+
+**/
+EFI_STATUS
+CirrusLogic5430VideoModeSetup (
+  CIRRUS_LOGIC_5430_PRIVATE_DATA  *Private
+  )
+{
+  EFI_STATUS                             Status;
+  UINT32                                 Index;
+  BOOLEAN                                EdidFound;
+  EFI_EDID_OVERRIDE_PROTOCOL             *EdidOverride;
+  UINT32                                 EdidAttributes;
+  BOOLEAN                                EdidOverrideFound;
+  UINTN                                  EdidOverrideDataSize;
+  UINT8                                  *EdidOverrideDataBlock;
+  UINTN                                  EdidDiscoveredDataSize;
+  UINT8                                  *EdidDiscoveredDataBlock;
+  UINTN                                  EdidActiveDataSize;
+  UINT8                                  *EdidActiveDataBlock;
+  VALID_EDID_TIMING                      ValidEdidTiming;
+  UINT32                                 ValidModeCount;
+  CIRRUS_LOGIC_5430_MODE_DATA            *ModeData;
+  BOOLEAN                                TimingMatch;
+  CIRRUS_LOGIC_5430_VIDEO_MODES          *VideoMode;
+  EDID_TIMING                            TempTiming;
+
+  //
+  // setup EDID information
+  //
+  Private->EdidDiscovered.Edid       = NULL;
+  Private->EdidDiscovered.SizeOfEdid = 0;
+  Private->EdidActive.Edid           = NULL;
+  Private->EdidActive.SizeOfEdid     = 0;
+
+  EdidFound               = FALSE;
+  EdidOverrideFound       = FALSE;
+  EdidAttributes          = 0xff;
+  EdidOverrideDataSize    = 0;
+  EdidOverrideDataBlock   = NULL;
+  EdidActiveDataSize      = 0;
+  EdidActiveDataBlock     = NULL;
+  EdidDiscoveredDataBlock = NULL;
+
+  //
+  // Find EDID Override protocol firstly, this protocol is installed by platform if needed.
+  //
+  Status = gBS->LocateProtocol (
+                   &gEfiEdidOverrideProtocolGuid,
+                   NULL,
+                   (VOID **) &EdidOverride
+                   );
+  if (!EFI_ERROR (Status)) {
+    //
+    // Allocate double size of VESA_BIOS_EXTENSIONS_EDID_BLOCK_SIZE to avoid overflow
+    //
+    EdidOverrideDataBlock = AllocatePool (EDID_BLOCK_SIZE * 2);
+    if (NULL == EdidOverrideDataBlock) {
+  		Status = EFI_OUT_OF_RESOURCES;
+      goto Done;
+    }
+
+    Status = EdidOverride->GetEdid (
+                             EdidOverride,
+                             Private->Handle,
+                             &EdidAttributes,
+                             &EdidOverrideDataSize,
+                             (UINT8 **) &EdidOverrideDataBlock
+                             );
+    if (!EFI_ERROR (Status)  &&
+         EdidAttributes == 0 &&
+         EdidOverrideDataSize != 0) {
+      //
+      // Succeeded to get EDID Override Data
+      //
+      EdidOverrideFound = TRUE;
+    }
+  }
+
+  if (EdidOverrideFound != TRUE || EdidAttributes == EFI_EDID_OVERRIDE_DONT_OVERRIDE) {
+    //
+    // If EDID Override data doesn't exist or EFI_EDID_OVERRIDE_DONT_OVERRIDE returned,
+    // read EDID information through I2C Bus
+    //
+    if (ReadEdidData (Private, &EdidDiscoveredDataBlock, &EdidDiscoveredDataSize) == EFI_SUCCESS) {
+      Private->EdidDiscovered.SizeOfEdid = (UINT32) EdidDiscoveredDataSize;
+     	Private->EdidDiscovered.Edid = (UINT8 *) AllocateCopyPool (
+                                                          EdidDiscoveredDataSize,
+                                                          EdidDiscoveredDataBlock
+     																										  );
+
+      if (NULL == Private->EdidDiscovered.Edid) {
+     	  Status = EFI_OUT_OF_RESOURCES;
+        goto Done;
+      }
+
+      EdidActiveDataSize  = Private->EdidDiscovered.SizeOfEdid;
+      EdidActiveDataBlock = Private->EdidDiscovered.Edid;
+
+      EdidFound = TRUE;
+    }
+  }
+
+  if (EdidFound != TRUE && EdidOverrideFound == TRUE) {
+    EdidActiveDataSize  = EdidOverrideDataSize;
+    EdidActiveDataBlock = EdidOverrideDataBlock;
+    EdidFound = TRUE;
+ 	}
+
+ 	if (EdidFound == TRUE) {
+    //
+    // Parse EDID data structure to retrieve modes supported by monitor
+    //
+    if (ParseEdidData ((UINT8 *) EdidActiveDataBlock, &ValidEdidTiming) == TRUE) {
+      //
+      // Copy EDID Override Data to EDID Active Data
+      //
+      Private->EdidActive.SizeOfEdid = (UINT32) EdidActiveDataSize;
+      Private->EdidActive.Edid = (UINT8 *) AllocateCopyPool (
+                                             EdidActiveDataSize,
+                                             EdidActiveDataBlock
+                                             );
+      if (NULL == Private->EdidActive.Edid) {
+   		  Status = EFI_OUT_OF_RESOURCES;
+        goto Done;
+      }
+    }
+  } else {
+    Private->EdidActive.SizeOfEdid = 0;
+    Private->EdidActive.Edid = NULL;
+    EdidFound = FALSE;
+  }
+
+  if (EdidFound) {
+    //
+    // Initialize the private mode data with the supported modes.
+    //
+    ValidModeCount = 0;
+    ModeData = &Private->ModeData[0];
+    VideoMode = &CirrusLogic5430VideoModes[0];
+    for (Index = 0; Index < CIRRUS_LOGIC_5430_MODE_COUNT; Index++) {
+
+      TimingMatch = TRUE;
+
+      //
+      // Check whether match with CirrusLogic5430 video mode
+      //
+      TempTiming.HorizontalResolution = (UINT16) VideoMode->Width;
+      TempTiming.VerticalResolution   = (UINT16) VideoMode->Height;
+      TempTiming.RefreshRate          = (UINT16) VideoMode->RefreshRate;
+      if (SearchEdidTiming (&ValidEdidTiming, &TempTiming) != TRUE) {
+        TimingMatch = FALSE;
+      }
+
+      //
+      // Not export Mode 0x0 as GOP mode, this is not defined in spec.
+      //
+      if ((VideoMode->Width == 0) || (VideoMode->Height == 0)) {
+        TimingMatch = FALSE;
+      }
+
+      if (TimingMatch) {
+        ModeData->ModeNumber = Index;
+        ModeData->HorizontalResolution          = VideoMode->Width;
+        ModeData->VerticalResolution            = VideoMode->Height;
+        ModeData->ColorDepth                    = VideoMode->ColorDepth;
+        ModeData->RefreshRate                   = VideoMode->RefreshRate;
+
+        ModeData ++;
+        ValidModeCount ++;
+      }
+
+      VideoMode ++;
+    }
+
+    Private->MaxMode = ValidModeCount;
+
+  } else {
+    //
+    // If EDID information wasn't found
+    //
+    ModeData = &Private->ModeData[0];
+    VideoMode = &CirrusLogic5430VideoModes[0];
+    for (Index = 0; Index < CIRRUS_LOGIC_5430_MODE_COUNT; Index ++) {
+      ModeData->ModeNumber = Index;
+      ModeData->HorizontalResolution          = VideoMode->Width;
+      ModeData->VerticalResolution            = VideoMode->Height;
+      ModeData->ColorDepth                    = VideoMode->ColorDepth;
+      ModeData->RefreshRate                   = VideoMode->RefreshRate;
+
+      ModeData ++ ;
+      VideoMode ++;
+    }
+    Private->MaxMode = CIRRUS_LOGIC_5430_MODE_COUNT;
+  }
+
+  if (EdidOverrideDataBlock != NULL) {
+    FreePool (EdidOverrideDataBlock);
+  }
+
+  return EFI_SUCCESS;
+
+Done:
+  if (EdidOverrideDataBlock != NULL) {
+    FreePool (EdidOverrideDataBlock);
+  }
+  if (Private->EdidDiscovered.Edid != NULL) {
+    FreePool (Private->EdidDiscovered.Edid);
+  }
+  if (Private->EdidDiscovered.Edid != NULL) {
+    FreePool (Private->EdidActive.Edid);
+  }
+
+  return EFI_DEVICE_ERROR;
+}
diff --git a/Drivers/OptionRomPkg/Include/Library/BltLib.h b/Drivers/OptionRomPkg/Include/Library/BltLib.h
new file mode 100644
index 0000000000..48990a296d
--- /dev/null
+++ b/Drivers/OptionRomPkg/Include/Library/BltLib.h
@@ -0,0 +1,253 @@
+/** @file
+  Library for performing video blt operations
+
+  Copyright (c) 2009 - 2011, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef __BLT_LIB__
+#define __BLT_LIB__
+
+#include <Protocol/GraphicsOutput.h>
+
+
+/**
+  Configure the BltLib for a frame-buffer
+
+  @param[in] FrameBuffer      Pointer to the start of the frame buffer
+  @param[in] FrameBufferInfo  Describes the frame buffer characteristics
+
+  @retval  EFI_INVALID_PARAMETER - Invalid parameter passed in
+  @retval  EFI_SUCCESS - Blt operation success
+
+**/
+EFI_STATUS
+EFIAPI
+BltLibConfigure (
+  IN  VOID                                 *FrameBuffer,
+  IN  EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *FrameBufferInfo
+  );
+
+
+/**
+  Performs a UEFI Graphics Output Protocol Blt operation.
+
+  @param[in,out] BltBuffer     - The data to transfer to screen
+  @param[in]     BltOperation  - The operation to perform
+  @param[in]     SourceX       - The X coordinate of the source for BltOperation
+  @param[in]     SourceY       - The Y coordinate of the source for BltOperation
+  @param[in]     DestinationX  - The X coordinate of the destination for BltOperation
+  @param[in]     DestinationY  - The Y coordinate of the destination for BltOperation
+  @param[in]     Width         - The width of a rectangle in the blt rectangle in pixels
+  @param[in]     Height        - The height of a rectangle in the blt rectangle in pixels
+  @param[in]     Delta         - Not used for EfiBltVideoFill and EfiBltVideoToVideo operation.
+                                 If a Delta of 0 is used, the entire BltBuffer will be operated on.
+                                 If a subrectangle of the BltBuffer is used, then Delta represents
+                                 the number of bytes in a row of the BltBuffer.
+
+  @retval  EFI_DEVICE_ERROR - A hardware error occured
+  @retval  EFI_INVALID_PARAMETER - Invalid parameter passed in
+  @retval  EFI_SUCCESS - Blt operation success
+
+**/
+EFI_STATUS
+EFIAPI
+BltLibGopBlt (
+  IN OUT EFI_GRAPHICS_OUTPUT_BLT_PIXEL      *BltBuffer, OPTIONAL
+  IN     EFI_GRAPHICS_OUTPUT_BLT_OPERATION  BltOperation,
+  IN     UINTN                              SourceX,
+  IN     UINTN                              SourceY,
+  IN     UINTN                              DestinationX,
+  IN     UINTN                              DestinationY,
+  IN     UINTN                              Width,
+  IN     UINTN                              Height,
+  IN     UINTN                              Delta
+  );
+
+
+/**
+  Performs a UEFI Graphics Output Protocol Blt Video Fill.
+
+  @param[in]  Color         Color to fill the region with
+  @param[in]  DestinationX  X location to start fill operation
+  @param[in]  DestinationY  Y location to start fill operation
+  @param[in]  Width         Width (in pixels) to fill
+  @param[in]  Height        Height to fill
+
+  @retval  EFI_DEVICE_ERROR - A hardware error occured
+  @retval  EFI_INVALID_PARAMETER - Invalid parameter passed in
+  @retval  EFI_SUCCESS - Blt operation success
+
+**/
+EFI_STATUS
+EFIAPI
+BltLibVideoFill (
+  IN  EFI_GRAPHICS_OUTPUT_BLT_PIXEL         *Color,
+  IN  UINTN                                 DestinationX,
+  IN  UINTN                                 DestinationY,
+  IN  UINTN                                 Width,
+  IN  UINTN                                 Height
+  );
+
+
+/**
+  Performs a UEFI Graphics Output Protocol Blt Video to Buffer operation.
+
+  @param[out] BltBuffer     Output buffer for pixel color data
+  @param[in]  SourceX       X location within video
+  @param[in]  SourceY       Y location within video
+  @param[in]  Width         Width (in pixels)
+  @param[in]  Height        Height
+
+  @retval  EFI_DEVICE_ERROR - A hardware error occured
+  @retval  EFI_INVALID_PARAMETER - Invalid parameter passed in
+  @retval  EFI_SUCCESS - Blt operation success
+
+**/
+EFI_STATUS
+EFIAPI
+BltLibVideoToBltBuffer (
+  OUT EFI_GRAPHICS_OUTPUT_BLT_PIXEL         *BltBuffer,
+  IN  UINTN                                 SourceX,
+  IN  UINTN                                 SourceY,
+  IN  UINTN                                 Width,
+  IN  UINTN                                 Height
+  );
+
+
+/**
+  Performs a UEFI Graphics Output Protocol Blt Video to Buffer operation
+  with extended parameters.
+
+  @param[out] BltBuffer     Output buffer for pixel color data
+  @param[in]  SourceX       X location within video
+  @param[in]  SourceY       Y location within video
+  @param[in]  DestinationX  X location within BltBuffer
+  @param[in]  DestinationY  Y location within BltBuffer
+  @param[in]  Width         Width (in pixels)
+  @param[in]  Height        Height
+  @param[in]  Delta         Number of bytes in a row of BltBuffer
+
+  @retval  EFI_DEVICE_ERROR - A hardware error occured
+  @retval  EFI_INVALID_PARAMETER - Invalid parameter passed in
+  @retval  EFI_SUCCESS - Blt operation success
+
+**/
+EFI_STATUS
+EFIAPI
+BltLibVideoToBltBufferEx (
+  OUT EFI_GRAPHICS_OUTPUT_BLT_PIXEL         *BltBuffer,
+  IN  UINTN                                 SourceX,
+  IN  UINTN                                 SourceY,
+  IN  UINTN                                 DestinationX,
+  IN  UINTN                                 DestinationY,
+  IN  UINTN                                 Width,
+  IN  UINTN                                 Height,
+  IN  UINTN                                 Delta
+  );
+
+
+/**
+  Performs a UEFI Graphics Output Protocol Blt Buffer to Video operation.
+
+  @param[in]  BltBuffer     Output buffer for pixel color data
+  @param[in]  DestinationX  X location within video
+  @param[in]  DestinationY  Y location within video
+  @param[in]  Width         Width (in pixels)
+  @param[in]  Height        Height
+
+  @retval  EFI_DEVICE_ERROR - A hardware error occured
+  @retval  EFI_INVALID_PARAMETER - Invalid parameter passed in
+  @retval  EFI_SUCCESS - Blt operation success
+
+**/
+EFI_STATUS
+EFIAPI
+BltLibBufferToVideo (
+  IN  EFI_GRAPHICS_OUTPUT_BLT_PIXEL         *BltBuffer,
+  IN  UINTN                                 DestinationX,
+  IN  UINTN                                 DestinationY,
+  IN  UINTN                                 Width,
+  IN  UINTN                                 Height
+  );
+
+
+/**
+  Performs a UEFI Graphics Output Protocol Blt Buffer to Video operation
+  with extended parameters.
+
+  @param[in]  BltBuffer     Output buffer for pixel color data
+  @param[in]  SourceX       X location within BltBuffer
+  @param[in]  SourceY       Y location within BltBuffer
+  @param[in]  DestinationX  X location within video
+  @param[in]  DestinationY  Y location within video
+  @param[in]  Width         Width (in pixels)
+  @param[in]  Height        Height
+  @param[in]  Delta         Number of bytes in a row of BltBuffer
+
+  @retval  EFI_DEVICE_ERROR - A hardware error occured
+  @retval  EFI_INVALID_PARAMETER - Invalid parameter passed in
+  @retval  EFI_SUCCESS - Blt operation success
+
+**/
+EFI_STATUS
+EFIAPI
+BltLibBufferToVideoEx (
+  IN  EFI_GRAPHICS_OUTPUT_BLT_PIXEL         *BltBuffer,
+  IN  UINTN                                 SourceX,
+  IN  UINTN                                 SourceY,
+  IN  UINTN                                 DestinationX,
+  IN  UINTN                                 DestinationY,
+  IN  UINTN                                 Width,
+  IN  UINTN                                 Height,
+  IN  UINTN                                 Delta
+  );
+
+
+/**
+  Performs a UEFI Graphics Output Protocol Blt Video to Video operation
+
+  @param[in]  SourceX       X location within video
+  @param[in]  SourceY       Y location within video
+  @param[in]  DestinationX  X location within video
+  @param[in]  DestinationY  Y location within video
+  @param[in]  Width         Width (in pixels)
+  @param[in]  Height        Height
+
+  @retval  EFI_DEVICE_ERROR - A hardware error occured
+  @retval  EFI_INVALID_PARAMETER - Invalid parameter passed in
+  @retval  EFI_SUCCESS - Blt operation success
+
+**/
+EFI_STATUS
+EFIAPI
+BltLibVideoToVideo (
+  IN  UINTN                                 SourceX,
+  IN  UINTN                                 SourceY,
+  IN  UINTN                                 DestinationX,
+  IN  UINTN                                 DestinationY,
+  IN  UINTN                                 Width,
+  IN  UINTN                                 Height
+  );
+
+
+/**
+  Returns the sizes related to the video device
+
+  @param[out]  Width   Width (in pixels)
+  @param[out]  Height  Height (in pixels)
+
+  @retval  EFI_INVALID_PARAMETER - Invalid parameter passed in
+  @retval  EFI_SUCCESS - The sizes were returned
+
+**/
+EFI_STATUS
+EFIAPI
+BltLibGetSizes (
+  OUT UINTN                                 *Width,  OPTIONAL
+  OUT UINTN                                 *Height  OPTIONAL
+  );
+
+#endif
+
diff --git a/Drivers/OptionRomPkg/Library/FrameBufferBltLib/FrameBufferBltLib.c b/Drivers/OptionRomPkg/Library/FrameBufferBltLib/FrameBufferBltLib.c
new file mode 100644
index 0000000000..520fac4c63
--- /dev/null
+++ b/Drivers/OptionRomPkg/Library/FrameBufferBltLib/FrameBufferBltLib.c
@@ -0,0 +1,744 @@
+/** @file
+  FrameBufferBltLib - Library to perform blt operations on a frame buffer.
+
+  Copyright (c) 2007 - 2011, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "PiDxe.h"
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/BltLib.h>
+#include <Library/DebugLib.h>
+
+#if 0
+#define VDEBUG DEBUG
+#else
+#define VDEBUG(x)
+#endif
+
+#define MAX_LINE_BUFFER_SIZE (SIZE_4KB * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL))
+
+UINTN                           mBltLibColorDepth;
+UINTN                           mBltLibWidthInBytes;
+UINTN                           mBltLibBytesPerPixel;
+UINTN                           mBltLibWidthInPixels;
+UINTN                           mBltLibHeight;
+UINT8                           mBltLibLineBuffer[MAX_LINE_BUFFER_SIZE];
+UINT8                           *mBltLibFrameBuffer;
+EFI_GRAPHICS_PIXEL_FORMAT       mPixelFormat;
+EFI_PIXEL_BITMASK               mPixelBitMasks;
+INTN                            mPixelShl[4]; // R-G-B-Rsvd
+INTN                            mPixelShr[4]; // R-G-B-Rsvd
+
+
+VOID
+ConfigurePixelBitMaskFormat (
+  IN EFI_PIXEL_BITMASK          *BitMask
+  )
+{
+  UINTN   Loop;
+  UINT32  *Masks;
+  UINT32  MergedMasks;
+
+  MergedMasks = 0;
+  Masks = (UINT32*) BitMask;
+  for (Loop = 0; Loop < 3; Loop++) {
+    ASSERT ((Loop == 3) || (Masks[Loop] != 0));
+    ASSERT ((MergedMasks & Masks[Loop]) == 0);
+    mPixelShl[Loop] = HighBitSet32 (Masks[Loop]) - 23 + (Loop * 8);
+    if (mPixelShl[Loop] < 0) {
+      mPixelShr[Loop] = -mPixelShl[Loop];
+      mPixelShl[Loop] = 0;
+    } else {
+      mPixelShr[Loop] = 0;
+    }
+    MergedMasks = (UINT32) (MergedMasks | Masks[Loop]);
+    DEBUG ((EFI_D_INFO, "%d: shl:%d shr:%d mask:%x\n", Loop, mPixelShl[Loop], mPixelShr[Loop], Masks[Loop]));
+  }
+  MergedMasks = (UINT32) (MergedMasks | Masks[3]);
+
+  ASSERT (MergedMasks != 0);
+  mBltLibBytesPerPixel = (UINTN) ((HighBitSet32 (MergedMasks) + 7) / 8);
+
+  DEBUG ((EFI_D_INFO, "Bytes per pixel: %d\n", mBltLibBytesPerPixel));
+
+  CopyMem (&mPixelBitMasks, BitMask, sizeof (*BitMask));
+}
+
+
+/**
+  Configure the FrameBufferLib instance
+
+  @param[in] FrameBuffer      Pointer to the start of the frame buffer
+  @param[in] FrameBufferInfo  Describes the frame buffer characteristics
+
+  @retval  EFI_INVALID_PARAMETER - Invalid parameter
+  @retval  EFI_UNSUPPORTED - The BltLib does not support this configuration
+  @retval  EFI_SUCCESS - Blt operation success
+
+**/
+EFI_STATUS
+EFIAPI
+BltLibConfigure (
+  IN  VOID                                 *FrameBuffer,
+  IN  EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *FrameBufferInfo
+  )
+{
+  STATIC EFI_PIXEL_BITMASK  RgbPixelMasks =
+    { 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000 };
+  STATIC EFI_PIXEL_BITMASK  BgrPixelMasks =
+    { 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000 };
+
+  switch (FrameBufferInfo->PixelFormat) {
+  case PixelRedGreenBlueReserved8BitPerColor:
+    ConfigurePixelBitMaskFormat (&RgbPixelMasks);
+    break;
+  case PixelBlueGreenRedReserved8BitPerColor:
+    ConfigurePixelBitMaskFormat (&BgrPixelMasks);
+    break;
+  case PixelBitMask:
+    ConfigurePixelBitMaskFormat (&(FrameBufferInfo->PixelInformation));
+    break;
+  case PixelBltOnly:
+    ASSERT (FrameBufferInfo->PixelFormat != PixelBltOnly);
+    return EFI_UNSUPPORTED;
+  default:
+    ASSERT (FALSE);
+    return EFI_INVALID_PARAMETER;
+  }
+  mPixelFormat = FrameBufferInfo->PixelFormat;
+
+  mBltLibFrameBuffer = (UINT8*) FrameBuffer;
+  mBltLibWidthInPixels = (UINTN) FrameBufferInfo->HorizontalResolution;
+  mBltLibHeight = (UINTN) FrameBufferInfo->VerticalResolution;
+  mBltLibWidthInBytes = mBltLibWidthInPixels * mBltLibBytesPerPixel;
+
+  ASSERT (mBltLibWidthInBytes < sizeof (mBltLibLineBuffer));
+
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Performs a UEFI Graphics Output Protocol Blt operation.
+
+  @param[in,out] BltBuffer     - The data to transfer to screen
+  @param[in]     BltOperation  - The operation to perform
+  @param[in]     SourceX       - The X coordinate of the source for BltOperation
+  @param[in]     SourceY       - The Y coordinate of the source for BltOperation
+  @param[in]     DestinationX  - The X coordinate of the destination for BltOperation
+  @param[in]     DestinationY  - The Y coordinate of the destination for BltOperation
+  @param[in]     Width         - The width of a rectangle in the blt rectangle in pixels
+  @param[in]     Height        - The height of a rectangle in the blt rectangle in pixels
+  @param[in]     Delta         - Not used for EfiBltVideoFill and EfiBltVideoToVideo operation.
+                                 If a Delta of 0 is used, the entire BltBuffer will be operated on.
+                                 If a subrectangle of the BltBuffer is used, then Delta represents
+                                 the number of bytes in a row of the BltBuffer.
+
+  @retval  EFI_DEVICE_ERROR - A hardware error occured
+  @retval  EFI_INVALID_PARAMETER - Invalid parameter passed in
+  @retval  EFI_SUCCESS - Blt operation success
+
+**/
+EFI_STATUS
+EFIAPI
+BltLibGopBlt (
+  IN  EFI_GRAPHICS_OUTPUT_BLT_PIXEL         *BltBuffer, OPTIONAL
+  IN  EFI_GRAPHICS_OUTPUT_BLT_OPERATION     BltOperation,
+  IN  UINTN                                 SourceX,
+  IN  UINTN                                 SourceY,
+  IN  UINTN                                 DestinationX,
+  IN  UINTN                                 DestinationY,
+  IN  UINTN                                 Width,
+  IN  UINTN                                 Height,
+  IN  UINTN                                 Delta
+  )
+{
+  switch (BltOperation) {
+  case EfiBltVideoToBltBuffer:
+    return BltLibVideoToBltBufferEx (
+             BltBuffer,
+             SourceX,
+             SourceY,
+             DestinationX,
+             DestinationY,
+             Width,
+             Height,
+             Delta
+             );
+
+  case EfiBltVideoToVideo:
+    return BltLibVideoToVideo (
+             SourceX,
+             SourceY,
+             DestinationX,
+             DestinationY,
+             Width,
+             Height
+             );
+
+  case EfiBltVideoFill:
+    return BltLibVideoFill (
+             BltBuffer,
+             DestinationX,
+             DestinationY,
+             Width,
+             Height
+             );
+
+  case EfiBltBufferToVideo:
+    return BltLibBufferToVideoEx (
+             BltBuffer,
+             SourceX,
+             SourceY,
+             DestinationX,
+             DestinationY,
+             Width,
+             Height,
+             Delta
+             );
+  default:
+    return EFI_INVALID_PARAMETER;
+  }
+}
+
+
+/**
+  Performs a UEFI Graphics Output Protocol Blt Video Fill.
+
+  @param[in]  Color         Color to fill the region with
+  @param[in]  DestinationX  X location to start fill operation
+  @param[in]  DestinationY  Y location to start fill operation
+  @param[in]  Width         Width (in pixels) to fill
+  @param[in]  Height        Height to fill
+
+  @retval  EFI_DEVICE_ERROR - A hardware error occured
+  @retval  EFI_INVALID_PARAMETER - Invalid parameter passed in
+  @retval  EFI_SUCCESS - The sizes were returned
+
+**/
+EFI_STATUS
+EFIAPI
+BltLibVideoFill (
+  IN  EFI_GRAPHICS_OUTPUT_BLT_PIXEL         *Color,
+  IN  UINTN                                 DestinationX,
+  IN  UINTN                                 DestinationY,
+  IN  UINTN                                 Width,
+  IN  UINTN                                 Height
+  )
+{
+  UINTN                           DstY;
+  VOID                            *BltMemDst;
+  UINTN                           X;
+  UINT8                           Uint8;
+  UINT32                          Uint32;
+  UINT64                          WideFill;
+  BOOLEAN                         UseWideFill;
+  BOOLEAN                         LineBufferReady;
+  UINTN                           Offset;
+  UINTN                           WidthInBytes;
+  UINTN                           SizeInBytes;
+
+  //
+  // BltBuffer to Video: Source is BltBuffer, destination is Video
+  //
+  if (DestinationY + Height > mBltLibHeight) {
+    DEBUG ((EFI_D_INFO, "VideoFill: Past screen (Y)\n"));
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (DestinationX + Width > mBltLibWidthInPixels) {
+    DEBUG ((EFI_D_INFO, "VideoFill: Past screen (X)\n"));
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (Width == 0 || Height == 0) {
+    DEBUG ((EFI_D_INFO, "VideoFill: Width or Height is 0\n"));
+    return EFI_INVALID_PARAMETER;
+  }
+
+  WidthInBytes = Width * mBltLibBytesPerPixel;
+
+  Uint32 = *(UINT32*) Color;
+  WideFill =
+    (UINT32) (
+        (((Uint32 << mPixelShl[0]) >> mPixelShr[0]) & mPixelBitMasks.RedMask) |
+        (((Uint32 << mPixelShl[1]) >> mPixelShr[1]) & mPixelBitMasks.GreenMask) |
+        (((Uint32 << mPixelShl[2]) >> mPixelShr[2]) & mPixelBitMasks.BlueMask)
+      );
+  VDEBUG ((EFI_D_INFO, "VideoFill: color=0x%x, wide-fill=0x%x\n", Uint32, WideFill));
+
+  //
+  // If the size of the pixel data evenly divides the sizeof
+  // WideFill, then a wide fill operation can be used
+  //
+  UseWideFill = TRUE;
+  if ((sizeof (WideFill) % mBltLibBytesPerPixel) == 0) {
+    for (X = mBltLibBytesPerPixel; X < sizeof (WideFill); X++) {
+      ((UINT8*)&WideFill)[X] = ((UINT8*)&WideFill)[X % mBltLibBytesPerPixel];
+    }
+  } else {
+    //
+    // If all the bytes in the pixel are the same value, then use
+    // a wide fill operation.
+    //
+    for (
+      X = 1, Uint8 = ((UINT8*)&WideFill)[0];
+      X < mBltLibBytesPerPixel;
+      X++) {
+      if (Uint8 != ((UINT8*)&WideFill)[X]) {
+        UseWideFill = FALSE;
+        break;
+      }
+    }
+    if (UseWideFill) {
+      SetMem ((VOID*) &WideFill, sizeof (WideFill), Uint8);
+    }
+  }
+
+  if (UseWideFill && (DestinationX == 0) && (Width == mBltLibWidthInPixels)) {
+    VDEBUG ((EFI_D_INFO, "VideoFill (wide, one-shot)\n"));
+    Offset = DestinationY * mBltLibWidthInPixels;
+    Offset = mBltLibBytesPerPixel * Offset;
+    BltMemDst = (VOID*) (mBltLibFrameBuffer + Offset);
+    SizeInBytes = WidthInBytes * Height;
+    if (SizeInBytes >= 8) {
+      SetMem32 (BltMemDst, SizeInBytes & ~3, (UINT32) WideFill);
+      SizeInBytes = SizeInBytes & 3;
+    }
+    if (SizeInBytes > 0) {
+      SetMem (BltMemDst, SizeInBytes, (UINT8)(UINTN) WideFill);
+    }
+  } else {
+    LineBufferReady = FALSE;
+    for (DstY = DestinationY; DstY < (Height + DestinationY); DstY++) {
+      Offset = (DstY * mBltLibWidthInPixels) + DestinationX;
+      Offset = mBltLibBytesPerPixel * Offset;
+      BltMemDst = (VOID*) (mBltLibFrameBuffer + Offset);
+
+      if (UseWideFill && (((UINTN) BltMemDst & 7) == 0)) {
+        VDEBUG ((EFI_D_INFO, "VideoFill (wide)\n"));
+        SizeInBytes = WidthInBytes;
+        if (SizeInBytes >= 8) {
+          SetMem64 (BltMemDst, SizeInBytes & ~7, WideFill);
+          SizeInBytes = SizeInBytes & 7;
+        }
+        if (SizeInBytes > 0) {
+          CopyMem (BltMemDst, (VOID*) &WideFill, SizeInBytes);
+        }
+      } else {
+        VDEBUG ((EFI_D_INFO, "VideoFill (not wide)\n"));
+        if (!LineBufferReady) {
+          CopyMem (mBltLibLineBuffer, &WideFill, mBltLibBytesPerPixel);
+          for (X = 1; X < Width; ) {
+            CopyMem(
+              (mBltLibLineBuffer + (X * mBltLibBytesPerPixel)),
+              mBltLibLineBuffer,
+              MIN (X, Width - X) * mBltLibBytesPerPixel
+              );
+            X = X + MIN (X, Width - X);
+          }
+          LineBufferReady = TRUE;
+        }
+        CopyMem (BltMemDst, mBltLibLineBuffer, WidthInBytes);
+      }
+    }
+  }
+
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Performs a UEFI Graphics Output Protocol Blt Video to Buffer operation.
+
+  @param[out] BltBuffer     Output buffer for pixel color data
+  @param[in]  SourceX       X location within video
+  @param[in]  SourceY       Y location within video
+  @param[in]  Width         Width (in pixels)
+  @param[in]  Height        Height
+
+  @retval  EFI_DEVICE_ERROR - A hardware error occured
+  @retval  EFI_INVALID_PARAMETER - Invalid parameter passed in
+  @retval  EFI_SUCCESS - The sizes were returned
+
+**/
+EFI_STATUS
+EFIAPI
+BltLibVideoToBltBuffer (
+  OUT EFI_GRAPHICS_OUTPUT_BLT_PIXEL         *BltBuffer,
+  IN  UINTN                                 SourceX,
+  IN  UINTN                                 SourceY,
+  IN  UINTN                                 Width,
+  IN  UINTN                                 Height
+  )
+{
+  return BltLibVideoToBltBufferEx (
+           BltBuffer,
+           SourceX,
+           SourceY,
+           0,
+           0,
+           Width,
+           Height,
+           0
+           );
+}
+
+
+/**
+  Performs a UEFI Graphics Output Protocol Blt Video to Buffer operation
+  with extended parameters.
+
+  @param[out] BltBuffer     Output buffer for pixel color data
+  @param[in]  SourceX       X location within video
+  @param[in]  SourceY       Y location within video
+  @param[in]  DestinationX  X location within BltBuffer
+  @param[in]  DestinationY  Y location within BltBuffer
+  @param[in]  Width         Width (in pixels)
+  @param[in]  Height        Height
+  @param[in]  Delta         Number of bytes in a row of BltBuffer
+
+  @retval  EFI_DEVICE_ERROR - A hardware error occured
+  @retval  EFI_INVALID_PARAMETER - Invalid parameter passed in
+  @retval  EFI_SUCCESS - The sizes were returned
+
+**/
+EFI_STATUS
+EFIAPI
+BltLibVideoToBltBufferEx (
+  OUT EFI_GRAPHICS_OUTPUT_BLT_PIXEL         *BltBuffer,
+  IN  UINTN                                 SourceX,
+  IN  UINTN                                 SourceY,
+  IN  UINTN                                 DestinationX,
+  IN  UINTN                                 DestinationY,
+  IN  UINTN                                 Width,
+  IN  UINTN                                 Height,
+  IN  UINTN                                 Delta
+  )
+{
+  UINTN                           DstY;
+  UINTN                           SrcY;
+  EFI_GRAPHICS_OUTPUT_BLT_PIXEL   *Blt;
+  VOID                            *BltMemSrc;
+  VOID                            *BltMemDst;
+  UINTN                           X;
+  UINT32                          Uint32;
+  UINTN                           Offset;
+  UINTN                           WidthInBytes;
+
+  //
+  // Video to BltBuffer: Source is Video, destination is BltBuffer
+  //
+  if (SourceY + Height > mBltLibHeight) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (SourceX + Width > mBltLibWidthInPixels) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (Width == 0 || Height == 0) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // If Delta is zero, then the entire BltBuffer is being used, so Delta
+  // is the number of bytes in each row of BltBuffer.  Since BltBuffer is Width pixels size,
+  // the number of bytes in each row can be computed.
+  //
+  if (Delta == 0) {
+    Delta = Width * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL);
+  }
+
+  WidthInBytes = Width * mBltLibBytesPerPixel;
+
+  //
+  // Video to BltBuffer: Source is Video, destination is BltBuffer
+  //
+  for (SrcY = SourceY, DstY = DestinationY; DstY < (Height + DestinationY); SrcY++, DstY++) {
+
+    Offset = (SrcY * mBltLibWidthInPixels) + SourceX;
+    Offset = mBltLibBytesPerPixel * Offset;
+    BltMemSrc = (VOID *) (mBltLibFrameBuffer + Offset);
+
+    if (mPixelFormat == PixelBlueGreenRedReserved8BitPerColor) {
+      BltMemDst =
+        (VOID *) (
+            (UINT8 *) BltBuffer +
+            (DstY * Delta) +
+            (DestinationX * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL))
+          );
+    } else {
+      BltMemDst = (VOID *) mBltLibLineBuffer;
+    }
+
+    CopyMem (BltMemDst, BltMemSrc, WidthInBytes);
+
+    if (mPixelFormat != PixelBlueGreenRedReserved8BitPerColor) {
+      for (X = 0; X < Width; X++) {
+        Blt         = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *) ((UINT8 *) BltBuffer + (DstY * Delta) + (DestinationX + X) * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL));
+        Uint32 = *(UINT32*) (mBltLibLineBuffer + (X * mBltLibBytesPerPixel));
+        *(UINT32*) Blt =
+          (UINT32) (
+              (((Uint32 & mPixelBitMasks.RedMask)   >> mPixelShl[0]) << mPixelShr[0]) |
+              (((Uint32 & mPixelBitMasks.GreenMask) >> mPixelShl[1]) << mPixelShr[1]) |
+              (((Uint32 & mPixelBitMasks.BlueMask)  >> mPixelShl[2]) << mPixelShr[2])
+            );
+      }
+    }
+  }
+
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Performs a UEFI Graphics Output Protocol Blt Buffer to Video operation.
+
+  @param[in]  BltBuffer     Output buffer for pixel color data
+  @param[in]  DestinationX  X location within video
+  @param[in]  DestinationY  Y location within video
+  @param[in]  Width         Width (in pixels)
+  @param[in]  Height        Height
+
+  @retval  EFI_DEVICE_ERROR - A hardware error occured
+  @retval  EFI_INVALID_PARAMETER - Invalid parameter passed in
+  @retval  EFI_SUCCESS - The sizes were returned
+
+**/
+EFI_STATUS
+EFIAPI
+BltLibBufferToVideo (
+  IN  EFI_GRAPHICS_OUTPUT_BLT_PIXEL         *BltBuffer,
+  IN  UINTN                                 DestinationX,
+  IN  UINTN                                 DestinationY,
+  IN  UINTN                                 Width,
+  IN  UINTN                                 Height
+  )
+{
+  return BltLibBufferToVideoEx (
+           BltBuffer,
+           0,
+           0,
+           DestinationX,
+           DestinationY,
+           Width,
+           Height,
+           0
+           );
+}
+
+
+/**
+  Performs a UEFI Graphics Output Protocol Blt Buffer to Video operation
+  with extended parameters.
+
+  @param[in]  BltBuffer     Output buffer for pixel color data
+  @param[in]  SourceX       X location within BltBuffer
+  @param[in]  SourceY       Y location within BltBuffer
+  @param[in]  DestinationX  X location within video
+  @param[in]  DestinationY  Y location within video
+  @param[in]  Width         Width (in pixels)
+  @param[in]  Height        Height
+  @param[in]  Delta         Number of bytes in a row of BltBuffer
+
+  @retval  EFI_DEVICE_ERROR - A hardware error occured
+  @retval  EFI_INVALID_PARAMETER - Invalid parameter passed in
+  @retval  EFI_SUCCESS - The sizes were returned
+
+**/
+EFI_STATUS
+EFIAPI
+BltLibBufferToVideoEx (
+  IN  EFI_GRAPHICS_OUTPUT_BLT_PIXEL         *BltBuffer,
+  IN  UINTN                                 SourceX,
+  IN  UINTN                                 SourceY,
+  IN  UINTN                                 DestinationX,
+  IN  UINTN                                 DestinationY,
+  IN  UINTN                                 Width,
+  IN  UINTN                                 Height,
+  IN  UINTN                                 Delta
+  )
+{
+  UINTN                           DstY;
+  UINTN                           SrcY;
+  EFI_GRAPHICS_OUTPUT_BLT_PIXEL   *Blt;
+  VOID                            *BltMemSrc;
+  VOID                            *BltMemDst;
+  UINTN                           X;
+  UINT32                          Uint32;
+  UINTN                           Offset;
+  UINTN                           WidthInBytes;
+
+  //
+  // BltBuffer to Video: Source is BltBuffer, destination is Video
+  //
+  if (DestinationY + Height > mBltLibHeight) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (DestinationX + Width > mBltLibWidthInPixels) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (Width == 0 || Height == 0) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // If Delta is zero, then the entire BltBuffer is being used, so Delta
+  // is the number of bytes in each row of BltBuffer.  Since BltBuffer is Width pixels size,
+  // the number of bytes in each row can be computed.
+  //
+  if (Delta == 0) {
+    Delta = Width * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL);
+  }
+
+  WidthInBytes = Width * mBltLibBytesPerPixel;
+
+  for (SrcY = SourceY, DstY = DestinationY; SrcY < (Height + SourceY); SrcY++, DstY++) {
+
+    Offset = (DstY * mBltLibWidthInPixels) + DestinationX;
+    Offset = mBltLibBytesPerPixel * Offset;
+    BltMemDst = (VOID*) (mBltLibFrameBuffer + Offset);
+
+    if (mPixelFormat == PixelBlueGreenRedReserved8BitPerColor) {
+      BltMemSrc = (VOID *) ((UINT8 *) BltBuffer + (SrcY * Delta));
+    } else {
+      for (X = 0; X < Width; X++) {
+        Blt =
+          (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *) (
+              (UINT8 *) BltBuffer +
+              (SrcY * Delta) +
+              ((SourceX + X) * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL))
+            );
+        Uint32 = *(UINT32*) Blt;
+        *(UINT32*) (mBltLibLineBuffer + (X * mBltLibBytesPerPixel)) =
+          (UINT32) (
+              (((Uint32 << mPixelShl[0]) >> mPixelShr[0]) & mPixelBitMasks.RedMask) |
+              (((Uint32 << mPixelShl[1]) >> mPixelShr[1]) & mPixelBitMasks.GreenMask) |
+              (((Uint32 << mPixelShl[2]) >> mPixelShr[2]) & mPixelBitMasks.BlueMask)
+            );
+      }
+      BltMemSrc = (VOID *) mBltLibLineBuffer;
+    }
+
+    CopyMem (BltMemDst, BltMemSrc, WidthInBytes);
+  }
+
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Performs a UEFI Graphics Output Protocol Blt Video to Video operation
+
+  @param[in]  SourceX       X location within video
+  @param[in]  SourceY       Y location within video
+  @param[in]  DestinationX  X location within video
+  @param[in]  DestinationY  Y location within video
+  @param[in]  Width         Width (in pixels)
+  @param[in]  Height        Height
+
+  @retval  EFI_DEVICE_ERROR - A hardware error occured
+  @retval  EFI_INVALID_PARAMETER - Invalid parameter passed in
+  @retval  EFI_SUCCESS - The sizes were returned
+
+**/
+EFI_STATUS
+EFIAPI
+BltLibVideoToVideo (
+  IN  UINTN                                 SourceX,
+  IN  UINTN                                 SourceY,
+  IN  UINTN                                 DestinationX,
+  IN  UINTN                                 DestinationY,
+  IN  UINTN                                 Width,
+  IN  UINTN                                 Height
+  )
+{
+  VOID                            *BltMemSrc;
+  VOID                            *BltMemDst;
+  UINTN                           Offset;
+  UINTN                           WidthInBytes;
+  INTN                            LineStride;
+
+  //
+  // Video to Video: Source is Video, destination is Video
+  //
+  if (SourceY + Height > mBltLibHeight) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (SourceX + Width > mBltLibWidthInPixels) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (DestinationY + Height > mBltLibHeight) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (DestinationX + Width > mBltLibWidthInPixels) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (Width == 0 || Height == 0) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  WidthInBytes = Width * mBltLibBytesPerPixel;
+
+  Offset = (SourceY * mBltLibWidthInPixels) + SourceX;
+  Offset = mBltLibBytesPerPixel * Offset;
+  BltMemSrc = (VOID *) (mBltLibFrameBuffer + Offset);
+
+  Offset = (DestinationY * mBltLibWidthInPixels) + DestinationX;
+  Offset = mBltLibBytesPerPixel * Offset;
+  BltMemDst = (VOID *) (mBltLibFrameBuffer + Offset);
+
+  LineStride = mBltLibWidthInBytes;
+  if ((UINTN) BltMemDst > (UINTN) BltMemSrc) {
+    LineStride = -LineStride;
+  }
+
+  while (Height > 0) {
+    CopyMem (BltMemDst, BltMemSrc, WidthInBytes);
+
+    BltMemSrc = (VOID*) ((UINT8*) BltMemSrc + LineStride);
+    BltMemDst = (VOID*) ((UINT8*) BltMemDst + LineStride);
+    Height--;
+  }
+
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Returns the sizes related to the video device
+
+  @param[out]  Width   Width (in pixels)
+  @param[out]  Height  Height (in pixels)
+
+  @retval  EFI_INVALID_PARAMETER - Invalid parameter passed in
+  @retval  EFI_SUCCESS - The sizes were returned
+
+**/
+EFI_STATUS
+EFIAPI
+BltLibGetSizes (
+  OUT UINTN                                 *Width,  OPTIONAL
+  OUT UINTN                                 *Height  OPTIONAL
+  )
+{
+  if (Width != NULL) {
+    *Width = mBltLibWidthInPixels;
+  }
+  if (Height != NULL) {
+    *Height = mBltLibHeight;
+  }
+
+  return EFI_SUCCESS;
+}
+
diff --git a/Drivers/OptionRomPkg/Library/FrameBufferBltLib/FrameBufferBltLib.inf b/Drivers/OptionRomPkg/Library/FrameBufferBltLib/FrameBufferBltLib.inf
new file mode 100644
index 0000000000..7af8a1baa0
--- /dev/null
+++ b/Drivers/OptionRomPkg/Library/FrameBufferBltLib/FrameBufferBltLib.inf
@@ -0,0 +1,29 @@
+## @file
+#  FrameBufferBltLib - Library to perform blt operations on a frame buffer.
+#
+#  Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = FrameBufferBltLib
+  FILE_GUID                      = 2a40f516-c852-4baa-b7a8-0e9ea090d659
+  MODULE_TYPE                    = BASE
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = BltLib
+
+[Sources.common]
+  FrameBufferBltLib.c
+
+[LibraryClasses]
+  BaseLib
+  BaseMemoryLib
+  DebugLib
+
+[Packages]
+  MdePkg/MdePkg.dec
+  OptionRomPkg/OptionRomPkg.dec
+
diff --git a/Drivers/OptionRomPkg/Library/GopBltLib/GopBltLib.c b/Drivers/OptionRomPkg/Library/GopBltLib/GopBltLib.c
new file mode 100644
index 0000000000..f28affd26b
--- /dev/null
+++ b/Drivers/OptionRomPkg/Library/GopBltLib/GopBltLib.c
@@ -0,0 +1,449 @@
+/** @file
+  GopBltLib - Library to perform blt using the UEFI Graphics Output Protocol.
+
+  Copyright (c) 2007 - 2011, Intel Corporation. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "PiDxe.h"
+
+#include <Protocol/GraphicsOutput.h>
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/BltLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+
+EFI_GRAPHICS_OUTPUT_PROTOCOL         *mGop = NULL;
+
+
+/**
+  Configure the FrameBufferLib instance
+
+  @param[in] FrameBuffer      Pointer to the start of the frame buffer
+  @param[in] FrameBufferInfo  Describes the frame buffer characteristics
+
+  @retval  EFI_INVALID_PARAMETER - Invalid parameter
+  @retval  EFI_UNSUPPORTED - The BltLib does not support this configuration
+  @retval  EFI_SUCCESS - Blt operation success
+
+**/
+EFI_STATUS
+EFIAPI
+BltLibConfigure (
+  IN  VOID                                 *FrameBuffer,
+  IN  EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *FrameBufferInfo
+  )
+{
+  EFI_STATUS                      Status;
+  EFI_HANDLE                      *HandleBuffer;
+  UINTN                           HandleCount;
+  UINTN                           Index;
+  EFI_GRAPHICS_OUTPUT_PROTOCOL    *Gop;
+
+  Status = gBS->LocateHandleBuffer (
+                  ByProtocol,
+                  &gEfiGraphicsOutputProtocolGuid,
+                  NULL,
+                  &HandleCount,
+                  &HandleBuffer
+                  );
+  if (!EFI_ERROR (Status)) {
+    for (Index = 0; Index < HandleCount; Index++) {
+      Status = gBS->HandleProtocol (
+                      HandleBuffer[Index],
+                      &gEfiGraphicsOutputProtocolGuid,
+                      (VOID*) &Gop
+                      );
+      if (!EFI_ERROR (Status) &&
+          (FrameBuffer == (VOID*)(UINTN) Gop->Mode->FrameBufferBase)) {
+        mGop = Gop;
+        FreePool (HandleBuffer);
+        return EFI_SUCCESS;
+      }
+    }
+
+    FreePool (HandleBuffer);
+  }
+
+  return EFI_UNSUPPORTED;
+}
+
+
+/**
+  Performs a UEFI Graphics Output Protocol Blt operation.
+
+  @param[in,out] BltBuffer     - The data to transfer to screen
+  @param[in]     BltOperation  - The operation to perform
+  @param[in]     SourceX       - The X coordinate of the source for BltOperation
+  @param[in]     SourceY       - The Y coordinate of the source for BltOperation
+  @param[in]     DestinationX  - The X coordinate of the destination for BltOperation
+  @param[in]     DestinationY  - The Y coordinate of the destination for BltOperation
+  @param[in]     Width         - The width of a rectangle in the blt rectangle in pixels
+  @param[in]     Height        - The height of a rectangle in the blt rectangle in pixels
+  @param[in]     Delta         - Not used for EfiBltVideoFill and EfiBltVideoToVideo operation.
+                                 If a Delta of 0 is used, the entire BltBuffer will be operated on.
+                                 If a subrectangle of the BltBuffer is used, then Delta represents
+                                 the number of bytes in a row of the BltBuffer.
+
+  @retval  EFI_DEVICE_ERROR - A hardware error occured
+  @retval  EFI_INVALID_PARAMETER - Invalid parameter passed in
+  @retval  EFI_SUCCESS - Blt operation success
+
+**/
+EFI_STATUS
+InternalGopBltCommon (
+  IN  EFI_GRAPHICS_OUTPUT_BLT_PIXEL         *BltBuffer, OPTIONAL
+  IN  EFI_GRAPHICS_OUTPUT_BLT_OPERATION     BltOperation,
+  IN  UINTN                                 SourceX,
+  IN  UINTN                                 SourceY,
+  IN  UINTN                                 DestinationX,
+  IN  UINTN                                 DestinationY,
+  IN  UINTN                                 Width,
+  IN  UINTN                                 Height,
+  IN  UINTN                                 Delta
+  )
+{
+  if (mGop == NULL) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  return mGop->Blt (
+                 mGop,
+                 BltBuffer,
+                 BltOperation,
+                 SourceX,
+                 SourceY,
+                 DestinationX,
+                 DestinationY,
+                 Width,
+                 Height,
+                 Delta
+                 );
+}
+
+
+/**
+  Performs a UEFI Graphics Output Protocol Blt operation.
+
+  @param[in,out] BltBuffer     - The data to transfer to screen
+  @param[in]     BltOperation  - The operation to perform
+  @param[in]     SourceX       - The X coordinate of the source for BltOperation
+  @param[in]     SourceY       - The Y coordinate of the source for BltOperation
+  @param[in]     DestinationX  - The X coordinate of the destination for BltOperation
+  @param[in]     DestinationY  - The Y coordinate of the destination for BltOperation
+  @param[in]     Width         - The width of a rectangle in the blt rectangle in pixels
+  @param[in]     Height        - The height of a rectangle in the blt rectangle in pixels
+  @param[in]     Delta         - Not used for EfiBltVideoFill and EfiBltVideoToVideo operation.
+                                 If a Delta of 0 is used, the entire BltBuffer will be operated on.
+                                 If a subrectangle of the BltBuffer is used, then Delta represents
+                                 the number of bytes in a row of the BltBuffer.
+
+  @retval  EFI_DEVICE_ERROR - A hardware error occured
+  @retval  EFI_INVALID_PARAMETER - Invalid parameter passed in
+  @retval  EFI_SUCCESS - Blt operation success
+
+**/
+EFI_STATUS
+EFIAPI
+BltLibGopBlt (
+  IN  EFI_GRAPHICS_OUTPUT_BLT_PIXEL         *BltBuffer, OPTIONAL
+  IN  EFI_GRAPHICS_OUTPUT_BLT_OPERATION     BltOperation,
+  IN  UINTN                                 SourceX,
+  IN  UINTN                                 SourceY,
+  IN  UINTN                                 DestinationX,
+  IN  UINTN                                 DestinationY,
+  IN  UINTN                                 Width,
+  IN  UINTN                                 Height,
+  IN  UINTN                                 Delta
+  )
+{
+  return InternalGopBltCommon (
+           BltBuffer,
+           BltOperation,
+           SourceX,
+           SourceY,
+           DestinationX,
+           DestinationY,
+           Width,
+           Height,
+           Delta
+           );
+}
+
+
+/**
+  Performs a UEFI Graphics Output Protocol Blt Video Fill.
+
+  @param[in]  Color         Color to fill the region with
+  @param[in]  DestinationX  X location to start fill operation
+  @param[in]  DestinationY  Y location to start fill operation
+  @param[in]  Width         Width (in pixels) to fill
+  @param[in]  Height        Height to fill
+
+  @retval  EFI_DEVICE_ERROR - A hardware error occured
+  @retval  EFI_INVALID_PARAMETER - Invalid parameter passed in
+  @retval  EFI_SUCCESS - The sizes were returned
+
+**/
+EFI_STATUS
+EFIAPI
+BltLibVideoFill (
+  IN  EFI_GRAPHICS_OUTPUT_BLT_PIXEL         *Color,
+  IN  UINTN                                 DestinationX,
+  IN  UINTN                                 DestinationY,
+  IN  UINTN                                 Width,
+  IN  UINTN                                 Height
+  )
+{
+  return InternalGopBltCommon (
+           Color,
+           EfiBltVideoFill,
+           0,
+           0,
+           DestinationX,
+           DestinationY,
+           Width,
+           Height,
+           0
+           );
+}
+
+
+/**
+  Performs a UEFI Graphics Output Protocol Blt Video to Buffer operation.
+
+  @param[out] BltBuffer     Output buffer for pixel color data
+  @param[in]  SourceX       X location within video
+  @param[in]  SourceY       Y location within video
+  @param[in]  Width         Width (in pixels)
+  @param[in]  Height        Height
+
+  @retval  EFI_DEVICE_ERROR - A hardware error occured
+  @retval  EFI_INVALID_PARAMETER - Invalid parameter passed in
+  @retval  EFI_SUCCESS - The sizes were returned
+
+**/
+EFI_STATUS
+EFIAPI
+BltLibVideoToBltBuffer (
+  OUT EFI_GRAPHICS_OUTPUT_BLT_PIXEL         *BltBuffer,
+  IN  UINTN                                 SourceX,
+  IN  UINTN                                 SourceY,
+  IN  UINTN                                 Width,
+  IN  UINTN                                 Height
+  )
+{
+  return InternalGopBltCommon (
+           BltBuffer,
+           EfiBltVideoToBltBuffer,
+           SourceX,
+           SourceY,
+           0,
+           0,
+           Width,
+           Height,
+           0
+           );
+}
+
+
+/**
+  Performs a UEFI Graphics Output Protocol Blt Video to Buffer operation
+  with extended parameters.
+
+  @param[out] BltBuffer     Output buffer for pixel color data
+  @param[in]  SourceX       X location within video
+  @param[in]  SourceY       Y location within video
+  @param[in]  DestinationX  X location within BltBuffer
+  @param[in]  DestinationY  Y location within BltBuffer
+  @param[in]  Width         Width (in pixels)
+  @param[in]  Height        Height
+  @param[in]  Delta         Number of bytes in a row of BltBuffer
+
+  @retval  EFI_DEVICE_ERROR - A hardware error occured
+  @retval  EFI_INVALID_PARAMETER - Invalid parameter passed in
+  @retval  EFI_SUCCESS - The sizes were returned
+
+**/
+EFI_STATUS
+EFIAPI
+BltLibVideoToBltBufferEx (
+  OUT EFI_GRAPHICS_OUTPUT_BLT_PIXEL         *BltBuffer,
+  IN  UINTN                                 SourceX,
+  IN  UINTN                                 SourceY,
+  IN  UINTN                                 DestinationX,
+  IN  UINTN                                 DestinationY,
+  IN  UINTN                                 Width,
+  IN  UINTN                                 Height,
+  IN  UINTN                                 Delta
+  )
+{
+  return InternalGopBltCommon (
+           BltBuffer,
+           EfiBltVideoToBltBuffer,
+           SourceX,
+           SourceY,
+           DestinationX,
+           DestinationY,
+           Width,
+           Height,
+           Delta
+           );
+}
+
+
+/**
+  Performs a UEFI Graphics Output Protocol Blt Buffer to Video operation.
+
+  @param[in]  BltBuffer     Output buffer for pixel color data
+  @param[in]  DestinationX  X location within video
+  @param[in]  DestinationY  Y location within video
+  @param[in]  Width         Width (in pixels)
+  @param[in]  Height        Height
+
+  @retval  EFI_DEVICE_ERROR - A hardware error occured
+  @retval  EFI_INVALID_PARAMETER - Invalid parameter passed in
+  @retval  EFI_SUCCESS - The sizes were returned
+
+**/
+EFI_STATUS
+EFIAPI
+BltLibBufferToVideo (
+  IN  EFI_GRAPHICS_OUTPUT_BLT_PIXEL         *BltBuffer,
+  IN  UINTN                                 DestinationX,
+  IN  UINTN                                 DestinationY,
+  IN  UINTN                                 Width,
+  IN  UINTN                                 Height
+  )
+{
+  return InternalGopBltCommon (
+           BltBuffer,
+           EfiBltBufferToVideo,
+           0,
+           0,
+           DestinationX,
+           DestinationY,
+           Width,
+           Height,
+           0
+           );
+}
+
+
+/**
+  Performs a UEFI Graphics Output Protocol Blt Buffer to Video operation
+  with extended parameters.
+
+  @param[in]  BltBuffer     Output buffer for pixel color data
+  @param[in]  SourceX       X location within BltBuffer
+  @param[in]  SourceY       Y location within BltBuffer
+  @param[in]  DestinationX  X location within video
+  @param[in]  DestinationY  Y location within video
+  @param[in]  Width         Width (in pixels)
+  @param[in]  Height        Height
+  @param[in]  Delta         Number of bytes in a row of BltBuffer
+
+  @retval  EFI_DEVICE_ERROR - A hardware error occured
+  @retval  EFI_INVALID_PARAMETER - Invalid parameter passed in
+  @retval  EFI_SUCCESS - The sizes were returned
+
+**/
+EFI_STATUS
+EFIAPI
+BltLibBufferToVideoEx (
+  IN  EFI_GRAPHICS_OUTPUT_BLT_PIXEL         *BltBuffer,
+  IN  UINTN                                 SourceX,
+  IN  UINTN                                 SourceY,
+  IN  UINTN                                 DestinationX,
+  IN  UINTN                                 DestinationY,
+  IN  UINTN                                 Width,
+  IN  UINTN                                 Height,
+  IN  UINTN                                 Delta
+  )
+{
+  return InternalGopBltCommon (
+           BltBuffer,
+           EfiBltBufferToVideo,
+           SourceX,
+           SourceY,
+           DestinationX,
+           DestinationY,
+           Width,
+           Height,
+           Delta
+           );
+}
+
+
+/**
+  Performs a UEFI Graphics Output Protocol Blt Video to Video operation
+
+  @param[in]  SourceX       X location within video
+  @param[in]  SourceY       Y location within video
+  @param[in]  DestinationX  X location within video
+  @param[in]  DestinationY  Y location within video
+  @param[in]  Width         Width (in pixels)
+  @param[in]  Height        Height
+
+  @retval  EFI_DEVICE_ERROR - A hardware error occured
+  @retval  EFI_INVALID_PARAMETER - Invalid parameter passed in
+  @retval  EFI_SUCCESS - The sizes were returned
+
+**/
+EFI_STATUS
+EFIAPI
+BltLibVideoToVideo (
+  IN  UINTN                                 SourceX,
+  IN  UINTN                                 SourceY,
+  IN  UINTN                                 DestinationX,
+  IN  UINTN                                 DestinationY,
+  IN  UINTN                                 Width,
+  IN  UINTN                                 Height
+  )
+{
+  return InternalGopBltCommon (
+           NULL,
+           EfiBltVideoToVideo,
+           SourceX,
+           SourceY,
+           DestinationX,
+           DestinationY,
+           Width,
+           Height,
+           0
+           );
+}
+
+/**
+  Returns the sizes related to the video device
+
+  @param[out]  Width   Width (in pixels)
+  @param[out]  Height  Height (in pixels)
+
+  @retval  EFI_INVALID_PARAMETER - Invalid parameter passed in
+  @retval  EFI_SUCCESS - The sizes were returned
+
+**/
+EFI_STATUS
+EFIAPI
+BltLibGetSizes (
+  OUT UINTN                                 *Width,  OPTIONAL
+  OUT UINTN                                 *Height  OPTIONAL
+  )
+{
+  ASSERT (mGop != NULL);
+
+  if (Width != NULL) {
+    *Width = mGop->Mode->Info->HorizontalResolution;
+  }
+  if (Height != NULL) {
+    *Height = mGop->Mode->Info->VerticalResolution;
+  }
+
+  return EFI_SUCCESS;
+}
+
diff --git a/Drivers/OptionRomPkg/Library/GopBltLib/GopBltLib.inf b/Drivers/OptionRomPkg/Library/GopBltLib/GopBltLib.inf
new file mode 100644
index 0000000000..c5559133f9
--- /dev/null
+++ b/Drivers/OptionRomPkg/Library/GopBltLib/GopBltLib.inf
@@ -0,0 +1,31 @@
+## @file
+#  GopBltLib - Library to perform blt using the UEFI Graphics Output Protocol.
+#
+#  Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = GopBltLib
+  FILE_GUID                      = b75b91f0-a0b4-42fe-ba62-849027999b39
+  MODULE_TYPE                    = BASE
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = BltLib
+
+[Sources.common]
+  GopBltLib.c
+
+[LibraryClasses]
+  BaseLib
+  BaseMemoryLib
+  DebugLib
+  MemoryAllocationLib
+  UefiBootServicesTableLib
+
+[Packages]
+  MdePkg/MdePkg.dec
+  OptionRomPkg/OptionRomPkg.dec
+
diff --git a/Drivers/OptionRomPkg/OptionRomPkg.dec b/Drivers/OptionRomPkg/OptionRomPkg.dec
new file mode 100644
index 0000000000..6881f3648e
--- /dev/null
+++ b/Drivers/OptionRomPkg/OptionRomPkg.dec
@@ -0,0 +1,41 @@
+## @file
+# Option Rom Package Reference Implementations.
+#
+# This package is designed to interoperate with the EDK II open source project
+# at http://www.tianocore.org, and this package is required to build PCI compliant
+# Option ROM image for all CPU architectures, including EBC target.
+# A single driver can support mixes of EFI 1.1, UEFI 2.0 and UEFI 2.1.
+#
+# Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  DEC_SPECIFICATION              = 0x00010005
+  PACKAGE_NAME                   = OptionRomPkg
+  PACKAGE_GUID                   = AA3865E8-7F30-4f59-8696-99F560101852
+  PACKAGE_VERSION                = 0.1
+
+[Includes]
+  Include
+
+[LibraryClasses]
+  ##  @libraryclass  Provides an interface for performing UEFI Graphics
+  ##                 Output Protocol Video blt operations
+  ##
+  BltLib|Include/Library/BltLib.h
+
+[Guids]
+  gOptionRomPkgTokenSpaceGuid = { 0x1e43298f, 0x3478, 0x41a7, { 0xb5, 0x77, 0x86, 0x6, 0x46, 0x35, 0xc7, 0x28 } }
+
+[PcdsFeatureFlag]
+  gOptionRomPkgTokenSpaceGuid.PcdSupportScsiPassThru|TRUE|BOOLEAN|0x00010001
+  gOptionRomPkgTokenSpaceGuid.PcdSupportExtScsiPassThru|TRUE|BOOLEAN|0x00010002
+  gOptionRomPkgTokenSpaceGuid.PcdSupportGop|TRUE|BOOLEAN|0x00010004
+  gOptionRomPkgTokenSpaceGuid.PcdSupportUga|TRUE|BOOLEAN|0x00010005
+
+[PcdsFixedAtBuild, PcdsPatchableInModule]
+  gOptionRomPkgTokenSpaceGuid.PcdDriverSupportedEfiVersion|0x0002000a|UINT32|0x00010003
+
diff --git a/Drivers/OptionRomPkg/OptionRomPkg.dsc b/Drivers/OptionRomPkg/OptionRomPkg.dsc
new file mode 100644
index 0000000000..bea64b585e
--- /dev/null
+++ b/Drivers/OptionRomPkg/OptionRomPkg.dsc
@@ -0,0 +1,113 @@
+## @file
+# Option Rom Package build validation file for All Architectures.
+#
+# This package is designed to interoperate with the EDK II open source project
+# at http://www.tianocore.org, and this package is required to build PCI compliant
+# Option ROM image for all CPU architectures, including EBC target.
+# A single driver can support mixes of EFI 1.1, UEFI 2.0 and UEFI 2.1.
+#
+# Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
+# Copyright (c) 2016, Linaro Ltd. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+################################################################################
+#
+# Defines Section - statements that will be processed to create a Makefile.
+#
+################################################################################
+[Defines]
+  PLATFORM_NAME                  = OptionRomPkg
+  PLATFORM_GUID                  = C7B25F37-B1F4-4c46-99CB-3EA7DCF5FCDC
+  PLATFORM_VERSION               = 0.1
+  DSC_SPECIFICATION              = 0x00010005
+  OUTPUT_DIRECTORY               = Build/OptionRomPkg
+  SUPPORTED_ARCHITECTURES        = IA32|X64|EBC|ARM|AARCH64
+  BUILD_TARGETS                  = DEBUG|RELEASE
+  SKUID_IDENTIFIER               = DEFAULT
+
+################################################################################
+#
+# SKU Identification section - list of all SKU IDs supported by this
+#                              Platform.
+#
+################################################################################
+[SkuIds]
+  0|DEFAULT              # The entry: 0|DEFAULT is reserved and always required.
+
+[LibraryClasses]
+  DebugLib|MdePkg/Library/UefiDebugLibStdErr/UefiDebugLibStdErr.inf
+  DebugPrintErrorLevelLib|MdePkg/Library/BaseDebugPrintErrorLevelLib/BaseDebugPrintErrorLevelLib.inf  
+  BaseLib|MdePkg/Library/BaseLib/BaseLib.inf
+  BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf
+  BltLib|OptionRomPkg/Library/GopBltLib/GopBltLib.inf
+  PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf
+  TimerLib|MdePkg/Library/BaseTimerLibNullTemplate/BaseTimerLibNullTemplate.inf
+  UefiBootServicesTableLib|MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.inf
+  UefiRuntimeServicesTableLib|MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.inf
+  UefiDriverEntryPoint|MdePkg/Library/UefiDriverEntryPoint/UefiDriverEntryPoint.inf
+  UefiLib|MdePkg/Library/UefiLib/UefiLib.inf
+  PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf
+  MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf
+  DevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf
+  UefiApplicationEntryPoint|MdePkg/Library/UefiApplicationEntryPoint/UefiApplicationEntryPoint.inf
+  UefiRuntimeLib|MdePkg/Library/UefiRuntimeLib/UefiRuntimeLib.inf
+
+[LibraryClasses.AARCH64, LibraryClasses.ARM]
+  NULL|ArmPkg/Library/CompilerIntrinsicsLib/CompilerIntrinsicsLib.inf
+
+[LibraryClasses.ARM]
+  NULL|MdePkg/Library/BaseStackCheckLib/BaseStackCheckLib.inf
+
+################################################################################
+#
+# Pcd Section - list of all EDK II PCD Entries defined by this Platform
+#
+################################################################################
+[PcdsFeatureFlag]
+  gOptionRomPkgTokenSpaceGuid.PcdSupportScsiPassThru|TRUE
+  gOptionRomPkgTokenSpaceGuid.PcdSupportExtScsiPassThru|TRUE
+
+[PcdsFixedAtBuild]
+  gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0x27
+  gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0x80000042
+  gEfiMdePkgTokenSpaceGuid.PcdDebugClearMemoryValue|0x0
+  gEfiMdePkgTokenSpaceGuid.PcdMaximumUnicodeStringLength|0x0
+  gEfiMdePkgTokenSpaceGuid.PcdMaximumAsciiStringLength|0x0
+  gEfiMdePkgTokenSpaceGuid.PcdMaximumLinkedListLength|0x0
+  gOptionRomPkgTokenSpaceGuid.PcdDriverSupportedEfiVersion|0x0002000a # EFI_2_10_SYSTEM_TABLE_REVISION
+
+###################################################################################################
+#
+# Components Section - list of the modules and components that will be processed by compilation
+#                      tools and the EDK II tools to generate PE32/PE32+/Coff image files.
+#
+# Note: The EDK II DSC file is not used to specify how compiled binary images get placed
+#       into firmware volume images. This section is just a list of modules to compile from
+#       source into UEFI-compliant binaries.
+#       It is the FDF file that contains information on combining binary files into firmware
+#       volume images, whose concept is beyond UEFI and is described in PI specification.
+#       Binary modules do not need to be listed in this section, as they should be
+#       specified in the FDF file. For example: Shell binary (Shell_Full.efi), FAT binary (Fat.efi),
+#       Logo (Logo.bmp), and etc.
+#       There may also be modules listed in this section that are not required in the FDF file,
+#       When a module listed here is excluded from FDF file, then UEFI-compliant binary will be
+#       generated for it, but the binary will not be put into any firmware volume.
+#
+###################################################################################################
+
+[Components]
+  OptionRomPkg/Library/FrameBufferBltLib/FrameBufferBltLib.inf
+  OptionRomPkg/Library/GopBltLib/GopBltLib.inf
+
+  OptionRomPkg/AtapiPassThruDxe/AtapiPassThruDxe.inf
+  OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430Dxe.inf
+  OptionRomPkg/UndiRuntimeDxe/UndiRuntimeDxe.inf
+  OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe/FtdiUsbSerialDxe.inf
+  OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772/Ax88772.inf
+  OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/Ax88772b.inf
+
+[Components.IA32, Components.X64]
+  OptionRomPkg/Application/BltLibSample/BltLibSample.inf
diff --git a/Drivers/OptionRomPkg/ReadMe.txt b/Drivers/OptionRomPkg/ReadMe.txt
new file mode 100644
index 0000000000..99f97896da
--- /dev/null
+++ b/Drivers/OptionRomPkg/ReadMe.txt
@@ -0,0 +1,17 @@
+AtapiPassThru:
+  For now, AtapiPassThru driver in this package is to test Scsi Bus support:
+  ScsiBus driver should support both/either ScsiPassThru and ExtScsiPassThru
+  installed on a controller handle.
+   
+  AtapiPassThru driver in this package can selectively produce ScsiPassThru
+  and/or ExtScsiPassThru protocol based on feature flags of PcdSupportScsiPassThru
+  and PcdSupportExtScsiPassThru.
+
+CirrusLogic5430:
+  Sample implementation of UGA Draw or Graphics Output Protocol for the Cirrus
+  Logic 5430 family of PCI video card. It provides reference code for the GOP/UGA,
+  Component Name (2), EFI driver supported Verison protocol.
+
+Build Validation:
+ICC             IA32 X64 IPF
+
diff --git a/Drivers/OptionRomPkg/UndiRuntimeDxe/ComponentName.c b/Drivers/OptionRomPkg/UndiRuntimeDxe/ComponentName.c
new file mode 100644
index 0000000000..b310143271
--- /dev/null
+++ b/Drivers/OptionRomPkg/UndiRuntimeDxe/ComponentName.c
@@ -0,0 +1,359 @@
+/** @file
+    UEFI Component Name(2) protocol implementation for EFI UNDI32 driver.
+
+Copyright (c) 2012, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+
+#include "Undi32.h"
+
+//
+// EFI Component Name Functions
+//
+/**
+  Retrieves a Unicode string that is the user readable name of the driver.
+
+  This function retrieves the user readable name of a driver in the form of a
+  Unicode string. If the driver specified by This has a user readable name in
+  the language specified by Language, then a pointer to the driver name is
+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+  by This does not support the language specified by Language,
+  then EFI_UNSUPPORTED is returned.
+
+  @param  This[in]              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+                                EFI_COMPONENT_NAME_PROTOCOL instance.
+
+  @param  Language[in]          A pointer to a Null-terminated ASCII string
+                                array indicating the language. This is the
+                                language of the driver name that the caller is
+                                requesting, and it must match one of the
+                                languages specified in SupportedLanguages. The
+                                number of languages supported by a driver is up
+                                to the driver writer. Language is specified
+                                in RFC 4646 or ISO 639-2 language code format.
+
+  @param  DriverName[out]       A pointer to the Unicode string to return.
+                                This Unicode string is the name of the
+                                driver specified by This in the language
+                                specified by Language.
+
+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by
+                                This and the language specified by Language was
+                                returned in DriverName.
+
+  @retval EFI_INVALID_PARAMETER Language is NULL.
+
+  @retval EFI_INVALID_PARAMETER DriverName is NULL.
+
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support
+                                the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+UndiComponentNameGetDriverName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,
+  IN  CHAR8                        *Language,
+  OUT CHAR16                       **DriverName
+  );
+
+
+/**
+  Retrieves a Unicode string that is the user readable name of the controller
+  that is being managed by a driver.
+
+  This function retrieves the user readable name of the controller specified by
+  ControllerHandle and ChildHandle in the form of a Unicode string. If the
+  driver specified by This has a user readable name in the language specified by
+  Language, then a pointer to the controller name is returned in ControllerName,
+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently
+  managing the controller specified by ControllerHandle and ChildHandle,
+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not
+  support the language specified by Language, then EFI_UNSUPPORTED is returned.
+
+  @param  This[in]              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+                                EFI_COMPONENT_NAME_PROTOCOL instance.
+
+  @param  ControllerHandle[in]  The handle of a controller that the driver
+                                specified by This is managing.  This handle
+                                specifies the controller whose name is to be
+                                returned.
+
+  @param  ChildHandle[in]       The handle of the child controller to retrieve
+                                the name of.  This is an optional parameter that
+                                may be NULL.  It will be NULL for device
+                                drivers.  It will also be NULL for a bus drivers
+                                that wish to retrieve the name of the bus
+                                controller.  It will not be NULL for a bus
+                                driver that wishes to retrieve the name of a
+                                child controller.
+
+  @param  Language[in]          A pointer to a Null-terminated ASCII string
+                                array indicating the language.  This is the
+                                language of the driver name that the caller is
+                                requesting, and it must match one of the
+                                languages specified in SupportedLanguages. The
+                                number of languages supported by a driver is up
+                                to the driver writer. Language is specified in
+                                RFC 4646 or ISO 639-2 language code format.
+
+  @param  ControllerName[out]   A pointer to the Unicode string to return.
+                                This Unicode string is the name of the
+                                controller specified by ControllerHandle and
+                                ChildHandle in the language specified by
+                                Language from the point of view of the driver
+                                specified by This.
+
+  @retval EFI_SUCCESS           The Unicode string for the user readable name in
+                                the language specified by Language for the
+                                driver specified by This was returned in
+                                DriverName.
+
+  @retval EFI_INVALID_PARAMETER ControllerHandle is NULL.
+
+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid
+                                EFI_HANDLE.
+
+  @retval EFI_INVALID_PARAMETER Language is NULL.
+
+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+
+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently
+                                managing the controller specified by
+                                ControllerHandle and ChildHandle.
+
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support
+                                the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+UndiComponentNameGetControllerName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL                     *This,
+  IN  EFI_HANDLE                                      ControllerHandle,
+  IN  EFI_HANDLE                                      ChildHandle        OPTIONAL,
+  IN  CHAR8                                           *Language,
+  OUT CHAR16                                          **ControllerName
+  );
+
+
+//
+// EFI Component Name Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL  gUndiComponentName = {
+  UndiComponentNameGetDriverName,
+  UndiComponentNameGetControllerName,
+  "eng"
+};
+
+//
+// EFI Component Name 2 Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gUndiComponentName2 = {
+  (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) UndiComponentNameGetDriverName,
+  (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) UndiComponentNameGetControllerName,
+  "en"
+};
+
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mUndiDriverNameTable[] = {
+  {
+    "eng;en",
+    L"UNDI32 Driver"
+  },
+  {
+    NULL,
+    NULL
+  }
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mUndiControllerNameTable[] = {
+  {
+    "eng;en",
+    L"UNDI32 Controller"
+  },
+  {
+    NULL,
+    NULL
+  }
+};
+
+/**
+  Retrieves a Unicode string that is the user readable name of the driver.
+
+  This function retrieves the user readable name of a driver in the form of a
+  Unicode string. If the driver specified by This has a user readable name in
+  the language specified by Language, then a pointer to the driver name is
+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+  by This does not support the language specified by Language,
+  then EFI_UNSUPPORTED is returned.
+
+  @param  This[in]              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+                                EFI_COMPONENT_NAME_PROTOCOL instance.
+
+  @param  Language[in]          A pointer to a Null-terminated ASCII string
+                                array indicating the language. This is the
+                                language of the driver name that the caller is
+                                requesting, and it must match one of the
+                                languages specified in SupportedLanguages. The
+                                number of languages supported by a driver is up
+                                to the driver writer. Language is specified
+                                in RFC 4646 or ISO 639-2 language code format.
+
+  @param  DriverName[out]       A pointer to the Unicode string to return.
+                                This Unicode string is the name of the
+                                driver specified by This in the language
+                                specified by Language.
+
+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by
+                                This and the language specified by Language was
+                                returned in DriverName.
+
+  @retval EFI_INVALID_PARAMETER Language is NULL.
+
+  @retval EFI_INVALID_PARAMETER DriverName is NULL.
+
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support
+                                the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+UndiComponentNameGetDriverName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,
+  IN  CHAR8                        *Language,
+  OUT CHAR16                       **DriverName
+  )
+{
+  return LookupUnicodeString2 (
+           Language,
+           This->SupportedLanguages,
+           mUndiDriverNameTable,
+           DriverName,
+           (BOOLEAN)(This == &gUndiComponentName)
+           );
+}
+
+/**
+  Retrieves a Unicode string that is the user readable name of the controller
+  that is being managed by a driver.
+
+  This function retrieves the user readable name of the controller specified by
+  ControllerHandle and ChildHandle in the form of a Unicode string. If the
+  driver specified by This has a user readable name in the language specified by
+  Language, then a pointer to the controller name is returned in ControllerName,
+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently
+  managing the controller specified by ControllerHandle and ChildHandle,
+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not
+  support the language specified by Language, then EFI_UNSUPPORTED is returned.
+  Currently not implemented.
+
+  @param  This[in]              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+                                EFI_COMPONENT_NAME_PROTOCOL instance.
+
+  @param  ControllerHandle[in]  The handle of a controller that the driver
+                                specified by This is managing.  This handle
+                                specifies the controller whose name is to be
+                                returned.
+
+  @param  ChildHandle[in]       The handle of the child controller to retrieve
+                                the name of.  This is an optional parameter that
+                                may be NULL.  It will be NULL for device
+                                drivers.  It will also be NULL for a bus drivers
+                                that wish to retrieve the name of the bus
+                                controller.  It will not be NULL for a bus
+                                driver that wishes to retrieve the name of a
+                                child controller.
+
+  @param  Language[in]          A pointer to a Null-terminated ASCII string
+                                array indicating the language.  This is the
+                                language of the driver name that the caller is
+                                requesting, and it must match one of the
+                                languages specified in SupportedLanguages. The
+                                number of languages supported by a driver is up
+                                to the driver writer. Language is specified in
+                                RFC 4646 or ISO 639-2 language code format.
+
+  @param  ControllerName[out]   A pointer to the Unicode string to return.
+                                This Unicode string is the name of the
+                                controller specified by ControllerHandle and
+                                ChildHandle in the language specified by
+                                Language from the point of view of the driver
+                                specified by This.
+
+  @retval EFI_SUCCESS           The Unicode string for the user readable name in
+                                the language specified by Language for the
+                                driver specified by This was returned in
+                                DriverName.
+
+  @retval EFI_INVALID_PARAMETER ControllerHandle is NULL.
+
+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid
+                                EFI_HANDLE.
+
+  @retval EFI_INVALID_PARAMETER Language is NULL.
+
+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+
+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently
+                                managing the controller specified by
+                                ControllerHandle and ChildHandle.
+
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support
+                                the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+UndiComponentNameGetControllerName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL                     *This,
+  IN  EFI_HANDLE                                      ControllerHandle,
+  IN  EFI_HANDLE                                      ChildHandle        OPTIONAL,
+  IN  CHAR8                                           *Language,
+  OUT CHAR16                                          **ControllerName
+  )
+{
+  EFI_STATUS                                Status;
+  EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL *Nii;
+
+  if (ChildHandle != NULL) {
+    return EFI_UNSUPPORTED;
+  }
+
+  //
+  // Make sure this driver is currently managing ControllHandle
+  //
+  Status = EfiTestManagedDevice (
+             ControllerHandle,
+             gUndiDriverBinding.DriverBindingHandle,
+             &gEfiPciIoProtocolGuid
+             );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Retrieve an instance of a produced protocol from ControllerHandle
+  //
+  Status = gBS->OpenProtocol (
+                   ControllerHandle,
+                   &gEfiNetworkInterfaceIdentifierProtocolGuid_31,
+                  (VOID **)&Nii,
+                   NULL,
+                   NULL,
+                   EFI_OPEN_PROTOCOL_GET_PROTOCOL
+                   );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  return LookupUnicodeString2 (
+           Language,
+           This->SupportedLanguages,
+           mUndiControllerNameTable,
+           ControllerName,
+           (BOOLEAN)(This == &gUndiComponentName)
+           );
+}
diff --git a/Drivers/OptionRomPkg/UndiRuntimeDxe/Decode.c b/Drivers/OptionRomPkg/UndiRuntimeDxe/Decode.c
new file mode 100644
index 0000000000..b7a2d16bc6
--- /dev/null
+++ b/Drivers/OptionRomPkg/UndiRuntimeDxe/Decode.c
@@ -0,0 +1,1516 @@
+/** @file
+  Provides the basic UNID functions.
+
+Copyright (c) 2006 - 2017, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "Undi32.h"
+
+//
+// Global variables defined in this file
+//
+UNDI_CALL_TABLE api_table[PXE_OPCODE_LAST_VALID+1] = { \
+  {PXE_CPBSIZE_NOT_USED,PXE_DBSIZE_NOT_USED,0, (UINT16)(ANY_STATE),UNDI_GetState },\
+  {(UINT16)(DONT_CHECK),PXE_DBSIZE_NOT_USED,0,(UINT16)(ANY_STATE),UNDI_Start },\
+  {PXE_CPBSIZE_NOT_USED,PXE_DBSIZE_NOT_USED,0,MUST_BE_STARTED,UNDI_Stop },\
+  {PXE_CPBSIZE_NOT_USED,sizeof(PXE_DB_GET_INIT_INFO),0,MUST_BE_STARTED, UNDI_GetInitInfo },\
+  {PXE_CPBSIZE_NOT_USED,sizeof(PXE_DB_GET_CONFIG_INFO),0,MUST_BE_STARTED, UNDI_GetConfigInfo },\
+  {sizeof(PXE_CPB_INITIALIZE),(UINT16)(DONT_CHECK),(UINT16)(DONT_CHECK),MUST_BE_STARTED,UNDI_Initialize },\
+  {PXE_CPBSIZE_NOT_USED,PXE_DBSIZE_NOT_USED,(UINT16)(DONT_CHECK), MUST_BE_INITIALIZED,UNDI_Reset },\
+  {PXE_CPBSIZE_NOT_USED,PXE_DBSIZE_NOT_USED,0, MUST_BE_INITIALIZED,UNDI_Shutdown },\
+  {PXE_CPBSIZE_NOT_USED,PXE_DBSIZE_NOT_USED,(UINT16)(DONT_CHECK), MUST_BE_INITIALIZED,UNDI_Interrupt },\
+  {(UINT16)(DONT_CHECK),(UINT16)(DONT_CHECK),(UINT16)(DONT_CHECK), MUST_BE_INITIALIZED, UNDI_RecFilter },\
+  {(UINT16)(DONT_CHECK),(UINT16)(DONT_CHECK),(UINT16)(DONT_CHECK), MUST_BE_INITIALIZED, UNDI_StnAddr },\
+  {PXE_CPBSIZE_NOT_USED, (UINT16)(DONT_CHECK), (UINT16)(DONT_CHECK), MUST_BE_INITIALIZED, UNDI_Statistics },\
+  {sizeof(PXE_CPB_MCAST_IP_TO_MAC),sizeof(PXE_DB_MCAST_IP_TO_MAC), (UINT16)(DONT_CHECK),MUST_BE_INITIALIZED, UNDI_ip2mac },\
+  {(UINT16)(DONT_CHECK),(UINT16)(DONT_CHECK),(UINT16)(DONT_CHECK), MUST_BE_INITIALIZED, UNDI_NVData },\
+  {PXE_CPBSIZE_NOT_USED,(UINT16)(DONT_CHECK),(UINT16)(DONT_CHECK), MUST_BE_INITIALIZED, UNDI_Status },\
+  {(UINT16)(DONT_CHECK),PXE_DBSIZE_NOT_USED,(UINT16)(DONT_CHECK), MUST_BE_INITIALIZED, UNDI_FillHeader },\
+  {(UINT16)(DONT_CHECK),PXE_DBSIZE_NOT_USED,(UINT16)(DONT_CHECK), MUST_BE_INITIALIZED, UNDI_Transmit },\
+  {sizeof(PXE_CPB_RECEIVE),sizeof(PXE_DB_RECEIVE),0,MUST_BE_INITIALIZED, UNDI_Receive } \
+};
+
+//
+// end of global variables
+//
+
+
+/**
+  This routine determines the operational state of the UNDI.  It updates the state flags in the
+  Command Descriptor Block based on information derived from the AdapterInfo instance data.
+  To ensure the command has completed successfully, CdbPtr->StatCode will contain the result of
+  the command execution.
+  The CdbPtr->StatFlags will contain a STOPPED, STARTED, or INITIALIZED state once the command
+  has successfully completed.
+  Keep in mind the AdapterInfo->State is the active state of the adapter (based on software
+  interrogation), and the CdbPtr->StateFlags is the passed back information that is reflected
+  to the caller of the UNDI API.
+
+  @param  CdbPtr               Pointer to the command descriptor block.
+  @param  AdapterInfo          Pointer to the NIC data structure information which
+                               the UNDI driver is layering on..
+
+  @return None
+
+**/
+VOID
+UNDI_GetState (
+  IN  PXE_CDB           *CdbPtr,
+  IN  NIC_DATA_INSTANCE *AdapterInfo
+  )
+{
+  CdbPtr->StatFlags = (PXE_STATFLAGS) (CdbPtr->StatFlags | AdapterInfo->State);
+  return ;
+}
+
+
+/**
+  This routine is used to change the operational state of the UNDI from stopped to started.
+  It will do this as long as the adapter's state is PXE_STATFLAGS_GET_STATE_STOPPED, otherwise
+  the CdbPtr->StatFlags will reflect a command failure, and the CdbPtr->StatCode will reflect the
+  UNDI as having already been started.
+  This routine is modified to reflect the undi 1.1 specification changes. The
+  changes in the spec are mainly in the callback routines, the new spec adds
+  3 more callbacks and a unique id.
+  Since this UNDI supports both old and new undi specifications,
+  The NIC's data structure is filled in with the callback routines (depending
+  on the version) pointed to in the caller's CpbPtr.  This seeds the Delay,
+  Virt2Phys, Block, and Mem_IO for old and new versions and Map_Mem, UnMap_Mem
+  and Sync_Mem routines and a unique id variable for the new version.
+  This is the function which an external entity (SNP, O/S, etc) would call
+  to provide it's I/O abstraction to the UNDI.
+  It's final action is to change the AdapterInfo->State to PXE_STATFLAGS_GET_STATE_STARTED.
+
+  @param  CdbPtr               Pointer to the command descriptor block.
+  @param  AdapterInfo          Pointer to the NIC data structure information which
+                               the UNDI driver is layering on..
+
+  @return None
+
+**/
+VOID
+UNDI_Start (
+  IN  PXE_CDB           *CdbPtr,
+  IN  NIC_DATA_INSTANCE *AdapterInfo
+  )
+{
+  PXE_CPB_START_30  *CpbPtr;
+  PXE_CPB_START_31  *CpbPtr_31;
+
+  //
+  // check if it is already started.
+  //
+  if (AdapterInfo->State != PXE_STATFLAGS_GET_STATE_STOPPED) {
+    CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    CdbPtr->StatCode  = PXE_STATCODE_ALREADY_STARTED;
+    return ;
+  }
+
+  if (CdbPtr->CPBsize != sizeof(PXE_CPB_START_30) &&
+      CdbPtr->CPBsize != sizeof(PXE_CPB_START_31)) {
+
+    CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    CdbPtr->StatCode  = PXE_STATCODE_INVALID_CDB;
+    return ;
+  }
+
+  CpbPtr    = (PXE_CPB_START_30 *) (UINTN) (CdbPtr->CPBaddr);
+  CpbPtr_31 = (PXE_CPB_START_31 *) (UINTN) (CdbPtr->CPBaddr);
+
+  if (AdapterInfo->VersionFlag == 0x30) {
+    AdapterInfo->Delay_30     = (bsptr_30) (UINTN) CpbPtr->Delay;
+    AdapterInfo->Virt2Phys_30 = (virtphys_30) (UINTN) CpbPtr->Virt2Phys;
+    AdapterInfo->Block_30     = (block_30) (UINTN) CpbPtr->Block;
+    //
+    // patch for old buggy 3.0 code:
+    // In EFI1.0 undi used to provide the full (absolute) I/O address to the
+    // i/o calls and SNP used to provide a callback that used GlobalIoFncs and
+    // everything worked fine! In EFI 1.1, UNDI is not using the full
+    // i/o or memory address to access the device, The base values for the i/o
+    // and memory address is abstracted by the device specific PciIoFncs and
+    // UNDI only uses the offset values. Since UNDI3.0 cannot provide any
+    // identification to SNP, SNP cannot use nic specific PciIoFncs callback!
+    //
+    // To fix this and make undi3.0 work with SNP in EFI1.1 we
+    // use a TmpMemIo function that is defined in init.c
+    // This breaks the runtime driver feature of undi, but what to do
+    // if we have to provide the 3.0 compatibility (including the 3.0 bugs)
+    //
+    // This TmpMemIo function also takes a UniqueId parameter
+    // (as in undi3.1 design) and so initialize the UniqueId as well here
+    // Note: AdapterInfo->Mem_Io_30 is just filled for consistency with other
+    // parameters but never used, we only use Mem_Io field in the In/Out routines
+    // inside e100b.c.
+    //
+    AdapterInfo->Mem_Io_30  = (mem_io_30) (UINTN) CpbPtr->Mem_IO;
+    AdapterInfo->Mem_Io     = (mem_io) (UINTN) TmpMemIo;
+    AdapterInfo->Unique_ID  = (UINT64) (UINTN) AdapterInfo;
+
+  } else {
+    AdapterInfo->Delay      = (bsptr) (UINTN) CpbPtr_31->Delay;
+    AdapterInfo->Virt2Phys  = (virtphys) (UINTN) CpbPtr_31->Virt2Phys;
+    AdapterInfo->Block      = (block) (UINTN) CpbPtr_31->Block;
+    AdapterInfo->Mem_Io     = (mem_io) (UINTN) CpbPtr_31->Mem_IO;
+
+    AdapterInfo->Map_Mem    = (map_mem) (UINTN) CpbPtr_31->Map_Mem;
+    AdapterInfo->UnMap_Mem  = (unmap_mem) (UINTN) CpbPtr_31->UnMap_Mem;
+    AdapterInfo->Sync_Mem   = (sync_mem) (UINTN) CpbPtr_31->Sync_Mem;
+    AdapterInfo->Unique_ID  = CpbPtr_31->Unique_ID;
+  }
+
+  AdapterInfo->State = PXE_STATFLAGS_GET_STATE_STARTED;
+
+  return ;
+}
+
+
+/**
+  This routine is used to change the operational state of the UNDI from started to stopped.
+  It will not do this if the adapter's state is PXE_STATFLAGS_GET_STATE_INITIALIZED, otherwise
+  the CdbPtr->StatFlags will reflect a command failure, and the CdbPtr->StatCode will reflect the
+  UNDI as having already not been shut down.
+  The NIC's data structure will have the Delay, Virt2Phys, and Block, pointers zero'd out..
+  It's final action is to change the AdapterInfo->State to PXE_STATFLAGS_GET_STATE_STOPPED.
+
+  @param  CdbPtr               Pointer to the command descriptor block.
+  @param  AdapterInfo          Pointer to the NIC data structure information which
+                               the UNDI driver is layering on..
+
+  @return None
+
+**/
+VOID
+UNDI_Stop (
+  IN  PXE_CDB           *CdbPtr,
+  IN  NIC_DATA_INSTANCE *AdapterInfo
+  )
+{
+  if (AdapterInfo->State == PXE_STATFLAGS_GET_STATE_INITIALIZED) {
+    CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    CdbPtr->StatCode  = PXE_STATCODE_NOT_SHUTDOWN;
+    return ;
+  }
+
+  AdapterInfo->Delay_30     = 0;
+  AdapterInfo->Virt2Phys_30 = 0;
+  AdapterInfo->Block_30     = 0;
+
+  AdapterInfo->Delay        = 0;
+  AdapterInfo->Virt2Phys    = 0;
+  AdapterInfo->Block        = 0;
+
+  AdapterInfo->Map_Mem      = 0;
+  AdapterInfo->UnMap_Mem    = 0;
+  AdapterInfo->Sync_Mem     = 0;
+
+  AdapterInfo->State        = PXE_STATFLAGS_GET_STATE_STOPPED;
+
+  return ;
+}
+
+
+/**
+  This routine is used to retrieve the initialization information that is needed by drivers and
+  applications to initialize the UNDI.  This will fill in data in the Data Block structure that is
+  pointed to by the caller's CdbPtr->DBaddr.  The fields filled in are as follows:
+  MemoryRequired, FrameDataLen, LinkSpeeds[0-3], NvCount, NvWidth, MediaHeaderLen, HWaddrLen,
+  MCastFilterCnt, TxBufCnt, TxBufSize, RxBufCnt, RxBufSize, IFtype, Duplex, and LoopBack.
+  In addition, the CdbPtr->StatFlags ORs in that this NIC supports cable detection.  (APRIORI knowledge)
+
+  @param  CdbPtr               Pointer to the command descriptor block.
+  @param  AdapterInfo          Pointer to the NIC data structure information which
+                               the UNDI driver is layering on..
+
+  @return None
+
+**/
+VOID
+UNDI_GetInitInfo (
+  IN  PXE_CDB           *CdbPtr,
+  IN  NIC_DATA_INSTANCE *AdapterInfo
+  )
+{
+  PXE_DB_GET_INIT_INFO  *DbPtr;
+
+  DbPtr = (PXE_DB_GET_INIT_INFO *) (UINTN) (CdbPtr->DBaddr);
+
+  DbPtr->MemoryRequired = MEMORY_NEEDED;
+  DbPtr->FrameDataLen = PXE_MAX_TXRX_UNIT_ETHER;
+  DbPtr->LinkSpeeds[0] = 10;
+  DbPtr->LinkSpeeds[1] = 100;
+  DbPtr->LinkSpeeds[2] = DbPtr->LinkSpeeds[3] = 0;
+  DbPtr->NvCount = MAX_EEPROM_LEN;
+  DbPtr->NvWidth = 4;
+  DbPtr->MediaHeaderLen = PXE_MAC_HEADER_LEN_ETHER;
+  DbPtr->HWaddrLen = PXE_HWADDR_LEN_ETHER;
+  DbPtr->MCastFilterCnt = MAX_MCAST_ADDRESS_CNT;
+
+  DbPtr->TxBufCnt = TX_BUFFER_COUNT;
+  DbPtr->TxBufSize = (UINT16) sizeof (TxCB);
+  DbPtr->RxBufCnt = RX_BUFFER_COUNT;
+  DbPtr->RxBufSize = (UINT16) sizeof (RxFD);
+
+  DbPtr->IFtype = PXE_IFTYPE_ETHERNET;
+  DbPtr->SupportedDuplexModes = PXE_DUPLEX_ENABLE_FULL_SUPPORTED |
+                  PXE_DUPLEX_FORCE_FULL_SUPPORTED;
+  DbPtr->SupportedLoopBackModes = PXE_LOOPBACK_INTERNAL_SUPPORTED |
+                    PXE_LOOPBACK_EXTERNAL_SUPPORTED;
+
+  CdbPtr->StatFlags |= (PXE_STATFLAGS_CABLE_DETECT_SUPPORTED |
+                        PXE_STATFLAGS_GET_STATUS_NO_MEDIA_SUPPORTED);
+  return ;
+}
+
+
+/**
+  This routine is used to retrieve the configuration information about the NIC being controlled by
+  this driver.  This will fill in data in the Data Block structure that is pointed to by the caller's CdbPtr->DBaddr.
+  The fields filled in are as follows:
+  DbPtr->pci.BusType, DbPtr->pci.Bus, DbPtr->pci.Device, and DbPtr->pci.
+  In addition, the DbPtr->pci.Config.Dword[0-63] grabs a copy of this NIC's PCI configuration space.
+
+  @param  CdbPtr               Pointer to the command descriptor block.
+  @param  AdapterInfo          Pointer to the NIC data structure information which
+                               the UNDI driver is layering on..
+
+  @return None
+
+**/
+VOID
+UNDI_GetConfigInfo (
+  IN  PXE_CDB           *CdbPtr,
+  IN  NIC_DATA_INSTANCE *AdapterInfo
+  )
+{
+  UINT16                  Index;
+  PXE_DB_GET_CONFIG_INFO  *DbPtr;
+
+  DbPtr               = (PXE_DB_GET_CONFIG_INFO *) (UINTN) (CdbPtr->DBaddr);
+
+  DbPtr->pci.BusType  = PXE_BUSTYPE_PCI;
+  DbPtr->pci.Bus      = AdapterInfo->Bus;
+  DbPtr->pci.Device   = AdapterInfo->Device;
+  DbPtr->pci.Function = AdapterInfo->Function;
+
+  for (Index = 0; Index < MAX_PCI_CONFIG_LEN; Index++) {
+    DbPtr->pci.Config.Dword[Index] = AdapterInfo->Config[Index];
+  }
+
+  return ;
+}
+
+
+/**
+  This routine resets the network adapter and initializes the UNDI using the parameters supplied in
+  the CPB.  This command must be issued before the network adapter can be setup to transmit and
+  receive packets.
+  Once the memory requirements of the UNDI are obtained by using the GetInitInfo command, a block
+  of non-swappable memory may need to be allocated.  The address of this memory must be passed to
+  UNDI during the Initialize in the CPB.  This memory is used primarily for transmit and receive buffers.
+  The fields CableDetect, LinkSpeed, Duplex, LoopBack, MemoryPtr, and MemoryLength are set with information
+  that was passed in the CPB and the NIC is initialized.
+  If the NIC initialization fails, the CdbPtr->StatFlags are updated with PXE_STATFLAGS_COMMAND_FAILED
+  Otherwise, AdapterInfo->State is updated with PXE_STATFLAGS_GET_STATE_INITIALIZED showing the state of
+  the UNDI is now initialized.
+
+  @param  CdbPtr               Pointer to the command descriptor block.
+  @param  AdapterInfo          Pointer to the NIC data structure information which
+                               the UNDI driver is layering on..
+
+  @return None
+
+**/
+VOID
+UNDI_Initialize (
+  IN  PXE_CDB       *CdbPtr,
+  NIC_DATA_INSTANCE *AdapterInfo
+  )
+{
+  PXE_CPB_INITIALIZE  *CpbPtr;
+
+  if ((CdbPtr->OpFlags != PXE_OPFLAGS_INITIALIZE_DETECT_CABLE) &&
+      (CdbPtr->OpFlags != PXE_OPFLAGS_INITIALIZE_DO_NOT_DETECT_CABLE)) {
+    CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    CdbPtr->StatCode  = PXE_STATCODE_INVALID_CDB;
+    return ;
+  }
+
+  //
+  // check if it is already initialized
+  //
+  if (AdapterInfo->State == PXE_STATFLAGS_GET_STATE_INITIALIZED) {
+    CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    CdbPtr->StatCode  = PXE_STATCODE_ALREADY_INITIALIZED;
+    return ;
+  }
+
+  CpbPtr  = (PXE_CPB_INITIALIZE *) (UINTN) CdbPtr->CPBaddr;
+
+  if (CpbPtr->MemoryLength < (UINT32) MEMORY_NEEDED) {
+    CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    CdbPtr->StatCode  = PXE_STATCODE_INVALID_CPB;
+    return ;
+  }
+
+  //
+  // default behaviour is to detect the cable, if the 3rd param is 1,
+  // do not do that
+  //
+  AdapterInfo->CableDetect = (UINT8) ((CdbPtr->OpFlags == (UINT16) PXE_OPFLAGS_INITIALIZE_DO_NOT_DETECT_CABLE) ? (UINT8) 0 : (UINT8) 1);
+  AdapterInfo->LinkSpeedReq = (UINT16) CpbPtr->LinkSpeed;
+  AdapterInfo->DuplexReq    = CpbPtr->DuplexMode;
+  AdapterInfo->LoopBack     = CpbPtr->LoopBackMode;
+  AdapterInfo->MemoryPtr    = CpbPtr->MemoryAddr;
+  AdapterInfo->MemoryLength = CpbPtr->MemoryLength;
+
+  CdbPtr->StatCode          = (PXE_STATCODE) E100bInit (AdapterInfo);
+
+  if (CdbPtr->StatCode != PXE_STATCODE_SUCCESS) {
+    CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+  } else {
+    AdapterInfo->State = PXE_STATFLAGS_GET_STATE_INITIALIZED;
+  }
+
+  return ;
+}
+
+
+/**
+  This routine resets the network adapter and initializes the UNDI using the parameters supplied in
+  the CPB.  The transmit and receive queues are emptied and any pending interrupts are cleared.
+  If the NIC reset fails, the CdbPtr->StatFlags are updated with PXE_STATFLAGS_COMMAND_FAILED
+
+  @param  CdbPtr               Pointer to the command descriptor block.
+  @param  AdapterInfo          Pointer to the NIC data structure information which
+                               the UNDI driver is layering on..
+
+  @return None
+
+**/
+VOID
+UNDI_Reset (
+  IN  PXE_CDB           *CdbPtr,
+  IN  NIC_DATA_INSTANCE *AdapterInfo
+  )
+{
+  if (CdbPtr->OpFlags != PXE_OPFLAGS_NOT_USED &&
+      CdbPtr->OpFlags != PXE_OPFLAGS_RESET_DISABLE_INTERRUPTS &&
+      CdbPtr->OpFlags != PXE_OPFLAGS_RESET_DISABLE_FILTERS ) {
+
+    CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    CdbPtr->StatCode  = PXE_STATCODE_INVALID_CDB;
+    return ;
+  }
+
+  CdbPtr->StatCode = (UINT16) E100bReset (AdapterInfo, CdbPtr->OpFlags);
+
+  if (CdbPtr->StatCode != PXE_STATCODE_SUCCESS) {
+    CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+  }
+}
+
+
+/**
+  This routine resets the network adapter and leaves it in a safe state for another driver to
+  initialize.  Any pending transmits or receives are lost.  Receive filters and external
+  interrupt enables are disabled.  Once the UNDI has been shutdown, it can then be stopped
+  or initialized again.
+  If the NIC reset fails, the CdbPtr->StatFlags are updated with PXE_STATFLAGS_COMMAND_FAILED
+  Otherwise, AdapterInfo->State is updated with PXE_STATFLAGS_GET_STATE_STARTED showing the state of
+  the NIC as being started.
+
+  @param  CdbPtr               Pointer to the command descriptor block.
+  @param  AdapterInfo          Pointer to the NIC data structure information which
+                               the UNDI driver is layering on..
+
+  @return None
+
+**/
+VOID
+UNDI_Shutdown (
+  IN  PXE_CDB           *CdbPtr,
+  IN  NIC_DATA_INSTANCE *AdapterInfo
+  )
+{
+  //
+  // do the shutdown stuff here
+  //
+  CdbPtr->StatCode = (UINT16) E100bShutdown (AdapterInfo);
+
+  if (CdbPtr->StatCode != PXE_STATCODE_SUCCESS) {
+    CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+  } else {
+    AdapterInfo->State = PXE_STATFLAGS_GET_STATE_STARTED;
+  }
+
+  return ;
+}
+
+
+/**
+  This routine can be used to read and/or change the current external interrupt enable
+  settings.  Disabling an external interrupt enable prevents and external (hardware)
+  interrupt from being signaled by the network device.  Internally the interrupt events
+  can still be polled by using the UNDI_GetState command.
+  The resulting information on the interrupt state will be passed back in the CdbPtr->StatFlags.
+
+  @param  CdbPtr               Pointer to the command descriptor block.
+  @param  AdapterInfo          Pointer to the NIC data structure information which
+                               the UNDI driver is layering on..
+
+  @return None
+
+**/
+VOID
+UNDI_Interrupt (
+  IN  PXE_CDB           *CdbPtr,
+  IN  NIC_DATA_INSTANCE *AdapterInfo
+  )
+{
+  UINT8 IntMask;
+
+  IntMask = (UINT8)(UINTN)(CdbPtr->OpFlags & (PXE_OPFLAGS_INTERRUPT_RECEIVE |
+                                              PXE_OPFLAGS_INTERRUPT_TRANSMIT |
+                                              PXE_OPFLAGS_INTERRUPT_COMMAND |
+                                              PXE_OPFLAGS_INTERRUPT_SOFTWARE));
+
+  switch (CdbPtr->OpFlags & PXE_OPFLAGS_INTERRUPT_OPMASK) {
+  case PXE_OPFLAGS_INTERRUPT_READ:
+    break;
+
+  case PXE_OPFLAGS_INTERRUPT_ENABLE:
+    if (IntMask == 0) {
+      CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+      CdbPtr->StatCode  = PXE_STATCODE_INVALID_CDB;
+      return ;
+    }
+
+    AdapterInfo->int_mask = IntMask;
+    E100bSetInterruptState (AdapterInfo);
+    break;
+
+  case PXE_OPFLAGS_INTERRUPT_DISABLE:
+    if (IntMask != 0) {
+      AdapterInfo->int_mask = (UINT16) (AdapterInfo->int_mask & ~(IntMask));
+      E100bSetInterruptState (AdapterInfo);
+      break;
+    }
+
+  //
+  // else fall thru.
+  //
+  default:
+    CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    CdbPtr->StatCode  = PXE_STATCODE_INVALID_CDB;
+    return ;
+  }
+
+  if ((AdapterInfo->int_mask & PXE_OPFLAGS_INTERRUPT_RECEIVE) != 0) {
+    CdbPtr->StatFlags |= PXE_STATFLAGS_INTERRUPT_RECEIVE;
+
+  }
+
+  if ((AdapterInfo->int_mask & PXE_OPFLAGS_INTERRUPT_TRANSMIT) != 0) {
+    CdbPtr->StatFlags |= PXE_STATFLAGS_INTERRUPT_TRANSMIT;
+
+  }
+
+  if ((AdapterInfo->int_mask & PXE_OPFLAGS_INTERRUPT_COMMAND) != 0) {
+    CdbPtr->StatFlags |= PXE_STATFLAGS_INTERRUPT_COMMAND;
+
+  }
+
+  return ;
+}
+
+
+/**
+  This routine is used to read and change receive filters and, if supported, read
+  and change multicast MAC address filter list.
+
+  @param  CdbPtr               Pointer to the command descriptor block.
+  @param  AdapterInfo          Pointer to the NIC data structure information which
+                               the UNDI driver is layering on..
+
+  @return None
+
+**/
+VOID
+UNDI_RecFilter (
+  IN  PXE_CDB           *CdbPtr,
+  IN  NIC_DATA_INSTANCE *AdapterInfo
+  )
+{
+  UINT16                  NewFilter;
+  UINT16                  OpFlags;
+  PXE_DB_RECEIVE_FILTERS  *DbPtr;
+  UINT8                   *MacAddr;
+  UINTN                   MacCount;
+  UINT16                  Index;
+  UINT16                  copy_len;
+  UINT8                   *ptr1;
+  UINT8                   *ptr2;
+  BOOLEAN                 InvalidMacAddr;
+    
+  OpFlags   = CdbPtr->OpFlags;
+  NewFilter = (UINT16) (OpFlags & 0x1F);
+
+  switch (OpFlags & PXE_OPFLAGS_RECEIVE_FILTER_OPMASK) {
+  case PXE_OPFLAGS_RECEIVE_FILTER_READ:
+
+    //
+    // not expecting a cpb, not expecting any filter bits
+    //
+    if ((NewFilter != 0) || (CdbPtr->CPBsize != 0)) {
+      goto BadCdb;
+
+    }
+
+    if ((NewFilter & PXE_OPFLAGS_RECEIVE_FILTER_RESET_MCAST_LIST) == 0) {
+      goto JustRead;
+
+    }
+
+    NewFilter = (UINT16) (NewFilter | AdapterInfo->Rx_Filter);
+    //
+    // all other flags are ignored except mcast_reset
+    //
+    break;
+
+  case PXE_OPFLAGS_RECEIVE_FILTER_ENABLE:
+    //
+    // there should be atleast one other filter bit set.
+    //
+    if (NewFilter == 0) {
+      //
+      // nothing to enable
+      //
+      goto BadCdb;
+    }
+
+    if (CdbPtr->CPBsize != 0) {
+      //
+      // this must be a multicast address list!
+      // don't accept the list unless selective_mcast is set
+      // don't accept confusing mcast settings with this
+      //
+      if (((NewFilter & PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST) == 0) ||
+          ((NewFilter & PXE_OPFLAGS_RECEIVE_FILTER_RESET_MCAST_LIST) != 0) ||
+          ((NewFilter & PXE_OPFLAGS_RECEIVE_FILTER_ALL_MULTICAST) != 0) ||
+          ((CdbPtr->CPBsize % sizeof (PXE_MAC_ADDR)) != 0) ) {
+        goto BadCdb;
+      }
+
+      MacAddr   = (UINT8 *) ((UINTN) (CdbPtr->CPBaddr));
+      MacCount  = CdbPtr->CPBsize / sizeof (PXE_MAC_ADDR);
+
+      //
+      // The format of Ethernet multicast address for IPv6 is defined in RFC2464,
+      // for IPv4 is defined in RFC1112. Check whether the address is valid.
+      //
+      InvalidMacAddr = FALSE;
+      
+      for (; MacCount-- != 0; MacAddr += sizeof (PXE_MAC_ADDR)) {
+        if (MacAddr[0] == 0x01) {
+          //
+          // This multicast MAC address is mapped from IPv4 address.
+          //
+          if (MacAddr[1] != 0x00 || MacAddr[2] != 0x5E || (MacAddr[3] & 0x80) != 0) {
+            InvalidMacAddr = TRUE;
+          }          
+        } else if (MacAddr[0] == 0x33) {
+          //
+          // This multicast MAC address is mapped from IPv6 address.
+          //
+          if (MacAddr[1] != 0x33) {
+            InvalidMacAddr = TRUE;
+          }
+        } else {
+          InvalidMacAddr = TRUE;
+        }
+
+        if (InvalidMacAddr) {
+          CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+          CdbPtr->StatCode  = PXE_STATCODE_INVALID_CPB;
+          return ;
+        }
+      }
+    }
+
+    //
+    // check selective mcast case enable case
+    //
+    if ((OpFlags & PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST) != 0) {
+      if (((OpFlags & PXE_OPFLAGS_RECEIVE_FILTER_RESET_MCAST_LIST) != 0) ||
+          ((OpFlags & PXE_OPFLAGS_RECEIVE_FILTER_ALL_MULTICAST) != 0) ) {
+        goto BadCdb;
+
+      }
+      //
+      // if no cpb, make sure we have an old list
+      //
+      if ((CdbPtr->CPBsize == 0) && (AdapterInfo->mcast_list.list_len == 0)) {
+        goto BadCdb;
+      }
+    }
+    //
+    // if you want to enable anything, you got to have unicast
+    // and you have what you already enabled!
+    //
+    NewFilter = (UINT16) (NewFilter | (PXE_OPFLAGS_RECEIVE_FILTER_UNICAST | AdapterInfo->Rx_Filter));
+
+    break;
+
+  case PXE_OPFLAGS_RECEIVE_FILTER_DISABLE:
+
+    //
+    // mcast list not expected, i.e. no cpb here!
+    //
+    if (CdbPtr->CPBsize != PXE_CPBSIZE_NOT_USED) {
+      goto BadCdb;
+    }
+
+    NewFilter = (UINT16) ((~(CdbPtr->OpFlags & 0x1F)) & AdapterInfo->Rx_Filter);
+
+    break;
+
+  default:
+    goto BadCdb;
+  }
+
+  if ((OpFlags & PXE_OPFLAGS_RECEIVE_FILTER_RESET_MCAST_LIST) != 0) {
+    AdapterInfo->mcast_list.list_len = 0;
+    NewFilter &= (~PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST);
+  }
+
+  E100bSetfilter (AdapterInfo, NewFilter, CdbPtr->CPBaddr, CdbPtr->CPBsize);
+
+JustRead:
+  //
+  // give the current mcast list
+  //
+  if ((CdbPtr->DBsize != 0) && (AdapterInfo->mcast_list.list_len != 0)) {
+    //
+    // copy the mc list to db
+    //
+
+    DbPtr = (PXE_DB_RECEIVE_FILTERS *) (UINTN) CdbPtr->DBaddr;
+    ptr1  = (UINT8 *) (&DbPtr->MCastList[0]);
+
+    //
+    // DbPtr->mc_count = AdapterInfo->mcast_list.list_len;
+    //
+    copy_len = (UINT16) (AdapterInfo->mcast_list.list_len * PXE_MAC_LENGTH);
+
+    if (copy_len > CdbPtr->DBsize) {
+      copy_len = CdbPtr->DBsize;
+
+    }
+
+    ptr2 = (UINT8 *) (&AdapterInfo->mcast_list.mc_list[0]);
+    for (Index = 0; Index < copy_len; Index++) {
+      ptr1[Index] = ptr2[Index];
+    }
+  }
+  //
+  // give the stat flags here
+  //
+  if (AdapterInfo->Receive_Started) {
+    CdbPtr->StatFlags = (PXE_STATFLAGS) (CdbPtr->StatFlags | AdapterInfo->Rx_Filter);
+
+  }
+
+  return ;
+
+BadCdb:
+  CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+  CdbPtr->StatCode  = PXE_STATCODE_INVALID_CDB;
+}
+
+
+/**
+  This routine is used to get the current station and broadcast MAC addresses, and to change the
+  current station MAC address.
+
+  @param  CdbPtr               Pointer to the command descriptor block.
+  @param  AdapterInfo          Pointer to the NIC data structure information which
+                               the UNDI driver is layering on..
+
+  @return None
+
+**/
+VOID
+UNDI_StnAddr (
+  IN  PXE_CDB           *CdbPtr,
+  IN  NIC_DATA_INSTANCE *AdapterInfo
+  )
+{
+  PXE_CPB_STATION_ADDRESS *CpbPtr;
+  PXE_DB_STATION_ADDRESS  *DbPtr;
+  UINT16                  Index;
+
+  if (CdbPtr->OpFlags == PXE_OPFLAGS_STATION_ADDRESS_RESET) {
+    //
+    // configure the permanent address.
+    // change the AdapterInfo->CurrentNodeAddress field.
+    //
+    if (CompareMem (
+          &AdapterInfo->CurrentNodeAddress[0],
+          &AdapterInfo->PermNodeAddress[0],
+          PXE_MAC_LENGTH
+          ) != 0) {
+      for (Index = 0; Index < PXE_MAC_LENGTH; Index++) {
+        AdapterInfo->CurrentNodeAddress[Index] = AdapterInfo->PermNodeAddress[Index];
+      }
+
+      E100bSetupIAAddr (AdapterInfo);
+    }
+  }
+
+  if (CdbPtr->CPBaddr != (UINT64) 0) {
+    CpbPtr = (PXE_CPB_STATION_ADDRESS *) (UINTN) (CdbPtr->CPBaddr);
+    //
+    // configure the new address
+    //
+    for (Index = 0; Index < PXE_MAC_LENGTH; Index++) {
+      AdapterInfo->CurrentNodeAddress[Index] = CpbPtr->StationAddr[Index];
+    }
+
+    E100bSetupIAAddr (AdapterInfo);
+  }
+
+  if (CdbPtr->DBaddr != (UINT64) 0) {
+    DbPtr = (PXE_DB_STATION_ADDRESS *) (UINTN) (CdbPtr->DBaddr);
+    //
+    // fill it with the new values
+    //
+    for (Index = 0; Index < PXE_MAC_LENGTH; Index++) {
+      DbPtr->StationAddr[Index]   = AdapterInfo->CurrentNodeAddress[Index];
+      DbPtr->BroadcastAddr[Index] = AdapterInfo->BroadcastNodeAddress[Index];
+      DbPtr->PermanentAddr[Index] = AdapterInfo->PermNodeAddress[Index];
+    }
+  }
+
+  return ;
+}
+
+
+/**
+  This routine is used to read and clear the NIC traffic statistics.  This command is supported only
+  if the !PXE structure's Implementation flags say so.
+  Results will be parsed out in the following manner:
+  CdbPtr->DBaddr.Data[0]   R  Total Frames (Including frames with errors and dropped frames)
+  CdbPtr->DBaddr.Data[1]   R  Good Frames (All frames copied into receive buffer)
+  CdbPtr->DBaddr.Data[2]   R  Undersize Frames (Frames below minimum length for media <64 for ethernet)
+  CdbPtr->DBaddr.Data[4]   R  Dropped Frames (Frames that were dropped because receive buffers were full)
+  CdbPtr->DBaddr.Data[8]   R  CRC Error Frames (Frames with alignment or CRC errors)
+  CdbPtr->DBaddr.Data[A]   T  Total Frames (Including frames with errors and dropped frames)
+  CdbPtr->DBaddr.Data[B]   T  Good Frames (All frames copied into transmit buffer)
+  CdbPtr->DBaddr.Data[C]   T  Undersize Frames (Frames below minimum length for media <64 for ethernet)
+  CdbPtr->DBaddr.Data[E]   T  Dropped Frames (Frames that were dropped because of collisions)
+  CdbPtr->DBaddr.Data[14]  T  Total Collision Frames (Total collisions on this subnet)
+
+  @param  CdbPtr               Pointer to the command descriptor block.
+  @param  AdapterInfo          Pointer to the NIC data structure information which
+                               the UNDI driver is layering on..
+
+  @return None
+
+**/
+VOID
+UNDI_Statistics (
+  IN  PXE_CDB           *CdbPtr,
+  IN  NIC_DATA_INSTANCE *AdapterInfo
+  )
+{
+  if ((CdbPtr->OpFlags &~(PXE_OPFLAGS_STATISTICS_RESET)) != 0) {
+    CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    CdbPtr->StatCode  = PXE_STATCODE_INVALID_CDB;
+    return ;
+  }
+
+  if ((CdbPtr->OpFlags & PXE_OPFLAGS_STATISTICS_RESET) != 0) {
+    //
+    // Reset the statistics
+    //
+    CdbPtr->StatCode = (UINT16) E100bStatistics (AdapterInfo, 0, 0);
+  } else {
+    CdbPtr->StatCode = (UINT16) E100bStatistics (AdapterInfo, CdbPtr->DBaddr, CdbPtr->DBsize);
+  }
+
+  return ;
+}
+
+
+/**
+  This routine is used to translate a multicast IP address to a multicast MAC address.
+  This results in a MAC address composed of 25 bits of fixed data with the upper 23 bits of the IP
+  address being appended to it.  Results passed back in the equivalent of CdbPtr->DBaddr->MAC[0-5].
+
+  @param  CdbPtr               Pointer to the command descriptor block.
+  @param  AdapterInfo          Pointer to the NIC data structure information which
+                               the UNDI driver is layering on..
+
+  @return None
+
+**/
+VOID
+UNDI_ip2mac (
+  IN  PXE_CDB           *CdbPtr,
+  IN  NIC_DATA_INSTANCE *AdapterInfo
+  )
+{
+  PXE_CPB_MCAST_IP_TO_MAC *CpbPtr;
+  PXE_DB_MCAST_IP_TO_MAC  *DbPtr;
+  UINT8                   *TmpPtr;
+
+  CpbPtr  = (PXE_CPB_MCAST_IP_TO_MAC *) (UINTN) CdbPtr->CPBaddr;
+  DbPtr   = (PXE_DB_MCAST_IP_TO_MAC *) (UINTN) CdbPtr->DBaddr;
+
+  if ((CdbPtr->OpFlags & PXE_OPFLAGS_MCAST_IPV6_TO_MAC) != 0) {
+    //
+    // for now this is not supported
+    //
+    CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    CdbPtr->StatCode  = PXE_STATCODE_UNSUPPORTED;
+    return ;
+  }
+
+  TmpPtr = (UINT8 *) (&CpbPtr->IP.IPv4);
+  //
+  // check if the ip given is a mcast IP
+  //
+  if ((TmpPtr[0] & 0xF0) != 0xE0) {
+    CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    CdbPtr->StatCode  = PXE_STATCODE_INVALID_CPB;
+  }
+  //
+  // take the last 23 bits in IP.
+  // be very careful. accessing word on a non-word boundary will hang motherboard codenamed Big Sur
+  // casting the mac array (in the middle) to a UINT32 pointer and accessing
+  // the UINT32 content hung the system...
+  //
+  DbPtr->MAC[0] = 0x01;
+  DbPtr->MAC[1] = 0x00;
+  DbPtr->MAC[2] = 0x5e;
+  DbPtr->MAC[3] = (UINT8) (TmpPtr[1] & 0x7f);
+  DbPtr->MAC[4] = (UINT8) TmpPtr[2];
+  DbPtr->MAC[5] = (UINT8) TmpPtr[3];
+
+  return ;
+}
+
+
+/**
+  This routine is used to read and write non-volatile storage on the NIC (if supported).  The NVRAM
+  could be EEPROM, FLASH, or battery backed RAM.
+  This is an optional function according to the UNDI specification  (or will be......)
+
+  @param  CdbPtr               Pointer to the command descriptor block.
+  @param  AdapterInfo          Pointer to the NIC data structure information which
+                               the UNDI driver is layering on..
+
+  @return None
+
+**/
+VOID
+UNDI_NVData (
+  IN  PXE_CDB           *CdbPtr,
+  IN  NIC_DATA_INSTANCE *AdapterInfo
+  )
+{
+  PXE_DB_NVDATA *DbPtr;
+  UINT16        Index;
+
+  if ((CdbPtr->OpFlags == PXE_OPFLAGS_NVDATA_READ) != 0) {
+
+    if ((CdbPtr->DBsize == PXE_DBSIZE_NOT_USED) != 0) {
+      CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+      CdbPtr->StatCode  = PXE_STATCODE_INVALID_CDB;
+      return ;
+    }
+
+    DbPtr = (PXE_DB_NVDATA *) (UINTN) CdbPtr->DBaddr;
+
+    for (Index = 0; Index < MAX_PCI_CONFIG_LEN; Index++) {
+      DbPtr->Data.Dword[Index] = AdapterInfo->NVData[Index];
+
+    }
+
+  } else {
+    //
+    // no write for now
+    //
+    CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    CdbPtr->StatCode  = PXE_STATCODE_UNSUPPORTED;
+  }
+
+  return ;
+}
+
+
+/**
+  This routine returns the current interrupt status and/or the transmitted buffer addresses.
+  If the current interrupt status is returned, pending interrupts will be acknowledged by this
+  command.  Transmitted buffer addresses that are written to the DB are removed from the transmit
+  buffer queue.
+  Normally, this command would be polled with interrupts disabled.
+  The transmit buffers are returned in CdbPtr->DBaddr->TxBufer[0 - NumEntries].
+  The interrupt status is returned in CdbPtr->StatFlags.
+
+  @param  CdbPtr               Pointer to the command descriptor block.
+  @param  AdapterInfo          Pointer to the NIC data structure information which
+                               the UNDI driver is layering on..
+
+  @return None
+
+**/
+VOID
+UNDI_Status (
+  IN  PXE_CDB           *CdbPtr,
+  IN  NIC_DATA_INSTANCE *AdapterInfo
+  )
+{
+  PXE_DB_GET_STATUS *DbPtr;
+  PXE_DB_GET_STATUS TmpGetStatus;
+  UINT16            Index;
+  UINT16            Status;
+  UINT16            NumEntries;
+  RxFD              *RxPtr;
+
+  //
+  // Fill in temporary GetStatus storage.
+  //
+  RxPtr = &AdapterInfo->rx_ring[AdapterInfo->cur_rx_ind];
+
+  if ((RxPtr->cb_header.status & RX_COMPLETE) != 0) {
+    TmpGetStatus.RxFrameLen = RxPtr->ActualCount & 0x3fff;
+  } else {
+    TmpGetStatus.RxFrameLen = 0;
+  }
+
+  TmpGetStatus.reserved = 0;
+
+  //
+  // Fill in size of next available receive packet and
+  // reserved field in caller's DB storage.
+  //
+  DbPtr = (PXE_DB_GET_STATUS *) (UINTN) CdbPtr->DBaddr;
+
+  if (CdbPtr->DBsize > 0 && CdbPtr->DBsize < sizeof (UINT32) * 2) {
+    CopyMem (DbPtr, &TmpGetStatus, CdbPtr->DBsize);
+  } else {
+    CopyMem (DbPtr, &TmpGetStatus, sizeof (UINT32) * 2);
+  }
+
+  //
+  //
+  //
+  if ((CdbPtr->OpFlags & PXE_OPFLAGS_GET_TRANSMITTED_BUFFERS) != 0) {
+    //
+    // DBsize of zero is invalid if Tx buffers are requested.
+    //
+    if (CdbPtr->DBsize == 0) {
+      CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+      CdbPtr->StatCode  = PXE_STATCODE_INVALID_CDB;
+      return ;
+    }
+
+    //
+    // remember this b4 we overwrite
+    //
+    NumEntries = (UINT16) (CdbPtr->DBsize - sizeof (UINT64));
+
+    //
+    // We already filled in 2 UINT32s.
+    //
+    CdbPtr->DBsize = (UINT16) (sizeof (UINT32) * 2);
+
+    //
+    // will claim any hanging free CBs
+    //
+    CheckCBList (AdapterInfo);
+
+    if (AdapterInfo->xmit_done_head == AdapterInfo->xmit_done_tail) {
+      CdbPtr->StatFlags |= PXE_STATFLAGS_GET_STATUS_TXBUF_QUEUE_EMPTY;
+    } else {
+      for (Index = 0; ((Index < MAX_XMIT_BUFFERS) && (NumEntries >= sizeof (UINT64))); Index++, NumEntries -= sizeof (UINT64)) {
+        if (AdapterInfo->xmit_done_head != AdapterInfo->xmit_done_tail) {
+          DbPtr->TxBuffer[Index]      = AdapterInfo->xmit_done[AdapterInfo->xmit_done_head];
+          AdapterInfo->xmit_done_head = next (AdapterInfo->xmit_done_head);
+          CdbPtr->DBsize += sizeof (UINT64);
+        } else {
+          break;
+        }
+      }
+    }
+
+    if (AdapterInfo->xmit_done_head != AdapterInfo->xmit_done_tail) {
+      CdbPtr->StatFlags |= PXE_STATFLAGS_DB_WRITE_TRUNCATED;
+
+    }
+    //
+    // check for a receive buffer and give it's size in db
+    //
+  }
+  //
+  //
+  //
+  if ((CdbPtr->OpFlags & PXE_OPFLAGS_GET_INTERRUPT_STATUS) != 0) {
+
+    Status = InWord (AdapterInfo, AdapterInfo->ioaddr + SCBStatus);
+    AdapterInfo->Int_Status = (UINT16) (AdapterInfo->Int_Status | Status);
+
+    //
+    // acknoledge the interrupts
+    //
+    OutWord (AdapterInfo, (UINT16) (Status & 0xfc00), (UINT32) (AdapterInfo->ioaddr + SCBStatus));
+
+    //
+    // report all the outstanding interrupts
+    //
+    Status = AdapterInfo->Int_Status;
+    if ((Status & SCB_STATUS_FR) != 0) {
+      CdbPtr->StatFlags |= PXE_STATFLAGS_GET_STATUS_RECEIVE;
+    }
+
+    if ((Status & SCB_STATUS_SWI) != 0) {
+      CdbPtr->StatFlags |= PXE_STATFLAGS_GET_STATUS_SOFTWARE;
+    }
+  }
+
+  //
+  // Return current media status
+  //
+  if ((CdbPtr->OpFlags & PXE_OPFLAGS_GET_MEDIA_STATUS) != 0) {
+    AdapterInfo->PhyAddress = 0xFF;
+    AdapterInfo->CableDetect = 1;
+
+    if (!PhyDetect (AdapterInfo)) {
+      CdbPtr->StatFlags |= PXE_STATFLAGS_GET_STATUS_NO_MEDIA;
+    }
+  }
+
+  return ;
+}
+
+
+/**
+  This routine is used to fill media header(s) in transmit packet(s).
+  Copies the MAC address into the media header whether it is dealing
+  with fragmented or non-fragmented packets.
+
+  @param  CdbPtr               Pointer to the command descriptor block.
+  @param  AdapterInfo          Pointer to the NIC data structure information which
+                               the UNDI driver is layering on..
+
+  @return None
+
+**/
+VOID
+UNDI_FillHeader (
+  IN  PXE_CDB           *CdbPtr,
+  IN  NIC_DATA_INSTANCE *AdapterInfo
+  )
+{
+  PXE_CPB_FILL_HEADER             *Cpb;
+  PXE_CPB_FILL_HEADER_FRAGMENTED  *Cpbf;
+  EtherHeader                     *MacHeader;
+  UINTN                           Index;
+
+  if (CdbPtr->CPBsize == PXE_CPBSIZE_NOT_USED) {
+    CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    CdbPtr->StatCode  = PXE_STATCODE_INVALID_CDB;
+    return ;
+  }
+
+  if ((CdbPtr->OpFlags & PXE_OPFLAGS_FILL_HEADER_FRAGMENTED) != 0) {
+    Cpbf = (PXE_CPB_FILL_HEADER_FRAGMENTED *) (UINTN) CdbPtr->CPBaddr;
+
+    //
+    // assume 1st fragment is big enough for the mac header
+    //
+    if ((Cpbf->FragCnt == 0) || (Cpbf->FragDesc[0].FragLen < PXE_MAC_HEADER_LEN_ETHER)) {
+      //
+      // no buffers given
+      //
+      CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+      CdbPtr->StatCode  = PXE_STATCODE_INVALID_CDB;
+      return ;
+    }
+
+    MacHeader = (EtherHeader *) (UINTN) Cpbf->FragDesc[0].FragAddr;
+    //
+    // we don't swap the protocol bytes
+    //
+    MacHeader->type = Cpbf->Protocol;
+
+    for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) {
+      MacHeader->dest_addr[Index] = Cpbf->DestAddr[Index];
+      MacHeader->src_addr[Index]  = Cpbf->SrcAddr[Index];
+    }
+  } else {
+    Cpb       = (PXE_CPB_FILL_HEADER *) (UINTN) CdbPtr->CPBaddr;
+
+    MacHeader = (EtherHeader *) (UINTN) Cpb->MediaHeader;
+    //
+    // we don't swap the protocol bytes
+    //
+    MacHeader->type = Cpb->Protocol;
+
+    for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) {
+      MacHeader->dest_addr[Index] = Cpb->DestAddr[Index];
+      MacHeader->src_addr[Index]  = Cpb->SrcAddr[Index];
+    }
+  }
+
+  return ;
+}
+
+
+/**
+  This routine is used to place a packet into the transmit queue.  The data buffers given to
+  this command are to be considered locked and the application or network driver loses
+  ownership of these buffers and must not free or relocate them until the ownership returns.
+  When the packets are transmitted, a transmit complete interrupt is generated (if interrupts
+  are disabled, the transmit interrupt status is still set and can be checked using the UNDI_Status
+  command.
+  Some implementations and adapters support transmitting multiple packets with one transmit
+  command.  If this feature is supported, the transmit CPBs can be linked in one transmit
+  command.
+  All UNDIs support fragmented frames, now all network devices or protocols do.  If a fragmented
+  frame CPB is given to UNDI and the network device does not support fragmented frames
+  (see !PXE.Implementation flag), the UNDI will have to copy the fragments into a local buffer
+  before transmitting.
+
+  @param  CdbPtr               Pointer to the command descriptor block.
+  @param  AdapterInfo          Pointer to the NIC data structure information which
+                               the UNDI driver is layering on..
+
+  @return None
+
+**/
+VOID
+UNDI_Transmit (
+  IN  PXE_CDB           *CdbPtr,
+  IN  NIC_DATA_INSTANCE *AdapterInfo
+  )
+{
+
+  if (CdbPtr->CPBsize == PXE_CPBSIZE_NOT_USED) {
+    CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    CdbPtr->StatCode  = PXE_STATCODE_INVALID_CDB;
+    return ;
+  }
+
+  CdbPtr->StatCode = (PXE_STATCODE) E100bTransmit (AdapterInfo, CdbPtr->CPBaddr, CdbPtr->OpFlags);
+
+  if (CdbPtr->StatCode != PXE_STATCODE_SUCCESS) {
+    CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+  }
+
+  return ;
+}
+
+
+/**
+  When the network adapter has received a frame, this command is used to copy the frame
+  into the driver/application storage location.  Once a frame has been copied, it is
+  removed from the receive queue.
+
+  @param  CdbPtr               Pointer to the command descriptor block.
+  @param  AdapterInfo          Pointer to the NIC data structure information which
+                               the UNDI driver is layering on..
+
+  @return None
+
+**/
+VOID
+UNDI_Receive (
+  IN  PXE_CDB           *CdbPtr,
+  IN  NIC_DATA_INSTANCE *AdapterInfo
+  )
+{
+
+  //
+  // check if RU has started...
+  //
+  if (!AdapterInfo->Receive_Started) {
+    CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    CdbPtr->StatCode  = PXE_STATCODE_NOT_INITIALIZED;
+    return ;
+  }
+
+
+  CdbPtr->StatCode  = (UINT16) E100bReceive (AdapterInfo, CdbPtr->CPBaddr, CdbPtr->DBaddr);
+  if (CdbPtr->StatCode != PXE_STATCODE_SUCCESS) {
+    CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+
+  }
+
+  return ;
+}
+
+
+
+/**
+  This is the main SW UNDI API entry using the newer nii protocol.
+  The parameter passed in is a 64 bit flat model virtual
+  address of the cdb.  We then jump into the common routine for both old and
+  new nii protocol entries.
+
+  @param  CdbPtr               Pointer to the command descriptor block.
+  @param  AdapterInfo          Pointer to the NIC data structure information which
+                               the UNDI driver is layering on..
+
+  @return None
+
+**/
+// TODO:    cdb - add argument and description to function comment
+VOID
+EFIAPI
+UNDI_APIEntry_new (
+  IN  UINT64 cdb
+  )
+{
+  PXE_CDB           *CdbPtr;
+  NIC_DATA_INSTANCE *AdapterInfo;
+
+  if (cdb == (UINT64) 0) {
+    return ;
+
+  }
+
+  CdbPtr = (PXE_CDB *) (UINTN) cdb;
+
+  if (CdbPtr->IFnum >= (pxe_31->IFcnt | pxe_31->IFcntExt << 8) ) {
+    CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    CdbPtr->StatCode  = PXE_STATCODE_INVALID_CDB;
+    return ;
+  }
+
+  AdapterInfo               = &(UNDI32DeviceList[CdbPtr->IFnum]->NicInfo);
+  //
+  // entering from older entry point
+  //
+  AdapterInfo->VersionFlag  = 0x31;
+  UNDI_APIEntry_Common (cdb);
+}
+
+
+/**
+  This is the common routine for both old and new entry point procedures.
+  The parameter passed in is a 64 bit flat model virtual
+  address of the cdb.  We then jump into the service routine pointed to by the
+  Api_Table[OpCode].
+
+  @param  CdbPtr               Pointer to the command descriptor block.
+  @param  AdapterInfo          Pointer to the NIC data structure information which
+                               the UNDI driver is layering on..
+
+  @return None
+
+**/
+// TODO:    cdb - add argument and description to function comment
+VOID
+UNDI_APIEntry_Common (
+  IN  UINT64 cdb
+  )
+{
+  PXE_CDB           *CdbPtr;
+  NIC_DATA_INSTANCE *AdapterInfo;
+  UNDI_CALL_TABLE   *tab_ptr;
+
+  CdbPtr = (PXE_CDB *) (UINTN) cdb;
+
+  //
+  // check the OPCODE range
+  //
+  if ((CdbPtr->OpCode > PXE_OPCODE_LAST_VALID) ||
+      (CdbPtr->StatCode != PXE_STATCODE_INITIALIZE) ||
+      (CdbPtr->StatFlags != PXE_STATFLAGS_INITIALIZE) ||
+      (CdbPtr->IFnum >= (pxe_31->IFcnt |  pxe_31->IFcntExt << 8))) {
+    goto badcdb;
+
+  }
+
+  if (CdbPtr->CPBsize == PXE_CPBSIZE_NOT_USED) {
+    if (CdbPtr->CPBaddr != PXE_CPBADDR_NOT_USED) {
+      goto badcdb;
+    }
+  } else if (CdbPtr->CPBaddr == PXE_CPBADDR_NOT_USED) {
+    goto badcdb;
+  }
+
+  if (CdbPtr->DBsize == PXE_DBSIZE_NOT_USED) {
+    if (CdbPtr->DBaddr != PXE_DBADDR_NOT_USED) {
+      goto badcdb;
+    }
+  } else if (CdbPtr->DBaddr == PXE_DBADDR_NOT_USED) {
+    goto badcdb;
+  }
+
+  //
+  // check if cpbsize and dbsize are as needed
+  // check if opflags are as expected
+  //
+  tab_ptr = &api_table[CdbPtr->OpCode];
+
+  if (tab_ptr->cpbsize != (UINT16) (DONT_CHECK) && tab_ptr->cpbsize != CdbPtr->CPBsize) {
+    goto badcdb;
+  }
+
+  if (tab_ptr->dbsize != (UINT16) (DONT_CHECK) && tab_ptr->dbsize != CdbPtr->DBsize) {
+    goto badcdb;
+  }
+
+  if (tab_ptr->opflags != (UINT16) (DONT_CHECK) && tab_ptr->opflags != CdbPtr->OpFlags) {
+    goto badcdb;
+
+  }
+
+  AdapterInfo = &(UNDI32DeviceList[CdbPtr->IFnum]->NicInfo);
+
+  //
+  // check if UNDI_State is valid for this call
+  //
+  if (tab_ptr->state != (UINT16) (-1)) {
+    //
+    // should atleast be started
+    //
+    if (AdapterInfo->State == PXE_STATFLAGS_GET_STATE_STOPPED) {
+      CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+      CdbPtr->StatCode  = PXE_STATCODE_NOT_STARTED;
+      return ;
+    }
+    //
+    // check if it should be initialized
+    //
+    if (tab_ptr->state == 2) {
+      if (AdapterInfo->State != PXE_STATFLAGS_GET_STATE_INITIALIZED) {
+        CdbPtr->StatCode  = PXE_STATCODE_NOT_INITIALIZED;
+        CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+        return ;
+      }
+    }
+  }
+  //
+  // set the return variable for success case here
+  //
+  CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE;
+  CdbPtr->StatCode  = PXE_STATCODE_SUCCESS;
+
+  tab_ptr->api_ptr (CdbPtr, AdapterInfo);
+  return ;
+  //
+  // %% AVL - check for command linking
+  //
+badcdb:
+  CdbPtr->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+  CdbPtr->StatCode  = PXE_STATCODE_INVALID_CDB;
+  return ;
+}
+
+
+/**
+  When called with a null NicPtr, this routine decrements the number of NICs
+  this UNDI is supporting and removes the NIC_DATA_POINTER from the array.
+  Otherwise, it increments the number of NICs this UNDI is supported and
+  updates the pxe.Fudge to ensure a proper check sum results.
+
+  @param  NicPtr               Pointer to the NIC data structure.
+
+  @return None
+
+**/
+VOID
+PxeUpdate (
+  IN  NIC_DATA_INSTANCE *NicPtr,
+  IN PXE_SW_UNDI        *PxePtr
+  )
+{
+  UINT16 NicNum;
+  NicNum = (PxePtr->IFcnt | PxePtr->IFcntExt << 8);
+  
+  if (NicPtr == NULL) {
+    if (NicNum > 0) {
+      //
+      // number of NICs this undi supports
+      //
+      NicNum --;
+    }
+    goto done;
+  }
+
+  //
+  // number of NICs this undi supports
+  //
+  NicNum++;
+  
+done: 
+  PxePtr->IFcnt = (UINT8)(NicNum & 0xFF);
+  PxePtr->IFcntExt = (UINT8) ((NicNum & 0xFF00) >> 8);
+  PxePtr->Fudge = (UINT8) (PxePtr->Fudge - CalculateSum8 ((VOID *) PxePtr, PxePtr->Len));
+  return ;
+}
+
+
+/**
+  Initialize the !PXE structure
+
+  @param  PxePtr               Pointer to SW_UNDI data structure.
+
+  @retval EFI_SUCCESS          This driver is added to Controller.
+  @retval other                This driver does not support this device.
+
+**/
+VOID
+PxeStructInit (
+  IN PXE_SW_UNDI *PxePtr
+  )
+{
+  //
+  // Initialize the !PXE structure
+  //
+  PxePtr->Signature = PXE_ROMID_SIGNATURE;
+  PxePtr->Len       = (UINT8) sizeof (PXE_SW_UNDI);
+  //
+  // cksum
+  //
+  PxePtr->Fudge     = 0;
+  //
+  // number of NICs this undi supports
+  //
+  PxePtr->IFcnt = 0;
+  PxePtr->IFcntExt = 0;
+  PxePtr->Rev       = PXE_ROMID_REV;
+  PxePtr->MajorVer  = PXE_ROMID_MAJORVER;
+  PxePtr->MinorVer  = PXE_ROMID_MINORVER;
+  PxePtr->reserved1 = 0;
+
+  PxePtr->Implementation = PXE_ROMID_IMP_SW_VIRT_ADDR |
+    PXE_ROMID_IMP_FRAG_SUPPORTED |
+    PXE_ROMID_IMP_CMD_LINK_SUPPORTED |
+    PXE_ROMID_IMP_NVDATA_READ_ONLY |
+    PXE_ROMID_IMP_STATION_ADDR_SETTABLE |
+    PXE_ROMID_IMP_PROMISCUOUS_MULTICAST_RX_SUPPORTED |
+    PXE_ROMID_IMP_PROMISCUOUS_RX_SUPPORTED |
+    PXE_ROMID_IMP_BROADCAST_RX_SUPPORTED |
+    PXE_ROMID_IMP_FILTERED_MULTICAST_RX_SUPPORTED |
+    PXE_ROMID_IMP_SOFTWARE_INT_SUPPORTED |
+    PXE_ROMID_IMP_PACKET_RX_INT_SUPPORTED;
+
+  PxePtr->EntryPoint  = (UINT64) (UINTN) UNDI_APIEntry_new;
+  PxePtr->MinorVer    = PXE_ROMID_MINORVER_31;
+
+  PxePtr->reserved2[0]  = 0;
+  PxePtr->reserved2[1]  = 0;
+  PxePtr->reserved2[2]  = 0;
+  PxePtr->BusCnt        = 1;
+  PxePtr->BusType[0]    = PXE_BUSTYPE_PCI;
+
+  PxePtr->Fudge         = (UINT8) (PxePtr->Fudge - CalculateSum8 ((VOID *) PxePtr, PxePtr->Len));
+}
+
diff --git a/Drivers/OptionRomPkg/UndiRuntimeDxe/E100b.c b/Drivers/OptionRomPkg/UndiRuntimeDxe/E100b.c
new file mode 100644
index 0000000000..199f5430ad
--- /dev/null
+++ b/Drivers/OptionRomPkg/UndiRuntimeDxe/E100b.c
@@ -0,0 +1,3541 @@
+/** @file
+  Provides basic function upon network adapter card.
+
+Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "Undi32.h"
+
+UINT8 basic_config_cmd[22] = {
+                    22,        0x08,
+                    0,           0,
+                    0, (UINT8)0x80,
+                    0x32,        0x03,
+                    1,            0,
+                    0x2E,           0,
+                    0x60,           0,
+                    (UINT8)0xf2,        0x48,
+                    0,        0x40,
+                    (UINT8)0xf2, (UINT8)0x80, // 0x40=Force full-duplex
+                    0x3f,       0x05,
+};
+
+//
+// How to wait for the command unit to accept a command.
+// Typically this takes 0 ticks.
+//
+#define wait_for_cmd_done(cmd_ioaddr) \
+{                      \
+  INT16 wait_count = 2000;              \
+  while ((InByte (AdapterInfo, cmd_ioaddr) != 0) && --wait_count >= 0)  \
+    DelayIt (AdapterInfo, 10);  \
+  if (wait_count == 0) \
+    DelayIt (AdapterInfo, 50);    \
+}
+
+
+/**
+  This function calls the MemIo callback to read a byte from the device's
+  address space
+  Since UNDI3.0 uses the TmpMemIo function (instead of the callback routine)
+  which also takes the UniqueId parameter (as in UNDI3.1 spec) we don't have
+  to make undi3.0 a special case
+
+  @param  Port                            Which port to read from.
+
+  @retval Results                         The data read from the port.
+
+**/
+// TODO:    AdapterInfo - add argument and description to function comment
+UINT8
+InByte (
+  IN NIC_DATA_INSTANCE *AdapterInfo,
+  IN UINT32            Port
+  )
+{
+  UINT8 Results;
+
+  (*AdapterInfo->Mem_Io) (
+    AdapterInfo->Unique_ID,
+    PXE_MEM_READ,
+    1,
+    (UINT64)Port,
+    (UINT64) (UINTN) &Results
+    );
+  return Results;
+}
+
+
+/**
+  This function calls the MemIo callback to read a word from the device's
+  address space
+  Since UNDI3.0 uses the TmpMemIo function (instead of the callback routine)
+  which also takes the UniqueId parameter (as in UNDI3.1 spec) we don't have
+  to make undi3.0 a special case
+
+  @param  Port                            Which port to read from.
+
+  @retval Results                         The data read from the port.
+
+**/
+// TODO:    AdapterInfo - add argument and description to function comment
+UINT16
+InWord (
+  IN NIC_DATA_INSTANCE *AdapterInfo,
+  IN UINT32            Port
+  )
+{
+  UINT16  Results;
+
+  (*AdapterInfo->Mem_Io) (
+    AdapterInfo->Unique_ID,
+    PXE_MEM_READ,
+    2,
+    (UINT64)Port,
+    (UINT64)(UINTN)&Results
+    );
+  return Results;
+}
+
+
+/**
+  This function calls the MemIo callback to read a dword from the device's
+  address space
+  Since UNDI3.0 uses the TmpMemIo function (instead of the callback routine)
+  which also takes the UniqueId parameter (as in UNDI3.1 spec) we don't have
+  to make undi3.0 a special case
+
+  @param  Port                            Which port to read from.
+
+  @retval Results                         The data read from the port.
+
+**/
+// TODO:    AdapterInfo - add argument and description to function comment
+UINT32
+InLong (
+  IN NIC_DATA_INSTANCE *AdapterInfo,
+  IN UINT32            Port
+  )
+{
+  UINT32  Results;
+
+  (*AdapterInfo->Mem_Io) (
+    AdapterInfo->Unique_ID,
+    PXE_MEM_READ,
+    4,
+    (UINT64)Port,
+    (UINT64)(UINTN)&Results
+    );
+  return Results;
+}
+
+
+/**
+  This function calls the MemIo callback to write a byte from the device's
+  address space
+  Since UNDI3.0 uses the TmpMemIo function (instead of the callback routine)
+  which also takes the UniqueId parameter (as in UNDI3.1 spec) we don't have
+  to make undi3.0 a special case
+
+  @param  Data                            Data to write to Port.
+  @param  Port                            Which port to write to.
+
+  @return none
+
+**/
+// TODO:    AdapterInfo - add argument and description to function comment
+VOID
+OutByte (
+  IN NIC_DATA_INSTANCE *AdapterInfo,
+  IN UINT8             Data,
+  IN UINT32            Port
+  )
+{
+  UINT8 Val;
+
+  Val = Data;
+  (*AdapterInfo->Mem_Io) (
+     AdapterInfo->Unique_ID,
+     PXE_MEM_WRITE,
+     1,
+     (UINT64)Port,
+     (UINT64)(UINTN)(UINTN)&Val
+     );
+  return ;
+}
+
+
+/**
+  This function calls the MemIo callback to write a word from the device's
+  address space
+  Since UNDI3.0 uses the TmpMemIo function (instead of the callback routine)
+  which also takes the UniqueId parameter (as in UNDI3.1 spec) we don't have
+  to make undi3.0 a special case
+
+  @param  Data                            Data to write to Port.
+  @param  Port                            Which port to write to.
+
+  @return none
+
+**/
+// TODO:    AdapterInfo - add argument and description to function comment
+VOID
+OutWord (
+  IN NIC_DATA_INSTANCE *AdapterInfo,
+  IN UINT16            Data,
+  IN UINT32            Port
+  )
+{
+  UINT16  Val;
+
+  Val = Data;
+  (*AdapterInfo->Mem_Io) (
+     AdapterInfo->Unique_ID,
+     PXE_MEM_WRITE,
+     2,
+     (UINT64)Port,
+     (UINT64)(UINTN)&Val
+     );
+  return ;
+}
+
+
+/**
+  This function calls the MemIo callback to write a dword from the device's
+  address space
+  Since UNDI3.0 uses the TmpMemIo function (instead of the callback routine)
+  which also takes the UniqueId parameter (as in UNDI3.1 spec) we don't have
+  to make undi3.0 a special case
+
+  @param  Data                            Data to write to Port.
+  @param  Port                            Which port to write to.
+
+  @return none
+
+**/
+// TODO:    AdapterInfo - add argument and description to function comment
+VOID
+OutLong (
+  IN NIC_DATA_INSTANCE *AdapterInfo,
+  IN UINT32            Data,
+  IN UINT32            Port
+  )
+{
+  UINT32  Val;
+
+  Val = Data;
+  (*AdapterInfo->Mem_Io) (
+     AdapterInfo->Unique_ID,
+     PXE_MEM_WRITE,
+     4,
+     (UINT64)Port,
+     (UINT64)(UINTN)&Val
+     );
+  return ;
+}
+
+
+/**
+  TODO: Add function description
+
+  @param  AdapterInfo                     TODO: add argument description
+  @param  MemAddr                         TODO: add argument description
+  @param  Size                            TODO: add argument description
+  @param  Direction                       TODO: add argument description
+  @param  MappedAddr                      TODO: add argument description
+
+  @return TODO: add return values
+
+**/
+UINTN
+MapIt (
+  IN NIC_DATA_INSTANCE *AdapterInfo,
+  IN UINT64            MemAddr,
+  IN UINT32            Size,
+  IN UINT32            Direction,
+  OUT UINT64           MappedAddr
+  )
+{
+  UINT64  *PhyAddr;
+
+  PhyAddr = (UINT64 *) (UINTN) MappedAddr;
+  //
+  // mapping is different for theold and new NII protocols
+  //
+  if (AdapterInfo->VersionFlag == 0x30) {
+    if (AdapterInfo->Virt2Phys_30 == (VOID *) NULL) {
+      *PhyAddr = (UINT64) AdapterInfo->MemoryPtr;
+    } else {
+      (*AdapterInfo->Virt2Phys_30) (MemAddr, (UINT64) (UINTN) PhyAddr);
+    }
+
+    if (*PhyAddr > FOUR_GIGABYTE) {
+      return PXE_STATCODE_INVALID_PARAMETER;
+    }
+  } else {
+    if (AdapterInfo->Map_Mem == (VOID *) NULL) {
+      //
+      // this UNDI cannot handle addresses beyond 4 GB without a map routine
+      //
+      if (MemAddr > FOUR_GIGABYTE) {
+        return PXE_STATCODE_INVALID_PARAMETER;
+      } else {
+        *PhyAddr = MemAddr;
+      }
+    } else {
+      (*AdapterInfo->Map_Mem) (
+        AdapterInfo->Unique_ID,
+        MemAddr,
+        Size,
+        Direction,
+        MappedAddr
+        );
+    }
+  }
+
+  return PXE_STATCODE_SUCCESS;
+}
+
+
+/**
+  TODO: Add function description
+
+  @param  AdapterInfo                     TODO: add argument description
+  @param  MemAddr                         TODO: add argument description
+  @param  Size                            TODO: add argument description
+  @param  Direction                       TODO: add argument description
+  @param  MappedAddr                      TODO: add argument description
+
+  @return TODO: add return values
+
+**/
+VOID
+UnMapIt (
+  IN NIC_DATA_INSTANCE *AdapterInfo,
+  IN UINT64            MemAddr,
+  IN UINT32            Size,
+  IN UINT32            Direction,
+  IN UINT64            MappedAddr
+  )
+{
+  if (AdapterInfo->VersionFlag > 0x30) {
+    //
+    // no mapping service
+    //
+    if (AdapterInfo->UnMap_Mem != (VOID *) NULL) {
+      (*AdapterInfo->UnMap_Mem) (
+        AdapterInfo->Unique_ID,
+        MemAddr,
+        Size,
+        Direction,
+        MappedAddr
+        );
+
+    }
+  }
+
+  return ;
+}
+
+
+/**
+
+  @param  AdapterInfo                     Pointer to the NIC data structure
+                                          information which the UNDI driver is
+                                          layering on..
+
+
+**/
+// TODO:    MicroSeconds - add argument and description to function comment
+VOID
+DelayIt (
+  IN NIC_DATA_INSTANCE *AdapterInfo,
+  UINT16               MicroSeconds
+  )
+{
+  if (AdapterInfo->VersionFlag == 0x30) {
+    (*AdapterInfo->Delay_30) (MicroSeconds);
+  } else {
+    (*AdapterInfo->Delay) (AdapterInfo->Unique_ID, MicroSeconds);
+  }
+}
+
+
+/**
+
+  @param  AdapterInfo                     Pointer to the NIC data structure
+                                          information which the UNDI driver is
+                                          layering on..
+
+
+**/
+// TODO:    flag - add argument and description to function comment
+VOID
+BlockIt (
+  IN NIC_DATA_INSTANCE *AdapterInfo,
+  UINT32               flag
+  )
+{
+  if (AdapterInfo->VersionFlag == 0x30) {
+    (*AdapterInfo->Block_30) (flag);
+  } else {
+    (*AdapterInfo->Block) (AdapterInfo->Unique_ID, flag);
+  }
+}
+
+
+/**
+  TODO: Add function description
+
+  @param  AdapterInfo                     TODO: add argument description
+
+  @return TODO: add return values
+
+**/
+UINT8
+Load_Base_Regs (
+  NIC_DATA_INSTANCE *AdapterInfo
+  )
+{
+  //
+  // we will use the linear (flat) memory model and fill our base registers
+  // with 0's so that the entire physical address is our offset
+  //
+  //
+  // we reset the statistics totals here because this is where we are loading stats addr
+  //
+  AdapterInfo->RxTotals = 0;
+  AdapterInfo->TxTotals = 0;
+
+  //
+  // Load the statistics block address.
+  //
+  wait_for_cmd_done (AdapterInfo->ioaddr + SCBCmd);
+  OutLong (AdapterInfo, (UINT32) AdapterInfo->stat_phy_addr, AdapterInfo->ioaddr + SCBPointer);
+  OutByte (AdapterInfo, CU_STATSADDR, AdapterInfo->ioaddr + SCBCmd);
+  AdapterInfo->statistics->done_marker = 0;
+
+  wait_for_cmd_done (AdapterInfo->ioaddr + SCBCmd);
+  OutLong (AdapterInfo, 0, AdapterInfo->ioaddr + SCBPointer);
+  OutByte (AdapterInfo, RX_ADDR_LOAD, AdapterInfo->ioaddr + SCBCmd);
+
+  wait_for_cmd_done (AdapterInfo->ioaddr + SCBCmd);
+  OutLong (AdapterInfo, 0, AdapterInfo->ioaddr + SCBPointer);
+  OutByte (AdapterInfo, CU_CMD_BASE, AdapterInfo->ioaddr + SCBCmd);
+
+  return 0;
+}
+
+
+/**
+  TODO: Add function description
+
+  @param  AdapterInfo                     TODO: add argument description
+  @param  cmd_ptr                         TODO: add argument description
+
+  @return TODO: add return values
+
+**/
+UINT8
+IssueCB (
+  NIC_DATA_INSTANCE *AdapterInfo,
+  TxCB              *cmd_ptr
+  )
+{
+  UINT16  status;
+
+  wait_for_cmd_done (AdapterInfo->ioaddr + SCBCmd);
+
+  //
+  // read the CU status, if it is idle, write the address of cb_ptr
+  // in the scbpointer and issue a cu_start,
+  // if it is suspended, remove the suspend bit in the previous command
+  // block and issue a resume
+  //
+  // Ensure that the CU Active Status bit is not on from previous CBs.
+  //
+  status = InWord (AdapterInfo, AdapterInfo->ioaddr + SCBStatus);
+
+  //
+  // Skip acknowledging the interrupt if it is not already set
+  //
+
+  //
+  // ack only the cna the integer
+  //
+  if ((status & SCB_STATUS_CNA) != 0) {
+    OutWord (AdapterInfo, SCB_STATUS_CNA, AdapterInfo->ioaddr + SCBStatus);
+
+  }
+
+  if ((status & SCB_STATUS_CU_MASK) == SCB_STATUS_CU_IDLE) {
+    //
+    // give a cu_start
+    //
+    OutLong (AdapterInfo, cmd_ptr->PhysTCBAddress, AdapterInfo->ioaddr + SCBPointer);
+    OutByte (AdapterInfo, CU_START, AdapterInfo->ioaddr + SCBCmd);
+  } else {
+    //
+    // either active or suspended, give a resume
+    //
+
+    cmd_ptr->PrevTCBVirtualLinkPtr->cb_header.command &= ~(CmdSuspend | CmdIntr);
+    OutByte (AdapterInfo, CU_RESUME, AdapterInfo->ioaddr + SCBCmd);
+  }
+
+  return 0;
+}
+
+
+/**
+  TODO: Add function description
+
+  @param  AdapterInfo                     TODO: add argument description
+
+  @return TODO: add return values
+
+**/
+UINT8
+Configure (
+  NIC_DATA_INSTANCE *AdapterInfo
+  )
+{
+  //
+  // all command blocks are of TxCB format
+  //
+  TxCB  *cmd_ptr;
+  UINT8 *data_ptr;
+  volatile INT16 Index;
+  UINT8 my_filter;
+
+  cmd_ptr   = GetFreeCB (AdapterInfo);
+  ASSERT (cmd_ptr != NULL);
+  data_ptr  = (UINT8 *) cmd_ptr + sizeof (struct CB_Header);
+
+  //
+  // start the config data right after the command header
+  //
+  for (Index = 0; Index < sizeof (basic_config_cmd); Index++) {
+    data_ptr[Index] = basic_config_cmd[Index];
+  }
+
+  my_filter = (UINT8) ((AdapterInfo->Rx_Filter & PXE_OPFLAGS_RECEIVE_FILTER_PROMISCUOUS) ? 1 : 0);
+  my_filter = (UINT8) (my_filter | ((AdapterInfo->Rx_Filter & PXE_OPFLAGS_RECEIVE_FILTER_BROADCAST) ? 0 : 2));
+
+  data_ptr[15]  = (UINT8) (data_ptr[15] | my_filter);
+  data_ptr[19]  = (UINT8) (AdapterInfo->Duplex ? 0xC0 : 0x80);
+  data_ptr[21]  = (UINT8) ((AdapterInfo->Rx_Filter & PXE_OPFLAGS_RECEIVE_FILTER_ALL_MULTICAST) ? 0x0D : 0x05);
+
+  //
+  // check if we have to use the AUI port instead
+  //
+  if ((AdapterInfo->PhyRecord[0] & 0x8000) != 0) {
+    data_ptr[15] |= 0x80;
+    data_ptr[8] = 0;
+  }
+
+  BlockIt (AdapterInfo, TRUE);
+  cmd_ptr->cb_header.command = CmdSuspend | CmdConfigure;
+
+  IssueCB (AdapterInfo, cmd_ptr);
+  wait_for_cmd_done (AdapterInfo->ioaddr + SCBCmd);
+
+  BlockIt (AdapterInfo, FALSE);
+
+  CommandWaitForCompletion (cmd_ptr, AdapterInfo);
+
+  //
+  // restore the cb values for tx
+  //
+  cmd_ptr->PhysTBDArrayAddres = cmd_ptr->PhysArrayAddr;
+  cmd_ptr->ByteCount = cmd_ptr->Threshold = cmd_ptr->TBDCount = 0;
+  //
+  // fields beyond the immediatedata are assumed to be safe
+  // add the CB to the free list again
+  //
+  SetFreeCB (AdapterInfo, cmd_ptr);
+  return 0;
+}
+
+
+/**
+  TODO: Add function description
+
+  @param  AdapterInfo                     TODO: add argument description
+
+  @return TODO: add return values
+
+**/
+UINT8
+E100bSetupIAAddr (
+  NIC_DATA_INSTANCE *AdapterInfo
+  )
+{
+  //
+  // all command blocks are of TxCB format
+  //
+  TxCB    *cmd_ptr;
+  UINT16  *data_ptr;
+  UINT16  *eaddrs;
+
+  eaddrs    = (UINT16 *) AdapterInfo->CurrentNodeAddress;
+
+  cmd_ptr   = GetFreeCB (AdapterInfo);
+  ASSERT (cmd_ptr != NULL);
+  data_ptr  = (UINT16 *) ((UINT8 *) cmd_ptr +sizeof (struct CB_Header));
+
+  //
+  // AVOID a bug (?!) here by marking the command already completed.
+  //
+  cmd_ptr->cb_header.command  = (CmdSuspend | CmdIASetup);
+  cmd_ptr->cb_header.status   = 0;
+  data_ptr[0]                 = eaddrs[0];
+  data_ptr[1]                 = eaddrs[1];
+  data_ptr[2]                 = eaddrs[2];
+
+  BlockIt (AdapterInfo, TRUE);
+  IssueCB (AdapterInfo, cmd_ptr);
+  wait_for_cmd_done (AdapterInfo->ioaddr + SCBCmd);
+  BlockIt (AdapterInfo, FALSE);
+
+  CommandWaitForCompletion (cmd_ptr, AdapterInfo);
+
+  //
+  // restore the cb values for tx
+  //
+  cmd_ptr->PhysTBDArrayAddres = cmd_ptr->PhysArrayAddr;
+  cmd_ptr->ByteCount = cmd_ptr->Threshold = cmd_ptr->TBDCount = 0;
+  //
+  // fields beyond the immediatedata are assumed to be safe
+  // add the CB to the free list again
+  //
+  SetFreeCB (AdapterInfo, cmd_ptr);
+  return 0;
+}
+
+
+/**
+  Instructs the NIC to stop receiving packets.
+
+  @param  AdapterInfo                     Pointer to the NIC data structure
+                                          information which the UNDI driver is
+                                          layering on..
+
+
+**/
+VOID
+StopRU (
+  IN NIC_DATA_INSTANCE *AdapterInfo
+  )
+{
+  if (AdapterInfo->Receive_Started) {
+
+    //
+    // Todo: verify that we must wait for previous command completion.
+    //
+    wait_for_cmd_done (AdapterInfo->ioaddr + SCBCmd);
+
+    //
+    // Disable interrupts, and stop the chip's Rx process.
+    //
+    OutWord (AdapterInfo, INT_MASK, AdapterInfo->ioaddr + SCBCmd);
+    OutWord (AdapterInfo, INT_MASK | RX_ABORT, AdapterInfo->ioaddr + SCBCmd);
+
+    AdapterInfo->Receive_Started = FALSE;
+  }
+
+  return ;
+}
+
+
+/**
+  Instructs the NIC to start receiving packets.
+
+  @param  AdapterInfo                     Pointer to the NIC data structure
+                                          information which the UNDI driver is
+                                          layering on..
+
+  @retval 0                               Successful
+  @retval -1                              Already Started
+
+**/
+INT8
+StartRU (
+  NIC_DATA_INSTANCE *AdapterInfo
+  )
+{
+
+  if (AdapterInfo->Receive_Started) {
+    //
+    // already started
+    //
+    return -1;
+  }
+
+  AdapterInfo->cur_rx_ind = 0;
+  AdapterInfo->Int_Status = 0;
+
+  wait_for_cmd_done (AdapterInfo->ioaddr + SCBCmd);
+
+  OutLong (AdapterInfo, (UINT32) AdapterInfo->rx_phy_addr, AdapterInfo->ioaddr + SCBPointer);
+  OutByte (AdapterInfo, RX_START, AdapterInfo->ioaddr + SCBCmd);
+
+  wait_for_cmd_done (AdapterInfo->ioaddr + SCBCmd);
+
+  AdapterInfo->Receive_Started = TRUE;
+  return 0;
+}
+
+
+/**
+  Configures the chip.  This routine expects the NIC_DATA_INSTANCE structure to be filled in.
+
+  @param  AdapterInfo                     Pointer to the NIC data structure
+                                          information which the UNDI driver is
+                                          layering on..
+
+  @retval 0                               Successful
+  @retval PXE_STATCODE_NOT_ENOUGH_MEMORY  Insufficient length of locked memory
+  @retval other                           Failure initializing chip
+
+**/
+UINTN
+E100bInit (
+  IN NIC_DATA_INSTANCE *AdapterInfo
+  )
+{
+  PCI_CONFIG_HEADER *CfgHdr;
+  UINTN             stat;
+  UINTN             rx_size;
+  UINTN             tx_size;
+
+  if (AdapterInfo->MemoryLength < MEMORY_NEEDED) {
+    return PXE_STATCODE_NOT_ENOUGH_MEMORY;
+  }
+
+  stat = MapIt (
+          AdapterInfo,
+          AdapterInfo->MemoryPtr,
+          AdapterInfo->MemoryLength,
+          TO_AND_FROM_DEVICE,
+          (UINT64)(UINTN) &AdapterInfo->Mapped_MemoryPtr
+          );
+
+  if (stat != 0) {
+    return stat;
+  }
+
+  CfgHdr = (PCI_CONFIG_HEADER *) &(AdapterInfo->Config[0]);
+
+  //
+  // fill in the ioaddr, int... from the config space
+  //
+  AdapterInfo->int_num = CfgHdr->int_line;
+
+  //
+  // we don't need to validate integer number, what if they don't want to assign one?
+  // if (AdapterInfo->int_num == 0 || AdapterInfo->int_num == 0xff)
+  // return PXE_STATCODE_DEVICE_FAILURE;
+  //
+  AdapterInfo->ioaddr       = 0;
+  AdapterInfo->VendorID     = CfgHdr->VendorID;
+  AdapterInfo->DeviceID     = CfgHdr->DeviceID;
+  AdapterInfo->RevID        = CfgHdr->RevID;
+  AdapterInfo->SubVendorID  = CfgHdr->SubVendorID;
+  AdapterInfo->SubSystemID  = CfgHdr->SubSystemID;
+  AdapterInfo->flash_addr   = 0;
+
+  //
+  // Read the station address EEPROM before doing the reset.
+  // Perhaps this should even be done before accepting the device,
+  // then we wouldn't have a device name with which to report the error.
+  //
+  if (E100bReadEepromAndStationAddress (AdapterInfo) != 0) {
+    return PXE_STATCODE_DEVICE_FAILURE;
+
+  }
+  //
+  // ## calculate the buffer #s depending on memory given
+  // ## calculate the rx and tx ring pointers
+  //
+
+  AdapterInfo->TxBufCnt       = TX_BUFFER_COUNT;
+  AdapterInfo->RxBufCnt       = RX_BUFFER_COUNT;
+  rx_size                     = (AdapterInfo->RxBufCnt * sizeof (RxFD));
+  tx_size                     = (AdapterInfo->TxBufCnt * sizeof (TxCB));
+  AdapterInfo->rx_ring        = (RxFD *) (UINTN) (AdapterInfo->MemoryPtr);
+  AdapterInfo->tx_ring        = (TxCB *) (UINTN) (AdapterInfo->MemoryPtr + rx_size);
+  AdapterInfo->statistics     = (struct speedo_stats *) (UINTN) (AdapterInfo->MemoryPtr + rx_size + tx_size);
+
+  AdapterInfo->rx_phy_addr    = AdapterInfo->Mapped_MemoryPtr;
+  AdapterInfo->tx_phy_addr    = AdapterInfo->Mapped_MemoryPtr + rx_size;
+  AdapterInfo->stat_phy_addr  = AdapterInfo->tx_phy_addr + tx_size;
+
+  //
+  // auto detect.
+  //
+  AdapterInfo->PhyAddress     = 0xFF;
+  AdapterInfo->Rx_Filter            = PXE_OPFLAGS_RECEIVE_FILTER_BROADCAST;
+  AdapterInfo->Receive_Started      = FALSE;
+  AdapterInfo->mcast_list.list_len  = 0;
+  return InitializeChip (AdapterInfo);
+}
+
+
+/**
+  Sets the interrupt state for the NIC.
+
+  @param  AdapterInfo                     Pointer to the NIC data structure
+                                          information which the UNDI driver is
+                                          layering on..
+
+  @retval 0                               Successful
+
+**/
+UINT8
+E100bSetInterruptState (
+  IN NIC_DATA_INSTANCE *AdapterInfo
+  )
+{
+  //
+  // don't set receive interrupt if receiver is disabled...
+  //
+  UINT16  cmd_word;
+
+  if ((AdapterInfo->int_mask & PXE_OPFLAGS_INTERRUPT_RECEIVE) != 0) {
+    cmd_word = InWord (AdapterInfo, AdapterInfo->ioaddr + SCBCmd);
+    cmd_word &= ~INT_MASK;
+    OutWord (AdapterInfo, cmd_word, AdapterInfo->ioaddr + SCBCmd);
+  } else {
+    //
+    // disable ints, should not be given for SW Int.
+    //
+    OutWord (AdapterInfo, INT_MASK, AdapterInfo->ioaddr + SCBCmd);
+  }
+
+  if ((AdapterInfo->int_mask & PXE_OPFLAGS_INTERRUPT_SOFTWARE) != 0) {
+    //
+    // reset the bit in our mask, it is only one time!!
+    //
+    AdapterInfo->int_mask &= ~(PXE_OPFLAGS_INTERRUPT_SOFTWARE);
+    cmd_word = InWord (AdapterInfo, AdapterInfo->ioaddr + SCBCmd);
+    cmd_word |= DRVR_INT;
+    OutWord (AdapterInfo, cmd_word, AdapterInfo->ioaddr + SCBCmd);
+  }
+
+  return 0;
+}
+//
+// we are not going to disable broadcast for the WOL's sake!
+//
+
+/**
+  Instructs the NIC to start receiving packets.
+
+  @param  AdapterInfo                     Pointer to the NIC data structure
+                                          information which the UNDI driver is
+                                          layering on.. new_filter
+                                              - cpb                             -
+                                          cpbsize                         -
+
+  @retval 0                               Successful
+  @retval -1                              Already Started
+
+**/
+UINTN
+E100bSetfilter (
+  NIC_DATA_INSTANCE *AdapterInfo,
+  UINT16            new_filter,
+  UINT64            cpb,
+  UINT32            cpbsize
+  )
+{
+  PXE_CPB_RECEIVE_FILTERS *mc_list = (PXE_CPB_RECEIVE_FILTERS *) (UINTN)cpb;
+  UINT16                  cfg_flt;
+  UINT16                  old_filter;
+  UINT16                  Index;
+  UINT16                  Index2;
+  UINT16                  mc_count;
+  TxCB                    *cmd_ptr;
+  struct MC_CB_STRUCT     *data_ptr;
+  UINT16                  mc_byte_cnt;
+
+  old_filter  = AdapterInfo->Rx_Filter;
+
+  //
+  // only these bits need a change in the configuration
+  // actually change in bcast requires configure but we ignore that change
+  //
+  cfg_flt = PXE_OPFLAGS_RECEIVE_FILTER_PROMISCUOUS |
+            PXE_OPFLAGS_RECEIVE_FILTER_ALL_MULTICAST;
+
+  if ((old_filter & cfg_flt) != (new_filter & cfg_flt)) {
+    XmitWaitForCompletion (AdapterInfo);
+
+    if (AdapterInfo->Receive_Started) {
+      StopRU (AdapterInfo);
+    }
+
+    AdapterInfo->Rx_Filter = (UINT8) (new_filter | PXE_OPFLAGS_RECEIVE_FILTER_BROADCAST);
+    Configure (AdapterInfo);
+  }
+
+  //
+  // check if mcast setting changed
+  //
+  if ( ((new_filter & PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST) !=
+       (old_filter & PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST) ) ||
+       (mc_list != NULL) ) {
+
+
+    if (mc_list != NULL) {
+      mc_count = AdapterInfo->mcast_list.list_len = (UINT16) (cpbsize / PXE_MAC_LENGTH);
+
+      for (Index = 0; (Index < mc_count && Index < MAX_MCAST_ADDRESS_CNT); Index++) {
+        for (Index2 = 0; Index2 < PXE_MAC_LENGTH; Index2++) {
+          AdapterInfo->mcast_list.mc_list[Index][Index2] = mc_list->MCastList[Index][Index2];
+        }
+      }
+    }
+
+    //
+    // are we setting the list or resetting??
+    //
+    if ((new_filter & PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST) != 0) {
+      //
+      // we are setting a new list!
+      //
+      mc_count = AdapterInfo->mcast_list.list_len;
+      //
+      // count should be the actual # of bytes in the list
+      // so multiply this with 6
+      //
+      mc_byte_cnt = (UINT16) ((mc_count << 2) + (mc_count << 1));
+      AdapterInfo->Rx_Filter |= PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST;
+    } else {
+      //
+      // disabling the list in the NIC.
+      //
+      mc_byte_cnt = mc_count = 0;
+      AdapterInfo->Rx_Filter &= (~PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST);
+    }
+
+    //
+    // before issuing any new command!
+    //
+    XmitWaitForCompletion (AdapterInfo);
+
+    if (AdapterInfo->Receive_Started) {
+      StopRU (AdapterInfo);
+
+    }
+
+    cmd_ptr = GetFreeCB (AdapterInfo);
+    if (cmd_ptr == NULL) {
+      return PXE_STATCODE_QUEUE_FULL;
+    }
+    //
+    // fill the command structure and issue
+    //
+    data_ptr = (struct MC_CB_STRUCT *) (&cmd_ptr->PhysTBDArrayAddres);
+    //
+    // first 2 bytes are the count;
+    //
+    data_ptr->count = mc_byte_cnt;
+    for (Index = 0; Index < mc_count; Index++) {
+      for (Index2 = 0; Index2 < PXE_HWADDR_LEN_ETHER; Index2++) {
+        data_ptr->m_list[Index][Index2] = AdapterInfo->mcast_list.mc_list[Index][Index2];
+      }
+    }
+
+    cmd_ptr->cb_header.command  = CmdSuspend | CmdMulticastList;
+    cmd_ptr->cb_header.status   = 0;
+
+    BlockIt (AdapterInfo, TRUE);
+    IssueCB (AdapterInfo, cmd_ptr);
+    wait_for_cmd_done (AdapterInfo->ioaddr + SCBCmd);
+
+    BlockIt (AdapterInfo, FALSE);
+
+    CommandWaitForCompletion (cmd_ptr, AdapterInfo);
+
+    cmd_ptr->PhysTBDArrayAddres = cmd_ptr->PhysArrayAddr;
+    cmd_ptr->ByteCount = cmd_ptr->Threshold = cmd_ptr->TBDCount = 0;
+    //
+    // fields beyond the immediatedata are assumed to be safe
+    // add the CB to the free list again
+    //
+    SetFreeCB (AdapterInfo, cmd_ptr);
+  }
+
+  if (new_filter != 0) {
+    //
+    // enable unicast and start the RU
+    //
+    AdapterInfo->Rx_Filter = (UINT8) (AdapterInfo->Rx_Filter | (new_filter | PXE_OPFLAGS_RECEIVE_FILTER_UNICAST));
+    StartRU (AdapterInfo);
+  } else {
+    //
+    // may be disabling everything!
+    //
+    if (AdapterInfo->Receive_Started) {
+      StopRU (AdapterInfo);
+    }
+
+    AdapterInfo->Rx_Filter |= (~PXE_OPFLAGS_RECEIVE_FILTER_UNICAST);
+  }
+
+  return 0;
+}
+
+
+/**
+  TODO: Add function description
+
+  @param  AdapterInfo                     TODO: add argument description
+  @param  cpb                             TODO: add argument description
+  @param  opflags                         TODO: add argument description
+
+  @return TODO: add return values
+
+**/
+UINTN
+E100bTransmit (
+  NIC_DATA_INSTANCE *AdapterInfo,
+  UINT64            cpb,
+  UINT16            opflags
+  )
+{
+  PXE_CPB_TRANSMIT_FRAGMENTS  *tx_ptr_f;
+  PXE_CPB_TRANSMIT            *tx_ptr_1;
+  TxCB                        *tcb_ptr;
+  UINT64                      Tmp_ptr;
+  UINTN                       stat;
+  INT32                       Index;
+  UINT16                      wait_sec;
+
+  tx_ptr_1  = (PXE_CPB_TRANSMIT *) (UINTN) cpb;
+  tx_ptr_f  = (PXE_CPB_TRANSMIT_FRAGMENTS *) (UINTN) cpb;
+  Tmp_ptr = 0;
+
+  //
+  // stop reentrancy here
+  //
+  if (AdapterInfo->in_transmit) {
+    return PXE_STATCODE_BUSY;
+
+  }
+
+  AdapterInfo->in_transmit = TRUE;
+
+  //
+  // Prevent interrupts from changing the Tx ring from underneath us.
+  //
+  // Calculate the Tx descriptor entry.
+  //
+  if ((tcb_ptr = GetFreeCB (AdapterInfo)) == NULL) {
+    AdapterInfo->in_transmit = FALSE;
+    return PXE_STATCODE_QUEUE_FULL;
+  }
+
+  AdapterInfo->TxTotals++;
+
+  tcb_ptr->cb_header.command  = (CmdSuspend | CmdTx | CmdTxFlex);
+  tcb_ptr->cb_header.status   = 0;
+
+  //
+  // no immediate data, set EOF in the ByteCount
+  //
+  tcb_ptr->ByteCount = 0x8000;
+
+  //
+  // The data region is always in one buffer descriptor, Tx FIFO
+  // threshold of 256.
+  // 82557 multiplies the threashold value by 8, so give 256/8
+  //
+  tcb_ptr->Threshold = 32;
+  if ((opflags & PXE_OPFLAGS_TRANSMIT_FRAGMENTED) != 0) {
+
+    if (tx_ptr_f->FragCnt > MAX_XMIT_FRAGMENTS) {
+      SetFreeCB (AdapterInfo, tcb_ptr);
+      AdapterInfo->in_transmit = FALSE;
+      return PXE_STATCODE_INVALID_PARAMETER;
+    }
+
+    tcb_ptr->TBDCount = (UINT8) tx_ptr_f->FragCnt;
+
+    for (Index = 0; Index < tx_ptr_f->FragCnt; Index++) {
+      stat = MapIt (
+              AdapterInfo,
+              tx_ptr_f->FragDesc[Index].FragAddr,
+              tx_ptr_f->FragDesc[Index].FragLen,
+              TO_DEVICE,
+              (UINT64)(UINTN) &Tmp_ptr
+              );
+      if (stat != 0) {
+        SetFreeCB (AdapterInfo, tcb_ptr);
+        AdapterInfo->in_transmit = FALSE;
+        return PXE_STATCODE_INVALID_PARAMETER;
+      }
+
+      tcb_ptr->TBDArray[Index].phys_buf_addr  = (UINT32) Tmp_ptr;
+      tcb_ptr->TBDArray[Index].buf_len        = tx_ptr_f->FragDesc[Index].FragLen;
+    }
+
+    tcb_ptr->free_data_ptr = tx_ptr_f->FragDesc[0].FragAddr;
+
+  } else {
+    //
+    // non fragmented case
+    //
+    tcb_ptr->TBDCount = 1;
+    stat = MapIt (
+            AdapterInfo,
+            tx_ptr_1->FrameAddr,
+            tx_ptr_1->DataLen + tx_ptr_1->MediaheaderLen,
+            TO_DEVICE,
+            (UINT64)(UINTN) &Tmp_ptr
+            );
+    if (stat != 0) {
+      SetFreeCB (AdapterInfo, tcb_ptr);
+      AdapterInfo->in_transmit = FALSE;
+      return PXE_STATCODE_INVALID_PARAMETER;
+    }
+
+    tcb_ptr->TBDArray[0].phys_buf_addr  = (UINT32) (Tmp_ptr);
+    tcb_ptr->TBDArray[0].buf_len        = tx_ptr_1->DataLen + tx_ptr_1->MediaheaderLen;
+    tcb_ptr->free_data_ptr              = tx_ptr_1->FrameAddr;
+  }
+
+  //
+  // must wait for previous command completion only if it was a non-transmit
+  //
+  BlockIt (AdapterInfo, TRUE);
+  IssueCB (AdapterInfo, tcb_ptr);
+  BlockIt (AdapterInfo, FALSE);
+
+  //
+  // see if we need to wait for completion here
+  //
+  if ((opflags & PXE_OPFLAGS_TRANSMIT_BLOCK) != 0) {
+    //
+    // don't wait for more than 1 second!!!
+    //
+    wait_sec = 1000;
+    while (tcb_ptr->cb_header.status == 0) {
+      DelayIt (AdapterInfo, 10);
+      wait_sec--;
+      if (wait_sec == 0) {
+        break;
+      }
+    }
+    //
+    // we need to un-map any mapped buffers here
+    //
+    if ((opflags & PXE_OPFLAGS_TRANSMIT_FRAGMENTED) != 0) {
+
+      for (Index = 0; Index < tx_ptr_f->FragCnt; Index++) {
+        Tmp_ptr = tcb_ptr->TBDArray[Index].phys_buf_addr;
+        UnMapIt (
+          AdapterInfo,
+          tx_ptr_f->FragDesc[Index].FragAddr,
+          tx_ptr_f->FragDesc[Index].FragLen,
+          TO_DEVICE,
+          (UINT64) Tmp_ptr
+          );
+      }
+    } else {
+      Tmp_ptr = tcb_ptr->TBDArray[0].phys_buf_addr;
+      UnMapIt (
+        AdapterInfo,
+        tx_ptr_1->FrameAddr,
+        tx_ptr_1->DataLen + tx_ptr_1->MediaheaderLen,
+        TO_DEVICE,
+        (UINT64) Tmp_ptr
+        );
+    }
+
+    if (tcb_ptr->cb_header.status == 0) {
+      SetFreeCB (AdapterInfo, tcb_ptr);
+      AdapterInfo->in_transmit = FALSE;
+      return PXE_STATCODE_DEVICE_FAILURE;
+    }
+
+    SetFreeCB (AdapterInfo, tcb_ptr);
+  }
+  //
+  // CB will be set free later in get_status (or when we run out of xmit buffers
+  //
+  AdapterInfo->in_transmit = FALSE;
+
+  return 0;
+}
+
+
+/**
+  TODO: Add function description
+
+  @param  AdapterInfo                     TODO: add argument description
+  @param  cpb                             TODO: add argument description
+  @param  db                              TODO: add argument description
+
+  @return TODO: add return values
+
+**/
+UINTN
+E100bReceive (
+  NIC_DATA_INSTANCE *AdapterInfo,
+  UINT64            cpb,
+  UINT64            db
+  )
+{
+  PXE_CPB_RECEIVE *rx_cpbptr;
+  PXE_DB_RECEIVE  *rx_dbptr;
+  RxFD            *rx_ptr;
+  INT32           status;
+  INT32           Index;
+  UINT16          pkt_len;
+  UINT16          ret_code;
+  PXE_FRAME_TYPE  pkt_type;
+  UINT16          Tmp_len;
+  EtherHeader     *hdr_ptr;
+  ret_code  = PXE_STATCODE_NO_DATA;
+  pkt_type  = PXE_FRAME_TYPE_NONE;
+  status    = InWord (AdapterInfo, AdapterInfo->ioaddr + SCBStatus);
+  AdapterInfo->Int_Status = (UINT16) (AdapterInfo->Int_Status | status);
+  //
+  // acknoledge the interrupts
+  //
+  OutWord (AdapterInfo, (UINT16) (status & 0xfc00), (UINT32) (AdapterInfo->ioaddr + SCBStatus));
+
+  //
+  // include the prev ints as well
+  //
+  status = AdapterInfo->Int_Status;
+  rx_cpbptr = (PXE_CPB_RECEIVE *) (UINTN) cpb;
+  rx_dbptr  = (PXE_DB_RECEIVE *) (UINTN) db;
+
+  rx_ptr    = &AdapterInfo->rx_ring[AdapterInfo->cur_rx_ind];
+
+  //
+  // be in a loop just in case (we may drop a pkt)
+  //
+  while ((status = rx_ptr->cb_header.status) & RX_COMPLETE) {
+
+    AdapterInfo->RxTotals++;
+    //
+    // If we own the next entry, it's a new packet. Send it up.
+    //
+    if (rx_ptr->forwarded) {
+      goto FreeRFD;
+
+    }
+
+    //
+    // discard bad frames
+    //
+
+    //
+    // crc, align, dma overrun, too short, receive error (v22 no coll)
+    //
+    if ((status & 0x0D90) != 0) {
+      goto FreeRFD;
+
+    }
+
+    //
+    // make sure the status is OK
+    //
+    if ((status & 0x02000) == 0) {
+      goto FreeRFD;
+    }
+
+    pkt_len = (UINT16) (rx_ptr->ActualCount & 0x3fff);
+
+    if (pkt_len != 0) {
+
+      Tmp_len = pkt_len;
+      if (pkt_len > rx_cpbptr->BufferLen) {
+        Tmp_len = (UINT16) rx_cpbptr->BufferLen;
+      }
+
+      CopyMem ((INT8 *) (UINTN) rx_cpbptr->BufferAddr, (INT8 *) &rx_ptr->RFDBuffer, Tmp_len);
+
+      hdr_ptr = (EtherHeader *) &rx_ptr->RFDBuffer;
+      //
+      // fill the CDB and break the loop
+      //
+
+      //
+      // includes header
+      //
+      rx_dbptr->FrameLen = pkt_len;
+      rx_dbptr->MediaHeaderLen = PXE_MAC_HEADER_LEN_ETHER;
+
+      for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) {
+        if (hdr_ptr->dest_addr[Index] != AdapterInfo->CurrentNodeAddress[Index]) {
+          break;
+        }
+      }
+
+      if (Index >= PXE_HWADDR_LEN_ETHER) {
+        pkt_type = PXE_FRAME_TYPE_UNICAST;
+      } else {
+        for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) {
+          if (hdr_ptr->dest_addr[Index] != AdapterInfo->BroadcastNodeAddress[Index]) {
+            break;
+          }
+        }
+
+        if (Index >= PXE_HWADDR_LEN_ETHER) {
+          pkt_type = PXE_FRAME_TYPE_BROADCAST;
+        } else {
+          if ((hdr_ptr->dest_addr[0] & 1) == 1) {
+            //
+            // mcast
+            //
+
+            pkt_type = PXE_FRAME_TYPE_FILTERED_MULTICAST;
+          } else {
+            pkt_type = PXE_FRAME_TYPE_PROMISCUOUS;
+          }
+        }
+      }
+
+      rx_dbptr->Type      = pkt_type;
+      rx_dbptr->Protocol  = hdr_ptr->type;
+
+      for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) {
+        rx_dbptr->SrcAddr[Index]  = hdr_ptr->src_addr[Index];
+        rx_dbptr->DestAddr[Index] = hdr_ptr->dest_addr[Index];
+      }
+
+      rx_ptr->forwarded = TRUE;
+      //
+      // success
+      //
+      ret_code          = 0;
+      Recycle_RFD (AdapterInfo, AdapterInfo->cur_rx_ind);
+      AdapterInfo->cur_rx_ind++;
+      if (AdapterInfo->cur_rx_ind == AdapterInfo->RxBufCnt) {
+        AdapterInfo->cur_rx_ind = 0;
+      }
+      break;
+    }
+
+FreeRFD:
+    Recycle_RFD (AdapterInfo, AdapterInfo->cur_rx_ind);
+    AdapterInfo->cur_rx_ind++;
+    if (AdapterInfo->cur_rx_ind == AdapterInfo->RxBufCnt) {
+      AdapterInfo->cur_rx_ind = 0;
+    }
+
+    rx_ptr = &AdapterInfo->rx_ring[AdapterInfo->cur_rx_ind];
+  }
+
+  if (pkt_type == PXE_FRAME_TYPE_NONE) {
+    AdapterInfo->Int_Status &= (~SCB_STATUS_FR);
+  }
+
+  status = InWord (AdapterInfo, AdapterInfo->ioaddr + SCBStatus);
+  if ((status & SCB_RUS_NO_RESOURCES) != 0) {
+    //
+    // start the receive unit here!
+    // leave all the filled frames,
+    //
+    SetupReceiveQueues (AdapterInfo);
+    OutLong (AdapterInfo, (UINT32) AdapterInfo->rx_phy_addr, AdapterInfo->ioaddr + SCBPointer);
+    OutWord (AdapterInfo, RX_START, AdapterInfo->ioaddr + SCBCmd);
+    AdapterInfo->cur_rx_ind = 0;
+  }
+
+  return ret_code;
+}
+
+
+/**
+  TODO: Add function description
+
+  @param  AdapterInfo                     TODO: add argument description
+
+  @return TODO: add return values
+
+**/
+INT16
+E100bReadEepromAndStationAddress (
+  NIC_DATA_INSTANCE *AdapterInfo
+  )
+{
+  INT32   Index;
+  INT32   Index2;
+  UINT16  sum;
+  UINT16  eeprom_len;
+  UINT8   addr_len;
+  UINT16  *eedata;
+
+  eedata    = (UINT16 *) (&AdapterInfo->NVData[0]);
+
+  sum       = 0;
+  addr_len  = E100bGetEepromAddrLen (AdapterInfo);
+
+  //
+  // in words
+  //
+  AdapterInfo->NVData_Len = eeprom_len = (UINT16) (1 << addr_len);
+  for (Index2 = 0, Index = 0; ((Index2 < PXE_MAC_LENGTH - 1) && (Index < eeprom_len)); Index++) {
+    UINT16  value;
+    value         = E100bReadEeprom (AdapterInfo, Index, addr_len);
+    eedata[Index] = value;
+    sum           = (UINT16) (sum + value);
+    if (Index < 3) {
+      AdapterInfo->PermNodeAddress[Index2++]  = (UINT8) value;
+      AdapterInfo->PermNodeAddress[Index2++]  = (UINT8) (value >> 8);
+    }
+  }
+
+  if (sum != 0xBABA) {
+    return -1;
+  }
+
+  for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) {
+    AdapterInfo->CurrentNodeAddress[Index] = AdapterInfo->PermNodeAddress[Index];
+  }
+
+  for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) {
+    AdapterInfo->BroadcastNodeAddress[Index] = 0xff;
+  }
+
+  for (Index = PXE_HWADDR_LEN_ETHER; Index < PXE_MAC_LENGTH; Index++) {
+    AdapterInfo->CurrentNodeAddress[Index]    = 0;
+    AdapterInfo->PermNodeAddress[Index]       = 0;
+    AdapterInfo->BroadcastNodeAddress[Index]  = 0;
+  }
+
+  return 0;
+}
+
+//
+//  CBList is a circular linked list
+//  1) When all are free, Tail->next == Head and FreeCount == # allocated
+//  2) When none are free, Tail == Head and FreeCount == 0
+//  3) when one is free, Tail == Head and Freecount == 1
+//  4) First non-Free frame is always at Tail->next
+//
+
+/**
+  TODO: Add function description
+
+  @param  AdapterInfo                     TODO: add argument description
+
+  @return TODO: add return values
+
+**/
+UINT8
+SetupCBlink (
+  NIC_DATA_INSTANCE *AdapterInfo
+  )
+{
+  TxCB  *head_ptr;
+  TxCB  *tail_ptr;
+  TxCB  *cur_ptr;
+  INT32 Index;
+  UINTN array_off;
+
+  cur_ptr   = &(AdapterInfo->tx_ring[0]);
+  array_off = (UINTN) (&cur_ptr->TBDArray) - (UINTN) cur_ptr;
+  for (Index = 0; Index < AdapterInfo->TxBufCnt; Index++) {
+    cur_ptr[Index].cb_header.status   = 0;
+    cur_ptr[Index].cb_header.command  = 0;
+
+    cur_ptr[Index].PhysTCBAddress     =
+    (UINT32) AdapterInfo->tx_phy_addr + (Index * sizeof (TxCB));
+
+    cur_ptr[Index].PhysArrayAddr      = (UINT32)(cur_ptr[Index].PhysTCBAddress + array_off);
+    cur_ptr[Index].PhysTBDArrayAddres = (UINT32)(cur_ptr[Index].PhysTCBAddress + array_off);
+
+    cur_ptr->free_data_ptr = (UINT64) 0;
+
+    if (Index < AdapterInfo->TxBufCnt - 1) {
+      cur_ptr[Index].cb_header.link             = cur_ptr[Index].PhysTCBAddress + sizeof (TxCB);
+      cur_ptr[Index].NextTCBVirtualLinkPtr      = &cur_ptr[Index + 1];
+      cur_ptr[Index + 1].PrevTCBVirtualLinkPtr  = &cur_ptr[Index];
+    }
+  }
+
+  head_ptr                        = &cur_ptr[0];
+  tail_ptr                        = &cur_ptr[AdapterInfo->TxBufCnt - 1];
+  tail_ptr->cb_header.link        = head_ptr->PhysTCBAddress;
+  tail_ptr->NextTCBVirtualLinkPtr = head_ptr;
+  head_ptr->PrevTCBVirtualLinkPtr = tail_ptr;
+
+  AdapterInfo->FreeCBCount        = AdapterInfo->TxBufCnt;
+  AdapterInfo->FreeTxHeadPtr      = head_ptr;
+  //
+  // set tail of the free list, next to this would be either in use
+  // or the head itself
+  //
+  AdapterInfo->FreeTxTailPtr  = tail_ptr;
+
+  AdapterInfo->xmit_done_head = AdapterInfo->xmit_done_tail = 0;
+
+  return 0;
+}
+
+
+/**
+  TODO: Add function description
+
+  @param  AdapterInfo                     TODO: add argument description
+
+  @return TODO: add return values
+
+**/
+TxCB *
+GetFreeCB (
+  NIC_DATA_INSTANCE *AdapterInfo
+  )
+{
+  TxCB  *free_cb_ptr;
+
+  //
+  // claim any hanging free CBs
+  //
+  if (AdapterInfo->FreeCBCount <= 1) {
+    CheckCBList (AdapterInfo);
+  }
+
+  //
+  // don't use up the last CB problem if the previous CB that the CU used
+  // becomes the last CB we submit because of the SUSPEND bit we set.
+  // the CU thinks it was never cleared.
+  //
+
+  if (AdapterInfo->FreeCBCount <= 1) {
+    return NULL;
+  }
+
+  BlockIt (AdapterInfo, TRUE);
+  free_cb_ptr                 = AdapterInfo->FreeTxHeadPtr;
+  AdapterInfo->FreeTxHeadPtr  = free_cb_ptr->NextTCBVirtualLinkPtr;
+  --AdapterInfo->FreeCBCount;
+  BlockIt (AdapterInfo, FALSE);
+  return free_cb_ptr;
+}
+
+
+/**
+  TODO: Add function description
+
+  @param  AdapterInfo                     TODO: add argument description
+  @param  cb_ptr                          TODO: add argument description
+
+  @return TODO: add return values
+
+**/
+VOID
+SetFreeCB (
+  IN NIC_DATA_INSTANCE *AdapterInfo,
+  IN TxCB              *cb_ptr
+  )
+{
+  //
+  // here we assume cb are returned in the order they are taken out
+  // and we link the newly freed cb at the tail of free cb list
+  //
+  cb_ptr->cb_header.status    = 0;
+  cb_ptr->free_data_ptr       = (UINT64) 0;
+
+  AdapterInfo->FreeTxTailPtr  = cb_ptr;
+  ++AdapterInfo->FreeCBCount;
+  return ;
+}
+
+
+/**
+  TODO: Add function description
+
+  @param  ind                             TODO: add argument description
+
+  @return TODO: add return values
+
+**/
+UINT16
+next (
+  IN UINT16 ind
+  )
+{
+  UINT16  Tmp;
+
+  Tmp = (UINT16) (ind + 1);
+  if (Tmp >= (TX_BUFFER_COUNT << 1)) {
+    Tmp = 0;
+  }
+
+  return Tmp;
+}
+
+
+/**
+  TODO: Add function description
+
+  @param  AdapterInfo                     TODO: add argument description
+
+  @return TODO: add return values
+
+**/
+UINT16
+CheckCBList (
+  IN NIC_DATA_INSTANCE *AdapterInfo
+  )
+{
+  TxCB    *Tmp_ptr;
+  UINT16  cnt;
+
+  cnt = 0;
+  while (1) {
+    Tmp_ptr = AdapterInfo->FreeTxTailPtr->NextTCBVirtualLinkPtr;
+    if ((Tmp_ptr->cb_header.status & CMD_STATUS_MASK) != 0) {
+      //
+      // check if Q is full
+      //
+      if (next (AdapterInfo->xmit_done_tail) != AdapterInfo->xmit_done_head) {
+        ASSERT (AdapterInfo->xmit_done_tail < TX_BUFFER_COUNT << 1);
+        AdapterInfo->xmit_done[AdapterInfo->xmit_done_tail] = Tmp_ptr->free_data_ptr;
+
+        UnMapIt (
+          AdapterInfo,
+          Tmp_ptr->free_data_ptr,
+          Tmp_ptr->TBDArray[0].buf_len,
+          TO_DEVICE,
+          (UINT64) Tmp_ptr->TBDArray[0].phys_buf_addr
+          );
+
+        AdapterInfo->xmit_done_tail = next (AdapterInfo->xmit_done_tail);
+      }
+
+      SetFreeCB (AdapterInfo, Tmp_ptr);
+    } else {
+      break;
+    }
+  }
+
+  return cnt;
+}
+//
+// Description : Initialize the RFD list list by linking each element together
+//               in a circular list.  The simplified memory model is used.
+//               All data is in the RFD.  The RFDs are linked together and the
+//               last one points back to the first one.  When the current RFD
+//               is processed (frame received), its EL bit is set and the EL
+//               bit in the previous RXFD is cleared.
+//               Allocation done during INIT, this is making linked list.
+//
+
+/**
+  TODO: Add function description
+
+  @param  AdapterInfo                     TODO: add argument description
+
+  @return TODO: add return values
+
+**/
+UINT8
+SetupReceiveQueues (
+  IN NIC_DATA_INSTANCE *AdapterInfo
+  )
+{
+  RxFD    *rx_ptr;
+  RxFD    *tail_ptr;
+  UINT16  Index;
+
+  AdapterInfo->cur_rx_ind = 0;
+  rx_ptr                  = (&AdapterInfo->rx_ring[0]);
+
+  for (Index = 0; Index < AdapterInfo->RxBufCnt; Index++) {
+    rx_ptr[Index].cb_header.status  = 0;
+    rx_ptr[Index].cb_header.command = 0;
+    rx_ptr[Index].RFDSize           = RX_BUFFER_SIZE;
+    rx_ptr[Index].ActualCount       = 0;
+    //
+    // RBDs not used, simple memory model
+    //
+    rx_ptr[Index].rx_buf_addr       = (UINT32) (-1);
+
+    //
+    // RBDs not used, simple memory model
+    //
+    rx_ptr[Index].forwarded = FALSE;
+
+    //
+    // don't use Tmp_ptr if it is beyond the last one
+    //
+    if (Index < AdapterInfo->RxBufCnt - 1) {
+      rx_ptr[Index].cb_header.link = (UINT32) AdapterInfo->rx_phy_addr + ((Index + 1) * sizeof (RxFD));
+    }
+  }
+
+  tail_ptr                    = (&AdapterInfo->rx_ring[AdapterInfo->RxBufCnt - 1]);
+  tail_ptr->cb_header.link    = (UINT32) AdapterInfo->rx_phy_addr;
+
+  //
+  // set the EL bit
+  //
+  tail_ptr->cb_header.command = 0xC000;
+  AdapterInfo->RFDTailPtr = tail_ptr;
+  return 0;
+}
+
+
+/**
+  TODO: Add function description
+
+  @param  AdapterInfo                     TODO: add argument description
+  @param  rx_index                        TODO: add argument description
+
+  @return TODO: add return values
+
+**/
+VOID
+Recycle_RFD (
+  IN NIC_DATA_INSTANCE *AdapterInfo,
+  IN UINT16            rx_index
+  )
+{
+  RxFD  *rx_ptr;
+  RxFD  *tail_ptr;
+  //
+  // change the EL bit and change the AdapterInfo->RxTailPtr
+  // rx_ptr is assumed to be the head of the Q
+  // AdapterInfo->rx_forwarded[rx_index] = FALSE;
+  //
+  rx_ptr                    = &AdapterInfo->rx_ring[rx_index];
+  tail_ptr                  = AdapterInfo->RFDTailPtr;
+  //
+  // set el_bit and suspend bit
+  //
+  rx_ptr->cb_header.command = 0xc000;
+  rx_ptr->cb_header.status    = 0;
+  rx_ptr->ActualCount         = 0;
+  rx_ptr->forwarded           = FALSE;
+  AdapterInfo->RFDTailPtr     = rx_ptr;
+  //
+  // resetting the el_bit.
+  //
+  tail_ptr->cb_header.command = 0;
+  //
+  // check the receive unit, fix if there is any problem
+  //
+  return ;
+}
+//
+// Serial EEPROM section.
+//
+//  EEPROM_Ctrl bits.
+//
+#define EE_SHIFT_CLK  0x01  /* EEPROM shift clock. */
+#define EE_CS         0x02  /* EEPROM chip select. */
+#define EE_DI         0x04  /* EEPROM chip data in. */
+#define EE_WRITE_0    0x01
+#define EE_WRITE_1    0x05
+#define EE_DO         0x08  /* EEPROM chip data out. */
+#define EE_ENB        (0x4800 | EE_CS)
+
+//
+// Delay between EEPROM clock transitions.
+// This will actually work with no delay on 33Mhz PCI.
+//
+#define eeprom_delay(nanosec) DelayIt (AdapterInfo, nanosec);
+
+//
+// The EEPROM commands include the alway-set leading bit.
+//
+#define EE_WRITE_CMD  5 // 101b
+#define EE_READ_CMD   6 // 110b
+#define EE_ERASE_CMD  (7 << 6)
+
+VOID
+shift_bits_out (
+  IN NIC_DATA_INSTANCE *AdapterInfo,
+  IN UINT16            val,
+  IN UINT8             num_bits
+  )
+/*++
+
+Routine Description:
+
+  TODO: Add function description
+
+Arguments:
+
+  AdapterInfo - TODO: add argument description
+  val         - TODO: add argument description
+  num_bits    - TODO: add argument description
+
+Returns:
+
+  TODO: add return values
+
+--*/
+{
+  INT32   Index;
+  UINT8   Tmp;
+  UINT32  EEAddr;
+
+  EEAddr = AdapterInfo->ioaddr + SCBeeprom;
+
+  for (Index = num_bits; Index >= 0; Index--) {
+    INT16 dataval;
+
+    //
+    // will be 0 or 4
+    //
+    dataval = (INT16) ((val & (1 << Index)) ? EE_DI : 0);
+
+    //
+    // mask off the data_in bit
+    //
+    Tmp = (UINT8) (InByte (AdapterInfo, EEAddr) &~EE_DI);
+    Tmp = (UINT8) (Tmp | dataval);
+    OutByte (AdapterInfo, Tmp, EEAddr);
+    eeprom_delay (100);
+    //
+    // raise the eeprom clock
+    //
+    OutByte (AdapterInfo, (UINT8) (Tmp | EE_SHIFT_CLK), EEAddr);
+    eeprom_delay (150);
+    //
+    // lower the eeprom clock
+    //
+    OutByte (AdapterInfo, (UINT8) (Tmp &~EE_SHIFT_CLK), EEAddr);
+    eeprom_delay (150);
+  }
+}
+
+
+/**
+  TODO: Add function description
+
+  @param  AdapterInfo                     TODO: add argument description
+
+  @return TODO: add return values
+
+**/
+UINT16
+shift_bits_in (
+  IN NIC_DATA_INSTANCE *AdapterInfo
+  )
+{
+  UINT8   Tmp;
+  INT32   Index;
+  UINT16  retval;
+  UINT32  EEAddr;
+
+  EEAddr  = AdapterInfo->ioaddr + SCBeeprom;
+
+  retval  = 0;
+  for (Index = 15; Index >= 0; Index--) {
+    //
+    // raise the clock
+    //
+
+    //
+    // mask off the data_in bit
+    //
+    Tmp = InByte (AdapterInfo, EEAddr);
+    OutByte (AdapterInfo, (UINT8) (Tmp | EE_SHIFT_CLK), EEAddr);
+    eeprom_delay (100);
+    Tmp     = InByte (AdapterInfo, EEAddr);
+    retval  = (UINT16) ((retval << 1) | ((Tmp & EE_DO) ? 1 : 0));
+    //
+    // lower the clock
+    //
+    OutByte (AdapterInfo, (UINT8) (Tmp &~EE_SHIFT_CLK), EEAddr);
+    eeprom_delay (100);
+  }
+
+  return retval;
+}
+
+
+/**
+  This routine sets the EEPROM lockout bit to gain exclusive access to the
+  eeprom. the access bit is the most significant bit in the General Control
+  Register 2 in the SCB space.
+
+  @param  AdapterInfo                     Pointer to the NIC data structure
+                                          information which the UNDI driver is
+                                          layering on..
+
+  @retval TRUE                            if it got the access
+  @retval FALSE                           if it fails to get the exclusive access
+
+**/
+BOOLEAN
+E100bSetEepromLockOut (
+  IN NIC_DATA_INSTANCE  *AdapterInfo
+  )
+{
+  UINTN wait;
+  UINT8 tmp;
+
+  if ((AdapterInfo->DeviceID == D102_DEVICE_ID) ||
+      (AdapterInfo->RevID >= D102_REVID)) {
+
+    wait = 500;
+
+    while (wait--) {
+
+      tmp = InByte (AdapterInfo, AdapterInfo->ioaddr + SCBGenCtrl2);
+      tmp |= GCR2_EEPROM_ACCESS_SEMAPHORE;
+      OutByte (AdapterInfo, tmp, AdapterInfo->ioaddr + SCBGenCtrl2);
+
+      DelayIt (AdapterInfo, 50);
+      tmp = InByte (AdapterInfo, AdapterInfo->ioaddr + SCBGenCtrl2);
+
+      if (tmp & GCR2_EEPROM_ACCESS_SEMAPHORE) {
+        return TRUE;
+      }
+    }
+
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+
+/**
+  This routine Resets the EEPROM lockout bit to giveup access to the
+  eeprom. the access bit is the most significant bit in the General Control
+  Register 2 in the SCB space.
+
+  @param  AdapterInfo                     Pointer to the NIC data structure
+                                          information which the UNDI driver is
+                                          layering on..
+
+  @return None
+
+**/
+VOID
+E100bReSetEepromLockOut (
+  IN NIC_DATA_INSTANCE  *AdapterInfo
+  )
+{
+  UINT8 tmp;
+
+  if ((AdapterInfo->DeviceID == D102_DEVICE_ID) ||
+      (AdapterInfo->RevID >= D102_REVID)) {
+
+    tmp = InByte (AdapterInfo, AdapterInfo->ioaddr + SCBGenCtrl2);
+    tmp &= ~(GCR2_EEPROM_ACCESS_SEMAPHORE);
+    OutByte (AdapterInfo, tmp, AdapterInfo->ioaddr + SCBGenCtrl2);
+
+    DelayIt (AdapterInfo, 50);
+  }
+}
+
+
+/**
+  Using the NIC data structure information, read the EEPROM to get a Word of data for the MAC address.
+
+  @param  AdapterInfo                     Pointer to the NIC data structure
+                                          information which the UNDI driver is
+                                          layering on..
+  @param  Location                        Word offset into the MAC address to read.
+  @param  AddrLen                         Number of bits of address length.
+
+  @retval RetVal                          The word read from the EEPROM.
+
+**/
+UINT16
+E100bReadEeprom (
+  IN NIC_DATA_INSTANCE  *AdapterInfo,
+  IN INT32              Location,
+  IN UINT8              AddrLen
+  )
+{
+  UINT16  RetVal;
+  UINT8   Tmp;
+
+  UINT32  EEAddr;
+  UINT16  ReadCmd;
+
+  EEAddr  = AdapterInfo->ioaddr + SCBeeprom;
+  ReadCmd = (UINT16) (Location | (EE_READ_CMD << AddrLen));
+
+  RetVal  = 0;
+
+  //
+  // get exclusive access to the eeprom first!
+  //
+  E100bSetEepromLockOut (AdapterInfo);
+
+  //
+  // eeprom control reg bits: x,x,x,x,DO,DI,CS,SK
+  // to write the opcode+data value out one bit at a time in DI starting at msb
+  // and then out a 1 to sk, wait, out 0 to SK and wait
+  // repeat this for all the bits to be written
+  //
+
+  //
+  // 11110010b
+  //
+  Tmp = (UINT8) (InByte (AdapterInfo, EEAddr) & 0xF2);
+  OutByte (AdapterInfo, (UINT8) (Tmp | EE_CS), EEAddr);
+
+  //
+  // 3 for the read opcode 110b
+  //
+  shift_bits_out (AdapterInfo, ReadCmd, (UINT8) (3 + AddrLen));
+
+  //
+  // read the eeprom word one bit at a time
+  //
+  RetVal = shift_bits_in (AdapterInfo);
+
+  //
+  // Terminate the EEPROM access and leave eeprom in a clean state.
+  //
+  Tmp = InByte (AdapterInfo, EEAddr);
+  Tmp &= ~(EE_CS | EE_DI);
+  OutByte (AdapterInfo, Tmp, EEAddr);
+
+  //
+  // raise the clock and lower the eeprom shift clock
+  //
+  OutByte (AdapterInfo, (UINT8) (Tmp | EE_SHIFT_CLK), EEAddr);
+  eeprom_delay (100);
+
+  OutByte (AdapterInfo, (UINT8) (Tmp &~EE_SHIFT_CLK), EEAddr);
+  eeprom_delay (100);
+
+  //
+  // giveup access to the eeprom
+  //
+  E100bReSetEepromLockOut (AdapterInfo);
+
+  return RetVal;
+}
+
+
+/**
+  Using the NIC data structure information, read the EEPROM to determine how many bits of address length
+  this EEPROM is in Words.
+
+  @param  AdapterInfo                     Pointer to the NIC data structure
+                                          information which the UNDI driver is
+                                          layering on..
+
+  @retval RetVal                          The word read from the EEPROM.
+
+**/
+UINT8
+E100bGetEepromAddrLen (
+  IN NIC_DATA_INSTANCE *AdapterInfo
+  )
+{
+  UINT8   Tmp;
+  UINT8   AddrLen;
+  UINT32  EEAddr;
+  //
+  // assume 64word eeprom (so,6 bits of address_length)
+  //
+  UINT16  ReadCmd;
+
+  EEAddr  = AdapterInfo->ioaddr + SCBeeprom;
+  ReadCmd = (EE_READ_CMD << 6);
+
+  //
+  // get exclusive access to the eeprom first!
+  //
+  E100bSetEepromLockOut (AdapterInfo);
+
+  //
+  // address we are trying to read is 0
+  // eeprom control reg bits: x,x,x,x,DO,,DI,,CS,SK
+  // to write the opcode+data value out one bit at a time in DI starting at msb
+  // and then out a 1 to sk, wait, out 0 to SK and wait
+  // repeat this for all the bits to be written
+  //
+  Tmp = (UINT8) (InByte (AdapterInfo, EEAddr) & 0xF2);
+
+  //
+  // enable eeprom access
+  //
+  OutByte (AdapterInfo, (UINT8) (Tmp | EE_CS), EEAddr);
+
+  //
+  // 3 for opcode, 6 for the default address len
+  //
+  shift_bits_out (AdapterInfo, ReadCmd, (UINT8) (3 + 6));
+
+  //
+  // (in case of a 64 word eeprom).
+  // read the "dummy zero" from EE_DO to say that the address we wrote
+  // (six 0s) is accepted, write more zeros (until 8) to get a "dummy zero"
+  //
+
+  //
+  // assume the smallest
+  //
+  AddrLen = 6;
+  Tmp     = InByte (AdapterInfo, EEAddr);
+  while ((AddrLen < 8) && ((Tmp & EE_DO) != 0)) {
+    OutByte (AdapterInfo, (UINT8) (Tmp &~EE_DI), EEAddr);
+    eeprom_delay (100);
+
+    //
+    // raise the eeprom clock
+    //
+    OutByte (AdapterInfo, (UINT8) (Tmp | EE_SHIFT_CLK), EEAddr);
+    eeprom_delay (150);
+
+    //
+    // lower the eeprom clock
+    //
+    OutByte (AdapterInfo, (UINT8) (Tmp &~EE_SHIFT_CLK), EEAddr);
+    eeprom_delay (150);
+    Tmp = InByte (AdapterInfo, EEAddr);
+    AddrLen++;
+  }
+
+  //
+  // read the eeprom word, even though we don't need this
+  //
+  shift_bits_in (AdapterInfo);
+
+  //
+  // Terminate the EEPROM access.
+  //
+  Tmp = InByte (AdapterInfo, EEAddr);
+  Tmp &= ~(EE_CS | EE_DI);
+  OutByte (AdapterInfo, Tmp, EEAddr);
+
+  //
+  // raise the clock and lower the eeprom shift clock
+  //
+  OutByte (AdapterInfo, (UINT8) (Tmp | EE_SHIFT_CLK), EEAddr);
+  eeprom_delay (100);
+
+  OutByte (AdapterInfo, (UINT8) (Tmp &~EE_SHIFT_CLK), EEAddr);
+  eeprom_delay (100);
+
+  //
+  // giveup access to the eeprom!
+  //
+  E100bReSetEepromLockOut (AdapterInfo);
+
+  return AddrLen;
+}
+
+
+/**
+  TODO: Add function description
+
+  @param  AdapterInfo                     TODO: add argument description
+  @param  DBaddr                          TODO: add argument description
+  @param  DBsize                          TODO: add argument description
+
+  @return TODO: add return values
+
+**/
+UINTN
+E100bStatistics (
+  NIC_DATA_INSTANCE *AdapterInfo,
+  UINT64            DBaddr,
+  UINT16            DBsize
+  )
+{
+  PXE_DB_STATISTICS db;
+  //
+  // wait upto one second (each wait is 100 micro s)
+  //
+  UINT32            Wait;
+  Wait = 10000;
+  wait_for_cmd_done (AdapterInfo->ioaddr + SCBCmd);
+
+  //
+  // Clear statistics done marker.
+  //
+  AdapterInfo->statistics->done_marker = 0;
+
+  //
+  // Issue statistics dump (or dump w/ reset) command.
+  //
+  OutByte (
+    AdapterInfo,
+    (UINT8) (DBsize ? CU_SHOWSTATS : CU_DUMPSTATS),
+    (UINT32) (AdapterInfo->ioaddr + SCBCmd)
+    );
+
+  //
+  // Wait for command to complete.
+  //
+  // zero the db here just to chew up a little more time.
+  //
+
+  ZeroMem ((VOID *) &db, sizeof db);
+
+  while (Wait != 0) {
+    //
+    // Wait a bit before checking.
+    //
+
+    DelayIt (AdapterInfo, 100);
+
+    //
+    // Look for done marker at end of statistics.
+    //
+
+    switch (AdapterInfo->statistics->done_marker) {
+    case 0xA005:
+    case 0xA007:
+      break;
+
+    default:
+      Wait--;
+      continue;
+    }
+
+    //
+    // if we did not "continue" from the above switch, we are done,
+    //
+    break;
+  }
+
+  //
+  // If this is a reset, we are out of here!
+  //
+  if (DBsize == 0) {
+    return PXE_STATCODE_SUCCESS;
+  }
+
+  //
+  // Convert NIC statistics counter format to EFI/UNDI
+  // specification statistics counter format.
+  //
+
+  //
+  //                54 3210 fedc ba98 7654 3210
+  // db.Supported = 01 0000 0100 1101 0001 0111;
+  //
+  db.Supported = 0x104D17;
+
+  //
+  // Statistics from the NIC
+  //
+
+  db.Data[0x01] = AdapterInfo->statistics->rx_good_frames;
+
+  db.Data[0x02] = AdapterInfo->statistics->rx_runt_errs;
+
+  db.Data[0x08] = AdapterInfo->statistics->rx_crc_errs +
+                  AdapterInfo->statistics->rx_align_errs;
+
+  db.Data[0x04] = db.Data[0x02] +
+                  db.Data[0x08] +
+                  AdapterInfo->statistics->rx_resource_errs +
+                  AdapterInfo->statistics->rx_overrun_errs;
+
+  db.Data[0x00] = db.Data[0x01] + db.Data[0x04];
+
+  db.Data[0x0B] = AdapterInfo->statistics->tx_good_frames;
+
+  db.Data[0x0E] = AdapterInfo->statistics->tx_coll16_errs +
+    AdapterInfo->statistics->tx_late_colls +
+    AdapterInfo->statistics->tx_underruns +
+    AdapterInfo->statistics->tx_one_colls +
+    AdapterInfo->statistics->tx_multi_colls;
+
+  db.Data[0x14] = AdapterInfo->statistics->tx_total_colls;
+
+  db.Data[0x0A] = db.Data[0x0B] +
+                  db.Data[0x0E] +
+                  AdapterInfo->statistics->tx_lost_carrier;
+
+  if (DBsize > sizeof db) {
+    DBsize = (UINT16) sizeof (db);
+  }
+
+  CopyMem ((VOID *) (UINTN) DBaddr, (VOID *) &db, (UINTN) DBsize);
+
+  return PXE_STATCODE_SUCCESS;
+}
+
+
+/**
+  TODO: Add function description
+
+  @param  AdapterInfo                     TODO: add argument description
+  @param  OpFlags                         TODO: add argument description
+
+  @return TODO: add return values
+
+**/
+UINTN
+E100bReset (
+  IN NIC_DATA_INSTANCE *AdapterInfo,
+  IN INT32             OpFlags
+  )
+{
+
+  UINT16  save_filter;
+  //
+  // disable the interrupts
+  //
+  OutWord (AdapterInfo, INT_MASK, AdapterInfo->ioaddr + SCBCmd);
+
+  //
+  // wait for the tx queue to complete
+  //
+  CheckCBList (AdapterInfo);
+
+  XmitWaitForCompletion (AdapterInfo);
+
+  if (AdapterInfo->Receive_Started) {
+    StopRU (AdapterInfo);
+  }
+
+  InitializeChip (AdapterInfo);
+
+  //
+  // check the opflags and restart receive filters
+  //
+  if ((OpFlags & PXE_OPFLAGS_RESET_DISABLE_FILTERS) == 0) {
+
+    save_filter = AdapterInfo->Rx_Filter;
+    //
+    // if we give the filter same as Rx_Filter,
+    // this routine will not set mcast list (it thinks there is no change)
+    // to force it, we will reset that flag in the Rx_Filter
+    //
+    AdapterInfo->Rx_Filter &= (~PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST);
+    E100bSetfilter (AdapterInfo, save_filter, (UINT64) 0, (UINT32) 0);
+  }
+
+  if ((OpFlags & PXE_OPFLAGS_RESET_DISABLE_INTERRUPTS) != 0) {
+    //
+    // disable the interrupts
+    //
+    AdapterInfo->int_mask = 0;
+  }
+  //
+  // else leave the interrupt in the pre-set state!!!
+  //
+  E100bSetInterruptState (AdapterInfo);
+
+  return 0;
+}
+
+
+/**
+  TODO: Add function description
+
+  @param  AdapterInfo                     TODO: add argument description
+
+  @return TODO: add return values
+
+**/
+UINTN
+E100bShutdown (
+  IN NIC_DATA_INSTANCE *AdapterInfo
+  )
+{
+  //
+  // disable the interrupts
+  //
+  OutWord (AdapterInfo, INT_MASK, AdapterInfo->ioaddr + SCBCmd);
+
+  //
+  // stop the receive unit
+  //
+  if (AdapterInfo->Receive_Started) {
+    StopRU (AdapterInfo);
+  }
+
+  //
+  // wait for the tx queue to complete
+  //
+  CheckCBList (AdapterInfo);
+  if (AdapterInfo->FreeCBCount != AdapterInfo->TxBufCnt) {
+    wait_for_cmd_done (AdapterInfo->ioaddr + SCBCmd);
+  }
+
+  //
+  // we do not want to reset the phy, it takes a long time to renegotiate the
+  // link after that (3-4 seconds)
+  //
+  InitializeChip (AdapterInfo);
+  SelectiveReset (AdapterInfo);
+  return 0;
+}
+
+
+/**
+  This routine will write a value to the specified MII register
+  of an external MDI compliant device (e.g. PHY 100).  The command will
+  execute in polled mode.
+
+  @param  AdapterInfo                     pointer to the structure that contains
+                                          the NIC's context.
+  @param  RegAddress                      The MII register that we are writing to
+  @param  PhyAddress                      The MDI address of the Phy component.
+  @param  DataValue                       The value that we are writing to the MII
+                                          register.
+
+  @return nothing
+
+**/
+VOID
+MdiWrite (
+  IN NIC_DATA_INSTANCE *AdapterInfo,
+  IN UINT8             RegAddress,
+  IN UINT8             PhyAddress,
+  IN UINT16            DataValue
+  )
+{
+  UINT32  WriteCommand;
+
+  WriteCommand = ((UINT32) DataValue) |
+                 ((UINT32)(RegAddress << 16)) |
+                 ((UINT32)(PhyAddress << 21)) |
+                 ((UINT32)(MDI_WRITE << 26));
+
+  //
+  // Issue the write command to the MDI control register.
+  //
+  OutLong (AdapterInfo, WriteCommand, AdapterInfo->ioaddr + SCBCtrlMDI);
+
+  //
+  // wait 20usec before checking status
+  //
+  DelayIt (AdapterInfo, 20);
+
+  //
+  // poll for the mdi write to complete
+  while ((InLong (AdapterInfo, AdapterInfo->ioaddr + SCBCtrlMDI) &
+                    MDI_PHY_READY) == 0){
+    DelayIt (AdapterInfo, 20);
+  }
+}
+
+
+/**
+  This routine will read a value from the specified MII register
+  of an external MDI compliant device (e.g. PHY 100), and return
+  it to the calling routine.  The command will execute in polled mode.
+
+  @param  AdapterInfo                     pointer to the structure that contains
+                                          the NIC's context.
+  @param  RegAddress                      The MII register that we are reading from
+  @param  PhyAddress                      The MDI address of the Phy component.
+  @param  DataValue                       pointer to the value that we read from
+                                          the MII register.
+
+
+**/
+VOID
+MdiRead (
+  IN NIC_DATA_INSTANCE *AdapterInfo,
+  IN UINT8             RegAddress,
+  IN UINT8             PhyAddress,
+  IN OUT UINT16        *DataValue
+  )
+{
+  UINT32  ReadCommand;
+
+  ReadCommand = ((UINT32) (RegAddress << 16)) |
+                ((UINT32) (PhyAddress << 21)) |
+                ((UINT32) (MDI_READ << 26));
+
+  //
+  // Issue the read command to the MDI control register.
+  //
+  OutLong (AdapterInfo, ReadCommand, AdapterInfo->ioaddr + SCBCtrlMDI);
+
+  //
+  // wait 20usec before checking status
+  //
+  DelayIt (AdapterInfo, 20);
+
+  //
+  // poll for the mdi read to complete
+  //
+  while ((InLong (AdapterInfo, AdapterInfo->ioaddr + SCBCtrlMDI) &
+          MDI_PHY_READY) == 0) {
+    DelayIt (AdapterInfo, 20);
+
+  }
+
+  *DataValue = InWord (AdapterInfo, AdapterInfo->ioaddr + SCBCtrlMDI);
+}
+
+
+/**
+  This routine will reset the PHY that the adapter is currently
+  configured to use.
+
+  @param  AdapterInfo                     pointer to the structure that contains
+                                          the NIC's context.
+
+
+**/
+VOID
+PhyReset (
+  NIC_DATA_INSTANCE *AdapterInfo
+  )
+{
+  UINT16  MdiControlReg;
+
+  MdiControlReg = (MDI_CR_AUTO_SELECT |
+                  MDI_CR_RESTART_AUTO_NEG |
+                  MDI_CR_RESET);
+
+  //
+  // Write the MDI control register with our new Phy configuration
+  //
+  MdiWrite (
+    AdapterInfo,
+    MDI_CONTROL_REG,
+    AdapterInfo->PhyAddress,
+    MdiControlReg
+    );
+
+  return ;
+}
+
+
+/**
+  This routine will detect what phy we are using, set the line
+  speed, FDX or HDX, and configure the phy if necessary.
+  The following combinations are supported:
+  - TX or T4 PHY alone at PHY address 1
+  - T4 or TX PHY at address 1 and MII PHY at address 0
+  - 82503 alone (10Base-T mode, no full duplex support)
+  - 82503 and MII PHY (TX or T4) at address 0
+  The sequence / priority of detection is as follows:
+  - PHY 1 with cable termination
+  - PHY 0 with cable termination
+  - PHY 1 (if found) without cable termination
+  - 503 interface
+  Additionally auto-negotiation capable (NWAY) and parallel
+  detection PHYs are supported. The flow-chart is described in
+  the 82557 software writer's manual.
+  NOTE:  1.  All PHY MDI registers are read in polled mode.
+  2.  The routines assume that the 82557 has been RESET and we have
+  obtained the virtual memory address of the CSR.
+  3.  PhyDetect will not RESET the PHY.
+  4.  If FORCEFDX is set, SPEED should also be set. The driver will
+  check the values for inconsistency with the detected PHY
+  technology.
+  5.  PHY 1 (the PHY on the adapter) may have an address in the range
+  1 through 31 inclusive. The driver will accept addresses in
+  this range.
+  6.  Driver ignores FORCEFDX and SPEED overrides if a 503 interface
+  is detected.
+
+  @param  AdapterInfo                     pointer to the structure that contains
+                                          the NIC's context.
+
+  @retval TRUE                            If a Phy was detected, and configured
+                                          correctly.
+  @retval FALSE                           If a valid phy could not be detected and
+                                          configured.
+
+**/
+BOOLEAN
+PhyDetect (
+  NIC_DATA_INSTANCE *AdapterInfo
+  )
+{
+  UINT16  *eedata;
+  UINT16  MdiControlReg;
+  UINT16  MdiStatusReg;
+  BOOLEAN FoundPhy1;
+  UINT8   ReNegotiateTime;
+
+  eedata          = (UINT16 *) (&AdapterInfo->NVData[0]);
+
+  FoundPhy1       = FALSE;
+  ReNegotiateTime = 35;
+  //
+  // EEPROM word [6] contains the Primary PHY record in which the least 3 bits
+  // indicate the PHY address
+  // and word [7] contains the secondary PHY record
+  //
+  AdapterInfo->PhyRecord[0] = eedata[6];
+  AdapterInfo->PhyRecord[1] = eedata[7];
+  AdapterInfo->PhyAddress   = (UINT8) (AdapterInfo->PhyRecord[0] & 7);
+
+  //
+  // Check for a phy address over-ride of 32 which indicates force use of 82503
+  // not detecting the link in this case
+  //
+  if (AdapterInfo->PhyAddress == 32) {
+    //
+    // 503 interface over-ride
+    // Record the current speed and duplex.  We will be in half duplex
+    // mode unless the user used the force full duplex over-ride.
+    //
+    AdapterInfo->LinkSpeed = 10;
+    return (TRUE);
+  }
+
+  //
+  // If the Phy Address is between 1-31 then we must first look for phy 1,
+  // at that address.
+  //
+  if ((AdapterInfo->PhyAddress > 0) && (AdapterInfo->PhyAddress < 32)) {
+
+    //
+    // Read the MDI control and status registers at phy 1
+    // and check if we found a valid phy
+    //
+    MdiRead (
+      AdapterInfo,
+      MDI_CONTROL_REG,
+      AdapterInfo->PhyAddress,
+      &MdiControlReg
+      );
+
+    MdiRead (
+      AdapterInfo,
+      MDI_STATUS_REG,
+      AdapterInfo->PhyAddress,
+      &MdiStatusReg
+      );
+
+    if (!((MdiControlReg == 0xffff) ||
+          ((MdiStatusReg == 0) && (MdiControlReg == 0)))) {
+
+      //
+      // we have a valid phy1
+      // Read the status register again because of sticky bits
+      //
+      FoundPhy1 = TRUE;
+      MdiRead (
+        AdapterInfo,
+        MDI_STATUS_REG,
+        AdapterInfo->PhyAddress,
+        &MdiStatusReg
+        );
+
+      //
+      // If there is a valid link then use this Phy.
+      //
+      if (MdiStatusReg & MDI_SR_LINK_STATUS) {
+        return (SetupPhy(AdapterInfo));
+      }
+    }
+  }
+
+  //
+  // Next try to detect a PHY at address 0x00 because there was no Phy 1,
+  // or Phy 1 didn't have link, or we had a phy 0 over-ride
+  //
+
+  //
+  // Read the MDI control and status registers at phy 0
+  //
+  MdiRead (AdapterInfo, MDI_CONTROL_REG, 0, &MdiControlReg);
+  MdiRead (AdapterInfo, MDI_STATUS_REG, 0, &MdiStatusReg);
+
+  //
+  // check if we found a valid phy 0
+  //
+  if (((MdiControlReg == 0xffff) ||
+       ((MdiStatusReg == 0) && (MdiControlReg == 0)))) {
+
+    //
+    // we don't have a valid phy at address 0
+    // if phy address was forced to 0, then error out because we
+    // didn't find a phy at that address
+    //
+    if (AdapterInfo->PhyAddress == 0x0000) {
+      return (FALSE);
+    } else {
+      //
+      // at this point phy1 does not have link and there is no phy 0 at all
+      // if we are forced to detect the cable, error out here!
+      //
+      if (AdapterInfo->CableDetect != 0) {
+        return FALSE;
+
+      }
+
+      if (FoundPhy1) {
+        //
+        // no phy 0, but there is a phy 1 (no link I guess), so use phy 1
+        //
+        return SetupPhy (AdapterInfo);
+      } else {
+        //
+        // didn't find phy 0 or phy 1, so assume a 503 interface
+        //
+        AdapterInfo->PhyAddress = 32;
+
+        //
+        // Record the current speed and duplex.  We'll be in half duplex
+        // mode unless the user used the force full duplex over-ride.
+        //
+        AdapterInfo->LinkSpeed = 10;
+        return (TRUE);
+      }
+    }
+  } else {
+    //
+    // We have a valid phy at address 0.  If phy 0 has a link then we use
+    // phy 0.  If Phy 0 doesn't have a link then we use Phy 1 (no link)
+    // if phy 1 is present, or phy 0 if phy 1 is not present
+    // If phy 1 was present, then we must isolate phy 1 before we enable
+    // phy 0 to see if Phy 0 has a link.
+    //
+    if (FoundPhy1) {
+      //
+      // isolate phy 1
+      //
+      MdiWrite (
+        AdapterInfo,
+        MDI_CONTROL_REG,
+        AdapterInfo->PhyAddress,
+        MDI_CR_ISOLATE
+        );
+
+      //
+      // wait 100 microseconds for the phy to isolate.
+      //
+      DelayIt (AdapterInfo, 100);
+    }
+
+    //
+    // Since this Phy is at address 0, we must enable it.  So clear
+    // the isolate bit, and set the auto-speed select bit
+    //
+    MdiWrite (
+      AdapterInfo,
+      MDI_CONTROL_REG,
+      0,
+      MDI_CR_AUTO_SELECT
+      );
+
+    //
+    // wait 100 microseconds for the phy to be enabled.
+    //
+    DelayIt (AdapterInfo, 100);
+
+    //
+    // restart the auto-negotion process
+    //
+    MdiWrite (
+      AdapterInfo,
+      MDI_CONTROL_REG,
+      0,
+      MDI_CR_RESTART_AUTO_NEG | MDI_CR_AUTO_SELECT
+      );
+
+    //
+    // wait no more than 3.5 seconds for auto-negotiation to complete
+    //
+    while (ReNegotiateTime) {
+      //
+      // Read the status register twice because of sticky bits
+      //
+      MdiRead (AdapterInfo, MDI_STATUS_REG, 0, &MdiStatusReg);
+      MdiRead (AdapterInfo, MDI_STATUS_REG, 0, &MdiStatusReg);
+
+      if (MdiStatusReg & MDI_SR_AUTO_NEG_COMPLETE) {
+        break;
+      }
+
+      DelayIt (AdapterInfo, 100);
+      ReNegotiateTime--;
+    }
+
+    //
+    // Read the status register again because of sticky bits
+    //
+    MdiRead (AdapterInfo, MDI_STATUS_REG, 0, &MdiStatusReg);
+
+    //
+    // If the link was not set
+    //
+    if ((MdiStatusReg & MDI_SR_LINK_STATUS) == 0) {
+      //
+      // PHY1 does not have a link and phy 0 does not have a link
+      // do not proceed if we need to detect the link!
+      //
+      if (AdapterInfo->CableDetect != 0) {
+        return FALSE;
+      }
+
+      //
+      // the link wasn't set, so use phy 1 if phy 1 was present
+      //
+      if (FoundPhy1) {
+        //
+        // isolate phy 0
+        //
+        MdiWrite (AdapterInfo, MDI_CONTROL_REG, 0, MDI_CR_ISOLATE);
+
+        //
+        // wait 100 microseconds for the phy to isolate.
+        //
+        DelayIt (AdapterInfo, 100);
+
+        //
+        // Now re-enable PHY 1
+        //
+        MdiWrite (
+          AdapterInfo,
+          MDI_CONTROL_REG,
+          AdapterInfo->PhyAddress,
+          MDI_CR_AUTO_SELECT
+          );
+
+        //
+        // wait 100 microseconds for the phy to be enabled
+        //
+        DelayIt (AdapterInfo, 100);
+
+        //
+        // restart the auto-negotion process
+        //
+        MdiWrite (
+          AdapterInfo,
+          MDI_CONTROL_REG,
+          AdapterInfo->PhyAddress,
+          MDI_CR_RESTART_AUTO_NEG | MDI_CR_AUTO_SELECT
+          );
+
+        //
+        // Don't wait for it to complete (we didn't have link earlier)
+        //
+        return (SetupPhy (AdapterInfo));
+      }
+    }
+
+    //
+    // Definitely using Phy 0
+    //
+    AdapterInfo->PhyAddress = 0;
+    return (SetupPhy(AdapterInfo));
+  }
+}
+
+
+/**
+  This routine will setup phy 1 or phy 0 so that it is configured
+  to match a speed and duplex over-ride option.  If speed or
+  duplex mode is not explicitly specified in the registry, the
+  driver will skip the speed and duplex over-ride code, and
+  assume the adapter is automatically setting the line speed, and
+  the duplex mode.  At the end of this routine, any truly Phy
+  specific code will be executed (each Phy has its own quirks,
+  and some require that certain special bits are set).
+  NOTE:  The driver assumes that SPEED and FORCEFDX are specified at the
+  same time. If FORCEDPX is set without speed being set, the driver
+  will encouter a fatal error and log a message into the event viewer.
+
+  @param  AdapterInfo                     pointer to the structure that contains
+                                          the NIC's context.
+
+  @retval TRUE                            If the phy could be configured correctly
+  @retval FALSE                           If the phy couldn't be configured
+                                          correctly, because an unsupported
+                                          over-ride option was used
+
+**/
+BOOLEAN
+SetupPhy (
+  IN NIC_DATA_INSTANCE *AdapterInfo
+  )
+{
+  UINT16  MdiControlReg;
+  UINT16  MdiStatusReg;
+  UINT16  MdiIdLowReg;
+  UINT16  MdiIdHighReg;
+  UINT16  MdiMiscReg;
+  UINT32  PhyId;
+  BOOLEAN ForcePhySetting;
+
+  ForcePhySetting = FALSE;
+
+  //
+  // If we are NOT forcing a setting for line speed or full duplex, then
+  // we won't force a link setting, and we'll jump down to the phy
+  // specific code.
+  //
+  if (((AdapterInfo->LinkSpeedReq) || (AdapterInfo->DuplexReq))) {
+    //
+    // Find out what kind of technology this Phy is capable of.
+    //
+    MdiRead (
+      AdapterInfo,
+      MDI_STATUS_REG,
+      AdapterInfo->PhyAddress,
+      &MdiStatusReg
+      );
+
+    //
+    // Read the MDI control register at our phy
+    //
+    MdiRead (
+      AdapterInfo,
+      MDI_CONTROL_REG,
+      AdapterInfo->PhyAddress,
+      &MdiControlReg
+      );
+
+    //
+    // Now check the validity of our forced option.  If the force option is
+    // valid, then force the setting.  If the force option is not valid,
+    // we'll set a flag indicating that we should error out.
+    //
+
+    //
+    // If speed is forced to 10mb
+    //
+    if (AdapterInfo->LinkSpeedReq == 10) {
+      //
+      // If half duplex is forced
+      //
+      if ((AdapterInfo->DuplexReq & PXE_FORCE_HALF_DUPLEX) != 0) {
+        if (MdiStatusReg & MDI_SR_10T_HALF_DPX) {
+
+          MdiControlReg &= ~(MDI_CR_10_100 | MDI_CR_AUTO_SELECT | MDI_CR_FULL_HALF);
+          ForcePhySetting = TRUE;
+        }
+      } else if ((AdapterInfo->DuplexReq & PXE_FORCE_FULL_DUPLEX) != 0) {
+
+        //
+        // If full duplex is forced
+        //
+        if (MdiStatusReg & MDI_SR_10T_FULL_DPX) {
+
+          MdiControlReg &= ~(MDI_CR_10_100 | MDI_CR_AUTO_SELECT);
+          MdiControlReg |= MDI_CR_FULL_HALF;
+          ForcePhySetting = TRUE;
+        }
+      } else {
+        //
+        // If auto duplex (we actually set phy to 1/2)
+        //
+        if (MdiStatusReg & (MDI_SR_10T_FULL_DPX | MDI_SR_10T_HALF_DPX)) {
+
+          MdiControlReg &= ~(MDI_CR_10_100 | MDI_CR_AUTO_SELECT | MDI_CR_FULL_HALF);
+          ForcePhySetting = TRUE;
+        }
+      }
+    }
+
+    //
+    // If speed is forced to 100mb
+    //
+    else if (AdapterInfo->LinkSpeedReq == 100) {
+      //
+      // If half duplex is forced
+      //
+      if ((AdapterInfo->DuplexReq & PXE_FORCE_HALF_DUPLEX) != 0) {
+        if (MdiStatusReg & (MDI_SR_TX_HALF_DPX | MDI_SR_T4_CAPABLE)) {
+
+          MdiControlReg &= ~(MDI_CR_AUTO_SELECT | MDI_CR_FULL_HALF);
+          MdiControlReg |= MDI_CR_10_100;
+          ForcePhySetting = TRUE;
+        }
+      } else if ((AdapterInfo->DuplexReq & PXE_FORCE_FULL_DUPLEX) != 0) {
+        //
+        // If full duplex is forced
+        //
+        if (MdiStatusReg & MDI_SR_TX_FULL_DPX) {
+          MdiControlReg &= ~MDI_CR_AUTO_SELECT;
+          MdiControlReg |= (MDI_CR_10_100 | MDI_CR_FULL_HALF);
+          ForcePhySetting = TRUE;
+        }
+      } else {
+        //
+        // If auto duplex (we set phy to 1/2)
+        //
+        if (MdiStatusReg & (MDI_SR_TX_HALF_DPX | MDI_SR_T4_CAPABLE)) {
+
+          MdiControlReg &= ~(MDI_CR_AUTO_SELECT | MDI_CR_FULL_HALF);
+          MdiControlReg |= MDI_CR_10_100;
+          ForcePhySetting = TRUE;
+        }
+      }
+    }
+
+    if (!ForcePhySetting) {
+      return (FALSE);
+    }
+
+    //
+    // Write the MDI control register with our new Phy configuration
+    //
+    MdiWrite (
+      AdapterInfo,
+      MDI_CONTROL_REG,
+      AdapterInfo->PhyAddress,
+      MdiControlReg
+      );
+
+    //
+    // wait 100 milliseconds for auto-negotiation to complete
+    //
+    DelayIt (AdapterInfo, 100);
+  }
+
+  //
+  // Find out specifically what Phy this is.  We do this because for certain
+  // phys there are specific bits that must be set so that the phy and the
+  // 82557 work together properly.
+  //
+
+  MdiRead (
+    AdapterInfo,
+    PHY_ID_REG_1,
+    AdapterInfo->PhyAddress,
+    &MdiIdLowReg
+    );
+  MdiRead (
+    AdapterInfo,
+    PHY_ID_REG_2,
+    AdapterInfo->PhyAddress,
+    &MdiIdHighReg
+    );
+
+  PhyId = ((UINT32) MdiIdLowReg | ((UINT32) MdiIdHighReg << 16));
+
+  //
+  // And out the revsion field of the Phy ID so that we'll be able to detect
+  // future revs of the same Phy.
+  //
+  PhyId &= PHY_MODEL_REV_ID_MASK;
+
+  //
+  // Handle the National TX
+  //
+  if (PhyId == PHY_NSC_TX) {
+
+    MdiRead (
+      AdapterInfo,
+      NSC_CONG_CONTROL_REG,
+      AdapterInfo->PhyAddress,
+      &MdiMiscReg
+      );
+
+    MdiMiscReg |= (NSC_TX_CONG_TXREADY | NSC_TX_CONG_F_CONNECT);
+
+    MdiWrite (
+      AdapterInfo,
+      NSC_CONG_CONTROL_REG,
+      AdapterInfo->PhyAddress,
+      MdiMiscReg
+      );
+  }
+
+  FindPhySpeedAndDpx (AdapterInfo, PhyId);
+
+  //
+  // We put a hardware fix on to our adapters to work-around the PHY_100 errata
+  // described below.  The following code is only compiled in, if we wanted
+  // to attempt a software workaround to the PHY_100 A/B step problem.
+  //
+
+  return (TRUE);
+}
+
+
+/**
+  This routine will figure out what line speed and duplex mode
+  the PHY is currently using.
+
+  @param  AdapterInfo                     pointer to the structure that contains
+                                          the NIC's context.
+  @param  PhyId                           The ID of the PHY in question.
+
+  @return NOTHING
+
+**/
+VOID
+FindPhySpeedAndDpx (
+  IN NIC_DATA_INSTANCE *AdapterInfo,
+  IN UINT32            PhyId
+  )
+{
+  UINT16  MdiStatusReg;
+  UINT16  MdiMiscReg;
+  UINT16  MdiOwnAdReg;
+  UINT16  MdiLinkPartnerAdReg;
+
+  //
+  // If there was a speed and/or duplex override, then set our current
+  // value accordingly
+  //
+  AdapterInfo->LinkSpeed  = AdapterInfo->LinkSpeedReq;
+  AdapterInfo->Duplex = (UINT8) ((AdapterInfo->DuplexReq & PXE_FORCE_FULL_DUPLEX) ?
+                        FULL_DUPLEX : HALF_DUPLEX);
+
+  //
+  // If speed and duplex were forced, then we know our current settings, so
+  // we'll just return.  Otherwise, we'll need to figure out what NWAY set
+  // us to.
+  //
+  if (AdapterInfo->LinkSpeed && AdapterInfo->Duplex) {
+    return ;
+
+  }
+  //
+  // If we didn't have a valid link, then we'll assume that our current
+  // speed is 10mb half-duplex.
+  //
+
+  //
+  // Read the status register twice because of sticky bits
+  //
+  MdiRead (
+    AdapterInfo,
+    MDI_STATUS_REG,
+    AdapterInfo->PhyAddress,
+    &MdiStatusReg
+    );
+  MdiRead (
+    AdapterInfo,
+    MDI_STATUS_REG,
+    AdapterInfo->PhyAddress,
+    &MdiStatusReg
+    );
+
+  //
+  // If there wasn't a valid link then use default speed & duplex
+  //
+  if (!(MdiStatusReg & MDI_SR_LINK_STATUS)) {
+
+    AdapterInfo->LinkSpeed  = 10;
+    AdapterInfo->Duplex     = HALF_DUPLEX;
+    return ;
+  }
+
+  //
+  // If this is an Intel PHY (a T4 PHY_100 or a TX PHY_TX), then read bits
+  // 1 and 0 of extended register 0, to get the current speed and duplex
+  // settings.
+  //
+  if ((PhyId == PHY_100_A) || (PhyId == PHY_100_C) || (PhyId == PHY_TX_ID)) {
+    //
+    // Read extended register 0
+    //
+    MdiRead (
+      AdapterInfo,
+      EXTENDED_REG_0,
+      AdapterInfo->PhyAddress,
+      &MdiMiscReg
+      );
+
+    //
+    // Get current speed setting
+    //
+    if (MdiMiscReg & PHY_100_ER0_SPEED_INDIC) {
+      AdapterInfo->LinkSpeed = 100;
+    } else {
+      AdapterInfo->LinkSpeed = 10;
+    }
+
+    //
+    // Get current duplex setting -- if bit is set then FDX is enabled
+    //
+    if (MdiMiscReg & PHY_100_ER0_FDX_INDIC) {
+      AdapterInfo->Duplex = FULL_DUPLEX;
+    } else {
+      AdapterInfo->Duplex = HALF_DUPLEX;
+    }
+
+    return ;
+  }
+  //
+  // Read our link partner's advertisement register
+  //
+  MdiRead (
+    AdapterInfo,
+    AUTO_NEG_LINK_PARTNER_REG,
+    AdapterInfo->PhyAddress,
+    &MdiLinkPartnerAdReg
+    );
+
+  //
+  // See if Auto-Negotiation was complete (bit 5, reg 1)
+  //
+  MdiRead (
+    AdapterInfo,
+    MDI_STATUS_REG,
+    AdapterInfo->PhyAddress,
+    &MdiStatusReg
+    );
+
+  //
+  // If a True NWAY connection was made, then we can detect speed/duplex by
+  // ANDing our adapter's advertised abilities with our link partner's
+  // advertised ablilities, and then assuming that the highest common
+  // denominator was chosed by NWAY.
+  //
+  if ((MdiLinkPartnerAdReg & NWAY_LP_ABILITY) &&
+      (MdiStatusReg & MDI_SR_AUTO_NEG_COMPLETE)) {
+
+    //
+    // Read our advertisement register
+    //
+    MdiRead (
+      AdapterInfo,
+      AUTO_NEG_ADVERTISE_REG,
+      AdapterInfo->PhyAddress,
+      &MdiOwnAdReg
+      );
+
+    //
+    // AND the two advertisement registers together, and get rid of any
+    // extraneous bits.
+    //
+    MdiOwnAdReg = (UINT16) (MdiOwnAdReg & (MdiLinkPartnerAdReg & NWAY_LP_ABILITY));
+
+    //
+    // Get speed setting
+    //
+    if (MdiOwnAdReg & (NWAY_AD_TX_HALF_DPX | NWAY_AD_TX_FULL_DPX | NWAY_AD_T4_CAPABLE)) {
+      AdapterInfo->LinkSpeed = 100;
+    } else {
+      AdapterInfo->LinkSpeed = 10;
+    }
+
+    //
+    // Get duplex setting -- use priority resolution algorithm
+    //
+    if (MdiOwnAdReg & (NWAY_AD_T4_CAPABLE)) {
+      AdapterInfo->Duplex = HALF_DUPLEX;
+      return ;
+    } else if (MdiOwnAdReg & (NWAY_AD_TX_FULL_DPX)) {
+      AdapterInfo->Duplex = FULL_DUPLEX;
+      return ;
+    } else if (MdiOwnAdReg & (NWAY_AD_TX_HALF_DPX)) {
+      AdapterInfo->Duplex = HALF_DUPLEX;
+      return ;
+    } else if (MdiOwnAdReg & (NWAY_AD_10T_FULL_DPX)) {
+      AdapterInfo->Duplex = FULL_DUPLEX;
+      return ;
+    } else {
+      AdapterInfo->Duplex = HALF_DUPLEX;
+      return ;
+    }
+  }
+
+  //
+  // If we are connected to a dumb (non-NWAY) repeater or hub, and the line
+  // speed was determined automatically by parallel detection, then we have
+  // no way of knowing exactly what speed the PHY is set to unless that PHY
+  // has a propietary register which indicates speed in this situation.  The
+  // NSC TX PHY does have such a register.  Also, since NWAY didn't establish
+  // the connection, the duplex setting should HALF duplex.
+  //
+  AdapterInfo->Duplex = HALF_DUPLEX;
+
+  if (PhyId == PHY_NSC_TX) {
+    //
+    // Read register 25 to get the SPEED_10 bit
+    //
+    MdiRead (
+      AdapterInfo,
+      NSC_SPEED_IND_REG,
+      AdapterInfo->PhyAddress,
+      &MdiMiscReg
+      );
+
+    //
+    // If bit 6 was set then we're at 10mb
+    //
+    if (MdiMiscReg & NSC_TX_SPD_INDC_SPEED) {
+      AdapterInfo->LinkSpeed = 10;
+    } else {
+      AdapterInfo->LinkSpeed = 100;
+    }
+  }
+
+  //
+  // If we don't know what line speed we are set at, then we'll default to
+  // 10mbs
+  //
+  else {
+    AdapterInfo->LinkSpeed = 10;
+  }
+}
+
+
+/**
+  TODO: Add function description
+
+  @param  AdapterInfo                     TODO: add argument description
+
+  @return TODO: add return values
+
+**/
+VOID
+XmitWaitForCompletion (
+  NIC_DATA_INSTANCE *AdapterInfo
+  )
+{
+  TxCB  *TxPtr;
+
+  if (AdapterInfo->FreeCBCount == AdapterInfo->TxBufCnt) {
+    return ;
+  }
+
+  //
+  // used xmit cb list starts right after the free tail (ends before the
+  // free head ptr)
+  //
+  TxPtr = AdapterInfo->FreeTxTailPtr->NextTCBVirtualLinkPtr;
+  while (TxPtr != AdapterInfo->FreeTxHeadPtr) {
+    CommandWaitForCompletion (TxPtr, AdapterInfo);
+    SetFreeCB (AdapterInfo, TxPtr);
+    TxPtr = TxPtr->NextTCBVirtualLinkPtr;
+  }
+}
+
+
+/**
+  TODO: Add function description
+
+  @param  cmd_ptr                         TODO: add argument description
+  @param  AdapterInfo                     TODO: add argument description
+
+  @return TODO: add return values
+
+**/
+INT8
+CommandWaitForCompletion (
+  TxCB              *cmd_ptr,
+  NIC_DATA_INSTANCE *AdapterInfo
+  )
+{
+  INT16 wait;
+  wait = 5000;
+  while ((cmd_ptr->cb_header.status == 0) && (--wait > 0)) {
+    DelayIt (AdapterInfo, 10);
+  }
+
+  if (cmd_ptr->cb_header.status == 0) {
+    return -1;
+  }
+
+  return 0;
+}
+
+
+/**
+  TODO: Add function description
+
+  @param  AdapterInfo                     TODO: add argument description
+
+  @return TODO: add return values
+
+**/
+INT8
+SoftwareReset (
+  NIC_DATA_INSTANCE *AdapterInfo
+  )
+{
+  UINT8   tco_stat;
+  UINT16  wait;
+
+  tco_stat = 0;
+
+  //
+  // Reset the chip: stop Tx and Rx processes and clear counters.
+  // This takes less than 10usec and will easily finish before the next
+  // action.
+  //
+
+  OutLong (AdapterInfo, PORT_RESET, AdapterInfo->ioaddr + SCBPort);
+  //
+  // wait for 5 milli seconds here!
+  //
+  DelayIt (AdapterInfo, 5000);
+  //
+  // TCO Errata work around for 559s only
+  // -----------------------------------------------------------------------------------
+  // TCO Workaround Code
+  //  haifa workaround
+  // -----------------------------------------------------------------------------------
+  //    1. Issue SW-RST ^^^ (already done above)
+  //    2. Issue a redundant Set CU Base CMD immediately
+  //       Do not set the General Pointer before the Set CU Base cycle
+  //       Do not check the SCB CMD before the Set CU Base cycle
+  //    3. Wait for the SCB-CMD to be cleared
+  //       this indicates the transition to post-driver
+  //    4. Poll the TCO-Req bit in the PMDR to be cleared
+  //       this indicates the tco activity has stopped for real
+  //    5. Proceed with the nominal Driver Init:
+  //       Actual Set CU & RU Base ...
+  //
+  // Check for ICH2 device ID.  If this is an ICH2,
+  // do the TCO workaround code.
+  //
+  if (AdapterInfo->VendorID == D102_DEVICE_ID ||
+      AdapterInfo->VendorID == ICH3_DEVICE_ID_1 ||
+      AdapterInfo->VendorID == ICH3_DEVICE_ID_2 ||
+      AdapterInfo->VendorID == ICH3_DEVICE_ID_3 ||
+      AdapterInfo->VendorID == ICH3_DEVICE_ID_4 ||
+      AdapterInfo->VendorID == ICH3_DEVICE_ID_5 ||
+      AdapterInfo->VendorID == ICH3_DEVICE_ID_6 ||
+      AdapterInfo->VendorID == ICH3_DEVICE_ID_7 ||
+      AdapterInfo->VendorID == ICH3_DEVICE_ID_8 ||
+      AdapterInfo->RevID >= 8) {  // do the TCO fix
+    //
+    // donot load the scb pointer but just give load_cu cmd.
+    //
+    OutByte (AdapterInfo, CU_CMD_BASE, AdapterInfo->ioaddr + SCBCmd);
+    //
+    // wait for command to be accepted.
+    //
+    wait_for_cmd_done (AdapterInfo->ioaddr + SCBCmd);
+    //
+    // read PMDR register and check bit 1 in it to see if TCO is active
+    //
+
+    //
+    // wait for 5 milli seconds
+    //
+    wait = 5000;
+    while (wait) {
+      tco_stat = InByte (AdapterInfo, AdapterInfo->ioaddr + 0x1b);
+      if ((tco_stat & 2) == 0) {
+        //
+        // is the activity bit clear??
+        //
+        break;
+      }
+
+      wait--;
+      DelayIt (AdapterInfo, 1);
+    }
+
+    if ((tco_stat & 2) != 0) {
+      //
+      // not zero??
+      //
+      return -1;
+    }
+  }
+
+  return 0;
+}
+
+
+/**
+  TODO: Add function description
+
+  @param  AdapterInfo                     TODO: add argument description
+
+  @return TODO: add return values
+
+**/
+UINT8
+SelectiveReset (
+  IN NIC_DATA_INSTANCE *AdapterInfo
+  )
+{
+  UINT16  wait;
+  UINT32  stat;
+
+  wait  = 10;
+  stat  = 0;
+  OutLong (AdapterInfo, POR_SELECTIVE_RESET, AdapterInfo->ioaddr + SCBPort);
+  //
+  // wait for this to complete
+  //
+
+  //
+  // wait for 2 milli seconds here!
+  //
+  DelayIt (AdapterInfo, 2000);
+  while (wait > 0) {
+    wait--;
+    stat = InLong (AdapterInfo, AdapterInfo->ioaddr + SCBPort);
+    if (stat == 0) {
+      break;
+    }
+
+    //
+    // wait for 1 milli second
+    //
+    DelayIt (AdapterInfo, 1000);
+  }
+
+  if (stat != 0) {
+    return PXE_STATCODE_DEVICE_FAILURE;
+  }
+
+  return 0;
+}
+
+
+/**
+  TODO: Add function description
+
+  @param  AdapterInfo                     TODO: add argument description
+
+  @return TODO: add return values
+
+**/
+UINT16
+InitializeChip (
+  IN NIC_DATA_INSTANCE *AdapterInfo
+  )
+{
+  UINT16  ret_val;
+  if (SoftwareReset (AdapterInfo) != 0) {
+    return PXE_STATCODE_DEVICE_FAILURE;
+  }
+
+  //
+  // disable interrupts
+  //
+  OutWord (AdapterInfo, INT_MASK, AdapterInfo->ioaddr + SCBCmd);
+
+  //
+  // Load the base registers with 0s (we will give the complete address as
+  // offset later when we issue any command
+  //
+  if ((ret_val = Load_Base_Regs (AdapterInfo)) != 0) {
+    return ret_val;
+  }
+
+  if ((ret_val = SetupCBlink (AdapterInfo)) != 0) {
+    return ret_val;
+  }
+
+  if ((ret_val = SetupReceiveQueues (AdapterInfo)) != 0) {
+    return ret_val;
+  }
+
+  //
+  // detect the PHY only if we need to detect the cable as requested by the
+  // initialize parameters
+  //
+  AdapterInfo->PhyAddress = 0xFF;
+
+  if (AdapterInfo->CableDetect != 0) {
+    if (!PhyDetect (AdapterInfo)) {
+      return PXE_STATCODE_DEVICE_FAILURE;
+    }
+  }
+
+  if ((ret_val = E100bSetupIAAddr (AdapterInfo)) != 0) {
+    return ret_val;
+  }
+
+  if ((ret_val = Configure (AdapterInfo)) != 0) {
+    return ret_val;
+  }
+
+  return 0;
+}
diff --git a/Drivers/OptionRomPkg/UndiRuntimeDxe/E100b.h b/Drivers/OptionRomPkg/UndiRuntimeDxe/E100b.h
new file mode 100644
index 0000000000..18ff7aa94d
--- /dev/null
+++ b/Drivers/OptionRomPkg/UndiRuntimeDxe/E100b.h
@@ -0,0 +1,665 @@
+/** @file
+  Definitions for network adapter card.
+
+Copyright (c) 2006 - 2007, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef _E100B_H_
+#define _E100B_H_
+
+// pci config offsets:
+
+#define RX_BUFFER_COUNT 32
+#define TX_BUFFER_COUNT 32
+
+#define PCI_VENDOR_ID_INTEL 0x8086
+#define PCI_DEVICE_ID_INTEL_82557 0x1229
+#define D100_VENDOR_ID   0x8086
+#define D100_DEVICE_ID   0x1229
+#define D102_DEVICE_ID   0x2449
+
+#define ICH3_DEVICE_ID_1   0x1031
+#define ICH3_DEVICE_ID_2   0x1032
+#define ICH3_DEVICE_ID_3   0x1033
+#define ICH3_DEVICE_ID_4   0x1034
+#define ICH3_DEVICE_ID_5   0x1035
+#define ICH3_DEVICE_ID_6   0x1036
+#define ICH3_DEVICE_ID_7   0x1037
+#define ICH3_DEVICE_ID_8   0x1038
+
+#define SPEEDO_DEVICE_ID   0x1227
+#define SPLASH1_DEVICE_ID   0x1226
+
+
+// bit fields for the command
+#define PCI_COMMAND_MASTER  0x04  // bit 2
+#define PCI_COMMAND_IO    0x01  // bit 0
+#define PCI_COMMAND  0x04
+#define PCI_LATENCY_TIMER  0x0D
+
+#define ETHER_MAC_ADDR_LEN 6
+#ifdef AVL_XXX
+#define ETHER_HEADER_LEN 14
+// media interface type
+// #define INTERFACE_TYPE "
+
+// Hardware type values
+#define HW_ETHER_TYPE    1
+#define HW_EXPERIMENTAL_ETHER_TYPE 2
+#define HW_IEEE_TYPE    6
+#define HW_ARCNET_TYPE     7
+
+#endif  // AVL_XXX
+
+#define MAX_ETHERNET_PKT_SIZE 1514  // including eth header
+#define RX_BUFFER_SIZE 1536  // including crc and padding
+#define TX_BUFFER_SIZE 64
+#define ETH_MTU 1500  // does not include ethernet header length
+
+#define SPEEDO3_TOTAL_SIZE 0x20
+
+#pragma pack(1)
+
+typedef struct eth {
+  UINT8 dest_addr[PXE_HWADDR_LEN_ETHER];
+  UINT8 src_addr[PXE_HWADDR_LEN_ETHER];
+  UINT16 type;
+} EtherHeader;
+
+#pragma pack(1)
+typedef struct CONFIG_HEADER {
+  UINT16 VendorID;
+  UINT16 DeviceID;
+  UINT16 Command;
+  UINT16 Status;
+  UINT16 RevID;
+  UINT16 ClassID;
+  UINT8  CacheLineSize;
+  UINT8  LatencyTimer;
+  UINT8  HeaderType;    // must be zero to impose this structure...
+  UINT8  BIST;  // built-in self test
+  UINT32 BaseAddressReg_0;  // memory mapped address
+  UINT32 BaseAddressReg_1;  //io mapped address, Base IO address
+  UINT32 BaseAddressReg_2;  // option rom address
+  UINT32 BaseAddressReg_3;
+  UINT32 BaseAddressReg_4;
+  UINT32 BaseAddressReg_5;
+  UINT32 CardBusCISPtr;
+  UINT16 SubVendorID;
+  UINT16 SubSystemID;
+  UINT32 ExpansionROMBaseAddr;
+  UINT8 CapabilitiesPtr;
+  UINT8 reserved1;
+  UINT16 Reserved2;
+  UINT32 Reserved3;
+  UINT8 int_line;
+  UINT8 int_pin;
+  UINT8 Min_gnt;
+  UINT8 Max_lat;
+} PCI_CONFIG_HEADER;
+#pragma pack()
+
+//-------------------------------------------------------------------------
+// Offsets to the various registers.
+//   All accesses need not be longword aligned.
+//-------------------------------------------------------------------------
+enum speedo_offsets {
+  SCBStatus = 0, SCBCmd = 2,     // Rx/Command Unit command and status.
+  SCBPointer = 4,                // General purpose pointer.
+  SCBPort = 8,                   // Misc. commands and operands.
+  SCBflash = 12, SCBeeprom = 14, // EEPROM and flash memory control.
+  SCBCtrlMDI = 16,               // MDI interface control.
+  SCBEarlyRx = 20,               // Early receive byte count.
+  SCBEarlyRxInt = 24, SCBFlowCtrlReg = 25, SCBPmdr = 27,
+  // offsets for general control registers (GCRs)
+  SCBGenCtrl = 28, SCBGenStatus = 29, SCBGenCtrl2 = 30, SCBRsvd = 31
+};
+
+#define GCR2_EEPROM_ACCESS_SEMAPHORE 0x80 // bit offset into the gcr2
+
+//-------------------------------------------------------------------------
+// Action commands - Commands that can be put in a command list entry.
+//-------------------------------------------------------------------------
+enum commands {
+  CmdNOp = 0, CmdIASetup = 1, CmdConfigure = 2, CmdMulticastList = 3,
+  CmdTx = 4, CmdTDR = 5, CmdDump = 6, CmdDiagnose = 7,
+  CmdSuspend = 0x4000,    /* Suspend after completion. */
+  CmdIntr = 0x2000,      /* Interrupt after completion. */
+  CmdTxFlex = 0x0008      /* Use "Flexible mode" for CmdTx command. */
+};
+
+//-------------------------------------------------------------------------
+// port commands
+//-------------------------------------------------------------------------
+#define PORT_RESET 0
+#define PORT_SELF_TEST 1
+#define POR_SELECTIVE_RESET 2
+#define PORT_DUMP_POINTER 2
+
+//-------------------------------------------------------------------------
+// SCB Command Word bit definitions
+//-------------------------------------------------------------------------
+//- CUC fields
+#define   CU_START    0x0010
+#define   CU_RESUME    0x0020
+#define   CU_STATSADDR  0x0040
+#define   CU_SHOWSTATS  0x0050  /* Dump statistics counters. */
+#define   CU_CMD_BASE  0x0060  /* Base address to add to add CU commands. */
+#define   CU_DUMPSTATS  0x0070  /* Dump then reset stats counters. */
+
+//- RUC fields
+#define   RX_START  0x0001
+#define   RX_RESUME  0x0002
+#define   RX_ABORT  0x0004
+#define   RX_ADDR_LOAD  0x0006  /* load ru_base_reg */
+#define   RX_RESUMENR  0x0007
+
+// Interrupt fields (assuming byte addressing)
+#define INT_MASK  0x0100
+#define DRVR_INT  0x0200    /* Driver generated interrupt. */
+
+//- CB Status Word
+#define CMD_STATUS_COMPLETE 0x8000
+#define RX_STATUS_COMPLETE 0x8000
+#define CMD_STATUS_MASK 0xF000
+
+//-------------------------------------------------------------------------
+//- SCB Status bits:
+// Interrupts are ACKed by writing to the upper 6 interrupt bits
+//-------------------------------------------------------------------------
+#define SCB_STATUS_MASK        0xFC00 // bits 2-7 - STATUS/ACK Mask
+#define SCB_STATUS_CX_TNO      0x8000 // BIT_15  - CX or TNO Interrupt
+#define SCB_STATUS_FR          0x4000 // BIT_14 - FR Interrupt
+#define SCB_STATUS_CNA         0x2000 // BIT_13 - CNA Interrupt
+#define SCB_STATUS_RNR         0x1000 // BIT_12  - RNR Interrupt
+#define SCB_STATUS_MDI         0x0800 // BIT_11  - MDI R/W Done Interrupt
+#define SCB_STATUS_SWI         0x0400 // BIT_10  - SWI Interrupt
+
+// CU STATUS: bits 6 & 7
+#define SCB_STATUS_CU_MASK     0x00C0 // bits 6 & 7
+#define SCB_STATUS_CU_IDLE     0x0000 // 00
+#define SCB_STATUS_CU_SUSPEND  0x0040 // 01
+#define SCB_STATUS_CU_ACTIVE   0x0080 // 10
+
+// RU STATUS: bits 2-5
+#define SCB_RUS_IDLE         0x0000
+#define SCB_RUS_SUSPENDED    0x0004  // bit 2
+#define SCB_RUS_NO_RESOURCES   0x0008 // bit 3
+#define SCB_RUS_READY       0x0010 // bit 4
+
+//-------------------------------------------------------------------------
+// Bit Mask definitions
+//-------------------------------------------------------------------------
+#define BIT_0       0x0001
+#define BIT_1       0x0002
+#define BIT_2       0x0004
+#define BIT_3       0x0008
+#define BIT_4       0x0010
+#define BIT_5       0x0020
+#define BIT_6       0x0040
+#define BIT_7       0x0080
+#define BIT_8       0x0100
+#define BIT_9       0x0200
+#define BIT_10      0x0400
+#define BIT_11      0x0800
+#define BIT_12      0x1000
+#define BIT_13      0x2000
+#define BIT_14      0x4000
+#define BIT_15      0x8000
+#define BIT_24      0x01000000
+#define BIT_28      0x10000000
+
+
+//-------------------------------------------------------------------------
+// MDI Control register bit definitions
+//-------------------------------------------------------------------------
+#define MDI_DATA_MASK           BIT_0_15        // MDI Data port
+#define MDI_REG_ADDR            BIT_16_20       // which MDI register to read/write
+#define MDI_PHY_ADDR            BIT_21_25       // which PHY to read/write
+#define MDI_PHY_OPCODE          BIT_26_27       // which PHY to read/write
+#define MDI_PHY_READY           BIT_28          // PHY is ready for another MDI cycle
+#define MDI_PHY_INT_ENABLE      BIT_29          // Assert INT at MDI cycle completion
+
+#define BIT_0_2     0x0007
+#define BIT_0_3     0x000F
+#define BIT_0_4     0x001F
+#define BIT_0_5     0x003F
+#define BIT_0_6     0x007F
+#define BIT_0_7     0x00FF
+#define BIT_0_8     0x01FF
+#define BIT_0_13    0x3FFF
+#define BIT_0_15    0xFFFF
+#define BIT_1_2     0x0006
+#define BIT_1_3     0x000E
+#define BIT_2_5     0x003C
+#define BIT_3_4     0x0018
+#define BIT_4_5     0x0030
+#define BIT_4_6     0x0070
+#define BIT_4_7     0x00F0
+#define BIT_5_7     0x00E0
+#define BIT_5_9     0x03E0
+#define BIT_5_12    0x1FE0
+#define BIT_5_15    0xFFE0
+#define BIT_6_7     0x00c0
+#define BIT_7_11    0x0F80
+#define BIT_8_10    0x0700
+#define BIT_9_13    0x3E00
+#define BIT_12_15   0xF000
+
+#define BIT_16_20   0x001F0000
+#define BIT_21_25   0x03E00000
+#define BIT_26_27   0x0C000000
+
+//-------------------------------------------------------------------------
+// MDI Control register opcode definitions
+//-------------------------------------------------------------------------
+#define MDI_WRITE               1               // Phy Write
+#define MDI_READ                2               // Phy read
+
+//-------------------------------------------------------------------------
+// PHY 100 MDI Register/Bit Definitions
+//-------------------------------------------------------------------------
+// MDI register set
+#define MDI_CONTROL_REG             0x00        // MDI control register
+#define MDI_STATUS_REG              0x01        // MDI Status regiser
+#define PHY_ID_REG_1                0x02        // Phy indentification reg (word 1)
+#define PHY_ID_REG_2                0x03        // Phy indentification reg (word 2)
+#define AUTO_NEG_ADVERTISE_REG      0x04        // Auto-negotiation advertisement
+#define AUTO_NEG_LINK_PARTNER_REG   0x05        // Auto-negotiation link partner ability
+#define AUTO_NEG_EXPANSION_REG      0x06        // Auto-negotiation expansion
+#define AUTO_NEG_NEXT_PAGE_REG      0x07        // Auto-negotiation next page transmit
+#define EXTENDED_REG_0              0x10        // Extended reg 0 (Phy 100 modes)
+#define EXTENDED_REG_1              0x14        // Extended reg 1 (Phy 100 error indications)
+#define NSC_CONG_CONTROL_REG        0x17        // National (TX) congestion control
+#define NSC_SPEED_IND_REG           0x19        // National (TX) speed indication
+
+// MDI Control register bit definitions
+#define MDI_CR_COLL_TEST_ENABLE     BIT_7       // Collision test enable
+#define MDI_CR_FULL_HALF            BIT_8       // FDX =1, half duplex =0
+#define MDI_CR_RESTART_AUTO_NEG     BIT_9       // Restart auto negotiation
+#define MDI_CR_ISOLATE              BIT_10      // Isolate PHY from MII
+#define MDI_CR_POWER_DOWN           BIT_11      // Power down
+#define MDI_CR_AUTO_SELECT          BIT_12      // Auto speed select enable
+#define MDI_CR_10_100               BIT_13      // 0 = 10Mbs, 1 = 100Mbs
+#define MDI_CR_LOOPBACK             BIT_14      // 0 = normal, 1 = loopback
+#define MDI_CR_RESET                BIT_15      // 0 = normal, 1 = PHY reset
+
+// MDI Status register bit definitions
+#define MDI_SR_EXT_REG_CAPABLE      BIT_0       // Extended register capabilities
+#define MDI_SR_JABBER_DETECT        BIT_1       // Jabber detected
+#define MDI_SR_LINK_STATUS          BIT_2       // Link Status -- 1 = link
+#define MDI_SR_AUTO_SELECT_CAPABLE  BIT_3       // Auto speed select capable
+#define MDI_SR_REMOTE_FAULT_DETECT  BIT_4       // Remote fault detect
+#define MDI_SR_AUTO_NEG_COMPLETE    BIT_5       // Auto negotiation complete
+#define MDI_SR_10T_HALF_DPX         BIT_11      // 10BaseT Half Duplex capable
+#define MDI_SR_10T_FULL_DPX         BIT_12      // 10BaseT full duplex capable
+#define MDI_SR_TX_HALF_DPX          BIT_13      // TX Half Duplex capable
+#define MDI_SR_TX_FULL_DPX          BIT_14      // TX full duplex capable
+#define MDI_SR_T4_CAPABLE           BIT_15      // T4 capable
+
+// Auto-Negotiation advertisement register bit definitions
+#define NWAY_AD_SELCTOR_FIELD       BIT_0_4     // identifies supported protocol
+#define NWAY_AD_ABILITY             BIT_5_12    // technologies that are supported
+#define NWAY_AD_10T_HALF_DPX        BIT_5       // 10BaseT Half Duplex capable
+#define NWAY_AD_10T_FULL_DPX        BIT_6       // 10BaseT full duplex capable
+#define NWAY_AD_TX_HALF_DPX         BIT_7       // TX Half Duplex capable
+#define NWAY_AD_TX_FULL_DPX         BIT_8       // TX full duplex capable
+#define NWAY_AD_T4_CAPABLE          BIT_9       // T4 capable
+#define NWAY_AD_REMOTE_FAULT        BIT_13      // indicates local remote fault
+#define NWAY_AD_RESERVED            BIT_14      // reserved
+#define NWAY_AD_NEXT_PAGE           BIT_15      // Next page (not supported)
+
+// Auto-Negotiation link partner ability register bit definitions
+#define NWAY_LP_SELCTOR_FIELD       BIT_0_4     // identifies supported protocol
+#define NWAY_LP_ABILITY             BIT_5_9     // technologies that are supported
+#define NWAY_LP_REMOTE_FAULT        BIT_13      // indicates partner remote fault
+#define NWAY_LP_ACKNOWLEDGE         BIT_14      // acknowledge
+#define NWAY_LP_NEXT_PAGE           BIT_15      // Next page (not supported)
+
+// Auto-Negotiation expansion register bit definitions
+#define NWAY_EX_LP_NWAY             BIT_0       // link partner is NWAY
+#define NWAY_EX_PAGE_RECEIVED       BIT_1       // link code word received
+#define NWAY_EX_NEXT_PAGE_ABLE      BIT_2       // local is next page able
+#define NWAY_EX_LP_NEXT_PAGE_ABLE   BIT_3       // partner is next page able
+#define NWAY_EX_PARALLEL_DET_FLT    BIT_4       // parallel detection fault
+#define NWAY_EX_RESERVED            BIT_5_15    // reserved
+
+
+// PHY 100 Extended Register 0 bit definitions
+#define PHY_100_ER0_FDX_INDIC       BIT_0       // 1 = FDX, 0 = half duplex
+#define PHY_100_ER0_SPEED_INDIC     BIT_1       // 1 = 100mbs, 0= 10mbs
+#define PHY_100_ER0_WAKE_UP         BIT_2       // Wake up DAC
+#define PHY_100_ER0_RESERVED        BIT_3_4     // Reserved
+#define PHY_100_ER0_REV_CNTRL       BIT_5_7     // Revsion control (A step = 000)
+#define PHY_100_ER0_FORCE_FAIL      BIT_8       // Force Fail is enabled
+#define PHY_100_ER0_TEST            BIT_9_13    // Revsion control (A step = 000)
+#define PHY_100_ER0_LINKDIS         BIT_14      // Link integrity test is disabled
+#define PHY_100_ER0_JABDIS          BIT_15      // Jabber function is disabled
+
+
+// PHY 100 Extended Register 1 bit definitions
+#define PHY_100_ER1_RESERVED        BIT_0_8     // Reserved
+#define PHY_100_ER1_CH2_DET_ERR     BIT_9       // Channel 2 EOF detection error
+#define PHY_100_ER1_MANCH_CODE_ERR  BIT_10      // Manchester code error
+#define PHY_100_ER1_EOP_ERR         BIT_11      // EOP error
+#define PHY_100_ER1_BAD_CODE_ERR    BIT_12      // bad code error
+#define PHY_100_ER1_INV_CODE_ERR    BIT_13      // invalid code error
+#define PHY_100_ER1_DC_BAL_ERR      BIT_14      // DC balance error
+#define PHY_100_ER1_PAIR_SKEW_ERR   BIT_15      // Pair skew error
+
+// National Semiconductor TX phy congestion control register bit definitions
+#define NSC_TX_CONG_TXREADY         BIT_10      // Makes TxReady an input
+#define NSC_TX_CONG_ENABLE          BIT_8       // Enables congestion control
+#define NSC_TX_CONG_F_CONNECT       BIT_5       // Enables congestion control
+
+// National Semiconductor TX phy speed indication register bit definitions
+#define NSC_TX_SPD_INDC_SPEED       BIT_6       // 0 = 100mb, 1=10mb
+
+//-------------------------------------------------------------------------
+// Phy related constants
+//-------------------------------------------------------------------------
+#define PHY_503                 0
+#define PHY_100_A               0x000003E0
+#define PHY_100_C               0x035002A8
+#define PHY_TX_ID               0x015002A8
+#define PHY_NSC_TX              0x5c002000
+#define PHY_OTHER               0xFFFF
+
+#define PHY_MODEL_REV_ID_MASK   0xFFF0FFFF
+#define PARALLEL_DETECT         0
+#define N_WAY                   1
+
+#define RENEGOTIATE_TIME        35 // (3.5 Seconds)
+
+#define CONNECTOR_AUTO          0
+#define CONNECTOR_TPE           1
+#define CONNECTOR_MII           2
+
+//-------------------------------------------------------------------------
+
+/* The Speedo3 Rx and Tx frame/buffer descriptors. */
+#pragma pack(1)
+struct CB_Header {      /* A generic descriptor. */
+  UINT16 status;    /* Offset 0. */
+  UINT16 command;    /* Offset 2. */
+  UINT32 link;          /* struct descriptor *  */
+};
+
+/* transmit command block structure */
+#pragma pack(1)
+typedef struct s_TxCB {
+  struct CB_Header cb_header;
+  UINT32 PhysTBDArrayAddres;  /* address of an array that contains
+                physical TBD pointers */
+  UINT16 ByteCount;  /* immediate data count = 0 always */
+  UINT8 Threshold;
+  UINT8 TBDCount;
+  UINT8 ImmediateData[TX_BUFFER_SIZE];
+  /* following fields are not seen by the 82557 */
+  struct TBD {
+    UINT32 phys_buf_addr;
+    UINT32 buf_len;
+    } TBDArray[MAX_XMIT_FRAGMENTS];
+  UINT32 PhysArrayAddr;  /* in case the one in the header is lost */
+  UINT32 PhysTCBAddress;    /* for this TCB */
+  struct s_TxCB *NextTCBVirtualLinkPtr;
+  struct s_TxCB *PrevTCBVirtualLinkPtr;
+  UINT64 free_data_ptr;  // to be given to the upper layer when this xmit completes1
+}TxCB;
+
+/* The Speedo3 Rx and Tx buffer descriptors. */
+#pragma pack(1)
+typedef struct s_RxFD {          /* Receive frame descriptor. */
+  struct CB_Header cb_header;
+  UINT32 rx_buf_addr;      /* VOID * */
+  UINT16 ActualCount;
+  UINT16 RFDSize;
+  UINT8 RFDBuffer[RX_BUFFER_SIZE];
+  UINT8 forwarded;
+  UINT8 junk[3];
+}RxFD;
+
+/* Elements of the RxFD.status word. */
+#define RX_COMPLETE 0x8000
+#define RX_FRAME_OK 0x2000
+
+/* Elements of the dump_statistics block. This block must be lword aligned. */
+#pragma pack(1)
+struct speedo_stats {
+  UINT32 tx_good_frames;
+  UINT32 tx_coll16_errs;
+  UINT32 tx_late_colls;
+  UINT32 tx_underruns;
+  UINT32 tx_lost_carrier;
+  UINT32 tx_deferred;
+  UINT32 tx_one_colls;
+  UINT32 tx_multi_colls;
+  UINT32 tx_total_colls;
+  UINT32 rx_good_frames;
+  UINT32 rx_crc_errs;
+  UINT32 rx_align_errs;
+  UINT32 rx_resource_errs;
+  UINT32 rx_overrun_errs;
+  UINT32 rx_colls_errs;
+  UINT32 rx_runt_errs;
+  UINT32 done_marker;
+};
+#pragma pack()
+
+
+struct Krn_Mem{
+  RxFD rx_ring[RX_BUFFER_COUNT];
+  TxCB tx_ring[TX_BUFFER_COUNT];
+  struct speedo_stats statistics;
+};
+#define MEMORY_NEEDED  sizeof(struct Krn_Mem)
+
+/* The parameters for a CmdConfigure operation.
+   There are so many options that it would be difficult to document each bit.
+   We mostly use the default or recommended settings.
+*/
+
+/*
+ *--------------------------------------------------------------------------
+ * Configuration CB Parameter Bit Definitions
+ *--------------------------------------------------------------------------
+ */
+// - Byte 0  (Default Value = 16h)
+#define CFIG_BYTE_COUNT    0x16       // 22 Configuration Bytes
+
+//- Byte 1  (Default Value = 88h)
+#define CFIG_TXRX_FIFO_LIMIT  0x88
+
+//- Byte 2  (Default Value = 0)
+#define CFIG_ADAPTIVE_IFS    0
+
+//- Byte 3  (Default Value = 0, ALWAYS. This byte is RESERVED)
+#define CFIG_RESERVED        0
+
+//- Byte 4  (Default Value = 0. Default implies that Rx DMA cannot be
+//-          preempted).
+#define CFIG_RXDMA_BYTE_COUNT      0
+
+//- Byte 5  (Default Value = 80h. Default implies that Tx DMA cannot be
+//-          preempted. However, setting these counters is enabled.)
+#define CFIG_DMBC_ENABLE            0x80
+
+//- Byte 6  (Default Value = 33h. Late SCB enabled, No TNO interrupts,
+//-          CNA interrupts and do not save bad frames.)
+#define CFIG_LATE_SCB               1  // BIT 0
+#define CFIG_TNO_INTERRUPT          0x4  // BIT 2
+#define CFIG_CI_INTERRUPT           0x8  // BIT 3
+#define CFIG_SAVE_BAD_FRAMES        0x80  // BIT_7
+
+//- Byte 7  (Default Value = 7h. Discard short frames automatically and
+//-          attempt upto 3 retries on transmit.)
+#define CFIG_DISCARD_SHORTRX         0x00001
+#define CFIG_URUN_RETRY              BIT_1 OR BIT_2
+
+//- Byte 8  (Default Value = 1. Enable MII mode.)
+#define CFIG_503_MII              BIT_0
+
+//- Byte 9  (Default Value = 0, ALWAYS)
+
+//- Byte 10 (Default Value = 2Eh)
+#define CFIG_NSAI                   BIT_3
+#define CFIG_PREAMBLE_LENGTH         BIT_5      ;- Bit 5-4  = 1-0
+#define CFIG_NO_LOOPBACK             0
+#define CFIG_INTERNAL_LOOPBACK       BIT_6
+#define CFIG_EXT_LOOPBACK            BIT_7
+#define CFIG_EXT_PIN_LOOPBACK        BIT_6 OR BIT_7
+
+//- Byte 11 (Default Value = 0)
+#define CFIG_LINEAR_PRIORITY         0
+
+//- Byte 12 (Default Value = 60h)
+#define CFIG_LPRIORITY_MODE          0
+#define CFIG_IFS                     6          ;- 6 * 16 = 96
+
+//- Byte 13 (Default Value = 0, ALWAYS)
+
+//- Byte 14 (Default Value = 0F2h, ALWAYS)
+
+//- Byte 15 (Default Value = E8h)
+#define CFIG_PROMISCUOUS_MODE        BIT_0
+#define CFIG_BROADCAST_DISABLE       BIT_1
+#define CFIG_CRS_CDT                 BIT_7
+
+//- Byte 16 (Default Value = 0, ALWAYS)
+
+//- Byte 17 (Default Value = 40h, ALWAYS)
+
+//- Byte 18 (Default Value = F2h)
+#define CFIG_STRIPPING               BIT_0
+#define CFIG_PADDING                 BIT_1
+#define CFIG_RX_CRC_TRANSFER         BIT_2
+
+//- Byte 19 (Default Value = 80h)
+#define CFIG_FORCE_FDX               BIT_6
+#define CFIG_FDX_PIN_ENABLE          BIT_7
+
+//- Byte 20 (Default Value = 3Fh)
+#define CFIG_MULTI_IA                BIT_6
+
+//- Byte 21 (Default Value = 05)
+#define CFIG_MC_ALL                  BIT_3
+
+/*-----------------------------------------------------------------------*/
+#define D102_REVID 0x0b
+
+#define HALF_DUPLEX 1
+#define FULL_DUPLEX 2
+
+typedef struct s_data_instance {
+
+  UINT16 State;  // stopped, started or initialized
+  UINT16 Bus;
+  UINT8 Device;
+  UINT8 Function;
+  UINT16 VendorID;
+  UINT16 DeviceID;
+  UINT16 RevID;
+  UINT16 SubVendorID;
+  UINT16 SubSystemID;
+
+  UINT8 PermNodeAddress[PXE_MAC_LENGTH];
+  UINT8 CurrentNodeAddress[PXE_MAC_LENGTH];
+  UINT8 BroadcastNodeAddress[PXE_MAC_LENGTH];
+  UINT32 Config[MAX_PCI_CONFIG_LEN];
+  UINT32 NVData[MAX_EEPROM_LEN];
+
+  UINT32 ioaddr;
+  UINT32 flash_addr;
+
+  UINT16 LinkSpeed;     // actual link speed setting
+  UINT16 LinkSpeedReq;  // requested (forced) link speed
+  UINT8  DuplexReq;     // requested duplex
+  UINT8  Duplex;        // Duplex set
+  UINT8  CableDetect;   // 1 to detect and 0 not to detect the cable
+  UINT8  LoopBack;
+
+  UINT16 TxBufCnt;
+  UINT16 TxBufSize;
+  UINT16 RxBufCnt;
+  UINT16 RxBufSize;
+  UINT32 RxTotals;
+  UINT32 TxTotals;
+
+  UINT16 int_mask;
+  UINT16 Int_Status;
+  UINT16 PhyRecord[2];  // primary and secondary PHY record registers from eeprom
+  UINT8  PhyAddress;
+  UINT8  int_num;
+  UINT16 NVData_Len;
+  UINT32 MemoryLength;
+
+  RxFD *rx_ring;  // array of rx buffers
+  TxCB *tx_ring;  // array of tx buffers
+  struct speedo_stats *statistics;
+  TxCB *FreeTxHeadPtr;
+  TxCB *FreeTxTailPtr;
+  RxFD *RFDTailPtr;
+
+  UINT64 rx_phy_addr;  // physical addresses
+  UINT64 tx_phy_addr;
+  UINT64 stat_phy_addr;
+  UINT64 MemoryPtr;
+  UINT64 Mapped_MemoryPtr;
+
+  UINT64 xmit_done[TX_BUFFER_COUNT << 1]; // circular buffer
+  UINT16 xmit_done_head;  // index into the xmit_done array
+  UINT16 xmit_done_tail;  // where are we filling now (index into xmit_done)
+  UINT16 cur_rx_ind;  // current RX Q head index
+  UINT16 FreeCBCount;
+
+  BOOLEAN in_interrupt;
+  BOOLEAN in_transmit;
+  BOOLEAN Receive_Started;
+  UINT8 Rx_Filter;
+  UINT8 VersionFlag;  // UNDI30 or UNDI31??
+  UINT8 rsvd[3];
+
+  struct mc{
+    UINT16 reserved [3]; // padding for this structure to make it 8 byte aligned
+    UINT16 list_len;
+    UINT8 mc_list[MAX_MCAST_ADDRESS_CNT][PXE_MAC_LENGTH]; // 8*32 is the size
+  } mcast_list;
+
+  UINT64 Unique_ID;
+
+  EFI_PCI_IO_PROTOCOL   *Io_Function;
+  //
+  // Original PCI attributes
+  //
+  UINT64                OriginalPciAttributes;
+
+  VOID (*Delay_30)(UINTN);  // call back routine
+  VOID (*Virt2Phys_30)(UINT64 virtual_addr, UINT64 physical_ptr);  // call back routine
+  VOID (*Block_30)(UINT32 enable);  // call back routine
+  VOID (*Mem_Io_30)(UINT8 read_write, UINT8 len, UINT64 port, UINT64 buf_addr);
+  VOID (*Delay)(UINT64, UINTN);  // call back routine
+  VOID (*Virt2Phys)(UINT64 unq_id, UINT64 virtual_addr, UINT64 physical_ptr);  // call back routine
+  VOID (*Block)(UINT64 unq_id, UINT32 enable);  // call back routine
+  VOID (*Mem_Io)(UINT64 unq_id, UINT8 read_write, UINT8 len, UINT64 port,
+          UINT64 buf_addr);
+  VOID (*Map_Mem)(UINT64 unq_id, UINT64 virtual_addr, UINT32 size,
+                   UINT32 Direction, UINT64 mapped_addr);
+  VOID (*UnMap_Mem)(UINT64 unq_id, UINT64 virtual_addr, UINT32 size,
+            UINT32 Direction, UINT64 mapped_addr);
+  VOID (*Sync_Mem)(UINT64 unq_id, UINT64 virtual_addr,
+            UINT32 size, UINT32 Direction, UINT64 mapped_addr);
+} NIC_DATA_INSTANCE;
+
+#pragma pack(1)
+struct MC_CB_STRUCT{
+  UINT16 count;
+  UINT8 m_list[MAX_MCAST_ADDRESS_CNT][ETHER_MAC_ADDR_LEN];
+};
+#pragma pack()
+
+#define FOUR_GIGABYTE (UINT64)0x100000000ULL
+
+#endif
+
diff --git a/Drivers/OptionRomPkg/UndiRuntimeDxe/Init.c b/Drivers/OptionRomPkg/UndiRuntimeDxe/Init.c
new file mode 100644
index 0000000000..2625a6cc5c
--- /dev/null
+++ b/Drivers/OptionRomPkg/UndiRuntimeDxe/Init.c
@@ -0,0 +1,1051 @@
+/** @file
+  Initialization functions for EFI UNDI32 driver.
+
+Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "Undi32.h"
+//
+// Global Variables
+//
+
+PXE_SW_UNDI             *pxe_31 = NULL;  // 3.1 entry
+UNDI32_DEV              *UNDI32DeviceList[MAX_NIC_INTERFACES];
+UNDI_CONFIG_TABLE       *UndiDataPointer = NULL;
+
+//
+// UNDI Class Driver Global Variables
+//
+EFI_DRIVER_BINDING_PROTOCOL  gUndiDriverBinding = {
+  UndiDriverSupported,
+  UndiDriverStart,
+  UndiDriverStop,
+  0xa,
+  NULL,
+  NULL
+};
+
+
+/**
+  When address mapping changes to virtual this should make the appropriate
+  address conversions.
+
+  (Standard Event handler)
+
+  @return None
+
+**/
+VOID
+EFIAPI
+UndiNotifyVirtual (
+  EFI_EVENT Event,
+  VOID      *Context
+  )
+{
+  UINT16  Index;
+  VOID    *Pxe31Pointer;
+
+  if (pxe_31 != NULL) {
+    Pxe31Pointer = (VOID *) pxe_31;
+
+    EfiConvertPointer (
+      EFI_OPTIONAL_PTR,
+      (VOID **) &Pxe31Pointer
+      );
+
+    //
+    // UNDI32DeviceList is an array of pointers
+    //
+    for (Index = 0; Index < (pxe_31->IFcnt | pxe_31->IFcntExt << 8); Index++) {
+      UNDI32DeviceList[Index]->NIIProtocol_31.Id = (UINT64) (UINTN) Pxe31Pointer;
+      EfiConvertPointer (
+        EFI_OPTIONAL_PTR,
+        (VOID **) &(UNDI32DeviceList[Index])
+        );
+    }
+
+    EfiConvertPointer (
+      EFI_OPTIONAL_PTR,
+      (VOID **) &(pxe_31->EntryPoint)
+      );
+    pxe_31 = Pxe31Pointer;
+  }
+
+  for (Index = 0; Index <= PXE_OPCODE_LAST_VALID; Index++) {
+    EfiConvertPointer (
+      EFI_OPTIONAL_PTR,
+      (VOID **) &api_table[Index].api_ptr
+      );
+  }
+}
+
+
+/**
+  When EFI is shuting down the boot services, we need to install a
+  configuration table for UNDI to work at runtime!
+
+  (Standard Event handler)
+
+  @return None
+
+**/
+VOID
+EFIAPI
+UndiNotifyReadyToBoot (
+  EFI_EVENT Event,
+  VOID      *Context
+  )
+{
+  InstallConfigTable ();
+}
+
+
+/**
+  Test to see if this driver supports ControllerHandle. Any ControllerHandle
+  than contains a  DevicePath, PciIo protocol, Class code of 2, Vendor ID of 0x8086,
+  and DeviceId of (D100_DEVICE_ID || D102_DEVICE_ID || ICH3_DEVICE_ID_1 ||
+  ICH3_DEVICE_ID_2 || ICH3_DEVICE_ID_3 || ICH3_DEVICE_ID_4 || ICH3_DEVICE_ID_5 ||
+  ICH3_DEVICE_ID_6 || ICH3_DEVICE_ID_7 || ICH3_DEVICE_ID_8) can be supported.
+
+  @param  This                 Protocol instance pointer.
+  @param  Controller           Handle of device to test.
+  @param  RemainingDevicePath  Not used.
+
+  @retval EFI_SUCCESS          This driver supports this device.
+  @retval other                This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+UndiDriverSupported (
+  IN EFI_DRIVER_BINDING_PROTOCOL    *This,
+  IN EFI_HANDLE                     Controller,
+  IN EFI_DEVICE_PATH_PROTOCOL       *RemainingDevicePath
+  )
+{
+  EFI_STATUS          Status;
+  EFI_PCI_IO_PROTOCOL *PciIo;
+  PCI_TYPE00          Pci;
+
+  Status = gBS->OpenProtocol (
+                  Controller,
+                  &gEfiDevicePathProtocolGuid,
+                  NULL,
+                  This->DriverBindingHandle,
+                  Controller,
+                  EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+                  );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = gBS->OpenProtocol (
+                  Controller,
+                  &gEfiPciIoProtocolGuid,
+                  (VOID **) &PciIo,
+                  This->DriverBindingHandle,
+                  Controller,
+                  EFI_OPEN_PROTOCOL_BY_DRIVER
+                  );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = PciIo->Pci.Read (
+                        PciIo,
+                        EfiPciIoWidthUint8,
+                        0,
+                        sizeof (PCI_CONFIG_HEADER),
+                        &Pci
+                        );
+
+  if (!EFI_ERROR (Status)) {
+    Status = EFI_UNSUPPORTED;
+
+    if (Pci.Hdr.ClassCode[2] == 0x02 && Pci.Hdr.VendorId == PCI_VENDOR_ID_INTEL) {
+      switch (Pci.Hdr.DeviceId) {
+      case D100_DEVICE_ID:
+      case D102_DEVICE_ID:
+      case ICH3_DEVICE_ID_1:
+      case ICH3_DEVICE_ID_2:
+      case ICH3_DEVICE_ID_3:
+      case ICH3_DEVICE_ID_4:
+      case ICH3_DEVICE_ID_5:
+      case ICH3_DEVICE_ID_6:
+      case ICH3_DEVICE_ID_7:
+      case ICH3_DEVICE_ID_8:
+      case 0x1039:
+      case 0x103A:
+      case 0x103B:
+      case 0x103C:
+      case 0x103D:
+      case 0x103E:
+      case 0x1050:
+      case 0x1051:
+      case 0x1052:
+      case 0x1053:
+      case 0x1054:
+      case 0x1055:
+      case 0x1056:
+      case 0x1057:
+      case 0x1059:
+      case 0x1064:
+        Status = EFI_SUCCESS;
+      }
+    }
+  }
+
+  gBS->CloseProtocol (
+        Controller,
+        &gEfiPciIoProtocolGuid,
+        This->DriverBindingHandle,
+        Controller
+        );
+
+  return Status;
+}
+
+
+/**
+  Start this driver on Controller by opening PciIo and DevicePath protocol.
+  Initialize PXE structures, create a copy of the Controller Device Path with the
+  NIC's MAC address appended to it, install the NetworkInterfaceIdentifier protocol
+  on the newly created Device Path.
+
+  @param  This                 Protocol instance pointer.
+  @param  Controller           Handle of device to work with.
+  @param  RemainingDevicePath  Not used, always produce all possible children.
+
+  @retval EFI_SUCCESS          This driver is added to Controller.
+  @retval other                This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+UndiDriverStart (
+  IN EFI_DRIVER_BINDING_PROTOCOL    *This,
+  IN EFI_HANDLE                     Controller,
+  IN EFI_DEVICE_PATH_PROTOCOL       *RemainingDevicePath
+  )
+{
+  EFI_STATUS                Status;
+  EFI_DEVICE_PATH_PROTOCOL  *UndiDevicePath;
+  PCI_CONFIG_HEADER         *CfgHdr;
+  UNDI32_DEV                *UNDI32Device;
+  UINT16                    NewCommand;
+  UINT8                     *TmpPxePointer;
+  EFI_PCI_IO_PROTOCOL       *PciIoFncs;
+  UINTN                     Len;
+  UINT64                    Supports;
+  BOOLEAN                   PciAttributesSaved;
+
+  Status = gBS->OpenProtocol (
+                  Controller,
+                  &gEfiPciIoProtocolGuid,
+                  (VOID **) &PciIoFncs,
+                  This->DriverBindingHandle,
+                  Controller,
+                  EFI_OPEN_PROTOCOL_BY_DRIVER
+                  );
+
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = gBS->OpenProtocol (
+                  Controller,
+                  &gEfiDevicePathProtocolGuid,
+                  (VOID **) &UndiDevicePath,
+                  This->DriverBindingHandle,
+                  Controller,
+                  EFI_OPEN_PROTOCOL_BY_DRIVER
+                  );
+
+  if (EFI_ERROR (Status)) {
+    gBS->CloseProtocol (
+          Controller,
+          &gEfiPciIoProtocolGuid,
+          This->DriverBindingHandle,
+          Controller
+          );
+
+    return Status;
+  }
+
+  PciAttributesSaved = FALSE;
+
+  Status = gBS->AllocatePool (
+                  EfiRuntimeServicesData,
+                  sizeof (UNDI32_DEV),
+                  (VOID **) &UNDI32Device
+                  );
+
+  if (EFI_ERROR (Status)) {
+    goto UndiError;
+  }
+
+  ZeroMem ((CHAR8 *) UNDI32Device, sizeof (UNDI32_DEV));
+
+  //
+  // Get original PCI attributes
+  //
+  Status = PciIoFncs->Attributes (
+                    PciIoFncs,
+                    EfiPciIoAttributeOperationGet,
+                    0,
+                    &UNDI32Device->NicInfo.OriginalPciAttributes
+                    );
+
+  if (EFI_ERROR (Status)) {
+    goto UndiErrorDeleteDevice;
+  }
+  PciAttributesSaved = TRUE;
+
+  //
+  // allocate and initialize both (old and new) the !pxe structures here,
+  // there should only be one copy of each of these structure for any number
+  // of NICs this undi supports. Also, these structures need to be on a
+  // paragraph boundary as per the spec. so, while allocating space for these,
+  // make sure that there is space for 2 !pxe structures (old and new) and a
+  // 32 bytes padding for alignment adjustment (in case)
+  //
+  TmpPxePointer = NULL;
+  if (pxe_31 == NULL) {
+    Status = gBS->AllocatePool (
+                    EfiRuntimeServicesData,
+                    (sizeof (PXE_SW_UNDI) + sizeof (PXE_SW_UNDI) + 32),
+                    (VOID **) &TmpPxePointer
+                    );
+
+    if (EFI_ERROR (Status)) {
+      goto UndiErrorDeleteDevice;
+    }
+
+    ZeroMem (
+      TmpPxePointer,
+      sizeof (PXE_SW_UNDI) + sizeof (PXE_SW_UNDI) + 32
+      );
+    //
+    // check for paragraph alignment here, assuming that the pointer is
+    // already 8 byte aligned.
+    //
+    if (((UINTN) TmpPxePointer & 0x0F) != 0) {
+      pxe_31 = (PXE_SW_UNDI *) ((UINTN) (TmpPxePointer + 8));
+    } else {
+      pxe_31 = (PXE_SW_UNDI *) TmpPxePointer;
+    }
+
+    PxeStructInit (pxe_31);
+  }
+
+  UNDI32Device->NIIProtocol_31.Id = (UINT64) (UINTN) (pxe_31);
+
+  Status = PciIoFncs->Attributes (
+                        PciIoFncs,
+                        EfiPciIoAttributeOperationSupported,
+                        0,
+                        &Supports
+                        );
+  if (!EFI_ERROR (Status)) {
+    Supports &= EFI_PCI_DEVICE_ENABLE;
+    Status = PciIoFncs->Attributes (
+                          PciIoFncs,
+                          EfiPciIoAttributeOperationEnable,
+                          Supports,
+                          NULL
+                          );
+  }
+  //
+  // Read all the registers from device's PCI Configuration space
+  //
+  Status = PciIoFncs->Pci.Read (
+                            PciIoFncs,
+                            EfiPciIoWidthUint32,
+                            0,
+                            MAX_PCI_CONFIG_LEN,
+                            &UNDI32Device->NicInfo.Config
+                            );
+
+  CfgHdr = (PCI_CONFIG_HEADER *) &(UNDI32Device->NicInfo.Config[0]);
+
+  //
+  // make sure that this device is a PCI bus master
+  //
+
+  NewCommand = (UINT16) (CfgHdr->Command | PCI_COMMAND_MASTER | PCI_COMMAND_IO);
+  if (CfgHdr->Command != NewCommand) {
+    PciIoFncs->Pci.Write (
+                    PciIoFncs,
+                    EfiPciIoWidthUint16,
+                    PCI_COMMAND,
+                    1,
+                    &NewCommand
+                    );
+    CfgHdr->Command = NewCommand;
+  }
+
+  //
+  // make sure that the latency timer is at least 32
+  //
+  if (CfgHdr->LatencyTimer < 32) {
+    CfgHdr->LatencyTimer = 32;
+    PciIoFncs->Pci.Write (
+                    PciIoFncs,
+                    EfiPciIoWidthUint8,
+                    PCI_LATENCY_TIMER,
+                    1,
+                    &CfgHdr->LatencyTimer
+                    );
+  }
+  //
+  // the IfNum index for the current interface will be the total number
+  // of interfaces initialized so far
+  //
+  UNDI32Device->NIIProtocol_31.IfNum  = pxe_31->IFcnt | pxe_31->IFcntExt << 8;
+
+  PxeUpdate (&UNDI32Device->NicInfo, pxe_31);
+
+  UNDI32Device->NicInfo.Io_Function                    = PciIoFncs;
+  UNDI32DeviceList[UNDI32Device->NIIProtocol_31.IfNum] = UNDI32Device;
+  UNDI32Device->Undi32BaseDevPath                      = UndiDevicePath;
+
+  Status = AppendMac2DevPath (
+            &UNDI32Device->Undi32DevPath,
+            UNDI32Device->Undi32BaseDevPath,
+            &UNDI32Device->NicInfo
+            );
+
+  if (Status != 0) {
+    goto UndiErrorDeletePxe;
+  }
+
+  UNDI32Device->Signature                     = UNDI_DEV_SIGNATURE;
+
+  UNDI32Device->NIIProtocol_31.Revision       = EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL_REVISION_31;
+  UNDI32Device->NIIProtocol_31.Type           = EfiNetworkInterfaceUndi;
+  UNDI32Device->NIIProtocol_31.MajorVer       = PXE_ROMID_MAJORVER;
+  UNDI32Device->NIIProtocol_31.MinorVer       = PXE_ROMID_MINORVER_31;
+  UNDI32Device->NIIProtocol_31.ImageSize      = 0;
+  UNDI32Device->NIIProtocol_31.ImageAddr      = 0;
+  UNDI32Device->NIIProtocol_31.Ipv6Supported  = TRUE;
+
+  UNDI32Device->NIIProtocol_31.StringId[0]    = 'U';
+  UNDI32Device->NIIProtocol_31.StringId[1]    = 'N';
+  UNDI32Device->NIIProtocol_31.StringId[2]    = 'D';
+  UNDI32Device->NIIProtocol_31.StringId[3]    = 'I';
+
+  UNDI32Device->DeviceHandle                  = NULL;
+
+  UNDI32Device->Aip.GetInformation            = UndiAipGetInfo;
+  UNDI32Device->Aip.SetInformation            = UndiAipSetInfo;
+  UNDI32Device->Aip.GetSupportedTypes         = UndiAipGetSupportedTypes;
+
+  //
+  // install both the 3.0 and 3.1 NII protocols.
+  //
+  Status = gBS->InstallMultipleProtocolInterfaces (
+                  &UNDI32Device->DeviceHandle,
+                  &gEfiNetworkInterfaceIdentifierProtocolGuid_31,
+                  &UNDI32Device->NIIProtocol_31,
+                  &gEfiDevicePathProtocolGuid,
+                  UNDI32Device->Undi32DevPath,
+                  &gEfiAdapterInformationProtocolGuid,
+                  &UNDI32Device->Aip,
+                  NULL
+                  );
+
+  if (EFI_ERROR (Status)) {
+    goto UndiErrorDeleteDevicePath;
+  }
+
+  //
+  // if the table exists, free it and alloc again, or alloc it directly
+  //
+  if (UndiDataPointer != NULL) {
+    Status = gBS->FreePool(UndiDataPointer);
+  }
+  if (EFI_ERROR (Status)) {
+    goto UndiErrorDeleteDevicePath;
+  }
+
+  Len = ((pxe_31->IFcnt|pxe_31->IFcntExt << 8)* sizeof (UndiDataPointer->NII_entry)) + sizeof (UndiDataPointer);
+  Status = gBS->AllocatePool (EfiRuntimeServicesData, Len, (VOID **) &UndiDataPointer);
+
+  if (EFI_ERROR (Status)) {
+    goto UndiErrorAllocDataPointer;
+  }
+
+  //
+  // Open For Child Device
+  //
+  Status = gBS->OpenProtocol (
+                  Controller,
+                  &gEfiPciIoProtocolGuid,
+                  (VOID **) &PciIoFncs,
+                  This->DriverBindingHandle,
+                  UNDI32Device->DeviceHandle,
+                  EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+                  );
+
+  return EFI_SUCCESS;
+UndiErrorAllocDataPointer:
+  gBS->UninstallMultipleProtocolInterfaces (
+                  &UNDI32Device->DeviceHandle,
+                  &gEfiNetworkInterfaceIdentifierProtocolGuid_31,
+                  &UNDI32Device->NIIProtocol_31,
+                  &gEfiDevicePathProtocolGuid,
+                  UNDI32Device->Undi32DevPath,
+                  &gEfiAdapterInformationProtocolGuid,
+                  &UNDI32Device->Aip,
+                  NULL
+                  );
+
+UndiErrorDeleteDevicePath:
+  UNDI32DeviceList[UNDI32Device->NIIProtocol_31.IfNum] = NULL;
+  gBS->FreePool (UNDI32Device->Undi32DevPath);
+
+UndiErrorDeletePxe:
+  PxeUpdate (NULL, pxe_31);
+  if (TmpPxePointer != NULL) {
+    gBS->FreePool (TmpPxePointer);
+
+  }
+
+UndiErrorDeleteDevice:
+  if (PciAttributesSaved) {
+    //
+    // Restore original PCI attributes
+    //
+    PciIoFncs->Attributes (
+                    PciIoFncs,
+                    EfiPciIoAttributeOperationSet,
+                    UNDI32Device->NicInfo.OriginalPciAttributes,
+                    NULL
+                    );
+  }
+
+  gBS->FreePool (UNDI32Device);
+
+UndiError:
+  gBS->CloseProtocol (
+        Controller,
+        &gEfiDevicePathProtocolGuid,
+        This->DriverBindingHandle,
+        Controller
+        );
+
+  gBS->CloseProtocol (
+        Controller,
+        &gEfiPciIoProtocolGuid,
+        This->DriverBindingHandle,
+        Controller
+        );
+
+  return Status;
+}
+
+
+/**
+  Stop this driver on Controller by removing NetworkInterfaceIdentifier protocol and
+  closing the DevicePath and PciIo protocols on Controller.
+
+  @param  This                 Protocol instance pointer.
+  @param  Controller           Handle of device to stop driver on.
+  @param  NumberOfChildren     How many children need to be stopped.
+  @param  ChildHandleBuffer    Not used.
+
+  @retval EFI_SUCCESS          This driver is removed Controller.
+  @retval other                This driver was not removed from this device.
+
+**/
+// TODO:    EFI_DEVICE_ERROR - add return value to function comment
+EFI_STATUS
+EFIAPI
+UndiDriverStop (
+  IN  EFI_DRIVER_BINDING_PROTOCOL    *This,
+  IN  EFI_HANDLE                     Controller,
+  IN  UINTN                          NumberOfChildren,
+  IN  EFI_HANDLE                     *ChildHandleBuffer
+  )
+{
+  EFI_STATUS                                Status;
+  BOOLEAN                                   AllChildrenStopped;
+  UINTN                                     Index;
+  UNDI32_DEV                                *UNDI32Device;
+  EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL *NIIProtocol;
+
+  //
+  // Complete all outstanding transactions to Controller.
+  // Don't allow any new transaction to Controller to be started.
+  //
+  if (NumberOfChildren == 0) {
+
+    //
+    // Close the bus driver
+    //
+    Status = gBS->CloseProtocol (
+                    Controller,
+                    &gEfiDevicePathProtocolGuid,
+                    This->DriverBindingHandle,
+                    Controller
+                    );
+
+    Status = gBS->CloseProtocol (
+                    Controller,
+                    &gEfiPciIoProtocolGuid,
+                    This->DriverBindingHandle,
+                    Controller
+                    );
+
+    return Status;
+  }
+
+  AllChildrenStopped = TRUE;
+
+  for (Index = 0; Index < NumberOfChildren; Index++) {
+
+    Status = gBS->OpenProtocol (
+                    ChildHandleBuffer[Index],
+                    &gEfiNetworkInterfaceIdentifierProtocolGuid_31,
+                    (VOID **) &NIIProtocol,
+                    This->DriverBindingHandle,
+                    Controller,
+                    EFI_OPEN_PROTOCOL_GET_PROTOCOL
+                    );
+    if (!EFI_ERROR (Status)) {
+
+      UNDI32Device = UNDI_DEV_FROM_THIS (NIIProtocol);
+
+      Status = gBS->CloseProtocol (
+                      Controller,
+                      &gEfiPciIoProtocolGuid,
+                      This->DriverBindingHandle,
+                      ChildHandleBuffer[Index]
+                      );
+      if (!EFI_ERROR (Status)) {
+        Status = gBS->UninstallMultipleProtocolInterfaces (
+                        ChildHandleBuffer[Index],
+                        &gEfiDevicePathProtocolGuid,
+                        UNDI32Device->Undi32DevPath,
+                        &gEfiNetworkInterfaceIdentifierProtocolGuid_31,
+                        &UNDI32Device->NIIProtocol_31,
+                        NULL
+                        );
+        if (!EFI_ERROR (Status)) {
+          //
+          // Restore original PCI attributes
+          //
+          Status = UNDI32Device->NicInfo.Io_Function->Attributes (
+                                                        UNDI32Device->NicInfo.Io_Function,
+                                                        EfiPciIoAttributeOperationSet,
+                                                        UNDI32Device->NicInfo.OriginalPciAttributes,
+                                                        NULL
+                                                        );
+
+          ASSERT_EFI_ERROR (Status);
+
+          gBS->FreePool (UNDI32Device->Undi32DevPath);
+          gBS->FreePool (UNDI32Device);
+
+        }
+      }
+    }
+
+    if (EFI_ERROR (Status)) {
+      AllChildrenStopped = FALSE;
+    }
+  }
+
+  if (!AllChildrenStopped) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  return EFI_SUCCESS;
+
+}
+
+
+/**
+  Use the EFI boot services to produce a pause. This is also the routine which
+  gets replaced during RunTime by the O/S in the NIC_DATA_INSTANCE so it can
+  do it's own pause.
+
+  @param  UnqId                Runtime O/S routine might use this, this temp
+                               routine does not use it
+  @param  MicroSeconds         Determines the length of pause.
+
+  @return none
+
+**/
+VOID
+TmpDelay (
+  IN UINT64 UnqId,
+  IN UINTN  MicroSeconds
+  )
+{
+  gBS->Stall ((UINT32) MicroSeconds);
+}
+
+
+/**
+  Use the PCI IO abstraction to issue memory or I/O reads and writes.  This is also the routine which
+  gets replaced during RunTime by the O/S in the NIC_DATA_INSTANCE so it can do it's own I/O abstractions.
+
+  @param  UnqId                Runtime O/S routine may use this field, this temp
+                               routine does not.
+  @param  ReadWrite            Determine if it is an I/O or Memory Read/Write
+                               Operation.
+  @param  Len                  Determines the width of the data operation.
+  @param  Port                 What port to Read/Write from.
+  @param  BuffAddr             Address to read to or write from.
+
+  @return none
+
+**/
+VOID
+TmpMemIo (
+  IN UINT64 UnqId,
+  IN UINT8  ReadWrite,
+  IN UINT8  Len,
+  IN UINT64 Port,
+  IN UINT64 BuffAddr
+  )
+{
+  EFI_PCI_IO_PROTOCOL_WIDTH Width;
+  NIC_DATA_INSTANCE         *AdapterInfo;
+
+  Width       = (EFI_PCI_IO_PROTOCOL_WIDTH) 0;
+  AdapterInfo = (NIC_DATA_INSTANCE *) (UINTN) UnqId;
+  switch (Len) {
+  case 2:
+    Width = (EFI_PCI_IO_PROTOCOL_WIDTH) 1;
+    break;
+
+  case 4:
+    Width = (EFI_PCI_IO_PROTOCOL_WIDTH) 2;
+    break;
+
+  case 8:
+    Width = (EFI_PCI_IO_PROTOCOL_WIDTH) 3;
+    break;
+  }
+
+  switch (ReadWrite) {
+  case PXE_IO_READ:
+    AdapterInfo->Io_Function->Io.Read (
+                                  AdapterInfo->Io_Function,
+                                  Width,
+                                  1,
+                                  Port,
+                                  1,
+                                  (VOID *) (UINTN) (BuffAddr)
+                                  );
+    break;
+
+  case PXE_IO_WRITE:
+    AdapterInfo->Io_Function->Io.Write (
+                                  AdapterInfo->Io_Function,
+                                  Width,
+                                  1,
+                                  Port,
+                                  1,
+                                  (VOID *) (UINTN) (BuffAddr)
+                                  );
+    break;
+
+  case PXE_MEM_READ:
+    AdapterInfo->Io_Function->Mem.Read (
+                                    AdapterInfo->Io_Function,
+                                    Width,
+                                    0,
+                                    Port,
+                                    1,
+                                    (VOID *) (UINTN) (BuffAddr)
+                                    );
+    break;
+
+  case PXE_MEM_WRITE:
+    AdapterInfo->Io_Function->Mem.Write (
+                                    AdapterInfo->Io_Function,
+                                    Width,
+                                    0,
+                                    Port,
+                                    1,
+                                    (VOID *) (UINTN) (BuffAddr)
+                                    );
+    break;
+  }
+
+  return ;
+}
+
+
+/**
+  Using the NIC data structure information, read the EEPROM to get the MAC address and then allocate space
+  for a new devicepath (**DevPtr) which will contain the original device path the NIC was found on (*BaseDevPtr)
+  and an added MAC node.
+
+  @param  DevPtr               Pointer which will point to the newly created device
+                               path with the MAC node attached.
+  @param  BaseDevPtr           Pointer to the device path which the UNDI device
+                               driver is latching on to.
+  @param  AdapterInfo          Pointer to the NIC data structure information which
+                               the UNDI driver is layering on..
+
+  @retval EFI_SUCCESS          A MAC address was successfully appended to the Base
+                               Device Path.
+  @retval other                Not enough resources available to create new Device
+                               Path node.
+
+**/
+EFI_STATUS
+AppendMac2DevPath (
+  IN OUT  EFI_DEVICE_PATH_PROTOCOL **DevPtr,
+  IN      EFI_DEVICE_PATH_PROTOCOL *BaseDevPtr,
+  IN      NIC_DATA_INSTANCE        *AdapterInfo
+  )
+{
+  EFI_MAC_ADDRESS           MACAddress;
+  PCI_CONFIG_HEADER         *CfgHdr;
+  INT32                     Val;
+  INT32                     Index;
+  INT32                     Index2;
+  UINT8                     AddrLen;
+  MAC_ADDR_DEVICE_PATH      MacAddrNode;
+  EFI_DEVICE_PATH_PROTOCOL  *EndNode;
+  UINT8                     *DevicePtr;
+  UINT16                    TotalPathLen;
+  UINT16                    BasePathLen;
+  EFI_STATUS                Status;
+
+  //
+  // set the environment ready (similar to UNDI_Start call) so that we can
+  // execute the other UNDI_ calls to get the mac address
+  // we are using undi 3.1 style
+  //
+  AdapterInfo->Delay      = TmpDelay;
+  AdapterInfo->Virt2Phys  = (VOID *) 0;
+  AdapterInfo->Block      = (VOID *) 0;
+  AdapterInfo->Map_Mem    = (VOID *) 0;
+  AdapterInfo->UnMap_Mem  = (VOID *) 0;
+  AdapterInfo->Sync_Mem   = (VOID *) 0;
+  AdapterInfo->Mem_Io     = TmpMemIo;
+  //
+  // these tmp call-backs follow 3.1 undi style
+  // i.e. they have the unique_id parameter.
+  //
+  AdapterInfo->VersionFlag  = 0x31;
+  AdapterInfo->Unique_ID    = (UINT64) (UINTN) AdapterInfo;
+
+  //
+  // undi init portion
+  //
+  CfgHdr              = (PCI_CONFIG_HEADER *) &(AdapterInfo->Config[0]);
+  AdapterInfo->ioaddr = 0;
+  AdapterInfo->RevID  = CfgHdr->RevID;
+
+  AddrLen             = E100bGetEepromAddrLen (AdapterInfo);
+
+  for (Index = 0, Index2 = 0; Index < 3; Index++) {
+    Val                       = E100bReadEeprom (AdapterInfo, Index, AddrLen);
+    MACAddress.Addr[Index2++] = (UINT8) Val;
+    MACAddress.Addr[Index2++] = (UINT8) (Val >> 8);
+  }
+
+  SetMem (MACAddress.Addr + Index2, sizeof (EFI_MAC_ADDRESS) - Index2, 0);
+  //for (; Index2 < sizeof (EFI_MAC_ADDRESS); Index2++) {
+  //  MACAddress.Addr[Index2] = 0;
+  //}
+  //
+  // stop undi
+  //
+  AdapterInfo->Delay  = (VOID *) 0;
+  AdapterInfo->Mem_Io = (VOID *) 0;
+
+  //
+  // fill the mac address node first
+  //
+  ZeroMem ((CHAR8 *) &MacAddrNode, sizeof MacAddrNode);
+  CopyMem (
+    (CHAR8 *) &MacAddrNode.MacAddress,
+    (CHAR8 *) &MACAddress,
+    sizeof (EFI_MAC_ADDRESS)
+    );
+
+  MacAddrNode.Header.Type       = MESSAGING_DEVICE_PATH;
+  MacAddrNode.Header.SubType    = MSG_MAC_ADDR_DP;
+  MacAddrNode.Header.Length[0]  = (UINT8) sizeof (MacAddrNode);
+  MacAddrNode.Header.Length[1]  = 0;
+
+  //
+  // find the size of the base dev path.
+  //
+  EndNode = BaseDevPtr;
+
+  while (!IsDevicePathEnd (EndNode)) {
+    EndNode = NextDevicePathNode (EndNode);
+  }
+
+  BasePathLen = (UINT16) ((UINTN) (EndNode) - (UINTN) (BaseDevPtr));
+
+  //
+  // create space for full dev path
+  //
+  TotalPathLen = (UINT16) (BasePathLen + sizeof (MacAddrNode) + sizeof (EFI_DEVICE_PATH_PROTOCOL));
+
+  Status = gBS->AllocatePool (
+                  EfiRuntimeServicesData,
+                  TotalPathLen,
+                  (VOID **) &DevicePtr
+                  );
+
+  if (Status != EFI_SUCCESS) {
+    return Status;
+  }
+  //
+  // copy the base path, mac addr and end_dev_path nodes
+  //
+  *DevPtr = (EFI_DEVICE_PATH_PROTOCOL *) DevicePtr;
+  CopyMem (DevicePtr, (CHAR8 *) BaseDevPtr, BasePathLen);
+  DevicePtr += BasePathLen;
+  CopyMem (DevicePtr, (CHAR8 *) &MacAddrNode, sizeof (MacAddrNode));
+  DevicePtr += sizeof (MacAddrNode);
+  CopyMem (DevicePtr, (CHAR8 *) EndNode, sizeof (EFI_DEVICE_PATH_PROTOCOL));
+
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Install a GUID/Pointer pair into the system's configuration table.
+
+  none
+
+  @retval EFI_SUCCESS          Install a GUID/Pointer pair into the system's
+                               configuration table.
+  @retval other                Did not successfully install the GUID/Pointer pair
+                               into the configuration table.
+
+**/
+// TODO:    VOID - add argument and description to function comment
+EFI_STATUS
+InstallConfigTable (
+  IN VOID
+  )
+{
+  EFI_STATUS              Status;
+  EFI_CONFIGURATION_TABLE *CfgPtr;
+  UNDI_CONFIG_TABLE       *TmpData;
+  UINT16                  Index;
+  UNDI_CONFIG_TABLE       *UndiData;
+
+  if (pxe_31 == NULL) {
+    return EFI_SUCCESS;
+  }
+
+  if(UndiDataPointer == NULL) {
+    return EFI_SUCCESS;
+  }
+
+  UndiData = (UNDI_CONFIG_TABLE *)UndiDataPointer;
+
+  UndiData->NumberOfInterfaces  = (pxe_31->IFcnt | pxe_31->IFcntExt << 8);
+  UndiData->nextlink            = NULL;
+
+  for (Index = 0; Index < (pxe_31->IFcnt | pxe_31->IFcntExt << 8); Index++) {
+    UndiData->NII_entry[Index].NII_InterfacePointer = &UNDI32DeviceList[Index]->NIIProtocol_31;
+    UndiData->NII_entry[Index].DevicePathPointer    = UNDI32DeviceList[Index]->Undi32DevPath;
+  }
+
+  //
+  // see if there is an entry in the config table already
+  //
+  CfgPtr = gST->ConfigurationTable;
+
+  for (Index = 0; Index < gST->NumberOfTableEntries; Index++) {
+    Status = CompareGuid (
+              &CfgPtr->VendorGuid,
+              &gEfiNetworkInterfaceIdentifierProtocolGuid_31
+              );
+    if (Status != EFI_SUCCESS) {
+      break;
+    }
+
+    CfgPtr++;
+  }
+
+  if (Index < gST->NumberOfTableEntries) {
+    TmpData = (UNDI_CONFIG_TABLE *) CfgPtr->VendorTable;
+
+    //
+    // go to the last link
+    //
+    while (TmpData->nextlink != NULL) {
+      TmpData = TmpData->nextlink;
+    }
+
+    TmpData->nextlink = UndiData;
+
+    //
+    // 1st one in chain
+    //
+    UndiData = (UNDI_CONFIG_TABLE *) CfgPtr->VendorTable;
+  }
+
+  //
+  // create an entry in the configuration table for our GUID
+  //
+  Status = gBS->InstallConfigurationTable (
+                  &gEfiNetworkInterfaceIdentifierProtocolGuid_31,
+                  UndiData
+                  );
+  return Status;
+}
+
+/**
+
+**/
+EFI_STATUS
+EFIAPI
+InitializeUndi(
+  IN EFI_HANDLE           ImageHandle,
+  IN EFI_SYSTEM_TABLE     *SystemTable
+  )
+{
+  EFI_EVENT     Event;
+  EFI_STATUS    Status;
+
+  Status = EfiLibInstallDriverBindingComponentName2 (
+             ImageHandle,
+             SystemTable,
+             &gUndiDriverBinding,
+             ImageHandle,
+             &gUndiComponentName,
+             &gUndiComponentName2
+             );
+  ASSERT_EFI_ERROR (Status);
+
+  Status = gBS->CreateEventEx (
+                  EVT_NOTIFY_SIGNAL,
+                  TPL_NOTIFY,
+                  UndiNotifyReadyToBoot,
+                  NULL,
+                  &gEfiEventReadyToBootGuid,
+                  &Event
+                  );
+  ASSERT_EFI_ERROR (Status);
+
+  Status = gBS->CreateEventEx (
+                  EVT_NOTIFY_SIGNAL,
+                  TPL_NOTIFY,
+                  UndiNotifyVirtual,
+                  NULL,
+                  &gEfiEventVirtualAddressChangeGuid,
+                  &Event
+                  );
+  ASSERT_EFI_ERROR (Status);
+
+  return Status;
+}
diff --git a/Drivers/OptionRomPkg/UndiRuntimeDxe/Undi32.h b/Drivers/OptionRomPkg/UndiRuntimeDxe/Undi32.h
new file mode 100644
index 0000000000..31c55a8e11
--- /dev/null
+++ b/Drivers/OptionRomPkg/UndiRuntimeDxe/Undi32.h
@@ -0,0 +1,439 @@
+/** @file
+  EFI internal structures for the EFI UNDI driver.
+
+Copyright (c) 2006 - 2017, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef _UNDI_32_H_
+#define _UNDI_32_H_
+
+#include <Uefi.h>
+
+#include <Guid/EventGroup.h>
+#include <Protocol/PciIo.h>
+#include <Protocol/NetworkInterfaceIdentifier.h>
+#include <Protocol/DevicePath.h>
+#include <Protocol/AdapterInformation.h>
+
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/UefiRuntimeLib.h>
+#include <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/BaseLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/MemoryAllocationLib.h>
+
+#include <IndustryStandard/Pci.h>
+
+
+#include "E100b.h"
+
+extern EFI_DRIVER_BINDING_PROTOCOL  gUndiDriverBinding;
+extern EFI_COMPONENT_NAME_PROTOCOL  gUndiComponentName;
+extern EFI_COMPONENT_NAME2_PROTOCOL gUndiComponentName2;
+
+#define MAX_NIC_INTERFACES 16
+
+#define EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL_REVISION_31 0x00010001
+#define PXE_ROMID_MINORVER_31 0x10
+#define PXE_STATFLAGS_DB_WRITE_TRUNCATED  0x2000
+
+//
+// UNDI_CALL_TABLE.state can have the following values
+//
+#define DONT_CHECK -1
+#define ANY_STATE -1
+#define MUST_BE_STARTED 1
+#define MUST_BE_INITIALIZED 2
+
+#define UNDI_DEV_SIGNATURE   SIGNATURE_32('u','n','d','i')
+#define UNDI_DEV_FROM_THIS(a) CR(a, UNDI32_DEV, NIIProtocol_31, UNDI_DEV_SIGNATURE)
+#define UNDI_DEV_FROM_NIC(a) CR(a, UNDI32_DEV, NicInfo, UNDI_DEV_SIGNATURE)
+#define UNDI_DEV_FROM_AIP(a) CR(a, UNDI32_DEV, Aip, UNDI_DEV_SIGNATURE)
+
+typedef struct {
+  UINTN                                     Signature;
+  EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL NIIProtocol_31;
+  EFI_ADAPTER_INFORMATION_PROTOCOL          Aip;
+  EFI_HANDLE                                DeviceHandle;
+  EFI_DEVICE_PATH_PROTOCOL                  *Undi32BaseDevPath;
+  EFI_DEVICE_PATH_PROTOCOL                  *Undi32DevPath;
+  NIC_DATA_INSTANCE                         NicInfo;
+} UNDI32_DEV;
+
+typedef struct {
+  UINT16 cpbsize;
+  UINT16 dbsize;
+  UINT16 opflags;
+  UINT16 state;
+  VOID (*api_ptr)();
+} UNDI_CALL_TABLE;
+
+typedef VOID (*ptr)(VOID);
+typedef VOID (*bsptr_30)(UINTN);
+typedef VOID (*virtphys_30)(UINT64, UINT64);
+typedef VOID (*block_30)(UINT32);
+typedef VOID (*mem_io_30)(UINT8, UINT8, UINT64, UINT64);
+
+typedef VOID (*bsptr)(UINT64, UINTN);
+typedef VOID (*virtphys)(UINT64, UINT64, UINT64);
+typedef VOID (*block)(UINT64, UINT32);
+typedef VOID (*mem_io)(UINT64, UINT8, UINT8, UINT64, UINT64);
+
+typedef VOID (*map_mem)(UINT64, UINT64, UINT32, UINT32, UINT64);
+typedef VOID (*unmap_mem)(UINT64, UINT64, UINT32, UINT32, UINT64);
+typedef VOID (*sync_mem)(UINT64, UINT64, UINT32, UINT32, UINT64);
+
+extern UNDI_CALL_TABLE  api_table[];
+extern PXE_SW_UNDI      *pxe_31;  // !pxe structure for 3.1 drivers
+extern UNDI32_DEV       *UNDI32DeviceList[MAX_NIC_INTERFACES];
+
+//
+// functions defined in e100b.c
+//
+UINT8 InByte (NIC_DATA_INSTANCE *AdapterInfo, UINT32 Port);
+UINT16 InWord (NIC_DATA_INSTANCE *AdapterInfo, UINT32 Port);
+UINT32 InLong (NIC_DATA_INSTANCE *AdapterInfo, UINT32 Port);
+VOID  OutByte (NIC_DATA_INSTANCE *AdapterInfo, UINT8 Data, UINT32 Port);
+VOID  OutWord (NIC_DATA_INSTANCE *AdapterInfo, UINT16 Data, UINT32 Port);
+VOID  OutLong (NIC_DATA_INSTANCE *AdapterInfo, UINT32 Data, UINT32 Port);
+
+UINTN E100bInit (NIC_DATA_INSTANCE *AdapterInfo);
+UINTN E100bReset (NIC_DATA_INSTANCE *AdapterInfo, INT32 OpFlags);
+UINTN E100bShutdown (NIC_DATA_INSTANCE *AdapterInfo);
+UINTN E100bTransmit (NIC_DATA_INSTANCE *AdapterInfo, UINT64 cpb, UINT16 opflags);
+UINTN E100bReceive (NIC_DATA_INSTANCE *AdapterInfo, UINT64 cpb, UINT64 db);
+UINTN E100bSetfilter (NIC_DATA_INSTANCE *AdapterInfo, UINT16 New_filter,
+                      UINT64 cpb, UINT32 cpbsize);
+UINTN E100bStatistics(NIC_DATA_INSTANCE *AdapterInfo, UINT64 db, UINT16 dbsize);
+UINT8 E100bSetupIAAddr (NIC_DATA_INSTANCE *AdapterInfo);
+UINT8 E100bSetInterruptState (NIC_DATA_INSTANCE *AdapterInfo);
+
+UINT8 E100bGetEepromAddrLen (NIC_DATA_INSTANCE *AdapterInfo);
+UINT16 E100bReadEeprom (NIC_DATA_INSTANCE *AdapterInfo, INT32 Location, UINT8 address_len);
+INT16 E100bReadEepromAndStationAddress (NIC_DATA_INSTANCE *AdapterInfo);
+
+UINT16 next(UINT16);
+UINT8 SetupCBlink (NIC_DATA_INSTANCE *AdapterInfo);
+VOID SetFreeCB (NIC_DATA_INSTANCE *AdapterInfo,TxCB *);
+TxCB *GetFreeCB (NIC_DATA_INSTANCE *AdapterInfo);
+UINT16 CheckCBList (NIC_DATA_INSTANCE *AdapterInfo);
+
+UINT8 SelectiveReset (NIC_DATA_INSTANCE *AdapterInfo);
+UINT16 InitializeChip (NIC_DATA_INSTANCE *AdapterInfo);
+UINT8 SetupReceiveQueues (NIC_DATA_INSTANCE *AdapterInfo);
+VOID  Recycle_RFD (NIC_DATA_INSTANCE *AdapterInfo, UINT16);
+VOID XmitWaitForCompletion (NIC_DATA_INSTANCE *AdapterInfo);
+INT8 CommandWaitForCompletion (TxCB *cmd_ptr, NIC_DATA_INSTANCE *AdapterInfo);
+
+BOOLEAN PhyDetect (NIC_DATA_INSTANCE *AdapterInfo);
+VOID PhyReset (NIC_DATA_INSTANCE *AdapterInfo);
+VOID
+MdiWrite (
+  IN NIC_DATA_INSTANCE *AdapterInfo,
+  IN UINT8 RegAddress,
+  IN UINT8 PhyAddress,
+  IN UINT16 DataValue
+  );
+
+VOID
+MdiRead(
+  IN NIC_DATA_INSTANCE *AdapterInfo,
+  IN UINT8 RegAddress,
+  IN UINT8 PhyAddress,
+  IN OUT UINT16 *DataValue
+  );
+
+BOOLEAN SetupPhy (NIC_DATA_INSTANCE *AdapterInfo);
+VOID FindPhySpeedAndDpx (NIC_DATA_INSTANCE *AdapterInfo, UINT32 PhyId);
+
+
+
+//
+// functions defined in init.c
+//
+EFI_STATUS
+InstallConfigTable (
+  IN VOID
+  );
+
+EFI_STATUS
+EFIAPI
+InitializeUNDIDriver (
+  IN EFI_HANDLE           ImageHandle,
+  IN EFI_SYSTEM_TABLE     *SystemTable
+  );
+
+VOID
+UNDI_notify_virtual (
+  EFI_EVENT event,
+  VOID      *context
+  );
+
+EFI_STATUS
+EFIAPI
+UndiDriverSupported (
+  IN EFI_DRIVER_BINDING_PROTOCOL    *This,
+  IN EFI_HANDLE                     Controller,
+  IN EFI_DEVICE_PATH_PROTOCOL       *RemainingDevicePath
+  );
+
+EFI_STATUS
+EFIAPI
+UndiDriverStart (
+  IN EFI_DRIVER_BINDING_PROTOCOL    *This,
+  IN EFI_HANDLE                     Controller,
+  IN EFI_DEVICE_PATH_PROTOCOL       *RemainingDevicePath
+  );
+
+EFI_STATUS
+EFIAPI
+UndiDriverStop (
+  IN  EFI_DRIVER_BINDING_PROTOCOL    *This,
+  IN  EFI_HANDLE                     Controller,
+  IN  UINTN                          NumberOfChildren,
+  IN  EFI_HANDLE                     *ChildHandleBuffer
+  );
+
+EFI_STATUS
+AppendMac2DevPath (
+  IN OUT  EFI_DEVICE_PATH_PROTOCOL **DevPtr,
+  IN      EFI_DEVICE_PATH_PROTOCOL *BaseDevPtr,
+  IN      NIC_DATA_INSTANCE        *AdapterInfo
+  );
+
+VOID
+TmpDelay (
+  IN UINT64 UnqId,
+  IN UINTN MicroSeconds
+  );
+
+VOID
+TmpMemIo (
+  IN UINT64 UnqId,
+  IN UINT8 ReadWrite,
+  IN UINT8 Len,
+  IN UINT64 Port,
+  IN UINT64 BufAddr
+  );
+
+//
+// functions defined in decode.c
+//
+VOID
+UNDI_GetState (
+  IN  PXE_CDB           *CdbPtr,
+  IN  NIC_DATA_INSTANCE *AdapterInfo
+  );
+
+VOID
+UNDI_Start (
+  IN  PXE_CDB           *CdbPtr,
+  IN  NIC_DATA_INSTANCE *AdapterInfo
+  );
+
+VOID
+UNDI_Stop (
+  IN  PXE_CDB           *CdbPtr,
+  IN  NIC_DATA_INSTANCE *AdapterInfo
+  );
+
+VOID
+UNDI_GetInitInfo (
+  IN  PXE_CDB           *CdbPtr,
+  IN  NIC_DATA_INSTANCE *AdapterInfo
+  );
+
+VOID
+UNDI_GetConfigInfo (
+  IN  PXE_CDB           *CdbPtr,
+  IN  NIC_DATA_INSTANCE *AdapterInfo
+  );
+
+VOID
+UNDI_Initialize (
+  IN  PXE_CDB       *CdbPtr,
+  NIC_DATA_INSTANCE *AdapterInfo
+  );
+
+VOID
+UNDI_Reset (
+  IN  PXE_CDB           *CdbPtr,
+  IN  NIC_DATA_INSTANCE *AdapterInfo
+  );
+
+VOID
+UNDI_Shutdown (
+  IN  PXE_CDB           *CdbPtr,
+  IN  NIC_DATA_INSTANCE *AdapterInfo
+  );
+
+VOID
+UNDI_Interrupt (
+  IN  PXE_CDB           *CdbPtr,
+  IN  NIC_DATA_INSTANCE *AdapterInfo
+  );
+
+VOID
+UNDI_RecFilter (
+  IN  PXE_CDB           *CdbPtr,
+  IN  NIC_DATA_INSTANCE *AdapterInfo
+  );
+
+VOID
+UNDI_StnAddr (
+  IN  PXE_CDB           *CdbPtr,
+  IN  NIC_DATA_INSTANCE *AdapterInfo
+  );
+
+VOID
+UNDI_Statistics (
+  IN  PXE_CDB           *CdbPtr,
+  IN  NIC_DATA_INSTANCE *AdapterInfo
+  );
+
+VOID
+UNDI_ip2mac (
+  IN  PXE_CDB           *CdbPtr,
+  IN  NIC_DATA_INSTANCE *AdapterInfo
+  );
+
+VOID
+UNDI_NVData (
+  IN  PXE_CDB           *CdbPtr,
+  IN  NIC_DATA_INSTANCE *AdapterInfo
+  );
+
+VOID
+UNDI_Status (
+  IN  PXE_CDB           *CdbPtr,
+  IN  NIC_DATA_INSTANCE *AdapterInfo
+  );
+
+VOID
+UNDI_FillHeader (
+  IN  PXE_CDB           *CdbPtr,
+  IN  NIC_DATA_INSTANCE *AdapterInfo
+  );
+
+VOID
+UNDI_Transmit (
+  IN  PXE_CDB           *CdbPtr,
+  IN  NIC_DATA_INSTANCE *AdapterInfo
+  );
+
+VOID
+UNDI_Receive (
+  IN  PXE_CDB           *CdbPtr,
+  IN  NIC_DATA_INSTANCE *AdapterInfo
+  );
+
+VOID EFIAPI UNDI_APIEntry_new(UINT64);
+VOID UNDI_APIEntry_Common(UINT64);
+
+PXE_IPV4 convert_mcip(PXE_MAC_ADDR *);
+INT32 validate_mcip (PXE_MAC_ADDR *MCastAddr);
+
+VOID PxeStructInit (PXE_SW_UNDI *PxePtr);
+VOID PxeUpdate (NIC_DATA_INSTANCE *NicPtr, PXE_SW_UNDI *PxePtr);
+
+//
+// functions defined in UndiAipImpl.c
+//
+
+/**
+  Returns the current state information for the adapter.
+
+  This function returns information of type InformationType from the adapter.
+  If an adapter does not support the requested informational type, then
+  EFI_UNSUPPORTED is returned. 
+
+  @param[in]  This                   A pointer to the EFI_ADAPTER_INFORMATION_PROTOCOL instance.
+  @param[in]  InformationType        A pointer to an EFI_GUID that defines the contents of InformationBlock.
+  @param[out] InforamtionBlock       The service returns a pointer to the buffer with the InformationBlock
+                                     structure which contains details about the data specific to InformationType.
+  @param[out] InforamtionBlockSize   The driver returns the size of the InformationBlock in bytes.
+
+  @retval EFI_SUCCESS                The InformationType information was retrieved.
+  @retval EFI_UNSUPPORTED            The InformationType is not known.
+  @retval EFI_DEVICE_ERROR           The device reported an error.
+  @retval EFI_OUT_OF_RESOURCES       The request could not be completed due to a lack of resources.
+  @retval EFI_INVALID_PARAMETER      This is NULL. 
+  @retval EFI_INVALID_PARAMETER      InformationBlock is NULL. 
+  @retval EFI_INVALID_PARAMETER      InformationBlockSize is NULL.
+
+**/  
+EFI_STATUS
+EFIAPI
+UndiAipGetInfo (
+  IN   EFI_ADAPTER_INFORMATION_PROTOCOL *This,
+  IN   EFI_GUID                         *InformationType,
+  OUT  VOID                             **InformationBlock,
+  OUT  UINTN                            *InformationBlockSize
+  );
+
+/**
+  Sets state information for an adapter.
+
+  This function sends information of type InformationType for an adapter.
+  If an adapter does not support the requested information type, then EFI_UNSUPPORTED
+  is returned.
+
+  @param[in]  This                   A pointer to the EFI_ADAPTER_INFORMATION_PROTOCOL instance.
+  @param[in]  InformationType        A pointer to an EFI_GUID that defines the contents of InformationBlock.
+  @param[in]  InforamtionBlock       A pointer to the InformationBlock structure which contains details
+                                     about the data specific to InformationType.
+  @param[in]  InforamtionBlockSize   The size of the InformationBlock in bytes.
+
+  @retval EFI_SUCCESS                The information was received and interpreted successfully.
+  @retval EFI_UNSUPPORTED            The InformationType is not known.
+  @retval EFI_DEVICE_ERROR           The device reported an error.
+  @retval EFI_INVALID_PARAMETER      This is NULL.
+  @retval EFI_INVALID_PARAMETER      InformationBlock is NULL.
+  @retval EFI_WRITE_PROTECTED        The InformationType cannot be modified using EFI_ADAPTER_INFO_SET_INFO().
+
+**/                        
+EFI_STATUS
+EFIAPI
+UndiAipSetInfo (
+  IN   EFI_ADAPTER_INFORMATION_PROTOCOL *This,
+  IN   EFI_GUID                         *InformationType,
+  IN   VOID                             *InformationBlock,
+  IN   UINTN                            InformationBlockSize
+  );
+
+/**
+  Get a list of supported information types for this instance of the protocol.
+
+  This function returns a list of InformationType GUIDs that are supported on an
+  adapter with this instance of EFI_ADAPTER_INFORMATION_PROTOCOL. The list is returned
+  in InfoTypesBuffer, and the number of GUID pointers in InfoTypesBuffer is returned in
+  InfoTypesBufferCount.
+
+  @param[in]  This                  A pointer to the EFI_ADAPTER_INFORMATION_PROTOCOL instance.
+  @param[out] InfoTypesBuffer       A pointer to the list of InformationType GUID pointers that are supported
+                                    by This.
+  @param[out] InfoTypesBufferCount  A pointer to the number of GUID pointers present in InfoTypesBuffer.
+
+  @retval EFI_SUCCESS               The list of information type GUIDs that are supported on this adapter was
+                                    returned in InfoTypesBuffer. The number of information type GUIDs was
+                                    returned in InfoTypesBufferCount.
+  @retval EFI_INVALID_PARAMETER     This is NULL.
+  @retval EFI_INVALID_PARAMETER     InfoTypesBuffer is NULL.
+  @retval EFI_INVALID_PARAMETER     InfoTypesBufferCount is NULL.
+  @retval EFI_OUT_OF_RESOURCES      There is not enough pool memory to store the results.
+
+**/                        
+EFI_STATUS
+EFIAPI
+UndiAipGetSupportedTypes (
+  IN    EFI_ADAPTER_INFORMATION_PROTOCOL *This,
+  OUT   EFI_GUID                         **InfoTypesBuffer,
+  OUT   UINTN                            *InfoTypesBufferCount
+  );
+
+#endif
diff --git a/Drivers/OptionRomPkg/UndiRuntimeDxe/UndiAipImpl.c b/Drivers/OptionRomPkg/UndiRuntimeDxe/UndiAipImpl.c
new file mode 100644
index 0000000000..21151a076f
--- /dev/null
+++ b/Drivers/OptionRomPkg/UndiRuntimeDxe/UndiAipImpl.c
@@ -0,0 +1,145 @@
+/** @file
+
+Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+
+#include "Undi32.h"
+
+
+UINTN      mSupportedInfoTypesCount = 1;
+EFI_GUID   mSupportedInfoTypes[] = {
+  EFI_ADAPTER_INFO_UNDI_IPV6_SUPPORT_GUID
+};
+
+/**
+  Returns the current state information for the adapter.
+
+  This function returns information of type InformationType from the adapter.
+  If an adapter does not support the requested informational type, then
+  EFI_UNSUPPORTED is returned. 
+
+  @param[in]  This                   A pointer to the EFI_ADAPTER_INFORMATION_PROTOCOL instance.
+  @param[in]  InformationType        A pointer to an EFI_GUID that defines the contents of InformationBlock.
+  @param[out] InforamtionBlock       The service returns a pointer to the buffer with the InformationBlock
+                                     structure which contains details about the data specific to InformationType.
+  @param[out] InforamtionBlockSize   The driver returns the size of the InformationBlock in bytes.
+
+  @retval EFI_SUCCESS                The InformationType information was retrieved.
+  @retval EFI_UNSUPPORTED            The InformationType is not known.
+  @retval EFI_DEVICE_ERROR           The device reported an error.
+  @retval EFI_OUT_OF_RESOURCES       The request could not be completed due to a lack of resources.
+  @retval EFI_INVALID_PARAMETER      This is NULL. 
+  @retval EFI_INVALID_PARAMETER      InformationBlock is NULL. 
+  @retval EFI_INVALID_PARAMETER      InformationBlockSize is NULL.
+
+**/  
+EFI_STATUS
+EFIAPI
+UndiAipGetInfo (
+  IN   EFI_ADAPTER_INFORMATION_PROTOCOL *This,
+  IN   EFI_GUID                         *InformationType,
+  OUT  VOID                             **InformationBlock,
+  OUT  UINTN                            *InformationBlockSize
+  )
+{
+  UNDI32_DEV                            *UNDI32Device;
+  EFI_ADAPTER_INFO_UNDI_IPV6_SUPPORT    *UndiIpv6Support;
+
+  if (This == NULL || InformationBlock == NULL || InformationBlockSize == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (!CompareGuid (InformationType, &gEfiAdapterInfoUndiIpv6SupportGuid)) {
+    return EFI_UNSUPPORTED;
+  }
+
+  UNDI32Device = UNDI_DEV_FROM_AIP (This);
+  *InformationBlockSize = sizeof (EFI_ADAPTER_INFO_UNDI_IPV6_SUPPORT);
+  *InformationBlock = AllocateZeroPool (*InformationBlockSize);
+  if (*InformationBlock == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  UndiIpv6Support = (EFI_ADAPTER_INFO_UNDI_IPV6_SUPPORT *) (*InformationBlock);
+  UndiIpv6Support->Ipv6Support = UNDI32Device->NIIProtocol_31.Ipv6Supported;
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Sets state information for an adapter.
+
+  This function sends information of type InformationType for an adapter.
+  If an adapter does not support the requested information type, then EFI_UNSUPPORTED
+  is returned.
+
+  @param[in]  This                   A pointer to the EFI_ADAPTER_INFORMATION_PROTOCOL instance.
+  @param[in]  InformationType        A pointer to an EFI_GUID that defines the contents of InformationBlock.
+  @param[in]  InforamtionBlock       A pointer to the InformationBlock structure which contains details
+                                     about the data specific to InformationType.
+  @param[in]  InforamtionBlockSize   The size of the InformationBlock in bytes.
+
+  @retval EFI_SUCCESS                The information was received and interpreted successfully.
+  @retval EFI_UNSUPPORTED            The InformationType is not known.
+  @retval EFI_DEVICE_ERROR           The device reported an error.
+  @retval EFI_INVALID_PARAMETER      This is NULL.
+  @retval EFI_INVALID_PARAMETER      InformationBlock is NULL.
+  @retval EFI_WRITE_PROTECTED        The InformationType cannot be modified using EFI_ADAPTER_INFO_SET_INFO().
+
+**/                        
+EFI_STATUS
+EFIAPI
+UndiAipSetInfo (
+  IN   EFI_ADAPTER_INFORMATION_PROTOCOL *This,
+  IN   EFI_GUID                         *InformationType,
+  IN   VOID                             *InformationBlock,
+  IN   UINTN                            InformationBlockSize
+  )
+{
+  return EFI_WRITE_PROTECTED;
+}
+
+/**
+  Get a list of supported information types for this instance of the protocol.
+
+  This function returns a list of InformationType GUIDs that are supported on an
+  adapter with this instance of EFI_ADAPTER_INFORMATION_PROTOCOL. The list is returned
+  in InfoTypesBuffer, and the number of GUID pointers in InfoTypesBuffer is returned in
+  InfoTypesBufferCount.
+
+  @param[in]  This                  A pointer to the EFI_ADAPTER_INFORMATION_PROTOCOL instance.
+  @param[out] InfoTypesBuffer       A pointer to the list of InformationType GUID pointers that are supported
+                                    by This.
+  @param[out] InfoTypesBufferCount  A pointer to the number of GUID pointers present in InfoTypesBuffer.
+
+  @retval EFI_SUCCESS               The list of information type GUIDs that are supported on this adapter was
+                                    returned in InfoTypesBuffer. The number of information type GUIDs was
+                                    returned in InfoTypesBufferCount.
+  @retval EFI_INVALID_PARAMETER     This is NULL.
+  @retval EFI_INVALID_PARAMETER     InfoTypesBuffer is NULL.
+  @retval EFI_INVALID_PARAMETER     InfoTypesBufferCount is NULL.
+  @retval EFI_OUT_OF_RESOURCES      There is not enough pool memory to store the results.
+
+**/                        
+EFI_STATUS
+EFIAPI
+UndiAipGetSupportedTypes (
+  IN    EFI_ADAPTER_INFORMATION_PROTOCOL *This,
+  OUT   EFI_GUID                         **InfoTypesBuffer,
+  OUT   UINTN                            *InfoTypesBufferCount
+  )
+{
+  if (This == NULL || InfoTypesBuffer == NULL || InfoTypesBufferCount == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  *InfoTypesBufferCount = 1;
+  *InfoTypesBuffer = AllocateCopyPool (sizeof (EFI_GUID), &gEfiAdapterInfoUndiIpv6SupportGuid);
+  if (InfoTypesBuffer == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  return EFI_SUCCESS;
+}
diff --git a/Drivers/OptionRomPkg/UndiRuntimeDxe/UndiRuntimeDxe.inf b/Drivers/OptionRomPkg/UndiRuntimeDxe/UndiRuntimeDxe.inf
new file mode 100644
index 0000000000..96666dc88a
--- /dev/null
+++ b/Drivers/OptionRomPkg/UndiRuntimeDxe/UndiRuntimeDxe.inf
@@ -0,0 +1,72 @@
+## @file
+# Component description file for Undi module.
+#
+# This module provides support for Universal Network Driver Interface. 
+# Notes: this module is no longer regular maintained/validated.
+#
+# Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = UndiRuntimeDxe
+  FILE_GUID                      = A1f436EA-A127-4EF8-957C-8048606FF670
+  MODULE_TYPE                    = DXE_RUNTIME_DRIVER
+  VERSION_STRING                 = 1.0
+
+  ENTRY_POINT                    = InitializeUndi
+
+#
+#  VALID_ARCHITECTURES           = IA32 X64 EBC
+#
+
+[Sources]
+  Undi32.h
+  E100b.h
+  E100b.c
+  Decode.c
+  Init.c
+  ComponentName.c
+  UndiAipImpl.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+
+[LibraryClasses]
+  UefiLib
+  UefiBootServicesTableLib
+  BaseMemoryLib
+  DebugLib
+  UefiRuntimeLib
+  UefiDriverEntryPoint
+  BaseLib
+  MemoryAllocationLib
+
+[Protocols]
+  gEfiNetworkInterfaceIdentifierProtocolGuid_31
+  gEfiPciIoProtocolGuid
+  gEfiDevicePathProtocolGuid
+  gEfiAdapterInformationProtocolGuid
+
+[Guids]
+  gEfiEventExitBootServicesGuid        ## PRODUCES ## Event
+  gEfiEventVirtualAddressChangeGuid    ## PRODUCES ## Event
+  gEfiAdapterInfoUndiIpv6SupportGuid   ## PRODUCES
+
+[Depex]
+  gEfiBdsArchProtocolGuid AND
+  gEfiCpuArchProtocolGuid AND
+  gEfiMetronomeArchProtocolGuid AND
+  gEfiMonotonicCounterArchProtocolGuid AND
+  gEfiRealTimeClockArchProtocolGuid AND
+  gEfiResetArchProtocolGuid AND
+  gEfiRuntimeArchProtocolGuid AND
+  gEfiSecurityArchProtocolGuid AND
+  gEfiTimerArchProtocolGuid AND
+  gEfiVariableWriteArchProtocolGuid AND
+  gEfiVariableArchProtocolGuid AND
+  gEfiWatchdogTimerArchProtocolGuid
-- 
2.21.0.windows.1


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.

View/Reply Online (#40402): https://edk2.groups.io/g/devel/message/40402
Mute This Topic: https://groups.io/mt/31573410/1813853
Group Owner: devel+owner at edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub  [edk2-devel-archive at redhat.com]
-=-=-=-=-=-=-=-=-=-=-=-




More information about the edk2-devel-archive mailing list