/** @file Implementation of driver entry point and driver binding protocol. Copyright (c) 2005 - 2018, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "MnpDriver.h" #include "MnpImpl.h" #include "MnpVlan.h" EFI_DRIVER_BINDING_PROTOCOL gMnpDriverBinding = { MnpDriverBindingSupported, MnpDriverBindingStart, MnpDriverBindingStop, 0xa, NULL, NULL }; /** Callback function which provided by user to remove one node in NetDestroyLinkList process. @param[in] Entry The entry to be removed. @param[in] Context Pointer to the callback context corresponds to the Context in NetDestroyLinkList. @retval EFI_SUCCESS The entry has been removed successfully. @retval Others Fail to remove the entry. **/ EFI_STATUS EFIAPI MnpDestroyServiceDataEntry ( IN LIST_ENTRY *Entry, IN VOID *Context ) { MNP_SERVICE_DATA *MnpServiceData; MnpServiceData = MNP_SERVICE_DATA_FROM_LINK (Entry); return MnpDestroyServiceData (MnpServiceData); } /** Callback function which provided by user to remove one node in NetDestroyLinkList process. @param[in] Entry The entry to be removed. @param[in] Context Pointer to the callback context corresponds to the Context in NetDestroyLinkList. @retval EFI_SUCCESS The entry has been removed successfully. @retval Others Fail to remove the entry. **/ EFI_STATUS EFIAPI MnpDestroyServiceChildEntry ( IN LIST_ENTRY *Entry, IN VOID *Context ) { MNP_SERVICE_DATA *MnpServiceData; MnpServiceData = MNP_SERVICE_DATA_FROM_LINK (Entry); return MnpDestroyServiceChild (MnpServiceData); } /** Test to see if this driver supports ControllerHandle. This service is called by the EFI boot service ConnectController(). In order to make drivers as small as possible, there are a few calling restrictions for this service. ConnectController() must follow these calling restrictions. If any other agent wishes to call Supported() it must also follow these calling restrictions. @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 Others This driver does not support this device. **/ EFI_STATUS EFIAPI MnpDriverBindingSupported ( IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE ControllerHandle, IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL ) { EFI_STATUS Status; EFI_SIMPLE_NETWORK_PROTOCOL *Snp; // // Test to open the Simple Network protocol BY_DRIVER. // Status = gBS->OpenProtocol ( ControllerHandle, &gEfiSimpleNetworkProtocolGuid, (VOID **)&Snp, This->DriverBindingHandle, ControllerHandle, EFI_OPEN_PROTOCOL_BY_DRIVER ); if (EFI_ERROR (Status)) { return Status; } // // Close the opened SNP protocol. // gBS->CloseProtocol ( ControllerHandle, &gEfiSimpleNetworkProtocolGuid, This->DriverBindingHandle, ControllerHandle ); return EFI_SUCCESS; } /** Start this driver on ControllerHandle. This service is called by the EFI boot service ConnectController(). In order to make drivers as small as possible, there are a few calling restrictions for this service. ConnectController() must follow these calling restrictions. If any other agent wishes to call Start() it must also follow these calling restrictions. @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_ALREADY_STARTED This driver is already running on ControllerHandle. @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for Mnp Service Data. @retval Others This driver does not support this device. **/ EFI_STATUS EFIAPI MnpDriverBindingStart ( IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE ControllerHandle, IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL ) { EFI_STATUS Status; MNP_SERVICE_DATA *MnpServiceData; MNP_DEVICE_DATA *MnpDeviceData; LIST_ENTRY *Entry; VLAN_TCI *VlanVariable; UINTN NumberOfVlan; UINTN Index; VlanVariable = NULL; // // Initialize the Mnp Device Data // MnpDeviceData = AllocateZeroPool (sizeof (MNP_DEVICE_DATA)); if (MnpDeviceData == NULL) { DEBUG ((DEBUG_ERROR, "MnpDriverBindingStart(): Failed to allocate the Mnp Device Data.\n")); return EFI_OUT_OF_RESOURCES; } Status = MnpInitializeDeviceData (MnpDeviceData, This->DriverBindingHandle, ControllerHandle); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "MnpDriverBindingStart: MnpInitializeDeviceData failed, %r.\n", Status)); FreePool (MnpDeviceData); return Status; } // // Check whether NIC driver has already produced VlanConfig protocol // Status = gBS->OpenProtocol ( ControllerHandle, &gEfiVlanConfigProtocolGuid, NULL, This->DriverBindingHandle, ControllerHandle, EFI_OPEN_PROTOCOL_TEST_PROTOCOL ); if (!EFI_ERROR (Status)) { // // NIC hardware already implement VLAN, // no need to provide software VLAN implementation in MNP driver // MnpDeviceData->NumberOfVlan = 0; ZeroMem (&MnpDeviceData->VlanConfig, sizeof (EFI_VLAN_CONFIG_PROTOCOL)); MnpServiceData = MnpCreateServiceData (MnpDeviceData, 0, 0); Status = (MnpServiceData != NULL) ? EFI_SUCCESS : EFI_OUT_OF_RESOURCES; goto Exit; } // // Install VLAN Config Protocol // Status = gBS->InstallMultipleProtocolInterfaces ( &ControllerHandle, &gEfiVlanConfigProtocolGuid, &MnpDeviceData->VlanConfig, NULL ); if (EFI_ERROR (Status)) { goto Exit; } // // Get current VLAN configuration from EFI Variable // NumberOfVlan = 0; Status = MnpGetVlanVariable (MnpDeviceData, &NumberOfVlan, &VlanVariable); if (EFI_ERROR (Status)) { // // No VLAN is set, create a default MNP service data for untagged frame // MnpDeviceData->NumberOfVlan = 0; MnpServiceData = MnpCreateServiceData (MnpDeviceData, 0, 0); Status = (MnpServiceData != NULL) ? EFI_SUCCESS : EFI_OUT_OF_RESOURCES; goto Exit; } // // Create MNP service data for each VLAN // MnpDeviceData->NumberOfVlan = NumberOfVlan; for (Index = 0; Index < NumberOfVlan; Index++) { MnpServiceData = MnpCreateServiceData ( MnpDeviceData, VlanVariable[Index].Bits.Vid, (UINT8)VlanVariable[Index].Bits.Priority ); if (MnpServiceData == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Exit; } } Exit: if (VlanVariable != NULL) { FreePool (VlanVariable); } if (EFI_ERROR (Status)) { // // Destroy all MNP service data // while (!IsListEmpty (&MnpDeviceData->ServiceList)) { Entry = GetFirstNode (&MnpDeviceData->ServiceList); MnpServiceData = MNP_SERVICE_DATA_FROM_LINK (Entry); MnpDestroyServiceData (MnpServiceData); } // // Uninstall the VLAN Config Protocol if any // if (MnpDeviceData->VlanConfig.Set != NULL) { gBS->UninstallMultipleProtocolInterfaces ( MnpDeviceData->ControllerHandle, &gEfiVlanConfigProtocolGuid, &MnpDeviceData->VlanConfig, NULL ); } // // Destroy Mnp Device Data // MnpDestroyDeviceData (MnpDeviceData, This->DriverBindingHandle); FreePool (MnpDeviceData); } return Status; } /** Stop this driver on ControllerHandle. This service is called by the EFI boot service DisconnectController(). In order to make drivers as small as possible, there are a few calling restrictions for this service. DisconnectController() must follow these calling restrictions. If any other agent wishes to call Stop() it must also follow these calling restrictions. @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 EFI_DEVICE_ERROR The device could not be stopped due to a device error. **/ EFI_STATUS EFIAPI MnpDriverBindingStop ( IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE ControllerHandle, IN UINTN NumberOfChildren, IN EFI_HANDLE *ChildHandleBuffer OPTIONAL ) { EFI_STATUS Status; EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; EFI_VLAN_CONFIG_PROTOCOL *VlanConfig; MNP_DEVICE_DATA *MnpDeviceData; MNP_SERVICE_DATA *MnpServiceData; LIST_ENTRY *List; UINTN ListLength; // // Try to retrieve MNP service binding protocol from the ControllerHandle // Status = gBS->OpenProtocol ( ControllerHandle, &gEfiManagedNetworkServiceBindingProtocolGuid, (VOID **)&ServiceBinding, This->DriverBindingHandle, ControllerHandle, EFI_OPEN_PROTOCOL_GET_PROTOCOL ); if (EFI_ERROR (Status)) { // // Retrieve VLAN Config Protocol from the ControllerHandle // Status = gBS->OpenProtocol ( ControllerHandle, &gEfiVlanConfigProtocolGuid, (VOID **)&VlanConfig, This->DriverBindingHandle, ControllerHandle, EFI_OPEN_PROTOCOL_GET_PROTOCOL ); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "MnpDriverBindingStop: try to stop unknown Controller.\n")); return EFI_DEVICE_ERROR; } MnpDeviceData = MNP_DEVICE_DATA_FROM_THIS (VlanConfig); } else { MnpServiceData = MNP_SERVICE_DATA_FROM_THIS (ServiceBinding); MnpDeviceData = MnpServiceData->MnpDeviceData; } if (NumberOfChildren == 0) { // // Destroy all MNP service data // List = &MnpDeviceData->ServiceList; Status = NetDestroyLinkList ( List, MnpDestroyServiceDataEntry, NULL, &ListLength ); if (EFI_ERROR (Status) || (ListLength != 0)) { return EFI_DEVICE_ERROR; } // // Uninstall the VLAN Config Protocol if any // if (MnpDeviceData->VlanConfig.Set != NULL) { gBS->UninstallMultipleProtocolInterfaces ( MnpDeviceData->ControllerHandle, &gEfiVlanConfigProtocolGuid, &MnpDeviceData->VlanConfig, NULL ); } // // Destroy Mnp Device Data // MnpDestroyDeviceData (MnpDeviceData, This->DriverBindingHandle); FreePool (MnpDeviceData); if (gMnpControllerNameTable != NULL) { FreeUnicodeStringTable (gMnpControllerNameTable); gMnpControllerNameTable = NULL; } return EFI_SUCCESS; } // // Stop all MNP child // List = &MnpDeviceData->ServiceList; Status = NetDestroyLinkList ( List, MnpDestroyServiceChildEntry, NULL, &ListLength ); if (EFI_ERROR (Status)) { return EFI_DEVICE_ERROR; } return EFI_SUCCESS; } /** Creates a child handle with a set of I/O services. @param[in] This Protocol instance pointer. @param[in, out] ChildHandle Pointer to the handle of the child to create. If it is NULL, then a new handle is created. If it is not NULL, then the I/O services are added to the existing child handle. @retval EFI_SUCCESS The protocol was added to ChildHandle. @retval EFI_INVALID_PARAMETER ChildHandle is NULL. @retval EFI_OUT_OF_RESOURCES There are not enough resources available to create the child. @retval Others The child handle was not created. **/ EFI_STATUS EFIAPI MnpServiceBindingCreateChild ( IN EFI_SERVICE_BINDING_PROTOCOL *This, IN OUT EFI_HANDLE *ChildHandle ) { EFI_STATUS Status; MNP_SERVICE_DATA *MnpServiceData; MNP_INSTANCE_DATA *Instance; VOID *MnpSb; EFI_TPL OldTpl; if ((This == NULL) || (ChildHandle == NULL)) { return EFI_INVALID_PARAMETER; } MnpServiceData = MNP_SERVICE_DATA_FROM_THIS (This); // // Allocate buffer for the new instance. // Instance = AllocateZeroPool (sizeof (MNP_INSTANCE_DATA)); if (Instance == NULL) { DEBUG ((DEBUG_ERROR, "MnpServiceBindingCreateChild: Failed to allocate memory for the new instance.\n")); return EFI_OUT_OF_RESOURCES; } // // Init the instance data. // MnpInitializeInstanceData (MnpServiceData, Instance); Status = gBS->InstallMultipleProtocolInterfaces ( ChildHandle, &gEfiManagedNetworkProtocolGuid, &Instance->ManagedNetwork, NULL ); if (EFI_ERROR (Status)) { DEBUG ( (DEBUG_ERROR, "MnpServiceBindingCreateChild: Failed to install the MNP protocol, %r.\n", Status) ); goto ErrorExit; } // // Save the instance's childhandle. // Instance->Handle = *ChildHandle; Status = gBS->OpenProtocol ( MnpServiceData->ServiceHandle, &gEfiManagedNetworkServiceBindingProtocolGuid, (VOID **)&MnpSb, gMnpDriverBinding.DriverBindingHandle, Instance->Handle, EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER ); if (EFI_ERROR (Status)) { goto ErrorExit; } // // Add the child instance into ChildrenList. // OldTpl = gBS->RaiseTPL (TPL_CALLBACK); InsertTailList (&MnpServiceData->ChildrenList, &Instance->InstEntry); MnpServiceData->ChildrenNumber++; gBS->RestoreTPL (OldTpl); ErrorExit: if (EFI_ERROR (Status)) { if (Instance->Handle != NULL) { gBS->UninstallMultipleProtocolInterfaces ( Instance->Handle, &gEfiManagedNetworkProtocolGuid, &Instance->ManagedNetwork, NULL ); } FreePool (Instance); } return Status; } /** Destroys a child handle with a set of I/O services. The DestroyChild() function does the opposite of CreateChild(). It removes a protocol that was installed by CreateChild() from ChildHandle. If the removed protocol is the last protocol on ChildHandle, then ChildHandle is destroyed. @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. @param[in] ChildHandle Handle of the child to destroy. @retval EFI_SUCCESS The protocol was removed from ChildHandle. @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is being removed. @retval EFI_INVALID_PARAMETER ChildHandle is NULL. @retval EFI_ACCESS_DENIED The protocol could not be removed from the ChildHandle because its services are being used. @retval Others The child handle was not destroyed. **/ EFI_STATUS EFIAPI MnpServiceBindingDestroyChild ( IN EFI_SERVICE_BINDING_PROTOCOL *This, IN EFI_HANDLE ChildHandle ) { EFI_STATUS Status; MNP_SERVICE_DATA *MnpServiceData; EFI_MANAGED_NETWORK_PROTOCOL *ManagedNetwork; MNP_INSTANCE_DATA *Instance; EFI_TPL OldTpl; if ((This == NULL) || (ChildHandle == NULL)) { return EFI_INVALID_PARAMETER; } MnpServiceData = MNP_SERVICE_DATA_FROM_THIS (This); // // Try to retrieve ManagedNetwork Protocol from ChildHandle. // Status = gBS->OpenProtocol ( ChildHandle, &gEfiManagedNetworkProtocolGuid, (VOID **)&ManagedNetwork, gMnpDriverBinding.DriverBindingHandle, ChildHandle, EFI_OPEN_PROTOCOL_GET_PROTOCOL ); if (EFI_ERROR (Status)) { return EFI_UNSUPPORTED; } Instance = MNP_INSTANCE_DATA_FROM_THIS (ManagedNetwork); // // MnpServiceBindingDestroyChild may be called twice: first called by // MnpServiceBindingStop, second called by uninstalling the MNP protocol // in this ChildHandle. Use destroyed to make sure the resource clean code // will only excecute once. // if (Instance->Destroyed) { return EFI_SUCCESS; } Instance->Destroyed = TRUE; // // Close the Simple Network protocol. // gBS->CloseProtocol ( MnpServiceData->ServiceHandle, &gEfiManagedNetworkServiceBindingProtocolGuid, MnpServiceData->MnpDeviceData->ImageHandle, ChildHandle ); // // Uninstall the ManagedNetwork protocol. // Status = gBS->UninstallMultipleProtocolInterfaces ( ChildHandle, &gEfiManagedNetworkProtocolGuid, &Instance->ManagedNetwork, NULL ); if (EFI_ERROR (Status)) { DEBUG ( (DEBUG_ERROR, "MnpServiceBindingDestroyChild: Failed to uninstall the ManagedNetwork protocol, %r.\n", Status) ); Instance->Destroyed = FALSE; return Status; } OldTpl = gBS->RaiseTPL (TPL_CALLBACK); // // Reset the configuration. // ManagedNetwork->Configure (ManagedNetwork, NULL); // // Try to flush the RcvdPacketQueue. // MnpFlushRcvdDataQueue (Instance); // // Clean the RxTokenMap. // NetMapClean (&Instance->RxTokenMap); // // Remove this instance from the ChildrenList. // RemoveEntryList (&Instance->InstEntry); MnpServiceData->ChildrenNumber--; gBS->RestoreTPL (OldTpl); FreePool (Instance); return Status; } /** The entry point for Mnp driver which installs the driver binding and component name protocol on its ImageHandle. @param[in] ImageHandle The image handle of the driver. @param[in] SystemTable The system table. @retval EFI_SUCCESS The driver binding and component name protocols are successfully installed. @retval Others Other errors as indicated. **/ EFI_STATUS EFIAPI MnpDriverEntryPoint ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { return EfiLibInstallDriverBindingComponentName2 ( ImageHandle, SystemTable, &gMnpDriverBinding, ImageHandle, &gMnpComponentName, &gMnpComponentName2 ); }