/**
 * Copyright (C) 2011 Samsung Electronics Co., Ltd. All rights reserved.
 *
 * Mobile Communication Division,
 * Digital Media & Communications Business, Samsung Electronics Co., Ltd.
 *
 * This software and its documentation are confidential and proprietary
 * information of Samsung Electronics Co., Ltd.  No part of the software and
 * documents may be copied, reproduced, transmitted, translated, or reduced to
 * any electronic medium or machine-readable form without the prior written
 * consent of Samsung Electronics.
 *
 * Samsung Electronics makes no representations with respect to the contents,
 * and assumes no responsibility for any errors that might appear in the
 * software and documents. This publication and the contents hereof are subject
 * to change without notice.
 *
 */

/**
 * @file hdcp2_exynos.cpp
 * @author
 * @date
 * @brief This file contains core functions to map/unmap address and initializeing/releasing the secure memory parameters.
 */

#include <hdcp2.h>
#include <time.h>
#include <stdio.h>
#include <memory.h>
#include <sys/time.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <signal.h>
#include <pthread.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <sys/stat.h>

#include <android/log.h>
#include "hdcp2_common.h"
#include "ion.h"
#include "log.h"

#ifdef USE_MTK
#include "MTK/ion_drv.h"
#endif /* USE_MTK */

#define DUMP_HEX

/**
 * @def ion_phys_addr_t
 *
 * The macro is assigned value unsigned long to be maintained everywhere.
 */
#ifdef CONFIG_KERNEL_ARCH32
#define ion_phys_addr_t unsigned long
#else
#define ion_phys_addr_t uint64_t
#endif /* CONFIG_KERNEL_ARCH32 */

#ifdef USE_MTK
#define ION_HEAP_DMA_RESERVED_MASK (1 << 23)
#endif /* USE_MTK */

/**
 * @struct secfd_info
 *
 * This structure contains information regarding secured fd
 *
 */
struct secfd_info {
	int fd;
	ion_phys_addr_t phys;
};

/**
 * @struct fdphy_list
 *
 * This structure contains information regarding list of file desciptors.
 *
 */
struct fdphy_list {
	struct fdphy_list *next;
	struct fdphy_list *prev;
	struct secfd_info fdinfo;
};

/**
 * @struct secchunk_info
 *
 * This structure contains information regarding secured chunk
 *
 */
#ifdef CONFIG_KERNEL_ARCH32
struct secchunk_info {
	int index;
	uint8_t name[20];
	uint32_t base;
	uint32_t size;
};
#else
struct secchunk_info {
	int index;
	uint8_t name[20];
	uint64_t base;
	uint64_t size;
};
#endif /* CONFIG_KERNEL_ARCH32 */

/* secmem ioctl command */
/**
 * @def SECMEM_IOC_CHUNKINFO
 *
 * This macro is assigned a value of _IOWR('S', 1, struct secchunk_info) to be maintained everywhere.
 */
#define SECMEM_IOC_CHUNKINFO	_IOWR('S', 1, struct secchunk_info)

/**
 * @def SECMEM_IOC_SET_DRM_ONOFF
 *
 * This macro is assigned a value of _IOWR('S', 2, int) to be maintained everywhere.
 */
#define SECMEM_IOC_SET_DRM_ONOFF	_IOWR('S', 2, int)

/**
 * @def SECMEM_IOC_GET_DRM_ONOFF
 *
 * This macro is assigned a value of _IOWR('S', 3, int) to be maintained everywhere.
 */
#define SECMEM_IOC_GET_DRM_ONOFF	_IOWR('S', 3, int)

/**
 * @def SECMEM_IOC_GET_CRYPTO_LOCK
 *
 * This macro is assigned a value of _IOR('S', 4, int) to be maintained everywhere.
 */
#define SECMEM_IOC_GET_CRYPTO_LOCK	_IOR('S', 4, int)

/**
 * @def SECMEM_IOC_RELEASE_CRYPTO_LOCK
 *
 * This macro is assigned a value of _IOR('S', 5, int) to be maintained everywhere.
 */
#define SECMEM_IOC_RELEASE_CRYPTO_LOCK	_IOR('S', 5, int)

/**
 * @def SECMEM_IOC_GET_ADDR
 *
 * This macro is assigned a value of _IOWR('S', 6, int) to be maintained everywhere.
 */
#define SECMEM_IOC_GET_ADDR	_IOWR('S', 6, int)

/**
 * @def SECMEM_IOC_RELEASE_ADDR
 *
 * This macro is assigned a value of _IOWR('S', 7, int) to be maintained everywhere.
 */
#define SECMEM_IOC_RELEASE_ADDR	_IOWR('S', 7, int)

/**
 * @def SECMEM_IOC_GET_FD_PHYS_ADDR
 *
 * This macro is assigned a value of _IOWR('S', 8, struct secfd_info) to be maintained everywhere.
 */
#define SECMEM_IOC_GET_FD_PHYS_ADDR	_IOWR('S', 8, struct secfd_info)

/**
 * @def SECMEM_IOC_GET_CHUNK_NUM
 *
 * This macro is assigned a value of IOWR('S', 9, int) to be maintained everywhere.
 */
#define SECMEM_IOC_GET_CHUNK_NUM	_IOWR('S', 9, int)

/**
 * @def SECMEM_IOC_REQ_MIF_LOCK
 *
 * This macro is assigned a value of _IOWR('S', 10, int) to be maintained everywhere.
 */
#define SECMEM_IOC_REQ_MIF_LOCK	_IOWR('S', 10, int)

/**
 * @def ION_HEAP_EXYNOS_CONTIG_MASK
 *
 * This macro is assigned a value of (1 << 4) to be maintained everywhere.
 */
#define ION_HEAP_EXYNOS_CONTIG_MASK (1 << 4)

struct fdphy_list g_fdphy_head;

static void *input_virtaddr, *input_phyaddr;
static void *output_virtaddr, *output_phyaddr;
static struct secmem_buffer input_buf = {-1, NULL, 0, 0 };
static struct secmem_buffer output_buf = { -1, NULL, 0, 0 };
static int g_secmem_init_count;
static int g_ion_fd = -1;

/**
 * @def ERR_CRYPTO_DEV_OPEN
 *
 * This macro is assigned a value of 0x0001 to be maintained everywhere.
 */
#define ERR_CRYPTO_DEV_OPEN	0x0001

/**
 * @def ERR_CRYPTO_DEV_GET_LOCK
 *
 * This macro is assigned a value of 0x0002 to be maintained everywhere.
 */
#define ERR_CRYPTO_DEV_GET_LOCK	0x0002

/**
 * @def ERR_CRYPTO_DEV_RELEASE_LOCK
 *
 * This macro is assigned a value of 0x0003 to be maintained everywhere.
 */
#define ERR_CRYPTO_DEV_RELEASE_LOCK	0x0003

/**
 * @def GET_MIF_LOCK
 *
 * This macro is assigned a value of 1 to be maintained everywhere.
 */
#define GET_MIF_LOCK	1

/**
 * @def RELEASE_MIF_LOCK
 *
 * This macro is assigned a value of 0 to be maintained everywhere.
 */
#define RELEASE_MIF_LOCK	0

/**
 * @def SMEM_PATH
 *
 * This macro is assigned a value of "/dev/s5p-smem" to be maintained everywhere.
 */
#define SMEM_PATH	"/dev/s5p-smem"

/**
 * @def TBASE_PRODUCTID_310
 *
 * This macro is assigned a value of Android-310 to check if version of tbase is 310
 */
#define TBASE_PRODUCTID_310	"Android-310"

/**
 * @def MAX_NAME_LENGTH
 *
 * This macro is assigned a value of 20 to be maintained everywhere
 *
 */
#define MAX_NAME_LENGTH	20

const mcUuid_t UUID_TRANSMITTER = TL_HDCP2_TRANSMITTER_UUID;
const mcUuid_t UUID_RECEIVER = TL_HDCP2_RECEIVER_UUID;
const uint32_t DEVICE_ID = MC_DEVICE_ID_DEFAULT;

static mcSessionHandle_t s_sessionHandle = {0,0};

static u8 *s_map_buffer = 0;

/**
 * @def MAP_BUFFER_SIZE
 *
 * This macro is assigned a value of 1024*512 to be maintained everywhere
 *
 */
#ifdef USE_MTK
#define MAP_BUFFER_SIZE	((1024*512)+50)
#else
#define MAP_BUFFER_SIZE	((1024*512)+20) /*0.5MB*/ //added 20 bytes for copy_to_secumem
#endif /* USE_MTK */

/**
 * @fn int crypto_dev_lock()
 * @brief Sets the crypto lock in the device
 * @return int - 0 in case of success, error code in case of failure
 */
int crypto_dev_lock()
{
	int fd;
	int req_mif_lock = GET_MIF_LOCK;

	fd = open(SMEM_PATH, O_RDWR | O_NDELAY);
	if (fd < 0)
		return -ERR_CRYPTO_DEV_OPEN;

	/* Call MIF lock - crypto h/w clock up */
	if (ioctl(fd, SECMEM_IOC_REQ_MIF_LOCK, &req_mif_lock) != 0) {
		HDCP2_Log("Need MIF lock");
	}

	//Set Crypto Lock
	if (ioctl(fd, SECMEM_IOC_GET_CRYPTO_LOCK) < 0) {
		close(fd);
		return -ERR_CRYPTO_DEV_GET_LOCK;
	}

	close(fd);
	return 0;
}
/**
 * @fn int crypto_dev_unlock()
 * @brief This function releases the MIF lock
 * @return int - 0 in case of success, error code in case of failure
 */
int crypto_dev_unlock()
{
	int fd;
	int req_mif_lock = RELEASE_MIF_LOCK;

	fd = open(SMEM_PATH, O_RDWR | O_NDELAY);

	if (fd < 0)
		return -ERR_CRYPTO_DEV_OPEN;

	/* Call MIF lock - crypto h/w clock release */
	if (ioctl(fd, SECMEM_IOC_REQ_MIF_LOCK, &req_mif_lock) != 0) {
		HDCP2_Log("Need to release MIF lock");
	}

	//Release Crypto Lock
	if (ioctl(fd, SECMEM_IOC_RELEASE_CRYPTO_LOCK) < 0) {
		close(fd);
		return -ERR_CRYPTO_DEV_RELEASE_LOCK;
	}

	close(fd);
	return 0;
}

/**
 * @fn static unsigned long ion2paddr(const int fd_devion, const int ion_fd)
 * @brief This function obtains physical address for ion_fd
 * @param fd_devion - open file descriptor
 * @param ion_fd - secure memory file desciptor
 * @return long - physical address for file desciptor ion_fd.
 */
static addr_s ion2paddr(const int fd_devion, const int ion_fd)
{
	int ret;
	struct secfd_info secfd;

	secfd.fd = ion_fd;
	ret = ioctl(fd_devion, SECMEM_IOC_GET_FD_PHYS_ADDR, &secfd);
	if (ret < 0) {
		HDCP2_Log("cannot obtain phys addr for %d (err %d)", ion_fd, ret);
		HDCP2_Log("%s", strerror(errno));
		return 0;
	}

	return secfd.phys;
}

/**
 * @fn static int _alloc_buf(HDCP2_Ctx *hdcp, int len, secmem_buffer *buf, const int g_fd_mem)
 * @brief This function allocates buffer and map physical address to virtual address.
 * @param hdcp - pointer to HDCP context
 * @param len - size of buffer
 * @param buf - type of buffer - input or output buffer
 * @param g_fd_mem - open file descriptor
 * @return int - returns 0 if success else returns corresponding error code.
 */
static int _alloc_buf(HDCP2_Ctx *hdcp, int len, secmem_buffer *buf, const int g_fd_mem)
{
	int ret;

	if (!hdcp || len < 0 || g_fd_mem < 0 ){
		HDCP2_Log("Invalid input in _alloc_buf(). hdcp:%x, len:%d, g_fd_mem:%d", hdcp, len, g_fd_mem);
		return HDCP2_ERR_INVALID_INPUT;
	}

	ret = ion_alloc_fd(hdcp->ion_fd, len, 0, ION_HEAP_EXYNOS_CONTIG_MASK, 0, &buf->buf_fd);
	if (ret < 0) {
		HDCP2_Log("%s..len = %d", strerror(errno), len);
		goto err_alloc;
	}
	buf->len = len;

	buf->virt_addr = (u8*)mmap(NULL, len, PROT_READ | PROT_WRITE,
							MAP_SHARED, buf->buf_fd, 0);
	if (buf->virt_addr == MAP_FAILED) {
		HDCP2_Log("cannot map ion buf %d (len=%d)", buf->buf_fd, len);
		goto err_map;
	}

	if ((buf->phys_addr = ion2paddr(g_fd_mem, buf->buf_fd)) == 0)
		return -1;

	return 0;

err_map:
	close(buf->buf_fd);

err_alloc:
	buf->buf_fd = -1;
	return -1;
}

/**
 * @fn static int _free_buf(HDCP2_Ctx *hdcp, secmem_buffer *buf)
 * @brief This function unmaps the buffer virtual address and closes fd.
 * @param hdcp - pointer to HDCP context
 * @param buf - buffer to be freed
 * @return int - returns HDCP2_OK else returns HDCP2_ERR_INVALID_INPUT.
 */
static int _free_buf(HDCP2_Ctx *hdcp, secmem_buffer *buf)
{
	if (!hdcp || !buf || buf->buf_fd < 0) {
		HDCP2_Log("Invalid input in _free_buf(). hdcp:%x, buf:%x", hdcp, buf);
		return HDCP2_ERR_INVALID_INPUT;
	}

	munmap(buf->virt_addr, buf->len);
	close(buf->buf_fd);
	buf->buf_fd = -1;

	return HDCP2_OK;
}

#ifdef USE_MTK
int ion_custom_ioctl(int fd, unsigned int cmd, void* arg) {
    struct ion_custom_data custom_data;
    custom_data.cmd = cmd;
    custom_data.arg = (unsigned long) arg;

    int ret = ioctl(fd, ION_IOC_CUSTOM, &custom_data);
    if (ret < 0) {
        return -errno;
    }

    return ret;
}

int HDCP2_IonMemInit(HDCP2_Ctx *hdcp, SECMEM_INIT_CMD *cmd)
{
	int ret = 0;
	struct ion_sys_data sys_data1;
	struct ion_sys_data sys_data2;
	ion_user_handle_t handle1 = 0;
	ion_user_handle_t handle2 = 0;
	memset(&sys_data1, 0x0, sizeof(sys_data1));
	memset(&sys_data2, 0x0, sizeof(sys_data2));

	if (!hdcp) {
		HDCP2_Log("[ion_meminit] Invaild input");
		return HDCP2_ERR_INVALID_INPUT;
	}
	hdcp->ion_fd = ion_open();
	if (hdcp->ion_fd < 0) {
		HDCP2_Log("cannot open ion");
		return HDCP2_ERR_CLOSED;
	}

	ret = ion_alloc_fd(hdcp->ion_fd, MAX_VIDEO_ENCRYPT_BUFFER, 0, ION_HEAP_DMA_RESERVED_MASK, 0, &hdcp->input_buf.buf_fd);
	if (ret < 0) {
		HDCP2_Log("cannot allocate input buf(%d)", strerror(errno), ret);
		goto err;
	}

	hdcp->input_buf.len = MAX_VIDEO_ENCRYPT_BUFFER;

	hdcp->input_buf.virt_addr = (u8*)mmap(NULL, MAX_VIDEO_ENCRYPT_BUFFER, PROT_READ | PROT_WRITE, MAP_SHARED, hdcp->input_buf.buf_fd, 0);
	if (hdcp->input_buf.virt_addr == MAP_FAILED) {
		HDCP2_Log("cannot map ion buf %d (len=%d)", hdcp->input_buf.buf_fd, hdcp->input_buf.len);
		goto err;
	}

	ret = ion_import(hdcp->ion_fd, hdcp->input_buf.buf_fd, &handle1);
	if (ret < 0) {
		HDCP2_Log("ion import fail with error code %d i.e %s. ",errno, strerror(errno));
		goto err;
	}
	sys_data1.sys_cmd = ION_SYS_GET_PHYS;
	sys_data1.get_phys_param.handle = handle1;

	ret = ion_custom_ioctl(hdcp->ion_fd, ION_CMD_SYSTEM, &sys_data1);
	if (ret) {
		HDCP2_Log("ioctl[ION_ION_CUSTOM] Get phys failed!(ret %d, errono %d, i.e %s)", ret, errno, strerror(errno));
		ret = -1;
		goto err;
	}

	hdcp->input_buf.phys_addr = sys_data1.get_phys_param.phy_addr;

	ret = ion_alloc_fd(hdcp->ion_fd, MAX_VIDEO_ENCRYPT_BUFFER, 0, ION_HEAP_DMA_RESERVED_MASK, 0, &hdcp->output_buf.buf_fd);
	if (ret < 0) {
		HDCP2_Log("cannot allocate output buf(%d)", strerror(errno), ret);
		goto err;
	}

	hdcp->output_buf.len = MAX_VIDEO_ENCRYPT_BUFFER;

	hdcp->output_buf.virt_addr = (u8*)mmap(NULL, MAX_VIDEO_ENCRYPT_BUFFER, PROT_READ | PROT_WRITE, MAP_SHARED, hdcp->output_buf.buf_fd, 0);
	if (hdcp->output_buf.virt_addr == MAP_FAILED) {
		HDCP2_Log("cannot map ion buf %d (len=%d)", hdcp->output_buf.buf_fd, hdcp->output_buf.len);
		goto err;
	}

	ret = ion_import(hdcp->ion_fd, hdcp->output_buf.buf_fd, &handle2);
	if (ret < 0) {
		HDCP2_Log("ion import fail with error code %d i.e %s. ",errno, strerror(errno));
		goto err;
	}

	sys_data2.sys_cmd = ION_SYS_GET_PHYS;
	sys_data2.get_phys_param.handle = handle2;

	ret = ion_custom_ioctl(hdcp->ion_fd, ION_CMD_SYSTEM, &sys_data2);
	if (ret) {
		HDCP2_Log("ioctl[ION_ION_CUSTOM] Get phys failed!(ret %d, errono %d, i.e %s)", ret, errno, strerror(errno));
		ret = -1;
		goto err;
	}

	hdcp->output_buf.phys_addr = sys_data2.get_phys_param.phy_addr;

	hdcp->input_virtaddr = (u8 *)hdcp->input_buf.virt_addr;
	hdcp->input_physaddr = (addr_s)hdcp->input_buf.phys_addr;
	hdcp->output_virtaddr = (u8 *)hdcp->output_buf.virt_addr;
	hdcp->output_physaddr = (addr_s)hdcp->output_buf.phys_addr;

	memset(hdcp->input_virtaddr, 0x00, hdcp->input_buf.len);
	memset(hdcp->output_virtaddr, 0x00, hdcp->output_buf.len);

	return HDCP2_OK;
err:
	ion_close(hdcp->ion_fd);
	hdcp->ion_fd = -1;
	return -1;
}
#endif /* USE_MTK */

/**
 * @fn static int crypt_mem_init(HDCP2_Ctx *hdcp, const int g_fd_mem)
 * @brief This function allocated input/output memory buffers and virtual addresses
 * @param hdcp - pointer to HDCP context
 * @param g_fd_mem - open file descriptor
 * @return int - returns 0 in case of success else returns -1.
 */
static int crypt_mem_init(HDCP2_Ctx *hdcp, const int g_fd_mem)
{
	if (hdcp == NULL || g_fd_mem < 0 ){
		HDCP2_Log("[crypt_mem_init] Invalid Input");
		goto err_ion_open;
	}

	hdcp->ion_fd = ion_open();
	if (hdcp->ion_fd < 0) {
		HDCP2_Log("cannot open ion");
		goto err_ion_open;
	}

	if (_alloc_buf(hdcp, MAX_VIDEO_ENCRYPT_BUFFER, &hdcp->input_buf, g_fd_mem) < 0) {
		HDCP2_Log("cannot allocate input buf");
		goto err_alloc;
	}
	if (_alloc_buf(hdcp, MAX_VIDEO_ENCRYPT_BUFFER, &hdcp->output_buf, g_fd_mem) < 0) {
		HDCP2_Log("cannot allocate output buf");
		goto err_alloc;
	}

	hdcp->input_virtaddr = (u8 *)hdcp->input_buf.virt_addr;
	hdcp->input_physaddr = (addr_s)hdcp->input_buf.phys_addr;
	hdcp->output_virtaddr = (u8 *)hdcp->output_buf.virt_addr;
	hdcp->output_physaddr = (addr_s)hdcp->output_buf.phys_addr;

	memset(hdcp->input_virtaddr, 0x00, hdcp->input_buf.len);
	memset(hdcp->output_virtaddr, 0x00, hdcp->output_buf.len);
	return 0;

err_alloc:
	ion_close(hdcp->ion_fd);
	hdcp->ion_fd = -1;

err_ion_open:
	close(g_fd_mem);
	return -1;
}

/**
 * @fn static int crypt_mem_final(HDCP2_Ctx *hdcp)
 * @brief This releases input and output buffer, remove the input_virtaddr and output_virtaddr
 * @param hdcp - pointer to HDCP context
 * @return int 0 if success, else returns corresponding error number.
 */
static int crypt_mem_final(HDCP2_Ctx *hdcp)
{
	if (!hdcp) {
		HDCP2_Log("Invalid input in crypt_mem_final(). hdcp:%x", hdcp);
		return HDCP2_ERR_INVALID_INPUT;
	}

	if (hdcp->ion_fd >= 0) {
		_free_buf(hdcp, &hdcp->input_buf);
		_free_buf(hdcp, &hdcp->output_buf);
		if (!hdcp->input_virtaddr)
			hdcp->input_virtaddr = NULL;
		if (!hdcp->output_virtaddr)
			hdcp->output_virtaddr = NULL;

		ion_close(hdcp->ion_fd);
		hdcp->ion_fd = -1;
	}

	return 0;
}

/**
 * @fn int HDCP2_SecureMemoryInit(HDCP2_Ctx *hdcp, SECMEM_INIT_CMD *cmd)
 * @brief This function initializes secure memory parameters.
 * @param hdcp - pointer to HDCP context
 * @param cmd - command for secured memory init,SECMEM_INIT_CMD
 * @return int - returns HDCP2_OK if success else returns corresponding error code.
 */
int HDCP2_SecureMemoryInit(HDCP2_Ctx *hdcp, SECMEM_INIT_CMD *cmd)
{
	int ret;
	int fd;
	int idx = 0;

#ifdef HDCP2_DATAPATH_PROTECTION
	int value = 0;
#endif /* HDCP2_DATAPATH_PROTECTION */

	/* Open secmem device */
	fd = open(SMEM_PATH, O_RDWR);
	if (fd < 0) {
		HDCP2_Log("Open s5p-smem device error");
		return HDCP2_ERR_HW_INIT;
	}

#ifdef USE_SECMEM_IOC_CHUNK
	/* MC310 doesn't support SECMEM_IOC_GET_CHUNK_NUM & SECMEM_IOC_CHUNKINFO ioctl */
	if (NULL == strstr(hdcp->versionInfo.productId,TBASE_PRODUCTID_310)) {
		ret = ioctl(fd, SECMEM_IOC_GET_CHUNK_NUM, &cmd->chunk_num);
		if (ret != 0) {
			HDCP2_Log("Failed to get chunk info number: %s: ret(%d)", strerror(errno), ret);
			close(fd);
			return HDCP2_OK;
		}
	}
#endif /* USE_SECMEM_IOC_CHUNK */

	ret = crypt_mem_init(hdcp, fd);
	if (ret != 0) {
		return HDCP2_ERR_HW_INIT;
	}

	HDCP2_DEBUG("in_vir = 0x%.8x, in_phy = 0x%.8x\n", hdcp->input_virtaddr, hdcp->input_physaddr);
	HDCP2_DEBUG("out_vir = 0x%.8x, out_phy = 0x%.8x\n", hdcp->output_virtaddr, hdcp->output_physaddr);

#ifdef HDCP2_DATAPATH_PROTECTION
	if ((hdcp->entity == HDCP2_RECEIVER)||(hdcp->entity == HDCP2_REPEATER)) {
		ret = ioctl(fd, SECMEM_IOC_GET_DRM_ONOFF, &value);
		if (ret != 0) {
			HDCP2_Log("Fail to get SECMEM info: ret(%d)", ret);
			close(fd);
			return HDCP2_ERR_HW_INIT;
		}

		if (value == 1) {
			HDCP2_Log("DRM playback is on progress");
			close(fd);
			return HDCP2_ERR_HW_INIT;
		}
	}
#endif /* HDCP2_DATAPATH_PROTECTION */

	memset(cmd->secmem, 0x00, sizeof(cmd->secmem));

#ifdef USE_SECMEM_IOC_CHUNK
	/* MC310 doesn't support SECMEM_IOC_GET_CHUNK_NUM & SECMEM_IOC_CHUNKINFO ioctl */
	if (NULL == strstr(hdcp->versionInfo.productId,TBASE_PRODUCTID_310)) {
		for (idx = 0; idx < cmd->chunk_num; idx++) {
			cmd->secmem[idx].index = idx;

			if (ioctl(fd, SECMEM_IOC_CHUNKINFO, &cmd->secmem[idx]) != 0)
				break;

			HDCP2_DEBUG("index = %d, name = %s, base = 0x%x, size = %d",
					cmd->secmem[idx].index, cmd->secmem[idx].name, cmd->secmem[idx].base, cmd->secmem[idx].size);
		}
	}
#endif /* USE_SECMEM_IOC_CHUNK */

#ifdef HDCP2_DATAPATH_PROTECTION
	if ((hdcp->entity == HDCP2_RECEIVER)||(hdcp->entity == HDCP2_REPEATER)) {
		value = 1;
		ioctl(fd, SECMEM_IOC_SET_DRM_ONOFF, &value);
	}
#endif /* HDCP2_DATAPATH_PROTECTION */

	close(fd);

	return HDCP2_OK;
}

/**
 * @fn int HDCP2_SecureMemoryRelease(HDCP2_Ctx *hdcp)
 * @brief This function releases secure memory resources.
 * @param hdcp - pointer to HDCP context
 * @return int HDCP2_OK
 */
int HDCP2_SecureMemoryRelease(HDCP2_Ctx *hdcp)
{
	crypt_mem_final(hdcp);

	return HDCP2_OK;
}

/**
 * @fn int HDCP2_HW_Init(HDCP2_Ctx *hdcp)
 * @brief This function gets crypto device lock, initializes the secure memory and sets the hdcp2 flag by initializing the trustzone
 * @param hdcp - pointer to HDCP context
 * @return int - HDCP2_OK if success else returns corresponding error code.
 */
int HDCP2_HW_Init(HDCP2_Ctx *hdcp)
{
	int ret = 0;
	SECMEM_INIT_CMD cmd;

	if (!hdcp) {
		HDCP2_Log("Invalid input in HDCP2_HW_Init(). hdcp:%x", hdcp);
		return HDCP2_ERR_INVALID_INPUT;
	}

#ifdef USE_MTK
	ret = HDCP2_IonMemInit(hdcp, &cmd);
	if (ret != HDCP2_OK) {
		HDCP2_Log("ION initialization error");
	}
#else
	// Get crypto device lock
	if ((ret = crypto_dev_lock()) < 0) {
		HDCP2_Log("Cannot use crypto device. errno(%d)", ret);
		return ret;
	}

	ret = HDCP2_SecureMemoryInit(hdcp, &cmd);
	if (ret != HDCP2_OK) {
		HDCP2_Log("set secmem initialization error");
		return ret;
	}
#endif /* USE_MTK */

	if (hdcp->entity == HDCP2_TRANSMITTER)
		ret = TZ_COMMAND_T(CMD_TRUSTZONE_INIT, (u8 *)&cmd, sizeof(cmd), NULL, 0);
	else
		ret = TZ_COMMAND_R(CMD_TRUSTZONE_INIT, (u8 *)&cmd, sizeof(cmd), NULL, 0);

	if (ret == HDCP2_ERR_SET_HDCP2_FLAG) {
		HDCP2_Log("Cannot set HDCP2 Flag...Ignore...");
		ret = HDCP2_OK;
	} else {
		hdcp->hwinit = 1;
	}

	return ret;
}

/**
 * @fn int HDCP2_HW_Close(HDCP2_Ctx *hdcp)
 * @brief This function closes the trustzone, releases the secure memory and  the crypto device lock.
 * @param hdcp - pointer to HDCP context.
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error.
 */
int HDCP2_HW_Close(HDCP2_Ctx *hdcp)
{
	int ret = HDCP2_OK;

	if (!hdcp) {
		HDCP2_Log("Invalid input in HDCP2_HW_Close(). hdcp:%x", hdcp);
		return HDCP2_ERR_INVALID_INPUT;
	}

	if (!hdcp->hwinit)
		return HDCP2_OK;

	if (hdcp->entity == HDCP2_TRANSMITTER)
		ret = TZ_COMMAND_T(CMD_TRUSTZONE_CLOSE, NULL, 0, NULL, 0);
	else
		ret = TZ_COMMAND_R(CMD_TRUSTZONE_CLOSE, NULL, 0, NULL, 0);

	if (ret != HDCP2_OK)
		HDCP2_Log("Cannot close HW (%d)", ret);

	// Finalize secmem even if tz close was failed.
	if ((ret = HDCP2_SecureMemoryRelease(hdcp)) != HDCP2_OK)
		HDCP2_Log("Cannot release sec mem. errno(%d)", ret);
#ifndef USE_MTK
	// Release crypto device lock even if tz and secmem close failed.
	if ((ret = crypto_dev_unlock()) < 0)
		HDCP2_Log("Cannot release crypto device lock. errno(%d)", ret);
#endif

	if (ret != HDCP2_OK)
		HDCP2_Log("HW Close Failed(%d)...Ignore", ret);
	hdcp->hwinit = 0;
	return ret;
}

/**
 * @fn int HDCP2_SetMagicKey(HDCP2_Ctx *hdcp)
 * @brief This function is to set Magic Key in secure world(trustzone).
 * @param hdcp - pointer to HDCP context.
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error.
 */
int HDCP2_SetMagicKey(HDCP2_Ctx *hdcp)
{
	int ret = 0;

	TZ_COMMAND_T(CMD_SET_HDCP2_MAGICKEY, NULL, 0, (u8 *)&ret, 4);
	HDCP2_Log("HDCP2_SetMagicKey");

	return ret;
}

/**
 * @fn int HDCP2_Ion2Addr(const int ionfd, u8 **paddr, u8 **vaddr, const int length)
 * @brief This function gets the physical address of file descriptor passed and map it to virtual memory
 * @param ionfd - input file desciptor
 * @param paddr - physical address of input
 * @param vaddr - virtual address to which it is mapped
 * @param length - length of input.
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int HDCP2_Ion2Addr(const int ionfd, addr_s **paddr, u8 **vaddr, const int length)
{
	int ret = -1;
	int fd = 0;
#ifdef USE_MTK
	ion_user_handle_t handle = 0;
	struct ion_sys_data sys_data;

	fd = ion_open();
	if (fd < 0) {
		HDCP2_Log("Cannot open ion");
		goto err;
	}
	ret = ion_import(fd, ionfd, &handle);
	if (ret < 0) {
		HDCP2_Log("ion import fail(%d) i.e %s", errno, strerror(errno));
		goto err;
	}
	memset(&sys_data, 0x0, sizeof(sys_data));
	sys_data.sys_cmd = ION_SYS_GET_PHYS;
	sys_data.get_phys_param.handle = handle;

	ret = ion_custom_ioctl(fd, ION_CMD_SYSTEM, &sys_data);
	if (ret) {
		HDCP2_Log("ioctl[ION_ION_CUSTOM] Get phys failed!(ret %d, errono %d, i.e %s)", ret, errno, strerror(errno));
		goto err;
	}

	HDCP2_DEBUG("physical address : 0x%x(len: %d)", sys_data.get_phys_param.phy_addr, sys_data.get_phys_param.len);

	**paddr = sys_data.get_phys_param.phy_addr;

	ret = HDCP2_OK;
#else
	struct secfd_info secfd;
	if (ionfd < 0 || paddr == 0 || length <= 0) {
		HDCP2_Log("Invalid input in HDCP2_Ion2Addr(). ionfd: %d, paddr:%x, length:%d", ionfd, paddr, length);
		return HDCP2_ERR_INVALID_INPUT;
	}

	/* Open secmem device */
	fd = open(SMEM_PATH, O_RDWR);
	if (fd < 0) {
		HDCP2_Log("Open s5p-smem device error");
		goto done;
	}

	secfd.fd = ionfd;
	ret = ioctl(fd, SECMEM_IOC_GET_FD_PHYS_ADDR, &secfd);
	if (ret < 0) {
		HDCP2_Log("cannot obtain phys addr for 0x%x (err %d)", ionfd, ret);
		**paddr = 0;
	} else {
		**paddr = secfd.phys;
	}

	if (vaddr != NULL) {
		if ((*vaddr = (u8 *)mmap(0, length, PROT_READ|PROT_WRITE, MAP_SHARED, fd, secfd.phys)) == MAP_FAILED) {
			HDCP2_Log("mmap failed for %d", ionfd);
			*vaddr = 0;
			goto done;
		}
	}
	ret = HDCP2_OK;

done:
	if (fd >= 0)
		close(fd);

	return ret;
#endif /* USE_MTK */
err:
	if (fd >= 0)
		ion_close(fd);
	return ret;
}

/**
 * @fn int HDCP2_UnmapAddrs(u8 **vaddr, const int length)
 * @brief This function unmaps the virtual address vaddr given in the argument.
 * @param vaddr - virtual address to be unmapped.
 * @param length - length of memory to be unmapped.
 * @return int - HDCP2_OK
 */
int HDCP2_UnmapAddrs(u8 **vaddr, const int length)
{
	if (vaddr != NULL && *vaddr != NULL && length > 0)
		munmap(*vaddr, length);

	return HDCP2_OK;
}

/**
 * @fn int HDCP2_ConvertAddrs(const int in_ion, u8 **in_paddr, u8 **in_vaddr, const int out_ion, u8 **out_paddr, u8 **out_vaddr, const int length)
 * @brief This function gets the virtual address of the given physical address by calling function HDCP2_Ion2Addr(const int ionfd, u8 **paddr, u8 **vaddr, const int length)
 * @param in_ion - pointer to input, data at physical address
 * @param in_paddr - input data physical address
 * @param in_vaddr - input data virtual address
 * @param out_ion - pointer to output, data at virtual address
 * @param out_paddr - output data physical address
 * @param out_vaddr - input data physical address
 * @param length - length of input
 * @return int - HDCP2_OK
 */
int HDCP2_ConvertAddrs(const int in_ion, addr_s **in_paddr, u8 **in_vaddr,
			const int out_ion, u8 **out_paddr, u8 **out_vaddr, const int length)
{
	int ret = 0;

	if ((ret = HDCP2_Ion2Addr(in_ion, in_paddr, in_vaddr, length)) != 0)
		return ret;

	*out_vaddr = (u8 *)(uintptr_t)out_ion;

	return HDCP2_OK;
}

/**
 * @fn int HDCP2_GetOemFlag(HDCP2_Ctx *hdcp, u32* flag)
 * @brief This functions checks for OEM information
 * @param hdcp - pointer to HDCP context
 * @param flag - The pointer to the OEM flag populated in this function
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int HDCP2_GetOemFlag(HDCP2_Ctx *hdcp, u32* flag)
{
	int ret = 0;
	u8 res[4] = { 0 };

	if ((ret = TZ_COMMAND_T(CMD_OEM_GET_FLAG, NULL, 0, res, 4)) < 0) {
		HDCP2_Log("HDCP2_GetOemFlag Failed with %d...", ret);
		return HDCP2_ERR_GET_OEM_FLAG;
	}

	*flag = *(u32*)res;
	return 0;
}

/**
 * @fn int HDCP2_TZ_Open(HDCP2_Ctx *hdcp, const int entity)
 * @brief This function opens session with the Trustlet by acquiring the required resources
 * @param hdcp - pointer to HDCP context
 * @param entity - specifies if the devices is a transmitter or receiver or a repeater.
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error.
 */
int HDCP2_TZ_Open(HDCP2_Ctx *hdcp, const int entity)
{
	mcResult_t ret = HDCP2_OK;
	mcUuid_t *uuid = 0;

	if (!hdcp)
		return HDCP2_ERR_INVALID_INPUT;

	if ((s_map_buffer = (uint8_t *) malloc(MAP_BUFFER_SIZE)) == NULL) {
		HDCP2_Log("WSM allocation failed");
		return HDCP2_ERR_HW_INIT;
	}

	uuid = (mcUuid_t *)((entity == HDCP2_TRANSMITTER) ? &UUID_TRANSMITTER : &UUID_RECEIVER);
	//HDCP2_LogHex("Trustlet Connector Start", (u8 *) uuid, sizeof(mcUuid_t));

	do {
		// Close if previous session was not closed.
		if (s_sessionHandle.sessionId) {
			HDCP2_Log("TZ is already opened");
			HDCP2_TZ_Close(hdcp);
			ret = HDCP2_ERR_HW_INIT;
			break;
		}

		// Step1 : Open the MobiCore Drivers
		ret = mcOpenDevice(DEVICE_ID);
		if (MC_DRV_OK != ret) {
			HDCP2_Log("MobiCore opening device: %d", ret);
		}

		// Get the Mobicore Version
		memset(&hdcp->versionInfo, 0, sizeof(hdcp->versionInfo));
		ret = mcGetMobiCoreVersion(MC_DEVICE_ID_DEFAULT, &hdcp->versionInfo);
		if (MC_DRV_OK != ret) {
			HDCP2_Log("mcGetMobiCoreVersion failed %d.", ret);
			mcCloseDevice(DEVICE_ID);
			break;
		}

		// step2 : Allocate WSM Buffer for the TCI
		ret = mcMallocWsm(DEVICE_ID, 0, sizeof(tci_t), (uint8_t **) &hdcp->tci, 0);
		if (MC_DRV_OK != ret) {
			HDCP2_Log("Allocation of TCI WSM failed : %d", ret);
			break;
		}

		// step3 : Open session with the Trustlet
		//       : Clear the session handle
		bzero(&hdcp->sessionHandle, sizeof(hdcp->sessionHandle));

		//       : The device ID(default device is used)
		hdcp->sessionHandle.deviceId = DEVICE_ID;
		ret = mcOpenSession(&hdcp->sessionHandle, (const mcUuid_t *) uuid, (uint8_t *) hdcp->tci,
					(uint32_t) sizeof(tci_t));
		if (MC_DRV_OK != ret) {
			HDCP2_Log("Open session failed: %d", ret);
			ret = HDCP2_ERR_HW_INIT;
			break;
		}

		// Set Opened
		hdcp->tlc_opened = 1;
		memcpy(&s_sessionHandle, &hdcp->sessionHandle, sizeof(mcSessionHandle_t));

		// Map additional memory to Trustlet (session)
		ret = mcMap(&hdcp->sessionHandle, s_map_buffer, MAP_BUFFER_SIZE, &hdcp->mapInfo);
		if (MC_DRV_OK != ret) {
			HDCP2_Log("Error during memory registration (input=%x, inlen=%d, ret=%d)\n",
					s_map_buffer, MAP_BUFFER_SIZE, ret);
			break;
		}

		ret = HDCP2_OK;
	} while (false);

	if (entity != HDCP2_FACTORY && ret == HDCP2_OK) {
		if ((ret=HDCP2_HW_Init(hdcp)) != HDCP2_OK) {
			HDCP2_Log("Cannot init h/w");
			HDCP2_TZ_Close(hdcp);
			return ret;
		}
	}

	return ret;
}

/**
 * @fn void HDCP2_TZ_Close(HDCP2_Ctx *hdcp)
 * @brief It closes the connection and releases the resources
 * @param hdcp - pointer to HDCP context
 * @return void
 */
void HDCP2_TZ_Close(HDCP2_Ctx *hdcp)
{
	int i = 0;
	mcResult_t ret;

	if (!hdcp) {
		HDCP2_Log("TZ Close: null context...");
		return;
	}

	if (!s_sessionHandle.sessionId) {
		HDCP2_Log("TZ is not opened...");
		return;
	}

	/*
	 * HW_Close
	 */
	if (hdcp->entity != HDCP2_FACTORY)
		HDCP2_HW_Close(hdcp);

	do {
		// step6 : Unregister memory from MobiCore
		for (i=0; i<5; i++) {
			if ((ret = mcUnmap(&hdcp->sessionHandle, s_map_buffer, &hdcp->mapInfo)) == MC_DRV_OK)
				break;
			usleep(1000);
		}
		if (MC_DRV_OK != ret) {
			HDCP2_Log("Error during memory unmapping");
		}

		// step7 : Close session with the Trustlet
		ret = mcCloseSession(&s_sessionHandle);
		if (MC_DRV_OK != ret) {
			HDCP2_Log("Closing session failed : %d", ret);
		}

		// Clear static variable
		memset(&s_sessionHandle, 0, sizeof(mcSessionHandle_t));
		hdcp->tlc_opened = 0;

		// step8 : Close the MobiCore device
		for (i=0; i<10; i++) {
			if ((ret = mcCloseDevice(DEVICE_ID)) == MC_DRV_OK)
				break;
			usleep(1000);
		}

		if (MC_DRV_OK != ret) {
			HDCP2_Log("Closing Mobicore device failed: %d", ret);
			break;
		}
	} while (false);

	if (s_map_buffer) {
		free(s_map_buffer);
		s_map_buffer = 0;
	}
}

/**
 * @fn int HDCP2_TZ_Reset(HDCP2_Ctx *hdcp)
 * @brief This function resets the trustzone
 * @param hdcp - pointer to HDCP context
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int HDCP2_TZ_Reset(HDCP2_Ctx *hdcp)
{
	int ret = 0;
	if (!hdcp) {
		HDCP2_Log("TZ Close: null context...");
		return HDCP2_ERR_HW_RESET;
	}

	if ((ret = TZ_COMMAND_R(CMD_TRUSTZONE_RESET, NULL, 0, NULL, 0)) < 0) {
		HDCP2_Log("HDCP2_TZ_Reset Failed with %d...", ret);
		return HDCP2_ERR_HW_RESET;
	}
	return 0;
}

/**
 *
 * @fn int TLC_COMMAND(HDCP2_Ctx *hdcp, const uint8_t command, uint8_t *request, uint32_t req_size, uint8_t *response, uint32_t res_size);
 * @brief This function transfers the control to the secure world when using mobicore
 * @param hdcp - pointer to HDCP context
 * @param command - specifies the command to be executed.
 * @param request - This is the pointer to the request buffer which holds data that has to be worked upon
 * @param req_size - specifies the size of request
 * @param response - This is the pointer to the response buffer that is generally populated in SWD and sent back to NWD
 * @param res_size - specifies the size of response
 * @return int
 */
int TLC_COMMAND(HDCP2_Ctx *hdcp, const uint8_t command, uint8_t *request, uint32_t req_size,
			uint8_t *response, uint32_t res_size)
{
	static int locked = 0;
	int retcode = HDCP2_ERR;
	mcResult_t ret;
	u8 dummy[HDCP2_BUFFER_LEN] = {0};
	u8 *input = NULL;
	u8 *output = NULL;
	int inlen = 0;
	CIP_DATA_INFO *data = NULL;

	if (locked && ! (command == CMD_TRUSTZONE_RESET+CMD_RECEIVER)) {
		HDCP2_Log("TL Locked, Drop COMMAND %d", command);
		return HDCP2_OK;
	}

	if (!hdcp || !s_map_buffer || req_size > MAP_BUFFER_SIZE) {
		HDCP2_Log("Invalid parameter in TLC_COMMAND()");
		retcode = HDCP2_ERR_INVALID_INPUT;
		goto err;
	}

	if (!hdcp->tlc_opened) {
		HDCP2_Log("Cannot process TLC_COMMAND : tlc is not opened");
		retcode = HDCP2_ERR;
		goto err;
	}

	input = (request) ? request : dummy;
	output = (response) ? response : dummy;
	inlen = (request) ? req_size : HDCP2_BUFFER_LEN;

	HDCP2_DEBUG("TLC_COMMAND(s): command = %d, input = 0x%x, inlen = %d, output = 0x%x",
			command, input, inlen, output);

	locked = 1;

	do {
		// Map additional memory to Trustlet (session)		
		if (command != (CMD_TRUSTZONE_INIT + CMD_RECEIVER)
			&& command != (CMD_TRUSTZONE_INIT + CMD_TRANSMITTER))
			memcpy(s_map_buffer, input, inlen);

		// step5 : Call the HDCP Trustlet
		do {
			// step 5.1 : Prepare command message in TCI
			if (command == (CMD_TRUSTZONE_INIT + CMD_RECEIVER)
				|| command == (CMD_TRUSTZONE_INIT + CMD_TRANSMITTER)) {
				hdcp->tci->message.request_static.header.commandId = command;
				memcpy(hdcp->tci->message.request_static.buffer, input, inlen);
				hdcp->tci->message.request_static.offset = 0;
				hdcp->tci->message.request_static.length = inlen;
			} else {
				hdcp->tci->message.request.header.commandId = command;
				hdcp->tci->message.request.buffer = (uint8_t *)(uintptr_t)hdcp->mapInfo.sVirtualAddr;
				hdcp->tci->message.request.offset = 0;
				hdcp->tci->message.request.length = inlen;
			}

			// step 5.2 : signal the trustlet
			ret = mcNotify(&hdcp->sessionHandle);
			if (MC_DRV_OK != ret) {
				HDCP2_Log("Notify failed : %d", ret);
				break;
			}

			// step 5.3 : Wait for the Trustlet response
			ret = mcWaitNotification(&hdcp->sessionHandle, -1);
			if (MC_DRV_OK != ret) {
				HDCP2_Log("Wait for response failed: %d", ret);
				break;
			}

			// step 5.4 : Verify the Trustlet response
			if ((RSP_ID(command) != hdcp->tci->message.responseHeader.responseId)) {
				HDCP2_Log(
						"TZ did not send a valid response! responseId: %d",
						hdcp->tci->message.responseHeader.responseId);
				break;
			}

			// step 5.5 : Check the Trustlet return code
			if (hdcp->tci->message.responseHeader.returnCode != HDCP2_OK) {
				retcode = hdcp->tci->message.responseHeader.returnCode;
				if (command != CMD_CIP_DEC_VIR + CMD_RECEIVER)
					HDCP2_Log("TZ Return code: %d (0x%08x)",
							hdcp->tci->message.responseHeader.returnCode,
							((hdcp->tci->message.responseHeader.returnCode-HDCP2_ERR_TRUSTZONE_BASE)*(-1)));
				break;
			}

			if (command != CMD_CIP_ENC_DATA+CMD_TRANSMITTER &&
				command != CMD_CIP_DEC_DATA+CMD_RECEIVER &&
				command != CMD_CIP_DEC_VIR+CMD_RECEIVER &&
				command != CMD_CIP_SPS_PPS_DATA+CMD_RECEIVER &&
				command != CMD_CIP_DEC_AUDIO+CMD_RECEIVER) {
				if (hdcp->tci->message.response.length > 0) {
					memcpy(output, hdcp->tci->message.response.buffer,
							hdcp->tci->message.response.length);
				}
			}
			if (command == CMD_CIP_DEC_VIR+CMD_RECEIVER) {
				memcpy(input, s_map_buffer, inlen);
			}
			retcode = hdcp->tci->message.response.length;
		} while (false);
	} while (false);

	HDCP2_DEBUG("TLC_COMMAND(e): cmd = %d, ret = %d, res_size = %d",
			command, retcode, hdcp->tci->message.response.length);

err:
	locked = 0;
	return retcode;
}

/**
 * @fn int HDCP2_Encrypt_PV(HDCP2_Ctx *hdcp, u32 str_ctr, u64 in_ctr, u8 *input, int inlen, u8 *output)
 * @brief This function encrypts the data stream
 * @param hdcp - pointer to HDCP context
 * @param str_ctr - It is 32 bit stream counter. HDCP transmitter assigns a distinct stream counter for each Packetized Elementary Stream (PES).
 * @param in_ctr - It is 64 bit input counter. It is initialized to zero after SKE and must not be reset at any other time.
 * @param input - pointer to the data to be encrypted.
 * @param inlen - length of input data.
 * @param output - pointer to the output data, that will be populated in this function.
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int index_data = 0;
int HDCP2_Encrypt_PV(HDCP2_Ctx *hdcp, u32 str_ctr, u64 in_ctr, addr_s *input,
			int inlen, u8 *output)
{
	int ret = 0;
	int remain = inlen;
	int enclen = 0;
	addr_s i = 0;
	CIP_DATA_INFO data = { 0 };
	u8 *out = output;

	if (inlen < 0 || !hdcp || !input || !output) {
		HDCP2_Log("HDCP2_ERR_INVALID_INPUT (inlen = %d)", inlen);
		return HDCP2_ERR_INVALID_INPUT;
	}
	if (HDCP2_IsClosed(hdcp))
		return HDCP2_ERR_CLOSED;

#ifdef USE_64BIT_ADDR
	HDCP2_DEBUG("input=0x%llx, output=0x%.8x", *input, output);
#else
	HDCP2_DEBUG("input=0x%.8x, output=0x%.8x", *input, output);
#endif /* USE_64BIT_ADDR */

	while (remain > 0) {
		enclen = (remain > MAX_ENCRYPT_BUFFER) ? MAX_ENCRYPT_BUFFER : remain;
		data.str_ctr = (u32) str_ctr;
		data.inp_ctr = (u64) (in_ctr + i * MAX_ENCRYPT_BUFFER_BL);
		HDCP2_Log("inlen=%d, remain=%d, enclen=%d", inlen, remain, enclen);

		data.input = *input + i * MAX_ENCRYPT_BUFFER;
		data.output = hdcp->output_physaddr;
		data.length = enclen;

		ret = TZ_COMMAND_T(CMD_CIP_ENC_DATA, (u8 *)&data, sizeof(CIP_DATA_INFO), out, enclen);
		if (ret < 0) {
			HDCP2_Log("Ignore Frame, l=%d", data.length);
			break;
		}

		/* copy encrypted buffer to wfd ouput buffer */
#ifdef USE_MTK
		memcpy((u8 *) (output + i * MAX_ENCRYPT_BUFFER), s_map_buffer, enclen);
#else
		memcpy((u8 *) (output + i * MAX_ENCRYPT_BUFFER), ((u8*)hdcp->output_virtaddr), enclen);
#endif /* USE_MTK */

		i++;
		remain -= enclen;
	}

	return ret;
}

/**
 * @fn int HDCP2_Encrypt_VV(HDCP2_Ctx *hdcp, u32 str_ctr, u64 in_ctr, u8 *input, int inlen, u8 *output)
 * @brief This function encrypts the data.
 * @param hdcp - pointer to HDCP context
 * @param str_ctr - It is 32 bit stream counter. HDCP transmitter assigns a distinct stream counter for each Packetized Elementary Stream (PES).
 * @param in_ctr - It is 64 bit input counter. It is initialized to zero after SKE and must not be reset at any other time.
 * @param input - pointer to the data to be encrypted.
 * @param inlen - length of input data.
 * @param output - pointer to the output data, that will be populated in this function.
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
#ifdef USE_MTK
int HDCP2_Encrypt_VV(HDCP2_Ctx *hdcp, u32 str_ctr, u64 in_ctr, u8 *input,
			int inlen, u8 *output, int type)
{
	int ret = 0;
	int remain = inlen;
	int i = 0;
	int enclen = 0;
	int in_offset = 0;
	CIP_DATA_INFO data = { 0 };
	u8 *out = output;
	data.type = type;

	if (!hdcp || !hdcp->input_virtaddr || !input ) {
		HDCP2_Log("Invalid parameter in HDCP2_Encrypt_VV()");
		return HDCP2_ERR_INVALID_INPUT;
	}

	if (inlen < 0) {
		HDCP2_Log("HDCP2_Encrypt_VV Invalid Input Length (inlen = %d)", inlen);
		return HDCP2_ERR_INVALID_INPUT;
	}

	while (remain > 0) {
		enclen = (remain > MAX_ENCRYPT_BUFFER) ? MAX_ENCRYPT_BUFFER : remain;
		memcpy(hdcp->input_virtaddr, input + in_offset, enclen);

		data.str_ctr = (u32) str_ctr;
		data.inp_ctr = (u64) (in_ctr + i * MAX_ENCRYPT_BUFFER_BL);

		HDCP2_Log("input=%d, remain=%d, enclen=%d", inlen, remain, enclen);

		data.input = hdcp->input_physaddr;
		data.output = hdcp->output_physaddr;
		data.length = enclen;

		ret = TZ_COMMAND_T(CMD_CIP_ENC_DATA, (u8 *)&data, sizeof(CIP_DATA_INFO), out, enclen);

		if (ret < 0) {
			HDCP2_Log("Ignore Frame, l=%d", data.length);
			break;
		}

		/* copy encrypted buffer to wfd ouput buffer */
		memcpy((u8 *) (output + i * MAX_ENCRYPT_BUFFER), s_map_buffer, enclen);
		i++;
		remain -= enclen;
		in_offset += enclen;
	}

	return ret;
}
#else
int HDCP2_Encrypt_VV(HDCP2_Ctx *hdcp, u32 str_ctr, u64 in_ctr, u8 *input,
			int inlen, u8 *output)
{
	int ret = 0;
	int remain = inlen;
	int i = 0;
	int enclen = 0;
	int in_offset = 0;
	CIP_DATA_INFO data = { 0 };
	u8 *out = output;

	if (!hdcp || !hdcp->input_virtaddr || !input ) {
		HDCP2_Log("Invalid parameter in HDCP2_Encrypt_VV()");
		return HDCP2_ERR_INVALID_INPUT;
	}

	if (inlen < 0) {
		HDCP2_Log("HDCP2_Encrypt_VV Invalid Input Length (inlen = %d)", inlen);
		return HDCP2_ERR_INVALID_INPUT;
	}

	while (remain > 0) {
		enclen = (remain > MAX_ENCRYPT_BUFFER) ? MAX_ENCRYPT_BUFFER : remain;
		memcpy(hdcp->input_virtaddr, input + in_offset, enclen);

		data.str_ctr = (u32) str_ctr;
		data.inp_ctr = (u64) (in_ctr + i * MAX_ENCRYPT_BUFFER_BL);

		HDCP2_Log("input=%d, remain=%d, enclen=%d", inlen, remain, enclen);

		data.input = hdcp->input_physaddr;
		data.output = hdcp->output_physaddr;
		data.length = enclen;

		ret = TZ_COMMAND_T(CMD_CIP_ENC_DATA, (u8 *)&data, sizeof(CIP_DATA_INFO), out, enclen);

		if (ret < 0) {
			HDCP2_Log("Ignore Frame, l=%d", data.length);
			break;
		}

		/* copy encrypted buffer to wfd ouput buffer */
		memcpy((u8 *) (output + i * MAX_ENCRYPT_BUFFER), hdcp->output_virtaddr, enclen);
		i++;
		remain -= enclen;
		in_offset += enclen;
	}

	return ret;
}
#endif /* USE_MTK */

/**
 * @fn int HDCP2_Encrypt_PP(HDCP2_Ctx *hdcp, u32 str_ctr, u64 in_ctr, u8 *input, int inlen, u8 *output)
 * @brief This function encrypts the data stream
 * @param hdcp - pointer to HDCP context
 * @param str_ctr - It is 32 bit stream counter. HDCP transmitter assigns a distinct stream counter for each Packetized Elementary Stream (PES).
 * @param in_ctr - It is 64 bit input counter. It is initialized to zero after SKE and must not be reset at any other time.
 * @param input - pointer to the data to be encrypted.
 * @param inlen - length of input data.
 * @param output - pointer to the output data, that will be populated in this function.
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int HDCP2_Encrypt_PP(HDCP2_Ctx *hdcp, u32 str_ctr, u64 in_ctr, u8 *input,
			int inlen, u8 *output)
{
	int ret = 0;
	int remain = inlen;
	int i = 0;
	int enclen = 0;
	CIP_DATA_INFO data = { 0 };
	u8 *out = output;

	if (inlen < 0 || !hdcp || !input || !output || inlen > MAX_ENCRYPT_BUFFER) {
		HDCP2_Log("HDCP2_ERR_INVALID_INPUT (inlen = %d)", inlen);
		return HDCP2_OK; // If it returns Error, the session will be disconnected.
	}
	if (HDCP2_IsClosed(hdcp))
		return HDCP2_ERR_CLOSED;

	HDCP2_DEBUG("input=0x%.8x, output=0x%.8x", input, output);

	while (remain > 0) {
		enclen = (remain > MAX_ENCRYPT_BUFFER) ? MAX_ENCRYPT_BUFFER : remain;
		data.str_ctr = (u32) str_ctr;
		data.inp_ctr = (u64) (in_ctr + i * MAX_ENCRYPT_BUFFER_BL);

		HDCP2_DEBUG("input=%d, remain=%d, enclen=%d", inlen, remain, enclen);

		data.input = *input + i * MAX_ENCRYPT_BUFFER;
		data.output = *output + i * MAX_ENCRYPT_BUFFER;
		data.length = enclen;

		ret = TZ_COMMAND_T(CMD_CIP_ENC_DATA, (u8 *)&data, sizeof(CIP_DATA_INFO), out, enclen);

		if (ret < 0) {
			HDCP2_Log("Ignore Frame, l=%d", data.length);
			break;
		}

		i++;
		remain -= enclen;
	}

	return ret;
}

/**
 * @fn int HDCP2_Decrypt_Ex_IsKeyFrame(HDCP2_Ctx *hdcp, u32 str_ctr, u64 in_str, u8 *input, int inlen, u8 *output, u32 dec_type, int codec_type)
 * @brief Checks whether the received frame is a key frame
 * @param hdcp - pointer to HDCP context
 * @param str_ctr - It is 32 bit stream counter. HDCP transmitter assigns a distinct stream counter for each Packetized Elementary Stream (PES).
 * @param in_str - It is 64 bit input counter. It is initialized to zero after SKE and must not be reset at any other time.
 * @param input - pointer to the data to be decrypted.
 * @param inlen - length of input data.
 * @param output - pointer to the output data, that will be populated in this function.
 * @param dec_type -  Decryption type
 * @param codec_type - Codec type
 * @return int - returns input length in case of success else returns error values in case of failures.
 */
int HDCP2_Decrypt_Ex_IsKeyFrame(HDCP2_Ctx *hdcp, u32 str_ctr, u64 in_str,
			u8 *input, int inlen, u8 *output, u32 dec_type, int codec_type)
{
	int ret = 0;
	CIP_DATA_INFO_VIR data;

	if (!hdcp || inlen > sizeof(data.input))
		return HDCP2_ERR_INVALID_INPUT;

	memset(&data, 0, sizeof(CIP_DATA_INFO_VIR));
	memcpy(data.input, input, inlen);
	data.str_ctr = (u32)str_ctr;
	data.inp_ctr = (u64)in_str;
	data.length = inlen;
	data.dec_type = dec_type;
	data.codec_type = codec_type;

	if ((ret = TZ_COMMAND_R(CMD_CIP_DEC_VIR, (u8 *)&data, sizeof(CIP_DATA_INFO_VIR), NULL, 0)) < 0) {
		HDCP2_Log("CMD_CIP_DEC_VIR Fail");
	}

	return ret;
}

/**
 * @fn int HDCP2_Decrypt_Ex_Audio(HDCP2_Ctx *hdcp, u32 str_ctr, u64 in_str, u8 *input, int inlen, u8 *output, u32 dec_type, int codec_type)
 * @brief Decrypts the audio data stream
 * @param hdcp - pointer to HDCP context
 * @param str_ctr - It is 32 bit stream counter. HDCP transmitter assigns a distinct stream counter for each Packetized Elementary Stream (PES).
 * @param in_str - It is 64 bit input counter. It is initialized to zero after SKE and must not be reset at any other time.
 * @param input - pointer to the data to be decrypted.
 * @param inlen - length of input data.
 * @param output - pointer to the output data, that will be populated in this function.
 * @param dec_type -  Decryption type
 * @param codec_type - Codec type
 * @return int - returns input length in case of success else returns error values in case of failures.
 */
int HDCP2_Decrypt_Ex_Audio(HDCP2_Ctx *hdcp, u32 str_ctr, u64 in_str, u8 *input,
			int inlen, u8 *output, u32 dec_type, int codec_type)
{
	int ret = 0;
	CIP_DATA_INFO_RX data;

	if(!hdcp || !input || !output)
		return HDCP2_ERR_INVALID_INPUT;

	if (inlen > MAX_ENCRYPT_BUFFER)
		return HDCP2_ERR_INVALID_INPUT;

	memset(&data, 0, sizeof(CIP_DATA_INFO_RX));

	memcpy(hdcp->input_virtaddr, input, inlen);
	data.input = hdcp->input_physaddr;
	data.output = hdcp->output_physaddr;
	data.str_ctr = (u32)str_ctr;
	data.inp_ctr = (u64)in_str;
	data.length = inlen;
	data.dec_type = dec_type;
	data.codec_type = codec_type;

	if ((ret = TZ_COMMAND_R(CMD_CIP_DEC_AUDIO, (u8 *)&data, sizeof(CIP_DATA_INFO_RX), NULL, 0)) < 0) {
		HDCP2_Log("CMD_CIP_DEC_AUDIO Fail");
	}
	if (dec_type == DEC_TYPE_AUDIO) {
		// If input is video, then we should return error
		if ((hdcp->output_virtaddr[0] == 0x00 && hdcp->output_virtaddr[1] == 0x00 && hdcp->output_virtaddr[2] == 0x01)
			|| (hdcp->output_virtaddr[0] == 0x00 && hdcp->output_virtaddr[1] == 0x00
			&& hdcp->output_virtaddr[2] == 0x00 && hdcp->output_virtaddr[3] == 0x01)) {
			ret = HDCP2_ERR_AUDIO;
		} else {
			memcpy(output, hdcp->output_virtaddr, inlen);
			ret = inlen;
		}
	}

	return ret;
}

/**
 * @fn int HDCP2_Decrypt_Ex_VP(HDCP2_Ctx *hdcp, u32 str_ctr, u64 in_str, u8 *input, int inlen, u8 *output, u32 dec_type, int codec_type)
 * @brief This function decrypts data stream in unsecure world.
 * @param hdcp - pointer to HDCP context
 * @param str_ctr - It is 32 bit stream counter. HDCP transmitter assigns a distinct stream counter for each Packetized Elementary Stream (PES).
 * @param in_str - It is 64 bit input counter. It is initialized to zero after SKE and must not be reset at any other time.
 * @param input - pointer to the data to be decrypted.
 * @param inlen - length of input data.
 * @param output - pointer to the output data, that will be populated in this function.
 * @param dec_type -  decrytion type
 * @param codec_type - codec type
 * @return int - returns input length in case of success else returns error values in case of failures.
 */
int HDCP2_Decrypt_Ex_VP(HDCP2_Ctx *hdcp, u32 str_ctr, u64 in_str, u8 *input,
			int inlen, addr_s *output, u32 dec_type, int codec_type)
{
	int ret = 0;
	int remain = inlen;
	int declen = 0;
	addr_s i = 0;
	CIP_DATA_INFO_RX data;
	addr_s *out = output;

	if (!hdcp || inlen < 0 || !input || !output || inlen > MAX_ENCRYPT_BUFFER) {
		HDCP2_Log("HDCP2_ERR_INVALID_INPUT (inlen = %d)", inlen);
		return HDCP2_ERR_INVALID_INPUT;
	}

	if (!hdcp->opened) {
		HDCP2_Log("TZ is closed...");
		return HDCP2_ERR_INVALID_INPUT;
	}

	HDCP2_DEBUG("\n<<<input virt=0x%.8x, input phys=0x%.8x, output=0x%.8x, inlen=%d, str_ctr=%ld, in_str=%ld",
			input, hdcp->input_physaddr, output, inlen, str_ctr, in_str);

	while (remain > 0) {
		declen = (remain > MAX_ENCRYPT_BUFFER) ? MAX_ENCRYPT_BUFFER : remain;
#ifdef USE_MTK
		memcpy(s_map_buffer + sizeof(CIP_DATA_INFO_RX), (u8 *)(input + i*MAX_ENCRYPT_BUFFER), declen);
#else
		memcpy(hdcp->input_virtaddr, (u8 *)(input + i*MAX_ENCRYPT_BUFFER), declen);
#endif /* USE_MTK */

		data.input = hdcp->input_physaddr;
		data.output = *output + i*MAX_ENCRYPT_BUFFER;
		data.str_ctr = (u32)str_ctr;
		data.inp_ctr = (u64)(in_str + i*MAX_ENCRYPT_BUFFER_BL);
		data.length = declen;
		data.dec_type = dec_type;
		data.codec_type = codec_type;

		HDCP2_DEBUG("inlen=%d, remain=%d, declen=%d", inlen, remain, declen);

		if ((ret = TZ_COMMAND_R(CMD_CIP_DEC_DATA, (u8 *)&data, sizeof(CIP_DATA_INFO_RX), (u8 *)out, declen)) < 0) {
			HDCP2_Log("Ignore frame..%d...", inlen);
			break;
		}

		i++;
		remain -= declen;
	}

	HDCP2_DEBUG("ret=%d, inlen=%d>>>\n", ret, inlen);

	if (ret < 0)
		return ret;
	else
		return inlen;
}

#ifdef USE_MTK
int HDCP2_Decrypt_ION(HDCP2_Ctx *hdcp, u32 str_ctr, u64 in_str, u8 *input, int inlen, u32 ion_fd)
{
	if (inlen < 0 || !hdcp || !input || inlen > MAX_ENCRYPT_BUFFER) {
		HDCP2_Log("HDCP2_ERR_INVALID_INPUT (inlen = %d)", inlen);
		return HDCP2_ERR_INVALID_INPUT;
	}

	addr_s out_pa = 0;
	addr_s *output_pa = &out_pa;
	if (HDCP2_Ion2Addr((int)ion_fd, &output_pa, NULL, inlen) != 0) {
		HDCP2_Log("Cannot convert ION handle");
		return HDCP2_ERR_INVALID_INPUT;
	}

	HDCP2_DEBUG("HDCP2_DecryptVideo: ionfd=%d, out_pa=%x, inlen=%d", (int)ion_fd, output_pa, inlen);

	return HDCP2_Decrypt_Ex_VP(hdcp, str_ctr, in_str, input, inlen, output_pa, DEC_TYPE_VIDEO, 0);
}
#endif
