/** @file
  ACPI Sdt Protocol Driver

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

**/

#include "AcpiTable.h"

/**
  Return the child objects buffer from AML Handle's buffer.

  @param[in]        AmlParentHandle Parent handle.
  @param[in]        CurrentBuffer   The current child buffer.
  @param[out]       Buffer          On return, points to the next returned child buffer or NULL if there are no
                                    child buffer.

  @retval EFI_SUCCESS               Success
  @retval EFI_INVALID_PARAMETER     AmlParentHandle does not refer to a valid ACPI object.
**/
EFI_STATUS
AmlGetChildFromObjectBuffer (
  IN EFI_AML_HANDLE         *AmlParentHandle,
  IN UINT8                  *CurrentBuffer,
  OUT VOID                  **Buffer
  )
{
  AML_BYTE_ENCODING   *AmlByteEncoding;
  UINTN               DataSize;

  //
  // Root is considered as SCOPE, which has TermList.
  // We need return only Object in TermList.
  //
  while ((UINTN)CurrentBuffer < (UINTN)(AmlParentHandle->Buffer + AmlParentHandle->Size)) {
    AmlByteEncoding = AmlSearchByOpByte (CurrentBuffer);
    if (AmlByteEncoding == NULL) {
      return EFI_INVALID_PARAMETER;
    }
    //
    // NOTE: We need return everything, because user might need parse the returned object.
    //
    if ((AmlByteEncoding->Attribute & AML_IS_NAME_CHAR) == 0) {
      *Buffer = CurrentBuffer;
      return EFI_SUCCESS;
    }

    DataSize = AmlGetObjectSize (
                 AmlByteEncoding,
                 CurrentBuffer,
                 (UINTN)AmlParentHandle->Buffer + AmlParentHandle->Size - (UINTN)CurrentBuffer
                 );
    if (DataSize == 0) {
      return EFI_INVALID_PARAMETER;
    }
    CurrentBuffer += DataSize;
  }

  //
  // No more
  //
  *Buffer = NULL;
  return EFI_SUCCESS;
}

/**
  Return the child ACPI objects from Root Handle.

  @param[in]        AmlParentHandle Parent handle. It is Root Handle.
  @param[in]        AmlHandle       The previously returned handle or NULL to start with the first handle.
  @param[out]       Buffer          On return, points to the next returned ACPI handle or NULL if there are no
                                    child objects.

  @retval EFI_SUCCESS               Success
  @retval EFI_INVALID_PARAMETER     ParentHandle is NULL or does not refer to a valid ACPI object.
**/
EFI_STATUS
AmlGetChildFromRoot (
  IN EFI_AML_HANDLE         *AmlParentHandle,
  IN EFI_AML_HANDLE         *AmlHandle,
  OUT VOID                  **Buffer
  )
{
  UINT8               *CurrentBuffer;

  if (AmlHandle == NULL) {
    //
    // First One
    //
    CurrentBuffer = (VOID *)AmlParentHandle->Buffer;
  } else {
    CurrentBuffer = (VOID *)(AmlHandle->Buffer + AmlHandle->Size);
  }

  return AmlGetChildFromObjectBuffer (AmlParentHandle, CurrentBuffer, Buffer);
}

/**
  Return the child objects buffer from AML Handle's option list.

  @param[in]        AmlParentHandle Parent handle.
  @param[in]        AmlHandle       The current child handle.
  @param[out]       Buffer          On return, points to the next returned child buffer or NULL if there are no
                                    child buffer.

  @retval EFI_SUCCESS               Success
  @retval EFI_INVALID_PARAMETER     AmlParentHandle does not refer to a valid ACPI object.
**/
EFI_STATUS
AmlGetChildFromOptionList (
  IN EFI_AML_HANDLE         *AmlParentHandle,
  IN EFI_AML_HANDLE         *AmlHandle,
  OUT VOID                  **Buffer
  )
{
  EFI_ACPI_DATA_TYPE  DataType;
  VOID                *Data;
  UINTN               DataSize;
  AML_OP_PARSE_INDEX  Index;
  EFI_STATUS          Status;
  AML_OP_PARSE_INDEX  MaxTerm;

  Index = AML_OP_PARSE_INDEX_GET_TERM1;
  MaxTerm = AmlParentHandle->AmlByteEncoding->MaxIndex;
  while (Index <= MaxTerm) {
    Status = AmlParseOptionHandleCommon (
               AmlParentHandle,
               (AML_OP_PARSE_INDEX)Index,
               &DataType,
               (VOID **)&Data,
               &DataSize
               );
    if (EFI_ERROR (Status)) {
      return EFI_INVALID_PARAMETER;
    }
    if (DataType == EFI_ACPI_DATA_TYPE_NONE) {
      //
      // Not found
      //
      break;
    }

    //
    // Find it, and Check Data
    //
    if ((DataType == EFI_ACPI_DATA_TYPE_CHILD) &&
        ((UINTN)AmlHandle->Buffer < (UINTN)Data)) {
      //
      // Buffer < Data means current node is next one
      //
      *Buffer = Data;
      return EFI_SUCCESS;
    }
    //
    // Not Child
    //
    Index ++;
  }

  *Buffer = NULL;
  return EFI_SUCCESS;
}

/**
  Return the child objects buffer from AML Handle's object child list.

  @param[in]        AmlParentHandle Parent handle.
  @param[in]        AmlHandle       The current child handle.
  @param[out]       Buffer          On return, points to the next returned child buffer or NULL if there are no
                                    child buffer.

  @retval EFI_SUCCESS               Success
  @retval EFI_INVALID_PARAMETER     AmlParentHandle does not refer to a valid ACPI object.
**/
EFI_STATUS
AmlGetChildFromObjectChildList (
  IN EFI_AML_HANDLE         *AmlParentHandle,
  IN EFI_AML_HANDLE         *AmlHandle,
  OUT VOID                  **Buffer
  )
{
  EFI_STATUS          Status;
  UINT8               *CurrentBuffer;

  CurrentBuffer = NULL;

  if ((AmlParentHandle->AmlByteEncoding->Attribute & AML_HAS_CHILD_OBJ) == 0) {
    //
    // No ObjectList
    //
    *Buffer = NULL;
    return EFI_SUCCESS;
  }

  //
  // Do we need add node within METHOD?
  // Yes, just add Object is OK. But we need filter NameString for METHOD invoke.
  //

  //
  // Now, we get the last node.
  //
  Status = AmlGetOffsetAfterLastOption (AmlParentHandle, &CurrentBuffer);
  if (EFI_ERROR (Status)) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // Go through all the reset buffer.
  //
  if ((UINTN)AmlHandle->Buffer < (UINTN)CurrentBuffer) {
    //
    // Buffer < Data means next node is first object
    //
  } else if ((UINTN)AmlHandle->Buffer + AmlHandle->Size < (UINTN)AmlParentHandle->Buffer + AmlParentHandle->Size) {
    //
    // There is still more node
    //
    CurrentBuffer = AmlHandle->Buffer + AmlHandle->Size;
  } else {
    //
    // No more data
    //
    *Buffer = NULL;
    return EFI_SUCCESS;
  }

  return AmlGetChildFromObjectBuffer (AmlParentHandle, CurrentBuffer, Buffer);
}

/**
  Return the child ACPI objects from Non-Root Handle.

  @param[in]        AmlParentHandle Parent handle. It is Non-Root Handle.
  @param[in]        AmlHandle       The previously returned handle or NULL to start with the first handle.
  @param[out]       Buffer          On return, points to the next returned ACPI handle or NULL if there are no
                                    child objects.

  @retval EFI_SUCCESS               Success
  @retval EFI_INVALID_PARAMETER     ParentHandle is NULL or does not refer to a valid ACPI object.
**/
EFI_STATUS
AmlGetChildFromNonRoot (
  IN EFI_AML_HANDLE         *AmlParentHandle,
  IN EFI_AML_HANDLE         *AmlHandle,
  OUT VOID                  **Buffer
  )
{
  EFI_STATUS          Status;

  if (AmlHandle == NULL) {
    //
    // NULL means first one
    //
    AmlHandle = AmlParentHandle;
  }

  //
  // 1. Get Option
  //
  Status = AmlGetChildFromOptionList (AmlParentHandle, AmlHandle, Buffer);
  if (EFI_ERROR (Status)) {
    return EFI_INVALID_PARAMETER;
  }
  if (*Buffer != NULL) {
    return EFI_SUCCESS;
  }

  //
  // 2. search ObjectList
  //
  return AmlGetChildFromObjectChildList (AmlParentHandle, AmlHandle, Buffer);
}
