/**
 * @file teec_common_qsee.c
 * @brief Common client interface implementation for TA emulation as dyn library
 * @author Iaroslav Makarchuk (i.makarchuk@samsung.com)
 * @date Created Oct 3, 2016
 * @par In Samsung Ukraine R&D Center (SURC) under a contract between
 * @par LLC "Samsung Electronics Ukraine Company" (Kiev, Ukraine) and
 * @par "Samsung Elecrtronics Co", Ltd (Seoul, Republic of Korea)
 * @par Copyright: (c) Samsung Electronics Co, Ltd 2015. All rights reserved.
 *
 * This software is proprietary of Samsung Electronics.
 * No part of this software, either material or conceptual may be copied
 * or distributed, transmitted, transcribed, stored in a retrieval system
 * or translated into any human or computer language in any form by any means,
 * electronic, mechanical, manual or otherwise, or disclosed to third parties
 * without the express written permission of Samsung Electronics.
 */
#include <teec_common.h>

#include <dlfcn.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdio.h>
#include <stdlib.h>

#include <ta_lib_iface.h>
#include <teec_param_utils.h>

#define TA_STR_LEN 64
#define TA_LIB_NAME_LEN 200
typedef int (*ta_caller)(ProtocolCmd *);

typedef struct SoLibSessionStruct {
  char ta_str_[TA_STR_LEN];
  void *ta_handle;
  ta_caller call;
  void *parent_context;
} SoLibSession;

TEEC_Result SoLibUnloadTA(void *ta_session) {
  TEEC_Result retval = TEEC_ERROR_BAD_PARAMETERS;
  SoLibSession *solib_session = (SoLibSession *)ta_session;

  if (!solib_session) {
    goto exit;
  }

  solib_session->call = NULL;
  if (solib_session->ta_handle) {
    dlclose(solib_session->ta_handle);
    solib_session->ta_handle = NULL;
  }

  UnRegisterSwdCmdHandler_t * cb_unregister = dlsym(solib_session->ta_handle, "UnRegisterSwdCmdHandler");
  if (cb_unregister) {
    cb_unregister();
  }

  retval = TEEC_SUCCESS;

exit:
  return retval;
}


TEEC_Result SoLibSendCommand(void *ta_session,
                            uint32_t command_id,
                            TEEC_Operation *operation,
                            uint32_t *returnOrigin,
                            int32_t timeout)
{
  TEEC_Result ret = TEEC_ERROR_BAD_PARAMETERS;
  ProtocolCmd command;
  SoLibSession *solib_session = (SoLibSession *)ta_session;

  memset (&command, 0x00, sizeof(command));

  // Return origin: "API" by default.
  command.return_origin = TEEC_ORIGIN_API;

  if (!solib_session) {
    goto exit;
  }

  command.cmd_id = command_id;
  command.param_types = 0;

  if (operation) {
    command.param_types = operation->paramTypes;
    ret = FillCommandArgs(&command, operation, NULL);

    if (TEEC_SUCCESS != ret) {
      goto exit;
    }
  }

  //Handle panics
  if (MUST_UNLOAD_TA == solib_session->call(&command)) {
    (void)SoLibUnloadTA(solib_session);
  }

  // Return origin: "TEE" or "TRUSTED_APP" after communication with TEE.

  ret = command.cmd_ret;

  if (TEEC_SUCCESS == ret && operation) {
    ret = FillOperationArgs(operation, &command);
    if (TEEC_SUCCESS != ret) {
      command.return_origin = TEEC_ORIGIN_API;
    }
  }

exit:
  if (returnOrigin) {
    *returnOrigin = command.return_origin;
  }

  return ret;
}

TEEC_Result SoLibLoadTA(void *ta_session, const TEEC_UUID *uuid) {
  char ta_lib_name_str[TA_LIB_NAME_LEN] = {0};
  uint32_t i = 0;
  TEEC_Result retval = TEEC_SUCCESS;

  SoLibSession *solib_session = (SoLibSession *)ta_session;

  int write_offset = snprintf(solib_session->ta_str_,
                              sizeof(solib_session->ta_str_), "%08x%04x%04x",
                              uuid->timeLow, uuid->timeMid,
                              uuid->timeHiAndVersion);

  for (i = 0; i < sizeof(uuid->clockSeqAndNode); i++) {
    write_offset += snprintf(solib_session->ta_str_ + write_offset,
                             sizeof(solib_session->ta_str_) - write_offset,
                             "%02x", uuid->clockSeqAndNode[i]);
  }

  snprintf(ta_lib_name_str, sizeof(ta_lib_name_str),
           "libta%s.so", solib_session->ta_str_);

  solib_session->ta_handle = dlopen(ta_lib_name_str, RTLD_LAZY);

  if (!solib_session->ta_handle) {
    retval = TEEC_ERROR_ITEM_NOT_FOUND;
    goto exit;
  }

  solib_session->call = dlsym(solib_session->ta_handle, "HandleCommand");

  if (!solib_session->call) {
    dlclose(solib_session->ta_handle);
    solib_session->ta_handle = NULL;
    retval = TEEC_ERROR_GENERIC;
  }

  RegisterSwdCmdHandler_t * cb_register = dlsym(solib_session->ta_handle, "RegisterSwdCmdHandler");
  if (!cb_register) {
    dlclose(solib_session->ta_handle);
    solib_session->ta_handle = NULL;
    retval = TEEC_ERROR_GENERIC;
  }
  cb_register(&MbHandleSWdCommand, uuid);

exit:
  return retval;
}

PlatformClientImpl g_solib_platform_impl = {
  SoLibLoadTA,
  SoLibUnloadTA,
  SoLibSendCommand,
  NULL,
  NULL,
  NULL,
  NULL,
};

PlatformClientImpl *GetPlatformClientImpl(void) {
  return &g_solib_platform_impl;
}

size_t GetPlatformClientImplSize(void) {
  return sizeof(SoLibSession);
}

void ClientImplSetParentContext(void *session, void *context) {
  SoLibSession *solib_session = (SoLibSession *)session;

  if (solib_session) {
    solib_session->parent_context = context;
  }
}

void *ClientImplGetParentContext(void *session) {
  void *ret = NULL;
  SoLibSession *solib_session = (SoLibSession *)session;

  if (solib_session) {
    ret = solib_session->parent_context;
  }

  return ret;
}
