/*
 * app_driver.c
 */

#ifdef TEEGRIS_SDK5X
#include <core/uio.h>
#endif
#include <tee_internal_api.h>

#include <errno.h> // errno
#include <fcntl.h>  // O_RDWR
#include <sys/ioctl.h> // ioctl
#include <unistd.h> // close
#include <stdbool.h>

#ifdef TZ_TAG_MTK
#include "tz_tag.h"
#endif
#include "app_main.h"
#include "app_core.h"
#include "app_driver.h"

#include "icccOperations_grdm.h"

#ifdef TEEGRIS_DBG
static inline uint32_t calculate_diff(const TEE_Time *time1, const TEE_Time *time2)
{
    return (time1->millis - time2->millis) + (time1->seconds - time2->seconds) * 1000;
}
#endif

/* For ICCC secure address */
static uint32_t iccc_sec_mem_addr = ICCC_SECURE_MEM_BASE_ADDR;

uint32_t get_sec_ICCC_address(int type)
{
#if defined (TZ_TAG_MTK)
    iccc_sec_mem_addr = get_swd_buffer();
#if DEBUG_ICCC
    printf(TAG "get_swd_buffer iccc_sec_mem_addr : %X \n", iccc_sec_mem_addr);
#endif
#endif
    if (iccc_sec_mem_addr == 0) {
        printf(TAG "Wrong ICCC_SECURE_MEM_BASE_ADDR \n");
        return ICCC_MEM_ADDR_ERROR;
    }
#if DEBUG_ICCC
    printf(TAG "get_sec_ICCC_address iccc_sec_mem_addr : %X \n", iccc_sec_mem_addr);
#endif
    iccc_sec_mem_addr = get_swd_buffer();

    switch (type) {
        case PARAM_FOR_ICCC_SEC_MEM:
            return iccc_sec_mem_addr;
        default:
            return ICCC_MEM_ADDR_ERROR;
    }
}

// .../tima_common/src/TZ_Vendor_tl.c
uint32_t Iccc_phys_write(void *offset, uint32_t length, void *data_buf)
{
    uint32_t ret = TZ_GRDM_ICCC_SUCCESS;
    int drv_ret = 0;
    struct ioctl_mem_access_data data;

    drv_fd = open(drv_name, O_RDWR, 0);
    if (drv_fd < 0) {
        printf(TAG "Iccc_phys_write: drv_open_client failed errno = %d \n", errno);
        return drv_fd;
    }

    data.phys_addr = (uint32_t)offset;
    data.len = length;
    data.status = -1;
    data.rw_addr = data_buf;

#ifdef TEEGRIS_SDK5X
    printf(TAG "Iccc_phys_write: ioctl for TEEGRIS SDK 5.x \n");
    struct ioctl_arg data_new = {};
    data_new.input[0].iov_base = &data;
    data_new.input[0].iov_len = sizeof(struct ioctl_mem_access_data);
    data_new.input[1].iov_base = data_buf;
    data_new.input[1].iov_len = sizeof(data_buf);
    data_new.input_cnt = 2;

    data_new.output[0].iov_base = &data;
    data_new.output[0].iov_len = sizeof(struct ioctl_mem_access_data);
    data_new.output[1].iov_base = data_buf;
    data_new.output[1].iov_len = sizeof(data_buf);
    data_new.output_cnt = 2;
    drv_ret = ioctl(drv_fd, ICCCUTIL_SECURE_PHYS_WRITE, (unsigned long)&data_new);
#else
    printf(TAG "Iccc_phys_write: ioctl for TEEGRIS SDK 4.x \n");
    drv_ret = ioctl(drv_fd, ICCCUTIL_SECURE_PHYS_WRITE, (unsigned long)&data);
#endif
    if (drv_ret < 0) {
        printf(TAG "Iccc_phys_write: ioctl error, errno = %d \n", errno);
        ret = TZ_GRDM_ICCC_FAILURE;
        goto exit;
    }

    if (data.status < 0) {
        printf(TAG "Iccc_phys_write: data.status = %d \n", data.status);
        ret = TZ_GRDM_ICCC_FAILURE;
        goto exit;
    }

exit:
    drv_ret = close(drv_fd);
    if (drv_ret < 0) {
        printf(TAG "Iccc_phys_write: drv_client_close(): close error, errno = %d \n", errno);
        return drv_ret;
    }

    return ret;
}

// .../tima_common/src/TZ_Vendor_tl.c
uint32_t Iccc_phys_read(void *offset, uint32_t length, void *ret_buf)
{
    uint32_t ret = TZ_GRDM_ICCC_SUCCESS;
    int drv_ret = 0;
    struct ioctl_mem_access_data data;

#ifdef TEEGRIS_DBG
    TEE_Time start_time;
    TEE_Time end_time;
    uint32_t time_ms = 0;
    TEE_GetSystemTime(&start_time);
#endif

    drv_fd = open(drv_name, O_RDWR, 0);
    if (drv_fd < 0) {
        printf(TAG "Iccc_phys_read: drv_open_client failed errno = %d \n", errno);
        return drv_fd;
    }

#ifdef TEEGRIS_DBG
    TEE_GetSystemTime(&end_time);
    time_ms = calculate_diff(&end_time, &start_time);
    printf("driver open time_ms: %u\n", time_ms);
    TEE_MemFill(&start_time, 0, sizeof(TEE_Time));
    TEE_MemFill(&end_time, 0, sizeof(TEE_Time));
    time_ms = 0;
    TEE_GetSystemTime(&start_time);
#endif

    data.phys_addr = (uint32_t)offset;
    data.len = length;
    data.rw_addr = ret_buf;
    data.status = -1;
    TEE_MemFill(data.rw_addr, 0, length);

#ifdef TEEGRIS_SDK5X
    printf(TAG "Iccc_phys_read: ioctl for TEEGRIS SDK 5.x \n");
    struct ioctl_arg data_new = {};
    data_new.input[0].iov_base = &data;
    data_new.input[0].iov_len = sizeof(struct ioctl_mem_access_data);
    data_new.input[1].iov_base = ret_buf;
    data_new.input[1].iov_len = sizeof(ret_buf);
    data_new.input_cnt = 2;

    data_new.output[0].iov_base = &data;
    data_new.output[0].iov_len = sizeof(struct ioctl_mem_access_data);
    data_new.output[1].iov_base = ret_buf;
    data_new.output[1].iov_len = sizeof(ret_buf);
    data_new.output_cnt = 2;
    drv_ret = ioctl(drv_fd, ICCCUTIL_SECURE_PHYS_READ, (unsigned long)&data_new);
#else
    printf(TAG "Iccc_phys_read: ioctl for TEEGRIS SDK 4.x \n");
    drv_ret = ioctl(drv_fd, ICCCUTIL_SECURE_PHYS_READ, (unsigned long)&data);
#endif
    if (drv_ret < 0) {
        printf(TAG "Iccc_phys_read: ioctl error, errno = %d \n", errno);
        ret = TZ_GRDM_ICCC_FAILURE;
        goto exit;
    }

#ifdef TEEGRIS_DBG
    TEE_GetSystemTime(&end_time);
    time_ms = calculate_diff(&end_time, &start_time);
    printf("driver ioctl time_ms: %u\n", time_ms);
    TEE_MemFill(&start_time, 0, sizeof(TEE_Time));
    TEE_MemFill(&end_time, 0, sizeof(TEE_Time));
    time_ms = 0;
#endif

    if (data.status < 0) {
        printf(TAG "Iccc_phys_read: data.status = %d \n", data.status);
        ret = TZ_GRDM_ICCC_FAILURE;
        goto exit;
    }

exit:
    drv_ret = close(drv_fd);
    if (drv_ret < 0) {
        printf(TAG "Iccc_phys_read: drv_client_close(): close error, errno = %d \n", errno);
        return drv_ret;
    }

    return ret;
}
