[edk2-devel] [PATCH v3 1/3] MdeModulePkg/Bus/Usb/UsbNetwork/UsbRndis: Add USB RNDIS devices support

RichardHo [何明忠] via groups.io richardho=ami.com at groups.io
Thu Jun 8 01:58:46 UTC 2023


Hi Hao Wu,

Do you have any update on this patch?

Thanks,
Richard


-----Original Message-----
From: devel at edk2.groups.io <devel at edk2.groups.io> On Behalf Of RichardHo [何明忠] via groups.io
Sent: 2023年5月8日 11:53 AM
To: devel at edk2.groups.io
Cc: Andrew Fish <afish at apple.com>; Leif Lindholm <quic_llindhol at quicinc.com>; Michael D Kinney <michael.d.kinney at intel.com>; Michael Kubacki <mikuback at linux.microsoft.com>; Zhiguang Liu <zhiguang.liu at intel.com>; Liming Gao <gaoliming at byosoft.com.cn>; Hao A Wu <hao.a.wu at intel.com>; Ray Ni <ray.ni at intel.com>; Tinh Nguyen <tinhnguyen at os.amperecomputing.com>; Rebecca Cran <rebecca at bsdio.com>; Tony Lo (羅金松) <TonyLo at ami.com>
Subject: [EXTERNAL] [edk2-devel] [PATCH v3 1/3] MdeModulePkg/Bus/Usb/UsbNetwork/UsbRndis: Add USB RNDIS devices support


**CAUTION: The e-mail below is from an external source. Please exercise caution before opening attachments, clicking links, or following guidance.**

This driver provides UEFI driver for USB RNDIS device

Signed-off-by: Richard Ho <richardho at ami.com>
Cc: Andrew Fish <afish at apple.com>
Cc: Leif Lindholm <quic_llindhol at quicinc.com>
Cc: Michael D Kinney <michael.d.kinney at intel.com>
Cc: Michael Kubacki <mikuback at linux.microsoft.com>
Cc: Zhiguang Liu <zhiguang.liu at intel.com>
Cc: Liming Gao <gaoliming at byosoft.com.cn>
Cc: Hao A Wu <hao.a.wu at intel.com>
Cc: Ray Ni <ray.ni at intel.com>
Tested-by: Tinh Nguyen <tinhnguyen at os.amperecomputing.com>
Reviewed-by: Rebecca Cran <rebecca at bsdio.com>
Reviewed-by: Tony Lo <tonylo at ami.com>
---
 Maintainers.txt                               |    6 +
 .../UsbNetwork/NetworkCommon/ComponentName.c  |  263 +++
 .../UsbNetwork/NetworkCommon/DriverBinding.c  |  595 ++++++
 .../UsbNetwork/NetworkCommon/DriverBinding.h  |  266 +++
 .../NetworkCommon/NetworkCommon.inf           |   48 +
 .../UsbNetwork/NetworkCommon/PxeFunction.c    | 1803 +++++++++++++++++
 .../Usb/UsbNetwork/UsbRndis/ComponentName.c   |  172 ++
 .../Bus/Usb/UsbNetwork/UsbRndis/UsbRndis.c    |  886 ++++++++
 .../Bus/Usb/UsbNetwork/UsbRndis/UsbRndis.h    |  586 ++++++
 .../Bus/Usb/UsbNetwork/UsbRndis/UsbRndis.inf  |   42 +
 .../UsbNetwork/UsbRndis/UsbRndisFunction.c    | 1718 ++++++++++++++++
 .../Include/Protocol/UsbEthernetProtocol.h    |  878 ++++++++
 MdeModulePkg/MdeModulePkg.dec                 |   18 +
 MdeModulePkg/MdeModulePkg.dsc                 |    4 +
 14 files changed, 7285 insertions(+)
 create mode 100644 MdeModulePkg/Bus/Usb/UsbNetwork/NetworkCommon/ComponentName.c
 create mode 100644 MdeModulePkg/Bus/Usb/UsbNetwork/NetworkCommon/DriverBinding.c
 create mode 100644 MdeModulePkg/Bus/Usb/UsbNetwork/NetworkCommon/DriverBinding.h
 create mode 100644 MdeModulePkg/Bus/Usb/UsbNetwork/NetworkCommon/NetworkCommon.inf
 create mode 100644 MdeModulePkg/Bus/Usb/UsbNetwork/NetworkCommon/PxeFunction.c
 create mode 100644 MdeModulePkg/Bus/Usb/UsbNetwork/UsbRndis/ComponentName.c
 create mode 100644 MdeModulePkg/Bus/Usb/UsbNetwork/UsbRndis/UsbRndis.c
 create mode 100644 MdeModulePkg/Bus/Usb/UsbNetwork/UsbRndis/UsbRndis.h
 create mode 100644 MdeModulePkg/Bus/Usb/UsbNetwork/UsbRndis/UsbRndis.inf
 create mode 100644 MdeModulePkg/Bus/Usb/UsbNetwork/UsbRndis/UsbRndisFunction.c
 create mode 100644 MdeModulePkg/Include/Protocol/UsbEthernetProtocol.h

diff --git a/Maintainers.txt b/Maintainers.txt
index 09d04af27a..9a15cdb993 100644
--- a/Maintainers.txt
+++ b/Maintainers.txt
@@ -437,6 +437,12 @@ R: Zhiguang Liu <zhiguang.liu at intel.com> [LiuZhiguang001]
 R: Ray Ni <ray.ni at intel.com> [niruiyu]
 R: Gua Guo <gua.guo at intel.com> [gguo11837463]

+MdeModulePkg: USB Network modules
+F: MdeModulePkg/Bus/Usb/UsbNetwork
+F: MdeModulePkg/Include/Protocol/UsbEthernetProtocol.h
+M: Richard Ho <richardho at ami.com> [richardho]
+R: Rebecca Cran <rebecca at bsdio.com> [bcran]
+
 MdePkg
 F: MdePkg/
 W: https://github.com/tianocore/tianocore.github.io/wiki/MdePkg
diff --git a/MdeModulePkg/Bus/Usb/UsbNetwork/NetworkCommon/ComponentName.c b/MdeModulePkg/Bus/Usb/UsbNetwork/NetworkCommon/ComponentName.c
new file mode 100644
index 0000000000..e83469e130
--- /dev/null
+++ b/MdeModulePkg/Bus/Usb/UsbNetwork/NetworkCommon/ComponentName.c
@@ -0,0 +1,263 @@
+/** @file
+  This file contains code for USB network common driver
+  component name definitions
+
+  Copyright (c) 2023, American Megatrends International LLC. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include "DriverBinding.h"
+
+extern EFI_DRIVER_BINDING_PROTOCOL  gNetworkCommonDriverBinding;
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE  gNetworkCommonDriverNameTable[] = {
+  {
+    "eng;en",
+    L"Network Common Driver"
+  },
+  {
+    NULL,
+    NULL
+  }
+};
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE  *gNetworkCommonControllerNameTable = NULL;
+
+EFI_STATUS
+EFIAPI
+NetworkCommonComponentNameGetDriverName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,
+  IN  CHAR8                        *Language,
+  OUT CHAR16                       **DriverName
+  );
+
+EFI_STATUS
+EFIAPI
+NetworkCommonComponentNameGetControllerName (
+  IN EFI_COMPONENT_NAME_PROTOCOL  *This,
+  IN EFI_HANDLE                   Controller,
+  IN EFI_HANDLE                   ChildHandle        OPTIONAL,
+  IN CHAR8                        *Language,
+  OUT CHAR16                      **ControllerName
+  );
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL  gNetworkCommonComponentName = {
+  NetworkCommonComponentNameGetDriverName,
+  NetworkCommonComponentNameGetControllerName,
+  "eng"
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL  gNetworkCommonComponentName2 = {
+  (EFI_COMPONENT_NAME2_GET_DRIVER_NAME)NetworkCommonComponentNameGetDriverName,
+  (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME)NetworkCommonComponentNameGetControllerName,
+  "en"
+};
+
+/**
+  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]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+                                EFI_COMPONENT_NAME_PROTOCOL instance.
+  @param[in]  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[out] 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
+NetworkCommonComponentNameGetDriverName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,
+  IN  CHAR8                        *Language,
+  OUT CHAR16                       **DriverName
+  )
+{
+  return LookupUnicodeString2 (
+           Language,
+           This->SupportedLanguages,
+           gNetworkCommonDriverNameTable,
+           DriverName,
+           (BOOLEAN)(This == &gNetworkCommonComponentName)
+           );
+}
+
+/**
+  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]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+                                EFI_COMPONENT_NAME_PROTOCOL instance.
+  @param[in]  Controller        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]  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[out] 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
+NetworkCommonComponentNameGetControllerName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,
+  IN  EFI_HANDLE                   Controller,
+  IN  EFI_HANDLE                   ChildHandle        OPTIONAL,
+  IN  CHAR8                        *Language,
+  OUT CHAR16                       **ControllerName
+  )
+{
+  EFI_STATUS                 Status;
+  CHAR16                     *HandleName;
+  EFI_USB_IO_PROTOCOL        *UsbIo;
+  EFI_USB_DEVICE_DESCRIPTOR  DevDesc;
+
+  if (!Language || !ControllerName) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (ChildHandle == NULL) {
+    return EFI_UNSUPPORTED;
+  }
+
+  //
+  // Make sure this driver is currently managing ControllerHandle
+  //
+  Status = EfiTestManagedDevice (
+             Controller,
+             gNetworkCommonDriverBinding.DriverBindingHandle,
+             &gEdkIIUsbEthProtocolGuid
+             );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Make sure this driver produced ChildHandle
+  //
+  Status = EfiTestChildHandle (
+             Controller,
+             ChildHandle,
+             &gEdkIIUsbEthProtocolGuid
+             );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = gBS->HandleProtocol (Controller, &gEfiUsbIoProtocolGuid, (VOID **)&UsbIo);
+
+  if (!EFI_ERROR (Status)) {
+    Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DevDesc);
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+
+    Status = UsbIo->UsbGetStringDescriptor (UsbIo, 0x409, DevDesc.StrManufacturer, &HandleName);
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+
+    *ControllerName = HandleName;
+
+    if (gNetworkCommonControllerNameTable != NULL) {
+      FreeUnicodeStringTable (gNetworkCommonControllerNameTable);
+      gNetworkCommonControllerNameTable = NULL;
+    }
+
+    Status = AddUnicodeString2 (
+               "eng",
+               gNetworkCommonComponentName.SupportedLanguages,
+               &gNetworkCommonControllerNameTable,
+               HandleName,
+               TRUE
+               );
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+
+    Status = AddUnicodeString2 (
+               "en",
+               gNetworkCommonComponentName2.SupportedLanguages,
+               &gNetworkCommonControllerNameTable,
+               HandleName,
+               FALSE
+               );
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+
+    return LookupUnicodeString2 (
+             Language,
+             This->SupportedLanguages,
+             gNetworkCommonControllerNameTable,
+             ControllerName,
+             (BOOLEAN)(This == &gNetworkCommonComponentName)
+             );
+  }
+
+  return EFI_UNSUPPORTED;
+}
diff --git a/MdeModulePkg/Bus/Usb/UsbNetwork/NetworkCommon/DriverBinding.c b/MdeModulePkg/Bus/Usb/UsbNetwork/NetworkCommon/DriverBinding.c
new file mode 100644
index 0000000000..8520cf886c
--- /dev/null
+++ b/MdeModulePkg/Bus/Usb/UsbNetwork/NetworkCommon/DriverBinding.c
@@ -0,0 +1,595 @@
+/** @file
+  This file contains code for USB network binding driver
+
+  Copyright (c) 2023, American Megatrends International LLC. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include "DriverBinding.h"
+
+PXE_SW_UNDI  *gPxe = NULL;
+NIC_DEVICE   *gLanDeviceList[MAX_LAN_INTERFACE];
+UINT32       gRateLimitingCredit;
+UINT32       gRateLimitingPollTimer;
+BOOLEAN      gRateLimitingEnable;
+
+EFI_DRIVER_BINDING_PROTOCOL  gNetworkCommonDriverBinding = {
+  NetworkCommonSupported,
+  NetworkCommonDriverStart,
+  NetworkCommonDriverStop,
+  NETWORK_COMMON_DRIVER_VERSION,
+  NULL,
+  NULL
+};
+
+/**
+  Create MAC Device Path
+
+  @param[in, out] Dev             A pointer to the EFI_DEVICE_PATH_PROTOCOL instance.
+  @param[in]      BaseDev         A pointer to the EFI_DEVICE_PATH_PROTOCOL instance.
+  @param[in]      Nic             A pointer to the Network interface controller data.
+
+  @retval EFI_OUT_OF_RESOURCES    The device path could not be created successfully due to a lack of resources.
+  @retval EFI_SUCCESS             MAC device path created successfully.
+
+**/
+EFI_STATUS
+CreateMacDevicePath (
+  IN OUT  EFI_DEVICE_PATH_PROTOCOL  **Dev,
+  IN      EFI_DEVICE_PATH_PROTOCOL  *BaseDev,
+  IN      NIC_DATA                  *Nic
+  )
+{
+  EFI_STATUS                Status;
+  MAC_ADDR_DEVICE_PATH      MacAddrNode;
+  EFI_DEVICE_PATH_PROTOCOL  *EndNode;
+  UINT8                     *DevicePath;
+  UINT16                    TotalLength;
+  UINT16                    BaseLength;
+
+  ZeroMem (&MacAddrNode, sizeof (MAC_ADDR_DEVICE_PATH));
+  CopyMem (&MacAddrNode.MacAddress, &Nic->MacAddr, 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;
+
+  EndNode = BaseDev;
+
+  while (!IsDevicePathEnd (EndNode)) {
+    EndNode = NextDevicePathNode (EndNode);
+  }
+
+  BaseLength  = (UINT16)((UINTN)(EndNode) - (UINTN)(BaseDev));
+  TotalLength = (UINT16)(BaseLength + sizeof (MacAddrNode) + sizeof (EFI_DEVICE_PATH_PROTOCOL));
+
+  Status = gBS->AllocatePool (EfiBootServicesData, TotalLength, (VOID **)&DevicePath);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  *Dev = (EFI_DEVICE_PATH_PROTOCOL *)DevicePath;
+  CopyMem (DevicePath, (CHAR8 *)BaseDev, BaseLength);
+  DevicePath += BaseLength;
+  CopyMem (DevicePath, (CHAR8 *)&MacAddrNode, sizeof (MacAddrNode));
+  DevicePath += sizeof (MacAddrNode);
+  CopyMem (DevicePath, (CHAR8 *)EndNode, sizeof (EFI_DEVICE_PATH_PROTOCOL));
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Network Common Driver Binding Support.
+
+  @param[in]  This                    Protocol instance pointer.
+  @param[in]  ControllerHandle        Handle of device to test.
+  @param[in]  RemainingDevicePath     Optional parameter use to pick a specific child
+                                      device to start.
+
+  @retval EFI_SUCCESS                 This driver supports this device.
+  @retval EFI_ALREADY_STARTED         This driver is already running on this device.
+  @retval other                       This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+NetworkCommonSupported (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   ControllerHandle,
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
+  )
+{
+  EFI_STATUS                   Status;
+  EDKII_USB_ETHERNET_PROTOCOL  *UsbEth;
+
+  Status = gBS->OpenProtocol (
+                  ControllerHandle,
+                  &gEdkIIUsbEthProtocolGuid,
+                  (VOID **)&UsbEth,
+                  This->DriverBindingHandle,
+                  ControllerHandle,
+                  EFI_OPEN_PROTOCOL_BY_DRIVER
+                  );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  gBS->CloseProtocol (
+         ControllerHandle,
+         &gEdkIIUsbEthProtocolGuid,
+         This->DriverBindingHandle,
+         ControllerHandle
+         );
+  return Status;
+}
+
+/**
+  Network Common Driver Binding Start.
+
+  @param[in]  This                    Protocol instance pointer.
+  @param[in]  ControllerHandle        Handle of device to bind driver to.
+  @param[in]  RemainingDevicePath     Optional parameter use to pick a specific child
+                                      device to start.
+
+  @retval EFI_SUCCESS                 This driver is added to ControllerHandle
+  @retval EFI_DEVICE_ERROR            This driver could not be started due to a device error
+  @retval EFI_OUT_OF_RESOURCES        The driver could not install successfully due to a lack of resources.
+  @retval other                       This driver does not support this device
+
+**/
+EFI_STATUS
+EFIAPI
+NetworkCommonDriverStart (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   ControllerHandle,
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
+  )
+{
+  EFI_STATUS                   Status;
+  EFI_DEVICE_PATH_PROTOCOL     *UsbEthPath;
+  EDKII_USB_ETHERNET_PROTOCOL  *UsbEth;
+  EFI_MAC_ADDRESS              MacAddress;
+  UINTN                        BulkDataSize;
+  NIC_DEVICE                   *NicDevice;
+  UINT8                        *TmpPxePointer;
+
+  Status = gBS->OpenProtocol (
+                  ControllerHandle,
+                  &gEdkIIUsbEthProtocolGuid,
+                  (VOID **)&UsbEth,
+                  This->DriverBindingHandle,
+                  ControllerHandle,
+                  EFI_OPEN_PROTOCOL_BY_DRIVER
+                  );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = gBS->OpenProtocol (
+                  ControllerHandle,
+                  &gEfiDevicePathProtocolGuid,
+                  (VOID **)&UsbEthPath,
+                  This->DriverBindingHandle,
+                  ControllerHandle,
+                  EFI_OPEN_PROTOCOL_BY_DRIVER
+                  );
+
+  if (EFI_ERROR (Status)) {
+    gBS->CloseProtocol (
+           ControllerHandle,
+           &gEdkIIUsbEthProtocolGuid,
+           This->DriverBindingHandle,
+           ControllerHandle
+           );
+    return Status;
+  }
+
+  ZeroMem (&MacAddress, sizeof (EFI_MAC_ADDRESS));
+
+  Status = UsbEth->UsbEthMacAddress (UsbEth, &MacAddress);
+  ASSERT_EFI_ERROR (Status);
+  Status = UsbEth->UsbEthMaxBulkSize (UsbEth, &BulkDataSize);
+
+  if (EFI_ERROR (Status)) {
+    gBS->CloseProtocol (
+           ControllerHandle,
+           &gEfiDevicePathProtocolGuid,
+           This->DriverBindingHandle,
+           ControllerHandle
+           );
+    gBS->CloseProtocol (
+           ControllerHandle,
+           &gEdkIIUsbEthProtocolGuid,
+           This->DriverBindingHandle,
+           ControllerHandle
+           );
+    return Status;
+  }
+
+  NicDevice = AllocateZeroPool (sizeof (NIC_DEVICE) + BulkDataSize + 4096);
+  if (!NicDevice) {
+    gBS->CloseProtocol (
+           ControllerHandle,
+           &gEfiDevicePathProtocolGuid,
+           This->DriverBindingHandle,
+           ControllerHandle
+           );
+    gBS->CloseProtocol (
+           ControllerHandle,
+           &gEdkIIUsbEthProtocolGuid,
+           This->DriverBindingHandle,
+           ControllerHandle
+           );
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  // for alignment adjustment
+  if (gPxe == NULL) {
+    TmpPxePointer = NULL;
+    TmpPxePointer = AllocateZeroPool (sizeof (PXE_SW_UNDI) + 16);
+    if (!TmpPxePointer) {
+      if (NicDevice != NULL) {
+        FreePool (NicDevice);
+      }
+
+      gBS->CloseProtocol (
+             ControllerHandle,
+             &gEfiDevicePathProtocolGuid,
+             This->DriverBindingHandle,
+             ControllerHandle
+             );
+      gBS->CloseProtocol (
+             ControllerHandle,
+             &gEdkIIUsbEthProtocolGuid,
+             This->DriverBindingHandle,
+             ControllerHandle
+             );
+
+      return EFI_OUT_OF_RESOURCES;
+    } else {
+      // check for paragraph alignment here
+      if (((UINTN)TmpPxePointer & 0x0F) != 0) {
+        gPxe = (PXE_SW_UNDI *)(TmpPxePointer + 8);
+      } else {
+        gPxe = (PXE_SW_UNDI *)TmpPxePointer;
+      }
+
+      if (!gPxe) {
+        if (NicDevice != NULL) {
+          FreePool (NicDevice);
+        }
+
+        gBS->CloseProtocol (
+               ControllerHandle,
+               &gEfiDevicePathProtocolGuid,
+               This->DriverBindingHandle,
+               ControllerHandle
+               );
+        gBS->CloseProtocol (
+               ControllerHandle,
+               &gEdkIIUsbEthProtocolGuid,
+               This->DriverBindingHandle,
+               ControllerHandle
+               );
+        return EFI_OUT_OF_RESOURCES;
+      }
+
+      PxeStructInit (gPxe);
+    }
+  }
+
+  NicDevice->NiiProtocol.Id    = (UINT64)(UINTN)(gPxe);
+  NicDevice->NiiProtocol.IfNum = gPxe->IFcnt | gPxe->IFcntExt << 8;
+
+  UpdateNicNum (&NicDevice->NicInfo, gPxe);
+
+  NicDevice->NicInfo.Signature = NIC_DATA_SIGNATURE;
+
+  NicDevice->NicInfo.UsbEth         = UsbEth;
+  NicDevice->NicInfo.MaxSegmentSize = (UINT16)BulkDataSize;
+  NicDevice->NicInfo.CableDetect    = 0;
+  NicDevice->ReceiveBuffer          = ALIGN_POINTER ((VOID *)NicDevice, 4096);
+
+  CopyMem ((CHAR8 *)&(NicDevice->NicInfo.MacAddr), (CHAR8 *)&MacAddress, sizeof (MacAddress));
+
+  NicDevice->NicInfo.TxBufferCount = 0;
+
+  if (NicDevice->NiiProtocol.IfNum < MAX_LAN_INTERFACE) {
+    gLanDeviceList[NicDevice->NiiProtocol.IfNum] = NicDevice;
+  } else {
+    gBS->CloseProtocol (
+           ControllerHandle,
+           &gEfiDevicePathProtocolGuid,
+           This->DriverBindingHandle,
+           ControllerHandle
+           );
+    gBS->CloseProtocol (
+           ControllerHandle,
+           &gEdkIIUsbEthProtocolGuid,
+           This->DriverBindingHandle,
+           ControllerHandle
+           );
+
+    if (TmpPxePointer != NULL) {
+      FreePool (TmpPxePointer);
+    }
+
+    if (NicDevice != NULL) {
+      FreePool (NicDevice);
+    }
+
+    return EFI_DEVICE_ERROR;
+  }
+
+  Status = CreateMacDevicePath (
+             &NicDevice->DevPath,
+             UsbEthPath,
+             &NicDevice->NicInfo
+             );
+
+  if (EFI_ERROR (Status)) {
+    UpdateNicNum (NULL, gPxe);
+    if (TmpPxePointer != NULL) {
+      FreePool (TmpPxePointer);
+    }
+  }
+
+  NicDevice->Signature                 = UNDI_DEV_SIGNATURE;
+  NicDevice->NiiProtocol.Revision      = EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL_REVISION;
+  NicDevice->NiiProtocol.Type          = EfiNetworkInterfaceUndi;
+  NicDevice->NiiProtocol.MajorVer      = PXE_ROMID_MAJORVER;
+  NicDevice->NiiProtocol.MinorVer      = PXE_ROMID_MINORVER;
+  NicDevice->NiiProtocol.ImageSize     = 0;
+  NicDevice->NiiProtocol.ImageAddr     = 0;
+  NicDevice->NiiProtocol.Ipv6Supported = TRUE;
+
+  NicDevice->NiiProtocol.StringId[0] = 'U';
+  NicDevice->NiiProtocol.StringId[1] = 'N';
+  NicDevice->NiiProtocol.StringId[2] = 'D';
+  NicDevice->NiiProtocol.StringId[3] = 'I';
+  NicDevice->DeviceHandle            = NULL;
+
+  NicDevice->NicInfo.RateLimitingEnable      = gRateLimitingEnable;
+  NicDevice->NicInfo.RateLimitingCreditCount = 0;
+  NicDevice->NicInfo.RateLimitingCredit      = gRateLimitingCredit;
+  NicDevice->NicInfo.RateLimitingPollTimer   = gRateLimitingPollTimer;
+  NicDevice->NicInfo.RateLimiter             = NULL;
+
+  ZeroMem (&NicDevice->NicInfo.Request, sizeof (EFI_USB_DEVICE_REQUEST));
+
+  Status = UsbEth->UsbEthInterrupt (UsbEth, TRUE, NETWORK_COMMON_POLLING_INTERVAL, &NicDevice->NicInfo.Request);
+  ASSERT_EFI_ERROR (Status);
+
+  Status = gBS->InstallMultipleProtocolInterfaces (
+                  &NicDevice->DeviceHandle,
+                  &gEfiNetworkInterfaceIdentifierProtocolGuid_31,
+                  &NicDevice->NiiProtocol,
+                  &gEfiDevicePathProtocolGuid,
+                  NicDevice->DevPath,
+                  NULL
+                  );
+
+  if (EFI_ERROR (Status)) {
+    if (NicDevice->NiiProtocol.IfNum < MAX_LAN_INTERFACE) {
+      gLanDeviceList[NicDevice->NiiProtocol.IfNum] = NULL;
+    }
+
+    gBS->CloseProtocol (
+           ControllerHandle,
+           &gEfiDevicePathProtocolGuid,
+           This->DriverBindingHandle,
+           ControllerHandle
+           );
+    gBS->CloseProtocol (
+           ControllerHandle,
+           &gEdkIIUsbEthProtocolGuid,
+           This->DriverBindingHandle,
+           ControllerHandle
+           );
+
+    if (TmpPxePointer != NULL) {
+      FreePool (TmpPxePointer);
+    }
+
+    if (NicDevice->DevPath != NULL) {
+      FreePool (NicDevice->DevPath);
+    }
+
+    if (NicDevice != NULL) {
+      FreePool (NicDevice);
+    }
+
+    return EFI_DEVICE_ERROR;
+  }
+
+  Status = gBS->OpenProtocol (
+                  ControllerHandle,
+                  &gEdkIIUsbEthProtocolGuid,
+                  (VOID **)&UsbEth,
+                  This->DriverBindingHandle,
+                  NicDevice->DeviceHandle,
+                  EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+                  );
+
+  return Status;
+}
+
+/**
+  Network Common Driver Binding Stop.
+
+  @param[in]  This                  Protocol instance pointer.
+  @param[in]  ControllerHandle      Handle of device to stop driver on
+  @param[in]  NumberOfChildren      Number of Handles in ChildHandleBuffer. If number of
+                                    children is zero stop the entire bus driver.
+  @param[in]  ChildHandleBuffer     List of Child Handles to Stop.
+
+  @retval EFI_SUCCESS               This driver is removed ControllerHandle
+  @retval other                     This driver was not removed from this device
+
+**/
+EFI_STATUS
+EFIAPI
+NetworkCommonDriverStop (
+  IN  EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN  EFI_HANDLE                   ControllerHandle,
+  IN  UINTN                        NumberOfChildren,
+  IN  EFI_HANDLE                   *ChildHandleBuffer
+  )
+{
+  EFI_STATUS                                 Status;
+  BOOLEAN                                    AllChildrenStopped;
+  UINTN                                      Index;
+  EDKII_USB_ETHERNET_PROTOCOL                *UsbEth;
+  NIC_DEVICE                                 *NicDevice;
+  EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL  *NiiProtocol;
+
+  if (NumberOfChildren == 0) {
+    Status = gBS->OpenProtocol (
+                    ControllerHandle,
+                    &gEfiNetworkInterfaceIdentifierProtocolGuid_31,
+                    (VOID **)&NiiProtocol,
+                    This->DriverBindingHandle,
+                    ControllerHandle,
+                    EFI_OPEN_PROTOCOL_GET_PROTOCOL
+                    );
+
+    if (EFI_ERROR (Status)) {
+      gBS->CloseProtocol (
+             ControllerHandle,
+             &gEfiDevicePathProtocolGuid,
+             This->DriverBindingHandle,
+             ControllerHandle
+             );
+      gBS->CloseProtocol (
+             ControllerHandle,
+             &gEdkIIUsbEthProtocolGuid,
+             This->DriverBindingHandle,
+             ControllerHandle
+             );
+      return EFI_SUCCESS;
+    }
+
+    NicDevice = UNDI_DEV_FROM_THIS (NiiProtocol);
+    Status    = gBS->UninstallMultipleProtocolInterfaces (
+                       ControllerHandle,
+                       &gEfiNetworkInterfaceIdentifierProtocolGuid_31,
+                       &NicDevice->NiiProtocol,
+                       &gEfiDevicePathProtocolGuid,
+                       NicDevice->DevPath,
+                       NULL
+                       );
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+
+    FreePool (NicDevice->DevPath);
+    FreePool (NicDevice);
+
+    gBS->CloseProtocol (
+           ControllerHandle,
+           &gEfiDevicePathProtocolGuid,
+           This->DriverBindingHandle,
+           ControllerHandle
+           );
+    gBS->CloseProtocol (
+           ControllerHandle,
+           &gEdkIIUsbEthProtocolGuid,
+           This->DriverBindingHandle,
+           ControllerHandle
+           );
+    return EFI_SUCCESS;
+  }
+
+  AllChildrenStopped = TRUE;
+
+  for (Index = 0; Index < NumberOfChildren; Index++) {
+    Status = gBS->OpenProtocol (
+                    ChildHandleBuffer[Index],
+                    &gEfiNetworkInterfaceIdentifierProtocolGuid_31,
+                    (VOID **)&NiiProtocol,
+                    This->DriverBindingHandle,
+                    ControllerHandle,
+                    EFI_OPEN_PROTOCOL_GET_PROTOCOL
+                    );
+    if (EFI_ERROR (Status)) {
+      AllChildrenStopped = FALSE;
+      continue;
+    }
+
+    NicDevice = UNDI_DEV_FROM_THIS (NiiProtocol);
+
+    gBS->CloseProtocol (
+           ControllerHandle,
+           &gEdkIIUsbEthProtocolGuid,
+           This->DriverBindingHandle,
+           ChildHandleBuffer[Index]
+           );
+
+    Status = gBS->UninstallMultipleProtocolInterfaces (
+                    ChildHandleBuffer[Index],
+                    &gEfiNetworkInterfaceIdentifierProtocolGuid_31,
+                    &NicDevice->NiiProtocol,
+                    &gEfiDevicePathProtocolGuid,
+                    NicDevice->DevPath,
+                    NULL
+                    );
+    if (EFI_ERROR (Status)) {
+      Status = gBS->OpenProtocol (
+                      ControllerHandle,
+                      &gEdkIIUsbEthProtocolGuid,
+                      (VOID **)&UsbEth,
+                      This->DriverBindingHandle,
+                      ChildHandleBuffer[Index],
+                      EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+                      );
+    } else {
+      FreePool (NicDevice->DevPath);
+      FreePool (NicDevice);
+    }
+  }
+
+  if (!AllChildrenStopped) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  return Status;
+}
+
+/**
+  Entrypoint of Network Common Driver.
+
+  This function is the entrypoint of Network Common Driver. It installs Driver Binding
+  Protocols together with Component Name Protocols.
+
+  @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.
+
+**/
+EFI_STATUS
+EFIAPI
+NetworkCommonEntry (
+  IN EFI_HANDLE        ImageHandle,
+  IN EFI_SYSTEM_TABLE  *SystemTable
+  )
+{
+  EFI_STATUS  Status;
+
+  gNetworkCommonDriverBinding.DriverBindingHandle = ImageHandle;
+  gNetworkCommonDriverBinding.ImageHandle         = ImageHandle;
+  gRateLimitingEnable                             = PcdGetBool (PcdEnableUsbNetworkRateLimiting);
+  gRateLimitingCredit                             = PcdGet32 (PcdUsbNetworkRateLimitingCredit);
+  gRateLimitingPollTimer                          = PcdGet32 (PcdUsbNetworkRateLimitingFactor);
+
+  Status = gBS->InstallMultipleProtocolInterfaces (
+                  &gNetworkCommonDriverBinding.DriverBindingHandle,
+                  &gEfiDriverBindingProtocolGuid,
+                  &gNetworkCommonDriverBinding,
+                  &gEfiComponentName2ProtocolGuid,
+                  &gNetworkCommonComponentName2,
+                  NULL
+                  );
+  return Status;
+}
diff --git a/MdeModulePkg/Bus/Usb/UsbNetwork/NetworkCommon/DriverBinding.h b/MdeModulePkg/Bus/Usb/UsbNetwork/NetworkCommon/DriverBinding.h
new file mode 100644
index 0000000000..a805834a78
--- /dev/null
+++ b/MdeModulePkg/Bus/Usb/UsbNetwork/NetworkCommon/DriverBinding.h
@@ -0,0 +1,266 @@
+/** @file
+  Header file for for USB network common driver
+
+  Copyright (c) 2023, American Megatrends International LLC. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef _DRIVER_BINDING_H_
+#define _DRIVER_BINDING_H_
+
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/PcdLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/UefiUsbLib.h>
+#include <Protocol/UsbIo.h>
+#include <Protocol/NetworkInterfaceIdentifier.h>
+#include <Protocol/UsbEthernetProtocol.h>
+
+#define NETWORK_COMMON_DRIVER_VERSION    1
+#define NETWORK_COMMON_POLLING_INTERVAL  0x10
+#define RX_BUFFER_COUNT                  32
+#define TX_BUFFER_COUNT                  32
+#define MEMORY_REQUIRE                   0
+
+#define UNDI_DEV_SIGNATURE  SIGNATURE_32('u','n','d','i')
+#define UNDI_DEV_FROM_THIS(a)  CR(a, NIC_DEVICE, NiiProtocol, UNDI_DEV_SIGNATURE)
+#define UNDI_DEV_FROM_NIC(a)   CR(a, NIC_DEVICE, NicInfo, UNDI_DEV_SIGNATURE)
+
+#pragma pack(1)
+typedef struct {
+  UINT8     DestAddr[PXE_HWADDR_LEN_ETHER];
+  UINT8     SrcAddr[PXE_HWADDR_LEN_ETHER];
+  UINT16    Protocol;
+} EthernetHeader;
+#pragma pack()
+
+typedef struct {
+  UINTN                                        Signature;
+  EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL    NiiProtocol;
+  EFI_HANDLE                                   DeviceHandle;
+  EFI_DEVICE_PATH_PROTOCOL                     *BaseDevPath;
+  EFI_DEVICE_PATH_PROTOCOL                     *DevPath;
+  NIC_DATA                                     NicInfo;
+  VOID                                         *ReceiveBuffer;
+} NIC_DEVICE;
+
+typedef VOID (*API_FUNC)(
+  PXE_CDB *,
+  NIC_DATA *
+  );
+
+extern PXE_SW_UNDI                   *gPxe;
+extern NIC_DEVICE                    *gLanDeviceList[MAX_LAN_INTERFACE];
+extern EFI_COMPONENT_NAME2_PROTOCOL  gNetworkCommonComponentName2;
+
+EFI_STATUS
+EFIAPI
+NetworkCommonSupported (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   ControllerHandle,
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
+  );
+
+EFI_STATUS
+EFIAPI
+NetworkCommonDriverStart (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   ControllerHandle,
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
+  );
+
+EFI_STATUS
+EFIAPI
+NetworkCommonDriverStop (
+  IN  EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN  EFI_HANDLE                   ControllerHandle,
+  IN  UINTN                        NumberOfChildren,
+  IN  EFI_HANDLE                   *ChildHandleBuffer
+  );
+
+VOID
+PxeStructInit (
+  OUT PXE_SW_UNDI  *PxeSw
+  );
+
+VOID
+UpdateNicNum (
+  IN      NIC_DATA     *Nic,
+  IN OUT  PXE_SW_UNDI  *PxeSw
+  );
+
+EFI_STATUS
+EFIAPI
+UndiApiEntry (
+  IN  UINT64  Cdb
+  );
+
+UINTN
+MapIt (
+  IN NIC_DATA  *Nic,
+  IN UINT64    MemAddr,
+  IN UINT32    Size,
+  IN UINT32    Direction,
+  OUT UINT64   MappedAddr
+  );
+
+VOID
+UnMapIt (
+  IN NIC_DATA  *Nic,
+  IN UINT64    MemAddr,
+  IN UINT32    Size,
+  IN UINT32    Direction,
+  IN UINT64    MappedAddr
+  );
+
+VOID
+UndiGetState (
+  IN  PXE_CDB   *Cdb,
+  IN  NIC_DATA  *Nic
+  );
+
+VOID
+UndiStart (
+  IN  PXE_CDB   *Cdb,
+  IN  NIC_DATA  *Nic
+  );
+
+VOID
+UndiStop (
+  IN  PXE_CDB   *Cdb,
+  IN  NIC_DATA  *Nic
+  );
+
+VOID
+UndiGetInitInfo (
+  IN  PXE_CDB   *Cdb,
+  IN  NIC_DATA  *Nic
+  );
+
+VOID
+UndiGetConfigInfo (
+  IN  PXE_CDB   *Cdb,
+  IN  NIC_DATA  *Nic
+  );
+
+VOID
+UndiInitialize (
+  IN      PXE_CDB   *Cdb,
+  IN OUT  NIC_DATA  *Nic
+  );
+
+VOID
+UndiReset (
+  IN  PXE_CDB   *Cdb,
+  IN  NIC_DATA  *Nic
+  );
+
+VOID
+UndiShutdown (
+  IN      PXE_CDB   *Cdb,
+  IN OUT  NIC_DATA  *Nic
+  );
+
+VOID
+UndiInterruptEnable (
+  IN  PXE_CDB   *Cdb,
+  IN  NIC_DATA  *Nic
+  );
+
+VOID
+UndiReceiveFilter (
+  IN  PXE_CDB   *Cdb,
+  IN  NIC_DATA  *Nic
+  );
+
+VOID
+UndiStationAddress (
+  IN  PXE_CDB   *Cdb,
+  IN  NIC_DATA  *Nic
+  );
+
+VOID
+UndiStatistics (
+  IN  PXE_CDB   *Cdb,
+  IN  NIC_DATA  *Nic
+  );
+
+VOID
+UndiMcastIp2Mac (
+  IN OUT  PXE_CDB   *Cdb,
+  IN      NIC_DATA  *Nic
+  );
+
+VOID
+UndiNvData (
+  IN  PXE_CDB   *Cdb,
+  IN  NIC_DATA  *Nic
+  );
+
+VOID
+UndiGetStatus (
+  IN  PXE_CDB   *Cdb,
+  IN  NIC_DATA  *Nic
+  );
+
+VOID
+UndiFillHeader (
+  IN  PXE_CDB   *Cdb,
+  IN  NIC_DATA  *Nic
+  );
+
+VOID
+UndiTransmit (
+  IN  PXE_CDB   *Cdb,
+  IN  NIC_DATA  *Nic
+  );
+
+VOID
+UndiReceive (
+  IN  PXE_CDB   *Cdb,
+  IN  NIC_DATA  *Nic
+  );
+
+UINT16
+Initialize (
+  IN      PXE_CDB   *Cdb,
+  IN OUT  NIC_DATA  *Nic
+  );
+
+UINT16
+Transmit (
+  IN      PXE_CDB   *Cdb,
+  IN OUT  NIC_DATA  *Nic,
+  IN      UINT64    CpbAddr,
+  IN      UINT16    OpFlags
+  );
+
+UINT16
+Receive (
+  IN PXE_CDB       *Cdb,
+  IN OUT NIC_DATA  *Nic,
+  IN UINT64        CpbAddr,
+  IN OUT UINT64    DbAddr
+  );
+
+UINT16
+SetFilter (
+  IN  NIC_DATA  *Nic,
+  IN  UINT16    SetFilter,
+  IN  UINT64    CpbAddr,
+  IN  UINT32    CpbSize
+  );
+
+UINT16
+Statistics (
+  IN NIC_DATA  *Nic,
+  IN UINT64    DbAddr,
+  IN UINT16    DbSize
+  );
+
+#endif
diff --git a/MdeModulePkg/Bus/Usb/UsbNetwork/NetworkCommon/NetworkCommon.inf b/MdeModulePkg/Bus/Usb/UsbNetwork/NetworkCommon/NetworkCommon.inf
new file mode 100644
index 0000000000..f9ca31fe80
--- /dev/null
+++ b/MdeModulePkg/Bus/Usb/UsbNetwork/NetworkCommon/NetworkCommon.inf
@@ -0,0 +1,48 @@
+## @file
+#   This is Usb Network Common driver for DXE phase.
+#
+# Copyright (c) 2023, American Megatrends International LLC. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = NetworkCommon
+  FILE_GUID                      = ca6eb4f4-f1d6-4375-97d6-18856871e1bf
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = NetworkCommonEntry
+
+[Sources]
+  DriverBinding.c
+  DriverBinding.h
+  ComponentName.c
+  PxeFunction.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+
+[LibraryClasses]
+  UefiDriverEntryPoint
+  UefiBootServicesTableLib
+  UefiLib
+  DebugLib
+  UefiUsbLib
+  MemoryAllocationLib
+  BaseMemoryLib
+
+[Protocols]
+  gEfiNetworkInterfaceIdentifierProtocolGuid_31
+  gEfiUsbIoProtocolGuid
+  gEfiDevicePathProtocolGuid
+  gEfiDriverBindingProtocolGuid
+  gEdkIIUsbEthProtocolGuid
+
+[Pcd]
+  gEfiMdeModulePkgTokenSpaceGuid.PcdEnableUsbNetworkRateLimiting
+  gEfiMdeModulePkgTokenSpaceGuid.PcdUsbNetworkRateLimitingCredit
+  gEfiMdeModulePkgTokenSpaceGuid.PcdUsbNetworkRateLimitingFactor
+
+[Depex]
+  TRUE
diff --git a/MdeModulePkg/Bus/Usb/UsbNetwork/NetworkCommon/PxeFunction.c b/MdeModulePkg/Bus/Usb/UsbNetwork/NetworkCommon/PxeFunction.c
new file mode 100644
index 0000000000..687cabca4c
--- /dev/null
+++ b/MdeModulePkg/Bus/Usb/UsbNetwork/NetworkCommon/PxeFunction.c
@@ -0,0 +1,1803 @@
+/** @file
+  This file contains code for UNDI command based on UEFI specification.
+
+  Copyright (c) 2023, American Megatrends International LLC. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include "DriverBinding.h"
+
+// API table, defined in UEFI specification
+API_FUNC  gUndiApiTable[] = {
+  UndiGetState,
+  UndiStart,
+  UndiStop,
+  UndiGetInitInfo,
+  UndiGetConfigInfo,
+  UndiInitialize,
+  UndiReset,
+  UndiShutdown,
+  UndiInterruptEnable,
+  UndiReceiveFilter,
+  UndiStationAddress,
+  UndiStatistics,
+  UndiMcastIp2Mac,
+  UndiNvData,
+  UndiGetStatus,
+  UndiFillHeader,
+  UndiTransmit,
+  UndiReceive
+};
+
+/**
+   Callback function for enable Rate Limiter
+
+   @param[in] Event           Event whose notification function is being invoked
+   @param[in] Context         Pointer to the notification function's context
+
+**/
+VOID
+EFIAPI
+UndiRateLimiterCallback (
+  IN  EFI_EVENT  Event,
+  IN  VOID       *Context
+  )
+{
+  NIC_DATA  *Nic = Context;
+
+  if (Nic->RateLimitingCreditCount < Nic->RateLimitingCredit) {
+    Nic->RateLimitingCreditCount++;
+  }
+}
+
+/**
+  This command is used to determine the operational state of the UNDI.
+
+  @param[in]  Cdb  A pointer to the command descriptor block.
+  @param[in]  Nic  A pointer to the Network interface controller data.
+
+**/
+VOID
+UndiGetState (
+  IN  PXE_CDB   *Cdb,
+  IN  NIC_DATA  *Nic
+  )
+{
+  EFI_STATUS  Status;
+
+  if ((Cdb->OpCode != PXE_OPCODE_GET_STATE) ||
+      (Cdb->StatCode != PXE_STATCODE_INITIALIZE) ||
+      (Cdb->StatFlags != PXE_STATFLAGS_INITIALIZE) ||
+      (Cdb->IFnum >= (gPxe->IFcnt | gPxe->IFcntExt << 8)) ||
+      (Cdb->CPBsize != PXE_CPBSIZE_NOT_USED) ||
+      (Cdb->CPBaddr != PXE_CPBADDR_NOT_USED) ||
+      (Cdb->DBsize != PXE_DBSIZE_NOT_USED) ||
+      (Cdb->DBaddr != PXE_DBADDR_NOT_USED) ||
+      (Cdb->OpFlags != PXE_OPFLAGS_NOT_USED))
+  {
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    Cdb->StatCode  = PXE_STATCODE_INVALID_CDB;
+    return;
+  } else {
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE;
+    Cdb->StatCode  = PXE_STATCODE_SUCCESS;
+  }
+
+  Cdb->StatFlags = Cdb->StatFlags | Nic->State;
+
+  if (Nic->UsbEth->UsbEthUndi.UsbEthUndiGetState != NULL) {
+    Status = Nic->UsbEth->UsbEthUndi.UsbEthUndiGetState (Cdb, Nic);
+    if (EFI_ERROR (Status)) {
+      Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    }
+  }
+}
+
+/**
+  This command is used to change the UNDI operational state from stopped to started.
+
+  @param[in]  Cdb  A pointer to the command descriptor block.
+  @param[in]  Nic  A pointer to the Network interface controller data.
+
+**/
+VOID
+UndiStart (
+  IN  PXE_CDB   *Cdb,
+  IN  NIC_DATA  *Nic
+  )
+{
+  PXE_CPB_START_31  *Cpb;
+  EFI_STATUS        Status;
+  BOOLEAN           EventError;
+
+  if ((Cdb->OpCode != PXE_OPCODE_START) ||
+      (Cdb->StatCode != PXE_STATCODE_INITIALIZE) ||
+      (Cdb->StatFlags != PXE_STATFLAGS_INITIALIZE) ||
+      (Cdb->IFnum >= (gPxe->IFcnt | gPxe->IFcntExt << 8)) ||
+      (Cdb->CPBsize != sizeof (PXE_CPB_START_31)) ||
+      (Cdb->DBsize != PXE_DBSIZE_NOT_USED) ||
+      (Cdb->DBaddr != PXE_DBADDR_NOT_USED) ||
+      (Cdb->OpFlags != PXE_OPFLAGS_NOT_USED))
+  {
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    Cdb->StatCode  = PXE_STATCODE_INVALID_CDB;
+    return;
+  }
+
+  if (Nic->State != PXE_STATFLAGS_GET_STATE_STOPPED) {
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    Cdb->StatCode  = PXE_STATCODE_ALREADY_STARTED;
+    return;
+  }
+
+  Cpb = (PXE_CPB_START_31 *)(UINTN)Cdb->CPBaddr;
+
+  Nic->PxeStart.Delay     = Cpb->Delay;
+  Nic->PxeStart.Virt2Phys = Cpb->Virt2Phys;
+  Nic->PxeStart.Block     = Cpb->Block;
+  Nic->PxeStart.Map_Mem   = 0;
+  Nic->PxeStart.UnMap_Mem = 0;
+  Nic->PxeStart.Sync_Mem  = Cpb->Sync_Mem;
+  Nic->PxeStart.Unique_ID = Cpb->Unique_ID;
+  EventError              = FALSE;
+  Status                  = EFI_SUCCESS;
+  if (Nic->RateLimitingEnable == TRUE) {
+    Status = gBS->CreateEvent (
+                    EVT_TIMER | EVT_NOTIFY_SIGNAL,
+                    TPL_NOTIFY,
+                    UndiRateLimiterCallback,
+                    Nic,
+                    &Nic->RateLimiter
+                    );
+    if (!EFI_ERROR (Status)) {
+      Status = gBS->SetTimer (
+                      Nic->RateLimiter,
+                      TimerPeriodic,
+                      Nic->RateLimitingPollTimer * 10000
+                      );
+      if (EFI_ERROR (Status)) {
+        EventError = TRUE;
+      }
+    }
+  }
+
+  if ((Nic->UsbEth->UsbEthUndi.UsbEthUndiStart != NULL) && (EventError == FALSE)) {
+    Status = Nic->UsbEth->UsbEthUndi.UsbEthUndiStart (Cdb, Nic);
+  }
+
+  if (!EFI_ERROR (Status)) {
+    // Initial the state for UNDI start.
+    Nic->State     = PXE_STATFLAGS_GET_STATE_STARTED;
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE;
+    Cdb->StatCode  = PXE_STATCODE_SUCCESS;
+  } else {
+    if (Nic->RateLimitingEnable == TRUE) {
+      if (!EventError) {
+        gBS->SetTimer (&Nic->RateLimiter, TimerCancel, 0);
+      }
+
+      if (Nic->RateLimiter) {
+        gBS->CloseEvent (&Nic->RateLimiter);
+        Nic->RateLimiter = 0;
+      }
+    }
+
+    // Initial the state when UNDI start is fail
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    Cdb->StatCode  = PXE_STATCODE_DEVICE_FAILURE;
+  }
+}
+
+/**
+  This command is used to change the UNDI operational state from started to stopped.
+
+  @param[in]  Cdb  A pointer to the command descriptor block.
+  @param[in]  Nic  A pointer to the Network interface controller data.
+
+**/
+VOID
+UndiStop (
+  IN  PXE_CDB   *Cdb,
+  IN  NIC_DATA  *Nic
+  )
+{
+  EFI_STATUS  Status;
+
+  if ((Cdb->OpCode != PXE_OPCODE_STOP) ||
+      (Cdb->StatCode != PXE_STATCODE_INITIALIZE) ||
+      (Cdb->StatFlags != PXE_STATFLAGS_INITIALIZE) ||
+      (Cdb->IFnum >= (gPxe->IFcnt | gPxe->IFcntExt << 8)) ||
+      (Cdb->CPBsize != PXE_CPBSIZE_NOT_USED) ||
+      (Cdb->CPBaddr != PXE_CPBADDR_NOT_USED) ||
+      (Cdb->DBsize != PXE_DBSIZE_NOT_USED) ||
+      (Cdb->DBaddr != PXE_DBADDR_NOT_USED) ||
+      (Cdb->OpFlags != PXE_OPFLAGS_NOT_USED))
+  {
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    Cdb->StatCode  = PXE_STATCODE_INVALID_CDB;
+    return;
+  } else {
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE;
+    Cdb->StatCode  = PXE_STATCODE_SUCCESS;
+  }
+
+  if (Nic->State == PXE_STATFLAGS_GET_STATE_STOPPED) {
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    Cdb->StatCode  = PXE_STATCODE_NOT_STARTED;
+    return;
+  }
+
+  if (Nic->State == PXE_STATFLAGS_GET_STATE_INITIALIZED) {
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    Cdb->StatCode  = PXE_STATCODE_NOT_SHUTDOWN;
+    return;
+  }
+
+  Nic->PxeStart.Delay     = 0;
+  Nic->PxeStart.Virt2Phys = 0;
+  Nic->PxeStart.Block     = 0;
+  Nic->PxeStart.Map_Mem   = 0;
+  Nic->PxeStart.UnMap_Mem = 0;
+  Nic->PxeStart.Sync_Mem  = 0;
+  Nic->State              = PXE_STATFLAGS_GET_STATE_STOPPED;
+
+  if (Nic->RateLimitingEnable == TRUE) {
+    gBS->SetTimer (&Nic->RateLimiter, TimerCancel, 0);
+    gBS->CloseEvent (&Nic->RateLimiter);
+  }
+
+  if (Nic->UsbEth->UsbEthUndi.UsbEthUndiStop != NULL) {
+    Status = Nic->UsbEth->UsbEthUndi.UsbEthUndiStop (Cdb, Nic);
+    if (EFI_ERROR (Status)) {
+      Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    }
+  }
+}
+
+/**
+  This command is used to retrieve initialization information that is
+  needed by drivers and applications to initialized UNDI.
+
+  @param[in]  Cdb  A pointer to the command descriptor block.
+  @param[in]  Nic  A pointer to the Network interface controller data.
+
+**/
+VOID
+UndiGetInitInfo (
+  IN  PXE_CDB   *Cdb,
+  IN  NIC_DATA  *Nic
+  )
+{
+  PXE_DB_GET_INIT_INFO  *Db;
+  EFI_STATUS            Status;
+
+  if ((Cdb->OpCode != PXE_OPCODE_GET_INIT_INFO) ||
+      (Cdb->StatCode != PXE_STATCODE_INITIALIZE) ||
+      (Cdb->StatFlags != PXE_STATFLAGS_INITIALIZE) ||
+      (Cdb->IFnum >= (gPxe->IFcnt | gPxe->IFcntExt << 8)) ||
+      (Cdb->CPBsize != PXE_CPBSIZE_NOT_USED) ||
+      (Cdb->CPBaddr != PXE_CPBADDR_NOT_USED) ||
+      (Cdb->DBsize != sizeof (PXE_DB_GET_INIT_INFO)) ||
+      (Cdb->OpFlags != PXE_OPFLAGS_NOT_USED))
+  {
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    Cdb->StatCode  = PXE_STATCODE_INVALID_CDB;
+    return;
+  } else {
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE;
+    Cdb->StatCode  = PXE_STATCODE_SUCCESS;
+  }
+
+  if (Nic->State == PXE_STATFLAGS_GET_STATE_STOPPED) {
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    Cdb->StatCode  = PXE_STATCODE_NOT_STARTED;
+    return;
+  }
+
+  Db = (PXE_DB_GET_INIT_INFO *)(UINTN)Cdb->DBaddr;
+
+  Db->MemoryRequired         = MEMORY_REQUIRE;
+  Db->FrameDataLen           = PXE_MAX_TXRX_UNIT_ETHER;
+  Db->LinkSpeeds[0]          = 10;
+  Db->LinkSpeeds[1]          = 100;
+  Db->LinkSpeeds[2]          = 1000;
+  Db->LinkSpeeds[3]          = 0;
+  Db->MediaHeaderLen         = PXE_MAC_HEADER_LEN_ETHER;
+  Db->HWaddrLen              = PXE_HWADDR_LEN_ETHER;
+  Db->MCastFilterCnt         = MAX_MCAST_ADDRESS_CNT;
+  Db->TxBufCnt               = Nic->PxeInit.TxBufCnt;
+  Db->TxBufSize              = Nic->PxeInit.TxBufSize;
+  Db->RxBufCnt               = Nic->PxeInit.RxBufCnt;
+  Db->RxBufSize              = Nic->PxeInit.RxBufSize;
+  Db->IFtype                 = PXE_IFTYPE_ETHERNET;
+  Db->SupportedDuplexModes   = PXE_DUPLEX_DEFAULT;
+  Db->SupportedLoopBackModes = LOOPBACK_NORMAL;
+
+  Cdb->StatFlags |= (PXE_STATFLAGS_CABLE_DETECT_SUPPORTED |
+                     PXE_STATFLAGS_GET_STATUS_NO_MEDIA_SUPPORTED);
+
+  if (Nic->UsbEth->UsbEthUndi.UsbEthUndiGetInitInfo != NULL) {
+    Status = Nic->UsbEth->UsbEthUndi.UsbEthUndiGetInitInfo (Cdb, Nic);
+    if (EFI_ERROR (Status)) {
+      Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    }
+  }
+}
+
+/**
+  This command is used to retrieve configuration information about
+  the NIC being controlled by the UNDI.
+
+  @param[in]  Cdb  A pointer to the command descriptor block.
+  @param[in]  Nic  A pointer to the Network interface controller data.
+
+**/
+VOID
+UndiGetConfigInfo (
+  IN  PXE_CDB   *Cdb,
+  IN  NIC_DATA  *Nic
+  )
+{
+  PXE_DB_GET_CONFIG_INFO  *Db;
+  EFI_STATUS              Status;
+
+  if ((Cdb->OpCode != PXE_OPCODE_GET_CONFIG_INFO) ||
+      (Cdb->StatCode != PXE_STATCODE_INITIALIZE) ||
+      (Cdb->StatFlags != PXE_STATFLAGS_INITIALIZE) ||
+      (Cdb->IFnum >= (gPxe->IFcnt | gPxe->IFcntExt << 8)) ||
+      (Cdb->CPBsize != PXE_CPBSIZE_NOT_USED) ||
+      (Cdb->CPBaddr != PXE_CPBADDR_NOT_USED) ||
+      (Cdb->DBsize != sizeof (PXE_DB_GET_CONFIG_INFO)) ||
+      (Cdb->OpFlags != PXE_OPFLAGS_NOT_USED))
+  {
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    Cdb->StatCode  = PXE_STATCODE_INVALID_CDB;
+    return;
+  } else {
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE;
+    Cdb->StatCode  = PXE_STATCODE_SUCCESS;
+  }
+
+  if (Nic->State == PXE_STATFLAGS_GET_STATE_STOPPED) {
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    Cdb->StatCode  = PXE_STATCODE_NOT_STARTED;
+    return;
+  }
+
+  Db = (PXE_DB_GET_CONFIG_INFO *)(UINTN)Cdb->DBaddr;
+
+  Db->pci.BusType = PXE_BUSTYPE_USB;
+
+  if (Nic->UsbEth->UsbEthUndi.UsbEthUndiGetConfigInfo != NULL) {
+    Status = Nic->UsbEth->UsbEthUndi.UsbEthUndiGetConfigInfo (Cdb, Nic);
+    if (EFI_ERROR (Status)) {
+      Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    }
+  }
+}
+
+/**
+  This command resets the network adapter and initializes UNDI using
+  the parameters supplied in the CPB.
+
+  @param[in]      Cdb  A pointer to the command descriptor block.
+  @param[in, out] Nic  A pointer to the Network interface controller data.
+
+**/
+VOID
+UndiInitialize (
+  IN      PXE_CDB   *Cdb,
+  IN OUT  NIC_DATA  *Nic
+  )
+{
+  PXE_CPB_INITIALIZE  *Cpb;
+  PXE_DB_INITIALIZE   *Db;
+  EFI_STATUS          Status;
+
+  if ((Cdb->OpCode != PXE_OPCODE_INITIALIZE) ||
+      (Cdb->StatCode != PXE_STATCODE_INITIALIZE) ||
+      (Cdb->StatFlags != PXE_STATFLAGS_INITIALIZE) ||
+      (Cdb->IFnum >= (gPxe->IFcnt | gPxe->IFcntExt << 8)) ||
+      (Cdb->CPBsize != sizeof (PXE_CPB_INITIALIZE)))
+  {
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    Cdb->StatCode  = PXE_STATCODE_INVALID_CDB;
+    return;
+  } else {
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE;
+  }
+
+  if (Nic->State == PXE_STATFLAGS_GET_STATE_STOPPED) {
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    Cdb->StatCode  = PXE_STATCODE_NOT_STARTED;
+    return;
+  }
+
+  if ((Cdb->OpFlags != PXE_OPFLAGS_INITIALIZE_DETECT_CABLE) &&
+      (Cdb->OpFlags != PXE_OPFLAGS_INITIALIZE_DO_NOT_DETECT_CABLE))
+  {
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    Cdb->StatCode  = PXE_STATCODE_INVALID_CDB;
+    return;
+  }
+
+  if (Nic->State == PXE_STATFLAGS_GET_STATE_INITIALIZED) {
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    Cdb->StatCode  = PXE_STATCODE_ALREADY_INITIALIZED;
+    return;
+  }
+
+  Cpb = (PXE_CPB_INITIALIZE *)(UINTN)Cdb->CPBaddr;
+  Db  = (PXE_DB_INITIALIZE *)(UINTN)Cdb->DBaddr;
+
+  Nic->PxeInit.LinkSpeed    = Cpb->LinkSpeed;
+  Nic->PxeInit.DuplexMode   = Cpb->DuplexMode;
+  Nic->PxeInit.LoopBackMode = Cpb->LoopBackMode;
+  Nic->PxeInit.MemoryAddr   = Cpb->MemoryAddr;
+  Nic->PxeInit.MemoryLength = Cpb->MemoryLength;
+  Nic->PxeInit.TxBufCnt     = TX_BUFFER_COUNT;
+  Nic->PxeInit.TxBufSize    = Nic->MaxSegmentSize;
+  Nic->PxeInit.RxBufCnt     = RX_BUFFER_COUNT;
+  Nic->PxeInit.RxBufSize    = Nic->MaxSegmentSize;
+
+  Cdb->StatCode = Initialize (Cdb, Nic);
+
+  Db->MemoryUsed = MEMORY_REQUIRE;
+  Db->TxBufCnt   = Nic->PxeInit.TxBufCnt;
+  Db->TxBufSize  = Nic->PxeInit.TxBufSize;
+  Db->RxBufCnt   = Nic->PxeInit.RxBufCnt;
+  Db->RxBufSize  = Nic->PxeInit.RxBufSize;
+
+  Nic->RxFilter    = PXE_OPFLAGS_RECEIVE_FILTER_BROADCAST;
+  Nic->CanTransmit = FALSE;
+
+  if (Cdb->OpFlags == PXE_OPFLAGS_INITIALIZE_DETECT_CABLE) {
+    if ((Nic->Request.Request == USB_CDC_NETWORK_CONNECTION) && (Nic->Request.Value == NETWORK_DISCONNECT)) {
+      Nic->CableDetect = 0;
+    } else if ((Nic->Request.Request == USB_CDC_NETWORK_CONNECTION) && (Nic->Request.Value == NETWORK_CONNECTED)) {
+      Nic->CableDetect = 1;
+    }
+
+    if (Nic->CableDetect == 0) {
+      Cdb->StatFlags |= PXE_STATFLAGS_INITIALIZED_NO_MEDIA;
+    }
+  }
+
+  if (Cdb->StatCode != PXE_STATCODE_SUCCESS) {
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+  } else {
+    Nic->State = PXE_STATFLAGS_GET_STATE_INITIALIZED;
+  }
+
+  if (Nic->UsbEth->UsbEthUndi.UsbEthUndiInitialize != NULL) {
+    Status = Nic->UsbEth->UsbEthUndi.UsbEthUndiInitialize (Cdb, Nic);
+    if (EFI_ERROR (Status)) {
+      Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    }
+  }
+}
+
+/**
+  Initialize Network interface controller data.
+
+  @param[in]      Cdb     A pointer to the command descriptor block.
+  @param[in, out] Nic     A pointer to the Network interface controller data.
+
+  @retval Status  A value of Pxe statcode.
+
+**/
+UINT16
+Initialize (
+  IN      PXE_CDB   *Cdb,
+  IN OUT  NIC_DATA  *Nic
+  )
+{
+  UINTN       Status;
+  UINT32      Index;
+  EFI_STATUS  EfiStatus;
+
+  Status = MapIt (
+             Nic,
+             Nic->PxeInit.MemoryAddr,
+             Nic->PxeInit.MemoryLength,
+             TO_AND_FROM_DEVICE,
+             (UINT64)(UINTN)&Nic->MappedAddr
+             );
+
+  if (Status != 0) {
+    return (UINT16)Status;
+  }
+
+  for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) {
+    Nic->PermNodeAddress[Index] = Nic->MacAddr.Addr[Index];
+  }
+
+  for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) {
+    Nic->CurrentNodeAddress[Index] = Nic->PermNodeAddress[Index];
+  }
+
+  for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) {
+    Nic->BroadcastNodeAddress[Index] = 0xFF;
+  }
+
+  for (Index = PXE_HWADDR_LEN_ETHER; Index < PXE_MAC_LENGTH; Index++) {
+    Nic->CurrentNodeAddress[Index]   = 0;
+    Nic->PermNodeAddress[Index]      = 0;
+    Nic->BroadcastNodeAddress[Index] = 0;
+  }
+
+  if (Nic->UsbEth->UsbEthInitialize != NULL) {
+    EfiStatus = Nic->UsbEth->UsbEthInitialize (Cdb, Nic);
+    if (EFI_ERROR (EfiStatus)) {
+      return PXE_STATFLAGS_COMMAND_FAILED;
+    }
+  }
+
+  return (UINT16)Status;
+}
+
+/**
+  This command resets the network adapter and reinitializes the UNDI
+  with the same parameters provided in the Initialize command.
+
+  @param[in]  Cdb  A pointer to the command descriptor block.
+  @param[in]  Nic  A pointer to the Network interface controller data.
+
+**/
+VOID
+UndiReset (
+  IN  PXE_CDB   *Cdb,
+  IN  NIC_DATA  *Nic
+  )
+{
+  EFI_STATUS  Status;
+
+  if ((Cdb->OpCode != PXE_OPCODE_RESET) ||
+      (Cdb->StatCode != PXE_STATCODE_INITIALIZE) ||
+      (Cdb->StatFlags != PXE_STATFLAGS_INITIALIZE) ||
+      (Cdb->IFnum >= (gPxe->IFcnt | gPxe->IFcntExt << 8)) ||
+      (Cdb->CPBsize != PXE_CPBSIZE_NOT_USED) ||
+      (Cdb->CPBaddr != PXE_CPBADDR_NOT_USED) ||
+      (Cdb->DBsize != PXE_DBSIZE_NOT_USED) ||
+      (Cdb->DBaddr != PXE_DBADDR_NOT_USED))
+  {
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    Cdb->StatCode  = PXE_STATCODE_INVALID_CDB;
+    return;
+  } else {
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE;
+    Cdb->StatCode  = PXE_STATCODE_SUCCESS;
+  }
+
+  if (Nic->State != PXE_STATFLAGS_GET_STATE_INITIALIZED) {
+    Cdb->StatCode  = PXE_STATCODE_NOT_INITIALIZED;
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    return;
+  }
+
+  if ((Cdb->OpFlags != PXE_OPFLAGS_NOT_USED) &&
+      (Cdb->OpFlags != PXE_OPFLAGS_RESET_DISABLE_INTERRUPTS) &&
+      (Cdb->OpFlags != PXE_OPFLAGS_RESET_DISABLE_FILTERS))
+  {
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    Cdb->StatCode  = PXE_STATCODE_INVALID_CDB;
+    return;
+  }
+
+  if ((Cdb->OpFlags & PXE_OPFLAGS_RESET_DISABLE_FILTERS) == 0) {
+    Nic->RxFilter = PXE_OPFLAGS_RECEIVE_FILTER_BROADCAST;
+  }
+
+  if ((Cdb->OpFlags & PXE_OPFLAGS_RESET_DISABLE_INTERRUPTS) != 0) {
+    Nic->InterrupOpFlag = 0;
+  }
+
+  if (Nic->UsbEth->UsbEthUndi.UsbEthUndiReset != NULL) {
+    Status = Nic->UsbEth->UsbEthUndi.UsbEthUndiReset (Cdb, Nic);
+    if (EFI_ERROR (Status)) {
+      Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    }
+  }
+}
+
+/**
+  The Shutdown command resets the network adapter and leaves it in a
+  safe state for another driver to initialize.
+
+  @param[in]      Cdb  A pointer to the command descriptor block.
+  @param[in, out] Nic  A pointer to the Network interface controller data.
+
+**/
+VOID
+UndiShutdown (
+  IN      PXE_CDB   *Cdb,
+  IN OUT  NIC_DATA  *Nic
+  )
+{
+  EFI_STATUS  Status;
+
+  if ((Cdb->OpCode != PXE_OPCODE_SHUTDOWN) ||
+      (Cdb->StatCode != PXE_STATCODE_INITIALIZE) ||
+      (Cdb->StatFlags != PXE_STATFLAGS_INITIALIZE) ||
+      (Cdb->IFnum >= (gPxe->IFcnt | gPxe->IFcntExt << 8)) ||
+      (Cdb->CPBsize != PXE_CPBSIZE_NOT_USED) ||
+      (Cdb->CPBaddr != PXE_CPBADDR_NOT_USED) ||
+      (Cdb->DBsize != PXE_DBSIZE_NOT_USED) ||
+      (Cdb->DBaddr != PXE_DBADDR_NOT_USED) ||
+      (Cdb->OpFlags != PXE_OPFLAGS_NOT_USED))
+  {
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    Cdb->StatCode  = PXE_STATCODE_INVALID_CDB;
+    return;
+  } else {
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE;
+    Cdb->StatCode  = PXE_STATCODE_SUCCESS;
+  }
+
+  if (Nic->State != PXE_STATFLAGS_GET_STATE_INITIALIZED) {
+    Cdb->StatCode  = PXE_STATCODE_NOT_INITIALIZED;
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    return;
+  }
+
+  Nic->CanTransmit = FALSE;
+
+  Nic->State = PXE_STATFLAGS_GET_STATE_STARTED;
+
+  if (Nic->UsbEth->UsbEthUndi.UsbEthUndiShutdown != NULL) {
+    Status = Nic->UsbEth->UsbEthUndi.UsbEthUndiShutdown (Cdb, Nic);
+    if (EFI_ERROR (Status)) {
+      Cdb->StatCode  = PXE_STATCODE_NOT_INITIALIZED;
+      Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    }
+  }
+}
+
+/**
+  The Interrupt Enables command can be used to read and/or change
+  the current external interrupt enable settings.
+
+  @param[in]  Cdb  A pointer to the command descriptor block.
+  @param[in]  Nic  A pointer to the Network interface controller data.
+
+**/
+VOID
+UndiInterruptEnable (
+  IN  PXE_CDB   *Cdb,
+  IN  NIC_DATA  *Nic
+  )
+{
+  EFI_STATUS  Status;
+
+  Cdb->StatCode = PXE_STATCODE_UNSUPPORTED;
+  if (Nic->UsbEth->UsbEthUndi.UsbEthUndiInterruptEnable != NULL) {
+    Status = Nic->UsbEth->UsbEthUndi.UsbEthUndiInterruptEnable (Cdb, Nic);
+    if (EFI_ERROR (Status)) {
+      Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    } else {
+      Cdb->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE;
+      Cdb->StatCode  = PXE_STATCODE_SUCCESS;
+    }
+  }
+}
+
+/**
+  This command is used to read and change receive filters and,
+  if supported, read and change the multicast MAC address filter list.
+
+  @param[in]  Cdb  A pointer to the command descriptor block.
+  @param[in]  Nic  A pointer to the Network interface controller data.
+
+**/
+VOID
+UndiReceiveFilter (
+  IN  PXE_CDB   *Cdb,
+  IN  NIC_DATA  *Nic
+  )
+{
+  UINT16                  NewFilter;
+  PXE_DB_RECEIVE_FILTERS  *Db;
+  EFI_STATUS              Status;
+
+  if ((Cdb->OpCode != PXE_OPCODE_RECEIVE_FILTERS) ||
+      (Cdb->StatCode != PXE_STATCODE_INITIALIZE) ||
+      (Cdb->StatFlags != PXE_STATFLAGS_INITIALIZE) ||
+      (Cdb->IFnum >= (gPxe->IFcnt | gPxe->IFcntExt << 8)))
+  {
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    Cdb->StatCode  = PXE_STATCODE_INVALID_CDB;
+    return;
+  } else {
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE;
+    Cdb->StatCode  = PXE_STATCODE_SUCCESS;
+  }
+
+  if (Nic->State != PXE_STATFLAGS_GET_STATE_INITIALIZED) {
+    Cdb->StatCode  = PXE_STATCODE_NOT_INITIALIZED;
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    return;
+  }
+
+  NewFilter = (UINT16)(Cdb->OpFlags & 0x1F);
+
+  switch (Cdb->OpFlags & PXE_OPFLAGS_RECEIVE_FILTER_OPMASK) {
+    case PXE_OPFLAGS_RECEIVE_FILTER_READ:
+      if (Cdb->CPBsize != PXE_CPBSIZE_NOT_USED) {
+        Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+        Cdb->StatCode  = PXE_STATCODE_INVALID_CDB;
+      }
+
+      if ((NewFilter & PXE_OPFLAGS_RECEIVE_FILTER_RESET_MCAST_LIST) == 0) {
+        if ((Cdb->DBsize != 0)) {
+          Db = (PXE_DB_RECEIVE_FILTERS *)(UINTN)Cdb->DBaddr;
+          CopyMem (Db, &Nic->McastList, Nic->McastCount);
+        }
+      }
+
+      break;
+
+    case PXE_OPFLAGS_RECEIVE_FILTER_ENABLE:
+      if (NewFilter == 0) {
+        Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+      }
+
+      if (Cdb->CPBsize != 0) {
+        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) ||
+            ((Cdb->CPBsize % sizeof (PXE_MAC_ADDR)) != 0))
+        {
+          Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+        }
+      }
+
+      if ((Cdb->OpFlags & PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST) != 0) {
+        if (((Cdb->OpFlags & PXE_OPFLAGS_RECEIVE_FILTER_RESET_MCAST_LIST) != 0) ||
+            ((Cdb->OpFlags & PXE_OPFLAGS_RECEIVE_FILTER_ALL_MULTICAST) != 0))
+        {
+          Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+        }
+
+        if ((Cdb->CPBsize == 0)) {
+          Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+        }
+      }
+
+      Cdb->StatCode = SetFilter (Nic, NewFilter, Cdb->CPBaddr, Cdb->CPBsize);
+      break;
+
+    case PXE_OPFLAGS_RECEIVE_FILTER_DISABLE:
+      if (Cdb->CPBsize != PXE_CPBSIZE_NOT_USED) {
+        Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+        Cdb->StatCode  = PXE_STATCODE_INVALID_CDB;
+      }
+
+      break;
+
+    default:
+      Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+      Cdb->StatCode  = PXE_STATCODE_INVALID_CDB;
+  }
+
+  Cdb->StatFlags = (PXE_STATFLAGS)(Cdb->StatFlags | Nic->RxFilter);
+
+  if (Nic->UsbEth->UsbEthUndi.UsbEthUndiReceiveFilter != NULL) {
+    Status = Nic->UsbEth->UsbEthUndi.UsbEthUndiReceiveFilter (Cdb, Nic);
+    if (EFI_ERROR (Status)) {
+      Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    }
+  }
+}
+
+/**
+  Set PXE receive filter.
+
+  @param[in]  Nic         A pointer to the Network interface controller data.
+  @param[in]  SetFilter   PXE receive filter
+  @param[in]  CpbAddr     Command Parameter Block Address
+  @param[in]  CpbSize     Command Parameter Block Size
+
+**/
+UINT16
+SetFilter (
+  IN  NIC_DATA  *Nic,
+  IN  UINT16    SetFilter,
+  IN  UINT64    CpbAddr,
+  IN  UINT32    CpbSize
+  )
+{
+  EFI_STATUS                   Status;
+  UINT8                        *McastList;
+  UINT8                        Count;
+  UINT8                        Index1;
+  UINT8                        Index2;
+  PXE_CPB_RECEIVE_FILTERS      *Cpb;
+  USB_ETHERNET_FUN_DESCRIPTOR  UsbEthFunDescriptor;
+
+  Count = 0;
+  Cpb   = (PXE_CPB_RECEIVE_FILTERS *)(UINTN)CpbAddr;
+
+  // The Cpb could be NULL.(ref:PXE_CPBADDR_NOT_USED)
+  Nic->RxFilter = (UINT8)SetFilter;
+
+  if (((SetFilter & PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST) != 0) || (Cpb != NULL)) {
+    if (Cpb != NULL) {
+      Nic->McastCount = (UINT8)(CpbSize / PXE_MAC_LENGTH);
+      CopyMem (&Nic->McastList, Cpb, Nic->McastCount);
+    }
+
+    Nic->UsbEth->UsbEthFunDescriptor (Nic->UsbEth, &UsbEthFunDescriptor);
+    if ((UsbEthFunDescriptor.NumberMcFilters << 1) == 0) {
+      Nic->RxFilter |= PXE_OPFLAGS_RECEIVE_FILTER_ALL_MULTICAST;
+      Nic->UsbEth->SetUsbEthPacketFilter (Nic->UsbEth, Nic->RxFilter);
+    } else {
+      Status = gBS->AllocatePool (EfiBootServicesData, Nic->McastCount * 6, (VOID **)&McastList);
+      if (EFI_ERROR (Status)) {
+        return PXE_STATCODE_INVALID_PARAMETER;
+      }
+
+      if (Cpb != NULL) {
+        for (Index1 = 0; Index1 < Nic->McastCount; Index1++) {
+          for (Index2 = 0; Index2 < 6; Index2++) {
+            McastList[Count++] = Cpb->MCastList[Index1][Index2];
+          }
+        }
+      }
+
+      Nic->RxFilter |= PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST;
+      if (Cpb != NULL) {
+        Nic->UsbEth->SetUsbEthMcastFilter (Nic->UsbEth, Nic->McastCount, McastList);
+      }
+
+      Nic->UsbEth->SetUsbEthPacketFilter (Nic->UsbEth, Nic->RxFilter);
+      FreePool (McastList);
+    }
+  }
+
+  return PXE_STATCODE_SUCCESS;
+}
+
+/**
+  This command is used to get current station and broadcast MAC addresses
+  and, if supported, to change the current station MAC address.
+
+  @param[in]  Cdb  A pointer to the command descriptor block.
+  @param[in]  Nic  A pointer to the Network interface controller data.
+
+**/
+VOID
+UndiStationAddress (
+  IN  PXE_CDB   *Cdb,
+  IN  NIC_DATA  *Nic
+  )
+{
+  PXE_CPB_STATION_ADDRESS  *Cpb;
+  PXE_DB_STATION_ADDRESS   *Db;
+  UINT16                   Index;
+  EFI_STATUS               Status;
+
+  if ((Cdb->OpCode != PXE_OPCODE_STATION_ADDRESS) ||
+      (Cdb->StatCode != PXE_STATCODE_INITIALIZE) ||
+      (Cdb->StatFlags != PXE_STATFLAGS_INITIALIZE) ||
+      (Cdb->IFnum >= (gPxe->IFcnt | gPxe->IFcntExt << 8)) ||
+      (Cdb->DBsize != sizeof (PXE_DB_STATION_ADDRESS)))
+  {
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    Cdb->StatCode  = PXE_STATCODE_INVALID_CDB;
+    return;
+  } else {
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE;
+    Cdb->StatCode  = PXE_STATCODE_SUCCESS;
+  }
+
+  if (Nic->State != PXE_STATFLAGS_GET_STATE_INITIALIZED) {
+    Cdb->StatCode  = PXE_STATCODE_NOT_INITIALIZED;
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    return;
+  }
+
+  if (Cdb->OpFlags == PXE_OPFLAGS_STATION_ADDRESS_RESET) {
+    if (CompareMem (&Nic->CurrentNodeAddress[0], &Nic->PermNodeAddress[0], PXE_MAC_LENGTH) != 0) {
+      for (Index = 0; Index < PXE_MAC_LENGTH; Index++) {
+        Nic->CurrentNodeAddress[Index] = Nic->PermNodeAddress[Index];
+      }
+    }
+  }
+
+  if (Cdb->CPBaddr != 0) {
+    Cpb = (PXE_CPB_STATION_ADDRESS *)(UINTN)Cdb->CPBaddr;
+    for (Index = 0; Index < PXE_MAC_LENGTH; Index++) {
+      Nic->CurrentNodeAddress[Index] = Cpb->StationAddr[Index];
+    }
+  }
+
+  if (Cdb->DBaddr != 0) {
+    Db = (PXE_DB_STATION_ADDRESS *)(UINTN)Cdb->DBaddr;
+    for (Index = 0; Index < PXE_MAC_LENGTH; Index++) {
+      Db->StationAddr[Index]   = Nic->CurrentNodeAddress[Index];
+      Db->BroadcastAddr[Index] = Nic->BroadcastNodeAddress[Index];
+      Db->PermanentAddr[Index] = Nic->PermNodeAddress[Index];
+    }
+  }
+
+  if (Nic->UsbEth->UsbEthUndi.UsbEthUndiStationAddress != NULL) {
+    Status = Nic->UsbEth->UsbEthUndi.UsbEthUndiStationAddress (Cdb, Nic);
+    if (EFI_ERROR (Status)) {
+      Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    }
+  }
+}
+
+/**
+  This command is used to read and clear the NIC traffic statistics.
+
+  @param[in]  Cdb  A pointer to the command descriptor block.
+  @param[in]  Nic  A pointer to the Network interface controller data.
+
+**/
+VOID
+UndiStatistics (
+  IN  PXE_CDB   *Cdb,
+  IN  NIC_DATA  *Nic
+  )
+{
+  EFI_STATUS  Status;
+
+  if ((Cdb->OpCode != PXE_OPCODE_STATISTICS) ||
+      (Cdb->StatCode != PXE_STATCODE_INITIALIZE) ||
+      (Cdb->StatFlags != PXE_STATFLAGS_INITIALIZE) ||
+      (Cdb->IFnum >= (gPxe->IFcnt | gPxe->IFcntExt << 8)) ||
+      (Cdb->CPBsize != PXE_CPBSIZE_NOT_USED) ||
+      (Cdb->CPBaddr != PXE_CPBADDR_NOT_USED))
+  {
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    Cdb->StatCode  = PXE_STATCODE_INVALID_CDB;
+    return;
+  } else {
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE;
+  }
+
+  if (Nic->State != PXE_STATFLAGS_GET_STATE_INITIALIZED) {
+    Cdb->StatCode  = PXE_STATCODE_NOT_INITIALIZED;
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    return;
+  }
+
+  if ((Cdb->OpFlags != PXE_OPFLAGS_STATISTICS_RESET) &&
+      (Cdb->OpFlags != PXE_OPFLAGS_STATISTICS_READ))
+  {
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    Cdb->StatCode  = PXE_STATCODE_INVALID_CDB;
+    return;
+  }
+
+  Cdb->StatCode = Statistics (Nic, Cdb->DBaddr, Cdb->DBsize);
+
+  if (Nic->UsbEth->UsbEthUndi.UsbEthUndiStatistics != NULL) {
+    Status = Nic->UsbEth->UsbEthUndi.UsbEthUndiStatistics (Cdb, Nic);
+    if (EFI_ERROR (Status)) {
+      Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    }
+  }
+}
+
+/**
+  Return data for DB data.
+
+  @param[in]  Nic      A pointer to the Network interface controller data.
+  @param[in]  DbAddr   Data Block Address.
+  @param[in]  DbSize   Data Block Size.
+
+**/
+UINT16
+Statistics (
+  IN NIC_DATA  *Nic,
+  IN UINT64    DbAddr,
+  IN UINT16    DbSize
+  )
+{
+  PXE_DB_STATISTICS  *DbStatistic;
+  EFI_STATUS         Status;
+
+  DbStatistic = (PXE_DB_STATISTICS *)(UINTN)DbAddr;
+
+  if (DbSize == 0) {
+    return PXE_STATCODE_SUCCESS;
+  }
+
+  DbStatistic->Supported  = 0x802;
+  DbStatistic->Data[0x01] = Nic->RxFrame;
+  DbStatistic->Data[0x0B] = Nic->TxFrame;
+
+  if (Nic->UsbEth->UsbEthStatistics != NULL) {
+    Status = Nic->UsbEth->UsbEthStatistics (Nic, DbAddr, DbSize);
+    if (EFI_ERROR (Status)) {
+      return PXE_STATFLAGS_COMMAND_FAILED;
+    }
+  }
+
+  return PXE_STATCODE_SUCCESS;
+}
+
+/**
+  Translate a multicast IPv4 or IPv6 address to a multicast MAC address.
+
+  @param[in, out] Cdb  A pointer to the command descriptor block.
+  @param[in]      Nic  A pointer to the Network interface controller data.
+
+**/
+VOID
+UndiMcastIp2Mac (
+  IN OUT  PXE_CDB   *Cdb,
+  IN      NIC_DATA  *Nic
+  )
+{
+  PXE_CPB_MCAST_IP_TO_MAC  *Cpb;
+  PXE_DB_MCAST_IP_TO_MAC   *Db;
+  UINT8                    *Tmp;
+  EFI_STATUS               Status;
+
+  if ((Cdb->OpCode != PXE_OPCODE_MCAST_IP_TO_MAC) ||
+      (Cdb->StatCode != PXE_STATCODE_INITIALIZE) ||
+      (Cdb->StatFlags != PXE_STATFLAGS_INITIALIZE) ||
+      (Cdb->IFnum >= (gPxe->IFcnt | gPxe->IFcntExt << 8)) ||
+      (Cdb->CPBsize != sizeof (PXE_CPB_MCAST_IP_TO_MAC)) ||
+      (Cdb->DBsize != sizeof (PXE_DB_MCAST_IP_TO_MAC)))
+  {
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    Cdb->StatCode  = PXE_STATCODE_INVALID_CDB;
+    return;
+  } else {
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE;
+    Cdb->StatCode  = PXE_STATCODE_SUCCESS;
+  }
+
+  if (Nic->State != PXE_STATFLAGS_GET_STATE_INITIALIZED) {
+    Cdb->StatCode  = PXE_STATCODE_NOT_INITIALIZED;
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    return;
+  }
+
+  Cpb = (PXE_CPB_MCAST_IP_TO_MAC *)(UINTN)Cdb->CPBaddr;
+  Db  = (PXE_DB_MCAST_IP_TO_MAC *)(UINTN)Cdb->DBaddr;
+
+  if ((Cdb->OpFlags & PXE_OPFLAGS_MCAST_IPV6_TO_MAC) != 0) {
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    Cdb->StatCode  = PXE_STATCODE_UNSUPPORTED;
+    return;
+  }
+
+  Tmp = (UINT8 *)(&Cpb->IP.IPv4);
+
+  if ((Tmp[0] & 0xF0) != 0xE0) {
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    Cdb->StatCode  = PXE_STATCODE_INVALID_CPB;
+  }
+
+  Db->MAC[0] = 0x01;
+  Db->MAC[1] = 0x00;
+  Db->MAC[2] = 0x5E;
+  Db->MAC[3] = Tmp[1] & 0x7F;
+  Db->MAC[4] = Tmp[2];
+  Db->MAC[5] = Tmp[3];
+
+  if (Nic->UsbEth->UsbEthUndi.UsbEthUndiMcastIp2Mac != NULL) {
+    Status = Nic->UsbEth->UsbEthUndi.UsbEthUndiMcastIp2Mac (Cdb, Nic);
+    if (EFI_ERROR (Status)) {
+      Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    }
+  }
+}
+
+/**
+  This command is used to read and write (if supported by NIC H/W)
+  nonvolatile storage on the NIC.
+
+  @param[in]  Cdb  A pointer to the command descriptor block.
+  @param[in]  Nic  A pointer to the Network interface controller data.
+
+**/
+VOID
+UndiNvData (
+  IN  PXE_CDB   *Cdb,
+  IN  NIC_DATA  *Nic
+  )
+{
+  EFI_STATUS  Status;
+
+  Cdb->StatCode = PXE_STATCODE_UNSUPPORTED;
+  if (Nic->UsbEth->UsbEthUndi.UsbEthUndiNvData != NULL) {
+    Status = Nic->UsbEth->UsbEthUndi.UsbEthUndiNvData (Cdb, Nic);
+    if (EFI_ERROR (Status)) {
+      Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    } else {
+      Cdb->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE;
+      Cdb->StatCode  = PXE_STATCODE_SUCCESS;
+    }
+  }
+}
+
+/**
+  This command returns the current interrupt status and/or the
+  transmitted buffer addresses and the current media status.
+
+  @param[in]  Cdb  A pointer to the command descriptor block.
+  @param[in]  Nic  A pointer to the Network interface controller data.
+
+**/
+VOID
+UndiGetStatus (
+  IN  PXE_CDB   *Cdb,
+  IN  NIC_DATA  *Nic
+  )
+{
+  PXE_DB_GET_STATUS  *Db;
+  PXE_DB_GET_STATUS  TmpGetStatus;
+  UINT16             NumEntries;
+  UINTN              Index;
+  EFI_STATUS         Status;
+
+  if ((Cdb->OpCode != PXE_OPCODE_GET_STATUS) ||
+      (Cdb->StatCode != PXE_STATCODE_INITIALIZE) ||
+      (Cdb->StatFlags != PXE_STATFLAGS_INITIALIZE) ||
+      (Cdb->IFnum >= (gPxe->IFcnt | gPxe->IFcntExt << 8)) ||
+      (Cdb->CPBsize != PXE_CPBSIZE_NOT_USED) ||
+      (Cdb->CPBaddr != PXE_CPBADDR_NOT_USED))
+  {
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    Cdb->StatCode  = PXE_STATCODE_INVALID_CDB;
+    return;
+  } else {
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE;
+    Cdb->StatCode  = PXE_STATCODE_SUCCESS;
+  }
+
+  if (Nic->State != PXE_STATFLAGS_GET_STATE_INITIALIZED) {
+    Cdb->StatCode  = PXE_STATCODE_NOT_INITIALIZED;
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    return;
+  }
+
+  TmpGetStatus.RxFrameLen = 0;
+  TmpGetStatus.reserved   = 0;
+  Db                      = (PXE_DB_GET_STATUS *)(UINTN)Cdb->DBaddr;
+
+  if ((Cdb->DBsize > 0) && (Cdb->DBsize < sizeof (UINT32) * 2)) {
+    CopyMem (Db, &TmpGetStatus, Cdb->DBsize);
+  } else {
+    CopyMem (Db, &TmpGetStatus, sizeof (UINT32) * 2);
+  }
+
+  if ((Cdb->OpFlags & PXE_OPFLAGS_GET_TRANSMITTED_BUFFERS) != 0) {
+    if (Cdb->DBsize == 0) {
+      Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+      Cdb->StatCode  = PXE_STATCODE_INVALID_CDB;
+      return;
+    }
+
+    NumEntries  = Cdb->DBsize - sizeof (UINT64);
+    Cdb->DBsize = sizeof (UINT32) * 2;
+
+    for (Index = 0; NumEntries >= sizeof (UINT64); Index++, NumEntries -= sizeof (UINT64)) {
+      if (Nic->TxBufferCount > 0) {
+        Nic->TxBufferCount--;
+        Db->TxBuffer[Index] = Nic->MediaHeader[Nic->TxBufferCount];
+      }
+    }
+  }
+
+  if ((Cdb->OpFlags & PXE_OPFLAGS_GET_INTERRUPT_STATUS) != 0) {
+    if (Nic->ReceiveStatus != 0) {
+      Cdb->StatFlags |= PXE_STATFLAGS_GET_STATUS_RECEIVE;
+    }
+  }
+
+  if ((Nic->Request.Request == USB_CDC_NETWORK_CONNECTION) && (Nic->Request.Value == NETWORK_DISCONNECT)) {
+    Nic->CableDetect = 0;
+  } else if ((Nic->Request.Request == USB_CDC_NETWORK_CONNECTION) && (Nic->Request.Value == NETWORK_CONNECTED)) {
+    Nic->CableDetect = 1;
+  }
+
+  if ((Cdb->OpFlags & PXE_OPFLAGS_GET_MEDIA_STATUS) != 0) {
+    if (Nic->CableDetect == 0) {
+      Cdb->StatFlags |= PXE_STATFLAGS_GET_STATUS_NO_MEDIA;
+    }
+  }
+
+  if (Nic->UsbEth->UsbEthUndi.UsbEthUndiGetStatus != NULL) {
+    Status = Nic->UsbEth->UsbEthUndi.UsbEthUndiGetStatus (Cdb, Nic);
+    if (EFI_ERROR (Status)) {
+      Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    }
+  }
+}
+
+/**
+  This command is used to fill the media header(s) in transmit packet(s).
+
+  @param[in]  Cdb  A pointer to the command descriptor block.
+  @param[in]  Nic  A pointer to the Network interface controller data.
+
+**/
+VOID
+UndiFillHeader (
+  IN  PXE_CDB   *Cdb,
+  IN  NIC_DATA  *Nic
+  )
+{
+  PXE_CPB_FILL_HEADER             *CpbFillHeader;
+  PXE_CPB_FILL_HEADER_FRAGMENTED  *CpbFill;
+  EthernetHeader                  *MacHeader;
+  UINTN                           Index;
+  EFI_STATUS                      Status;
+
+  if ((Cdb->OpCode != PXE_OPCODE_FILL_HEADER) ||
+      (Cdb->StatCode != PXE_STATCODE_INITIALIZE) ||
+      (Cdb->StatFlags != PXE_STATFLAGS_INITIALIZE) ||
+      (Cdb->IFnum >= (gPxe->IFcnt | gPxe->IFcntExt << 8)) ||
+      (Cdb->CPBsize != sizeof (PXE_CPB_FILL_HEADER_FRAGMENTED)) ||
+      (Cdb->DBsize != PXE_DBSIZE_NOT_USED) ||
+      (Cdb->DBaddr != PXE_DBADDR_NOT_USED))
+  {
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    Cdb->StatCode  = PXE_STATCODE_INVALID_CDB;
+    return;
+  } else {
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE;
+    Cdb->StatCode  = PXE_STATCODE_SUCCESS;
+  }
+
+  if (Nic->State != PXE_STATFLAGS_GET_STATE_INITIALIZED) {
+    Cdb->StatCode  = PXE_STATCODE_NOT_INITIALIZED;
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    return;
+  }
+
+  if (Cdb->CPBsize == PXE_CPBSIZE_NOT_USED) {
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    Cdb->StatCode  = PXE_STATCODE_INVALID_CDB;
+    return;
+  }
+
+  if ((Cdb->OpFlags & PXE_OPFLAGS_FILL_HEADER_FRAGMENTED) != 0) {
+    CpbFill = (PXE_CPB_FILL_HEADER_FRAGMENTED *)(UINTN)Cdb->CPBaddr;
+
+    if ((CpbFill->FragCnt == 0) || (CpbFill->FragDesc[0].FragLen < PXE_MAC_HEADER_LEN_ETHER)) {
+      Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+      Cdb->StatCode  = PXE_STATCODE_INVALID_CDB;
+      return;
+    }
+
+    MacHeader           = (EthernetHeader *)(UINTN)CpbFill->FragDesc[0].FragAddr;
+    MacHeader->Protocol = CpbFill->Protocol;
+
+    for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) {
+      MacHeader->DestAddr[Index] = CpbFill->DestAddr[Index];
+      MacHeader->SrcAddr[Index]  = CpbFill->SrcAddr[Index];
+    }
+  } else {
+    CpbFillHeader = (PXE_CPB_FILL_HEADER *)(UINTN)Cdb->CPBaddr;
+
+    MacHeader           = (EthernetHeader *)(UINTN)CpbFillHeader->MediaHeader;
+    MacHeader->Protocol = CpbFillHeader->Protocol;
+
+    for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) {
+      MacHeader->DestAddr[Index] = CpbFillHeader->DestAddr[Index];
+      MacHeader->SrcAddr[Index]  = CpbFillHeader->SrcAddr[Index];
+    }
+  }
+
+  if (Nic->UsbEth->UsbEthUndi.UsbEthUndiFillHeader != NULL) {
+    Status = Nic->UsbEth->UsbEthUndi.UsbEthUndiFillHeader (Cdb, Nic);
+    if (EFI_ERROR (Status)) {
+      Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    }
+  }
+}
+
+/**
+  The Transmit command is used to place a packet into the transmit queue.
+
+  @param[in]  Cdb  A pointer to the command descriptor block.
+  @param[in]  Nic  A pointer to the Network interface controller data.
+
+**/
+VOID
+UndiTransmit (
+  IN  PXE_CDB   *Cdb,
+  IN  NIC_DATA  *Nic
+  )
+{
+  EFI_STATUS  Status;
+
+  if ((Cdb->OpCode != PXE_OPCODE_TRANSMIT) ||
+      (Cdb->StatCode != PXE_STATCODE_INITIALIZE) ||
+      (Cdb->StatFlags != PXE_STATFLAGS_INITIALIZE) ||
+      (Cdb->IFnum >= (gPxe->IFcnt | gPxe->IFcntExt << 8)) ||
+      (Cdb->CPBsize != sizeof (PXE_CPB_TRANSMIT)) ||
+      (Cdb->DBsize != PXE_DBSIZE_NOT_USED) ||
+      (Cdb->DBaddr != PXE_DBADDR_NOT_USED))
+  {
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    Cdb->StatCode  = PXE_STATCODE_INVALID_CDB;
+    return;
+  } else {
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE;
+    Cdb->StatCode  = PXE_STATCODE_SUCCESS;
+  }
+
+  if (Nic->State != PXE_STATFLAGS_GET_STATE_INITIALIZED) {
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    Cdb->StatCode  = PXE_STATCODE_NOT_INITIALIZED;
+    return;
+  }
+
+  if (Cdb->CPBsize == PXE_CPBSIZE_NOT_USED) {
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    Cdb->StatCode  = PXE_STATCODE_INVALID_CDB;
+    return;
+  }
+
+  if (Nic->UsbEth->UsbEthUndi.UsbEthUndiTransmit != NULL) {
+    Status = Nic->UsbEth->UsbEthUndi.UsbEthUndiTransmit (Cdb, Nic);
+    if (EFI_ERROR (Status)) {
+      Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    }
+
+    return;
+  }
+
+  Cdb->StatCode = Transmit (Cdb, Nic, Cdb->CPBaddr, Cdb->OpFlags);
+
+  if (Cdb->StatCode != PXE_STATCODE_SUCCESS) {
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+  }
+}
+
+/**
+  Use USB Ethernet Protocol Bulk out command to transmit data.
+
+  @param[in]      Cdb      A pointer to the command descriptor block.
+  @param[in, out] Nic      A pointer to the Network interface controller data.
+  @param[in]      CpbAddr  Command Parameter Block Address.
+  @param[in]      OpFlags  Operation Flags.
+
+**/
+UINT16
+Transmit (
+  IN      PXE_CDB   *Cdb,
+  IN OUT  NIC_DATA  *Nic,
+  IN      UINT64    CpbAddr,
+  IN      UINT16    OpFlags
+  )
+{
+  EFI_STATUS        Status;
+  PXE_CPB_TRANSMIT  *Cpb;
+  UINT64            BulkOutData;
+  UINTN             DataLength;
+  UINTN             TransmitLength;
+  UINTN             Map;
+  UINT32            Counter;
+  UINT16            StatCode;
+
+  BulkOutData = 0;
+  Counter     = 0;
+  Cpb         = (PXE_CPB_TRANSMIT *)(UINTN)CpbAddr;
+
+  if (Nic->CanTransmit) {
+    return PXE_STATCODE_BUSY;
+  }
+
+  Nic->CanTransmit = TRUE;
+
+  if ((OpFlags & PXE_OPFLAGS_TRANSMIT_FRAGMENTED) != 0) {
+    return PXE_STATCODE_INVALID_PARAMETER;
+  }
+
+  Map = MapIt (
+          Nic,
+          Cpb->FrameAddr,
+          Cpb->DataLen + (UINT32)Cpb->MediaheaderLen,
+          TO_DEVICE,
+          (UINT64)(UINTN)&BulkOutData
+          );
+
+  if (Map != 0) {
+    Nic->CanTransmit = FALSE;
+    return PXE_STATCODE_INVALID_PARAMETER;
+  }
+
+  if (Nic->TxBufferCount < MAX_XMIT_BUFFERS) {
+    Nic->MediaHeader[Nic->TxBufferCount] = Cpb->FrameAddr;
+    Nic->TxBufferCount++;
+  }
+
+  DataLength = (UINTN)(Cpb->DataLen + (UINT32)Cpb->MediaheaderLen);
+
+  while (1) {
+    if (Counter >= 3) {
+      StatCode = PXE_STATCODE_BUSY;
+      break;
+    }
+
+    TransmitLength = DataLength;
+
+    Status = Nic->UsbEth->UsbEthTransmit (Cdb, Nic->UsbEth, (VOID *)(UINTN)BulkOutData, &TransmitLength);
+    if (EFI_ERROR (Status)) {
+      StatCode =  PXE_STATFLAGS_COMMAND_FAILED;
+    }
+
+    if (Status == EFI_INVALID_PARAMETER) {
+      StatCode = PXE_STATCODE_INVALID_PARAMETER;
+      break;
+    }
+
+    if (Status == EFI_DEVICE_ERROR) {
+      StatCode = PXE_STATCODE_DEVICE_FAILURE;
+      break;
+    }
+
+    if (!EFI_ERROR (Status)) {
+      Nic->TxFrame++;
+      StatCode = PXE_STATCODE_SUCCESS;
+      break;
+    }
+
+    Counter++;
+  }
+
+  UnMapIt (
+    Nic,
+    Cpb->FrameAddr,
+    Cpb->DataLen + (UINT32)Cpb->MediaheaderLen,
+    TO_DEVICE,
+    BulkOutData
+    );
+
+  Nic->CanTransmit = FALSE;
+
+  return StatCode;
+}
+
+/**
+  When the network adapter has received a frame, this command is used
+  to copy the frame into driver/application storage.
+
+  @param[in]  Cdb  A pointer to the command descriptor block.
+  @param[in]  Nic  A pointer to the Network interface controller data.
+
+**/
+VOID
+UndiReceive (
+  IN  PXE_CDB   *Cdb,
+  IN  NIC_DATA  *Nic
+  )
+{
+  EFI_STATUS  Status;
+
+  if ((Cdb->OpCode != PXE_OPCODE_RECEIVE) ||
+      (Cdb->StatCode != PXE_STATCODE_INITIALIZE) ||
+      (Cdb->StatFlags != PXE_STATFLAGS_INITIALIZE) ||
+      (Cdb->IFnum >= (gPxe->IFcnt | gPxe->IFcntExt << 8)) ||
+      (Cdb->CPBsize != sizeof (PXE_CPB_RECEIVE)) ||
+      (Cdb->DBsize != sizeof (PXE_DB_RECEIVE)) ||
+      (Cdb->OpFlags != PXE_OPFLAGS_NOT_USED))
+  {
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    Cdb->StatCode  = PXE_STATCODE_INVALID_CDB;
+    return;
+  } else {
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_COMPLETE;
+    Cdb->StatCode  = PXE_STATCODE_SUCCESS;
+  }
+
+  if (Nic->State != PXE_STATFLAGS_GET_STATE_INITIALIZED) {
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    Cdb->StatCode  = PXE_STATCODE_NOT_INITIALIZED;
+    return;
+  }
+
+  if (Nic->UsbEth->UsbEthUndi.UsbEthUndiReceive != NULL) {
+    Status = Nic->UsbEth->UsbEthUndi.UsbEthUndiReceive (Cdb, Nic);
+    if (EFI_ERROR (Status)) {
+      Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+    }
+
+    return;
+  }
+
+  Cdb->StatCode = Receive (Cdb, Nic, Cdb->CPBaddr, Cdb->DBaddr);
+
+  if (Cdb->StatCode != PXE_STATCODE_SUCCESS) {
+    Cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED;
+  }
+}
+
+/**
+  Use USB Ethernet Protocol Bulk in command to receive data.
+
+  @param[in]      Cdb      A pointer to the command descriptor block.
+  @param[in, out] Nic      A pointer to the Network interface controller data.
+  @param[in]      CpbAddr  Command Parameter Block Address.
+  @param[in, out] DbAddr   Data Block Address.
+
+**/
+UINT16
+Receive (
+  IN PXE_CDB       *Cdb,
+  IN OUT NIC_DATA  *Nic,
+  IN UINT64        CpbAddr,
+  IN OUT UINT64    DbAddr
+  )
+{
+  EFI_STATUS       Status;
+  UINTN            Index;
+  PXE_FRAME_TYPE   FrameType;
+  PXE_CPB_RECEIVE  *Cpb;
+  PXE_DB_RECEIVE   *Db;
+  NIC_DEVICE       *NicDevice;
+  UINT8            *BulkInData;
+  UINTN            DataLength;
+  EthernetHeader   *Header;
+  EFI_TPL          OriginalTpl;
+
+  FrameType  = PXE_FRAME_TYPE_NONE;
+  NicDevice  = UNDI_DEV_FROM_NIC (Nic);
+  BulkInData = NicDevice->ReceiveBuffer;
+  DataLength = (UINTN)Nic->MaxSegmentSize;
+  Cpb        = (PXE_CPB_RECEIVE *)(UINTN)CpbAddr;
+  Db         = (PXE_DB_RECEIVE *)(UINTN)DbAddr;
+
+  if (!BulkInData) {
+    return PXE_STATCODE_INVALID_PARAMETER;
+  }
+
+  if ((Nic->RateLimitingCreditCount == 0) && (Nic->RateLimitingEnable == TRUE)) {
+    return PXE_STATCODE_NO_DATA;
+  }
+
+  Status = Nic->UsbEth->UsbEthReceive (Cdb, Nic->UsbEth, (VOID *)BulkInData, &DataLength);
+  if (EFI_ERROR (Status)) {
+    Nic->ReceiveStatus = 0;
+    if (Nic->RateLimitingEnable == TRUE) {
+      OriginalTpl = gBS->RaiseTPL (TPL_NOTIFY);
+      if (Nic->RateLimitingCreditCount != 0) {
+        Nic->RateLimitingCreditCount--;
+      }
+
+      gBS->RestoreTPL (OriginalTpl);
+    }
+
+    return PXE_STATCODE_NO_DATA;
+  }
+
+  Nic->RxFrame++;
+
+  if (DataLength != 0) {
+    if (DataLength > Cpb->BufferLen) {
+      DataLength = (UINTN)Cpb->BufferLen;
+    }
+
+    CopyMem ((UINT8 *)(UINTN)Cpb->BufferAddr, (UINT8 *)BulkInData, DataLength);
+
+    Header = (EthernetHeader *)BulkInData;
+
+    Db->FrameLen       = (UINT32)DataLength;
+    Db->MediaHeaderLen = PXE_MAC_HEADER_LEN_ETHER;
+
+    for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) {
+      if (Header->DestAddr[Index] != Nic->CurrentNodeAddress[Index]) {
+        break;
+      }
+    }
+
+    if (Index >= PXE_HWADDR_LEN_ETHER) {
+      FrameType = PXE_FRAME_TYPE_UNICAST;
+    } else {
+      for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) {
+        if (Header->DestAddr[Index] != Nic->BroadcastNodeAddress[Index]) {
+          break;
+        }
+      }
+
+      if (Index >= PXE_HWADDR_LEN_ETHER) {
+        FrameType = PXE_FRAME_TYPE_BROADCAST;
+      } else {
+        if ((Header->DestAddr[0] & 1) == 1) {
+          FrameType = PXE_FRAME_TYPE_FILTERED_MULTICAST;
+        } else {
+          FrameType = PXE_FRAME_TYPE_PROMISCUOUS;
+        }
+      }
+    }
+
+    Db->Type     = FrameType;
+    Db->Protocol = Header->Protocol;
+
+    for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) {
+      Db->SrcAddr[Index]  = Header->SrcAddr[Index];
+      Db->DestAddr[Index] = Header->DestAddr[Index];
+    }
+  }
+
+  if (FrameType == PXE_FRAME_TYPE_NONE) {
+    Nic->ReceiveStatus = 0;
+  } else {
+    Nic->ReceiveStatus = 1;
+  }
+
+  return PXE_STATCODE_SUCCESS;
+}
+
+/**
+  Fill out PXE SW UNDI structure.
+
+  @param[out]  PxeSw      A pointer to the PXE SW UNDI structure.
+
+**/
+VOID
+PxeStructInit (
+  OUT PXE_SW_UNDI  *PxeSw
+  )
+{
+  PxeSw->Signature = PXE_ROMID_SIGNATURE;
+  PxeSw->Len       = (UINT8)sizeof (PXE_SW_UNDI);
+  PxeSw->Fudge     = 0;
+  PxeSw->IFcnt     = 0;
+  PxeSw->IFcntExt  = 0;
+  PxeSw->Rev       = PXE_ROMID_REV;
+  PxeSw->MajorVer  = PXE_ROMID_MAJORVER;
+  PxeSw->MinorVer  = PXE_ROMID_MINORVER;
+  PxeSw->reserved1 = 0;
+
+  PxeSw->Implementation = PXE_ROMID_IMP_SW_VIRT_ADDR |
+                          PXE_ROMID_IMP_FRAG_SUPPORTED |
+                          PXE_ROMID_IMP_CMD_LINK_SUPPORTED |
+                          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;
+
+  PxeSw->EntryPoint   = (UINT64)(UINTN)UndiApiEntry;
+  PxeSw->reserved2[0] = 0;
+  PxeSw->reserved2[1] = 0;
+  PxeSw->reserved2[2] = 0;
+  PxeSw->BusCnt       = 1;
+  PxeSw->BusType[0]   = PXE_BUSTYPE_USB;
+  PxeSw->Fudge        = PxeSw->Fudge - CalculateSum8 ((VOID *)PxeSw, PxeSw->Len);
+}
+
+/**
+  Update NIC number.
+
+  @param[in]      Nic       A pointer to the Network interface controller data.
+  @param[in, out] PxeSw     A pointer to the PXE SW UNDI structure.
+
+**/
+VOID
+UpdateNicNum (
+  IN      NIC_DATA     *Nic,
+  IN OUT  PXE_SW_UNDI  *PxeSw
+  )
+{
+  UINT16  NicNum;
+
+  NicNum = (PxeSw->IFcnt | PxeSw->IFcntExt << 8);
+
+  if (Nic == NULL) {
+    if (NicNum > 0) {
+      NicNum--;
+    }
+
+    PxeSw->IFcnt    = (UINT8)(NicNum & 0xFF);          // Get lower byte
+    PxeSw->IFcntExt = (UINT8)((NicNum & 0xFF00) >> 8); // Get upper byte
+    PxeSw->Fudge    = (UINT8)(PxeSw->Fudge - CalculateSum8 ((VOID *)PxeSw, PxeSw->Len));
+    return;
+  }
+
+  NicNum++;
+
+  PxeSw->IFcnt    = (UINT8)(NicNum & 0xFF);          // Get lower byte
+  PxeSw->IFcntExt = (UINT8)((NicNum & 0xFF00) >> 8); // Get upper byte
+  PxeSw->Fudge    = (UINT8)(PxeSw->Fudge - CalculateSum8 ((VOID *)PxeSw, PxeSw->Len));
+}
+
+/**
+  UNDI API table entry.
+
+  @param[in]  Cdb  A pointer to the command descriptor block.
+
+**/
+EFI_STATUS
+EFIAPI
+UndiApiEntry (
+  IN  UINT64  Cdb
+  )
+{
+  PXE_CDB   *CdbPtr;
+  NIC_DATA  *Nic;
+
+  if (Cdb == 0) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  CdbPtr = (PXE_CDB *)(UINTN)Cdb;
+  Nic    = &(gLanDeviceList[CdbPtr->IFnum]->NicInfo);
+  gUndiApiTable[CdbPtr->OpCode](CdbPtr, Nic);
+  return EFI_SUCCESS;
+}
+
+/**
+  Map virtual memory address for DMA. This field can be set to
+  zero if there is no mapping service.
+
+  @param[in]  Nic           A pointer to the Network interface controller data.
+  @param[in]  MemAddr       Virtual address to be mapped.
+  @param[in]  Size          Size of memory to be mapped.
+  @param[in]  Direction     Direction of data flow for this memory's usage:
+                            cpu->device, device->cpu or both ways.
+  @param[out] MappedAddr    Pointer to return the mapped device address.
+
+**/
+UINTN
+MapIt (
+  IN NIC_DATA  *Nic,
+  IN UINT64    MemAddr,
+  IN UINT32    Size,
+  IN UINT32    Direction,
+  OUT UINT64   MappedAddr
+  )
+{
+  UINT64  *PhyAddr;
+
+  PhyAddr = (UINT64 *)(UINTN)MappedAddr;
+
+  if (Nic->PxeStart.Map_Mem == 0) {
+    *PhyAddr = MemAddr;
+  } else {
+    ((void (*)(UINT64, UINT64, UINT32, UINT32, UINT64))(UINTN) Nic->PxeStart.Map_Mem)(
+  Nic->PxeStart.Unique_ID,
+  MemAddr,
+  Size,
+  Direction,
+  MappedAddr
+  );
+  }
+
+  return PXE_STATCODE_SUCCESS;
+}
+
+/**
+  Un-map previously mapped virtual memory address. This field can be set
+  to zero only if the Map_Mem() service is also set to zero.
+
+  @param[in]  Nic           A pointer to the Network interface controller data.
+  @param[in]  MemAddr       Virtual address to be mapped.
+  @param[in]  Size          Size of memory to be mapped.
+  @param[in]  Direction     Direction of data flow for this memory's usage:
+                            cpu->device, device->cpu or both ways.
+  @param[in]  MappedAddr    Pointer to return the mapped device address.
+
+**/
+VOID
+UnMapIt (
+  IN NIC_DATA  *Nic,
+  IN UINT64    MemAddr,
+  IN UINT32    Size,
+  IN UINT32    Direction,
+  IN UINT64    MappedAddr
+  )
+{
+  if (Nic->PxeStart.UnMap_Mem != 0) {
+    ((void (*)(UINT64, UINT64, UINT32, UINT32, UINT64))(UINTN) Nic->PxeStart.UnMap_Mem)(
+  Nic->PxeStart.Unique_ID,
+  MemAddr,
+  Size,
+  Direction,
+  MappedAddr
+  );
+  }
+
+  return;
+}
diff --git a/MdeModulePkg/Bus/Usb/UsbNetwork/UsbRndis/ComponentName.c b/MdeModulePkg/Bus/Usb/UsbNetwork/UsbRndis/ComponentName.c
new file mode 100644
index 0000000000..b9ba170c13
--- /dev/null
+++ b/MdeModulePkg/Bus/Usb/UsbNetwork/UsbRndis/ComponentName.c
@@ -0,0 +1,172 @@
+/** @file
+  This file contains code for USB RNDIS Driver Component
+  Name definitions
+
+  Copyright (c) 2023, American Megatrends International LLC. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include "UsbRndis.h"
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE  gUsbRndisDriverNameTable[] = {
+  {
+    "eng;en",
+    L"USB RNDIS Driver"
+  },
+  {
+    NULL,
+    NULL
+  }
+};
+
+EFI_STATUS
+EFIAPI
+UsbRndisComponentNameGetDriverName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,
+  IN  CHAR8                        *Language,
+  OUT CHAR16                       **DriverName
+  );
+
+EFI_STATUS
+EFIAPI
+UsbRndisComponentNameGetControllerName (
+  IN EFI_COMPONENT_NAME_PROTOCOL  *This,
+  IN EFI_HANDLE                   Controller,
+  IN EFI_HANDLE                   ChildHandle        OPTIONAL,
+  IN CHAR8                        *Language,
+  OUT CHAR16                      **ControllerName
+  );
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL  gUsbRndisComponentName = {
+  UsbRndisComponentNameGetDriverName,
+  UsbRndisComponentNameGetControllerName,
+  "eng"
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL  gUsbRndisComponentName2 = {
+  (EFI_COMPONENT_NAME2_GET_DRIVER_NAME)UsbRndisComponentNameGetDriverName,
+  (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME)UsbRndisComponentNameGetControllerName,
+  "en"
+};
+
+/**
+  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]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+                                EFI_COMPONENT_NAME_PROTOCOL instance.
+  @param[in]  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[out] 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
+UsbRndisComponentNameGetDriverName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,
+  IN  CHAR8                        *Language,
+  OUT CHAR16                       **DriverName
+  )
+{
+  return LookupUnicodeString2 (
+           Language,
+           This->SupportedLanguages,
+           gUsbRndisDriverNameTable,
+           DriverName,
+           (BOOLEAN)(This == &gUsbRndisComponentName)
+           );
+}
+
+/**
+  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]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+                                EFI_COMPONENT_NAME_PROTOCOL instance.
+  @param[in]  Controller        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]  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[out] 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
+UsbRndisComponentNameGetControllerName (
+  IN EFI_COMPONENT_NAME_PROTOCOL  *This,
+  IN EFI_HANDLE                   Controller,
+  IN EFI_HANDLE                   ChildHandle        OPTIONAL,
+  IN CHAR8                        *Language,
+  OUT CHAR16                      **ControllerName
+  )
+{
+  return EFI_UNSUPPORTED;
+}
diff --git a/MdeModulePkg/Bus/Usb/UsbNetwork/UsbRndis/UsbRndis.c b/MdeModulePkg/Bus/Usb/UsbNetwork/UsbRndis/UsbRndis.c
new file mode 100644
index 0000000000..9c74d73f78
--- /dev/null
+++ b/MdeModulePkg/Bus/Usb/UsbNetwork/UsbRndis/UsbRndis.c
@@ -0,0 +1,886 @@
+/** @file
+  This file contains code for USB Remote Network Driver
+  Interface Spec. Driver Binding
+
+  Copyright (c) 2023, American Megatrends International LLC. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include "UsbRndis.h"
+
+EFI_DRIVER_BINDING_PROTOCOL  gUsbRndisDriverBinding = {
+  UsbRndisDriverSupported,
+  UsbRndisDriverStart,
+  UsbRndisDriverStop,
+  USB_RNDIS_DRIVER_VERSION,
+  NULL,
+  NULL
+};
+
+/**
+  Check if this interface is USB Rndis SubType
+
+  @param[in]  UsbIo  A pointer to the EFI_USB_IO_PROTOCOL instance.
+
+  @retval TRUE   USB Rndis SubType.
+  @retval FALSE  Not USB Rndis SubType.
+
+**/
+BOOLEAN
+IsSupportedDevice (
+  IN EFI_USB_IO_PROTOCOL  *UsbIo
+  )
+{
+  EFI_STATUS                    Status;
+  EFI_USB_INTERFACE_DESCRIPTOR  InterfaceDescriptor;
+
+  Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &InterfaceDescriptor);
+  if (EFI_ERROR (Status)) {
+    return FALSE;
+  }
+
+  // Check specific device/RNDIS and CDC-DATA
+  if (((InterfaceDescriptor.InterfaceClass == 0x2) &&
+       (InterfaceDescriptor.InterfaceSubClass == 0x2) &&
+       (InterfaceDescriptor.InterfaceProtocol == 0xFF)) || \
+      ((InterfaceDescriptor.InterfaceClass == 0xEF) &&
+       (InterfaceDescriptor.InterfaceSubClass == 0x4) &&
+       (InterfaceDescriptor.InterfaceProtocol == 0x1)) || \
+      ((InterfaceDescriptor.InterfaceClass == 0xA) &&
+       (InterfaceDescriptor.InterfaceSubClass == 0x0) &&
+       (InterfaceDescriptor.InterfaceProtocol == 0x00))
+      )
+  {
+    return TRUE;
+  }
+
+  return FALSE;
+}
+
+/**
+  Check if this interface is USB Rndis SubType but not CDC Data interface
+
+  @param[in]  UsbIo  A pointer to the EFI_USB_IO_PROTOCOL instance.
+
+  @retval TRUE   USB Rndis SubType.
+  @retval FALSE  Not USB Rndis SubType.
+**/
+BOOLEAN
+IsRndisInterface (
+  IN EFI_USB_IO_PROTOCOL  *UsbIo
+  )
+{
+  EFI_STATUS                    Status;
+  EFI_USB_INTERFACE_DESCRIPTOR  InterfaceDescriptor;
+
+  Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &InterfaceDescriptor);
+  if (EFI_ERROR (Status)) {
+    return FALSE;
+  }
+
+  // Check for specific device/RNDIS and CDC-DATA
+  if (((InterfaceDescriptor.InterfaceClass == 0x2) &&
+       (InterfaceDescriptor.InterfaceSubClass == 0x2) &&
+       (InterfaceDescriptor.InterfaceProtocol == 0xFF)) || \
+      ((InterfaceDescriptor.InterfaceClass == 0xEF) &&
+       (InterfaceDescriptor.InterfaceSubClass == 0x4) &&
+       (InterfaceDescriptor.InterfaceProtocol == 0x1))
+      )
+  {
+    return TRUE;
+  }
+
+  return FALSE;
+}
+
+/**
+  Check if the USB RNDIS and USB CDC Data interfaces are from the same device.
+
+  @param[in]  UsbRndisDataPath  A pointer to the EFI_DEVICE_PATH_PROTOCOL instance.
+  @param[in]  UsbCdcDataPath    A pointer to the EFI_DEVICE_PATH_PROTOCOL instance.
+
+  @retval EFI_SUCCESS           Is the same device.
+  @retval EFI_UNSUPPORTED       Is not the same device.
+
+**/
+EFI_STATUS
+IsSameDevice (
+  IN  EFI_DEVICE_PATH_PROTOCOL  *UsbRndisDataPath,
+  IN  EFI_DEVICE_PATH_PROTOCOL  *UsbCdcDataPath
+  )
+{
+  DEBUG ((DEBUG_VERBOSE, "IsSameDevice Entry \n"));
+  while (1) {
+    if (IsDevicePathEnd (NextDevicePathNode (UsbRndisDataPath))) {
+      if (((USB_DEVICE_PATH *)UsbRndisDataPath)->ParentPortNumber ==
+          ((USB_DEVICE_PATH *)UsbCdcDataPath)->ParentPortNumber)
+      {
+        return EFI_SUCCESS;
+      } else {
+        return EFI_UNSUPPORTED;
+      }
+    } else {
+      if (CompareMem (UsbCdcDataPath, UsbRndisDataPath, sizeof (EFI_DEVICE_PATH_PROTOCOL)) != 0) {
+        return EFI_UNSUPPORTED;
+      }
+
+      UsbRndisDataPath = NextDevicePathNode (UsbRndisDataPath);
+      UsbCdcDataPath   = NextDevicePathNode (UsbCdcDataPath);
+    }
+  }
+
+  DEBUG ((DEBUG_VERBOSE, "IsSameDevice Exit \n"));
+}
+
+/**
+  Check if the USB CDC Data(UsbIo) installed and return USB CDC Data Handle.
+
+  @param[in]  UsbIo  A pointer to the EFI_USB_IO_PROTOCOL instance.
+
+  @retval TRUE              USB CDC Data(UsbIo) installed.
+  @retval FALSE             USB CDC Data(UsbIo) did not installed.
+
+**/
+BOOLEAN
+IsUsbCdcData (
+  IN EFI_USB_IO_PROTOCOL  *UsbIo
+  )
+{
+  EFI_STATUS                    Status;
+  EFI_USB_INTERFACE_DESCRIPTOR  InterfaceDescriptor;
+
+  Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &InterfaceDescriptor);
+  if (EFI_ERROR (Status)) {
+    return FALSE;
+  }
+
+  // Check for CDC-DATA
+  if ((InterfaceDescriptor.InterfaceClass == 0xA) &&
+      (InterfaceDescriptor.InterfaceSubClass == 0x0) &&
+      (InterfaceDescriptor.InterfaceProtocol == 0x0))
+  {
+    return TRUE;
+  }
+
+  return FALSE;
+}
+
+/**
+  Check if the USB Rndis(UsbIo) installed
+
+  @param[in]  UsbIo        A pointer to the EFI_USB_IO_PROTOCOL instance.
+
+  @retval TRUE              USB Rndis(UsbIo) installed.
+  @retval FALSE             USB Rndis(UsbIo) did not installed.
+
+**/
+BOOLEAN
+IsUsbRndis (
+  IN EFI_USB_IO_PROTOCOL  *UsbIo
+  )
+{
+  EFI_STATUS                    Status;
+  EFI_USB_INTERFACE_DESCRIPTOR  InterfaceDescriptor;
+
+  Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &InterfaceDescriptor);
+  if (EFI_ERROR (Status)) {
+    return FALSE;
+  }
+
+  // Check for Rndis
+  if ((InterfaceDescriptor.InterfaceClass == 0x2) &&
+      (InterfaceDescriptor.InterfaceSubClass == 0x2) &&
+      (InterfaceDescriptor.InterfaceProtocol == 0xFF))
+  {
+    return TRUE;
+  }
+
+  return FALSE;
+}
+
+/**
+  Control comes here when a CDC device is found.Check if a RNDIS interface is already found for this device or not.
+  For one device two USBIO will be installed each for CDC and RNDIS interface.
+
+  @param[in]  UsbEthPath        A pointer to the EFI_DEVICE_PATH_PROTOCOL instance.
+  @param[out] UsbRndisDevice    A pointer to the USB_RNDIS_DEVICE Data.
+
+  @retval EFI_SUCCESS             The USB_RNDIS_DEVICE matching this CDC Data is found.
+  @retval EFI_NOT_FOUND           The USB_RNDIS_DEVICE matching this CDC Data is not found.
+
+**/
+EFI_STATUS
+UpdateRndisDevice (
+  IN  EFI_DEVICE_PATH_PROTOCOL  *UsbCdcDataPath,
+  OUT USB_RNDIS_DEVICE          **UsbRndisDevice
+  )
+{
+  EFI_STATUS                   Status;
+  UINTN                        Index;
+  UINTN                        HandleCount;
+  EFI_HANDLE                   *HandleBuffer;
+  EDKII_USB_ETHERNET_PROTOCOL  *UsbEthDevice;
+  EFI_DEVICE_PATH_PROTOCOL     *UsbRndisDataPath;
+  EFI_USB_IO_PROTOCOL          *UsbIo;
+  BOOLEAN                      IsRndisInterfaceFlag;
+
+  IsRndisInterfaceFlag = FALSE;
+
+  Status = gBS->LocateHandleBuffer (
+                  ByProtocol,
+                  &gEdkIIUsbEthProtocolGuid,
+                  NULL,
+                  &HandleCount,
+                  &HandleBuffer
+                  );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  for (Index = 0; Index < HandleCount; Index++) {
+    Status = gBS->HandleProtocol (
+                    HandleBuffer[Index],
+                    &gEdkIIUsbEthProtocolGuid,
+                    (VOID **)&UsbEthDevice
+                    );
+    if (EFI_ERROR (Status)) {
+      continue;
+    }
+
+    Status = gBS->HandleProtocol (
+                    HandleBuffer[Index],
+                    &gEfiUsbIoProtocolGuid,
+                    (VOID **)&UsbIo
+                    );
+    if (EFI_ERROR (Status)) {
+      continue;
+    }
+
+    IsRndisInterfaceFlag = IsRndisInterface (UsbIo);
+    if (IsRndisInterfaceFlag == FALSE) {
+      continue;
+    }
+
+    Status = gBS->HandleProtocol (
+                    HandleBuffer[Index],
+                    &gEfiDevicePathProtocolGuid,
+                    (VOID **)&UsbRndisDataPath
+                    );
+    if (EFI_ERROR (Status)) {
+      continue;
+    }
+
+    Status = IsSameDevice (UsbRndisDataPath, UsbCdcDataPath);
+
+    DEBUG ((DEBUG_VERBOSE, "Rndis IsSameDevice %r\n", Status));
+
+    if (!EFI_ERROR (Status)) {
+      *UsbRndisDevice = USB_RNDIS_DEVICE_FROM_THIS (UsbEthDevice);
+      FreePool (HandleBuffer);
+      return EFI_SUCCESS;
+    }
+  }   // End of For loop
+
+  FreePool (HandleBuffer);
+  return EFI_NOT_FOUND;
+}
+
+/**
+
+  For the given Rndis Device, find a matching CDC device already exists or not. If found update the handle
+  and UsbIO protocol.
+
+  @param[in]  UsbRndisDevice        A pointer to the USB_RNDIS_DEVICE data.
+
+**/
+VOID
+FindMatchingCdcData (
+  IN USB_RNDIS_DEVICE  *UsbRndisDevice
+  )
+{
+  EFI_STATUS                Status;
+  UINTN                     Index;
+  UINTN                     HandleCount;
+  EFI_HANDLE                *HandleBuffer;
+  EFI_USB_IO_PROTOCOL       *UsbIo;
+  EFI_DEVICE_PATH_PROTOCOL  *UsbRndisDataPath;
+  EFI_DEVICE_PATH_PROTOCOL  *UsbCdcDataPath;
+
+  // Find the parent RNDIS and update the UsbIo for the CDC device
+  Status = gBS->HandleProtocol (
+                  UsbRndisDevice->UsbRndisHandle,
+                  &gEfiDevicePathProtocolGuid,
+                  (VOID **)&UsbRndisDataPath
+                  );
+
+  if (EFI_ERROR (Status)) {
+    return;
+  }
+
+  Status = gBS->LocateHandleBuffer (
+                  ByProtocol,
+                  &gEfiUsbIoProtocolGuid,
+                  NULL,
+                  &HandleCount,
+                  &HandleBuffer
+                  );
+  if (EFI_ERROR (Status)) {
+    return;
+  }
+
+  for (Index = 0; Index < HandleCount; Index++) {
+    Status = gBS->HandleProtocol (
+                    HandleBuffer[Index],
+                    &gEfiUsbIoProtocolGuid,
+                    (VOID **)&UsbIo
+                    );
+    ASSERT_EFI_ERROR (Status);
+
+    if (IsUsbCdcData (UsbIo)) {
+      DEBUG ((DEBUG_VERBOSE, "Rndis FindMatchingCdcData CDCData interface found\n"));
+
+      Status = gBS->HandleProtocol (
+                      HandleBuffer[Index],
+                      &gEfiDevicePathProtocolGuid,
+                      (VOID **)&UsbCdcDataPath
+                      );
+      if (EFI_ERROR (Status)) {
+        DEBUG ((DEBUG_VERBOSE, "Rndis CDCData DevicePath not found\n"));
+        FreePool (HandleBuffer);
+        return;
+      }
+
+      Status = IsSameDevice (UsbRndisDataPath, UsbCdcDataPath);
+      DEBUG ((DEBUG_VERBOSE, "Rndis IsSameDevice %r\n", Status));
+      if (!EFI_ERROR (Status)) {
+        UsbRndisDevice->UsbCdcDataHandle = HandleBuffer[Index];
+        UsbRndisDevice->UsbIoCdcData     = UsbIo;
+        GetEndpoint (UsbRndisDevice->UsbIoCdcData, UsbRndisDevice);
+        FreePool (HandleBuffer);
+        return;
+      }
+    }
+  }   // End of For loop
+
+  FreePool (HandleBuffer);
+}
+
+/**
+
+  For the given UsbIo CdcData, find a matching RNDIS device already exists or not.
+
+  @param[in]  CdcHandle       A pointer to the EFI_HANDLE for USB CDC Data.
+  @param[out] CdcUsbIo        A pointer for retrieve the EFI_USB_IO_PROTOCOL instance.
+  @param[out] RndisHandle     A pointer for retrieve the handle of RNDIS device.
+
+  @retval EFI_SUCCESS             The USB_RNDIS_DEVICE matching this CDC Data is found.
+  @retval EFI_NOT_FOUND           The USB_RNDIS_DEVICE matching this CDC Data is not found.
+
+**/
+EFI_STATUS
+EFIAPI
+FindMatchingRndisDev (
+  IN  EFI_HANDLE           CdcHandle,
+  OUT EFI_USB_IO_PROTOCOL  **CdcUsbIo,
+  OUT EFI_HANDLE           *RndisHandle
+  )
+{
+  EFI_STATUS                Status;
+  UINTN                     Index;
+  UINTN                     HandleCount;
+  EFI_HANDLE                *HandleBuffer;
+  EFI_USB_IO_PROTOCOL       *UsbIo;
+  EFI_DEVICE_PATH_PROTOCOL  *UsbRndisDataPath;
+  EFI_DEVICE_PATH_PROTOCOL  *UsbCdcDataPath;
+
+  // Find the parent RNDIS and update the UsbIo for the CDC device
+  Status = gBS->HandleProtocol (
+                  CdcHandle,
+                  &gEfiDevicePathProtocolGuid,
+                  (VOID **)&UsbCdcDataPath
+                  );
+
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = gBS->LocateHandleBuffer (
+                  ByProtocol,
+                  &gEfiUsbIoProtocolGuid,
+                  NULL,
+                  &HandleCount,
+                  &HandleBuffer
+                  );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  for (Index = 0; Index < HandleCount; Index++) {
+    Status = gBS->HandleProtocol (
+                    HandleBuffer[Index],
+                    &gEfiUsbIoProtocolGuid,
+                    (VOID **)&UsbIo
+                    );
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+
+    if (IsUsbRndis (UsbIo)) {
+      Status = gBS->HandleProtocol (
+                      HandleBuffer[Index],
+                      &gEfiDevicePathProtocolGuid,
+                      (VOID **)&UsbRndisDataPath
+                      );
+      if (EFI_ERROR (Status)) {
+        DEBUG ((DEBUG_ERROR, "Usb Rndis DevicePath not found\n"));
+        break;
+      }
+
+      Status = IsSameDevice (UsbRndisDataPath, UsbCdcDataPath);
+
+      if (!EFI_ERROR (Status)) {
+        *RndisHandle = HandleBuffer[Index];
+        *CdcUsbIo    = UsbIo;
+        FreePool (HandleBuffer);
+        return Status;
+      }
+    }
+  }   // End of For loop
+
+  FreePool (HandleBuffer);
+
+  return EFI_NOT_FOUND;
+}
+
+/**
+  USB Rndis Driver Binding Support.
+
+  @param[in]  This                    Protocol instance pointer.
+  @param[in]  ControllerHandle        Handle of device to test.
+  @param[in]  RemainingDevicePath     Optional parameter use to pick a specific child
+                                      device to start.
+
+  @retval EFI_SUCCESS                 This driver supports this device.
+  @retval EFI_ALREADY_STARTED         This driver is already running on this device.
+  @retval other                       This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+UsbRndisDriverSupported (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   ControllerHandle,
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
+  )
+{
+  EFI_STATUS           Status;
+  EFI_USB_IO_PROTOCOL  *UsbIo;
+
+  Status = gBS->OpenProtocol (
+                  ControllerHandle,
+                  &gEfiUsbIoProtocolGuid,
+                  (VOID **)&UsbIo,
+                  This->DriverBindingHandle,
+                  ControllerHandle,
+                  EFI_OPEN_PROTOCOL_BY_DRIVER
+                  );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = IsSupportedDevice (UsbIo) ? EFI_SUCCESS : EFI_UNSUPPORTED;
+
+  gBS->CloseProtocol (
+         ControllerHandle,
+         &gEfiUsbIoProtocolGuid,
+         This->DriverBindingHandle,
+         ControllerHandle
+         );
+  return Status;
+}
+
+/**
+  USB RNDIS Driver Binding Start.
+
+  @param[in]  This                    Protocol instance pointer.
+  @param[in]  ControllerHandle        Handle of device to bind driver to.
+  @param[in]  RemainingDevicePath     Optional parameter use to pick a specific child
+                                      device to start.
+
+  @retval EFI_SUCCESS                 This driver is added to ControllerHandle
+  @retval EFI_DEVICE_ERROR            This driver could not be started due to a device error
+  @retval EFI_OUT_OF_RESOURCES        The driver could not install successfully due to a lack of resources.
+  @retval other                       This driver does not support this device
+
+**/
+EFI_STATUS
+EFIAPI
+UsbRndisDriverStart (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   ControllerHandle,
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
+  )
+{
+  EFI_STATUS                    Status;
+  USB_RNDIS_DEVICE              *UsbRndisDevice;
+  EFI_DEVICE_PATH_PROTOCOL      *UsbEthPath;
+  EFI_USB_IO_PROTOCOL           *UsbIo;
+  EFI_USB_INTERFACE_DESCRIPTOR  Interface;
+  EFI_HANDLE                    RndisHandle;
+
+  RndisHandle = ControllerHandle;
+
+  Status = gBS->OpenProtocol (
+                  ControllerHandle,
+                  &gEfiUsbIoProtocolGuid,
+                  (VOID **)&UsbIo,
+                  This->DriverBindingHandle,
+                  ControllerHandle,
+                  EFI_OPEN_PROTOCOL_BY_DRIVER
+                  );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = gBS->OpenProtocol (
+                  ControllerHandle,
+                  &gEfiDevicePathProtocolGuid,
+                  (VOID **)&UsbEthPath,
+                  This->DriverBindingHandle,
+                  ControllerHandle,
+                  EFI_OPEN_PROTOCOL_BY_DRIVER
+                  );
+  if (EFI_ERROR (Status)) {
+    gBS->CloseProtocol (
+           ControllerHandle,
+           &gEfiUsbIoProtocolGuid,
+           This->DriverBindingHandle,
+           ControllerHandle
+           );
+    return Status;
+  }
+
+  gBS->CloseProtocol (
+         ControllerHandle,
+         &gEfiDevicePathProtocolGuid,
+         This->DriverBindingHandle,
+         ControllerHandle
+         );
+
+  // Controls come here for RNDIS and CDC. If it is CDC, check whether RNDIS is present on the same controller or not.
+  if (IsUsbCdcData (UsbIo)) {
+    DEBUG ((DEBUG_INFO, "Rndis CDCData interface found\n"));
+
+    // Find the parent RNDIS and update the UsbIo for the CDC device
+    Status = UpdateRndisDevice (
+               UsbEthPath,
+               &UsbRndisDevice
+               );
+
+    if (!EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_INFO, "Rndis Matching interface found\n"));
+      UsbRndisDevice->UsbCdcDataHandle = ControllerHandle;
+      UsbRndisDevice->UsbIoCdcData     = UsbIo;
+      GetEndpoint (UsbRndisDevice->UsbIoCdcData, UsbRndisDevice);
+      return Status;
+    } else {
+      // Check if RnDis exist
+      Status = FindMatchingRndisDev (
+                 ControllerHandle,
+                 &UsbIo,
+                 &RndisHandle
+                 );
+
+      if (EFI_ERROR (Status)) {
+        gBS->CloseProtocol (
+               ControllerHandle,
+               &gEfiUsbIoProtocolGuid,
+               This->DriverBindingHandle,
+               ControllerHandle
+               );
+        return Status;
+      }
+    }
+  }
+
+  UsbRndisDevice = AllocateZeroPool (sizeof (USB_RNDIS_DEVICE));
+
+  if (!UsbRndisDevice) {
+    DEBUG ((DEBUG_ERROR, "AllocateZeroPool Fail\n"));
+
+    gBS->CloseProtocol (
+           ControllerHandle,
+           &gEfiUsbIoProtocolGuid,
+           This->DriverBindingHandle,
+           ControllerHandle
+           );
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  Status = LoadAllDescriptor (
+             UsbIo,
+             &UsbRndisDevice->Config
+             );
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a:LoadAllDescriptor status = %r\n", __func__, Status));
+    gBS->CloseProtocol (
+           ControllerHandle,
+           &gEfiUsbIoProtocolGuid,
+           This->DriverBindingHandle,
+           ControllerHandle
+           );
+    FreePool (UsbRndisDevice);
+    return Status;
+  }
+
+  Status = UsbIo->UsbGetInterfaceDescriptor (
+                    UsbIo,
+                    &Interface
+                    );
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a:UsbGetInterfaceDescriptor status = %r\n", __func__, Status));
+    gBS->CloseProtocol (
+           ControllerHandle,
+           &gEfiUsbIoProtocolGuid,
+           This->DriverBindingHandle,
+           ControllerHandle
+           );
+    FreePool (UsbRndisDevice->Config);
+    FreePool (UsbRndisDevice);
+    return Status;
+  }
+
+  UsbRndisDevice->Signature                          = USB_RNDIS_SIGNATURE;
+  UsbRndisDevice->NumOfInterface                     = Interface.InterfaceNumber;
+  UsbRndisDevice->UsbRndisHandle                     = RndisHandle;
+  UsbRndisDevice->UsbCdcDataHandle                   = 0;
+  UsbRndisDevice->UsbIo                              = UsbIo;
+  UsbRndisDevice->UsbEth.UsbEthReceive               = RndisUndiReceive;
+  UsbRndisDevice->UsbEth.UsbEthTransmit              = RndisUndiTransmit;
+  UsbRndisDevice->UsbEth.UsbEthInterrupt             = UsbRndisInterrupt;
+  UsbRndisDevice->UsbEth.UsbEthMacAddress            = GetUsbEthMacAddress;
+  UsbRndisDevice->UsbEth.UsbEthMaxBulkSize           = UsbEthBulkSize;
+  UsbRndisDevice->UsbEth.UsbHeaderFunDescriptor      = GetUsbHeaderFunDescriptor;
+  UsbRndisDevice->UsbEth.UsbUnionFunDescriptor       = GetUsbUnionFunDescriptor;
+  UsbRndisDevice->UsbEth.UsbEthFunDescriptor         = GetUsbRndisFunDescriptor;
+  UsbRndisDevice->UsbEth.SetUsbEthMcastFilter        = SetUsbRndisMcastFilter;
+  UsbRndisDevice->UsbEth.SetUsbEthPowerPatternFilter = SetUsbRndisPowerFilter;
+  UsbRndisDevice->UsbEth.GetUsbEthPowerPatternFilter = GetUsbRndisPowerFilter;
+  UsbRndisDevice->UsbEth.SetUsbEthPacketFilter       = SetUsbRndisPacketFilter;
+  UsbRndisDevice->UsbEth.GetUsbEthStatistic          = GetRndisStatistic;
+
+  UsbRndisDevice->UsbEth.UsbEthUndi.UsbEthUndiGetState        = RndisDummyReturn;
+  UsbRndisDevice->UsbEth.UsbEthUndi.UsbEthUndiStart           = RndisUndiStart;
+  UsbRndisDevice->UsbEth.UsbEthUndi.UsbEthUndiStop            = RndisUndiStop;
+  UsbRndisDevice->UsbEth.UsbEthUndi.UsbEthUndiGetInitInfo     = RndisUndiGetInitInfo;
+  UsbRndisDevice->UsbEth.UsbEthUndi.UsbEthUndiGetConfigInfo   = RndisUndiGetConfigInfo;
+  UsbRndisDevice->UsbEth.UsbEthUndi.UsbEthUndiInitialize      = RndisUndiInitialize;
+  UsbRndisDevice->UsbEth.UsbEthUndi.UsbEthUndiReset           = RndisUndiReset;
+  UsbRndisDevice->UsbEth.UsbEthUndi.UsbEthUndiShutdown        = RndisUndiShutdown;
+  UsbRndisDevice->UsbEth.UsbEthUndi.UsbEthUndiInterruptEnable = RndisDummyReturn;
+  UsbRndisDevice->UsbEth.UsbEthUndi.UsbEthUndiReceiveFilter   = RndisUndiReceiveFilter;
+  UsbRndisDevice->UsbEth.UsbEthUndi.UsbEthUndiStationAddress  = RndisDummyReturn;
+  UsbRndisDevice->UsbEth.UsbEthUndi.UsbEthUndiStatistics      = NULL;
+  UsbRndisDevice->UsbEth.UsbEthUndi.UsbEthUndiMcastIp2Mac     = RndisDummyReturn;
+  UsbRndisDevice->UsbEth.UsbEthUndi.UsbEthUndiNvData          = RndisDummyReturn;
+  UsbRndisDevice->UsbEth.UsbEthUndi.UsbEthUndiGetStatus       = RndisUndiGetStatus;
+  UsbRndisDevice->UsbEth.UsbEthUndi.UsbEthUndiFillHeader      = RndisDummyReturn;
+  UsbRndisDevice->UsbEth.UsbEthUndi.UsbEthUndiTransmit        = NULL;
+  UsbRndisDevice->UsbEth.UsbEthUndi.UsbEthUndiReceive         = NULL;
+
+  UsbRndisDevice->MaxTransferSize       = RNDIS_MAX_TRANSFER_SIZE;
+  UsbRndisDevice->MaxPacketsPerTransfer = 1;
+  UsbRndisDevice->PacketAlignmentFactor = 0;
+
+  InitializeListHead (&UsbRndisDevice->ReceivePacketList);
+
+  // This is a RNDIS interface. See whether CDC-DATA interface has already been connected or not
+  FindMatchingCdcData (UsbRndisDevice);
+
+  if (UsbRndisDevice->UsbIoCdcData) {
+    Status = gBS->InstallProtocolInterface (
+                    &ControllerHandle,
+                    &gEdkIIUsbEthProtocolGuid,
+                    EFI_NATIVE_INTERFACE,
+                    &(UsbRndisDevice->UsbEth)
+                    );
+    if (EFI_ERROR (Status)) {
+      gBS->CloseProtocol (
+             ControllerHandle,
+             &gEfiUsbIoProtocolGuid,
+             This->DriverBindingHandle,
+             ControllerHandle
+             );
+
+      FreePool (UsbRndisDevice->Config);
+      FreePool (UsbRndisDevice);
+      return Status;
+    }
+
+    GetEndpoint (UsbRndisDevice->UsbIo, UsbRndisDevice);
+
+    DEBUG ((DEBUG_INFO, "Rndis DeviceHandle %r\n", UsbRndisDevice->UsbRndisHandle));
+    DEBUG ((DEBUG_INFO, "CDC DeviceHandle %r\n", UsbRndisDevice->UsbCdcDataHandle));
+    return EFI_SUCCESS;
+  }
+
+  FreePool (UsbRndisDevice->Config);
+  FreePool (UsbRndisDevice);
+
+  return EFI_SUCCESS;
+}
+
+/**
+  CheckandStopRndisDevice
+
+  @param[in]  This                 Protocol instance pointer.
+  @param[in]  ControllerHandle     Handle of device to bind driver to.
+
+  @retval EFI_SUCCESS          This driver is added to ControllerHandle
+  @retval EFI_DEVICE_ERROR     This driver could not be started due to a device error
+  @retval other                This driver does not support this device
+
+**/
+EFI_STATUS
+EFIAPI
+CheckandStopRndisDevice (
+  IN  EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN  EFI_HANDLE                   ControllerHandle
+  )
+{
+  EFI_STATUS           Status;
+  EFI_USB_IO_PROTOCOL  *UsbIo;
+
+  Status = gBS->OpenProtocol (
+                  ControllerHandle,
+                  &gEfiUsbIoProtocolGuid,
+                  (VOID **)&UsbIo,
+                  This->DriverBindingHandle,
+                  ControllerHandle,
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
+                  );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  if (IsUsbRndis (UsbIo)) {
+    Status = gBS->CloseProtocol (
+                    ControllerHandle,
+                    &gEfiUsbIoProtocolGuid,
+                    This->DriverBindingHandle,
+                    ControllerHandle
+                    );
+    DEBUG ((DEBUG_ERROR, "Rndis ControllerHandle Stop %r\n", Status));
+    return Status;
+  }
+
+  return EFI_UNSUPPORTED;
+}
+
+/**
+  USB Rndis Driver Binding Stop.
+
+  @param[in]  This                  Protocol instance pointer.
+  @param[in]  ControllerHandle      Handle of device to stop driver on
+  @param[in]  NumberOfChildren      Number of Handles in ChildHandleBuffer. If number of
+                                    children is zero stop the entire bus driver.
+  @param[in]  ChildHandleBuffer     List of Child Handles to Stop.
+
+  @retval EFI_SUCCESS               This driver is removed ControllerHandle
+  @retval other                     This driver was not removed from this device
+
+**/
+EFI_STATUS
+EFIAPI
+UsbRndisDriverStop (
+  IN  EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN  EFI_HANDLE                   ControllerHandle,
+  IN  UINTN                        NumberOfChildren,
+  IN  EFI_HANDLE                   *ChildHandleBuffer
+  )
+{
+  EFI_STATUS                   Status;
+  EDKII_USB_ETHERNET_PROTOCOL  *UsbEthProtocol;
+  USB_RNDIS_DEVICE             *UsbRndisDevice;
+
+  DEBUG ((DEBUG_INFO, "UsbRndisDriverStop ControllerHandle %lx\n", ControllerHandle));
+
+  Status = gBS->OpenProtocol (
+                  ControllerHandle,
+                  &gEdkIIUsbEthProtocolGuid,
+                  (VOID **)&UsbEthProtocol,
+                  This->DriverBindingHandle,
+                  ControllerHandle,
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
+                  );
+  if (EFI_ERROR (Status)) {
+    Status = CheckandStopRndisDevice (This, ControllerHandle);
+    return Status;
+  }
+
+  UsbRndisDevice = USB_RNDIS_DEVICE_FROM_THIS (UsbEthProtocol);
+
+  Status = gBS->CloseProtocol (
+                  UsbRndisDevice->UsbCdcDataHandle,
+                  &gEfiUsbIoProtocolGuid,
+                  This->DriverBindingHandle,
+                  UsbRndisDevice->UsbCdcDataHandle
+                  );
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a:CloseProtocol status = %r\n", __func__, Status));
+  }
+
+  Status = gBS->UninstallProtocolInterface (
+                  ControllerHandle,
+                  &gEdkIIUsbEthProtocolGuid,
+                  UsbEthProtocol
+                  );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = gBS->CloseProtocol (
+                  ControllerHandle,
+                  &gEfiUsbIoProtocolGuid,
+                  This->DriverBindingHandle,
+                  ControllerHandle
+                  );
+
+  FreePool (UsbRndisDevice->Config);
+  FreePool (UsbRndisDevice);
+
+  DEBUG ((DEBUG_INFO, "UsbRndisDriverStop %r\n", Status));
+  return Status;
+}
+
+/**
+  Entrypoint of RNDIS Driver.
+
+  This function is the entrypoint of RNDIS Driver. It installs Driver Binding
+  Protocols together with Component Name Protocols.
+
+  @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.
+
+**/
+EFI_STATUS
+EFIAPI
+UsbRndisEntry (
+  IN EFI_HANDLE        ImageHandle,
+  IN EFI_SYSTEM_TABLE  *SystemTable
+  )
+{
+  gUsbRndisDriverBinding.DriverBindingHandle = ImageHandle;
+  gUsbRndisDriverBinding.ImageHandle         = ImageHandle;
+
+  return gBS->InstallMultipleProtocolInterfaces (
+                &gUsbRndisDriverBinding.DriverBindingHandle,
+                &gEfiDriverBindingProtocolGuid,
+                &gUsbRndisDriverBinding,
+                &gEfiComponentName2ProtocolGuid,
+                &gUsbRndisComponentName2,
+                NULL
+                );
+}
diff --git a/MdeModulePkg/Bus/Usb/UsbNetwork/UsbRndis/UsbRndis.h b/MdeModulePkg/Bus/Usb/UsbNetwork/UsbRndis/UsbRndis.h
new file mode 100644
index 0000000000..9c38642ffe
--- /dev/null
+++ b/MdeModulePkg/Bus/Usb/UsbNetwork/UsbRndis/UsbRndis.h
@@ -0,0 +1,586 @@
+/** @file
+  Header file for for USB Rndis driver
+
+  Copyright (c) 2023, American Megatrends International LLC. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef _USB_RNDIS_H_
+#define _USB_RNDIS_H_
+
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/UefiUsbLib.h>
+#include <Protocol/UsbIo.h>
+#include <Protocol/UsbEthernetProtocol.h>
+
+typedef struct _REMOTE_NDIS_MSG_HEADER REMOTE_NDIS_MSG_HEADER;
+
+typedef struct {
+  UINT32                         Signature;
+  EDKII_USB_ETHERNET_PROTOCOL    UsbEth;
+  EFI_HANDLE                     UsbCdcDataHandle;
+  EFI_HANDLE                     UsbRndisHandle;
+  EFI_USB_IO_PROTOCOL            *UsbIo;
+  EFI_USB_IO_PROTOCOL            *UsbIoCdcData;
+  EFI_USB_CONFIG_DESCRIPTOR      *Config;
+  UINT8                          NumOfInterface;
+  UINT8                          BulkInEndpoint;
+  UINT8                          BulkOutEndpoint;
+  UINT8                          InterrupEndpoint;
+  EFI_MAC_ADDRESS                MacAddress;
+  UINT32                         RequestId;
+  UINT32                         Medium;
+  UINT32                         MaxPacketsPerTransfer;
+  UINT32                         MaxTransferSize;
+  UINT32                         PacketAlignmentFactor;
+  LIST_ENTRY                     ReceivePacketList;
+} USB_RNDIS_DEVICE;
+
+#define USB_RNDIS_DRIVER_VERSION       1
+#define USB_TX_ETHERNET_BULK_TIMEOUT   3000
+#define USB_RX_ETHERNET_BULK_TIMEOUT   3
+#define USB_ETHERNET_TRANSFER_TIMEOUT  200
+
+#define LAN_BULKIN_CMD_CONTROL      1
+#define MAXIMUM_STOPBULKIN_CNT      300        // Indicating maximum counts for waiting bulk in command
+#define MINIMUM_STOPBULKIN_CNT      3          // Indicating minimum counts for waiting bulk in command
+#define BULKIN_CMD_POLLING_CNT      300        // Indicating the waiting counts for send bulk in command when system pending
+#define RNDIS_RESERVED_BYTE_LENGTH  8
+
+#define USB_RNDIS_SIGNATURE  SIGNATURE_32('r', 'n', 'd', 's')
+#define USB_RNDIS_DEVICE_FROM_THIS(a)  CR (a, USB_RNDIS_DEVICE, UsbEth, USB_RNDIS_SIGNATURE)
+
+extern EFI_COMPONENT_NAME2_PROTOCOL  gUsbRndisComponentName2;
+
+struct BIT_MAP {
+  unsigned int    Src;
+  unsigned int    Dst;
+};
+
+EFI_STATUS
+EFIAPI
+UsbRndisDriverSupported (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   ControllerHandle,
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
+  );
+
+EFI_STATUS
+EFIAPI
+UsbRndisDriverStart (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   ControllerHandle,
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
+  );
+
+EFI_STATUS
+EFIAPI
+UsbRndisDriverStop (
+  IN  EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN  EFI_HANDLE                   ControllerHandle,
+  IN  UINTN                        NumberOfChildren,
+  IN  EFI_HANDLE                   *ChildHandleBuffer
+  );
+
+EFI_STATUS
+LoadAllDescriptor (
+  IN  EFI_USB_IO_PROTOCOL        *UsbIo,
+  OUT EFI_USB_CONFIG_DESCRIPTOR  **ConfigDesc
+  );
+
+BOOLEAN
+NextDescriptor (
+  IN EFI_USB_CONFIG_DESCRIPTOR  *Desc,
+  IN OUT UINTN                  *Offset
+  );
+
+EFI_STATUS
+GetFunctionalDescriptor (
+  IN  EFI_USB_CONFIG_DESCRIPTOR  *Config,
+  IN  UINT8                      FunDescriptorType,
+  OUT VOID                       *DataBuffer
+  );
+
+VOID
+GetEndpoint (
+  IN      EFI_USB_IO_PROTOCOL  *UsbIo,
+  IN OUT  USB_RNDIS_DEVICE     *UsbRndisDevice
+  );
+
+EFI_STATUS
+EFIAPI
+UsbRndisInterrupt (
+  IN EDKII_USB_ETHERNET_PROTOCOL  *This,
+  IN BOOLEAN                      IsNewTransfer,
+  IN UINTN                        PollingInterval,
+  IN EFI_USB_DEVICE_REQUEST       *Requst
+  );
+
+EFI_STATUS
+EFIAPI
+InterruptCallback (
+  IN  VOID    *Data,
+  IN  UINTN   DataLength,
+  IN  VOID    *Context,
+  IN  UINT32  Status
+  );
+
+EFI_STATUS
+EFIAPI
+GetUsbEthMacAddress (
+  IN  EDKII_USB_ETHERNET_PROTOCOL  *This,
+  OUT EFI_MAC_ADDRESS              *MacAddress
+  );
+
+EFI_STATUS
+EFIAPI
+UsbEthBulkSize (
+  IN  EDKII_USB_ETHERNET_PROTOCOL  *This,
+  OUT UINTN                        *BulkSize
+  );
+
+EFI_STATUS
+EFIAPI
+RndisDummyReturn (
+  IN  PXE_CDB   *Cdb,
+  IN  NIC_DATA  *Nic
+  );
+
+EFI_STATUS
+EFIAPI
+RndisUndiStart (
+  IN PXE_CDB   *Cdb,
+  IN NIC_DATA  *Nic
+  );
+
+EFI_STATUS
+EFIAPI
+RndisUndiStop (
+  IN PXE_CDB   *Cdb,
+  IN NIC_DATA  *Nic
+  );
+
+EFI_STATUS
+EFIAPI
+RndisUndiGetInitInfo (
+  IN PXE_CDB   *Cdb,
+  IN NIC_DATA  *Nic
+  );
+
+EFI_STATUS
+EFIAPI
+RndisUndiGetConfigInfo (
+  IN PXE_CDB   *Cdb,
+  IN NIC_DATA  *Nic
+  );
+
+EFI_STATUS
+EFIAPI
+RndisUndiInitialize (
+  IN PXE_CDB   *Cdb,
+  IN NIC_DATA  *Nic
+  );
+
+EFI_STATUS
+EFIAPI
+RndisUndiTransmit (
+  IN      PXE_CDB                      *Cdb,
+  IN      EDKII_USB_ETHERNET_PROTOCOL  *This,
+  IN      VOID                         *BulkOutData,
+  IN OUT  UINTN                        *DataLength
+  );
+
+EFI_STATUS
+EFIAPI
+RndisUndiReceive (
+  IN     PXE_CDB                      *Cdb,
+  IN     EDKII_USB_ETHERNET_PROTOCOL  *This,
+  IN OUT VOID                         *BulkInData,
+  IN OUT UINTN                        *DataLength
+  );
+
+EFI_STATUS
+EFIAPI
+RndisUndiReset (
+  IN PXE_CDB   *Cdb,
+  IN NIC_DATA  *Nic
+  );
+
+EFI_STATUS
+EFIAPI
+RndisUndiShutdown (
+  IN PXE_CDB   *Cdb,
+  IN NIC_DATA  *Nic
+  );
+
+EFI_STATUS
+EFIAPI
+RndisUndiReceiveFilter (
+  IN PXE_CDB   *Cdb,
+  IN NIC_DATA  *Nic
+  );
+
+EFI_STATUS
+EFIAPI
+RndisUndiGetStatus (
+  IN PXE_CDB   *Cdb,
+  IN NIC_DATA  *Nic
+  );
+
+EFI_STATUS
+EFIAPI
+GetUsbHeaderFunDescriptor (
+  IN  EDKII_USB_ETHERNET_PROTOCOL  *This,
+  OUT USB_HEADER_FUN_DESCRIPTOR    *UsbHeaderFunDescriptor
+  );
+
+EFI_STATUS
+EFIAPI
+GetUsbUnionFunDescriptor (
+  IN  EDKII_USB_ETHERNET_PROTOCOL  *This,
+  OUT USB_UNION_FUN_DESCRIPTOR     *UsbUnionFunDescriptor
+  );
+
+EFI_STATUS
+EFIAPI
+GetUsbRndisFunDescriptor (
+  IN  EDKII_USB_ETHERNET_PROTOCOL  *This,
+  OUT USB_ETHERNET_FUN_DESCRIPTOR  *UsbEthFunDescriptor
+  );
+
+EFI_STATUS
+EFIAPI
+SetUsbRndisMcastFilter (
+  IN EDKII_USB_ETHERNET_PROTOCOL  *This,
+  IN UINT16                       Value,
+  IN VOID                         *McastAddr
+  );
+
+EFI_STATUS
+EFIAPI
+SetUsbRndisPowerFilter (
+  IN EDKII_USB_ETHERNET_PROTOCOL  *This,
+  IN UINT16                       Value,
+  IN UINT16                       Length,
+  IN VOID                         *PatternFilter
+  );
+
+EFI_STATUS
+EFIAPI
+GetUsbRndisPowerFilter (
+  IN EDKII_USB_ETHERNET_PROTOCOL  *This,
+  IN UINT16                       Value,
+  IN BOOLEAN                      *PatternActive
+  );
+
+EFI_STATUS
+EFIAPI
+SetUsbRndisPacketFilter (
+  IN EDKII_USB_ETHERNET_PROTOCOL  *This,
+  IN UINT16                       Value
+  );
+
+EFI_STATUS
+EFIAPI
+GetRndisStatistic (
+  IN EDKII_USB_ETHERNET_PROTOCOL  *This,
+  IN UINT16                       Value,
+  IN VOID                         *Statistic
+  );
+
+EFI_STATUS
+RndisControlMsg (
+  IN  USB_RNDIS_DEVICE        *UsbRndisDevice,
+  IN  REMOTE_NDIS_MSG_HEADER  *RndisMsg,
+  OUT REMOTE_NDIS_MSG_HEADER  *RndisMsgResponse
+  );
+
+EFI_STATUS
+RndisTransmitDataMsg (
+  IN  USB_RNDIS_DEVICE        *UsbRndisDevice,
+  IN  REMOTE_NDIS_MSG_HEADER  *RndisMsg,
+  UINTN                       *TransferLength
+  );
+
+EFI_STATUS
+RndisReceiveDataMsg (
+  IN  USB_RNDIS_DEVICE        *UsbRndisDevice,
+  IN  REMOTE_NDIS_MSG_HEADER  *RndisMsg,
+  UINTN                       *TransferLength
+  );
+
+VOID
+PrintRndisMsg (
+  IN  REMOTE_NDIS_MSG_HEADER  *RndisMsg
+  );
+
+#define RNDIS_MAJOR_VERSION      0x00000001
+#define RNDIS_MINOR_VERSION      0x00000000
+#define RNDIS_MAX_TRANSFER_SIZE  0x4000
+
+#define RNDIS_PACKET_MSG           0x00000001
+#define RNDIS_INITIALIZE_MSG       0x00000002
+#define RNDIS_INITIALIZE_CMPLT     0x80000002
+#define RNDIS_HLT_MSG              0x00000003
+#define RNDIS_QUERY_MSG            0x00000004
+#define RNDIS_QUERY_CMPLT          0x80000004
+#define RNDIS_SET_MSG              0x00000005
+#define RNDIS_SET_CMPLT            0x80000005
+#define RNDIS_RESET_MSG            0x00000006
+#define RNDIS_RESET_CMPLT          0x80000006
+#define RNDIS_INDICATE_STATUS_MSG  0x00000007
+#define RNDIS_KEEPALIVE_MSG        0x00000008
+#define RNDIS_KEEPALIVE_CMPLT      0x80000008
+
+#define RNDIS_STATUS_SUCCESS           0x00000000
+#define RNDIS_STATUS_FAILURE           0xC0000001
+#define RNDIS_STATUS_INVALID_DATA      0xC0010015
+#define RNDIS_STATUS_NOT_SUPPORTED     0xC00000BB
+#define RNDIS_STATUS_MEDIA_CONNECT     0x4001000B
+#define RNDIS_STATUS_MEDIA_DISCONNECT  0x4001000C
+
+#define RNDIS_CONTROL_TIMEOUT    10000            // 10sec
+#define RNDIS_KEEPALIVE_TIMEOUT  5000             // 5sec
+
+#define SEND_ENCAPSULATED_COMMAND  0x00000000
+#define GET_ENCAPSULATED_RESPONSE  0x00000001
+
+//
+// General Objects
+//
+// Taken from NTDDNDIS.H
+#define OID_GEN_SUPPORTED_LIST         0x00010101
+#define OID_GEN_HARDWARE_STATUS        0x00010102
+#define OID_GEN_MEDIA_SUPPORTED        0x00010103
+#define OID_GEN_MEDIA_IN_USE           0x00010104
+#define OID_GEN_MAXIMUM_LOOKAHEAD      0x00010105
+#define OID_GEN_MAXIMUM_FRAME_SIZE     0x00010106
+#define OID_GEN_LINK_SPEED             0x00010107
+#define OID_GEN_TRANSMIT_BUFFER_SPACE  0x00010108
+#define OID_GEN_RECEIVE_BUFFER_SPACE   0x00010109
+#define OID_GEN_TRANSMIT_BLOCK_SIZE    0x0001010A
+#define OID_GEN_RECEIVE_BLOCK_SIZE     0x0001010B
+#define OID_GEN_VENDOR_ID              0x0001010C
+#define OID_GEN_VENDOR_DESCRIPTION     0x0001010D
+#define OID_GEN_CURRENT_PACKET_FILTER  0x0001010E
+#define OID_GEN_CURRENT_LOOKAHEAD      0x0001010F
+#define OID_GEN_DRIVER_VERSION         0x00010110
+#define OID_GEN_MAXIMUM_TOTAL_SIZE     0x00010111
+#define OID_GEN_PROTOCOL_OPTIONS       0x00010112
+#define OID_GEN_MAC_OPTIONS            0x00010113
+#define OID_GEN_MEDIA_CONNECT_STATUS   0x00010114
+#define OID_GEN_MAXIMUM_SEND_PACKETS   0x00010115
+#define OID_GEN_VENDOR_DRIVER_VERSION  0x00010116
+
+#define OID_GEN_XMIT_OK        0x00020101
+#define OID_GEN_RCV_OK         0x00020102
+#define OID_GEN_XMIT_ERROR     0x00020103
+#define OID_GEN_RCV_ERROR      0x00020104
+#define OID_GEN_RCV_NO_BUFFER  0x00020105
+
+#define OID_GEN_DIRECTED_BYTES_XMIT    0x00020201
+#define OID_GEN_DIRECTED_FRAMES_XMIT   0x00020202
+#define OID_GEN_MULTICAST_BYTES_XMIT   0x00020203
+#define OID_GEN_MULTICAST_FRAMES_XMIT  0x00020204
+#define OID_GEN_BROADCAST_BYTES_XMIT   0x00020205
+#define OID_GEN_BROADCAST_FRAMES_XMIT  0x00020206
+#define OID_GEN_DIRECTED_BYTES_RCV     0x00020207
+#define OID_GEN_DIRECTED_FRAMES_RCV    0x00020208
+#define OID_GEN_MULTICAST_BYTES_RCV    0x00020209
+#define OID_GEN_MULTICAST_FRAMES_RCV   0x0002020A
+#define OID_GEN_BROADCAST_BYTES_RCV    0x0002020B
+#define OID_GEN_BROADCAST_FRAMES_RCV   0x0002020C
+#define OID_GEN_RCV_CRC_ERROR          0x0002020D
+#define OID_GEN_TRANSMIT_QUEUE_LENGTH  0x0002020E
+
+#define OID_802_3_CURRENT_ADDRESS  0x01010102
+//
+// Ndis Packet Filter Bits (OID_GEN_CURRENT_PACKET_FILTER).
+//
+#define NDIS_PACKET_TYPE_DIRECTED        0x0001
+#define NDIS_PACKET_TYPE_MULTICAST       0x0002
+#define NDIS_PACKET_TYPE_ALL_MULTICAST   0x0004
+#define NDIS_PACKET_TYPE_BROADCAST       0x0008
+#define NDIS_PACKET_TYPE_SOURCE_ROUTING  0x0010
+#define NDIS_PACKET_TYPE_PROMISCUOUS     0x0020
+#define NDIS_PACKET_TYPE_SMT             0x0040
+#define NDIS_PACKET_TYPE_ALL_LOCAL       0x0080
+#define NDIS_PACKET_TYPE_MAC_FRAME       0x8000
+#define NDIS_PACKET_TYPE_FUNCTIONAL      0x4000
+#define NDIS_PACKET_TYPE_ALL_FUNCTIONAL  0x2000
+#define NDIS_PACKET_TYPE_GROUP           0x1000
+
+#pragma pack(1)
+
+typedef struct _REMOTE_NDIS_MSG_HEADER {
+  UINT32    MessageType;
+  UINT32    MessageLength;
+} REMOTE_NDIS_MSG_HEADER;
+
+typedef struct {
+  UINT32    MessageType;
+  UINT32    MessageLength;
+  UINT32    RequestID;
+  UINT32    MajorVersion;
+  UINT32    MinorVersion;
+  UINT32    MaxTransferSize;
+} REMOTE_NDIS_INITIALIZE_MSG;
+
+typedef struct {
+  UINT32    MessageType;
+  UINT32    MessageLength;
+  UINT32    RequestID;
+} REMOTE_NDIS_HALT_MSG;
+
+typedef struct {
+  UINT32    MessageType;
+  UINT32    MessageLength;
+  UINT32    RequestID;
+  UINT32    Oid;
+  UINT32    InformationBufferLength;
+  UINT32    InformationBufferOffset;
+  UINT32    Reserved;
+} REMOTE_NDIS_QUERY_MSG;
+
+typedef struct {
+  REMOTE_NDIS_QUERY_MSG    QueryMsg;
+  UINT8                    Addr[6];
+} REMOTE_NDIS_QUERY_MAC_MSG;
+
+typedef struct {
+  REMOTE_NDIS_QUERY_MSG    QueryMsg;
+  UINT32                   MaxTotalSize;
+} REMOTE_NDIS_QUERY_MAX_TOTAL_SIZE_MSG;
+
+typedef struct {
+  UINT32    MessageType;
+  UINT32    MessageLength;
+  UINT32    RequestID;
+  UINT32    Oid;
+  UINT32    InformationBufferLength;
+  UINT32    InformationBufferOffset;
+  UINT32    Reserved;
+} REMOTE_NDIS_SET_MSG;
+
+typedef struct {
+  UINT32    MessageType;
+  UINT32    MessageLength;
+  UINT32    Reserved;
+} REMOTE_NDIS_RESET_MSG;
+
+typedef struct {
+  UINT32    MessageType;
+  UINT32    MessageLength;
+  UINT32    Status;
+  UINT32    StatusBufferLength;
+  UINT32    StatusBufferOffset;
+} REMOTE_NDIS_INDICATE_STATUS_MSG;
+
+typedef struct {
+  UINT32    DiagStatus;
+  UINT32    ErrorOffset;
+} RNDIS_DIAGNOSTIC_INFO;
+
+typedef struct {
+  UINT32    MessageType;
+  UINT32    MessageLength;
+  UINT32    RequestID;
+} REMOTE_NDIS_KEEPALIVE_MSG;
+
+typedef struct {
+  UINT32    MessageType;
+  UINT32    MessageLength;
+  UINT32    RequestID;
+  UINT32    Status;
+  UINT32    MajorVersion;
+  UINT32    MinorVersion;
+  UINT32    DeviceFlags;
+  UINT32    Medium;
+  UINT32    MaxPacketsPerTransfer;
+  UINT32    MaxTransferSize;
+  UINT32    PacketAlignmentFactor;
+  UINT64    Reserved;
+} REMOTE_NDIS_INITIALIZE_CMPLT;
+
+typedef struct {
+  UINT32    MessageType;
+  UINT32    MessageLength;
+  UINT32    RequestID;
+  UINT32    Status;
+  UINT32    InformationBufferLength;
+  UINT32    InformationBufferOffset;
+} REMOTE_NDIS_QUERY_CMPLT;
+
+typedef struct {
+  REMOTE_NDIS_QUERY_CMPLT    QueryCmplt;
+  UINT8                      Addr[6];
+} REMOTE_NDIS_QUERY_MAC_CMPLT;
+
+typedef struct {
+  REMOTE_NDIS_QUERY_CMPLT    QueryCmplt;
+  UINT32                     MaxTotalSize;
+} REMOTE_NDIS_QUERY_MAX_TOTAL_SIZE_CMPLT;
+
+typedef struct {
+  UINT32    MessageType;
+  UINT32    MessageLength;
+  UINT32    RequestID;
+  UINT32    Status;
+} REMOTE_NDIS_SET_CMPLT;
+
+typedef struct {
+  UINT32    MessageType;
+  UINT32    MessageLength;
+  UINT32    Status;
+  UINT32    AddressingReset;
+} REMOTE_NDIS_RESET_CMPLT;
+
+typedef struct {
+  UINT32    MessageType;
+  UINT32    MessageLength;
+  UINT32    RequestID;
+  UINT32    Status;
+} REMOTE_NDIS_KEEPALIVE_CMPLT;
+
+typedef struct {
+  UINT32    MessageType;
+  UINT32    MessageLength;
+  UINT32    DataOffset;
+  UINT32    DataLength;
+  UINT32    OutOfBandDataOffset;
+  UINT32    OutOfBandDataLength;
+  UINT32    NumOutOfBandDataElements;
+  UINT32    PerPacketInfoOffset;
+  UINT32    PerPacketInfoLength;
+  UINT32    Reserved1;
+  UINT32    Reserved2;
+} REMOTE_NDIS_PACKET_MSG;
+
+typedef struct {
+  UINT32    Size;
+  UINT32    Type;
+  UINT32    ClassInformationOffset;
+} OUT_OF_BAND_DATA_RECORD;
+
+typedef struct {
+  UINT32    Size;
+  UINT32    Type;
+  UINT32    ClassInformationOffset;
+} PER_PACKET_INFO_DATA_RECORD;
+
+typedef struct {
+  LIST_ENTRY    PacketList;
+  UINT8         *OrgBuffer;
+  UINTN         RemainingLength;
+  UINT8         *PacketStartBuffer;    // Variable size data to follow
+} PACKET_LIST;
+
+#pragma pack()
+
+#endif
diff --git a/MdeModulePkg/Bus/Usb/UsbNetwork/UsbRndis/UsbRndis.inf b/MdeModulePkg/Bus/Usb/UsbNetwork/UsbRndis/UsbRndis.inf
new file mode 100644
index 0000000000..1027828527
--- /dev/null
+++ b/MdeModulePkg/Bus/Usb/UsbNetwork/UsbRndis/UsbRndis.inf
@@ -0,0 +1,42 @@
+## @file
+#   This is Usb Rndis driver for DXE phase.
+#
+# Copyright (c) 2023, American Megatrends International LLC. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = UsbRndis
+  FILE_GUID                      = 11E32C34-60B5-4991-8DEA-63D3E8C876DE
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = UsbRndisEntry
+
+[Sources]
+  UsbRndis.c
+  UsbRndis.h
+  UsbRndisFunction.c
+  ComponentName.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+
+[LibraryClasses]
+  UefiDriverEntryPoint
+  UefiBootServicesTableLib
+  UefiLib
+  DebugLib
+  UefiUsbLib
+  MemoryAllocationLib
+  BaseMemoryLib
+
+[Protocols]
+  gEfiUsbIoProtocolGuid
+  gEfiDevicePathProtocolGuid
+  gEfiDriverBindingProtocolGuid
+  gEdkIIUsbEthProtocolGuid
+
+[Depex]
+  TRUE
diff --git a/MdeModulePkg/Bus/Usb/UsbNetwork/UsbRndis/UsbRndisFunction.c b/MdeModulePkg/Bus/Usb/UsbNetwork/UsbRndis/UsbRndisFunction.c
new file mode 100644
index 0000000000..9aba3f1129
--- /dev/null
+++ b/MdeModulePkg/Bus/Usb/UsbNetwork/UsbRndis/UsbRndisFunction.c
@@ -0,0 +1,1718 @@
+/** @file
+  This file contains code for USB Ethernet descriptor
+  and specific requests implement.
+
+  Copyright (c) 2023, American Megatrends International LLC. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include "UsbRndis.h"
+
+UINT16  gStopBulkInCnt  = 0;
+UINT16  gBlockBulkInCnt = 0;
+
+/**
+  Load All of device descriptor.
+
+  @param[in]  UsbIo                 A pointer to the EFI_USB_IO_PROTOCOL instance.
+  @param[out] ConfigDesc            A pointer to the configuration descriptor.
+
+  @retval EFI_SUCCESS           The request executed successfully.
+  @retval EFI_OUT_OF_RESOURCES  The request could not be completed because the
+                                buffer specified by DescriptorLength and Descriptor
+                                is not large enough to hold the result of the request.
+  @retval EFI_TIMEOUT           A timeout occurred executing the request.
+  @retval EFI_DEVICE_ERROR      The request failed due to a device error. The transfer
+                                status is returned in Status.
+**/
+EFI_STATUS
+LoadAllDescriptor (
+  IN  EFI_USB_IO_PROTOCOL        *UsbIo,
+  OUT EFI_USB_CONFIG_DESCRIPTOR  **ConfigDesc
+  )
+{
+  EFI_STATUS                 Status;
+  UINT32                     TransStatus;
+  EFI_USB_CONFIG_DESCRIPTOR  Tmp;
+
+  Status = UsbIo->UsbGetConfigDescriptor (UsbIo, &Tmp);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a:UsbGetConfigDescriptor status = %r\n", __func__, Status));
+    return Status;
+  }
+
+  Status = gBS->AllocatePool (
+                  EfiBootServicesData,
+                  Tmp.TotalLength,
+                  (VOID **)ConfigDesc
+                  );
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a: AllocatePool status = %r\n", __func__, Status));
+    return Status;
+  }
+
+  Status = UsbGetDescriptor (
+             UsbIo,
+             USB_DESC_TYPE_CONFIG << 8 | (Tmp.ConfigurationValue - 1),                 // zero based
+             0,
+             Tmp.TotalLength,
+             *ConfigDesc,
+             &TransStatus
+             );
+  return Status;
+}
+
+/**
+  Returns pointer to the next descriptor for the pack of USB descriptors
+  located in continues memory segment
+
+  @param[in]      Desc   A pointer to the CONFIG_DESCRIPTOR instance.
+  @param[in, out] Offset A pointer to the sum of descriptor length.
+
+  @retval TRUE   The request executed successfully.
+  @retval FALSE  No next descriptor.
+
+**/
+BOOLEAN
+NextDescriptor (
+  IN      EFI_USB_CONFIG_DESCRIPTOR  *Desc,
+  IN OUT  UINTN                      *Offset
+  )
+{
+  if ((Desc == NULL) || (*Offset >= Desc->TotalLength)) {
+    return FALSE;
+  }
+
+  if (((EFI_USB_CONFIG_DESCRIPTOR *)((char *)Desc+*Offset))->Length == 0) {
+    return FALSE;
+  }
+
+  *Offset += ((EFI_USB_CONFIG_DESCRIPTOR *)((char *)Desc+*Offset))->Length;
+  if ( *Offset >= Desc->TotalLength ) {
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+/**
+  Read Function descriptor
+
+  @param[in]  Config             A pointer to all of configuration.
+  @param[in]  FunDescriptorType  USB CDC class descriptor SubType.
+  @param[out] DataBuffer         A pointer to the Data of corresponding to device capability.
+
+  @retval EFI_SUCCESS        The device capability descriptor was retrieved
+                             successfully.
+  @retval EFI_UNSUPPORTED    No supported.
+  @retval EFI_NOT_FOUND      The device capability descriptor was not found.
+
+**/
+EFI_STATUS
+GetFunctionalDescriptor (
+  IN  EFI_USB_CONFIG_DESCRIPTOR  *Config,
+  IN  UINT8                      FunDescriptorType,
+  OUT VOID                       *DataBuffer
+  )
+{
+  EFI_STATUS                    Status;
+  UINTN                         Offset;
+  EFI_USB_INTERFACE_DESCRIPTOR  *Interface;
+
+  Status = EFI_NOT_FOUND;
+
+  for (Offset = 0; NextDescriptor (Config, &Offset);) {
+    Interface = (EFI_USB_INTERFACE_DESCRIPTOR *)((UINT8 *)Config + Offset);
+    if (Interface->DescriptorType == CS_INTERFACE) {
+      if (((USB_HEADER_FUN_DESCRIPTOR *)Interface)->DescriptorSubtype == FunDescriptorType) {
+        switch (FunDescriptorType) {
+          case HEADER_FUN_DESCRIPTOR:
+            CopyMem (
+              DataBuffer,
+              (USB_HEADER_FUN_DESCRIPTOR *)Interface,
+              sizeof (USB_HEADER_FUN_DESCRIPTOR)
+              );
+            return EFI_SUCCESS;
+          case UNION_FUN_DESCRIPTOR:
+            CopyMem (
+              DataBuffer,
+              (USB_UNION_FUN_DESCRIPTOR *)Interface,
+              ((USB_UNION_FUN_DESCRIPTOR *)Interface)->FunctionLength
+              );
+            return EFI_SUCCESS;
+          case ETHERNET_FUN_DESCRIPTOR:
+            CopyMem (
+              DataBuffer,
+              (USB_ETHERNET_FUN_DESCRIPTOR *)Interface,
+              sizeof (USB_ETHERNET_FUN_DESCRIPTOR)
+              );
+            return EFI_SUCCESS;
+          default:
+            Status = EFI_UNSUPPORTED;
+            break;
+        }
+      }
+    }
+  }
+
+  return Status;
+}
+
+/**
+  Get USB Ethernet IO endpoint and USB CDC data IO endpoint.
+
+  @param[in]      UsbIo           A pointer to the EFI_USB_IO_PROTOCOL instance.
+  @param[in, out] UsbRndisDevice  A pointer to the USB_RNDIS_DEVICE instance.
+
+**/
+VOID
+GetEndpoint (
+  IN      EFI_USB_IO_PROTOCOL  *UsbIo,
+  IN OUT  USB_RNDIS_DEVICE     *UsbRndisDevice
+  )
+{
+  EFI_STATUS                    Status;
+  UINT8                         Index;
+  UINT32                        Result;
+  EFI_USB_INTERFACE_DESCRIPTOR  Interface;
+  EFI_USB_ENDPOINT_DESCRIPTOR   Endpoint;
+
+  Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &Interface);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a:UsbGetInterfaceDescriptor status = %r\n", __func__, Status));
+    return;
+  }
+
+  if (Interface.NumEndpoints == 0 ) {
+    Status = UsbSetInterface (UsbIo, 1, 0, &Result);
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_ERROR, "%a:UsbSetInterface status = %r\n", __func__, Status));
+      return;
+    }
+
+    Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &Interface);
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_ERROR, "%a:UsbGetInterfaceDescriptor status = %r\n", __func__, Status));
+      return;
+    }
+  }
+
+  for (Index = 0; Index < Interface.NumEndpoints; Index++) {
+    Status = UsbIo->UsbGetEndpointDescriptor (UsbIo, Index, &Endpoint);
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_ERROR, "%a:UsbGetEndpointDescriptor status = %r\n", __func__, Status));
+      return;
+    }
+
+    switch ((Endpoint.Attributes & (BIT0 | BIT1))) {
+      case USB_ENDPOINT_BULK:
+        if (Endpoint.EndpointAddress & BIT7) {
+          UsbRndisDevice->BulkInEndpoint = Endpoint.EndpointAddress;
+        } else {
+          UsbRndisDevice->BulkOutEndpoint = Endpoint.EndpointAddress;
+        }
+
+        break;
+      case USB_ENDPOINT_INTERRUPT:
+        UsbRndisDevice->InterrupEndpoint = Endpoint.EndpointAddress;
+        break;
+    }
+  }
+}
+
+/**
+  Async USB transfer callback routine.
+
+  @param[in]  Data            Data received or sent via the USB Asynchronous Transfer, if the
+                              transfer completed successfully.
+  @param[in]  DataLength      The length of Data received or sent via the Asynchronous
+                              Transfer, if transfer successfully completes.
+  @param[in]  Context         Data passed from UsbAsyncInterruptTransfer() request.
+  @param[in]  Status          Indicates the result of the asynchronous transfer.
+
+  @retval EFI_SUCCESS           The asynchronous USB transfer request has been successfully executed.
+  @retval EFI_DEVICE_ERROR      The asynchronous USB transfer request failed.
+
+**/
+EFI_STATUS
+EFIAPI
+InterruptCallback (
+  IN  VOID    *Data,
+  IN  UINTN   DataLength,
+  IN  VOID    *Context,
+  IN  UINT32  Status
+  )
+{
+  if ((Data == NULL) || (Context == NULL)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (((EFI_USB_DEVICE_REQUEST *)Data)->Request == 0) {
+    CopyMem (
+      (EFI_USB_DEVICE_REQUEST *)Context,
+      (EFI_USB_DEVICE_REQUEST *)Data,
+      sizeof (EFI_USB_DEVICE_REQUEST)
+      );
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  This function is used to manage a USB device with an interrupt transfer pipe.
+
+  @param[in]  This              A pointer to the EDKII_USB_ETHERNET_PROTOCOL instance.
+  @param[in]  IsNewTransfer     If TRUE, a new transfer will be submitted to USB controller. If
+                                FALSE, the interrupt transfer is deleted from the device's interrupt
+                                transfer queue.
+  @param[in]  PollingInterval   Indicates the periodic rate, in milliseconds, that the transfer is to be
+                                executed.This parameter is required when IsNewTransfer is TRUE. The
+                                value must be between 1 to 255, otherwise EFI_INVALID_PARAMETER is returned.
+                                The units are in milliseconds.
+  @param[in]  Request           A pointer to the EFI_USB_DEVICE_REQUEST data.
+
+  @retval EFI_SUCCESS           The asynchronous USB transfer request transfer has been successfully executed.
+  @retval EFI_DEVICE_ERROR      The asynchronous USB transfer request failed.
+
+**/
+EFI_STATUS
+EFIAPI
+UsbRndisInterrupt (
+  IN EDKII_USB_ETHERNET_PROTOCOL  *This,
+  IN BOOLEAN                      IsNewTransfer,
+  IN UINTN                        PollingInterval,
+  IN EFI_USB_DEVICE_REQUEST       *Requst
+  )
+{
+  EFI_STATUS        Status;
+  USB_RNDIS_DEVICE  *UsbRndisDevice;
+  UINTN             DataLength;
+
+  UsbRndisDevice = USB_RNDIS_DEVICE_FROM_THIS (This);
+  DataLength     = 0;
+
+  if (IsNewTransfer == TRUE) {
+    DataLength = sizeof (EFI_USB_DEVICE_REQUEST) + sizeof (USB_CONNECT_SPEED_CHANGE);
+    Status     = UsbRndisDevice->UsbIo->UsbAsyncInterruptTransfer (
+                                          UsbRndisDevice->UsbIo,
+                                          UsbRndisDevice->InterrupEndpoint,
+                                          IsNewTransfer,
+                                          PollingInterval,
+                                          DataLength,
+                                          InterruptCallback,
+                                          Requst
+                                          );
+
+    if (Status == EFI_INVALID_PARAMETER) {
+      // Because of Stacked AsyncInterrupt request are not supported
+      Status = UsbRndisDevice->UsbIo->UsbAsyncInterruptTransfer (
+                                        UsbRndisDevice->UsbIo,
+                                        UsbRndisDevice->InterrupEndpoint,
+                                        0,
+                                        0,
+                                        0,
+                                        NULL,
+                                        NULL
+                                        );
+    }
+  } else {
+    Status = UsbRndisDevice->UsbIo->UsbAsyncInterruptTransfer (
+                                      UsbRndisDevice->UsbIo,
+                                      UsbRndisDevice->InterrupEndpoint,
+                                      IsNewTransfer,
+                                      0,
+                                      0,
+                                      NULL,
+                                      NULL
+                                      );
+  }
+
+  return Status;
+}
+
+/**
+  This function is used to read USB interrupt transfer before the response RNDIS message.
+
+  @param[in]  This              A pointer to the USB_RNDIS_DEVICE instance.
+
+  @retval EFI_SUCCESS           The USB interrupt transfer has been successfully executed.
+  @retval EFI_DEVICE_ERROR      The USB interrupt transfer failed.
+
+**/
+EFI_STATUS
+EFIAPI
+ReadRndisResponseInterrupt (
+  IN USB_RNDIS_DEVICE  *UsbRndisDevice
+  )
+{
+  EFI_STATUS  Status;
+  UINT32      Data[2];
+  UINT32      UsbStatus;
+  UINTN       DataLength;
+
+  DataLength = 8;
+
+  ZeroMem (Data, sizeof (Data));
+
+  Status = UsbRndisDevice->UsbIo->UsbSyncInterruptTransfer (
+                                    UsbRndisDevice->UsbIo,
+                                    UsbRndisDevice->InterrupEndpoint,
+                                    &Data,
+                                    &DataLength,
+                                    0x20,
+                                    &UsbStatus
+                                    );
+
+  return Status;
+}
+
+/**
+  Retrieves the USB Ethernet Mac Address.
+
+  @param[in]  This          A pointer to the EDKII_USB_ETHERNET_PROTOCOL instance.
+  @param[out] MacAddress    A pointer to the caller allocated USB Ethernet Mac Address.
+
+  @retval EFI_SUCCESS           The USB Header Functional descriptor was retrieved successfully.
+  @retval EFI_INVALID_PARAMETER UsbHeaderFunDescriptor is NULL.
+  @retval EFI_NOT_FOUND         The USB Header Functional descriptor was not found.
+
+**/
+EFI_STATUS
+EFIAPI
+GetUsbEthMacAddress (
+  IN  EDKII_USB_ETHERNET_PROTOCOL  *This,
+  OUT EFI_MAC_ADDRESS              *MacAddress
+  )
+{
+  EFI_STATUS                   Status;
+  USB_RNDIS_DEVICE             *UsbRndisDevice;
+  USB_ETHERNET_FUN_DESCRIPTOR  UsbEthDescriptor;
+  CHAR16                       *Data;
+  CHAR16                       *DataPtr;
+  CHAR16                       TmpStr[1];
+  UINT8                        Index;
+  UINT8                        Hi;
+  UINT8                        Low;
+
+  REMOTE_NDIS_QUERY_MAC_MSG    RndisQueryMsg;
+  REMOTE_NDIS_QUERY_MAC_CMPLT  RndisQueryMsgCmplt;
+
+  UsbRndisDevice = USB_RNDIS_DEVICE_FROM_THIS (This);
+
+  ZeroMem (&RndisQueryMsg, sizeof (REMOTE_NDIS_QUERY_MAC_MSG));
+  ZeroMem (&RndisQueryMsgCmplt, sizeof (REMOTE_NDIS_QUERY_MAC_CMPLT));
+
+  RndisQueryMsg.QueryMsg.MessageType   = RNDIS_QUERY_MSG;
+  RndisQueryMsg.QueryMsg.MessageLength = sizeof (REMOTE_NDIS_QUERY_MAC_MSG);
+  RndisQueryMsg.QueryMsg.RequestID     = UsbRndisDevice->RequestId;
+  RndisQueryMsg.QueryMsg.Oid           = OID_802_3_CURRENT_ADDRESS;
+
+  RndisQueryMsgCmplt.QueryCmplt.MessageType   = RNDIS_QUERY_CMPLT;
+  RndisQueryMsgCmplt.QueryCmplt.MessageLength = sizeof (REMOTE_NDIS_QUERY_MAC_CMPLT);
+
+  Status = RndisControlMsg (
+             UsbRndisDevice,
+             (REMOTE_NDIS_MSG_HEADER *)&RndisQueryMsg,
+             (REMOTE_NDIS_MSG_HEADER *)&RndisQueryMsgCmplt
+             );
+  if (!EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_INFO, "Success to get Mac address from RNDIS message.\n"));
+    for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) {
+      MacAddress->Addr[Index] = RndisQueryMsgCmplt.Addr[Index];
+    }
+
+    UsbRndisDevice->RequestId++;
+    return Status;
+  }
+
+  // If it is not support the OID_802_3_CURRENT_ADDRESS.
+  // To check USB Ethernet functional Descriptor
+  Status = This->UsbEthFunDescriptor (This, &UsbEthDescriptor);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a:UsbEthFunDescriptor status = %r\n", __func__, Status));
+    return Status;
+  }
+
+  Status = UsbRndisDevice->UsbIo->UsbGetStringDescriptor (
+                                    UsbRndisDevice->UsbIo,
+                                    0x409,                       // English-US Language ID
+                                    UsbEthDescriptor.MacAddress,
+                                    &Data
+                                    );
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a:UsbGetStringDescriptor status = %r\n", __func__, Status));
+    return Status;
+  }
+
+  DataPtr = Data;
+  for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) {
+    CopyMem (TmpStr, DataPtr, sizeof (CHAR16));
+    DataPtr++;
+    Hi = (UINT8)StrHexToUintn (TmpStr);
+    CopyMem (TmpStr, DataPtr, sizeof (CHAR16));
+    DataPtr++;
+    Low                     = (UINT8)StrHexToUintn (TmpStr);
+    MacAddress->Addr[Index] = (Hi << 4) | Low;
+  }
+
+  return Status;
+}
+
+/**
+  Retrieves the USB Ethernet Bulk transfer data size.
+
+  @param[in]  This          A pointer to the EDKII_USB_ETHERNET_PROTOCOL instance.
+  @param[out] BulkSize      A pointer to the Bulk transfer data size.
+
+  @retval EFI_SUCCESS       The bulk transfer data size was retrieved successfully.
+  @retval other             Failed to retrieve the bulk transfer data size.
+
+**/
+EFI_STATUS
+EFIAPI
+UsbEthBulkSize (
+  IN  EDKII_USB_ETHERNET_PROTOCOL  *This,
+  OUT UINTN                        *BulkSize
+  )
+{
+  EFI_STATUS                   Status;
+  USB_ETHERNET_FUN_DESCRIPTOR  UsbEthFunDescriptor;
+  USB_RNDIS_DEVICE             *UsbRndisDevice;
+
+  REMOTE_NDIS_QUERY_MAX_TOTAL_SIZE_MSG    RndisQueryMsg;
+  REMOTE_NDIS_QUERY_MAX_TOTAL_SIZE_CMPLT  RndisQueryMsgCmplt;
+
+  UsbRndisDevice = USB_RNDIS_DEVICE_FROM_THIS (This);
+
+  ZeroMem (&RndisQueryMsg, sizeof (REMOTE_NDIS_QUERY_MAX_TOTAL_SIZE_MSG));
+  ZeroMem (&RndisQueryMsgCmplt, sizeof (REMOTE_NDIS_QUERY_MAX_TOTAL_SIZE_CMPLT));
+
+  RndisQueryMsg.QueryMsg.MessageType   = RNDIS_QUERY_MSG;
+  RndisQueryMsg.QueryMsg.MessageLength = sizeof (REMOTE_NDIS_QUERY_MAX_TOTAL_SIZE_MSG);
+  RndisQueryMsg.QueryMsg.RequestID     = UsbRndisDevice->RequestId;
+  RndisQueryMsg.QueryMsg.Oid           = OID_GEN_MAXIMUM_TOTAL_SIZE;
+
+  RndisQueryMsgCmplt.QueryCmplt.MessageType   = RNDIS_QUERY_CMPLT;
+  RndisQueryMsgCmplt.QueryCmplt.MessageLength = sizeof (REMOTE_NDIS_QUERY_MAX_TOTAL_SIZE_CMPLT);
+
+  Status = RndisControlMsg (
+             UsbRndisDevice,
+             (REMOTE_NDIS_MSG_HEADER *)&RndisQueryMsg,
+             (REMOTE_NDIS_MSG_HEADER *)&RndisQueryMsgCmplt
+             );
+  if (!EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_INFO, "Success to get Max Total size : %X  \n", RndisQueryMsgCmplt.MaxTotalSize));
+    *BulkSize = RndisQueryMsgCmplt.MaxTotalSize;
+    UsbRndisDevice->RequestId++;
+    return Status;
+  }
+
+  Status = This->UsbEthFunDescriptor (This, &UsbEthFunDescriptor);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  *BulkSize = (UINTN)UsbEthFunDescriptor.MaxSegmentSize;
+  return Status;
+}
+
+/**
+  Retrieves the USB Header functional Descriptor.
+
+  @param[in]  This                   A pointer to the EDKII_USB_ETHERNET_PROTOCOL instance.
+  @param[out] UsbHeaderFunDescriptor A pointer to the caller allocated USB Header Functional Descriptor.
+
+  @retval EFI_SUCCESS           The USB Header Functional descriptor was retrieved successfully.
+  @retval EFI_INVALID_PARAMETER UsbHeaderFunDescriptor is NULL.
+  @retval EFI_NOT_FOUND         The USB Header Functional descriptor was not found.
+
+**/
+EFI_STATUS
+EFIAPI
+GetUsbHeaderFunDescriptor (
+  IN  EDKII_USB_ETHERNET_PROTOCOL  *This,
+  OUT USB_HEADER_FUN_DESCRIPTOR    *UsbHeaderFunDescriptor
+  )
+{
+  EFI_STATUS        Status;
+  USB_RNDIS_DEVICE  *UsbRndisDevice;
+
+  UsbRndisDevice = USB_RNDIS_DEVICE_FROM_THIS (This);
+
+  if (UsbHeaderFunDescriptor == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Status = GetFunctionalDescriptor (
+             UsbRndisDevice->Config,
+             HEADER_FUN_DESCRIPTOR,
+             UsbHeaderFunDescriptor
+             );
+  return Status;
+}
+
+/**
+  Retrieves the USB Union functional Descriptor.
+
+  @param[in]  This                   A pointer to the EDKII_USB_ETHERNET_PROTOCOL instance.
+  @param[out] UsbUnionFunDescriptor  A pointer to the caller allocated USB Union Functional Descriptor.
+
+  @retval EFI_SUCCESS            The USB Union Functional descriptor was retrieved successfully.
+  @retval EFI_INVALID_PARAMETER  UsbUnionFunDescriptor is NULL.
+  @retval EFI_NOT_FOUND          The USB Union Functional descriptor was not found.
+
+**/
+EFI_STATUS
+EFIAPI
+GetUsbUnionFunDescriptor (
+  IN  EDKII_USB_ETHERNET_PROTOCOL  *This,
+  OUT USB_UNION_FUN_DESCRIPTOR     *UsbUnionFunDescriptor
+  )
+{
+  EFI_STATUS        Status;
+  USB_RNDIS_DEVICE  *UsbRndisDevice;
+
+  UsbRndisDevice = USB_RNDIS_DEVICE_FROM_THIS (This);
+
+  if (UsbUnionFunDescriptor == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Status = GetFunctionalDescriptor (
+             UsbRndisDevice->Config,
+             UNION_FUN_DESCRIPTOR,
+             UsbUnionFunDescriptor
+             );
+  return Status;
+}
+
+/**
+  Retrieves the USB Ethernet functional Descriptor.
+
+  This function get the Mac Address, Ethernet statistics, maximum segment size,
+  number of multicast filters, and number of pattern filters from Ethernet
+  functional Descriptor.
+
+  @param[in]  This                   A pointer to the EDKII_USB_ETHERNET_PROTOCOL instance.
+  @param[out] UsbEthFunDescriptor    A pointer to the caller allocated USB Ethernet Functional Descriptor.
+
+  @retval EFI_SUCCESS            The USB Ethernet Functional descriptor was retrieved successfully.
+  @retval EFI_INVALID_PARAMETER  UsbEthFunDescriptor is NULL.
+  @retval EFI_NOT_FOUND          The USB Ethernet Functional descriptor was not found.
+
+**/
+EFI_STATUS
+EFIAPI
+GetUsbRndisFunDescriptor (
+  IN  EDKII_USB_ETHERNET_PROTOCOL  *This,
+  OUT USB_ETHERNET_FUN_DESCRIPTOR  *UsbEthFunDescriptor
+  )
+{
+  EFI_STATUS        Status;
+  USB_RNDIS_DEVICE  *UsbRndisDevice;
+
+  UsbRndisDevice = USB_RNDIS_DEVICE_FROM_THIS (This);
+
+  if (UsbEthFunDescriptor == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Status = GetFunctionalDescriptor (
+             UsbRndisDevice->Config,
+             ETHERNET_FUN_DESCRIPTOR,
+             UsbEthFunDescriptor
+             );
+  return Status;
+}
+
+/**
+  This request sets the Ethernet device multicast filters as specified in the
+  sequential list of 48 bit Ethernet multicast addresses.
+
+  @param[in]  This                   A pointer to the EDKII_USB_ETHERNET_PROTOCOL instance.
+  @param[in]  Value                  Number of filters.
+  @param[in]  McastAddr              A pointer to the value of the multicast addresses.
+
+  @retval EFI_SUCCESS            The request executed successfully.
+  @retval EFI_TIMEOUT            A timeout occurred executing the request.
+  @retval EFI_DEVICE_ERROR       The request failed due to a device error.
+  @retval EFI_INVALID_PARAMETER  One of the parameters has an invalid value.
+  @retval EFI_UNSUPPORTED        Not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+SetUsbRndisMcastFilter (
+  IN EDKII_USB_ETHERNET_PROTOCOL  *This,
+  IN UINT16                       Value,
+  IN VOID                         *McastAddr
+  )
+{
+  EFI_STATUS                   Status;
+  EFI_USB_DEVICE_REQUEST       Request;
+  UINT32                       TransStatus;
+  USB_ETHERNET_FUN_DESCRIPTOR  UsbEthFunDescriptor;
+  USB_RNDIS_DEVICE             *UsbRndisDevice;
+
+  UsbRndisDevice = USB_RNDIS_DEVICE_FROM_THIS (This);
+
+  Status = This->UsbEthFunDescriptor (This, &UsbEthFunDescriptor);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  if ((UsbEthFunDescriptor.NumberMcFilters << 1) == 0) {
+    return EFI_UNSUPPORTED;
+  }
+
+  Request.RequestType = USB_ETHERNET_SET_REQ_TYPE;
+  Request.Request     = SET_ETH_MULTICAST_FILTERS_REQ;
+  Request.Value       = Value;
+  Request.Index       = UsbRndisDevice->NumOfInterface;
+  Request.Length      = Value * 6;
+
+  return UsbRndisDevice->UsbIo->UsbControlTransfer (
+                                  UsbRndisDevice->UsbIo,
+                                  &Request,
+                                  EfiUsbDataOut,
+                                  USB_ETHERNET_TRANSFER_TIMEOUT,
+                                  McastAddr,
+                                  Request.Length,
+                                  &TransStatus
+                                  );
+}
+
+/**
+  This request sets up the specified Ethernet power management pattern filter as
+  described in the data structure.
+
+  @param[in]  This                  A pointer to the EDKII_USB_ETHERNET_PROTOCOL instance.
+  @param[in]  Value                 Number of filters.
+  @param[in]  Length                Size of the power management pattern filter data.
+  @param[in]  PatternFilter         A pointer to the power management pattern filter structure.
+
+  @retval EFI_SUCCESS            The request executed successfully.
+  @retval EFI_TIMEOUT            A timeout occurred executing the request.
+  @retval EFI_DEVICE_ERROR       The request failed due to a device error.
+  @retval EFI_INVALID_PARAMETER  One of the parameters has an invalid value.
+  @retval EFI_UNSUPPORTED        Not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+SetUsbRndisPowerFilter (
+  IN EDKII_USB_ETHERNET_PROTOCOL  *This,
+  IN UINT16                       Value,
+  IN UINT16                       Length,
+  IN VOID                         *PatternFilter
+  )
+{
+  EFI_USB_DEVICE_REQUEST  Request;
+  UINT32                  TransStatus;
+  USB_RNDIS_DEVICE        *UsbRndisDevice;
+
+  UsbRndisDevice = USB_RNDIS_DEVICE_FROM_THIS (This);
+
+  Request.RequestType = USB_ETHERNET_SET_REQ_TYPE;
+  Request.Request     = SET_ETH_POWER_MANAGEMENT_PATTERN_FILTER_REQ;
+  Request.Value       = Value;
+  Request.Index       = UsbRndisDevice->NumOfInterface;
+  Request.Length      = Length;
+
+  return UsbRndisDevice->UsbIo->UsbControlTransfer (
+                                  UsbRndisDevice->UsbIo,
+                                  &Request,
+                                  EfiUsbDataOut,
+                                  USB_ETHERNET_TRANSFER_TIMEOUT,
+                                  PatternFilter,
+                                  Length,
+                                  &TransStatus
+                                  );
+}
+
+/**
+  This request retrieves the status of the specified Ethernet power management
+  pattern filter from the device.
+
+  @param[in]  This                   A pointer to the EDKII_USB_ETHERNET_PROTOCOL instance.
+  @param[in]  Value                  The filter number.
+  @param[out] PatternActive          A pointer to the pattern active boolean.
+
+  @retval EFI_SUCCESS            The request executed successfully.
+  @retval EFI_TIMEOUT            A timeout occurred executing the request.
+  @retval EFI_DEVICE_ERROR       The request failed due to a device error.
+  @retval EFI_INVALID_PARAMETER  One of the parameters has an invalid value.
+  @retval EFI_UNSUPPORTED        Not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+GetUsbRndisPowerFilter (
+  IN  EDKII_USB_ETHERNET_PROTOCOL  *This,
+  IN  UINT16                       Value,
+  OUT BOOLEAN                      *PatternActive
+  )
+{
+  EFI_USB_DEVICE_REQUEST  Request;
+  UINT32                  TransStatus;
+  USB_RNDIS_DEVICE        *UsbRndisDevice;
+
+  UsbRndisDevice = USB_RNDIS_DEVICE_FROM_THIS (This);
+
+  Request.RequestType = USB_ETHERNET_GET_REQ_TYPE;
+  Request.Request     = GET_ETH_POWER_MANAGEMENT_PATTERN_FILTER_REQ;
+  Request.Value       = Value;
+  Request.Index       = UsbRndisDevice->NumOfInterface;
+  Request.Length      = USB_ETH_POWER_FILTER_LENGTH;
+
+  return UsbRndisDevice->UsbIo->UsbControlTransfer (
+                                  UsbRndisDevice->UsbIo,
+                                  &Request,
+                                  EfiUsbDataIn,
+                                  USB_ETHERNET_TRANSFER_TIMEOUT,
+                                  PatternActive,
+                                  USB_ETH_POWER_FILTER_LENGTH,
+                                  &TransStatus
+                                  );
+}
+
+/**
+
+  Converts PXE filter settings to RNDIS values
+
+  @param[in]  Value      PXE filter data.
+  @param[out] CdcFilter  A pointer to the Ethernet Packet Filter Bitmap value converted by PXE_OPFLAGS.
+
+**/
+VOID
+ConvertFilter (
+  IN  UINT16  Value,
+  OUT UINT16  *CdcFilter
+  )
+{
+  UINT32                 Index;
+  UINT32                 Count;
+  static struct BIT_MAP  Table[] = {
+    { PXE_OPFLAGS_RECEIVE_FILTER_UNICAST,            NDIS_PACKET_TYPE_DIRECTED      },
+    { PXE_OPFLAGS_RECEIVE_FILTER_BROADCAST,          NDIS_PACKET_TYPE_BROADCAST     },
+    { PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST, NDIS_PACKET_TYPE_MULTICAST     },
+    { PXE_OPFLAGS_RECEIVE_FILTER_PROMISCUOUS,        NDIS_PACKET_TYPE_PROMISCUOUS   },
+    { PXE_OPFLAGS_RECEIVE_FILTER_ALL_MULTICAST,      NDIS_PACKET_TYPE_ALL_MULTICAST },
+  };
+
+  Count = sizeof (Table)/sizeof (Table[0]);
+
+  for (Index = 0; (Table[Index].Src != 0) && (Index < Count); Index++) {
+    if (Table[Index].Src & Value) {
+      *CdcFilter |= Table[Index].Dst;
+    }
+  }
+}
+
+/**
+
+  Updates Filter settings on the device.
+
+  @param[in]  Cdb  A pointer to the command descriptor block.
+  @param[in]  Nic  A pointer to the Network interface controller data.
+
+  @retval EFI_STATUS
+
+**/
+EFI_STATUS
+EFIAPI
+RndisUndiReceiveFilter (
+  IN  PXE_CDB   *Cdb,
+  IN  NIC_DATA  *Nic
+  )
+{
+  EFI_STATUS                   Status;
+  UINT8                        *McastList;
+  UINT8                        Count;
+  UINT8                        Index1;
+  UINT8                        Index2;
+  UINT64                       CpbAddr;
+  UINT32                       CpbSize;
+  UINT16                       SetFilter;
+  PXE_CPB_RECEIVE_FILTERS      *Cpb;
+  USB_ETHERNET_FUN_DESCRIPTOR  UsbEthFunDescriptor;
+
+  Count     = 0;
+  CpbAddr   = Cdb->CPBaddr;
+  CpbSize   = Cdb->CPBsize;
+  SetFilter = (UINT16)(Cdb->OpFlags & 0x1F);
+  Cpb       = (PXE_CPB_RECEIVE_FILTERS *)(UINTN)CpbAddr;
+
+  // The Cpb could be NULL.(ref:PXE_CPBADDR_NOT_USED)
+  Nic->RxFilter = (UINT8)SetFilter;
+
+  if (((SetFilter & PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST) != 0) || (Cpb != NULL)) {
+    if (Cpb != NULL) {
+      Nic->McastCount = (UINT8)(CpbSize / PXE_MAC_LENGTH);
+      CopyMem (&Nic->McastList, Cpb, Nic->McastCount);
+    } else {
+      Nic->McastCount = 0;
+    }
+
+    Nic->UsbEth->UsbEthFunDescriptor (Nic->UsbEth, &UsbEthFunDescriptor);
+    if ((UsbEthFunDescriptor.NumberMcFilters << 1) == 0) {
+      Nic->RxFilter |= PXE_OPFLAGS_RECEIVE_FILTER_ALL_MULTICAST;
+      DEBUG ((DEBUG_INFO, "SetUsbEthPacketFilter Nic %lx Nic->UsbEth %lx ", Nic, Nic->UsbEth));
+      Nic->UsbEth->SetUsbEthPacketFilter (Nic->UsbEth, Nic->RxFilter);
+    } else {
+      Status = gBS->AllocatePool (EfiBootServicesData, Nic->McastCount * 6, (VOID **)&McastList);
+      if (EFI_ERROR (Status)) {
+        return PXE_STATCODE_INVALID_PARAMETER;
+      }
+
+      if (Cpb != NULL) {
+        for (Index1 = 0; Index1 < Nic->McastCount; Index1++) {
+          for (Index2 = 0; Index2 < 6; Index2++) {
+            McastList[Count++] = Cpb->MCastList[Index1][Index2];
+          }
+        }
+      }
+
+      Nic->RxFilter |= PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST;
+      if (Cpb != NULL) {
+        Nic->UsbEth->SetUsbEthMcastFilter (Nic->UsbEth, Nic->McastCount, McastList);
+      }
+
+      Nic->UsbEth->SetUsbEthPacketFilter (Nic->UsbEth, Nic->RxFilter);
+      FreePool (McastList);
+    }
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  This request is used to configure device Ethernet packet filter settings.
+
+  @param[in]  This              A pointer to the EDKII_USB_ETHERNET_PROTOCOL instance.
+  @param[in]  Value             Packet Filter Bitmap.
+
+  @retval EFI_SUCCESS           The request executed successfully.
+  @retval EFI_TIMEOUT           A timeout occurred executing the request.
+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.
+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.
+  @retval EFI_UNSUPPORTED       Not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+SetUsbRndisPacketFilter (
+  IN EDKII_USB_ETHERNET_PROTOCOL  *This,
+  IN UINT16                       Value
+  )
+{
+  return EFI_SUCCESS;
+}
+
+/**
+  This request is used to retrieve a statistic based on the feature selector.
+
+  @param[in]  This                  A pointer to the EDKII_USB_ETHERNET_PROTOCOL instance.
+  @param[in]  FeatureSelector       Value of the feature selector.
+  @param[out] Statistic             A pointer to the 32 bit unsigned integer.
+
+  @retval EFI_SUCCESS           The request executed successfully.
+  @retval EFI_TIMEOUT           A timeout occurred executing the request.
+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.
+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.
+  @retval EFI_UNSUPPORTED       Not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+GetRndisStatistic (
+  IN  EDKII_USB_ETHERNET_PROTOCOL  *This,
+  IN  UINT16                       FeatureSelector,
+  OUT VOID                         *Statistic
+  )
+{
+  return EFI_SUCCESS;
+}
+
+/**
+  This function is called when UndiStart is invoked.
+
+  @param[in]  Cdb  A pointer to the command descriptor block.
+  @param[in]  Nic  A pointer to the Network interface controller data.
+
+  @retval EFI_SUCCESS     The request executed successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+RndisUndiStart (
+  IN  PXE_CDB   *Cdb,
+  IN  NIC_DATA  *Nic
+  )
+{
+  EFI_STATUS  Status;
+
+  DEBUG ((DEBUG_INFO, "RndisUndiStart Nic %lx Cdb %lx Nic State %x\n", Nic, Cdb, Nic->State));
+
+  // Issue Rndis Reset and bring the device to RNDIS_BUS_INITIALIZED state
+  Status = RndisUndiReset (Cdb, Nic);
+  if (EFI_ERROR (Status)) {
+    RndisUndiReset (Cdb, Nic);
+  }
+
+  Status = RndisUndiInitialize (Cdb, Nic);
+  if (EFI_ERROR (Status)) {
+    RndisUndiInitialize (Cdb, Nic);
+  }
+
+  RndisUndiShutdown (Cdb, Nic);
+
+  return EFI_SUCCESS;
+}
+
+/**
+  This function is called when Undistop is invoked.
+
+  @param[in]  Cdb  A pointer to the command descriptor block.
+  @param[in]  Nic  A pointer to the Network interface controller data.
+
+  @retval EFI_SUCCESS     The request executed successfully.
+**/
+EFI_STATUS
+EFIAPI
+RndisUndiStop (
+  IN  PXE_CDB   *Cdb,
+  IN  NIC_DATA  *Nic
+  )
+{
+  DEBUG ((DEBUG_INFO, "RndisUndiStop State %x\n", Nic->State));
+  return EFI_SUCCESS;
+}
+
+/**
+  This function is called when UndiGetInitInfo is invoked.
+
+  @param[in]  Cdb  A pointer to the command descriptor block.
+  @param[in]  Nic  A pointer to the Network interface controller data.
+
+  @retval EFI_SUCCESS     The request executed successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+RndisUndiGetInitInfo (
+  IN  PXE_CDB   *Cdb,
+  IN  NIC_DATA  *Nic
+  )
+{
+  EDKII_USB_ETHERNET_PROTOCOL  *UsbEthDevice;
+  USB_RNDIS_DEVICE             *UsbRndisDevice;
+  PXE_DB_GET_INIT_INFO         *Db;
+
+  DEBUG ((DEBUG_INFO, "RndisUndiGetInitInfo\n"));
+
+  UsbEthDevice   = Nic->UsbEth;
+  UsbRndisDevice = USB_RNDIS_DEVICE_FROM_THIS (UsbEthDevice);
+
+  Db = (PXE_DB_GET_INIT_INFO *)(UINTN)Cdb->DBaddr;
+
+  Db->FrameDataLen = UsbRndisDevice->MaxTransferSize - sizeof (REMOTE_NDIS_PACKET_MSG) - PXE_MAC_HEADER_LEN_ETHER;
+  // Limit Max MTU size to 1500 bytes as RNDIS spec.
+  if (Db->FrameDataLen > PXE_MAX_TXRX_UNIT_ETHER) {
+    Db->FrameDataLen = PXE_MAX_TXRX_UNIT_ETHER;
+  }
+
+  DEBUG ((DEBUG_INFO, "Db->FrameDataLen %x\n", Db->FrameDataLen));
+
+  return EFI_SUCCESS;
+}
+
+/**
+  This function is called when RndisUndiGetConfigInfo is invoked.
+
+  @param[in]  Cdb  A pointer to the command descriptor block.
+  @param[in]  Nic  A pointer to the Network interface controller data.
+
+  @retval EFI_SUCCESS     The request executed successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+RndisUndiGetConfigInfo (
+  IN  PXE_CDB   *Cdb,
+  IN  NIC_DATA  *Nic
+  )
+{
+  DEBUG ((DEBUG_INFO, "RndisUndiGetConfigInfo\n"));
+  return EFI_SUCCESS;
+}
+
+/**
+  This function is called when UndiInitialize is invoked.
+
+  @param[in]  Cdb  A pointer to the command descriptor block.
+  @param[in]  Nic  A pointer to the Network interface controller data.
+
+  @retval EFI_SUCCESS           The request executed successfully.
+  @retval EFI_UNSUPPORTED       Not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+RndisUndiInitialize (
+  IN  PXE_CDB   *Cdb,
+  IN  NIC_DATA  *Nic
+  )
+{
+  EDKII_USB_ETHERNET_PROTOCOL   *UsbEthDriver;
+  USB_RNDIS_DEVICE              *UsbRndisDevice;
+  REMOTE_NDIS_INITIALIZE_MSG    RndisInitMsg;
+  REMOTE_NDIS_INITIALIZE_CMPLT  RndisInitMsgCmplt;
+  EFI_STATUS                    Status;
+
+  DEBUG ((DEBUG_INFO, "RndisUndiInitialize\n"));
+
+  UsbEthDriver   = Nic->UsbEth;
+  UsbRndisDevice = USB_RNDIS_DEVICE_FROM_THIS (UsbEthDriver);
+
+  ZeroMem (&RndisInitMsg, sizeof (REMOTE_NDIS_INITIALIZE_MSG));
+  ZeroMem (&RndisInitMsgCmplt, sizeof (REMOTE_NDIS_INITIALIZE_CMPLT));
+
+  RndisInitMsg.MessageType     = RNDIS_INITIALIZE_MSG;
+  RndisInitMsg.MessageLength   = sizeof (REMOTE_NDIS_INITIALIZE_MSG);
+  RndisInitMsg.RequestID       = UsbRndisDevice->RequestId;
+  RndisInitMsg.MajorVersion    = RNDIS_MAJOR_VERSION;
+  RndisInitMsg.MinorVersion    = RNDIS_MINOR_VERSION;
+  RndisInitMsg.MaxTransferSize = RNDIS_MAX_TRANSFER_SIZE;
+
+  RndisInitMsgCmplt.MessageType   = RNDIS_INITIALIZE_CMPLT;
+  RndisInitMsgCmplt.MessageLength = sizeof (REMOTE_NDIS_INITIALIZE_CMPLT);
+
+  Status = RndisControlMsg (UsbRndisDevice, (REMOTE_NDIS_MSG_HEADER *)&RndisInitMsg, (REMOTE_NDIS_MSG_HEADER *)&RndisInitMsgCmplt);
+
+  UsbRndisDevice->RequestId++;
+
+  if (EFI_ERROR (Status) || (RndisInitMsgCmplt.Status & 0x80000000)) {
+    return Status;
+  }
+
+  // Only Wired Medium is supported
+  if (RndisInitMsgCmplt.Medium) {
+    return EFI_UNSUPPORTED;
+  }
+
+  UsbRndisDevice->Medium                = RndisInitMsgCmplt.Medium;
+  UsbRndisDevice->MaxPacketsPerTransfer = RndisInitMsgCmplt.MaxPacketsPerTransfer;
+  UsbRndisDevice->MaxTransferSize       = RndisInitMsgCmplt.MaxTransferSize;
+  UsbRndisDevice->PacketAlignmentFactor = RndisInitMsgCmplt.PacketAlignmentFactor;
+
+  DEBUG ((DEBUG_INFO, "Medium : %x \n", RndisInitMsgCmplt.Medium));
+  DEBUG ((DEBUG_INFO, "MaxPacketsPerTransfer : %x \n", RndisInitMsgCmplt.MaxPacketsPerTransfer));
+  DEBUG ((DEBUG_INFO, "MaxTransferSize : %x\n", RndisInitMsgCmplt.MaxTransferSize));
+  DEBUG ((DEBUG_INFO, "PacketAlignmentFactor : %x\n", RndisInitMsgCmplt.PacketAlignmentFactor));
+
+  return Status;
+}
+
+/**
+  This function is called when UndiReset is invoked.
+
+  @param[in]  Cdb  A pointer to the command descriptor block.
+  @param[in]  Nic  A pointer to the Network interface controller data.
+
+  @retval EFI_SUCCESS           The request executed successfully.
+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.
+
+**/
+EFI_STATUS
+EFIAPI
+RndisUndiReset (
+  IN  PXE_CDB   *Cdb,
+  IN  NIC_DATA  *Nic
+  )
+{
+  EDKII_USB_ETHERNET_PROTOCOL  *UsbEthDriver;
+  USB_RNDIS_DEVICE             *UsbRndisDevice;
+  REMOTE_NDIS_RESET_MSG        RndisResetMsg;
+  REMOTE_NDIS_RESET_CMPLT      RndisResetCmplt;
+  EFI_STATUS                   Status;
+
+  DEBUG ((DEBUG_INFO, "RndisUndiReset\n"));
+
+  UsbEthDriver   = Nic->UsbEth;
+  UsbRndisDevice = USB_RNDIS_DEVICE_FROM_THIS (UsbEthDriver);
+
+  ZeroMem (&RndisResetMsg, sizeof (REMOTE_NDIS_RESET_MSG));
+  ZeroMem (&RndisResetCmplt, sizeof (REMOTE_NDIS_RESET_CMPLT));
+
+  RndisResetMsg.MessageType   = RNDIS_RESET_MSG;
+  RndisResetMsg.MessageLength = sizeof (REMOTE_NDIS_RESET_MSG);
+
+  RndisResetCmplt.MessageType   = RNDIS_RESET_CMPLT;
+  RndisResetCmplt.MessageLength = sizeof (REMOTE_NDIS_RESET_CMPLT);
+
+  Status = RndisControlMsg (UsbRndisDevice, (REMOTE_NDIS_MSG_HEADER *)&RndisResetMsg, (REMOTE_NDIS_MSG_HEADER *)&RndisResetCmplt);
+
+  UsbRndisDevice->RequestId = 1;          // Let's start with 1
+
+  if (EFI_ERROR (Status) || (RndisResetCmplt.Status & 0x80000000)) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  This function is called when UndiShutdown is invoked.
+
+  @param[in]  Cdb  A pointer to the command descriptor block.
+  @param[in]  Nic  A pointer to the Network interface controller data.
+
+  @retval EFI_SUCCESS     The request executed successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+RndisUndiShutdown (
+  IN  PXE_CDB   *Cdb,
+  IN  NIC_DATA  *Nic
+  )
+{
+  EDKII_USB_ETHERNET_PROTOCOL  *UsbEthDriver;
+  USB_RNDIS_DEVICE             *UsbRndisDevice;
+  REMOTE_NDIS_HALT_MSG         RndisHltMsg;
+  EFI_STATUS                   Status;
+
+  DEBUG ((DEBUG_INFO, "RndisUndiShutdown\n"));
+
+  UsbEthDriver   = Nic->UsbEth;
+  UsbRndisDevice = USB_RNDIS_DEVICE_FROM_THIS (UsbEthDriver);
+
+  ZeroMem (&RndisHltMsg, sizeof (REMOTE_NDIS_HALT_MSG));
+
+  RndisHltMsg.MessageType   = RNDIS_HLT_MSG;
+  RndisHltMsg.MessageLength = sizeof (REMOTE_NDIS_HALT_MSG);
+
+  Status = RndisControlMsg (UsbRndisDevice, (REMOTE_NDIS_MSG_HEADER *)&RndisHltMsg, NULL);
+
+  if (Status == EFI_DEVICE_ERROR) {
+    Status = EFI_SUCCESS;
+  }
+
+  UsbRndisDevice->RequestId = 1;
+  return Status;
+}
+
+/**
+  Update the Media connection.
+
+  @param[in]  Cdb  A pointer to the command descriptor block.
+  @param[in]  Nic  A pointer to the Network interface controller data.
+
+  @retval EFI_SUCCESS     The request executed successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+RndisUndiGetStatus (
+  IN  PXE_CDB   *Cdb,
+  IN  NIC_DATA  *Nic
+  )
+{
+  Cdb->StatFlags &= ~(PXE_STATFLAGS_GET_STATUS_NO_MEDIA);
+  return EFI_SUCCESS;
+}
+
+/**
+  Transmit the data after appending RNDIS header.
+
+  @param[in]      Cdb           A pointer to the command descriptor block.
+  @param[in]      This          A pointer to the EDKII_USB_ETHERNET_PROTOCOL instance.
+  @param[in]      BulkOutData   A pointer to the buffer of data that will be transmitted to USB
+                                device or received from USB device.
+  @param[in, out] DataLength    A pointer to the PacketLength.
+
+  @retval EFI_SUCCESS     The request executed successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+RndisUndiTransmit (
+  IN      PXE_CDB                      *Cdb,
+  IN      EDKII_USB_ETHERNET_PROTOCOL  *This,
+  IN      VOID                         *BulkOutData,
+  IN OUT  UINTN                        *DataLength
+  )
+{
+  EFI_STATUS              Status;
+  USB_RNDIS_DEVICE        *UsbRndisDevice;
+  REMOTE_NDIS_PACKET_MSG  *RndisPacketMsg;
+  UINTN                   TransferLength;
+
+  DEBUG ((DEBUG_INFO, "RndisUndiTransmit DataLength : %x\n", *DataLength));
+
+  UsbRndisDevice = USB_RNDIS_DEVICE_FROM_THIS (This);
+
+  RndisPacketMsg = AllocateZeroPool (sizeof (REMOTE_NDIS_PACKET_MSG) + *DataLength);
+  if (RndisPacketMsg == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  RndisPacketMsg->MessageType   = RNDIS_PACKET_MSG;
+  RndisPacketMsg->MessageLength = sizeof (REMOTE_NDIS_PACKET_MSG) + (UINT32)*DataLength;
+  RndisPacketMsg->DataOffset    = sizeof (REMOTE_NDIS_PACKET_MSG) - 8;
+  RndisPacketMsg->DataLength    = (UINT32)*DataLength;
+
+  CopyMem (
+    ((UINT8 *)RndisPacketMsg) + sizeof (REMOTE_NDIS_PACKET_MSG),
+    BulkOutData,
+    *DataLength
+    );
+
+  TransferLength = RndisPacketMsg->MessageLength;
+
+  Status = RndisTransmitDataMsg (
+             UsbRndisDevice,
+             (REMOTE_NDIS_MSG_HEADER *)RndisPacketMsg,
+             &TransferLength
+             );
+
+  DEBUG ((DEBUG_INFO, "\nRndisUndiTransmit TransferLength %lx\n", TransferLength));
+
+  FreePool (RndisPacketMsg);
+
+  return Status;
+}
+
+/**
+  Receives and removes RNDIS header and returns the raw data.
+
+  @param[in]      Cdb           A pointer to the command descriptor block.
+  @param[in]      This          A pointer to the EDKII_USB_ETHERNET_PROTOCOL instance.
+  @param[in, out] BulkInData    A pointer to the buffer of data that will be transmitted to USB
+                                device or received from USB device.
+  @param[in, out] DataLength    A pointer to the PacketLength.
+
+  @retval EFI_SUCCESS           The request executed successfully.
+  @retval EFI_BUFFER_TOO_SMALL  The user provided buffer is too small
+  @retval EFI_NOT_FOUND         No buffer was found in the list.
+
+**/
+EFI_STATUS
+EFIAPI
+RndisUndiReceive (
+  IN     PXE_CDB                      *Cdb,
+  IN     EDKII_USB_ETHERNET_PROTOCOL  *This,
+  IN OUT VOID                         *BulkInData,
+  IN OUT UINTN                        *DataLength
+  )
+{
+  EFI_STATUS              Status;
+  USB_RNDIS_DEVICE        *UsbRndisDevice;
+  REMOTE_NDIS_PACKET_MSG  *RndisPacketMsg;
+  UINTN                   TransferLength;
+  VOID                    *Buffer;
+  PACKET_LIST             *HeadPacket;
+  PACKET_LIST             *PacketList;
+
+  // Check if there is any outstanding packet to receive
+  // The buffer allocated has a linked List followed by the packet.
+
+  UsbRndisDevice = USB_RNDIS_DEVICE_FROM_THIS (This);
+  Buffer         = NULL;
+  HeadPacket     = NULL;
+
+  while (1) {
+    Buffer = AllocateZeroPool (sizeof (PACKET_LIST) + sizeof (REMOTE_NDIS_PACKET_MSG) + UsbRndisDevice->MaxTransferSize);
+    if (Buffer == NULL) {
+      return EFI_OUT_OF_RESOURCES;
+    }
+
+    RndisPacketMsg                = (REMOTE_NDIS_PACKET_MSG *)(sizeof (PACKET_LIST) + (UINT8 *)Buffer);
+    PacketList                    = (PACKET_LIST *)Buffer;
+    PacketList->PacketStartBuffer = (UINT8 *)Buffer + sizeof (PACKET_LIST);
+    // Save the original address for freeing it up
+    PacketList->OrgBuffer = (UINT8 *)Buffer;
+    TransferLength        = UsbRndisDevice->MaxTransferSize;
+
+    Status = RndisReceiveDataMsg (
+               UsbRndisDevice,
+               (REMOTE_NDIS_MSG_HEADER *)RndisPacketMsg,
+               &TransferLength
+               );
+
+    if (EFI_ERROR (Status) || (TransferLength == 0)) {
+      FreePool (Buffer);
+      break;
+    }
+
+    // Collect all the RNDIS packet in Linked list.
+    if ((RndisPacketMsg->MessageType == RNDIS_PACKET_MSG) &&
+        (RndisPacketMsg->DataOffset == sizeof (REMOTE_NDIS_PACKET_MSG) - RNDIS_RESERVED_BYTE_LENGTH) &&
+        (TransferLength >= RndisPacketMsg->MessageLength))
+    {
+      // Insert Packet
+      PacketList->RemainingLength = TransferLength;
+      InsertTailList (&UsbRndisDevice->ReceivePacketList, Buffer);
+    } else {
+      FreePool (Buffer);
+    }
+  }
+
+  while (!IsListEmpty (&UsbRndisDevice->ReceivePacketList)) {
+    HeadPacket = (PACKET_LIST *)GetFirstNode (&UsbRndisDevice->ReceivePacketList);
+
+    RndisPacketMsg = (REMOTE_NDIS_PACKET_MSG *)(UINT8 *)HeadPacket->PacketStartBuffer;
+
+    PrintRndisMsg ((REMOTE_NDIS_MSG_HEADER *)RndisPacketMsg);
+
+    // Check whether the packet is valid RNDIS packet.
+    if ((HeadPacket->RemainingLength > sizeof (REMOTE_NDIS_PACKET_MSG)) && (RndisPacketMsg->MessageType == RNDIS_PACKET_MSG) &&
+        (RndisPacketMsg->DataOffset == (sizeof (REMOTE_NDIS_PACKET_MSG) - RNDIS_RESERVED_BYTE_LENGTH)) &&
+        (HeadPacket->RemainingLength >= RndisPacketMsg->MessageLength))
+    {
+      if (*DataLength >= RndisPacketMsg->DataLength) {
+        CopyMem (
+          BulkInData,
+          (UINT8 *)RndisPacketMsg + (RndisPacketMsg->DataOffset + RNDIS_RESERVED_BYTE_LENGTH),
+          RndisPacketMsg->DataLength
+          );
+
+        *DataLength =  RndisPacketMsg->DataLength;
+
+        HeadPacket->RemainingLength   = HeadPacket->RemainingLength - RndisPacketMsg->MessageLength;
+        HeadPacket->PacketStartBuffer = (UINT8 *)RndisPacketMsg + RndisPacketMsg->MessageLength;
+
+        return EFI_SUCCESS;
+      } else {
+        *DataLength = RndisPacketMsg->DataLength;
+        return EFI_BUFFER_TOO_SMALL;
+      }
+    }
+
+    RemoveEntryList (&HeadPacket->PacketList);
+    FreePool ((PACKET_LIST *)HeadPacket->OrgBuffer);
+  }
+
+  return EFI_NOT_FOUND;
+}
+
+/**
+  This is a dummy function which just returns. Unimplemented EDKII_USB_ETHERNET_PROTOCOL functions
+  point to this function.
+
+  @param[in]  Cdb  A pointer to the command descriptor block.
+  @param[in]  Nic  A pointer to the Network interface controller data.
+
+  @retval EFI_SUCCESS     The request executed successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+RndisDummyReturn (
+  IN  PXE_CDB   *Cdb,
+  IN  NIC_DATA  *Nic
+  )
+{
+  DEBUG ((DEBUG_INFO, "RndisDummyReturn called\n"));
+  return EFI_SUCCESS;
+}
+
+/**
+  This function send the RNDIS command through the device's control endpoint
+
+  @param[in]  UsbRndisDevice    A pointer to the USB_RNDIS_DEVICE instance.
+  @param[in]  RndisMsg          A pointer to the REMOTE_NDIS_MSG_HEADER data.
+  @param[out] RndisMsgResponse  A pointer to the REMOTE_NDIS_MSG_HEADER data for getting responses.
+
+  @retval EFI_SUCCESS           The bulk transfer has been successfully executed.
+
+**/
+EFI_STATUS
+RndisControlMsg (
+  IN  USB_RNDIS_DEVICE        *UsbRndisDevice,
+  IN  REMOTE_NDIS_MSG_HEADER  *RndisMsg,
+  OUT REMOTE_NDIS_MSG_HEADER  *RndisMsgResponse
+  )
+{
+  EFI_USB_IO_PROTOCOL           *UsbIo = UsbRndisDevice->UsbIo;
+  EFI_USB_DEVICE_REQUEST        DevReq;
+  UINT32                        UsbStatus;
+  EFI_STATUS                    Status;
+  UINT32                        SaveResponseType;
+  UINT32                        SaveResponseLength;
+  UINT32                        Index;
+  REMOTE_NDIS_INITIALIZE_CMPLT  *RndisInitCmplt;
+
+  SaveResponseType   = 0;
+  SaveResponseLength = 0;
+  RndisInitCmplt     = (REMOTE_NDIS_INITIALIZE_CMPLT *)RndisMsgResponse;
+
+  if (RndisMsgResponse) {
+    SaveResponseType   = RndisMsgResponse->MessageType;
+    SaveResponseLength = RndisMsgResponse->MessageLength;
+  }
+
+  ZeroMem (&DevReq, sizeof (EFI_USB_DEVICE_REQUEST));
+
+  DevReq.RequestType = USB_REQ_TYPE_CLASS | USB_TARGET_INTERFACE;
+  DevReq.Request     = SEND_ENCAPSULATED_COMMAND;
+  DevReq.Value       = 0;
+  DevReq.Index       = 0;
+  DevReq.Length      = (UINT16)RndisMsg->MessageLength;
+
+  PrintRndisMsg (RndisMsg);
+
+  Status = UsbIo->UsbControlTransfer (
+                    UsbIo,
+                    &DevReq,
+                    EfiUsbDataOut,
+                    USB_ETHERNET_TRANSFER_TIMEOUT,
+                    RndisMsg,
+                    RndisMsg->MessageLength,
+                    &UsbStatus
+                    );
+
+  DEBUG ((DEBUG_INFO, "RndisControlMsg: UsbStatus : %x Status : %r RndisMsgResponse : %lx\n", UsbStatus, Status, RndisMsgResponse));
+
+  // Error or no response expected
+  if ((EFI_ERROR (Status)) || (RndisMsgResponse == NULL)) {
+    DEBUG ((DEBUG_INFO, "RndisControlMsg: UsbStatus : %x Status : %r\n", UsbStatus, Status));
+    return Status;
+  }
+
+  for (Index = 0; Index < (RNDIS_CONTROL_TIMEOUT/100); Index++) {
+    ReadRndisResponseInterrupt (UsbRndisDevice);
+    ZeroMem (&DevReq, sizeof (EFI_USB_DEVICE_REQUEST));
+
+    DevReq.RequestType = USB_ENDPOINT_DIR_IN | USB_REQ_TYPE_CLASS | USB_TARGET_INTERFACE;
+    DevReq.Request     = GET_ENCAPSULATED_RESPONSE;
+    DevReq.Value       = 0;
+    DevReq.Index       = 0;
+    DevReq.Length      = (UINT16)RndisMsgResponse->MessageLength;
+
+    Status = UsbIo->UsbControlTransfer (
+                      UsbIo,
+                      &DevReq,
+                      EfiUsbDataIn,
+                      USB_ETHERNET_TRANSFER_TIMEOUT,
+                      RndisMsgResponse,
+                      RndisMsgResponse->MessageLength,
+                      &UsbStatus
+                      );
+
+    DEBUG ((DEBUG_INFO, "RndisControlMsg Response: UsbStatus : %x Status : %r \n", UsbStatus, Status));
+
+    PrintRndisMsg (RndisMsgResponse);
+
+    if (!EFI_ERROR (Status)) {
+      if ((RndisInitCmplt->RequestID != ((REMOTE_NDIS_INITIALIZE_CMPLT *)RndisMsg)->RequestID) || (RndisInitCmplt->MessageType != SaveResponseType)) {
+        DEBUG ((DEBUG_INFO, "Retry the response\n"));
+
+        RndisMsgResponse->MessageType   = SaveResponseType;
+        RndisMsgResponse->MessageLength = SaveResponseLength;
+        continue;
+      }
+    }
+
+    return Status;
+  }
+
+  DEBUG ((DEBUG_INFO, "RndisControlMsg: TimeOut\n"));
+
+  return EFI_TIMEOUT;
+}
+
+/**
+  This function send the RNDIS command through the device's Data endpoint
+
+  @param[in]      UsbRndisDevice  A pointer to the USB_RNDIS_DEVICE instance.
+  @param[in]      RndisMsg        A pointer to the REMOTE_NDIS_MSG_HEADER to send out.
+  @param[in, out] TransferLength  The length of the RndisMsg data to transfer.
+
+  @retval EFI_SUCCESS     The request executed successfully.
+
+**/
+EFI_STATUS
+RndisTransmitDataMsg (
+  IN      USB_RNDIS_DEVICE        *UsbRndisDevice,
+  IN      REMOTE_NDIS_MSG_HEADER  *RndisMsg,
+  IN OUT  UINTN                   *TransferLength
+  )
+{
+  EFI_STATUS  Status;
+  UINT32      UsbStatus;
+
+  if (UsbRndisDevice->BulkInEndpoint == 0) {
+    GetEndpoint (UsbRndisDevice->UsbIoCdcData, UsbRndisDevice);
+  }
+
+  PrintRndisMsg (RndisMsg);
+
+  Status = UsbRndisDevice->UsbIoCdcData->UsbBulkTransfer (
+                                           UsbRndisDevice->UsbIoCdcData,
+                                           UsbRndisDevice->BulkOutEndpoint,
+                                           RndisMsg,
+                                           TransferLength,
+                                           USB_TX_ETHERNET_BULK_TIMEOUT,
+                                           &UsbStatus
+                                           );
+
+  if (Status == EFI_SUCCESS) {
+    gStopBulkInCnt = MAXIMUM_STOPBULKIN_CNT;     // After sending cmd ,we will polling receive package for MAXIMUM_STOPBULKIN_CNT times
+  }
+
+  return Status;
+}
+
+/**
+  This function send the RNDIS command through the device's Data endpoint
+
+  @param[in]      UsbRndisDevice    A pointer to the USB_RNDIS_DEVICE instance.
+  @param[in, out] RndisMsg          A pointer to the REMOTE_NDIS_MSG_HEADER to send out.
+  @param[in, out] TransferLength    The length of the RndisMsg data to transfer.
+
+  @retval EFI_SUCCESS     The request executed successfully.
+
+**/
+EFI_STATUS
+RndisReceiveDataMsg (
+  IN      USB_RNDIS_DEVICE        *UsbRndisDevice,
+  IN OUT  REMOTE_NDIS_MSG_HEADER  *RndisMsg,
+  IN OUT  UINTN                   *TransferLength
+  )
+{
+  EFI_STATUS  Status;
+  UINT32      UsbStatus;
+
+  UsbStatus = 0;
+
+  if (UsbRndisDevice->BulkInEndpoint == 0) {
+    GetEndpoint (UsbRndisDevice->UsbIoCdcData, UsbRndisDevice);
+  }
+
+  // Use gStopBulkInCnt to stop BulkIn command
+  if (gStopBulkInCnt || LAN_BULKIN_CMD_CONTROL) {
+    Status = UsbRndisDevice->UsbIoCdcData->UsbBulkTransfer (
+                                             UsbRndisDevice->UsbIoCdcData,
+                                             UsbRndisDevice->BulkInEndpoint,
+                                             RndisMsg,
+                                             TransferLength,
+                                             USB_RX_ETHERNET_BULK_TIMEOUT,
+                                             &UsbStatus
+                                             );
+
+    if (!EFI_ERROR (Status)) {
+      gStopBulkInCnt = MINIMUM_STOPBULKIN_CNT;
+    } else {
+      gStopBulkInCnt--;
+    }
+  } else {
+    Status          = EFI_TIMEOUT;
+    *TransferLength = 0;
+    gBlockBulkInCnt++;
+  }
+
+  if (gBlockBulkInCnt > BULKIN_CMD_POLLING_CNT) {
+    gStopBulkInCnt  = MINIMUM_STOPBULKIN_CNT;
+    gBlockBulkInCnt = 0;
+  }
+
+  PrintRndisMsg (RndisMsg);
+
+  return Status;
+}
+
+/**
+  Prints RNDIS Header and Data
+
+  @param[in] RndisMsg    A pointer to the REMOTE_NDIS_MSG_HEADER data.
+
+**/
+VOID
+PrintRndisMsg (
+  IN  REMOTE_NDIS_MSG_HEADER  *RndisMsg
+  )
+{
+  UINTN                    Length;
+  REMOTE_NDIS_QUERY_CMPLT  *RndisQueryCmplt;
+
+  Length = 0;
+
+  switch (RndisMsg->MessageType) {
+    case RNDIS_PACKET_MSG:
+      DEBUG ((DEBUG_INFO, "RNDIS_PACKET_MSG:\n"));
+      Length = sizeof (REMOTE_NDIS_PACKET_MSG) + 0x14;
+      break;
+    case RNDIS_INITIALIZE_MSG:
+      DEBUG ((DEBUG_INFO, "RNDIS_INITIALIZE_MSG:\n"));
+      Length = sizeof (REMOTE_NDIS_INITIALIZE_MSG);
+      break;
+    case RNDIS_INITIALIZE_CMPLT:
+      DEBUG ((DEBUG_INFO, "RNDIS_INITIALIZE_CMPLT:\n"));
+      Length = sizeof (REMOTE_NDIS_INITIALIZE_CMPLT);
+      break;
+    case RNDIS_HLT_MSG:
+      DEBUG ((DEBUG_INFO, "RNDIS_HLT_MSG:\n"));
+      Length = sizeof (REMOTE_NDIS_HALT_MSG);
+      break;
+    case RNDIS_QUERY_MSG:
+      DEBUG ((DEBUG_INFO, "RNDIS_QUERY_MSG:\n"));
+      Length = sizeof (REMOTE_NDIS_QUERY_MSG);
+      break;
+    case RNDIS_QUERY_CMPLT:
+      DEBUG ((DEBUG_INFO, "RNDIS_QUERY_CMPLT:\n"));
+      RndisQueryCmplt = (REMOTE_NDIS_QUERY_CMPLT *)RndisMsg;
+      Length          = sizeof (REMOTE_NDIS_QUERY_CMPLT) + RndisQueryCmplt->InformationBufferLength;
+      break;
+    case RNDIS_SET_MSG:
+      DEBUG ((DEBUG_INFO, "RNDIS_SET_MSG:\n"));
+      Length = sizeof (REMOTE_NDIS_SET_MSG);
+      break;
+    case RNDIS_SET_CMPLT:
+      DEBUG ((DEBUG_INFO, "RNDIS_SET_CMPLT:\n"));
+      Length = sizeof (REMOTE_NDIS_SET_CMPLT);
+      break;
+    case RNDIS_RESET_MSG:
+      DEBUG ((DEBUG_INFO, "RNDIS_RESET_MSG:\n"));
+      Length = sizeof (REMOTE_NDIS_RESET_MSG);
+      break;
+    case RNDIS_RESET_CMPLT:
+      DEBUG ((DEBUG_INFO, "RNDIS_RESET_CMPLT:\n"));
+      Length = sizeof (REMOTE_NDIS_RESET_CMPLT);
+      break;
+    case RNDIS_INDICATE_STATUS_MSG:
+      DEBUG ((DEBUG_INFO, "RNDIS_INDICATE_STATUS_MSG:\n"));
+      Length = sizeof (REMOTE_NDIS_INDICATE_STATUS_MSG);
+      break;
+    case RNDIS_KEEPALIVE_MSG:
+      DEBUG ((DEBUG_INFO, "RNDIS_KEEPALIVE_MSG:\n"));
+      Length = sizeof (REMOTE_NDIS_KEEPALIVE_MSG);
+      break;
+    case RNDIS_KEEPALIVE_CMPLT:
+      DEBUG ((DEBUG_INFO, "RNDIS_KEEPALIVE_CMPLT:\n"));
+      Length = sizeof (REMOTE_NDIS_KEEPALIVE_CMPLT);
+  }
+
+  if (Length) {
+    UINTN  Index = 0;
+    for ( ; Length; Length -= 4, Index++) {
+      DEBUG ((DEBUG_INFO, "%8X\t", *((UINT32 *)RndisMsg + Index)));
+      if (((Index % 4) == 3) && (Index != 0)) {
+        DEBUG ((DEBUG_INFO, "\n"));
+      }
+
+      if ((Length < 8) && (Length > 4)) {
+        UINT32  Data32;
+        Index++;
+        Data32 = *((UINT32 *)RndisMsg + Index);
+        DEBUG ((DEBUG_INFO, "%8X\t", Data32));
+        break;
+      }
+    }
+
+    if (Index % 4) {
+      DEBUG ((DEBUG_INFO, "\n"));
+    }
+  }
+}
diff --git a/MdeModulePkg/Include/Protocol/UsbEthernetProtocol.h b/MdeModulePkg/Include/Protocol/UsbEthernetProtocol.h
new file mode 100644
index 0000000000..800945d4b3
--- /dev/null
+++ b/MdeModulePkg/Include/Protocol/UsbEthernetProtocol.h
@@ -0,0 +1,878 @@
+/** @file
+  Header file contains code for USB Ethernet Protocol
+  definitions
+
+  Copyright (c) 2023, American Megatrends International LLC. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef USB_ETHERNET_PROTOCOL_H_
+#define USB_ETHERNET_PROTOCOL_H_
+
+#define EDKII_USB_ETHERNET_PROTOCOL_GUID \
+    {0x8d8969cc, 0xfeb0, 0x4303, {0xb2, 0x1a, 0x1f, 0x11, 0x6f, 0x38, 0x56, 0x43}}
+
+typedef struct _EDKII_USB_ETHERNET_PROTOCOL EDKII_USB_ETHERNET_PROTOCOL;
+
+#define USB_CDC_CLASS          0x02
+#define USB_CDC_ACM_SUBCLASS   0x02
+#define USB_CDC_ECM_SUBCLASS   0x06
+#define USB_CDC_NCM_SUBCLASS   0x0D
+#define USB_CDC_DATA_CLASS     0x0A
+#define USB_CDC_DATA_SUBCLASS  0x00
+#define USB_NO_CLASS_PROTOCOL  0x00
+#define USB_NCM_NTB_PROTOCOL   0x01
+#define USB_VENDOR_PROTOCOL    0xFF
+
+// Type Values for the DescriptorType Field
+#define CS_INTERFACE  0x24
+#define CS_ENDPOINT   0x25
+
+// Descriptor SubType in Functional Descriptors
+#define HEADER_FUN_DESCRIPTOR    0x00
+#define UNION_FUN_DESCRIPTOR     0x06
+#define ETHERNET_FUN_DESCRIPTOR  0x0F
+
+#define MAX_LAN_INTERFACE  0x10
+
+// Table 20: Class-Specific Notification Codes
+#define USB_CDC_NETWORK_CONNECTION  0x00
+
+// 6.3.1 NetworkConnection
+#define NETWORK_CONNECTED   0x01
+#define NETWORK_DISCONNECT  0x00
+
+// USB Header functional Descriptor
+typedef struct {
+  UINT8     FunctionLength;
+  UINT8     DescriptorType;
+  UINT8     DescriptorSubtype;
+  UINT16    BcdCdc;
+} USB_HEADER_FUN_DESCRIPTOR;
+
+// USB Union Functional Descriptor
+typedef struct {
+  UINT8    FunctionLength;
+  UINT8    DescriptorType;
+  UINT8    DescriptorSubtype;
+  UINT8    MasterInterface;
+  UINT8    SlaveInterface;
+} USB_UNION_FUN_DESCRIPTOR;
+
+// USB Ethernet Functional Descriptor
+typedef struct {
+  UINT8     FunctionLength;
+  UINT8     DescriptorType;
+  UINT8     DescriptorSubtype;
+  UINT8     MacAddress;
+  UINT32    EthernetStatistics;
+  UINT16    MaxSegmentSize;
+  UINT16    NumberMcFilters;
+  UINT8     NumberPowerFilters;
+} USB_ETHERNET_FUN_DESCRIPTOR;
+
+typedef struct {
+  UINT32    UsBitRate;
+  UINT32    DsBitRate;
+} USB_CONNECT_SPEED_CHANGE;
+
+// Request Type Codes for USB Ethernet
+#define USB_ETHERNET_GET_REQ_TYPE  0xA1
+#define USB_ETHERNET_SET_REQ_TYPE  0x21
+
+// Class-Specific Request Codes for Ethernet subclass
+// USB ECM 1.2 specification, Section 6.2
+#define SET_ETH_MULTICAST_FILTERS_REQ                0x40
+#define SET_ETH_POWER_MANAGEMENT_PATTERN_FILTER_REQ  0x41
+#define GET_ETH_POWER_MANAGEMENT_PATTERN_FILTER_REQ  0x42
+#define SET_ETH_PACKET_FILTER_REQ                    0x43
+#define GET_ETH_STATISTIC_REQ                        0x44
+
+// USB ECM command request length
+#define USB_ETH_POWER_FILTER_LENGTH   2 // Section 6.2.3
+#define USB_ETH_PACKET_FILTER_LENGTH  0 // Section 6.2.4
+#define USB_ETH_STATISTIC             4 // Section 6.2.5
+
+// USB Ethernet Packet Filter Bitmap
+// USB ECM 1.2 specification, Section 6.2.4
+#define USB_ETH_PACKET_TYPE_PROMISCUOUS    BIT0
+#define USB_ETH_PACKET_TYPE_ALL_MULTICAST  BIT1
+#define USB_ETH_PACKET_TYPE_DIRECTED       BIT2
+#define USB_ETH_PACKET_TYPE_BROADCAST      BIT3
+#define USB_ETH_PACKET_TYPE_MULTICAST      BIT4
+
+// USB Ethernet Statistics Feature Selector Codes
+// USB ECM 1.2 specification, Section 6.2.5
+#define USB_ETH_XMIT_OK                 0x01
+#define USB_ETH_RCV_OK                  0x02
+#define USB_ETH_XMIT_ERROR              0x03
+#define USB_ETH_RCV_ERROR               0x04
+#define USB_ETH_RCV_NO_BUFFER           0x05
+#define USB_ETH_DIRECTED_BYTES_XMIT     0x06
+#define USB_ETH_DIRECTED_FRAMES_XMIT    0x07
+#define USB_ETH_MULTICAST_BYTES_XMIT    0x08
+#define USB_ETH_MULTICAST_FRAMES_XMIT   0x09
+#define USB_ETH_BROADCAST_BYTES_XMIT    0x0A
+#define USB_ETH_BROADCAST_FRAMES_XMIT   0x0B
+#define USB_ETH_DIRECTED_BYTES_RCV      0x0C
+#define USB_ETH_DIRECTED_FRAMES_RCV     0x0D
+#define USB_ETH_MULTICAST_BYTES_RCV     0x0E
+#define USB_ETH_MULTICAST_FRAMES_RCV    0x0F
+#define USB_ETH_BROADCAST_BYTES_RCV     0x10
+#define USB_ETH_BROADCAST_FRAMES_RCV    0x11
+#define USB_ETH_RCV_CRC_ERROR           0x12
+#define USB_ETH_TRANSMIT_QUEUE_LENGTH   0x13
+#define USB_ETH_RCV_ERROR_ALIGNMENT     0x14
+#define USB_ETH_XMIT_ONE_COLLISION      0x15
+#define USB_ETH_XMIT_MORE_COLLISIONS    0x16
+#define USB_ETH_XMIT_DEFERRED           0x17
+#define USB_ETH_XMIT_MAX_COLLISIONS     0x18
+#define USB_ETH_RCV_OVERRUN             0x19
+#define USB_ETH_XMIT_UNDERRUN           0x1A
+#define USB_ETH_XMIT_HEARTBEAT_FAILURE  0x1B
+#define USB_ETH_XMIT_TIMES_CRS_LOST     0x1C
+#define USB_ETH_XMIT_LATE_COLLISIONS    0x1D
+
+// NIC Information
+typedef struct {
+  UINT32                         Signature;
+  EDKII_USB_ETHERNET_PROTOCOL    *UsbEth;
+  UINT16                         InterrupOpFlag;
+  UINT64                         MappedAddr;
+  PXE_MAC_ADDR                   McastList[MAX_MCAST_ADDRESS_CNT];
+  UINT8                          McastCount;
+  UINT64                         MediaHeader[MAX_XMIT_BUFFERS];
+  UINT8                          TxBufferCount;
+  UINT16                         State;
+  BOOLEAN                        CanTransmit;
+  UINT16                         ReceiveStatus;
+  UINT8                          RxFilter;
+  UINT32                         RxFrame;
+  UINT32                         TxFrame;
+  UINT16                         NetworkConnect;
+  UINT8                          CableDetect;
+  UINT16                         MaxSegmentSize;
+  EFI_MAC_ADDRESS                MacAddr;
+  PXE_CPB_START_31               PxeStart;
+  PXE_CPB_INITIALIZE             PxeInit;
+  UINT8                          PermNodeAddress[PXE_MAC_LENGTH];
+  UINT8                          CurrentNodeAddress[PXE_MAC_LENGTH];
+  UINT8                          BroadcastNodeAddress[PXE_MAC_LENGTH];
+  EFI_USB_DEVICE_REQUEST         Request;
+  EFI_EVENT                      RateLimiter;
+  UINT32                         RateLimitingCredit;
+  UINT32                         RateLimitingCreditCount;
+  UINT32                         RateLimitingPollTimer;
+  BOOLEAN                        RateLimitingEnable;
+} NIC_DATA;
+
+#define NIC_DATA_SIGNATURE  SIGNATURE_32('n', 'i', 'c', 'd')
+#define NIC_DATA_FROM_EDKII_USB_ETHERNET_PROTOCOL(a)  CR (a, NIC_DATA, UsbEth, NIC_DATA_SIGNATURE)
+
+/**
+  This command is used to determine the operational state of the UNDI.
+
+  @param[in]  Cdb  A pointer to the command descriptor block.
+  @param[in]  Nic  A pointer to the Network interface controller data.
+
+  @retval EFI_SUCCESS           The request executed successfully.
+  @retval EFI_TIMEOUT           A timeout occurred executing the request.
+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.
+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.
+  @retval EFI_UNSUPPORTED       Not supported.
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EDKII_USB_ETHERNET_UNDI_GET_STATE)(
+  IN  PXE_CDB     *Cdb,
+  IN  NIC_DATA    *Nic
+  );
+
+/**
+  This command is used to change the UNDI operational state from stopped to started.
+
+  @param[in]  Cdb  A pointer to the command descriptor block.
+  @param[in]  Nic  A pointer to the Network interface controller data.
+
+  @retval EFI_SUCCESS           The request executed successfully.
+  @retval EFI_TIMEOUT           A timeout occurred executing the request.
+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.
+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.
+  @retval EFI_UNSUPPORTED       Not supported.
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EDKII_USB_ETHERNET_UNDI_START)(
+  IN  PXE_CDB     *Cdb,
+  IN  NIC_DATA    *Nic
+  );
+
+/**
+  This command is used to change the UNDI operational state from started to stopped.
+
+  @param[in]  Cdb  A pointer to the command descriptor block.
+  @param[in]  Nic  A pointer to the Network interface controller data.
+
+  @retval EFI_SUCCESS           The request executed successfully.
+  @retval EFI_TIMEOUT           A timeout occurred executing the request.
+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.
+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.
+  @retval EFI_UNSUPPORTED       Not supported.
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EDKII_USB_ETHERNET_UNDI_STOP)(
+  IN  PXE_CDB     *Cdb,
+  IN  NIC_DATA    *Nic
+  );
+
+/**
+  This command is used to retrieve initialization information that is
+  needed by drivers and applications to initialized UNDI.
+
+  @param[in]  Cdb  A pointer to the command descriptor block.
+  @param[in]  Nic  A pointer to the Network interface controller data.
+
+  @retval EFI_SUCCESS           The request executed successfully.
+  @retval EFI_TIMEOUT           A timeout occurred executing the request.
+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.
+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.
+  @retval EFI_UNSUPPORTED       Not supported.
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EDKII_USB_ETHERNET_UNDI_GET_INIT_INFO)(
+  IN  PXE_CDB     *Cdb,
+  IN  NIC_DATA    *Nic
+  );
+
+/**
+  This command is used to retrieve configuration information about
+  the NIC being controlled by the UNDI.
+
+  @param[in]  Cdb  A pointer to the command descriptor block.
+  @param[in]  Nic  A pointer to the Network interface controller data.
+
+  @retval EFI_SUCCESS           The request executed successfully.
+  @retval EFI_TIMEOUT           A timeout occurred executing the request.
+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.
+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.
+  @retval EFI_UNSUPPORTED       Not supported.
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EDKII_USB_ETHERNET_UNDI_GET_CONFIG_INFO)(
+  IN  PXE_CDB     *Cdb,
+  IN  NIC_DATA    *Nic
+  );
+
+/**
+  This command resets the network adapter and initializes UNDI using
+  the parameters supplied in the CPB.
+
+  @param[in]  Cdb  A pointer to the command descriptor block.
+  @param[in]  Nic  A pointer to the Network interface controller data.
+
+  @retval EFI_SUCCESS           The request executed successfully.
+  @retval EFI_TIMEOUT           A timeout occurred executing the request.
+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.
+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.
+  @retval EFI_UNSUPPORTED       Not supported.
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EDKII_USB_ETHERNET_UNDI_INITIALIZE)(
+  IN  PXE_CDB   *Cdb,
+  IN  NIC_DATA  *Nic
+  );
+
+/**
+  This command resets the network adapter and reinitializes the UNDI
+  with the same parameters provided in the Initialize command.
+
+  @param[in]  Cdb  A pointer to the command descriptor block.
+  @param[in]  Nic  A pointer to the Network interface controller data.
+
+  @retval EFI_SUCCESS           The request executed successfully.
+  @retval EFI_TIMEOUT           A timeout occurred executing the request.
+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.
+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.
+  @retval EFI_UNSUPPORTED       Not supported.
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EDKII_USB_ETHERNET_UNDI_RESET)(
+  IN  PXE_CDB     *Cdb,
+  IN  NIC_DATA    *Nic
+  );
+
+/**
+  The Shutdown command resets the network adapter and leaves it in a
+  safe state for another driver to initialize.
+
+  @param[in]  Cdb  A pointer to the command descriptor block.
+  @param[in]  Nic  A pointer to the Network interface controller data.
+
+  @retval EFI_SUCCESS           The request executed successfully.
+  @retval EFI_TIMEOUT           A timeout occurred executing the request.
+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.
+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.
+  @retval EFI_UNSUPPORTED       Not supported.
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EDKII_USB_ETHERNET_UNDI_SHUTDOWN)(
+  IN  PXE_CDB     *Cdb,
+  IN  NIC_DATA    *Nic
+  );
+
+/**
+  The Interrupt Enables command can be used to read and/or change
+  the current external interrupt enable settings.
+
+  @param[in]  Cdb  A pointer to the command descriptor block.
+  @param[in]  Nic  A pointer to the Network interface controller data.
+
+  @retval EFI_SUCCESS           The request executed successfully.
+  @retval EFI_TIMEOUT           A timeout occurred executing the request.
+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.
+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.
+  @retval EFI_UNSUPPORTED       Not supported.
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EDKII_USB_ETHERNET_UNDI_INTERRUPT_ENABLE)(
+  IN  PXE_CDB     *Cdb,
+  IN  NIC_DATA    *Nic
+  );
+
+/**
+  This command is used to read and change receive filters and,
+  if supported, read and change the multicast MAC address filter list.
+
+  @param[in]  Cdb  A pointer to the command descriptor block.
+  @param[in]  Nic  A pointer to the Network interface controller data.
+
+  @retval EFI_SUCCESS           The request executed successfully.
+  @retval EFI_TIMEOUT           A timeout occurred executing the request.
+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.
+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.
+  @retval EFI_UNSUPPORTED       Not supported.
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EDKII_USB_ETHERNET_UNDI_RECEIVE_FILTER)(
+  IN  PXE_CDB     *Cdb,
+  IN  NIC_DATA    *Nic
+  );
+
+/**
+  This command is used to get current station and broadcast MAC addresses
+  and, if supported, to change the current station MAC address.
+
+  @param[in]  Cdb  A pointer to the command descriptor block.
+  @param[in]  Nic  A pointer to the Network interface controller data.
+
+  @retval EFI_SUCCESS           The request executed successfully.
+  @retval EFI_TIMEOUT           A timeout occurred executing the request.
+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.
+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.
+  @retval EFI_UNSUPPORTED       Not supported.
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EDKII_USB_ETHERNET_UNDI_STATION_ADDRESS)(
+  IN  PXE_CDB     *Cdb,
+  IN  NIC_DATA    *Nic
+  );
+
+/**
+  This command is used to read and clear the NIC traffic statistics.
+
+  @param[in]  Cdb  A pointer to the command descriptor block.
+  @param[in]  Nic  A pointer to the Network interface controller data.
+
+  @retval EFI_SUCCESS           The request executed successfully.
+  @retval EFI_TIMEOUT           A timeout occurred executing the request.
+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.
+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.
+  @retval EFI_UNSUPPORTED       Not supported.
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EDKII_USB_ETHERNET_UNDI_STATISTICS)(
+  IN  PXE_CDB     *Cdb,
+  IN  NIC_DATA    *Nic
+  );
+
+/**
+  Translate a multicast IPv4 or IPv6 address to a multicast MAC address.
+
+  @param[in]  Cdb  A pointer to the command descriptor block.
+  @param[in]  Nic  A pointer to the Network interface controller data.
+
+  @retval EFI_SUCCESS           The request executed successfully.
+  @retval EFI_TIMEOUT           A timeout occurred executing the request.
+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.
+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.
+  @retval EFI_UNSUPPORTED       Not supported.
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EDKII_USB_ETHERNET_UNDI_MCAST_IPTOMAC)(
+  IN  PXE_CDB     *Cdb,
+  IN  NIC_DATA    *Nic
+  );
+
+/**
+  This command is used to read and write (if supported by NIC H/W)
+  nonvolatile storage on the NIC.
+
+  @param[in]  Cdb  A pointer to the command descriptor block.
+  @param[in]  Nic  A pointer to the Network interface controller data.
+
+  @retval EFI_SUCCESS           The request executed successfully.
+  @retval EFI_TIMEOUT           A timeout occurred executing the request.
+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.
+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.
+  @retval EFI_UNSUPPORTED       Not supported.
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EDKII_USB_ETHERNET_UNDI_NV_DATA)(
+  IN  PXE_CDB     *Cdb,
+  IN  NIC_DATA    *Nic
+  );
+
+/**
+  This command returns the current interrupt status and/or the
+  transmitted buffer addresses and the current media status.
+
+  @param[in]  Cdb  A pointer to the command descriptor block.
+  @param[in]  Nic  A pointer to the Network interface controller data.
+
+  @retval EFI_SUCCESS           The request executed successfully.
+  @retval EFI_TIMEOUT           A timeout occurred executing the request.
+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.
+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.
+  @retval EFI_UNSUPPORTED       Not supported.
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EDKII_USB_ETHERNET_UNDI_GET_STATUS)(
+  IN  PXE_CDB     *Cdb,
+  IN  NIC_DATA    *Nic
+  );
+
+/**
+  This command is used to fill the media header(s) in transmit packet(s).
+
+  @param[in]  Cdb  A pointer to the command descriptor block.
+  @param[in]  Nic  A pointer to the Network interface controller data.
+
+  @retval EFI_SUCCESS           The request executed successfully.
+  @retval EFI_TIMEOUT           A timeout occurred executing the request.
+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.
+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.
+  @retval EFI_UNSUPPORTED       Not supported.
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EDKII_USB_ETHERNET_UNDI_FILL_HEADER)(
+  IN  PXE_CDB     *Cdb,
+  IN  NIC_DATA    *Nic
+  );
+
+/**
+  The Transmit command is used to place a packet into the transmit queue.
+
+  @param[in]  Cdb  A pointer to the command descriptor block.
+  @param[in]  Nic  A pointer to the Network interface controller data.
+
+  @retval EFI_SUCCESS           The request executed successfully.
+  @retval EFI_TIMEOUT           A timeout occurred executing the request.
+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.
+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.
+  @retval EFI_UNSUPPORTED       Not supported.
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EDKII_USB_ETHERNET_UNDI_TRANSMIT)(
+  IN  PXE_CDB     *Cdb,
+  IN  NIC_DATA    *Nic
+  );
+
+/**
+  When the network adapter has received a frame, this command is used
+  to copy the frame into driver/application storage.
+
+  @param[in]  Cdb  A pointer to the command descriptor block.
+  @param[in]  Nic  A pointer to the Network interface controller data.
+
+  @retval EFI_SUCCESS           The request executed successfully.
+  @retval EFI_TIMEOUT           A timeout occurred executing the request.
+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.
+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.
+  @retval EFI_UNSUPPORTED       Not supported.
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EDKII_USB_ETHERNET_UNDI_RECEIVE)(
+  IN  PXE_CDB     *Cdb,
+  IN  NIC_DATA    *Nic
+  );
+
+/**
+  This command resets the network adapter and initializes UNDI using
+  the parameters supplied in the CPB.
+
+  @param[in]      Cdb  A pointer to the command descriptor block.
+  @param[in, out] Nic  A pointer to the Network interface controller data.
+
+  @retval EFI_SUCCESS           The request executed successfully.
+  @retval EFI_TIMEOUT           A timeout occurred executing the request.
+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.
+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.
+  @retval EFI_UNSUPPORTED       Not supported.
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EDKII_USB_ETHERNET_INITIALIZE)(
+  IN      PXE_CDB   *Cdb,
+  IN OUT  NIC_DATA  *Nic
+  );
+
+/**
+  This command is used to read and clear the NIC traffic statistics.
+
+  @param[in]  Nic     A pointer to the Network interface controller data.
+  @param[in]  DbAddr  Data Block Address.
+  @param[in]  DbSize  Data Block Size.
+
+  @retval EFI_SUCCESS           The request executed successfully.
+  @retval EFI_TIMEOUT           A timeout occurred executing the request.
+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.
+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.
+  @retval EFI_UNSUPPORTED       Not supported.
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EDKII_USB_ETHERNET_STATISTICS)(
+  IN  NIC_DATA    *Nic,
+  IN  UINT64      DbAddr,
+  IN  UINT16      DbSize
+  );
+
+/**
+  This function is used to manage a USB device with the bulk transfer pipe. The endpoint is Bulk in.
+
+  @param[in]      Cdb           A pointer to the command descriptor block.
+  @param[in]      This          A pointer to the EDKII_USB_ETHERNET_PROTOCOL instance.
+  @param[in, out] Packet        A pointer to the buffer of data that will be transmitted to USB
+                                device or received from USB device.
+  @param[in, out] PacketLength  A pointer to the PacketLength.
+
+  @retval EFI_SUCCESS           The bulk transfer has been successfully executed.
+  @retval EFI_DEVICE_ERROR      The transfer failed. The transfer status is returned in status.
+  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+  @retval EFI_OUT_OF_RESOURCES  The request could not be submitted due to a lack of resources.
+  @retval EFI_TIMEOUT           The control transfer fails due to timeout.
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EDKII_USB_ETHERNET_RECEIVE)(
+  IN     PXE_CDB               *Cdb,
+  IN     EDKII_USB_ETHERNET_PROTOCOL *This,
+  IN OUT VOID                  *Packet,
+  IN OUT UINTN                 *PacketLength
+  );
+
+/**
+  This function is used to manage a USB device with the bulk transfer pipe. The endpoint is Bulk out.
+
+  @param[in]      Cdb           A pointer to the command descriptor block.
+  @param[in]      This          A pointer to the EDKII_USB_ETHERNET_PROTOCOL instance.
+  @param[in, out] Packet        A pointer to the buffer of data that will be transmitted to USB
+                                device or received from USB device.
+  @param[in, out] PacketLength  A pointer to the PacketLength.
+
+  @retval EFI_SUCCESS           The bulk transfer has been successfully executed.
+  @retval EFI_DEVICE_ERROR      The transfer failed. The transfer status is returned in status.
+  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+  @retval EFI_OUT_OF_RESOURCES  The request could not be submitted due to a lack of resources.
+  @retval EFI_TIMEOUT           The control transfer fails due to timeout.
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EDKII_USB_ETHERNET_TRANSMIT)(
+  IN     PXE_CDB               *Cdb,
+  IN     EDKII_USB_ETHERNET_PROTOCOL *This,
+  IN OUT VOID                  *Packet,
+  IN OUT UINTN                 *PacketLength
+  );
+
+/**
+  This function is used to manage a USB device with an interrupt transfer pipe.
+
+  @param[in]  This              A pointer to the EDKII_USB_ETHERNET_PROTOCOL instance.
+  @param[in]  IsNewTransfer     If TRUE, a new transfer will be submitted to USB controller. If
+                                FALSE, the interrupt transfer is deleted from the device's interrupt
+                                transfer queue.
+  @param[in]  PollingInterval   Indicates the periodic rate, in milliseconds, that the transfer is to be
+                                executed.This parameter is required when IsNewTransfer is TRUE. The
+                                value must be between 1 to 255, otherwise EFI_INVALID_PARAMETER is returned.
+                                The units are in milliseconds.
+  @param[in]  Request           A pointer to the EFI_USB_DEVICE_REQUEST data.
+
+  @retval EFI_SUCCESS           The asynchronous USB transfer request transfer has been successfully executed.
+  @retval EFI_DEVICE_ERROR      The asynchronous USB transfer request failed.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EDKII_USB_ETHERNET_INTERRUPT)(
+  IN EDKII_USB_ETHERNET_PROTOCOL   *This,
+  IN BOOLEAN                 IsNewTransfer,
+  IN UINTN                   PollingInterval,
+  IN EFI_USB_DEVICE_REQUEST  *Request
+  );
+
+/**
+  Retrieves the USB Ethernet Mac Address.
+
+  @param[in]  This          A pointer to the EDKII_USB_ETHERNET_PROTOCOL instance.
+  @param[out] MacAddress    A pointer to the caller allocated USB Ethernet Mac Address.
+
+  @retval EFI_SUCCESS           The USB Header Functional descriptor was retrieved successfully.
+  @retval EFI_INVALID_PARAMETER UsbHeaderFunDescriptor is NULL.
+  @retval EFI_NOT_FOUND         The USB Header Functional descriptor was not found.
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EDKII_USB_GET_ETH_MAC_ADDRESS)(
+  IN  EDKII_USB_ETHERNET_PROTOCOL *This,
+  OUT EFI_MAC_ADDRESS       *MacAddress
+  );
+
+/**
+  Retrieves the USB Ethernet Bulk transfer data size.
+
+  @param[in]  This          A pointer to the EDKII_USB_ETHERNET_PROTOCOL instance.
+  @param[out] BulkSize      A pointer to the Bulk transfer data size.
+
+  @retval EFI_SUCCESS           The USB Header Functional descriptor was retrieved successfully.
+  @retval EFI_INVALID_PARAMETER UsbHeaderFunDescriptor is NULL.
+  @retval EFI_NOT_FOUND         The USB Header Functional descriptor was not found.
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EDKII_USB_ETH_MAX_BULK_SIZE)(
+  IN  EDKII_USB_ETHERNET_PROTOCOL *This,
+  OUT UINTN                 *BulkSize
+  );
+
+/**
+  Retrieves the USB Header functional Descriptor.
+
+  @param[in]  This                   A pointer to the EDKII_USB_ETHERNET_PROTOCOL instance.
+  @param[out] UsbHeaderFunDescriptor A pointer to the caller allocated USB Header Functional Descriptor.
+
+  @retval EFI_SUCCESS           The USB Header Functional descriptor was retrieved successfully.
+  @retval EFI_INVALID_PARAMETER UsbHeaderFunDescriptor is NULL.
+  @retval EFI_NOT_FOUND         The USB Header Functional descriptor was not found.
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EDKII_USB_HEADER_FUNCTIONAL_DESCRIPTOR)(
+  IN EDKII_USB_ETHERNET_PROTOCOL      *This,
+  OUT USB_HEADER_FUN_DESCRIPTOR *UsbHeaderFunDescriptor
+  );
+
+/**
+  Retrieves the USB Union functional Descriptor.
+
+  @param[in]  This                   A pointer to the EDKII_USB_ETHERNET_PROTOCOL instance.
+  @param[out] UsbUnionFunDescriptor  A pointer to the caller allocated USB Union Functional Descriptor.
+
+  @retval EFI_SUCCESS           The USB Union Functional descriptor was retrieved successfully.
+  @retval EFI_INVALID_PARAMETER UsbUnionFunDescriptor is NULL.
+  @retval EFI_NOT_FOUND         The USB Union Functional descriptor was not found.
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EDKII_USB_UNION_FUNCTIONAL_DESCRIPTOR)(
+  IN EDKII_USB_ETHERNET_PROTOCOL     *This,
+  OUT USB_UNION_FUN_DESCRIPTOR *UsbUnionFunDescriptor
+  );
+
+/**
+  Retrieves the USB Ethernet functional Descriptor.
+
+  @param[in]  This                   A pointer to the EDKII_USB_ETHERNET_PROTOCOL instance.
+  @param[out] UsbEthFunDescriptor    A pointer to the caller allocated USB Ethernet Functional Descriptor.
+
+  @retval EFI_SUCCESS           The USB Ethernet Functional descriptor was retrieved successfully.
+  @retval EFI_INVALID_PARAMETER UsbEthFunDescriptor is NULL.
+  @retval EFI_NOT_FOUND         The USB Ethernet Functional descriptor was not found.
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EDKII_USB_ETHERNET_FUNCTIONAL_DESCRIPTOR)(
+  IN EDKII_USB_ETHERNET_PROTOCOL        *This,
+  OUT USB_ETHERNET_FUN_DESCRIPTOR *UsbEthFunDescriptor
+  );
+
+/**
+  This request sets the Ethernet device multicast filters as specified in the
+  sequential list of 48 bit Ethernet multicast addresses.
+
+  @param[in]  This                   A pointer to the EDKII_USB_ETHERNET_PROTOCOL instance.
+  @param[in]  Value                  Number of filters.
+  @param[in]  McastAddr              A pointer to the value of the multicast addresses.
+
+  @retval EFI_SUCCESS           The request executed successfully.
+  @retval EFI_TIMEOUT           A timeout occurred executing the request.
+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.
+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.
+  @retval EFI_UNSUPPORTED       Not supported.
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EDKII_USB_ETHERNET_SET_ETH_MULTICAST_FILTERS)(
+  IN EDKII_USB_ETHERNET_PROTOCOL *This,
+  IN UINT16                Value,
+  IN VOID                  *McastAddr
+  );
+
+/**
+  This request sets up the specified Ethernet power management pattern filter as
+  described in the data structure.
+
+  @param[in]  This                   A pointer to the EDKII_USB_ETHERNET_PROTOCOL instance.
+  @param[in]  Value                  Number of filters.
+  @param[in]  Length                 Size of the power management pattern filter data.
+  @param[in]  PatternFilter          A pointer to the power management pattern filter structure.
+
+  @retval EFI_SUCCESS           The request executed successfully.
+  @retval EFI_TIMEOUT           A timeout occurred executing the request.
+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.
+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.
+  @retval EFI_UNSUPPORTED       Not supported.
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EDKII_USB_ETHERNET_SET_ETH_POWER_MANAGE_PATTERN_FILTER)(
+  IN EDKII_USB_ETHERNET_PROTOCOL *This,
+  IN UINT16                Value,
+  IN UINT16                Length,
+  IN VOID                  *PatternFilter
+  );
+
+/**
+  This request retrieves the status of the specified Ethernet power management
+  pattern filter from the device.
+
+  @param[in]  This                   A pointer to the EDKII_USB_ETHERNET_PROTOCOL instance.
+  @param[in]  Value                  The filter number.
+  @param[out] PatternActive          A pointer to the pattern active boolean.
+
+  @retval EFI_SUCCESS           The request executed successfully.
+  @retval EFI_TIMEOUT           A timeout occurred executing the request.
+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.
+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.
+  @retval EFI_UNSUPPORTED       Not supported.
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EDKII_USB_ETHERNET_GET_ETH_POWER_MANAGE_PATTERN_FILTER)(
+  IN   EDKII_USB_ETHERNET_PROTOCOL *This,
+  IN   UINT16                Value,
+  OUT  BOOLEAN               *PatternActive
+  );
+
+/**
+  This request is used to configure device Ethernet packet filter settings.
+
+  @param[in]  This              A pointer to the EDKII_USB_ETHERNET_PROTOCOL instance.
+  @param[in]  Value             Packet Filter Bitmap.
+
+  @retval EFI_SUCCESS           The request executed successfully.
+  @retval EFI_TIMEOUT           A timeout occurred executing the request.
+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.
+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.
+  @retval EFI_UNSUPPORTED       Not supported.
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EDKII_USB_ETHERNET_SET_ETH_PACKET_FILTER)(
+  IN EDKII_USB_ETHERNET_PROTOCOL *This,
+  IN UINT16                Value
+  );
+
+/**
+  This request is used to retrieve a statistic based on the feature selector.
+
+  @param[in]  This                  A pointer to the EDKII_USB_ETHERNET_PROTOCOL instance.
+  @param[in]  FeatureSelector       Value of the feature selector.
+  @param[out] Statistic             A pointer to the 32 bit unsigned integer.
+
+  @retval EFI_SUCCESS           The request executed successfully.
+  @retval EFI_TIMEOUT           A timeout occurred executing the request.
+  @retval EFI_DEVICE_ERROR      The request failed due to a device error.
+  @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.
+  @retval EFI_UNSUPPORTED       Not supported.
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EDKII_USB_ETHERNET_GET_ETH_STATISTIC)(
+  IN  EDKII_USB_ETHERNET_PROTOCOL  *This,
+  IN  UINT16                 FeatureSelector,
+  OUT VOID                   *Statistic
+  );
+
+typedef struct {
+  EDKII_USB_ETHERNET_UNDI_GET_STATE           UsbEthUndiGetState;
+  EDKII_USB_ETHERNET_UNDI_START               UsbEthUndiStart;
+  EDKII_USB_ETHERNET_UNDI_STOP                UsbEthUndiStop;
+  EDKII_USB_ETHERNET_UNDI_GET_INIT_INFO       UsbEthUndiGetInitInfo;
+  EDKII_USB_ETHERNET_UNDI_GET_CONFIG_INFO     UsbEthUndiGetConfigInfo;
+  EDKII_USB_ETHERNET_UNDI_INITIALIZE          UsbEthUndiInitialize;
+  EDKII_USB_ETHERNET_UNDI_RESET               UsbEthUndiReset;
+  EDKII_USB_ETHERNET_UNDI_SHUTDOWN            UsbEthUndiShutdown;
+  EDKII_USB_ETHERNET_UNDI_INTERRUPT_ENABLE    UsbEthUndiInterruptEnable;
+  EDKII_USB_ETHERNET_UNDI_RECEIVE_FILTER      UsbEthUndiReceiveFilter;
+  EDKII_USB_ETHERNET_UNDI_STATION_ADDRESS     UsbEthUndiStationAddress;
+  EDKII_USB_ETHERNET_UNDI_STATISTICS          UsbEthUndiStatistics;
+  EDKII_USB_ETHERNET_UNDI_MCAST_IPTOMAC       UsbEthUndiMcastIp2Mac;
+  EDKII_USB_ETHERNET_UNDI_NV_DATA             UsbEthUndiNvData;
+  EDKII_USB_ETHERNET_UNDI_GET_STATUS          UsbEthUndiGetStatus;
+  EDKII_USB_ETHERNET_UNDI_FILL_HEADER         UsbEthUndiFillHeader;
+  EDKII_USB_ETHERNET_UNDI_TRANSMIT            UsbEthUndiTransmit;
+  EDKII_USB_ETHERNET_UNDI_RECEIVE             UsbEthUndiReceive;
+} EDKII_USB_ETHERNET_UNDI;
+
+// The EDKII_USB_ETHERNET_PROTOCOL provides some basic USB Ethernet device relevant
+// descriptor and specific requests.
+struct _EDKII_USB_ETHERNET_PROTOCOL {
+  EDKII_USB_ETHERNET_UNDI                                   UsbEthUndi;
+  // for calling the UNDI child functions
+  EDKII_USB_ETHERNET_INITIALIZE                             UsbEthInitialize;
+  EDKII_USB_ETHERNET_STATISTICS                             UsbEthStatistics;
+  EDKII_USB_ETHERNET_RECEIVE                                UsbEthReceive;
+  EDKII_USB_ETHERNET_TRANSMIT                               UsbEthTransmit;
+  EDKII_USB_ETHERNET_INTERRUPT                              UsbEthInterrupt;
+  EDKII_USB_GET_ETH_MAC_ADDRESS                             UsbEthMacAddress;
+  EDKII_USB_ETH_MAX_BULK_SIZE                               UsbEthMaxBulkSize;
+  EDKII_USB_HEADER_FUNCTIONAL_DESCRIPTOR                    UsbHeaderFunDescriptor;
+  EDKII_USB_UNION_FUNCTIONAL_DESCRIPTOR                     UsbUnionFunDescriptor;
+  EDKII_USB_ETHERNET_FUNCTIONAL_DESCRIPTOR                  UsbEthFunDescriptor;
+  EDKII_USB_ETHERNET_SET_ETH_MULTICAST_FILTERS              SetUsbEthMcastFilter;
+  EDKII_USB_ETHERNET_SET_ETH_POWER_MANAGE_PATTERN_FILTER    SetUsbEthPowerPatternFilter;
+  EDKII_USB_ETHERNET_GET_ETH_POWER_MANAGE_PATTERN_FILTER    GetUsbEthPowerPatternFilter;
+  EDKII_USB_ETHERNET_SET_ETH_PACKET_FILTER                  SetUsbEthPacketFilter;
+  EDKII_USB_ETHERNET_GET_ETH_STATISTIC                      GetUsbEthStatistic;
+};
+
+extern EFI_GUID  gEdkIIUsbEthProtocolGuid;
+
+#endif
diff --git a/MdeModulePkg/MdeModulePkg.dec b/MdeModulePkg/MdeModulePkg.dec
index 3eb4a79bf7..860eec0f3c 100644
--- a/MdeModulePkg/MdeModulePkg.dec
+++ b/MdeModulePkg/MdeModulePkg.dec
@@ -685,6 +685,9 @@
   ## Include/Protocol/VariablePolicy.h
   gEdkiiVariablePolicyProtocolGuid = { 0x81D1675C, 0x86F6, 0x48DF, { 0xBD, 0x95, 0x9A, 0x6E, 0x4F, 0x09, 0x25, 0xC3 } }

+  ## Include/Protocol/UsbEthernetProtocol.h
+  gEdkIIUsbEthProtocolGuid = { 0x8d8969cc, 0xfeb0, 0x4303, { 0xb2, 0x1a, 0x1f, 0x11, 0x6f, 0x38, 0x56, 0x43 } }
+
 [PcdsFeatureFlag]
   ## Indicates if the platform can support update capsule across a system reset.<BR><BR>
   #   TRUE  - Supports update capsule across a system reset.<BR>
@@ -2108,6 +2111,21 @@
   # @Prompt The shared bit mask when Intel Tdx is enabled.
   gEfiMdeModulePkgTokenSpaceGuid.PcdTdxSharedBitMask|0x0|UINT64|0x10000025

+  ## Indicates if the Usb Network rate limiting Supported.<BR><BR>
+  #   TRUE  - Usb Network rate limiting is supported.<BR>
+  #   FALSE - Usb Network rate limiting is not supported.<BR>
+  # @Prompt Enable Usb Network rate limiting support.
+  gEfiMdeModulePkgTokenSpaceGuid.PcdEnableUsbNetworkRateLimiting|FALSE|BOOLEAN|0x10000026
+
+  ## The rate limiting Credit value is check in rate limiter event.
+  #  It is to control the RateLimitingCreditCount max value.
+  # @Prompt The value is use for Usb Network rate limiting supported.
+  gEfiMdeModulePkgTokenSpaceGuid.PcdUsbNetworkRateLimitingCredit|10|UINT32|0x10000027
+
+  ## The value of rate limiter event for timeout check. Default value is 100(unit 1ms).
+  # @Prompt The value is use for Usb Network rate limiting supported.
+  gEfiMdeModulePkgTokenSpaceGuid.PcdUsbNetworkRateLimitingFactor|100|UINT32|0x10000028
+
 [PcdsPatchableInModule]
   ## Specify memory size with page number for PEI code when
   #  Loading Module at Fixed Address feature is enabled.
diff --git a/MdeModulePkg/MdeModulePkg.dsc b/MdeModulePkg/MdeModulePkg.dsc
index 1014598f31..0aadd405f3 100644
--- a/MdeModulePkg/MdeModulePkg.dsc
+++ b/MdeModulePkg/MdeModulePkg.dsc
@@ -274,6 +274,10 @@
   MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassStorageDxe.inf
   MdeModulePkg/Bus/Usb/UsbMouseAbsolutePointerDxe/UsbMouseAbsolutePointerDxe.inf
   MdeModulePkg/Bus/Usb/UsbMouseDxe/UsbMouseDxe.inf
+  MdeModulePkg/Bus/Usb/UsbNetwork/NetworkCommon/NetworkCommon.inf
+  MdeModulePkg/Bus/Usb/UsbNetwork/UsbCdcEcm/UsbCdcEcm.inf
+  MdeModulePkg/Bus/Usb/UsbNetwork/UsbCdcNcm/UsbCdcNcm.inf
+  MdeModulePkg/Bus/Usb/UsbNetwork/UsbRndis/UsbRndis.inf
   MdeModulePkg/Bus/I2c/I2cDxe/I2cBusDxe.inf
   MdeModulePkg/Bus/I2c/I2cDxe/I2cHostDxe.inf
   MdeModulePkg/Bus/I2c/I2cDxe/I2cDxe.inf
--
2.35.1.windows.2
-The information contained in this message may be confidential and proprietary to American Megatrends (AMI). This communication is intended to be read only by the individual or entity to whom it is addressed or by their designee. If the reader of this message is not the intended recipient, you are on notice that any distribution of this message, in any form, is strictly prohibited. Please promptly notify the sender by reply e-mail or by telephone at 770-246-8600, and then delete or destroy all copies of the transmission.





-The information contained in this message may be confidential and proprietary to American Megatrends (AMI). This communication is intended to be read only by the individual or entity to whom it is addressed or by their designee. If the reader of this message is not the intended recipient, you are on notice that any distribution of this message, in any form, is strictly prohibited. Please promptly notify the sender by reply e-mail or by telephone at 770-246-8600, and then delete or destroy all copies of the transmission.


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#105889): https://edk2.groups.io/g/devel/message/105889
Mute This Topic: https://groups.io/mt/99398816/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