Newer
Older
AMI-Aptio-BIOS-Reversed / PcAtChipsetPkg / PcatRealTimeClockSmm / PcatRealTimeClockSmm / MnpDxe / MnpDriver.c
@Ajax Dong Ajax Dong 2 days ago 26 KB Restructure the repo
/**
 * @file MnpDriver.c
 *
 * @brief MNP DXE Driver - Driver Binding and Service Binding Implementation
 *
 * Implements the EFI driver binding protocol (Supported, Start, Stop) and
 * the EFI service binding protocol (CreateChild, DestroyChild) for the
 * Managed Network Protocol.
 *
 * Architecture:
 *   MNP_DEVICE_DATA (per-NIC)
 *     -> MNP_SERVICE_DATA (per-VLAN, linked via MnpDeviceData->ServiceList)
 *          -> MNP_INSTANCE_DATA (per-opened-protocol, linked via Service->InstanceList)
 *
 * Source: AmiNetworkPkg/UefiNetworkStack/Common/MnpDxe/MnpDriver.c
 */

#include "MnpDxe.h"

//
// GUID definitions (extern references to globals in the data section)
//
EFI_GUID  gMnpDriverBindingGuid      = EFI_DRIVER_BINDING_PROTOCOL_GUID;
EFI_GUID  gMnpServiceBindingGuid     = EFI_MANAGED_NETWORK_SERVICE_BINDING_PROTOCOL_GUID;
EFI_GUID  gMnpProtocolGuid           = EFI_MANAGED_NETWORK_PROTOCOL_GUID;
EFI_GUID  gMnpSnpProtocolGuid        = EFI_SIMPLE_NETWORK_PROTOCOL_GUID;
EFI_GUID  gMnpDevicePathGuid         = EFI_DEVICE_PATH_PROTOCOL_GUID;
EFI_GUID  gMnpVlanConfigGuid         = EFI_VLAN_CONFIG_PROTOCOL_GUID;
EFI_GUID  gMnpComponentNameGuid      = EFI_COMPONENT_NAME2_PROTOCOL_GUID;

//
// External globals
//
extern EFI_BOOT_SERVICES  *gBS;
extern EFI_HANDLE          gImageHandle;

//
// Forward declarations
//
EFI_STATUS
EFIAPI
MnpDriverBindingSupported (
  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE                   ControllerHandle,
  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath  OPTIONAL
  );

EFI_STATUS
EFIAPI
MnpDriverBindingStart (
  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE                   ControllerHandle,
  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath  OPTIONAL
  );

EFI_STATUS
EFIAPI
MnpDriverBindingStop (
  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE                   ControllerHandle,
  IN UINTN                        NumberOfChildren,
  IN EFI_HANDLE                   *ChildHandleBuffer    OPTIONAL
  );

EFI_STATUS
EFIAPI
MnpServiceBindingCreateChild (
  IN EFI_SERVICE_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE                     *ChildHandle
  );

EFI_STATUS
EFIAPI
MnpServiceBindingDestroyChild (
  IN EFI_SERVICE_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE                     ChildHandle
  );

//
// Driver Binding Protocol instance
//
EFI_DRIVER_BINDING_PROTOCOL  gMnpDriverBinding = {
  MnpDriverBindingSupported,
  MnpDriverBindingStart,
  MnpDriverBindingStop,
  0x0A,                           /* Version = 10 */
  NULL,                           /* ImageHandle (filled at entry) */
  NULL                            /* DriverBindingHandle (filled later) */
};

/* ====================================================================== */
/*  MnpDriverBindingSupported                                            */
/* ====================================================================== */

/**
 * Test whether the driver supports the controller.
 *
 * Opens the SNP protocol on ControllerHandle; if it succeeds and the
 * controller does NOT already have a DevicePath child (which would indicate
 * a child handle rather than a NIC handle), returns EFI_SUCCESS.
 *
 * @param[in]  This                  Driver binding protocol.
 * @param[in]  ControllerHandle      Handle to test.
 * @param[in]  RemainingDevicePath   Optional device path.
 *
 * @retval EFI_SUCCESS               Controller is supported.
 * @retval EFI_UNSUPPORTED           Controller is not supported.
 */
EFI_STATUS
EFIAPI
MnpDriverBindingSupported (
  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE                   ControllerHandle,
  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath  OPTIONAL
  )
{
  EFI_STATUS  Status;

  Status = gBS->OpenProtocol (
                  ControllerHandle,
                  &gMnpSnpProtocolGuid,
                  NULL,
                  This->DriverBindingHandle,
                  ControllerHandle,
                  EFI_OPEN_PROTOCOL_TEST_PROTOCOL
                  );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  gBS->CloseProtocol (
         ControllerHandle,
         &gMnpSnpProtocolGuid,
         This->DriverBindingHandle,
         ControllerHandle
         );

  //
  // If the handle has a DevicePath protocol that is a child handle
  // (i.e. not the root), return UNSUPPORTED.
  //
  {
    EFI_DEVICE_PATH_PROTOCOL  *DevicePath;
    UINTN                     DevPathSize;

    DevicePath = DevicePathFromHandle (ControllerHandle);
    if (DevicePath != NULL && !IsDevicePathEnd (DevicePath)) {
      //
      // This is a child handle; do not bind directly.
      //
      DEBUG ((DEBUG_ERROR, "Returning EFI_UNSUPPORTED\n"));
      return EFI_UNSUPPORTED;
    }
  }

  return EFI_SUCCESS;
}

/* ====================================================================== */
/*  MnpDriverBindingStart                                                */
/* ====================================================================== */

/**
 * Start the MNP driver on a controller handle.
 *
 * Allocates and initializes a MNP_DEVICE_DATA structure, opens the SNP
 * protocol, creates service data instances (VLAN and non-VLAN), and
 * installs the driver binding protocol.
 *
 * @param[in]  This                  Driver binding protocol.
 * @param[in]  ControllerHandle      Handle to start.
 * @param[in]  RemainingDevicePath   Optional device path.
 *
 * @retval EFI_SUCCESS               Driver started.
 * @retval EFI_OUT_OF_RESOURCES      Memory allocation failed.
 * @retval Others                    Error from sub-operations.
 */
EFI_STATUS
EFIAPI
MnpDriverBindingStart (
  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE                   ControllerHandle,
  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath  OPTIONAL
  )
{
  MNP_DEVICE_DATA  *MnpDeviceData;
  EFI_STATUS        Status;
  UINTN             NumberOfVlan;
  UINT16            *VlanVariable;
  UINTN             Index;

  //
  // Allocate MNP_DEVICE_DATA (size includes free NET_BUF queue).
  //
  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));
    goto ErrorFreeDeviceData;
  }

  //
  // Open the driver binding protocol on the controller handle.
  //
  Status = gBS->OpenProtocol (
                  &ControllerHandle,
                  &gMnpDriverBindingGuid,
                  &MnpDeviceData->DriverBindingHandle,
                  This->DriverBindingHandle,
                  ControllerHandle,
                  EFI_OPEN_PROTOCOL_BY_DRIVER
                  );
  if (Status == EFI_ALREADY_STARTED) {
    //
    // Protocol already opened; check VLAN config.
    //
    gBS->CloseProtocol (
           &ControllerHandle,
           &gMnpDriverBindingGuid,
           This->DriverBindingHandle,
           ControllerHandle
           );
    ZeroMem (&MnpDeviceData->DriverBindingHandle,
             sizeof (MnpDeviceData->DriverBindingHandle));

    //
    // Open the device path protocol for VLAN.
    //
    Status = gBS->OpenProtocol (
                    &ControllerHandle,
                    &gMnpDevicePathGuid,
                    &DevicePathInterface,
                    This->DriverBindingHandle,
                    ControllerHandle,
                    EFI_OPEN_PROTOCOL_BY_DRIVER
                    );
    if (EFI_ERROR (Status)) {
      goto DestroyServices;
    }

    //
    // Read the VLAN configuration variable.
    //
    NumberOfVlan = 0;
    VlanVariable = NULL;
    Status = MnpReadVlanConfiguration (
               MnpDeviceData,
               &NumberOfVlan,
               &VlanVariable
               );

    if (!EFI_ERROR (Status)) {
      MnpDeviceData->NumberOfVlan = NumberOfVlan;

      if (NumberOfVlan > 0) {
        //
        // Create one service data per VLAN.
        //
        for (Index = 0; Index < NumberOfVlan; Index++) {
          if (!MnpCreateServiceData (
                MnpDeviceData,
                VlanVariable[Index] & 0xFFF,
                (UINT8)((VlanVariable[Index] >> 13) & 7)
                )) {
            Status = EFI_OUT_OF_RESOURCES;
            break;
          }
        }
      } else {
        MnpDeviceData->NumberOfVlan = 0;
        Status = MnpCreateServiceData (MnpDeviceData, 0, 0)
                   ? EFI_SUCCESS
                   : EFI_OUT_OF_RESOURCES;
      }
    } else {
      //
      // No VLAN variable; create a default service data.
      //
      MnpDeviceData->NumberOfVlan = 0;
      Status = MnpCreateServiceData (MnpDeviceData, 0, 0)
                 ? EFI_SUCCESS
                 : EFI_OUT_OF_RESOURCES;
    }

    if (VlanVariable != NULL) {
      FreePool (VlanVariable);
    }
  } else if (Status == EFI_SUCCESS) {
    //
    // First open; create a default service.
    //
    MnpDeviceData->NumberOfVlan = 0;
    Status = MnpCreateServiceData (MnpDeviceData, 0, 0)
               ? EFI_SUCCESS
               : EFI_OUT_OF_RESOURCES;
  }

  if (!EFI_ERROR (Status)) {
    if (MnpDeviceData->NumberOfVlan > 0) {
      Status = EFI_SUCCESS;
    }
    return Status;
  }

DestroyServices:
  //
  // Clean up all service data entries.
  //
  while (!IsListEmpty (&MnpDeviceData->ServiceList)) {
    LIST_ENTRY      *Entry;
    MNP_SERVICE_DATA *Service;

    Entry = GetFirstNode (&MnpDeviceData->ServiceList);
    if (((MNP_SERVICE_DATA *)Entry)->Signature == MNP_SERVICE_DATA_SIGNATURE) {
      Service = (MNP_SERVICE_DATA *)Entry;
    } else {
      CR_CHECK_FAIL (Entry);
      Service = (MNP_SERVICE_DATA *)Entry;
    }
    MnpDestroyServiceData (Service);
  }

  if (MnpDeviceData->DriverBindingHandle != 0) {
    gBS->CloseProtocol (
           ControllerHandle,
           &gMnpDriverBindingGuid,
           MnpDeviceData->DriverBindingHandle,
           0
           );
  }

  MnpReleaseDeviceData (MnpDeviceData, This->DriverBindingHandle);

ErrorFreeDeviceData:
  FreePool (MnpDeviceData);
  return Status;
}

/* ====================================================================== */
/*  MnpDriverBindingStop                                                 */
/* ====================================================================== */

/**
 * Stop the MNP driver on a controller handle.
 *
 * @param[in]  This                  Driver binding protocol.
 * @param[in]  ControllerHandle      Handle to stop.
 * @param[in]  NumberOfChildren      Number of child handles.
 * @param[in]  ChildHandleBuffer     Array of child handles.
 *
 * @retval EFI_SUCCESS               Driver stopped.
 * @retval EFI_DEVICE_ERROR          Failed to stop.
 */
EFI_STATUS
EFIAPI
MnpDriverBindingStop (
  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE                   ControllerHandle,
  IN UINTN                        NumberOfChildren,
  IN EFI_HANDLE                   *ChildHandleBuffer  OPTIONAL
  )
{
  MNP_DEVICE_DATA  *MnpDeviceData;
  MNP_SERVICE_DATA *Service;
  EFI_STATUS        Status;
  LIST_ENTRY       *Entry;
  EFI_HANDLE        SnpChildHandle;
  UINTN             DevicePathSize;
  BOOLEAN           ByDevicePath;

  //
  // Locate the MNP device data.
  //
  Status = gBS->OpenProtocol (
                  ControllerHandle,
                  &gMnpDevicePathGuid,
                  (VOID **)&DevicePathInterface,
                  This->DriverBindingHandle,
                  ControllerHandle,
                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
                  );
  if (!EFI_ERROR (Status)) {
    //
    // Found via device path -> get MNP_SERVICE_DATA from controller
    // handle (child handle). Back-calculate to MNP_DEVICE_DATA.
    //
    Service    = MNP_SERVICE_DATA_FROM_DEVICE_PATH (DevicePathInterface);
    MnpDeviceData = Service->MnpDeviceData;
  } else {
    Status = gBS->OpenProtocol (
                    ControllerHandle,
                    &gMnpDriverBindingGuid,
                    (VOID **)&MnpDeviceData,
                    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;
    }
    //
    // Verify signature.
    //
    if (MnpDeviceData->Signature != MNP_DEVICE_DATA_SIGNATURE) {
      MnpDeviceData = (MNP_DEVICE_DATA *)((UINTN)MnpDeviceData -
                                          OFFSET_OF(MNP_DEVICE_DATA,
                                                    DriverBindingHandle));
    }
  }

  if (NumberOfChildren > 0) {
    //
    // Destroy specific children.
    //
    Status = NetMapIterate (
               &MnpDeviceData->ServiceList,
               (NET_MAP_ITEM_CALLBACK)MnpDestroyChildByHandle,
               ChildHandleBuffer
               );
    if (EFI_ERROR (Status)) {
      return EFI_DEVICE_ERROR;
    }
    return EFI_SUCCESS;
  }

  //
  // Destroy all services and release the device data.
  //
  while (!IsListEmpty (&MnpDeviceData->ServiceList)) {
    Entry   = GetFirstNode (&MnpDeviceData->ServiceList);
    Service = MNP_SERVICE_DATA_FROM_LINK (Entry);
    MnpDestroyServiceData (Service);
  }

  if (MnpDeviceData->DriverBindingHandle != 0) {
    gBS->CloseProtocol (
           ControllerHandle,
           &gMnpDriverBindingGuid,
           MnpDeviceData->DriverBindingHandle,
           ControllerHandle
           );
  }

  MnpReleaseDeviceData (MnpDeviceData, This->DriverBindingHandle);
  FreePool (MnpDeviceData);

  if (gComponentNameTable != NULL) {
    FreeUnicodeStringTable (gComponentNameTable);
    gComponentNameTable = NULL;
  }

  return EFI_SUCCESS;
}

/* ====================================================================== */
/*  MnpServiceBindingCreateChild                                         */
/* ====================================================================== */

/**
 * Create a child MNP instance on the service.
 *
 * Allocates and initializes an MNP_INSTANCE_DATA, installs the MNP protocol
 * on a new child handle, and adds it to the service's instance list.
 *
 * @param[in]     This         Service binding protocol.
 * @param[in,out] ChildHandle  On input, optional handle; on output, new child.
 *
 * @retval EFI_SUCCESS             Child created.
 * @retval EFI_INVALID_PARAMETER   Invalid parameter.
 * @retval EFI_OUT_OF_RESOURCES    Memory allocation failed.
 */
EFI_STATUS
EFIAPI
MnpServiceBindingCreateChild (
  IN     EFI_SERVICE_BINDING_PROTOCOL  *This,
  IN OUT EFI_HANDLE                    *ChildHandle
  )
{
  MNP_SERVICE_DATA    *Service;
  MNP_INSTANCE_DATA   *Instance;
  EFI_STATUS           Status;
  EFI_TPL              OldTpl;

  if (This == NULL || ChildHandle == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Service = MNP_SERVICE_DATA_FROM_THIS (This);

  Instance = AllocateZeroPool (sizeof (MNP_INSTANCE_DATA));
  if (Instance == NULL) {
    DEBUG ((DEBUG_ERROR,
            "MnpServiceBindingCreateChild: Faild to allocate memory for "
            "the new instance.\n"));
    return EFI_OUT_OF_RESOURCES;
  }

  MnpInitializeInstanceData (Service, Instance);

  //
  // Install the MNP protocol on a new child handle.
  //
  Status = gBS->InstallMultipleProtocolInterfaces (
                  ChildHandle,
                  &gMnpProtocolGuid,
                  &Instance->Protocol,
                  NULL
                  );
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR,
            "MnpServiceBindingCreateChild: Failed to install the "
            "MNP protocol, %r.\n", Status));
    goto ErrorFreeInstance;
  }

  Instance->Handle = *ChildHandle;

  //
  // Open the device path protocol from the service handle to the new child.
  //
  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
  Status = gBS->OpenProtocol (
                  Service->ControllerHandle,
                  &gMnpDevicePathGuid,
                  &DevicePathInterface,
                  gImageHandle,
                  *ChildHandle,
                  EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
                  );
  if (EFI_ERROR (Status)) {
    gBS->RestoreTPL (OldTpl);
    goto ErrorUninstallProtocol;
  }

  InsertTailList (&Service->InstanceList, &Instance->Link);
  Service->InstanceCount++;

  gBS->RestoreTPL (OldTpl);

  return EFI_SUCCESS;

ErrorUninstallProtocol:
  gBS->UninstallMultipleProtocolInterfaces (
         *ChildHandle,
         &gMnpProtocolGuid,
         &Instance->Protocol,
         NULL
         );

ErrorFreeInstance:
  FreePool (Instance);
  return Status;
}

/* ====================================================================== */
/*  MnpServiceBindingDestroyChild                                        */
/* ====================================================================== */

/**
 * Destroy a child MNP instance.
 *
 * @param[in] This         Service binding protocol.
 * @param[in] ChildHandle  Handle of the child to destroy.
 *
 * @retval EFI_SUCCESS           Child destroyed.
 * @retval EFI_INVALID_PARAMETER Invalid parameter.
 * @retval EFI_UNSUPPORTED       Protocol not found on child.
 */
EFI_STATUS
EFIAPI
MnpServiceBindingDestroyChild (
  IN EFI_SERVICE_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE                     ChildHandle
  )
{
  MNP_SERVICE_DATA    *Service;
  MNP_INSTANCE_DATA   *Instance;
  EFI_STATUS           Status;
  EFI_TPL              OldTpl;
  VOID                *ProtocolInterface;

  if (This == NULL || ChildHandle == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Service = MNP_SERVICE_DATA_FROM_THIS (This);

  //
  // Locate the MNP protocol on the child handle.
  //
  Status = gBS->OpenProtocol (
                  ChildHandle,
                  &gMnpProtocolGuid,
                  &ProtocolInterface,
                  gImageHandle,
                  ChildHandle,
                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
                  );
  if (EFI_ERROR (Status)) {
    return EFI_UNSUPPORTED;
  }

  Instance = MNP_INSTANCE_DATA_FROM_THIS ((EFI_MANAGED_NETWORK_PROTOCOL *)ProtocolInterface);

  if (Instance->IsDestroyed) {
    return EFI_SUCCESS;
  }

  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);

  Instance->IsDestroyed = TRUE;

  //
  // Close the device path protocol opened for this child.
  //
  gBS->CloseProtocol (
         Service->ControllerHandle,
         &gMnpDevicePathGuid,
         gImageHandle,
         ChildHandle
         );

  //
  // Uninstall the MNP protocol.
  //
  Status = gBS->UninstallMultipleProtocolInterfaces (
                  ChildHandle,
                  &gMnpProtocolGuid,
                  &Instance->Protocol,
                  NULL
                  );
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR,
            "MnpServiceBindingDestroyChild: Failed to uninstall the "
            "ManagedNetwork protocol, %r.\n", Status));
    Instance->IsDestroyed = FALSE;
    gBS->RestoreTPL (OldTpl);
    return Status;
  }

  //
  // Clean up instance state.
  //
  MnpCleanInstance (Instance);
  NetMapClean (&Instance->RcvMap);

  //
  // Remove from the service's instance list.
  //
  RemoveEntryList (&Instance->Link);
  Service->InstanceCount--;

  gBS->RestoreTPL (OldTpl);

  FreePool (Instance);
  return EFI_SUCCESS;
}

/* ====================================================================== */
/*  MnpComponentNameGetDriverName                                        */
/* ====================================================================== */

/**
 * Retrieves the driver name.
 *
 * @param[in]  This       Component name protocol.
 * @param[in]  Language   Language code.
 * @param[out] DriverName Driver name string.
 *
 * @retval EFI_SUCCESS           Name returned.
 * @retval EFI_UNSUPPORTED       Language not supported.
 */
EFI_STATUS
EFIAPI
MnpComponentNameGetDriverName (
  IN   EFI_COMPONENT_NAME2_PROTOCOL  *This,
  IN   CHAR8                         *Language,
  OUT  CHAR16                        **DriverName
  )
{
  return LookupUnicodeString2 (
           Language,
           This->SupportedLanguages,
           &gMnpDriverNameTable,
           DriverName,
           (This == &gMnpComponentName2)
           );
}

/* ====================================================================== */
/*  MnpComponentNameGetControllerName                                    */
/* ====================================================================== */

/**
 * Retrieves the controller name.
 *
 * Queries the SNP and MNP protocols on the controller and formats a
 * descriptive name string.
 *
 * @param[in]  This           Component name protocol.
 * @param[in]  Controller     Controller handle.
 * @param[in]  ChildHandle    Child handle (MNP instance).
 * @param[in]  Language       Language code.
 * @param[out] ControllerName Controller name string.
 *
 * @retval EFI_SUCCESS           Name returned.
 * @retval EFI_UNSUPPORTED       Controller not supported.
 */
EFI_STATUS
EFIAPI
MnpComponentNameGetControllerName (
  IN   EFI_COMPONENT_NAME2_PROTOCOL  *This,
  IN   EFI_HANDLE                     Controller,
  IN   EFI_HANDLE                     ChildHandle     OPTIONAL,
  IN   CHAR8                          *Language,
  OUT  CHAR16                         **ControllerName
  )
{
  EFI_STATUS           Status;
  EFI_SIMPLE_NETWORK   *Snp;
  MNP_INSTANCE_DATA    *Instance;
  MNP_SERVICE_DATA     *Service;
  EFI_HANDLE           SnpHandle;
  CHAR16               NameString[80];
  UINTN                Offset;
  UINTN                Index;

  if (ChildHandle == NULL) {
    return EFI_UNSUPPORTED;
  }

  //
  // Locate the SNP protocol to verify the controller.
  //
  Status = gBS->OpenProtocol (
                  Controller,
                  &gMnpSnpProtocolGuid,
                  (VOID **)&Snp,
                  gImageHandle,
                  Controller,
                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
                  );
  if (Status == EFI_SUCCESS) {
    gBS->CloseProtocol (
           Controller,
           &gMnpSnpProtocolGuid,
           gImageHandle,
           Controller
           );
    return EFI_UNSUPPORTED;
  }

  if (Status != EFI_NOT_FOUND) {
    return EFI_UNSUPPORTED;
  }

  //
  // Locate the device path protocol on the controller.
  //
  {
    EFI_DEVICE_PATH_PROTOCOL  *DevicePath;
    UINTN                     DevicePathSize;

    Status = gBS->OpenProtocol (
                    Controller,
                    &gMnpDevicePathGuid,
                    (VOID **)&DevicePath,
                    gImageHandle,
                    Controller,
                    EFI_OPEN_PROTOCOL_GET_PROTOCOL
                    );
    if (EFI_ERROR (Status)) {
      return EFI_UNSUPPORTED;
    }

    Status = FindControllerHandleForDevicePath (
               Controller,
               DevicePath,
               &SnpHandle,
               &DevicePathSize
               );
    if (EFI_ERROR (Status)) {
      return Status;
    }
  }

  //
  // Try to get the MNP instance to query protocol type and VLAN.
  //
  Status = gBS->OpenProtocol (
                  ChildHandle,
                  &gMnpProtocolGuid,
                  (VOID **)&Instance,
                  0,
                  0,
                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
                  );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  Status = MnpControllerName (Instance);
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Free the old table and rebuild.
  //
  if (gComponentNameTable != NULL) {
    FreeUnicodeStringTable (gComponentNameTable);
    gComponentNameTable = NULL;
  }

  return AddUnicodeString2 (
           Language,
           &gMnpDriverNameTable,
           ControllerName,
           NameString,
           (This == &gMnpComponentName2)
           );
}

/* ====================================================================== */
/*  Driver Unload                                                        */
/* ====================================================================== */

/**
 * Unload the MNP driver.
 *
 * Iterates all handles with the service binding protocol installed and
 * uninstalls them if the driver binding handle matches ImageHandle.
 *
 * @param[in] ImageHandle  The image handle of the driver.
 *
 * @retval EFI_SUCCESS     Driver unloaded.
 * @retval Others          Error from LocateHandleBuffer.
 */
EFI_STATUS
EFIAPI
MnpUnload (
  IN EFI_HANDLE  ImageHandle
  )
{
  EFI_STATUS  Status;
  UINTN       HandleCount;
  EFI_HANDLE  *HandleBuffer;
  UINTN       Index;

  Status = gBS->LocateHandleBuffer (
                  ByProtocol,
                  &gMnpServiceBindingGuid,
                  NULL,
                  &HandleCount,
                  &HandleBuffer
                  );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  for (Index = 0; Index < HandleCount; Index++) {
    EFI_SERVICE_BINDING_PROTOCOL  *ServiceBinding;

    Status = gBS->OpenProtocol (
                    HandleBuffer[Index],
                    &gMnpServiceBindingGuid,
                    (VOID **)&ServiceBinding,
                    gImageHandle,
                    HandleBuffer[Index],
                    EFI_OPEN_PROTOCOL_GET_PROTOCOL
                    );
    if (!EFI_ERROR (Status) &&
        ServiceBinding->ImageHandle == ImageHandle) {
      //
      // Close all child protocol opens.
      //
      UINTN  SubIndex;
      for (SubIndex = 0; SubIndex < HandleCount; SubIndex++) {
        gBS->CloseProtocol (
               HandleBuffer[SubIndex],
               ServiceBinding->ProtocolGuid,
               0,
               ServiceBinding->ImageHandle
               );
      }

      //
      // Uninstall the service binding protocol.
      //
      gBS->UninstallMultipleProtocolInterfaces (
             HandleBuffer[Index],
             &gMnpServiceBindingGuid,
             ServiceBinding,
             NULL
             );

      //
      // Uninstall component name protocol if present.
      //
      {
        EFI_COMPONENT_NAME2_PROTOCOL  *ComponentName2;

        Status = gBS->OpenProtocol (
                        HandleBuffer[Index],
                        &gMnpComponentNameGuid,
                        (VOID **)&ComponentName2,
                        gImageHandle,
                        HandleBuffer[Index],
                        EFI_OPEN_PROTOCOL_GET_PROTOCOL
                        );
        if (!EFI_ERROR (Status)) {
          gBS->UninstallMultipleProtocolInterfaces (
                 HandleBuffer[Index],
                 &gMnpComponentNameGuid,
                 ComponentName2,
                 NULL
                 );
        }
      }
    }
  }

  if (HandleBuffer != NULL) {
    FreePool (HandleBuffer);
  }

  return EFI_SUCCESS;
}