/** @file
  This is service binding for Hash driver.

Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include "Driver.h"

EFI_SERVICE_BINDING_PROTOCOL  mHash2ServiceBindingProtocol = {
  Hash2ServiceBindingCreateChild,
  Hash2ServiceBindingDestroyChild
};

/**
  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
Hash2ServiceBindingCreateChild (
  IN     EFI_SERVICE_BINDING_PROTOCOL  *This,
  IN OUT EFI_HANDLE                    *ChildHandle
  )
{
  EFI_STATUS           Status;
  HASH2_SERVICE_DATA   *Hash2ServiceData;
  HASH2_INSTANCE_DATA  *Instance;
  EFI_TPL              OldTpl;

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

  Hash2ServiceData = HASH2_SERVICE_DATA_FROM_THIS (This);

  //
  // Allocate buffer for the new instance.
  //
  Instance = AllocateZeroPool (sizeof (HASH2_INSTANCE_DATA));
  if (Instance == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  //
  // Init the instance data.
  //
  Instance->Signature = HASH2_INSTANCE_DATA_SIGNATURE;
  CopyMem (&Instance->Hash2Protocol, &mHash2Protocol, sizeof (Instance->Hash2Protocol));
  Instance->Hash2ServiceData = Hash2ServiceData;

  Status = gBS->InstallMultipleProtocolInterfaces (
                  ChildHandle,
                  &gEfiHash2ProtocolGuid,
                  &Instance->Hash2Protocol,
                  NULL
                  );
  if (EFI_ERROR (Status)) {
    FreePool (Instance);
    return Status;
  }

  Instance->Handle = *ChildHandle;

  //
  // Add the child instance into ChildrenList.
  //
  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);

  InsertTailList (&Hash2ServiceData->ChildrenList, &Instance->InstEntry);

  gBS->RestoreTPL (OldTpl);

  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
Hash2ServiceBindingDestroyChild (
  IN EFI_SERVICE_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE                    ChildHandle
  )
{
  EFI_STATUS           Status;
  HASH2_SERVICE_DATA   *Hash2ServiceData;
  EFI_HASH2_PROTOCOL   *Hash2Protocol;
  HASH2_INSTANCE_DATA  *Instance;
  EFI_TPL              OldTpl;
  LIST_ENTRY           *Entry;

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

  Hash2ServiceData = HASH2_SERVICE_DATA_FROM_THIS (This);

  //
  // Check if this ChildHandle is valid
  //
  Instance = NULL;
  for (Entry = (&Hash2ServiceData->ChildrenList)->ForwardLink; Entry != (&Hash2ServiceData->ChildrenList); Entry = Entry->ForwardLink) {
    Instance = HASH2_INSTANCE_DATA_FROM_LINK (Entry);
    if (Instance->Handle == ChildHandle) {
      break;
    } else {
      Instance = NULL;
    }
  }

  if (Instance == NULL) {
    DEBUG ((DEBUG_ERROR, "Hash2ServiceBindingDestroyChild - Invalid handle\n"));
    return EFI_UNSUPPORTED;
  }

  //
  // Get HashProtocol
  //
  Status = gBS->HandleProtocol (
                  ChildHandle,
                  &gEfiHash2ProtocolGuid,
                  (VOID **)&Hash2Protocol
                  );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  ASSERT (Hash2Protocol == &Instance->Hash2Protocol);

  //
  // Uninstall the Hash protocol.
  //
  Status = gBS->UninstallMultipleProtocolInterfaces (
                  ChildHandle,
                  &gEfiHash2ProtocolGuid,
                  &Instance->Hash2Protocol,
                  NULL
                  );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);

  //
  // Remove this instance from the ChildrenList.
  //
  RemoveEntryList (&Instance->InstEntry);

  gBS->RestoreTPL (OldTpl);

  FreePool (Instance);

  return Status;
}

/**
  The entry point for Hash driver which installs the service binding protocol.

  @param[in]  ImageHandle  The image handle of the driver.
  @param[in]  SystemTable  The system table.

  @retval EFI_SUCCESS      The service binding protocols is successfully installed.
  @retval Others           Other errors as indicated.

**/
EFI_STATUS
EFIAPI
Hash2DriverEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS          Status;
  HASH2_SERVICE_DATA  *Hash2ServiceData;

  //
  // Initialize the Hash Service Data.
  //
  Hash2ServiceData = AllocateZeroPool (sizeof (HASH2_SERVICE_DATA));
  if (Hash2ServiceData == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  Hash2ServiceData->Signature = HASH2_SERVICE_DATA_SIGNATURE;
  CopyMem (&Hash2ServiceData->ServiceBinding, &mHash2ServiceBindingProtocol, sizeof (EFI_SERVICE_BINDING_PROTOCOL));
  InitializeListHead (&Hash2ServiceData->ChildrenList);

  //
  // Install the HASH Service Binding Protocol
  //
  Status = gBS->InstallMultipleProtocolInterfaces (
                  &Hash2ServiceData->ServiceHandle,
                  &gEfiHash2ServiceBindingProtocolGuid,
                  &Hash2ServiceData->ServiceBinding,
                  NULL
                  );
  if (EFI_ERROR (Status)) {
    FreePool (Hash2ServiceData);
  }

  return Status;
}
