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

Ni, Ray ray.ni at intel.com
Sat May 11 01:52:05 UTC 2019


Reviewed-by: Ray Ni <ray.ni at intel.com>

> -----Original Message-----
> From: Kinney, Michael D
> Sent: Friday, May 10, 2019 11:35 AM
> To: devel at edk2.groups.io
> Cc: Ni, Ray <ray.ni at intel.com>; Leif Lindholm <leif.lindholm at linaro.org>;
> Ard Biesheuvel <ard.biesheuvel at linaro.org>
> Subject: [edk2-platforms: Patch 7/8] Drivers/OptionRomPkg: Import
> OptionRomPkg from edk2
> 
> 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/ComponentNam
> e.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/ComponentNa
> me.c
> b/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772/ComponentNa
> me.c
> new file mode 100644
> index 0000000000..b6dce7e7cb
> --- /dev/null
> +++
> b/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772/ComponentNa
> me.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/ComponentNa
> me.c
> b/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/ComponentN
> ame.c
> new file mode 100644
> index 0000000000..76a732a7b0
> --- /dev/null
> +++
> b/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/ComponentN
> ame.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/SimpleNetwor
> k.c
> b/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/SimpleNetwor
> k.c
> new file mode 100644
> index 0000000000..76babedb20
> --- /dev/null
> +++
> b/Drivers/OptionRomPkg/Bus/Usb/UsbNetworking/Ax88772b/SimpleNetwor
> k.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/CirrusLogic5430GraphicsOutpu
> t.c
> b/Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430GraphicsOutp
> ut.c
> new file mode 100644
> index 0000000000..b74d84b131
> --- /dev/null
> +++
> b/Drivers/OptionRomPkg/CirrusLogic5430Dxe/CirrusLogic5430GraphicsOutp
> ut.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|BOOLEA
> N|0x00010002
> +
> gOptionRomPkgTokenSpaceGuid.PcdSupportGop|TRUE|BOOLEAN|0x00010
> 004
> +
> gOptionRomPkgTokenSpaceGuid.PcdSupportUga|TRUE|BOOLEAN|0x000100
> 05
> +
> +[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/Base
> DebugPrintErrorLevelLib.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/BaseTimerLibNullTem
> plate.inf
> +
> UefiBootServicesTableLib|MdePkg/Library/UefiBootServicesTableLib/UefiBo
> otServicesTableLib.inf
> +
> UefiRuntimeServicesTableLib|MdePkg/Library/UefiRuntimeServicesTableLib/
> UefiRuntimeServicesTableLib.inf
> +
> UefiDriverEntryPoint|MdePkg/Library/UefiDriverEntryPoint/UefiDriverEntry
> Point.inf
> +  UefiLib|MdePkg/Library/UefiLib/UefiLib.inf
> +  PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf
> +
> MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemo
> ryAllocationLib.inf
> +  DevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf
> +
> UefiApplicationEntryPoint|MdePkg/Library/UefiApplicationEntryPoint/UefiA
> pplicationEntryPoint.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),U
> NDI_Start },\
> +
> {PXE_CPBSIZE_NOT_USED,PXE_DBSIZE_NOT_USED,0,MUST_BE_STARTED,UN
> DI_Stop },\
> +
> {PXE_CPBSIZE_NOT_USED,sizeof(PXE_DB_GET_INIT_INFO),0,MUST_BE_STAR
> TED, UNDI_GetInitInfo },\
> +
> {PXE_CPBSIZE_NOT_USED,sizeof(PXE_DB_GET_CONFIG_INFO),0,MUST_BE_S
> TARTED, UNDI_GetConfigInfo },\
> +
> {sizeof(PXE_CPB_INITIALIZE),(UINT16)(DONT_CHECK),(UINT16)(DONT_CHEC
> K),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 (#40461): https://edk2.groups.io/g/devel/message/40461
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