summaryrefslogtreecommitdiffstats
path: root/MdeModulePkg
diff options
context:
space:
mode:
authorRichard Ho <richardho@ami.com>2023-07-13 14:15:32 +0800
committermergify[bot] <37929162+mergify[bot]@users.noreply.github.com>2023-07-13 17:18:40 +0000
commitfc0d5922f1f866b47561545ebd15883d618c91bf (patch)
tree63601d953389118e103ed6ce84bae66dfe121581 /MdeModulePkg
parent8dab4eebe435fc28cae329867a74cee45d040d3e (diff)
downloadedk2-fc0d5922f1f866b47561545ebd15883d618c91bf.tar.gz
edk2-fc0d5922f1f866b47561545ebd15883d618c91bf.tar.bz2
edk2-fc0d5922f1f866b47561545ebd15883d618c91bf.zip
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@ami.com> Cc: Andrew Fish <afish@apple.com> Cc: Leif Lindholm <quic_llindhol@quicinc.com> Cc: Michael D Kinney <michael.d.kinney@intel.com> Cc: Michael Kubacki <mikuback@linux.microsoft.com> Cc: Zhiguang Liu <zhiguang.liu@intel.com> Cc: Liming Gao <gaoliming@byosoft.com.cn> Cc: Hao A Wu <hao.a.wu@intel.com> Cc: Ray Ni <ray.ni@intel.com> Tested-by: Tinh Nguyen <tinhnguyen@os.amperecomputing.com> Acked-by: Hao A Wu <hao.a.wu@intel.com> Reviewed-by: Rebecca Cran <rebecca@bsdio.com> Reviewed-by: Tony Lo <tonylo@ami.com>
Diffstat (limited to 'MdeModulePkg')
-rw-r--r--MdeModulePkg/Bus/Usb/UsbNetwork/NetworkCommon/ComponentName.c263
-rw-r--r--MdeModulePkg/Bus/Usb/UsbNetwork/NetworkCommon/DriverBinding.c595
-rw-r--r--MdeModulePkg/Bus/Usb/UsbNetwork/NetworkCommon/DriverBinding.h266
-rw-r--r--MdeModulePkg/Bus/Usb/UsbNetwork/NetworkCommon/NetworkCommon.inf48
-rw-r--r--MdeModulePkg/Bus/Usb/UsbNetwork/NetworkCommon/PxeFunction.c1805
-rw-r--r--MdeModulePkg/Bus/Usb/UsbNetwork/UsbRndis/ComponentName.c172
-rw-r--r--MdeModulePkg/Bus/Usb/UsbNetwork/UsbRndis/UsbRndis.c886
-rw-r--r--MdeModulePkg/Bus/Usb/UsbNetwork/UsbRndis/UsbRndis.h586
-rw-r--r--MdeModulePkg/Bus/Usb/UsbNetwork/UsbRndis/UsbRndis.inf42
-rw-r--r--MdeModulePkg/Bus/Usb/UsbNetwork/UsbRndis/UsbRndisFunction.c1721
-rw-r--r--MdeModulePkg/Include/Protocol/UsbEthernetProtocol.h878
-rw-r--r--MdeModulePkg/MdeModulePkg.dec18
12 files changed, 7280 insertions, 0 deletions
diff --git a/MdeModulePkg/Bus/Usb/UsbNetwork/NetworkCommon/ComponentName.c b/MdeModulePkg/Bus/Usb/UsbNetwork/NetworkCommon/ComponentName.c
new file mode 100644
index 0000000000..2e40702390
--- /dev/null
+++ b/MdeModulePkg/Bus/Usb/UsbNetwork/NetworkCommon/ComponentName.c
@@ -0,0 +1,263 @@
+/** @file
+ This file contains code for USB network common driver
+ component name definitions
+
+ Copyright (c) 2023, American Megatrends International LLC. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include "DriverBinding.h"
+
+extern EFI_DRIVER_BINDING_PROTOCOL gNetworkCommonDriverBinding;
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE gNetworkCommonDriverNameTable[] = {
+ {
+ "eng;en",
+ L"Network Common Driver"
+ },
+ {
+ NULL,
+ NULL
+ }
+};
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE *gNetworkCommonControllerNameTable = NULL;
+
+EFI_STATUS
+EFIAPI
+NetworkCommonComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ );
+
+EFI_STATUS
+EFIAPI
+NetworkCommonComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ );
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gNetworkCommonComponentName = {
+ NetworkCommonComponentNameGetDriverName,
+ NetworkCommonComponentNameGetControllerName,
+ "eng"
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gNetworkCommonComponentName2 = {
+ (EFI_COMPONENT_NAME2_GET_DRIVER_NAME)NetworkCommonComponentNameGetDriverName,
+ (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME)NetworkCommonComponentNameGetControllerName,
+ "en"
+};
+
+/**
+ Retrieves a Unicode string that is the user readable name of the driver.
+
+ This function retrieves the user readable name of a driver in the form of a
+ Unicode string. If the driver specified by This has a user readable name in
+ the language specified by Language, then a pointer to the driver name is
+ returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+ by This does not support the language specified by Language,
+ then EFI_UNSUPPORTED is returned.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+ @param[in] Language A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified
+ in RFC 4646 or ISO 639-2 language code format.
+ @param[out] DriverName A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ driver specified by This in the language
+ specified by Language.
+
+ @retval EFI_SUCCESS The Unicode string for the Driver specified by
+ This and the language specified by Language was
+ returned in DriverName.
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+ @retval EFI_INVALID_PARAMETER DriverName is NULL.
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+NetworkCommonComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ )
+{
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ gNetworkCommonDriverNameTable,
+ DriverName,
+ (BOOLEAN)(This == &gNetworkCommonComponentName)
+ );
+}
+
+/**
+ Retrieves a Unicode string that is the user readable name of the controller
+ that is being managed by a driver.
+
+ This function retrieves the user readable name of the controller specified by
+ ControllerHandle and ChildHandle in the form of a Unicode string. If the
+ driver specified by This has a user readable name in the language specified by
+ Language, then a pointer to the controller name is returned in ControllerName,
+ and EFI_SUCCESS is returned. If the driver specified by This is not currently
+ managing the controller specified by ControllerHandle and ChildHandle,
+ then EFI_UNSUPPORTED is returned. If the driver specified by This does not
+ support the language specified by Language, then EFI_UNSUPPORTED is returned.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+ @param[in] Controller The handle of a controller that the driver
+ specified by This is managing. This handle
+ specifies the controller whose name is to be
+ returned.
+ @param[in] ChildHandle The handle of the child controller to retrieve
+ the name of. This is an optional parameter that
+ may be NULL. It will be NULL for device
+ drivers. It will also be NULL for a bus drivers
+ that wish to retrieve the name of the bus
+ controller. It will not be NULL for a bus
+ driver that wishes to retrieve the name of a
+ child controller.
+ @param[in] Language A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified in
+ RFC 4646 or ISO 639-2 language code format.
+ @param[out] ControllerName A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ controller specified by ControllerHandle and
+ ChildHandle in the language specified by
+ Language from the point of view of the driver
+ specified by This.
+
+ @retval EFI_SUCCESS The Unicode string for the user readable name in
+ the language specified by Language for the
+ driver specified by This was returned in
+ DriverName.
+ @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.
+ @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid
+ EFI_HANDLE.
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+ @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+ @retval EFI_UNSUPPORTED The driver specified by This is not currently
+ managing the controller specified by
+ ControllerHandle and ChildHandle.
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+NetworkCommonComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ )
+{
+ EFI_STATUS Status;
+ CHAR16 *HandleName;
+ EFI_USB_IO_PROTOCOL *UsbIo;
+ EFI_USB_DEVICE_DESCRIPTOR DevDesc;
+
+ if ((Language == NULL) || (ControllerName == NULL)) {
+ 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..1de194d8e6
--- /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 == NULL) {
+ 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..0fafff73e9
--- /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;
+} ETHERNET_HEADER;
+#pragma pack()
+
+typedef struct {
+ UINTN Signature;
+ EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL NiiProtocol;
+ EFI_HANDLE DeviceHandle;
+ EFI_DEVICE_PATH_PROTOCOL *BaseDevPath;
+ EFI_DEVICE_PATH_PROTOCOL *DevPath;
+ NIC_DATA NicInfo;
+ VOID *ReceiveBuffer;
+} NIC_DEVICE;
+
+typedef VOID (*API_FUNC)(
+ PXE_CDB *,
+ NIC_DATA *
+ );
+
+extern PXE_SW_UNDI *gPxe;
+extern NIC_DEVICE *gLanDeviceList[MAX_LAN_INTERFACE];
+extern EFI_COMPONENT_NAME2_PROTOCOL gNetworkCommonComponentName2;
+
+EFI_STATUS
+EFIAPI
+NetworkCommonSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ );
+
+EFI_STATUS
+EFIAPI
+NetworkCommonDriverStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ );
+
+EFI_STATUS
+EFIAPI
+NetworkCommonDriverStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ );
+
+VOID
+PxeStructInit (
+ OUT PXE_SW_UNDI *PxeSw
+ );
+
+VOID
+UpdateNicNum (
+ IN NIC_DATA *Nic,
+ IN OUT PXE_SW_UNDI *PxeSw
+ );
+
+EFI_STATUS
+EFIAPI
+UndiApiEntry (
+ IN UINT64 Cdb
+ );
+
+UINTN
+MapIt (
+ IN NIC_DATA *Nic,
+ IN UINT64 MemAddr,
+ IN UINT32 Size,
+ IN UINT32 Direction,
+ OUT UINT64 MappedAddr
+ );
+
+VOID
+UnMapIt (
+ IN NIC_DATA *Nic,
+ IN UINT64 MemAddr,
+ IN UINT32 Size,
+ IN UINT32 Direction,
+ IN UINT64 MappedAddr
+ );
+
+VOID
+UndiGetState (
+ IN PXE_CDB *Cdb,
+ IN NIC_DATA *Nic
+ );
+
+VOID
+UndiStart (
+ IN PXE_CDB *Cdb,
+ IN NIC_DATA *Nic
+ );
+
+VOID
+UndiStop (
+ IN PXE_CDB *Cdb,
+ IN NIC_DATA *Nic
+ );
+
+VOID
+UndiGetInitInfo (
+ IN PXE_CDB *Cdb,
+ IN NIC_DATA *Nic
+ );
+
+VOID
+UndiGetConfigInfo (
+ IN PXE_CDB *Cdb,
+ IN NIC_DATA *Nic
+ );
+
+VOID
+UndiInitialize (
+ IN PXE_CDB *Cdb,
+ IN OUT NIC_DATA *Nic
+ );
+
+VOID
+UndiReset (
+ IN PXE_CDB *Cdb,
+ IN NIC_DATA *Nic
+ );
+
+VOID
+UndiShutdown (
+ IN PXE_CDB *Cdb,
+ IN OUT NIC_DATA *Nic
+ );
+
+VOID
+UndiInterruptEnable (
+ IN PXE_CDB *Cdb,
+ IN NIC_DATA *Nic
+ );
+
+VOID
+UndiReceiveFilter (
+ IN PXE_CDB *Cdb,
+ IN NIC_DATA *Nic
+ );
+
+VOID
+UndiStationAddress (
+ IN PXE_CDB *Cdb,
+ IN NIC_DATA *Nic
+ );
+
+VOID
+UndiStatistics (
+ IN PXE_CDB *Cdb,
+ IN NIC_DATA *Nic
+ );
+
+VOID
+UndiMcastIp2Mac (
+ IN OUT PXE_CDB *Cdb,
+ IN NIC_DATA *Nic
+ );
+
+VOID
+UndiNvData (
+ IN PXE_CDB *Cdb,
+ IN NIC_DATA *Nic
+ );
+
+VOID
+UndiGetStatus (
+ IN PXE_CDB *Cdb,
+ IN NIC_DATA *Nic
+ );
+
+VOID
+UndiFillHeader (
+ IN PXE_CDB *Cdb,
+ IN NIC_DATA *Nic
+ );
+
+VOID
+UndiTransmit (
+ IN PXE_CDB *Cdb,
+ IN NIC_DATA *Nic
+ );
+
+VOID
+UndiReceive (
+ IN PXE_CDB *Cdb,
+ IN NIC_DATA *Nic
+ );
+
+UINT16
+Initialize (
+ IN PXE_CDB *Cdb,
+ IN OUT NIC_DATA *Nic
+ );
+
+UINT16
+Transmit (
+ IN PXE_CDB *Cdb,
+ IN OUT NIC_DATA *Nic,
+ IN UINT64 CpbAddr,
+ IN UINT16 OpFlags
+ );
+
+UINT16
+Receive (
+ IN PXE_CDB *Cdb,
+ IN OUT NIC_DATA *Nic,
+ IN UINT64 CpbAddr,
+ IN OUT UINT64 DbAddr
+ );
+
+UINT16
+SetFilter (
+ IN NIC_DATA *Nic,
+ IN UINT16 SetFilter,
+ IN UINT64 CpbAddr,
+ IN UINT32 CpbSize
+ );
+
+UINT16
+Statistics (
+ IN NIC_DATA *Nic,
+ IN UINT64 DbAddr,
+ IN UINT16 DbSize
+ );
+
+#endif
diff --git a/MdeModulePkg/Bus/Usb/UsbNetwork/NetworkCommon/NetworkCommon.inf b/MdeModulePkg/Bus/Usb/UsbNetwork/NetworkCommon/NetworkCommon.inf
new file mode 100644
index 0000000000..16a068d313
--- /dev/null
+++ b/MdeModulePkg/Bus/Usb/UsbNetwork/NetworkCommon/NetworkCommon.inf
@@ -0,0 +1,48 @@
+## @file
+# This is Usb Network Common driver for DXE phase.
+#
+# Copyright (c) 2023, American Megatrends International LLC. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = NetworkCommon
+ FILE_GUID = ca6eb4f4-f1d6-4375-97d6-18856871e1bf
+ MODULE_TYPE = UEFI_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..daa30f0815
--- /dev/null
+++ b/MdeModulePkg/Bus/Usb/UsbNetwork/NetworkCommon/PxeFunction.c
@@ -0,0 +1,1805 @@
+/** @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;
+
+ 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;
+ ETHERNET_HEADER *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 = (ETHERNET_HEADER *)(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 = (ETHERNET_HEADER *)(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;
+ ETHERNET_HEADER *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 = (ETHERNET_HEADER *)BulkInData;
+
+ Db->FrameLen = (UINT32)DataLength;
+ Db->MediaHeaderLen = PXE_MAC_HEADER_LEN_ETHER;
+
+ for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) {
+ if (Header->DestAddr[Index] != Nic->CurrentNodeAddress[Index]) {
+ break;
+ }
+ }
+
+ if (Index >= PXE_HWADDR_LEN_ETHER) {
+ FrameType = PXE_FRAME_TYPE_UNICAST;
+ } else {
+ for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) {
+ if (Header->DestAddr[Index] != Nic->BroadcastNodeAddress[Index]) {
+ break;
+ }
+ }
+
+ if (Index >= PXE_HWADDR_LEN_ETHER) {
+ FrameType = PXE_FRAME_TYPE_BROADCAST;
+ } else {
+ if ((Header->DestAddr[0] & 1) == 1) {
+ FrameType = PXE_FRAME_TYPE_FILTERED_MULTICAST;
+ } else {
+ FrameType = PXE_FRAME_TYPE_PROMISCUOUS;
+ }
+ }
+ }
+
+ Db->Type = FrameType;
+ Db->Protocol = Header->Protocol;
+
+ for (Index = 0; Index < PXE_HWADDR_LEN_ETHER; Index++) {
+ Db->SrcAddr[Index] = Header->SrcAddr[Index];
+ Db->DestAddr[Index] = Header->DestAddr[Index];
+ }
+ }
+
+ if (FrameType == PXE_FRAME_TYPE_NONE) {
+ Nic->ReceiveStatus = 0;
+ } else {
+ Nic->ReceiveStatus = 1;
+ }
+
+ return PXE_STATCODE_SUCCESS;
+}
+
+/**
+ Fill out PXE SW UNDI structure.
+
+ @param[out] PxeSw A pointer to the PXE SW UNDI structure.
+
+**/
+VOID
+PxeStructInit (
+ OUT PXE_SW_UNDI *PxeSw
+ )
+{
+ PxeSw->Signature = PXE_ROMID_SIGNATURE;
+ PxeSw->Len = (UINT8)sizeof (PXE_SW_UNDI);
+ PxeSw->Fudge = 0;
+ PxeSw->IFcnt = 0;
+ PxeSw->IFcntExt = 0;
+ PxeSw->Rev = PXE_ROMID_REV;
+ PxeSw->MajorVer = PXE_ROMID_MAJORVER;
+ PxeSw->MinorVer = PXE_ROMID_MINORVER;
+ PxeSw->reserved1 = 0;
+
+ PxeSw->Implementation = PXE_ROMID_IMP_SW_VIRT_ADDR |
+ PXE_ROMID_IMP_FRAG_SUPPORTED |
+ PXE_ROMID_IMP_CMD_LINK_SUPPORTED |
+ PXE_ROMID_IMP_STATION_ADDR_SETTABLE |
+ PXE_ROMID_IMP_PROMISCUOUS_MULTICAST_RX_SUPPORTED |
+ PXE_ROMID_IMP_PROMISCUOUS_RX_SUPPORTED |
+ PXE_ROMID_IMP_BROADCAST_RX_SUPPORTED |
+ PXE_ROMID_IMP_FILTERED_MULTICAST_RX_SUPPORTED;
+
+ PxeSw->EntryPoint = (UINT64)(UINTN)UndiApiEntry;
+ PxeSw->reserved2[0] = 0;
+ PxeSw->reserved2[1] = 0;
+ PxeSw->reserved2[2] = 0;
+ PxeSw->BusCnt = 1;
+ PxeSw->BusType[0] = PXE_BUSTYPE_USB;
+ PxeSw->Fudge = PxeSw->Fudge - CalculateSum8 ((VOID *)PxeSw, PxeSw->Len);
+}
+
+/**
+ Update NIC number.
+
+ @param[in] Nic A pointer to the Network interface controller data.
+ @param[in, out] PxeSw A pointer to the PXE SW UNDI structure.
+
+**/
+VOID
+UpdateNicNum (
+ IN NIC_DATA *Nic,
+ IN OUT PXE_SW_UNDI *PxeSw
+ )
+{
+ UINT16 NicNum;
+
+ NicNum = (PxeSw->IFcnt | PxeSw->IFcntExt << 8);
+
+ if (Nic == NULL) {
+ if (NicNum > 0) {
+ NicNum--;
+ }
+
+ PxeSw->IFcnt = (UINT8)(NicNum & 0xFF); // Get lower byte
+ PxeSw->IFcntExt = (UINT8)((NicNum & 0xFF00) >> 8); // Get upper byte
+ PxeSw->Fudge = (UINT8)(PxeSw->Fudge - CalculateSum8 ((VOID *)PxeSw, PxeSw->Len));
+ return;
+ }
+
+ NicNum++;
+
+ PxeSw->IFcnt = (UINT8)(NicNum & 0xFF); // Get lower byte
+ PxeSw->IFcntExt = (UINT8)((NicNum & 0xFF00) >> 8); // Get upper byte
+ PxeSw->Fudge = (UINT8)(PxeSw->Fudge - CalculateSum8 ((VOID *)PxeSw, PxeSw->Len));
+}
+
+/**
+ UNDI API table entry.
+
+ @param[in] Cdb A pointer to the command descriptor block.
+
+**/
+EFI_STATUS
+EFIAPI
+UndiApiEntry (
+ IN UINT64 Cdb
+ )
+{
+ PXE_CDB *CdbPtr;
+ NIC_DATA *Nic;
+
+ if (Cdb == 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ CdbPtr = (PXE_CDB *)(UINTN)Cdb;
+ Nic = &(gLanDeviceList[CdbPtr->IFnum]->NicInfo);
+ gUndiApiTable[CdbPtr->OpCode](CdbPtr, Nic);
+ return EFI_SUCCESS;
+}
+
+/**
+ Map virtual memory address for DMA. This field can be set to
+ zero if there is no mapping service.
+
+ @param[in] Nic A pointer to the Network interface controller data.
+ @param[in] MemAddr Virtual address to be mapped.
+ @param[in] Size Size of memory to be mapped.
+ @param[in] Direction Direction of data flow for this memory's usage:
+ cpu->device, device->cpu or both ways.
+ @param[out] MappedAddr Pointer to return the mapped device address.
+
+**/
+UINTN
+MapIt (
+ IN NIC_DATA *Nic,
+ IN UINT64 MemAddr,
+ IN UINT32 Size,
+ IN UINT32 Direction,
+ OUT UINT64 MappedAddr
+ )
+{
+ UINT64 *PhyAddr;
+
+ PhyAddr = (UINT64 *)(UINTN)MappedAddr;
+
+ if (Nic->PxeStart.Map_Mem == 0) {
+ *PhyAddr = MemAddr;
+ } else {
+ ((void (*)(UINT64, UINT64, UINT32, UINT32, UINT64))(UINTN) Nic->PxeStart.Map_Mem)(
+ Nic->PxeStart.Unique_ID,
+ MemAddr,
+ Size,
+ Direction,
+ MappedAddr
+ );
+ }
+
+ return PXE_STATCODE_SUCCESS;
+}
+
+/**
+ Un-map previously mapped virtual memory address. This field can be set
+ to zero only if the Map_Mem() service is also set to zero.
+
+ @param[in] Nic A pointer to the Network interface controller data.
+ @param[in] MemAddr Virtual address to be mapped.
+ @param[in] Size Size of memory to be mapped.
+ @param[in] Direction Direction of data flow for this memory's usage:
+ cpu->device, device->cpu or both ways.
+ @param[in] MappedAddr Pointer to return the mapped device address.
+
+**/
+VOID
+UnMapIt (
+ IN NIC_DATA *Nic,
+ IN UINT64 MemAddr,
+ IN UINT32 Size,
+ IN UINT32 Direction,
+ IN UINT64 MappedAddr
+ )
+{
+ if (Nic->PxeStart.UnMap_Mem != 0) {
+ ((void (*)(UINT64, UINT64, UINT32, UINT32, UINT64))(UINTN) Nic->PxeStart.UnMap_Mem)(
+ Nic->PxeStart.Unique_ID,
+ MemAddr,
+ Size,
+ Direction,
+ MappedAddr
+ );
+ }
+
+ return;
+}
diff --git a/MdeModulePkg/Bus/Usb/UsbNetwork/UsbRndis/ComponentName.c b/MdeModulePkg/Bus/Usb/UsbNetwork/UsbRndis/ComponentName.c
new file mode 100644
index 0000000000..b9ba170c13
--- /dev/null
+++ b/MdeModulePkg/Bus/Usb/UsbNetwork/UsbRndis/ComponentName.c
@@ -0,0 +1,172 @@
+/** @file
+ This file contains code for USB RNDIS Driver Component
+ Name definitions
+
+ Copyright (c) 2023, American Megatrends International LLC. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include "UsbRndis.h"
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE gUsbRndisDriverNameTable[] = {
+ {
+ "eng;en",
+ L"USB RNDIS Driver"
+ },
+ {
+ NULL,
+ NULL
+ }
+};
+
+EFI_STATUS
+EFIAPI
+UsbRndisComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ );
+
+EFI_STATUS
+EFIAPI
+UsbRndisComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ );
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gUsbRndisComponentName = {
+ UsbRndisComponentNameGetDriverName,
+ UsbRndisComponentNameGetControllerName,
+ "eng"
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gUsbRndisComponentName2 = {
+ (EFI_COMPONENT_NAME2_GET_DRIVER_NAME)UsbRndisComponentNameGetDriverName,
+ (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME)UsbRndisComponentNameGetControllerName,
+ "en"
+};
+
+/**
+ Retrieves a Unicode string that is the user readable name of the driver.
+
+ This function retrieves the user readable name of a driver in the form of a
+ Unicode string. If the driver specified by This has a user readable name in
+ the language specified by Language, then a pointer to the driver name is
+ returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+ by This does not support the language specified by Language,
+ then EFI_UNSUPPORTED is returned.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+ @param[in] Language A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified
+ in RFC 4646 or ISO 639-2 language code format.
+ @param[out] DriverName A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ driver specified by This in the language
+ specified by Language.
+
+ @retval EFI_SUCCESS The Unicode string for the Driver specified by
+ This and the language specified by Language was
+ returned in DriverName.
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+ @retval EFI_INVALID_PARAMETER DriverName is NULL.
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+UsbRndisComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ )
+{
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ gUsbRndisDriverNameTable,
+ DriverName,
+ (BOOLEAN)(This == &gUsbRndisComponentName)
+ );
+}
+
+/**
+ Retrieves a Unicode string that is the user readable name of the controller
+ that is being managed by a driver.
+
+ This function retrieves the user readable name of the controller specified by
+ ControllerHandle and ChildHandle in the form of a Unicode string. If the
+ driver specified by This has a user readable name in the language specified by
+ Language, then a pointer to the controller name is returned in ControllerName,
+ and EFI_SUCCESS is returned. If the driver specified by This is not currently
+ managing the controller specified by ControllerHandle and ChildHandle,
+ then EFI_UNSUPPORTED is returned. If the driver specified by This does not
+ support the language specified by Language, then EFI_UNSUPPORTED is returned.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+ @param[in] Controller The handle of a controller that the driver
+ specified by This is managing. This handle
+ specifies the controller whose name is to be
+ returned.
+ @param[in] ChildHandle The handle of the child controller to retrieve
+ the name of. This is an optional parameter that
+ may be NULL. It will be NULL for device
+ drivers. It will also be NULL for a bus drivers
+ that wish to retrieve the name of the bus
+ controller. It will not be NULL for a bus
+ driver that wishes to retrieve the name of a
+ child controller.
+ @param[in] Language A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified in
+ RFC 4646 or ISO 639-2 language code format.
+ @param[out] ControllerName A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ controller specified by ControllerHandle and
+ ChildHandle in the language specified by
+ Language from the point of view of the driver
+ specified by This.
+
+ @retval EFI_SUCCESS The Unicode string for the user readable name in
+ the language specified by Language for the
+ driver specified by This was returned in
+ DriverName.
+ @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.
+ @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid
+ EFI_HANDLE.
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+ @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+ @retval EFI_UNSUPPORTED The driver specified by This is not currently
+ managing the controller specified by
+ ControllerHandle and ChildHandle.
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+UsbRndisComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ )
+{
+ return EFI_UNSUPPORTED;
+}
diff --git a/MdeModulePkg/Bus/Usb/UsbNetwork/UsbRndis/UsbRndis.c b/MdeModulePkg/Bus/Usb/UsbNetwork/UsbRndis/UsbRndis.c
new file mode 100644
index 0000000000..056b0ff0fd
--- /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] UsbCdcDataPath 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..5211d85fa1
--- /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;
+
+typedef struct {
+ UINT16 Src;
+ UINT16 Dst;
+} BIT_MAP;
+
+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..7e6a8cf064
--- /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 = UEFI_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..b3632233ad
--- /dev/null
+++ b/MdeModulePkg/Bus/Usb/UsbNetwork/UsbRndis/UsbRndisFunction.c
@@ -0,0 +1,1721 @@
+/** @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] Requst 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) {
+ 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] UsbRndisDevice 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
+ );
+}
+
+BIT_MAP gTable[] = {
+ { 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 },
+};
+
+/**
+
+ 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;
+
+ Count = sizeof (gTable)/sizeof (gTable[0]);
+
+ for (Index = 0; (gTable[Index].Src != 0) && (Index < Count); Index++) {
+ if (gTable[Index].Src & Value) {
+ *CdcFilter |= gTable[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;
+ EFI_USB_DEVICE_REQUEST DevReq;
+ UINT32 UsbStatus;
+ EFI_STATUS Status;
+ UINT32 SaveResponseType;
+ UINT32 SaveResponseLength;
+ UINT32 Index;
+ REMOTE_NDIS_INITIALIZE_CMPLT *RndisInitCmplt;
+
+ UsbIo = UsbRndisDevice->UsbIo;
+ SaveResponseType = 0;
+ SaveResponseLength = 0;
+ RndisInitCmplt = (REMOTE_NDIS_INITIALIZE_CMPLT *)RndisMsgResponse;
+
+ if (RndisMsgResponse != NULL) {
+ 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 != 0) || 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;
+ 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) != 0) {
+ 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 d65dae18aa..0ff058b0a9 100644
--- a/MdeModulePkg/MdeModulePkg.dec
+++ b/MdeModulePkg/MdeModulePkg.dec
@@ -692,6 +692,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>
@@ -2133,6 +2136,21 @@
# @Prompt The shared bit mask when Intel Tdx is enabled.
gEfiMdeModulePkgTokenSpaceGuid.PcdTdxSharedBitMask|0x0|UINT64|0x10000025
+ ## Indicates if the Usb Network rate limiting Supported.<BR><BR>
+ # TRUE - Usb Network rate limiting is supported.<BR>
+ # FALSE - Usb Network rate limiting is not supported.<BR>
+ # @Prompt Enable Usb Network rate limiting support.
+ gEfiMdeModulePkgTokenSpaceGuid.PcdEnableUsbNetworkRateLimiting|FALSE|BOOLEAN|0x10000026
+
+ ## The rate limiting Credit value is check in rate limiter event.
+ # It is to control the RateLimitingCreditCount max value.
+ # @Prompt The value is use for Usb Network rate limiting supported.
+ gEfiMdeModulePkgTokenSpaceGuid.PcdUsbNetworkRateLimitingCredit|10|UINT32|0x10000027
+
+ ## The value of rate limiter event for timeout check. Default value is 100(unit 1ms).
+ # @Prompt The value is use for Usb Network rate limiting supported.
+ gEfiMdeModulePkgTokenSpaceGuid.PcdUsbNetworkRateLimitingFactor|100|UINT32|0x10000028
+
[PcdsPatchableInModule]
## Specify memory size with page number for PEI code when
# Loading Module at Fixed Address feature is enabled.