mirror of https://github.com/acidanthera/audk.git
503 lines
16 KiB
C
503 lines
16 KiB
C
/** @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
|
|
);
|
|
}
|