/** @file
  Emulator Thunk to abstract OS services from pure EFI code
  Copyright (c) 2008 - 2011, Apple Inc. All rights reserved.
  Copyright (c) 2011 - 2019, Intel Corporation. All rights reserved.
  SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include 
#include 
#include 
#include 
#include 
#include 
#define EMU_IO_THUNK_PROTOCOL_DATA_SIGNATURE  SIGNATURE_32('E','m','u','T')
typedef struct {
  UINTN                    Signature;
  EMU_IO_THUNK_PROTOCOL    Data;
  BOOLEAN                  EmuBusDriver;
  LIST_ENTRY               Link;
} EMU_IO_THUNK_PROTOCOL_DATA;
LIST_ENTRY  mThunkList = INITIALIZE_LIST_HEAD_VARIABLE (mThunkList);
EFI_STATUS
EFIAPI
AddThunkProtocol (
  IN  EMU_IO_THUNK_PROTOCOL  *ThunkIo,
  IN  CHAR16                 *ConfigString,
  IN  BOOLEAN                EmuBusDriver
  )
{
  UINTN                       Size;
  CHAR16                      *StartString;
  CHAR16                      *SubString;
  UINTN                       Instance;
  EMU_IO_THUNK_PROTOCOL_DATA  *Private;
  if (ThunkIo == NULL) {
    return EFI_INVALID_PARAMETER;
  }
  Instance    = 0;
  Size        = StrSize (ConfigString);
  StartString = AllocatePool (Size);
  if (StartString == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }
  StrCpyS (StartString, Size / sizeof (CHAR16), ConfigString);
  while (*StartString != '\0') {
    //
    // Find the end of the sub string
    //
    SubString = StartString;
    while (*SubString != '\0' && *SubString != '!') {
      SubString++;
    }
    if (*SubString == '!') {
      //
      // Replace token with '\0' to make sub strings. If this is the end
      //  of the string SubString will already point to NULL.
      //
      *SubString = '\0';
      SubString++;
    }
    Private = AllocatePool (sizeof (EMU_IO_THUNK_PROTOCOL_DATA));
    if (Private == NULL) {
      return EFI_OUT_OF_RESOURCES;
    }
    Private->Signature    = EMU_IO_THUNK_PROTOCOL_DATA_SIGNATURE;
    Private->EmuBusDriver = EmuBusDriver;
    CopyMem (&Private->Data, ThunkIo, sizeof (EMU_IO_THUNK_PROTOCOL));
    Private->Data.Instance     = (UINT16)Instance++;
    Private->Data.ConfigString = StartString;
    InsertTailList (&mThunkList, &Private->Link);
    //
    // Parse Next sub string. This will point to '\0' if we are at the end.
    //
    StartString = SubString;
  }
  return EFI_SUCCESS;
}
EFI_STATUS
EFIAPI
GetNextThunkProtocol (
  IN  BOOLEAN                EmuBusDriver,
  OUT EMU_IO_THUNK_PROTOCOL  **Instance  OPTIONAL
  )
{
  LIST_ENTRY                  *Link;
  EMU_IO_THUNK_PROTOCOL_DATA  *Private;
  if (mThunkList.ForwardLink == &mThunkList) {
    // Skip parsing an empty list
    return EFI_NOT_FOUND;
  }
  for (Link = mThunkList.ForwardLink; Link != &mThunkList; Link = Link->ForwardLink) {
    Private = CR (Link, EMU_IO_THUNK_PROTOCOL_DATA, Link, EMU_IO_THUNK_PROTOCOL_DATA_SIGNATURE);
    if (EmuBusDriver & !Private->EmuBusDriver) {
      continue;
    } else if (*Instance == NULL) {
      // Find 1st match in list
      *Instance = &Private->Data;
      return EFI_SUCCESS;
    } else if (*Instance == &Private->Data) {
      // Matched previous call so look for valid next entry
      Link = Link->ForwardLink;
      if (Link == &mThunkList) {
        return EFI_NOT_FOUND;
      }
      Private   = CR (Link, EMU_IO_THUNK_PROTOCOL_DATA, Link, EMU_IO_THUNK_PROTOCOL_DATA_SIGNATURE);
      *Instance = &Private->Data;
      return EFI_SUCCESS;
    }
  }
  return EFI_NOT_FOUND;
}