/** @file This file contains code for USB network binding driver Copyright (c) 2023, American Megatrends International LLC. All rights reserved.
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; }