/**
 * 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.
 *
 */
 /*
 * hdcp2_qualcomm.c
 *
 *  Created on: 2012. 05. 24.
 */
 /**
 * @file hdcp2_qualcomm.cpp
 * @author
 * @date
 * @brief This file contains the functions to establish the connection between hdcp transmitter and receiver.
 */
#include <fcntl.h>
#include <sys/mman.h>
#include <ion.h>
#include <msm_ion.h>
#include <errno.h>

#ifdef CONFIG_ION_TYPE_MODERN
#include <ion_4.12.h>
#endif /* CONFIG_ION_TYPE_MODERN */

#ifdef CONFIG_HDCP_64BIT
#ifdef CONFIG_MSM8998
#include "QSEEComAPI_8998.h"
#else
#include "QSEEComAPI_8996.h"
#endif /* CONFIG_MSM8998 */
#else
#include "QSEEComAPI_8974.h"
#endif /* CONFIG_HDCP_64BIT */

#include "hdcp2.h"
#include "hdcp2_qualcomm.h"

/**
 * @def TRUSTLET_MAIN_STACK_SIZE
 * This macro is assigned a value of 0x00010000 to be maintained everywhere.
 */
#define TRUSTLET_MAIN_STACK_SIZE	0x00010000

#ifdef CONFIG_USE_VENDOR_PATH
#define IMAGE_PATH	"/vendor/firmware_mnt/image"
#else
#define IMAGE_PATH	"/firmware/image"
#endif /* CONFIG_USE_VENDOR_PATH */

/* Variables for ION */
/**
 * @struct REPEATERAUTH_SEND_RECEIVER_ID_LIST20
 *
 * This structure contains information regarding ion object.
 */
typedef struct ion_obj_t {
	uint8_t *ion_obj_virt_addr;
	int32_t ion_obj_fd;
	struct ion_fd_data ion_obj_fd_data;
} ION_OBJECT;

ION_OBJECT gInIONObject;
ION_OBJECT gOutIONObject;
ION_OBJECT gInIOnObject_Recv;

struct QSEECom_handle *l_QSEEComHandle = NULL;
u8 *dec_mem = NULL;

/* Function for release ION */
/**
 * @fn static int free_swd_ion(ION_OBJECT *ionObject)
 * @brief This function releases ION
 * @param ionObject - pointer to ion object
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
static int free_swd_ion(ION_OBJECT *ionObject)
{
	int ret = 0;

	/* free ion memory(munmap) */
	if (ionObject->ion_obj_virt_addr != NULL) {
		if (munmap(ionObject->ion_obj_virt_addr, MAX_VIDEO_ENCRYPT_BUFFER)) {
			HDCP2_Log("munmap fail");
			ret++;
		}
	} else {
		ionObject->ion_obj_virt_addr = NULL;
	}

	/* close ION fd_data.fd */
	if (ionObject->ion_obj_fd_data.fd > 0)
		close(ionObject->ion_obj_fd_data.fd);

	/* ION_IOC_FREE */
	if (ionObject->ion_obj_fd_data.handle != NULL) {
		if (ioctl(ionObject->ion_obj_fd, ION_IOC_FREE, &ionObject->ion_obj_fd_data)) {
			HDCP2_Log("ION_IOC_FREE fail");
			ret++;
		}
	} else {
		ionObject->ion_obj_fd_data.handle = NULL;
	}

	/* close ion device */
	if (ionObject->ion_obj_fd > 0)
		close(ionObject->ion_obj_fd);

	return ret;
}

/* Function for allocation ION */
/**
 * @fn static int allocate_swd_ion(ION_OBJECT *ionObject)
 * @brief This function allocates the ION
 * @param ionObject - pointer to ion object
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 *
 */
static int allocate_swd_ion(ION_OBJECT *ionObject)
{
	int ret = 0;

#ifdef CONFIG_ION_TYPE_MODERN
	struct ion_new_allocation_data alloc_data = {0, };
#else
	struct ion_allocation_data alloc_data = {0, };
#endif /* CONFIG_ION_TYPE_MODERN */

	//HDCP2_DEBUG("%s", __func__);

	/* open ion device */
	ionObject->ion_obj_fd = open("/dev/ion", O_RDONLY);
	if (ionObject->ion_obj_fd == -1) {
		HDCP2_Log("ion open fail");
		return -1;
	}

	/* ION_IOC_ALLOC */
	alloc_data.len = MAX_VIDEO_ENCRYPT_BUFFER;
	alloc_data.flags = 0;
	alloc_data.heap_id_mask = ION_HEAP(ION_QSECOM_HEAP_ID);

#ifdef CONFIG_ION_TYPE_MODERN
	HDCP2_DEBUG("%s, allocate_ion_memory_new", __func__);
	ret = ioctl(ionObject->ion_obj_fd, ION_IOC_NEW_ALLOC, &alloc_data);
	if (ret) {
		HDCP2_Log("ION alloc fail");
		ret = -1;
		goto error;
	}
	ionObject->ion_obj_fd_data.fd = alloc_data.fd;
#else
	alloc_data.align = 4096;
	HDCP2_DEBUG("%s, allocate_ion_memory_legacy", __func__);
	ret = ioctl(ionObject->ion_obj_fd, ION_IOC_ALLOC, &alloc_data);
	if (ret) {
		ret = -1;
		goto error;
	}

	/* get ION handle */
	ionObject->ion_obj_fd_data.handle = alloc_data.handle;

	/* get ION fd_data.fd */
	ret = ioctl(ionObject->ion_obj_fd, ION_IOC_MAP, &ionObject->ion_obj_fd_data);
	if (ret) {
		HDCP2_Log("ION_IOC_MAP fail");
		ret = -1;
		goto error;
	}
#endif /* CONFIG_ION_TYPE_MODERN */

	/* get virtual address(mmap) */
	ionObject->ion_obj_virt_addr = (unsigned char *)mmap(NULL, alloc_data.len, PROT_READ | PROT_WRITE,
												MAP_SHARED, ionObject->ion_obj_fd_data.fd, 0);

	if (ionObject->ion_obj_virt_addr == MAP_FAILED) {
		HDCP2_Log("mmap fail");
		ret = -1;
		goto error;
	}

	return ret;

error:
	free_swd_ion(ionObject);
	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)
{
	return HDCP2_OK;
}

/**
 * @fn int HDCP2_Decrypt_Ex(HDCP2_Ctx *hdcp, u32 str_ctr, u64 in_ctr, u8 *input, int inlen, u8 *output, u32 dec_type, int codec_type)
 * @brief This function decypts the data when qualcomm chipset is used.
 * @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 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 length of decrypted data in case of success, else error code corresponding to the error
 */
int HDCP2_Decrypt_Ex(HDCP2_Ctx *hdcp, u32 str_ctr, u64 in_ctr, u8 *input, int inlen, u8 *output, u32 dec_type, int codec_type)
{
	int ret = 0;
	int i = 0;
	CIP_DATA_INFO_RX 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_ERR_INVALID_INPUT;
	}

	data.str_ctr = (u32)str_ctr;
	data.inp_ctr = (u64)in_ctr;

	HDCP2_DEBUG("HDCP2_Decrypt_Ex: input=%d, in_ctr=%d, str_ctr=%d", inlen, in_ctr, str_ctr);

	data.input = (u8 *)(input);
	data.output = (u8 *)(output);
	data.length = inlen;
	data.dec_type = dec_type;
	data.codec_type = codec_type;

	if ((ret = TZ_COMMAND_R(CMD_CIP_DEC_DATA, (u8 *)&data, sizeof(CIP_DATA_INFO_RX), data.output, data.length)) < 0) {
		HDCP2_Log("Re-Decrypt Frame");
		if ((ret = TZ_COMMAND_R(CMD_CIP_DEC_DATA, (u8 *)&data, sizeof(CIP_DATA_INFO_RX), data.output, data.length)) < 0) {
			HDCP2_Log("Ignore Frame, l=%d", data.length);
		}

		ret = HDCP2_OK;
	}

	return ret;
}

/**
 * @fn int HDCP2_Decrypt_ION(HDCP2_Ctx *hdcp, u32 str_ctr, u64 in_str, u8 *input, int inlen, u32 ion_fd)
 * @brief This function decrypts the data stream in secure memory when using qualcomm chipset
 * @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 ion_fd- file descriptor corresponding to secure memory
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int HDCP2_Decrypt_ION(HDCP2_Ctx *hdcp, u32 str_ctr, u64 in_str, u8 *input, int inlen, u32 ion_fd)
{
	int ret = 0;
	int datasize = 0;

#ifdef CONFIG_HDCP_64BIT
	CIP_DATA_INFO_ION_RX data = {0, };
	datasize = sizeof(CIP_DATA_INFO_ION_RX);
#else
	CIP_DATA_INFO_RX data = {0, };
	datasize = sizeof(CIP_DATA_INFO_RX);
#endif /* CONFIG_HDCP_64BIT */

	if (inlen < 0 || !hdcp || !input || inlen > MAX_ENCRYPT_BUFFER) {
		HDCP2_Log("HDCP2_ERR_INVALID_INPUT (inlen = %d)", inlen);
		return HDCP2_ERR_INVALID_INPUT;
	}

	HDCP2_DEBUG("HDCP2_Decrypt_ION: input=0x%.8x, ion_fd=%d inlen=%d", input, ion_fd, inlen);

	data.str_ctr = (u32)str_ctr;
	data.inp_ctr = (u64)(in_str);

	data.input = (u8 *)(input);
	data.output = (u8 *)(uintptr_t)ion_fd;
	data.length = inlen;

	if ((ret = TZ_COMMAND_R_ION(CMD_CIP_DEC_DATA_ION, (u8 *)&data, datasize, ion_fd)) < 0) {
		HDCP2_Log("Re-Decrypt Frame");
		if ((ret = TZ_COMMAND_R_ION(CMD_CIP_DEC_DATA_ION, (u8 *)&data, datasize, ion_fd)) < 0) {
			HDCP2_Log("Ignore Frame, l=%d", data.length);
		}
		ret = HDCP2_OK;
	}

	HDCP2_DEBUG("HDCP2_Decrypt_ION: inlen %d outlen %d", inlen, ret);

	return ret;
}

/**
 * @def DUMMY_BUF_LEN
 * This macro is assigned a value of 64 to be maintained everywhere.
 */
#define DUMMY_BUF_LEN	64

/**
 * @fn int QSEE_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 qualcomm
 * @detail The control is transferred to the secure world(using SMC -secure monitor call internally)
 * @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 - HDCP2_OK in case of success, else error code corresponding to the error
 */
int QSEE_COMMAND(HDCP2_Ctx *hdcp, const u8 command, u8 *request, uint32_t req_size, u8 *response, uint32_t res_size)
{
	int ret = HDCP2_ERR_NOT_SUPPORTED_COMMAND;
	int tz_ret = 0;
	int revalue = -1;

	uint32_t inlen = (req_size) ? req_size : DUMMY_BUF_LEN;
	uint32_t outlen = (res_size) ? res_size + sizeof(int) : DUMMY_BUF_LEN;

	inlen = QSEECOM_ALIGN(inlen);
	outlen = QSEECOM_ALIGN(outlen);

	int input_offset = (int32_t)(intptr_t)(&(((CIP_DATA_INFO_RX *)0)->input));
	u8 *input = NULL;

	if (command == (CMD_CIP_DEC_DATA + CMD_RECEIVER)) {
		if (dec_mem == NULL) {
			dec_mem = (u8*)malloc(MAX_ENCRYPT_BUFFER + input_offset);
			memset(dec_mem, 0, MAX_ENCRYPT_BUFFER + input_offset);
		}
		input = dec_mem;
		memcpy((u8 *)input , request, input_offset);
		memcpy(input + input_offset,  ((CIP_DATA_INFO_RX *)request)->input, res_size);

		inlen += res_size -sizeof(CIP_DATA_INFO_RX) + input_offset;
	} else {
		input = (u8*)l_QSEEComHandle->ion_sbuffer;
		if (!input) {
			HDCP2_Log("QSEECom_send_cmd input error");
			revalue = HDCP2_ERR_INVALID_INPUT;
			return revalue;
		}

		memset(input, 0, inlen * sizeof(u8));

		if (request)
			memcpy(input, request, req_size);
	}

	u8 *output = (u8*)l_QSEEComHandle->ion_sbuffer + req_size;
	if (!output) {
		HDCP2_Log("QSEECom_send_cmd output error");
		revalue = HDCP2_ERR_INVALID_INPUT;
		goto out;
	}

	memset(output, 0, outlen * sizeof(u8));

	input[0] = (u8)command;
	output[0] = (u8)command;

	/* send request from HLOS to QSEEApp */
	/* internally create the ION buffer and copy the contents from local buffers which we passed "input" & "output*/
	ret = QSEECom_send_cmd(l_QSEEComHandle, (void *)input, inlen, (void *)output, outlen);
	if (ret < 0) {
		HDCP2_Log("QSEECom_send_cmd failed : %d", ret);
		revalue = HDCP2_ERR_INVALID_INPUT;
		goto out;
	}

	if(res_size == 0)
		memcpy(&tz_ret, output + sizeof(u8), sizeof(int));
	else
		memcpy(&tz_ret, output + res_size, sizeof(int));

	if (tz_ret < HDCP2_OK) {
		if(command == (CMD_TRANSMITTER + CMD_AKE_SET_PAIRING_INFO)) {
			HDCP2_Log("[T] There is no Pairing Information");
		} else if (command == (CMD_TRANSMITTER + CMD_HDCP2_LOADKEY) || command == (CMD_RECEIVER + CMD_HDCP2_LOADKEY)) {
			HDCP2_Log("There is no HDCP key. HDCP key installation is needed.");
		} else {
			HDCP2_Log("QSEECom_send_cmd failed with ret value : %d", tz_ret);
		}
		revalue = tz_ret;
		goto out;
	}

	if (response)
		memcpy(response, output, res_size);

	// For Key Frame check
	if ((command == (CMD_CIP_DEC_DATA + CMD_RECEIVER)) && (((CIP_DATA_INFO_RX *)request)->dec_type == DEC_TYPE_CHECK_KEY_FRAME) && (response != NULL)) {
		ret = (uint32_t)(*(u8*)response);
		memset(response, 0, sizeof(uint32_t));
		revalue = ret;
		goto out;
	}

	if (command == (CMD_HDCP2_LOADKEY + CMD_RECEIVER)
		|| command == (CMD_HDCP2_LOADKEY + CMD_TRANSMITTER)
		|| command == (CMD_LC_SEND_L_PRIME + CMD_TRANSMITTER)
		|| command == (CMD_AKE_SET_PAIRING_INFO + CMD_TRANSMITTER)) {
		revalue = HDCP2_OK;
	} else {
		revalue = res_size;
	}

out:
	return revalue;
}

int ion_alloc_input_tx = 0;
int ion_alloc_output_tx = 0;
int ion_alloc_input_rx = 0;

int QSEE_COMMAND_ION(const uint8_t command, uint8_t *request, uint32_t req_size, int ion_fd)
{
	int ret = HDCP2_ERR_NOT_SUPPORTED_COMMAND;
	uint8_t dummy[DUMMY_BUF_LEN] = {0, };
	int tz_ret = 0;
	int type = 0;
	u8 *input =  request;
	uint32_t inlen = req_size;
	u8 *output = dummy;
	uint32_t outlen = DUMMY_BUF_LEN;
	struct QSEECom_ion_fd_info ion_fd_info = {0, };
#ifdef CONFIG_HDCP_64BIT
	CIP_DATA_INFO_ION_RX *rx_data = NULL;
#else
	CIP_DATA_INFO_RX *rx_data = NULL;
#endif /* CONFIG_HDCP_64BIT */
	void *msg_req = NULL;

	if (command == (CMD_CIP_ENC_DATA + CMD_TRANSMITTER)) {
		CIP_DATA_INFO_QC_TX *tx_data = (CIP_DATA_INFO_QC_TX *)input;
		msg_req = (CIP_DATA_INFO_QC_TX *)l_QSEEComHandle->ion_sbuffer;
		memset(msg_req, 0, sizeof(CIP_DATA_INFO_QC_TX));
		type = tx_data->offset;

		// type (offset) is set as -1 for encrypt API. All other values refer to encryptNative API
		if (type == -1) {
			if (ion_alloc_input_tx == 0) {
				if ((ret = allocate_swd_ion(&gInIONObject)) != HDCP2_OK) {
					HDCP2_Log("QSEE_COMMAND_ION: Error allocating input memory ion");
					return ret;
				}
				ion_alloc_input_tx = 1;
			}
			memset(&ion_fd_info, 0, sizeof(struct QSEECom_ion_fd_info));
			ion_fd_info.data[0].fd = gInIONObject.ion_obj_fd_data.fd;
			ion_fd_info.data[0].cmd_buf_offset = (uint32_t)(uintptr_t)(&(((CIP_DATA_INFO_QC_TX *)0)->input_data));
#ifdef CONFIG_HDCP_64BIT
			ion_fd_info.data[0].cmd_buf_offset += 8;
#endif /* CONFIG_HDCP_64BIT */
			memcpy(gInIONObject.ion_obj_virt_addr, tx_data->input, tx_data->length);
		}

		if (ion_alloc_output_tx == 0) {
			if ((ret = allocate_swd_ion(&gOutIONObject)) != HDCP2_OK) {
				HDCP2_Log("QSEE_COMMAND_ION: Error allocating output memory ion");
				return ret;
			}
			ion_alloc_output_tx = 1;
		}

		if (type != -1) {
			ion_fd_info.data[0].fd = (int)(intptr_t)tx_data->input;
			ion_fd_info.data[0].cmd_buf_offset = (uint32_t)(uintptr_t)(&(((CIP_DATA_INFO_QC_TX *)0)->input_data));
#ifdef CONFIG_HDCP_64BIT
			ion_fd_info.data[0].cmd_buf_offset += 8;
#endif /* CONFIG_HDCP_64BIT */
		}

		ion_fd_info.data[1].fd = gOutIONObject.ion_obj_fd_data.fd;
		ion_fd_info.data[1].cmd_buf_offset = (uint32_t)(uintptr_t)(&(((CIP_DATA_INFO_QC_TX *)0)->output_data));
#ifdef CONFIG_HDCP_64BIT
		ion_fd_info.data[1].cmd_buf_offset += 8;
#endif /* CONFIG_HDCP_64BIT */

		tx_data->dummy = command;
		memcpy((CIP_DATA_INFO_QC_TX *)msg_req, tx_data, sizeof(CIP_DATA_INFO_QC_TX));
	 } else if((command == (CMD_CIP_DEC_DATA_ION + CMD_RECEIVER))|| (command == (CMD_CIP_SPS_PPS_DATA + CMD_RECEIVER))) {
#ifdef CONFIG_HDCP_64BIT
		rx_data = (CIP_DATA_INFO_ION_RX *)request;
		msg_req = (CIP_DATA_INFO_ION_RX *)l_QSEEComHandle->ion_sbuffer;
		memset(msg_req, 0, sizeof(CIP_DATA_INFO_ION_RX));
#else
		rx_data = (CIP_DATA_INFO_RX *)request;
		msg_req = (CIP_DATA_INFO_RX *)l_QSEEComHandle->ion_sbuffer;
		memset(msg_req, 0, sizeof(CIP_DATA_INFO_RX));
#endif

		if (ion_alloc_input_rx == 0) {
			ret = allocate_swd_ion(&gInIOnObject_Recv);
			if (ret) {
				HDCP2_Log("allocate_swd_ion failed");
				return ret;
			}
			ion_alloc_input_rx = 1;
		}

		rx_data->msg_id = command;
#ifdef CONFIG_HDCP_64BIT
		memcpy((CIP_DATA_INFO_ION_RX *)msg_req, rx_data, sizeof(CIP_DATA_INFO_ION_RX));
#else
		memcpy((CIP_DATA_INFO_RX *)msg_req, rx_data, sizeof(CIP_DATA_INFO_RX));
#endif

		HDCP2_DEBUG("res_size %d input_ctr %lld  str_ctr %d", rx_data->length, rx_data->inp_ctr, rx_data->str_ctr);

		/* send request from HLOS to QSEApp */
		/* internally create the ION buffer and copy the contents from local buffers which we passed "input" & "output*/
		memset(&ion_fd_info, 0, sizeof(struct QSEECom_ion_fd_info));

#ifdef CONFIG_HDCP_64BIT
		ion_fd_info.data[0].fd = gInIOnObject_Recv.ion_obj_fd_data.fd;
		ion_fd_info.data[0].cmd_buf_offset = (uint32_t)(uintptr_t)(&(((CIP_DATA_INFO_ION_RX *)0)->input_data)) ;
		ion_fd_info.data[1].fd = ion_fd;
		ion_fd_info.data[1].cmd_buf_offset = (uint32_t)(uintptr_t)(&(((CIP_DATA_INFO_ION_RX *)0)->output_data)) ;
		ion_fd_info.data[0].cmd_buf_offset += 8;
		ion_fd_info.data[1].cmd_buf_offset += 8;
#else
		ion_fd_info.data[0].fd = gInIOnObject_Recv.ion_obj_fd_data.fd;
		ion_fd_info.data[0].cmd_buf_offset =  (uint32_t)(uintptr_t)(&(((CIP_DATA_INFO_RX *)0)->input)) ;
		ion_fd_info.data[1].fd = ion_fd;
		ion_fd_info.data[1].cmd_buf_offset = (uint32_t)(uintptr_t)(&(((CIP_DATA_INFO_RX *)0)->output)) ;
#endif /* CONFIG_HDCP_64BIT */

		memcpy(gInIOnObject_Recv.ion_obj_virt_addr, rx_data->input, rx_data->length);
	} else {
		return ret;
	}

	HDCP2_DEBUG("QSEECom_send_modified_cmd end data->input:%x data->output=%x  offset=%d input data length=%d" , input, ion_fd_info.data[0].fd, ion_fd_info.data[0].cmd_buf_offset, inlen);
	HDCP2_DEBUG("QSEECom_send_modified_cmd end data->input=%x  offset=%d out data_length=%d" , ion_fd_info.data[1].fd, ion_fd_info.data[1].cmd_buf_offset, outlen);

#ifdef CONFIG_HDCP_64BIT
	ret = QSEECom_send_modified_cmd_64(l_QSEEComHandle, (void *)msg_req, inlen, (void *)output, outlen, &ion_fd_info);
#else
	ret = QSEECom_send_modified_cmd(l_QSEEComHandle, (void *)msg_req, inlen, (void *)output, outlen, &ion_fd_info);
#endif /* CONFIG_HDCP_64BIT */

	if (ret < HDCP2_OK) {
		if(command == (CMD_TRANSMITTER + CMD_AKE_SET_PAIRING_INFO))
			HDCP2_Log("[T] There is no Pairing Information");
		else
			HDCP2_Log("QSEECom_send_modified_cmd failed with ret value(1): %d", ret);
	}

	if (command == (CMD_CIP_ENC_DATA + CMD_TRANSMITTER)) {
		memcpy(((CIP_DATA_INFO_QC_TX *)input)->output, gOutIONObject.ion_obj_virt_addr, ((CIP_DATA_INFO_QC_TX *)input)->length);
	}

	memcpy(&tz_ret, output, sizeof(int));
	if (tz_ret < HDCP2_OK) {
		if(command == (CMD_TRANSMITTER + CMD_AKE_SET_PAIRING_INFO))
			HDCP2_Log("[T] There is no Pairing Information");
		else
			HDCP2_Log("QSEECom_send_modified_cmd failed with ret value(2): %d", tz_ret);

		return tz_ret;
	}

	if (command == (CMD_CIP_ENC_DATA + CMD_TRANSMITTER))
		return ret;
	else
		return rx_data->length;
}

/**
 * @fn int qsc_start_app(struct QSEECom_handle **l_QSEEComHandle, const char *appname, u32 buf_size)
 * @brief This function starts qualcomm trustlet
 * @param l_QSEEComHandle - pointer to QSEECom_handle
 * @param appname - pointer to app name
 * @param buf_size - size of buffer
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int qsc_start_app(struct QSEECom_handle **l_QSEEComHandle, const char *appname, u32 buf_size)
{
	int ret = HDCP2_ERR;

	HDCP2_Log("qsc_start_app is called:\n");

	ret = QSEECom_start_app(l_QSEEComHandle, IMAGE_PATH, appname, buf_size);
	if (ret)
		HDCP2_Log("Loading app is failed");
	else
		HDCP2_Log("Loading app is succeded");

	return ret;
}

/**
 * @fn int qsc_shutdown_app(struct QSEECom_handle **l_QSEEComHandle)
 * @brief This function closes qualcomm trustlet
 * @param l_QSEEComHandle - pointer to QSEECom_handle
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int qsc_shutdown_app(struct QSEECom_handle **l_QSEEComHandle)
{
	int ret = HDCP2_ERR;
	HDCP2_Log("qsc_shutdown_app is called\n");

	if (*l_QSEEComHandle != NULL) {
		ret = QSEECom_shutdown_app(l_QSEEComHandle);
		if (ret)
			HDCP2_Log("Shutdown app failed with ret = %d\n", ret);
		else
			HDCP2_Log("shutdown app: pass\n");
	} else {
		HDCP2_Log("cannot shutdown as the handle is NULL\n");
	}

	return ret;
}

/**
 * @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)
{
	int ret = HDCP2_OK;

	HDCP2_Log("Starting qseecom sshdcp2 client");

	ret = qsc_start_app(&l_QSEEComHandle,"sshdcpap", TRUSTLET_MAIN_STACK_SIZE);
	if (ret) {
		HDCP2_Log("HDCP_INIT: sshdcpap failed, try again with sshdcpapp");
		ret = qsc_start_app(&l_QSEEComHandle,"sshdcpapp", TRUSTLET_MAIN_STACK_SIZE);
		if (ret) {
			HDCP2_Log("HDCP_INIT: fail");
			return ret;
		}
	}
	HDCP2_Log("HDCP_INIT: pass");

	if (QSEECom_set_bandwidth(l_QSEEComHandle, true) < 0){
	   HDCP2_Log("QSEECom_set_bandwidth, true failed : %d");
	   return HDCP2_ERR_NOT_SUPPORTED_COMMAND;
	}

	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 ret = HDCP2_OK;

	HDCP2_Log("Closing qseecom sshdcp2 client");

	if (QSEECom_set_bandwidth(l_QSEEComHandle, false) < 0)
		HDCP2_Log("QSEECom_set_bandwidth, false failed");

	if (dec_mem) {
		free(dec_mem);
		dec_mem = NULL;
	}

	if (ion_alloc_input_tx) {
		free_swd_ion(&gInIONObject);
		ion_alloc_input_tx = 0;
	}

	if (ion_alloc_output_tx) {
		free_swd_ion(&gOutIONObject);
		ion_alloc_output_tx = 0;
	}

	if (ion_alloc_input_rx) {
		free_swd_ion(&gInIOnObject_Recv);
		ion_alloc_input_rx = 0;
	}

	if (l_QSEEComHandle) {
		ret = qsc_shutdown_app(&l_QSEEComHandle);
		l_QSEEComHandle = NULL;
	}

	if (ret)
		HDCP2_Log("HDCP2_TZ_Close: fail");
	else
		HDCP2_Log("HDCP2_TZ_Close: pass");
}
