| /** @file | |
| This file contains code for USB Ethernet Control Model | |
| Driver | |
| Copyright (c) 2023, American Megatrends International LLC. All rights reserved.<BR> | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| **/ | |
| #include "UsbCdcEcm.h" | |
| EFI_DRIVER_BINDING_PROTOCOL gUsbEcmDriverBinding = { | |
| UsbEcmDriverSupported, | |
| UsbEcmDriverStart, | |
| UsbEcmDriverStop, | |
| USB_ECM_DRIVER_VERSION, | |
| NULL, | |
| NULL | |
| }; | |
| /** | |
| Check if this interface is USB ECM SubType | |
| @param[in] UsbIo A pointer to the EFI_USB_IO_PROTOCOL instance. | |
| @retval TRUE USB ECM SubType. | |
| @retval FALSE Not USB ECM 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; | |
| } | |
| if ((InterfaceDescriptor.InterfaceClass == USB_CDC_CLASS) && | |
| (InterfaceDescriptor.InterfaceSubClass == USB_CDC_ECM_SUBCLASS) && | |
| (InterfaceDescriptor.InterfaceProtocol == USB_NO_CLASS_PROTOCOL)) | |
| { | |
| return TRUE; | |
| } | |
| return FALSE; | |
| } | |
| /** | |
| USB ECM 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 | |
| UsbEcmDriverSupported ( | |
| 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; | |
| } | |
| /** | |
| Check if the USB ECM and USB CDC Data interfaces are from the same device. | |
| @param[in] UsbEthPath 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_NOT_FOUND Is not the same device. | |
| **/ | |
| EFI_STATUS | |
| IsSameDevice ( | |
| IN EFI_DEVICE_PATH_PROTOCOL *UsbEthPath, | |
| IN EFI_DEVICE_PATH_PROTOCOL *UsbCdcDataPath | |
| ) | |
| { | |
| while (1) { | |
| if ((UsbEthPath->Type == ACPI_DEVICE_PATH) && (UsbEthPath->SubType == ACPI_DP)) { | |
| if (CompareMem ((ACPI_HID_DEVICE_PATH *)UsbCdcDataPath, (ACPI_HID_DEVICE_PATH *)UsbEthPath, sizeof (ACPI_HID_DEVICE_PATH))) { | |
| return EFI_NOT_FOUND; | |
| } | |
| } | |
| if ((UsbEthPath->Type == HARDWARE_DEVICE_PATH) && (UsbEthPath->SubType == HW_PCI_DP)) { | |
| if (CompareMem ((PCI_DEVICE_PATH *)UsbCdcDataPath, (PCI_DEVICE_PATH *)UsbEthPath, sizeof (PCI_DEVICE_PATH))) { | |
| return EFI_NOT_FOUND; | |
| } | |
| } | |
| if ((UsbEthPath->Type == MESSAGING_DEVICE_PATH) && (UsbEthPath->SubType == MSG_USB_DP)) { | |
| if (IsDevicePathEnd (NextDevicePathNode (UsbEthPath))) { | |
| if (((USB_DEVICE_PATH *)UsbEthPath)->ParentPortNumber == | |
| ((USB_DEVICE_PATH *)UsbCdcDataPath)->ParentPortNumber) | |
| { | |
| return EFI_SUCCESS; | |
| } else { | |
| return EFI_NOT_FOUND; | |
| } | |
| } else { | |
| if (CompareMem ((USB_DEVICE_PATH *)UsbCdcDataPath, (USB_DEVICE_PATH *)UsbEthPath, sizeof (USB_DEVICE_PATH))) { | |
| return EFI_NOT_FOUND; | |
| } | |
| } | |
| } | |
| UsbEthPath = NextDevicePathNode (UsbEthPath); | |
| UsbCdcDataPath = NextDevicePathNode (UsbCdcDataPath); | |
| } | |
| } | |
| /** | |
| Check if the USB CDC Data(UsbIo) installed and return USB CDC Data Handle. | |
| @param[in] UsbEthPath A pointer to the EFI_DEVICE_PATH_PROTOCOL instance. | |
| @param[in, out] UsbCdcDataHandle A pointer to the EFI_HANDLE for USB CDC Data. | |
| @retval TRUE USB CDC Data(UsbIo) installed. | |
| @retval FALSE USB CDC Data(UsbIo) did not installed. | |
| **/ | |
| BOOLEAN | |
| IsUsbCdcData ( | |
| IN EFI_DEVICE_PATH_PROTOCOL *UsbEthPath, | |
| IN OUT EFI_HANDLE *UsbCdcDataHandle | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINTN Index; | |
| UINTN HandleCount; | |
| EFI_HANDLE *HandleBuffer; | |
| EFI_USB_IO_PROTOCOL *UsbIo; | |
| EFI_USB_INTERFACE_DESCRIPTOR Interface; | |
| EFI_DEVICE_PATH_PROTOCOL *UsbCdcDataPath; | |
| Status = gBS->LocateHandleBuffer ( | |
| ByProtocol, | |
| &gEfiUsbIoProtocolGuid, | |
| NULL, | |
| &HandleCount, | |
| &HandleBuffer | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return FALSE; | |
| } | |
| for (Index = 0; Index < HandleCount; Index++) { | |
| Status = gBS->HandleProtocol ( | |
| HandleBuffer[Index], | |
| &gEfiUsbIoProtocolGuid, | |
| (VOID **)&UsbIo | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &Interface); | |
| ASSERT_EFI_ERROR (Status); | |
| if ((Interface.InterfaceClass == USB_CDC_DATA_CLASS) && | |
| (Interface.InterfaceSubClass == USB_CDC_DATA_SUBCLASS) && | |
| (Interface.InterfaceProtocol == USB_NO_CLASS_PROTOCOL)) | |
| { | |
| Status = gBS->HandleProtocol ( | |
| HandleBuffer[Index], | |
| &gEfiDevicePathProtocolGuid, | |
| (VOID **)&UsbCdcDataPath | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| continue; | |
| } | |
| Status = IsSameDevice (UsbEthPath, UsbCdcDataPath); | |
| if (!EFI_ERROR (Status)) { | |
| CopyMem (UsbCdcDataHandle, &HandleBuffer[Index], sizeof (EFI_HANDLE)); | |
| FreePool (HandleBuffer); | |
| return TRUE; | |
| } | |
| } | |
| } | |
| FreePool (HandleBuffer); | |
| return FALSE; | |
| } | |
| /** | |
| Call Back Function. | |
| @param[in] Event Event whose notification function is being invoked. | |
| @param[in] Context The pointer to the notification function's context, | |
| which is implementation-dependent. | |
| **/ | |
| VOID | |
| EFIAPI | |
| CallbackFunction ( | |
| IN EFI_EVENT Event, | |
| IN VOID *Context | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINTN Index; | |
| UINTN HandleCount; | |
| EFI_HANDLE *HandleBuffer; | |
| EFI_USB_IO_PROTOCOL *UsbIo; | |
| EFI_USB_INTERFACE_DESCRIPTOR Interface; | |
| 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); | |
| Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &Interface); | |
| ASSERT_EFI_ERROR (Status); | |
| if ((Interface.InterfaceClass == USB_CDC_CLASS) && | |
| (Interface.InterfaceSubClass == USB_CDC_ECM_SUBCLASS) && | |
| (Interface.InterfaceProtocol == USB_NO_CLASS_PROTOCOL)) | |
| { | |
| gBS->ConnectController (HandleBuffer[Index], NULL, NULL, TRUE); | |
| } | |
| } | |
| FreePool (HandleBuffer); | |
| gBS->CloseEvent (Event); | |
| } | |
| /** | |
| USB ECM 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 | |
| UsbEcmDriverStart ( | |
| IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
| IN EFI_HANDLE ControllerHandle, | |
| IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| VOID *Reg; | |
| EFI_EVENT Event; | |
| USB_ETHERNET_DRIVER *UsbEthDriver; | |
| EFI_DEVICE_PATH_PROTOCOL *UsbEthPath; | |
| EFI_HANDLE UsbCdcDataHandle; | |
| EFI_USB_IO_PROTOCOL *UsbIo; | |
| EFI_USB_INTERFACE_DESCRIPTOR Interface; | |
| 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_GET_PROTOCOL | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| gBS->CloseProtocol ( | |
| ControllerHandle, | |
| &gEfiUsbIoProtocolGuid, | |
| This->DriverBindingHandle, | |
| ControllerHandle | |
| ); | |
| return Status; | |
| } | |
| Status = IsUsbCdcData (UsbEthPath, &UsbCdcDataHandle) ? EFI_SUCCESS : EFI_UNSUPPORTED; | |
| if (EFI_ERROR (Status)) { | |
| gBS->CloseProtocol ( | |
| ControllerHandle, | |
| &gEfiUsbIoProtocolGuid, | |
| This->DriverBindingHandle, | |
| ControllerHandle | |
| ); | |
| Status = gBS->CreateEvent (EVT_NOTIFY_SIGNAL, TPL_CALLBACK, CallbackFunction, NULL, &Event); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| Status = gBS->RegisterProtocolNotify (&gEfiUsbIoProtocolGuid, Event, &Reg); | |
| return Status; | |
| } | |
| UsbEthDriver = AllocateZeroPool (sizeof (USB_ETHERNET_DRIVER)); | |
| if (!UsbEthDriver) { | |
| gBS->CloseProtocol ( | |
| ControllerHandle, | |
| &gEfiUsbIoProtocolGuid, | |
| This->DriverBindingHandle, | |
| ControllerHandle | |
| ); | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| Status = LoadAllDescriptor (UsbIo, &UsbEthDriver->Config); | |
| ASSERT_EFI_ERROR (Status); | |
| GetEndpoint (UsbIo, UsbEthDriver); | |
| Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &Interface); | |
| ASSERT_EFI_ERROR (Status); | |
| UsbEthDriver->Signature = USB_ETHERNET_SIGNATURE; | |
| UsbEthDriver->NumOfInterface = Interface.InterfaceNumber; | |
| UsbEthDriver->UsbCdcDataHandle = UsbCdcDataHandle; | |
| UsbEthDriver->UsbIo = UsbIo; | |
| UsbEthDriver->UsbEth.UsbEthReceive = UsbEthEcmReceive; | |
| UsbEthDriver->UsbEth.UsbEthTransmit = UsbEthEcmTransmit; | |
| UsbEthDriver->UsbEth.UsbEthInterrupt = UsbEthEcmInterrupt; | |
| UsbEthDriver->UsbEth.UsbEthMacAddress = GetUsbEthMacAddress; | |
| UsbEthDriver->UsbEth.UsbEthMaxBulkSize = UsbEthEcmBulkSize; | |
| UsbEthDriver->UsbEth.UsbHeaderFunDescriptor = GetUsbHeaderFunDescriptor; | |
| UsbEthDriver->UsbEth.UsbUnionFunDescriptor = GetUsbUnionFunDescriptor; | |
| UsbEthDriver->UsbEth.UsbEthFunDescriptor = GetUsbEthFunDescriptor; | |
| UsbEthDriver->UsbEth.SetUsbEthMcastFilter = SetUsbEthMcastFilter; | |
| UsbEthDriver->UsbEth.SetUsbEthPowerPatternFilter = SetUsbEthPowerFilter; | |
| UsbEthDriver->UsbEth.GetUsbEthPowerPatternFilter = GetUsbEthPowerFilter; | |
| UsbEthDriver->UsbEth.SetUsbEthPacketFilter = SetUsbEthPacketFilter; | |
| UsbEthDriver->UsbEth.GetUsbEthStatistic = GetUsbEthStatistic; | |
| Status = gBS->InstallProtocolInterface ( | |
| &ControllerHandle, | |
| &gEdkIIUsbEthProtocolGuid, | |
| EFI_NATIVE_INTERFACE, | |
| &(UsbEthDriver->UsbEth) | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| gBS->CloseProtocol ( | |
| ControllerHandle, | |
| &gEfiUsbIoProtocolGuid, | |
| This->DriverBindingHandle, | |
| ControllerHandle | |
| ); | |
| FreePool (UsbEthDriver); | |
| return Status; | |
| } | |
| return Status; | |
| } | |
| /** | |
| USB ECM 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 | |
| UsbEcmDriverStop ( | |
| 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_ETHERNET_DRIVER *UsbEthDriver; | |
| Status = gBS->OpenProtocol ( | |
| ControllerHandle, | |
| &gEdkIIUsbEthProtocolGuid, | |
| (VOID **)&UsbEthProtocol, | |
| This->DriverBindingHandle, | |
| ControllerHandle, | |
| EFI_OPEN_PROTOCOL_GET_PROTOCOL | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| UsbEthDriver = USB_ETHERNET_DEV_FROM_THIS (UsbEthProtocol); | |
| Status = gBS->UninstallProtocolInterface ( | |
| ControllerHandle, | |
| &gEdkIIUsbEthProtocolGuid, | |
| UsbEthProtocol | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| Status = gBS->CloseProtocol ( | |
| ControllerHandle, | |
| &gEfiUsbIoProtocolGuid, | |
| This->DriverBindingHandle, | |
| ControllerHandle | |
| ); | |
| FreePool (UsbEthDriver->Config); | |
| FreePool (UsbEthDriver); | |
| return Status; | |
| } | |
| /** | |
| Entrypoint of ECM Driver. | |
| This function is the entrypoint of ECM 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 | |
| UsbEcmEntry ( | |
| IN EFI_HANDLE ImageHandle, | |
| IN EFI_SYSTEM_TABLE *SystemTable | |
| ) | |
| { | |
| gUsbEcmDriverBinding.DriverBindingHandle = ImageHandle; | |
| gUsbEcmDriverBinding.ImageHandle = ImageHandle; | |
| return gBS->InstallMultipleProtocolInterfaces ( | |
| &gUsbEcmDriverBinding.DriverBindingHandle, | |
| &gEfiDriverBindingProtocolGuid, | |
| &gUsbEcmDriverBinding, | |
| &gEfiComponentName2ProtocolGuid, | |
| &gUsbEcmComponentName2, | |
| NULL | |
| ); | |
| } |