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

Wu, Hao A hao.a.wu at intel.com
Mon May 8 03:28:00 UTC 2023


> -----Original Message-----
> From: Richard Ho (ºÎÃ÷ÖÒ) <RichardHo at ami.com>
> Sent: Monday, May 8, 2023 11:18 AM
> To: devel at edk2.groups.io
> Cc: Andrew Fish <afish at apple.com>; Leif Lindholm
> <quic_llindhol at quicinc.com>; Kinney, Michael D
> <michael.d.kinney at intel.com>; Michael Kubacki
> <mikuback at linux.microsoft.com>; Liu, Zhiguang <zhiguang.liu at intel.com>;
> Gao, Liming <gaoliming at byosoft.com.cn>; Wu, Hao A <hao.a.wu at intel.com>;
> Ni, Ray <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: [PATCH v2 1/3] MdeModulePkg/Bus/Usb/UsbNetwork/UsbRndis: Add
> USB RNDIS devices support
> 
> 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                               |    7 +
>  .../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, 7286 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..24d62af760 100644
> --- a/Maintainers.txt
> +++ b/Maintainers.txt
> @@ -437,6 +437,13 @@ 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]
> +R: Hao A Wu <hao.a.wu at intel.com> [hwu25]


Sorry, could you help to remove me (Hao A Wu) as the proposed reviewer for this UsbNetwork module?
I do not think I have sufficient knowledge in this domain.

Sorry again for this.

Best Regards,
Hao Wu


> +
>  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)NetworkCommonComponent
> NameGetDriverName,
> +
> (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME)NetworkCommonComp
> onentNameGetControllerName,
> +  "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.in
> f
> b/MdeModulePkg/Bus/Usb/UsbNetwork/NetworkCommon/NetworkCommon.in
> f
> new file mode 100644
> index 0000000000..f9ca31fe80
> --- /dev/null
> +++
> b/MdeModulePkg/Bus/Usb/UsbNetwork/NetworkCommon/NetworkCommon.in
> f
> @@ -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)UsbRndisComponentNameGet
> DriverName,
> +
> (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME)UsbRndisComponentNa
> meGetControllerName,
> +  "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|0x100
> 00025
> 
> +  ## 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|UIN
> T32|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|UI
> NT32|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/UsbMouseAbsolutePoi
> nterDxe.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.


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