/*
 * Copyright (c) 2019,2021 Qualcomm Technologies, Inc.
 * All Rights Reserved.
 * Confidential and Proprietary - Qualcomm Technologies, Inc.
 */

#include <pthread.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>

#include "errno.h" /* For ENODEV code */
#include "example_listener.h"
#include "BufferAllocatorWrapperSim.h"
#include "listener.h"
#include "QSEEComAPI.h"

#define LISTENER_BUFFER_SIZE     0x1000
#define LISTENER_FLAGS           0
#define LISTENER_SHUTDOWN_CODE   -ENODEV

/* Used to demonstrate QSEECom_send_modified_resp */
#define LISTENER_ION_BUFFER_SIZE        32
#define LISTENER_ION_BUFFER_ALIGNMENT   0x1000

static BufferAllocatorSimHandle *listener_dma_handle = NULL;

static struct QSEECom_handle *listener_handle;

static pthread_t listener_thread;

static void *listener_fn(void *arg)
{
  (void) arg;

  int error;

  uint8_t user_req_data[LISTENER_BUFFER_SIZE] = {0};
  uint8_t user_rsp_data[LISTENER_BUFFER_SIZE] = {0};

  struct listener_req *req;
  struct listener_rsp *rsp;

  struct QSEECom_ion_fd_info ion_fd_info;

  for (;;) {
    /* Wait until a listener request is received from the TA. */
    error = QSEECom_receive_req(listener_handle,
                                user_req_data,
                                LISTENER_BUFFER_SIZE);
    if (error) {
      if (error != LISTENER_SHUTDOWN_CODE) {
        printf("QSEECom_receive_req failed with error %d\n", error);
      }
      break;
    }

    req = (struct listener_req *)user_req_data;

    printf("Listener receieved cmd: %d\n", req->cmd_id);

    rsp = (struct listener_rsp *)user_rsp_data;

    switch (req->cmd_id) {
    case LISTENER_CMD_1:
      rsp->result1 = req->arg1;
      rsp->result2 = req->arg2;
      break;

    case LISTENER_CMD_2:
      rsp->result1 = req->arg2;
      rsp->result2 = req->arg1;
      break;

    case LISTENER_CMD_3_MODIFIED_RSP:
      rsp->result1 = 0;
      rsp->result2 = 0;
      break;

    default:
      rsp->result1 = -1;
      rsp->result2 = -1;
      break;
    }

    if (req->cmd_id == LISTENER_CMD_3_MODIFIED_RSP) {
      rsp->shared_buffer_size = LISTENER_ION_BUFFER_SIZE;

      memset(&ion_fd_info, 0, sizeof(ion_fd_info));

      ion_fd_info.data[0].fd = mapGetFd(listener_dma_handle);
      ion_fd_info.data[0].cmd_buf_offset = (uintptr_t)&rsp->shared_buffer - (uintptr_t)rsp;

      if (sizeof(void *) == 8) {
        error = QSEECom_send_modified_resp_64(listener_handle,
                                              rsp,
                                              sizeof(*rsp),
                                              &ion_fd_info);
      } else {
        error = QSEECom_send_modified_resp(listener_handle,
                                           rsp,
                                           sizeof(*rsp),
                                           &ion_fd_info);
      }
    } else {
      error = QSEECom_send_resp(listener_handle, rsp, sizeof(*rsp));
    }

    if (error) {
      printf("Failed to send listener response\n");
      break;
    }
  }

  return NULL;
}

int setup_listener(void)
{
  int error;
  if(listener_dma_handle == NULL)
    listener_dma_handle = CreateDmabufHeapBufferAllocatorSim();

  if(!listener_dma_handle) {
    return -1;
  }

  int fd = DmabufHeapAllocSim(listener_dma_handle,
                           LISTENER_ION_BUFFER_SIZE,
                           LISTENER_ION_BUFFER_ALIGNMENT);
  if (fd < 0) {
    printf("example_listener: Failed to allocate ion buffer for QSEECom_send_modified_resp.\n");
    return -1;
  }


  error = QSEECom_register_listener(&listener_handle,
                                    LISTENER_ID,
                                    LISTENER_BUFFER_SIZE,
                                    LISTENER_FLAGS);
  if (error) {
    freeSim(fd);
    DmabufHeapBufferFreeSim(listener_dma_handle);
    return error;
  }

  error = pthread_create(&listener_thread, NULL, listener_fn, NULL);
  if (error) {
    freeSim(fd);
    DmabufHeapBufferFreeSim(listener_dma_handle);
    QSEECom_unregister_listener(listener_handle);
  }

  return error;
}

int cleanup_listener(void)
{
  int error;

  error = QSEECom_unregister_listener(listener_handle);

  pthread_join(listener_thread, NULL);

  freeSim(mapGetFd(listener_dma_handle));
  DmabufHeapBufferFreeSim(listener_dma_handle);

  return error;
}
