/**
 * 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 tz_hdcp2_receiver.c
 * @author
 * @date
 * @brief This file contains definition of all the functions used in receiver in SWD side
 */
#include <stdlib.h>
#include <stdarg.h>
#if defined(CONFIG_MSM8996) || defined(CONFIG_MSM8998)
#include "ACCommon.h"
#endif /* CONFIG_MSM8996 || CONFIG_MSM8998 */

#include "tz_hdcp2_crypto.h"
#include "tz_hdcp2_common.h"
#include "tz_hdcp2.h"
#include "qsee_cipher.h"
#include "qsee_core.h"
#include "qsee_fuse.h"
#include "qsee_log.h"
#include "qsee_services.h"

/**
 * @def NULL_ERR(x,y)
 * This macro is assigned a value of
 * if(!x)return(y);
 * to be maintained everywhere.
 */
#define NULL_ERR(x,y)	if(!x)return(y);
#define QSEECOM_ALIGN_SIZE	0x40
#define QSEECOM_ALIGN_MASK	(QSEECOM_ALIGN_SIZE - 1)
#define QSEECOM_ALIGN(x)	\
	((x + QSEECOM_ALIGN_SIZE) & (~QSEECOM_ALIGN_MASK))

static TZ_HDCP2_CTX hdcp2_ctx;
static HDCP2_KEY hdcp2_key;
unsigned char s_pKey[DECRYPT_BLK_SIZE];

/**
 * @def LOG_BUF_SIZE
 * This macro is assigned a value of 4096 for logs.
 */
#define LOG_BUF_SIZE	4096

/**
 * @fn void TZ_LOG_HEX(const char *title, unsigned char *data, int length);
 * @brief This function is used to print the Tz logs.
 * @param title - Title of the prints.
 * @param data- Pointer to the data to be printed
 * @param length - size of the data.
 * @return void
 */
void TZ_LOG_HEX(const char *title, unsigned char *data, int length)
{
#ifdef DEBUG
	int i = 0;
	int j = 0;
	int remain = 0;
	int declen = 0;
	char buffer[LOG_BUF_SIZE] = { 0 };
	unsigned char* pt = data;
	char dest_buffer[LOG_BUF_SIZE] = { 0 };

	// print title
	LOGD("%s[%d] = ", title, length);

	// print binary data
	remain = length;
	declen = (remain > LOG_BUF_SIZE) ? LOG_BUF_SIZE : remain;

	while (remain > 0) {
		pt = data + j * LOG_BUF_SIZE;
		memset(buffer, 0, LOG_BUF_SIZE);
		for (i = 0; i < declen ; i++) {
			if (i % 16 == 0) {
				LOGD("%s\n", buffer);
				buffer[0] = 0;
			}
			snprintf(dest_buffer, LOG_BUF_SIZE, "%s%.2x ", buffer, pt[i]);
			snprintf(buffer,LOG_BUF_SIZE,"%s",dest_buffer);
		}
		snprintf(dest_buffer, LOG_BUF_SIZE, "%s\n", buffer);
		snprintf(buffer,LOG_BUF_SIZE, "%s", dest_buffer);
		LOGD("%s\n", buffer);
		j++;
		remain -= declen;
	}
#endif /* DEBUG */
}

/**
 * @fn int TZ_HDCP2_STOREKEY_R(const uint8_t command, uint8_t *request, const u32 req_size, uint8_t *response, uint32_t *res_size)
 * @brief This function stores the provisioning key
 * @param command - The command to be executed, sent from NWD
 * @param request- pointer to request
 * @param req_size - size of request
 * @param response- pointer to response
 * @param res_size - size of response
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_HDCP2_STOREKEY_R(const uint8_t command, uint8_t *request,
			const u32 req_size, uint8_t *response, uint32_t *res_size)
{
	int ret = 0;
	*res_size = sizeof(u8);

	LOGI("TZ_HDCP2_STOREKEY_R: without SFS");
	ret = TZ_HDCP2_WRAP_NO_SFS(request, PROVISIONING_KEY_SIZE, response, res_size);

	return ret;
}

/**
 * @fn int TZ_HDCP2_LOADKEY_R(const uint8_t command, uint8_t *request,const u32 req_size, uint8_t *response, uint32_t *res_size);
 * @brief This function loads the key provisioning key.
 * @param command - The command to be executed, sent from NWD
 * @param request- pointer to request
 * @param req_size - size of request
 * @param response- pointer to response
 * @param res_size - size of response
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_HDCP2_LOADKEY_R(const uint8_t command, uint8_t *request,
			const u32 req_size, uint8_t *response, uint32_t *res_size)
{
	int ret = 0;
	*res_size = sizeof(u8);

	LOGI("Load key without SFS");
	ret = TZ_HDCP2_LOADKEY_NO_SFS(request, req_size, &hdcp2_key, (command == CMD_HDCP2_LOADKEY_ODM)?1:0);

	return ret;
}

/**
 * @fn int TZ_SET_HDCP_VERSION_T(const uint8_t command, uint8_t *request,const u32 req_size, uint8_t *response, uint32_t *res_size);
 * @brief This function sets the HDCP version on receiver side.
 * @param command - The command to be executed, sent from NWD
 * @param request- pointer to request
 * @param req_size - size of request
 * @param response- pointer to response
 * @param req_size - size of response
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_SET_HDCP_VERSION_R(const u8 command, u8 *request, u32 req_size,
			u8 *response, u32 *res_size)
{
	NULL_ERR(request, HDCP2_TZ_NULL_REQUEST);
	hdcp2_ctx.version = request[1];
	LOGW("TZ_SET_HDCP_VERSION_R : HDCP %d.%d version is setuped", hdcp2_ctx.version / 10, hdcp2_ctx.version % 10);

	return HDCP2_OK;
}

/**
 * @fn int TZ_AKE_Init_R(const uint8_t command, uint8_t *request,const u32 req_size, uint8_t *response, uint32_t *res_size);
 * @brief This function Copies rtx sent by transmitter to the TZ HDCP Context.
 * @param command - The command to be executed, sent from NWD
 * @param request- pointer to request containing AKE_INIT message
 * @param req_size - size of request
 * @param response- pointer to response
 * @param res_size - size of response
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_AKE_Init_R(const uint8_t command, uint8_t *request, const u32 req_size,
			uint8_t *response, uint32_t *res_size)
{
	AKE_INIT *p = (AKE_INIT *) request;
	boolean isblown = 0;

	*res_size = sizeof(u8);
	NULL_ERR(p, HDCP2_TZ_NULL_RESPONSE);

#ifndef DEBUG
	//Integrity Check
	if(qsee_is_sw_fuse_blown(OEMFLAG_TZ_DRM, &isblown, sizeof(boolean)))
		return HDCP2_ERR_INTEGRITY;
	if(isblown == 1)
		return HDCP2_ERR_INTEGRITY;
#endif

	LOGD(" TZ_AKE_Init_R called =  %d %s", command, p->rtx);

	memset(&hdcp2_ctx, 0, sizeof(TZ_HDCP2_CTX));
	memcpy(hdcp2_ctx.rtx, p->rtx, sizeof(hdcp2_ctx.rtx));

#ifndef HDCP2_NO_TIME_LIMIT
	GetTimeStamp(&hdcp2_ctx.time_ake_init);
#endif /* HDCP2_NO_TIME_LIMIT */

	return HDCP2_OK;
}

/**
 * @fn int TZ_AKE_Transmitter_Info_R(const uint8_t command, uint8_t *request,const u32 req_size, uint8_t *response, uint32_t *res_size);
 * @brief  This function Copies transmitter info received into TZ HDCP Context.
 * @param command - The command to be executed, sent from NWD
 * @param request- pointer to request containing AKE_TRANSMITTER_INFO message
 * @param req_size - size of request
 * @param response- pointer to response
 * @param res_size - size of response
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_AKE_Transmitter_Info_R(const uint8_t command, uint8_t *request,
			const u32 req_size, uint8_t *response, uint32_t *res_size)
{
	AKE_TRANSMITTER_INFO *p = (AKE_TRANSMITTER_INFO *) request;
	*res_size = sizeof(u8);
	NULL_ERR(p, HDCP2_TZ_NULL_REQUEST);

	LOGD(" TZ_AKE_Transmitter_Info_R called:Command ID:%d\n", command);
	memcpy(&hdcp2_ctx.transmitter_info, p, sizeof(AKE_TRANSMITTER_INFO));

	//TRANSMITTER_LOCALITY_PRECOMPUTE_SUPPORT is bit0 of TRANSMITTER_CAPABILITY_MASK
	hdcp2_ctx.Tx_LC_Precompute = p->TRANSMITTER_CAPABILITY_MASK[1] & LOCALITY_PRECOMPUTE_SUPPORT;

	LOGW("TZ_AKE_Transmitter_Info_R : transmitter_info.VERSION : 0x%02x", hdcp2_ctx.transmitter_info.VERSION);

	return HDCP2_OK;
}

/**
 * @fn int TZ_AKE_Send_Cert_R(const uint8_t command, uint8_t *request,const u32 req_size, uint8_t *response, uint32_t *res_size);
 * @brief This function Copies and sends receiver certificate to transmitter.
 * @param command - The command to be executed, sent from NWD
 * @param request- pointer to request
 * @param req_size - size of request
 * @param response- pointer to response containing AKE_SEND_CERT message
 * @param res_size - size of response
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_AKE_Send_Cert_R(const uint8_t command, uint8_t *request,
			const u32 req_size, uint8_t *response, uint32_t *res_size)
{
	AKE_SEND_CERT *p = (AKE_SEND_CERT *) response;
	*res_size = sizeof(AKE_SEND_CERT);

	LOGD(" TZ_AKE_Send_Cert_R called:res_size:%d\n", *res_size);

	LOGD(" TZ_AKE_Send_Cert_R called:Command ID:%d\n", command);
	NULL_ERR(p, HDCP2_TZ_NULL_RESPONSE);

	p->msg_id = command;
	memcpy(p->certrx, &hdcp2_key.cert, sizeof(p->certrx));
	if (request[1] == 1) {
		p->REPEATER = 0x01;
		hdcp2_ctx.REPEATER = p->REPEATER;
	} else {
		p->REPEATER = 0x00;
	}

	return HDCP2_OK;
}

/**
 * @fn int TZ_AKE_Receiver_Info_R(const uint8_t command, uint8_t *request,const u32 req_size, uint8_t *response, uint32_t *res_size);
 * @brief This function Forms receiver information and sends it to transmitter.
 * @param command - The command to be executed, sent from NWD
 * @param request- pointer to request
 * @param req_size - size of request
 * @param response- pointer to response containing AKE_RECEIVER_INFO message
 * @param res_size - size of response
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_AKE_Receiver_Info_R(const uint8_t command, uint8_t *request,
			const u32 req_size, uint8_t *response, uint32_t *res_size)
{
	AKE_RECEIVER_INFO *p = (AKE_RECEIVER_INFO *) response;
	*res_size = sizeof(AKE_RECEIVER_INFO);

	LOGD(" TZ_AKE_Receiver_Info_R called:Command ID:%d\n", command);
	NULL_ERR(p, HDCP2_TZ_NULL_RESPONSE);

	memset(p, 0, *res_size);
	p->msg_id = command;
	p->LENGTH[1] = 6; // HDCP 2.1
	/* RECEIVER_CAPABILITY_MASK[1] is changed to 0x00 to disable precomputation of L
		 It is beacuse TVs dont support precomputation yet.
		 RECEIVER_CAPABILITY_MASK[1] must be changed to 0x01 when TVs start supporting precomputation */
	p->RECEIVER_CAPABILITY_MASK[0] = 0;
	p->RECEIVER_CAPABILITY_MASK[1] = 0x00;

	p->RECEIVER_CAPABILITY_MASK[1] |= LOCALITY_PRECOMPUTE_SUPPORT;

	if (hdcp2_ctx.version >= HDCP2_VERSION_2_2) {
		hdcp2_ctx.receiver_info.RECEIVER_CAPABILITY_MASK[0] = p->RECEIVER_CAPABILITY_MASK[0];
		hdcp2_ctx.receiver_info.RECEIVER_CAPABILITY_MASK[1] = p->RECEIVER_CAPABILITY_MASK[1];
	}

	p->VERSION = hdcp2_ctx.version - HDCP2_VERSION_2_0;

	hdcp2_ctx.receiver_info.VERSION = p->VERSION;
	hdcp2_ctx.Rx_LC_Precompute = p->RECEIVER_CAPABILITY_MASK[1] & LOCALITY_PRECOMPUTE_SUPPORT;

	LOGW("TZ_AKE_Receiver_Info_R : receiver_info.VERSION : 0x%02x", hdcp2_ctx.receiver_info.VERSION);

	return HDCP2_OK;
}

/**
 * @fn int TZ_AKE_No_Store_km_R(const uint8_t command, uint8_t *request,const u32 req_size, uint8_t *response, uint32_t *res_size);
 * @brief This function Receives Ekpub_km from transmitter and Decrypts it to get km using RSA_OAEP_decrypt and copies it to the TZ HDCP Context
 * @param command - The command to be executed, sent from NWD
 * @param request- pointer to request containing AKE_NO_STORED_KM message
 * @param req_size - size of request
 * @param response- pointer to response
 * @param res_size - size of response
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_AKE_No_Store_km_R(const uint8_t command, uint8_t *request,
			const u32 req_size, uint8_t *response, uint32_t *res_size)
{
	u8 dec[HDCP2_SIZE_RECEIVER_PUBKEY] = {0};
	int ret = HDCP2_OK;
	AKE_NO_STORED_KM *p = (AKE_NO_STORED_KM *) request;
	*res_size = sizeof(u8);
	NULL_ERR(p, HDCP2_TZ_NULL_REQUEST);

	LOGD(" TZ_AKE_No_Store_km_R called:Command ID:%d\n", command);

	// 1. Decrypt Ekpub_km
	TZ_LOG_HEX("dec km", dec, sizeof(dec));
	if ((ret = TZ_RSA_OAEP_decrypt(&hdcp2_ctx, &hdcp2_key, p->Ekpub_km, dec)) < 0){
		LOGE("TZ_AKE_No_Store_km_R called:TZ_RSA_OAEP_decrypt error:%d\n", ret);
		return ret;
	}
	TZ_LOG_HEX("dec km", dec, sizeof(dec));

	// 2. Copy decrypted buffer to hdcp2_ctx.pairing_info.km
	memcpy(hdcp2_ctx.pairing_info.km, dec, sizeof(hdcp2_ctx.pairing_info.km));

	TZ_LOG_HEX("Ekpub_km", p->Ekpub_km, sizeof(p->Ekpub_km));
	TZ_LOG_HEX("km", hdcp2_ctx.pairing_info.km, sizeof(hdcp2_ctx.pairing_info.km));

	return HDCP2_OK;
}

/**
 * @fn int TZ_AKE_Store_km_R(const uint8_t command, uint8_t *request,const u32 req_size, uint8_t *response, uint32_t *res_size);
 * @brief This function Receives Ekh_km from transmitter and Decrypts it to get km and copies it to the TZ HDCP Context
 * @param command - The command to be executed, sent from NWD
 * @param request- pointer to request containing AKE_STORED_KM message
 * @param req_size - size of request
 * @param response- pointer to response
 * @param res_size - size of response
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_AKE_Store_km_R(const uint8_t command, uint8_t *request,
			const u32 req_size, uint8_t *response, uint32_t *res_size)
{
	u32 length = 0;
	uint8_t kh[32] = { 0 };
	int ret = 0;
	AKE_STORED_KM *p = (AKE_STORED_KM *) request;
	*res_size = sizeof(u8);
	NULL_ERR(p, HDCP2_TZ_NULL_REQUEST);

	// 1. Copy payload
	memcpy(hdcp2_ctx.pairing_info.m, p->m, sizeof(p->m));
	memcpy(hdcp2_ctx.pairing_info.Ekm, p->Ekh_km, sizeof(p->Ekh_km));
	LOGD(" TZ_AKE_Store_km_R called:Command ID:%d\n", command);

	// 2. Decrypt and get km
	ret = TZ_SHA256(kh, (uint8_t *) &hdcp2_key.private_key, 128);
	if(ret < HDCP2_OK)
		return ret;

	ret = TZ_AES_decrypt(kh, 16, p->Ekh_km, 16, hdcp2_ctx.pairing_info.km, &length, NULL, QSEE_CIPHER_MODE_ECB);
	if(ret < HDCP2_OK)
		return ret;

	return HDCP2_OK;
}

/**
 * @fn int TZ_AKE_Send_rrx_R(const uint8_t command, uint8_t *request,const u32 req_size, uint8_t *response, uint32_t *res_size);
 * @brief This function Generates and sends 8bytes of random number[rrx] to transmitter.
 * @param command - The command to be executed, sent from NWD
 * @param request- pointer to request
 * @param req_size - size of request
 * @param response- pointer to response containing AKE_SEND_RRX message
 * @param res_size - size of response
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_AKE_Send_rrx_R(const uint8_t command, uint8_t *request,
			const u32 req_size, uint8_t *response, uint32_t *res_size)
{
	AKE_SEND_RRX *p = (AKE_SEND_RRX *) response;
	*res_size = sizeof(AKE_SEND_RRX);

	LOGD(" TZ_AKE_Send_rrx_R called:Command ID:%d\n", command);
	NULL_ERR(p, HDCP2_TZ_NULL_RESPONSE);
	if (TZ_rand(hdcp2_ctx.rrx, sizeof(hdcp2_ctx.rrx))!=sizeof(hdcp2_ctx.rrx)) {
		LOGE("TZ_rand failed");
		return HDCP2_ERR;
	}

	// Make payload
	p->msg_id = command;
	memcpy(p->rrx, hdcp2_ctx.rrx, sizeof(p->rrx));

	return HDCP2_OK;
}

/**
 * @fn int TZ_AKE_Send_h_prime_R(const uint8_t command, uint8_t *request,const u32 req_size, uint8_t *response, uint32_t *res_size);
 * @brief This function Generates H prime and send it to the transmitter as below eqn.
 * @param command - The command to be executed, sent from NWD
 * @param request- pointer to request
 * @param req_size - size of request
 * @param response- pointer to response containing AKE_SEND_H_PRIME message
 * @param res_size - size of response
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_AKE_Send_h_prime_R(const uint8_t command, uint8_t *request,
			const u32 req_size, uint8_t *response, uint32_t *res_size)
{
	uint8_t input[8] = { 0 };
	int ret = 0;
	AKE_SEND_H_PRIME *p = (AKE_SEND_H_PRIME *) response;
	*res_size = sizeof(AKE_SEND_H_PRIME);

	NULL_ERR(p, HDCP2_TZ_NULL_RESPONSE);

	LOGD(" TZ_AKE_Send_h_prime_R called:Command ID:%d\n", command);
	// Derivate kd = dkey0 || dkey1
	ret = TZ_Derivate_dkey(&hdcp2_ctx);
	if(ret < HDCP2_OK)
		return ret;

	memcpy(hdcp2_ctx.kd, hdcp2_ctx.dkey, 16);
	ret = TZ_Derivate_dkey(&hdcp2_ctx);
	if(ret < HDCP2_OK)
		return ret;

	memcpy(hdcp2_ctx.kd + 16, hdcp2_ctx.dkey, 16);

	if (hdcp2_ctx.version >= HDCP2_VERSION_2_2 && hdcp2_key.cert.Protocol_Descriptor == 0x01 && hdcp2_ctx.transmitter_info.VERSION >= 0x02) {
		u8 temp_input[14] = { 0 };
		LOGI("HDCP version is %d.%d and Protocol Descriptor is 0x01", hdcp2_ctx.version/10, hdcp2_ctx.version%10);
		memset(temp_input, 0, sizeof(temp_input));
		memcpy(temp_input, hdcp2_ctx.rtx, sizeof(hdcp2_ctx.rtx));
		temp_input[7] ^= hdcp2_ctx.REPEATER;
		temp_input[8] = hdcp2_ctx.receiver_info.VERSION;
		memcpy(temp_input + 9, hdcp2_ctx.receiver_info.RECEIVER_CAPABILITY_MASK, sizeof(hdcp2_ctx.receiver_info.RECEIVER_CAPABILITY_MASK));
		temp_input[11] = hdcp2_ctx.transmitter_info.VERSION;
		memcpy(temp_input + 12, hdcp2_ctx.transmitter_info.TRANSMITTER_CAPABILITY_MASK, sizeof(hdcp2_ctx.transmitter_info.TRANSMITTER_CAPABILITY_MASK));
		//Compute H
		// HMAC-SHA256(rtx XOR REPEATER || receiver_info.VERSION || receiver_info.RECEIVER_CAPABILITY_MASK ||
		// transmitter_info.VERSION || transmitter_info.TRANSMITTER_CAPABILITY_MASK, kd)
		TZ_HMAC_SHA256(p->H, hdcp2_ctx.kd, sizeof(hdcp2_ctx.kd), temp_input, sizeof(temp_input));
	} else {
		// input
		memcpy(input, hdcp2_ctx.rtx, sizeof(hdcp2_ctx.rtx));
		input[7] ^= hdcp2_ctx.REPEATER;
		// HMAC-SHA256(rtx XOR REPEATER, kd)
		ret = TZ_HMAC_SHA256(p->H, hdcp2_ctx.kd, sizeof(hdcp2_ctx.kd), input, sizeof(input));
	}

	TZ_LOG_HEX("input", input, 8);
	TZ_LOG_HEX("rn", hdcp2_ctx.rn, sizeof(hdcp2_ctx.rn));
	TZ_LOG_HEX("km", hdcp2_ctx.pairing_info.km, sizeof(hdcp2_ctx.pairing_info.km));
	TZ_LOG_HEX("kd", hdcp2_ctx.kd, sizeof(hdcp2_ctx.kd));

	if (ret < HDCP2_OK)
		return ret;

	TZ_LOG_HEX("h_prime", p->H, 32);

	p->msg_id = command;

	return HDCP2_OK;
}

/**
 * @fn int TZ_AKE_Send_Pairing_Info_R(const uint8_t command, uint8_t *request,const u32 req_size, uint8_t *response, uint32_t *res_size);
 * @brief This function Generates Ekh_km and send it to the transmitter.
 * @param command - The command to be executed, sent from NWD
 * @param request- pointer to request
 * @param req_size - size of request
 * @param response- pointer to response containing AKE_SEND_PAIRING_INFO message
 * @param res_size - size of response
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_AKE_Send_Pairing_Info_R(const uint8_t command, uint8_t *request,
			const u32 req_size, uint8_t *response, uint32_t *res_size)
{
	u32 length = 16;
	int ret = 0;
	AKE_SEND_PAIRING_INFO *p = (AKE_SEND_PAIRING_INFO *) response;
	uint8_t kh[32] = { 0 };
	*res_size = sizeof(AKE_SEND_PAIRING_INFO);

	NULL_ERR(p, HDCP2_TZ_NULL_RESPONSE);

	// make Ekh_km
	p->msg_id = command;
	ret = TZ_SHA256(kh, (uint8_t *) &hdcp2_key.private_key, 128);
	if (ret < HDCP2_OK) {
		LOGE("TZ_SHA256 failed");
		return ret;
	}

	// encrypt
	ret = TZ_AES_encrypt(kh, 16, hdcp2_ctx.pairing_info.km, 16, p->Ekh_Km, &length, NULL, QSEE_CIPHER_MODE_ECB);
	if (ret < HDCP2_OK) {
		LOGE("TZ_AES_encrypt failed");
		return ret;
	}

	hdcp2_ctx.is_paired = 1;
	TZ_LOG_HEX("p->Ekh_km", p->Ekh_Km, sizeof(p->Ekh_Km));

	return HDCP2_OK;
}

/**
 * @fn int TZ_LC_Init_R(const uint8_t command, uint8_t *request,const u32 req_size, uint8_t *response, uint32_t *res_size);
 * @brief This function Copies received random value [rn] from transmitter
 * @param command - The command to be executed, sent from NWD
 * @param request- pointer to request containing LC_INIT message
 * @param req_size - size of request
 * @param response- pointer to response
 * @param res_size - size of response
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_LC_Init_R(const uint8_t command, uint8_t *request, const u32 req_size,
			uint8_t *response, uint32_t *res_size)
{
	int i = 0;
	uint8_t key[32] = { 0 };
	uint8_t L[32] = { 0 };
	LC_INIT *p = (LC_INIT *) request;
	*res_size = sizeof(u8);

	NULL_ERR(p, HDCP2_TZ_NULL_REQUEST);

	// Copy payload
	memcpy(hdcp2_ctx.rn, p->rn, sizeof(p->rn));

	if (hdcp2_ctx.Rx_LC_Precompute && hdcp2_ctx.Tx_LC_Precompute && hdcp2_ctx.version >= HDCP2_VERSION_2_1) {
		// key = kd ^ rrx
		memcpy(key, hdcp2_ctx.kd, sizeof(hdcp2_ctx.kd));
		for (i = 24; i < 32; i++)
			key[i] ^= hdcp2_ctx.rrx[i - 24];

		if (hdcp2_ctx.version >= HDCP2_VERSION_2_2 && hdcp2_ctx.transmitter_info.VERSION != 0x01) {
			u8 temp_rn[16] = {0};
			memcpy(temp_rn, hdcp2_ctx.rn, sizeof(hdcp2_ctx.rn));
			memcpy(temp_rn + sizeof(hdcp2_ctx.rn), hdcp2_ctx.rn, sizeof(hdcp2_ctx.rn));
			//As per HDCP2.2 SPec:HMAC-SHA256(rn || rn, kd XOR rrx)
			TZ_HMAC_SHA256(L, key, sizeof(key), temp_rn, sizeof(temp_rn));
		} else {
			TZ_HMAC_SHA256(L, key, sizeof(key), hdcp2_ctx.rn, sizeof(hdcp2_ctx.rn));
		}
		memcpy(hdcp2_ctx.L_msb, L , 16); //L msb
		memcpy(hdcp2_ctx.L_lsb, L+16, 16);
		hdcp2_ctx.Precompute_flag = 1;
		TZ_LOG_HEX("hdcp2_ctx.L_msb", hdcp2_ctx.L_msb, sizeof(hdcp2_ctx.L_msb));
		TZ_LOG_HEX("hdcp2_ctx.L_lsb", hdcp2_ctx.L_lsb, sizeof(hdcp2_ctx.L_lsb));
	}

	return HDCP2_OK;
}

/**
 * @fn int TZ_RTT_READY_R(const uint8_t command, uint8_t *request,const u32 req_size, uint8_t *response, uint32_t *res_size);
 * @brief This function sends RTT_Ready message to transmitter when L' computation is complete.
 * @param command - The command to be executed, sent from NWD
 * @param request- pointer to request
 * @param req_size - size of request
 * @param response- pointer to response containing RTT_READY message
 * @param res_size - size of response
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_RTT_READY_R(const uint8_t command, uint8_t *request,
			const u32 req_size, uint8_t *response, uint32_t *res_size)
{
	RTT_READY *p = (RTT_READY *) response;
	*res_size = sizeof(RTT_READY);
	NULL_ERR(p, HDCP2_TZ_NULL_RESPONSE);

	// Make payload
	p->msg_id = command;

	return HDCP2_OK;
}

/**
 * @fn int TZ_RTT_CHALLENGE_R(const uint8_t command, uint8_t *request,const u32 req_size, uint8_t *response, uint32_t *res_size);
 * @brief This function copies received least significant 128 bits of L from transmitter.
 * @param command - The command to be executed, sent from NWD
 * @param request- pointer to request RTT_CHALLENGE message
 * @param req_size - size of request
 * @param response- pointer to response containing
 * @param res_size - size of response
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_RTT_CHALLENGE_R(const uint8_t command, uint8_t *request, const u32 req_size,
			uint8_t *response, uint32_t *res_size)
{
	RTT_CHALLENGE *rtt = (RTT_CHALLENGE *)request;
	NULL_ERR(rtt, HDCP2_TZ_NULL_REQUEST);

	if (memcmp(rtt->L_lsb, hdcp2_ctx.L_lsb, 16) != 0) //L lsb
		return HDCP2_ERR_INVALID_L;

	return HDCP2_OK;
}

/**
 * @fn int TZ_LC_Send_L_prime_R(const uint8_t command, uint8_t *request,const u32 req_size, uint8_t *response, uint32_t *res_size);
 * @brief This function calaculates L by equation : HMAC_SHA256(rn, (kd ^ rrx) )
 * @param command - The command to be executed, sent from NWD
 * @param request- pointer to request
 * @param req_size - size of request
 * @param response- pointer to response containing LC_SEND_L_PRIME_PC message
 * @param res_size - size of response
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_LC_Send_L_prime_R(const uint8_t command, uint8_t *request,
			const u32 req_size, uint8_t *response, uint32_t *res_size)
{
	int i = 0;
	uint8_t key[32] = {0};

	if (hdcp2_ctx.Precompute_flag == 1) {
		LC_SEND_L_PRIME_PC *p = (LC_SEND_L_PRIME_PC *) response;
		*res_size = sizeof(LC_SEND_L_PRIME_PC);

		NULL_ERR(p, HDCP2_TZ_NULL_RESPONSE);
		memcpy(p->L_msb, hdcp2_ctx.L_msb, 16);

		// Make payload
		p->msg_id = command;
	} else {
		int ret = 0;
		LC_SEND_L_PRIME *p = (LC_SEND_L_PRIME *) response;
		*res_size = sizeof(LC_SEND_L_PRIME);

		NULL_ERR(p, HDCP2_TZ_NULL_RESPONSE);

		// key = kd ^ rrx
		memcpy(key, hdcp2_ctx.kd, sizeof(hdcp2_ctx.kd));
		for (i = 24; i < 32; i++)
			key[i] ^= hdcp2_ctx.rrx[i - 24];

		// Make payload
		p->msg_id = command;

		ret = TZ_HMAC_SHA256(p->L, key, sizeof(key), hdcp2_ctx.rn, sizeof(hdcp2_ctx.rn));
		if (ret < HDCP2_OK)
			return ret;
	}

	return HDCP2_OK;
}

/**
 * @fn int TZ_SKE_Send_Eks_R(const uint8_t command, uint8_t *request,const u32 req_size, uint8_t *response, uint32_t *res_size);
 * @brief This function decrypts Eks using riv
 * @param command - The command to be executed, sent from NWD
 * @param request- pointer to request containing SKE_SEND_EKS message
 * @param req_size - size of request
 * @param response- pointer to response
 * @param res_size - size of response
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_SKE_Send_Eks_R(const uint8_t command, uint8_t *request,
			const u32 req_size, uint8_t *response, uint32_t *res_size)
{
	uint8_t H[32] = { 0 };
	int i = 0;
	int ret = 0;

	NULL_ERR(request, HDCP2_TZ_NULL_REQUEST);
	*res_size = sizeof(u8);

	if (hdcp2_ctx.version == HDCP2_VERSION_2_3) {
		SKE_SEND_EKS_VER23 *p = (SKE_SEND_EKS_VER23 *) request;

		// 1. Copy request
		memcpy(hdcp2_ctx.riv, p->riv, sizeof(hdcp2_ctx.riv));

		// HDCP v2.3: calculate HMAC of riv and compare with received
		TZ_HMAC_SHA256(H, hdcp2_ctx.kd, sizeof(hdcp2_ctx.kd), hdcp2_ctx.riv,
				sizeof(hdcp2_ctx.riv));

		TZ_LOG_HEX("HMAC_R", H, 32);
		TZ_LOG_HEX("HMAC_T", p->H, 32);

		if (memcmp(p->H, H, sizeof(H))){
			LOGE("TZ_SKE_Send_Eks_R: Compare Hmac fail\n");
			return HDCP2_ERR_VERI_SKE;
		}

		// 2. Key Derivation (dkey2 <= ctr=2)
		ret = TZ_Derivate_dkey(&hdcp2_ctx);
		if (ret < HDCP2_OK)
			return ret;
		TZ_LOG_HEX("dkey2", hdcp2_ctx.dkey, sizeof(hdcp2_ctx.dkey));

		// 3. ks = Eks ^ dkey2 ^ rrx[8-15]
		for (i = 0; i < 16; i++) {
			hdcp2_ctx.ks[i] = (i < 8) ? p->Eks[i] ^ hdcp2_ctx.dkey[i] : p->Eks[i]
					^ hdcp2_ctx.dkey[i] ^ hdcp2_ctx.rrx[i - 8];
		}
		TZ_LOG_HEX("Eks", p->Eks, sizeof(p->Eks));
	} else {
		SKE_SEND_EKS *p = (SKE_SEND_EKS *) request;

		// 1. Copy request
		memcpy(hdcp2_ctx.riv, p->riv, sizeof(hdcp2_ctx.riv));

		// 2. Key Derivation (dkey2 <= ctr=2)
		ret = TZ_Derivate_dkey(&hdcp2_ctx);
		if (ret < HDCP2_OK)
		return ret;

		TZ_LOG_HEX("dkey2", hdcp2_ctx.dkey, sizeof(hdcp2_ctx.dkey));

		// 3. ks = Eks ^ dkey2 ^ rrx[8-15]
		for (i = 0; i < 16; i++) {
			hdcp2_ctx.ks[i] = (i < 8) ? p->Eks[i] ^ hdcp2_ctx.dkey[i] : p->Eks[i]
					^ hdcp2_ctx.dkey[i] ^ hdcp2_ctx.rrx[i - 8];
		}
		TZ_LOG_HEX("Eks", p->Eks, sizeof(p->Eks));
	}

	TZ_LOG_HEX("ks", hdcp2_ctx.ks, sizeof(hdcp2_ctx.ks));
	GetTimeStamp(&hdcp2_ctx.time_ske_done);

	// TODO : Check if it's ok to put here instead of TZ_DEC_Data
	if (TZ_Get_ContentKey(hdcp2_ctx.ks, hdcp2_key.lc128, s_pKey) != 0) {
		LOGE(" TZ_Get_ContentKey failed\n");
		return HDCP2_ERR_CRYPTO;
	}

	return HDCP2_OK;
}

/**
 * @fn int TZ_Is_Key_Frame(u8 *output_data, int length, int32_t *bIsKeyFrame, u32 codec_type)
 * @brief Checks whether the received frame is a key frame
 * @param output_data - pointer to the frame to be checked.
 * @param inlen - size of the input data.
 * @param bIsKeyFrame - pointer where the value has to be set
 * @param codec_type - codec type
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_Is_Key_Frame(u8 *output_data, u32 inlen, int *bIsKeyFrame, u32 codec_type)
{
	int ret = HDCP2_OK;
	int i = -1;
	u32 maxlen = inlen < DEC_MAX_HEADER_SIZE ? inlen : DEC_MAX_HEADER_SIZE;

	if (maxlen < DEC_MIN_HEADER_SIZE) {
		LOGE("TZ_Is_Key_Frame: Error, Invalid maxlen=%d, it should be not less than 5\n", maxlen);
		return HDCP2_ERR_INVALID_INPUT;
	}

	LOGD("%s", __func__);
	if (codec_type == 1) { // MPEG4
		for (i = 0 ; i < (maxlen - DEC_MIN_HEADER_SIZE) ; i++) {
			if (*(output_data+i) == 0x00 && *(output_data+i+1) == 0x00
				&& *(output_data+i+2) == 0x01 && *(output_data+i+3) == 0xb6) {
				// prefix = 0x00 0x00 0x01 , VOP start code = 0xb6
				if (((*(output_data+i+4) >> 6) & 0x3) == 0) { // 2 bit mask : 0 means I FRAME
					*bIsKeyFrame = 1;
					LOGD("%s - MPEG4 key frame exist !!! offset : %d", __func__, i);
					break;
				} else {
					*bIsKeyFrame = 0;
					LOGD("%s - MPEG4 key frame not exist !!! offset : %d", __func__, i);
					break;
				}
			}
		}
	} else if (codec_type == 2) { // 264 
		for (i = 0 ; i < maxlen - DEC_MIN_HEADER_SIZE ; i++) {
			if (*(output_data+i) == 0x00 && *(output_data+i+1) == 0x00
				&& *(output_data+i+2) == 0x00 && *(output_data+i+3) == 0x01) {
				// start code = 0x00 0x00 0x00 0x01
				if ((*(output_data+i+4) & 0x1f) == 5) { // 5 bit mask : 5 means I FRAME
					*bIsKeyFrame = 2;
					LOGD("%s - 264 key frame exist (start code = 0x00 0x00 0x00 0x01) !!! offset : %d", __func__, i);
					break;
				} else {
					*bIsKeyFrame = 0;
					LOGD("%s - 264 key frame not exist (start code = 0x00 0x00 0x00 0x01) !!! offset : %d", __func__, i);
				}
			} else if(*(output_data+i) == 0x00 && *(output_data+i+1) == 0x00
						&& *(output_data+i+2) == 0x01) { // start code = 0x00 0x00 0x01
				if ((*(output_data+i+3) & 0x1f) == 5) { // 5 bit mask : 5 means I FRAME
					*bIsKeyFrame = 3;
					LOGD("%s - 264 key frame exist (start code = 0x00 0x00 0x01) !!! offset :", __func__, i);
					break;
				} else {
					*bIsKeyFrame = 0;
					LOGD("%s - 264 key frame not exist (start code = 0x00 0x00 0x01) !!! offset :", __func__, i);
				}
			}
		}
	}

	if (*bIsKeyFrame) {
		LOGD("%s - key frame found", __func__);
	} else {
		if (codec_type == 1) {
			LOGD("%s - MPEG4 key frame not found", __func__);
		} else if (codec_type == 2) {
			LOGD("%s - 264 key frame not found", __func__);
		}
	}

	return ret;
}

/**
 * @fn int TZ_Is_Video(u8 *output_data, u32 inlen, int *bIsVideo);
 * @brief This function checks if input is video, then we should return error
 * @param output_data - The pointer to the data.
 * @param inlen - Length of the data.
 * @param bIsVideo - Pointer to the variable which tells whether input a video or not.
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_Is_Video(u8 *output_data, u32 inlen, int *bIsVideo)
{
	int ret = HDCP2_OK;

	LOGD("%s", __func__);
	*bIsVideo = 0;

	// If input is video, then we should return error
	if ((*(output_data) == 0x00 && *(output_data + 1) == 0x00 && *(output_data + 2) == 0x01)
		|| (*(output_data) == 0x00 && *(output_data + 1) == 0x00 
		&& *(output_data + 2) == 0x00 && *(output_data + 3) == 0x01)) {
		*bIsVideo = 1;
	}

	return ret;
}

/**
 * @fn int TZ_DEC_Data(const uint8_t command, uint8_t *request,const u32 req_size, uint8_t *response, uint32_t *res_size);
 * @brief This function decrypts the encrypted data stream.
 * @param command - The command to be executed, sent from NWD
 * @param request- pointer to request containing CIP_DATA_INFO_RX message
 * @param req_size - size of request
 * @param response- pointer to response
 * @param res_size - size of response
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_DEC_Data(uint8_t command, uint8_t *request, const u32 req_size,
			uint8_t *response, uint32_t *res_size)
{
	int dec_ret = 0;
	int bIsKeyFrame = 0;
	int adjust_size = 0;
	int qsee_align = 64; // QSEECOM_ALIGN(inlen) is 64, where inlen is sizeof(CIP_DATA_INFO_RX).
	unsigned char p[DECRYPT_BLK_SIZE] = {0, };

	u64 input_ctr = 0;
	adjust_size = qsee_align - sizeof(CIP_DATA_INFO_RX) + (int)(*(((CIP_DATA_INFO_RX *)0)->input)) + ADJUST_POINTER_SIZE;

	if (req_size < sizeof(CIP_DATA_INFO_RX) || req_size > DEC_MAX_HEADER_SIZE + adjust_size) {
		*res_size = 0; //For error return
		LOGE("Error, TZ_DEC_Data data size (%d)\n", req_size);
		return HDCP2_ERR;
	}

	CIP_DATA_INFO_RX *data = (CIP_DATA_INFO_RX *)request;

	if (!data || !data->input || !data->length || !data->output) {
		*res_size = 0; //For error return
		LOGE("TZ_DEC_Data: Error, Invalid Input\n");
		return HDCP2_ERR_INVALID_INPUT;
	}

	if (*res_size < data->length) {
		*res_size = 0; //For error return
		LOGE("Error, res_size less than data->length (%d)(%d)\n", *res_size, data->length);
		return HDCP2_ERR;
	}

	input_ctr = data->inp_ctr;
	p[0] = hdcp2_ctx.riv[0];
	p[1] = hdcp2_ctx.riv[1];
	p[2] = hdcp2_ctx.riv[2];
	p[3] = hdcp2_ctx.riv[3];
	p[4] = hdcp2_ctx.riv[4] ^ (u8)((data->str_ctr >> 24) & 0xff);
	p[5] = hdcp2_ctx.riv[5] ^ (u8)((data->str_ctr >> 16) & 0xff);
	p[6] = hdcp2_ctx.riv[6] ^ (u8)((data->str_ctr >> 8) & 0xff);
	p[7] = hdcp2_ctx.riv[7] ^ (u8)(data->str_ctr & 0xff);
	p[8] = (u8)((input_ctr>> 56) & 0xff);
	p[9] = (u8)((input_ctr >> 48) & 0xff);
	p[10] = (u8)((input_ctr>> 40) & 0xff);
	p[11] = (u8)((input_ctr >> 32) & 0xff);
	p[12] = (u8)((input_ctr >> 24) & 0xff);
	p[13] = (u8)((input_ctr>> 16) & 0xff);
	p[14] = (u8)((input_ctr>> 8) & 0xff);
	p[15] = (u8)(input_ctr& 0xff);

	LOGD("res_size %d input_ctr %lld  str_ctr %d data length %d", data->length, data->inp_ctr, data->str_ctr, data->length);
	//TZ_LOG_HEX("input", (u8*)data + (int)(&(((CIP_DATA_INFO_RX *)0)->input)), data->length);
	dec_ret = TZ_AES_decrypt(s_pKey, DECRYPT_BLK_SIZE, (u8*)data + (int)(*(((CIP_DATA_INFO_RX *)0)->input)), data->length,
					(u8*)response, res_size, p, QSEE_CIPHER_MODE_CTR);
	//TZ_LOG_HEX("output", response, *res_size);

	if (data->dec_type == DEC_TYPE_CHECK_KEY_FRAME) {
		TZ_Is_Key_Frame((u8 *) response, data->length, &bIsKeyFrame, data->codec_type);
		memset(response, 0x00, data->length); // erase decrypted data
		if (!(bIsKeyFrame < 0))
			*response = bIsKeyFrame;
		LOGD("keyframe = %d", *res_size);
	} else if (data->dec_type == DEC_TYPE_AUDIO) {
		TZ_Is_Video((u8 *) response, data->length, &bIsKeyFrame);
		if (bIsKeyFrame > 0) { // video frame dectected
			LOGD("%s DEC_TYPE_AUDIO: decrypted data is video", __func__);
			memset(response, 0x00, data->length); // erase decrypted data
		} else {
			LOGD("%s DEC_TYPE_AUDIO: decrypted data is audio", __func__);
		}
	} else if (data->dec_type == DEC_TYPE_VIDEO) {
		LOGD("%s DEC_TYPE_VIDEO: no support", __func__);
		memset(response, 0x00, data->length); // erase decrypted data
	} else if (data->dec_type == DEC_TYPE_NORMAL) {
		LOGD("%s DEC_TYPE_NORMAL: will be disabled in next version", __func__);
	}

	if (dec_ret == HDCP2_OK && *res_size == data->length) {
		return HDCP2_OK;
	} else {
		LOGE("TZ_DEC_Data decrypted size is different from input size");
		return HDCP2_ERR_CRYPTO;
	}
}

/**
 * @fn int TZ_RepeaterAuth_Send_ReceiverId_List_Rep(const uint8_t command, uint8_t *request,const u32 req_size, uint8_t *response, uint32_t *res_size);
 * @brief This function sends the message containing the information regarding the receivers connected to the repeater
 * @param command - The command to be executed, sent from NWD
 * @param request- pointer to request
 * @param req_size - size of request
 * @param response- pointer to response
 * @param res_size - size of response
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_RepeaterAuth_Send_ReceiverId_List_Rep(const u8 command, u8 *request,
			const u32 req_size, u8 *response, u32 *res_size)
{
	int ret;

	if (hdcp2_ctx.version >= HDCP2_VERSION_2_1)
		ret = TZ_RepeaterAuth_Send_ReceiverId_List21_Rep(response,res_size);
	else
		ret = TZ_RepeaterAuth_Send_ReceiverId_List20_Rep(response,res_size);

	return ret;
}

/**
 * @fn int TZ_RepeaterAuth_Send_ReceiverId_List20_Rep(uint8_t *response, u32 *res_size)
 * @brief This function gets the message containing the information regarding the hdcp2.0 compliant receivers connected to the repeater
 * @param command - The command to be executed, sent from NWD
 * @param request- pointer to request
 * @param req_size - size of request
 * @param response- pointer to response containing REPEATERAUTH_SEND_RECEIVER_ID_LIST20 message
 * @param res_size - size of response
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_RepeaterAuth_Send_ReceiverId_List20_Rep(uint8_t *response, u32 *res_size)
{
	u8 V[32] = { 0 };
	u8 input[159] = { 0 };
	u8 offset=0;
	u8 totalSize = 0;

	REPEATERAUTH_SEND_RECEIVER_ID_LIST20 *recidinfo =
			(REPEATERAUTH_SEND_RECEIVER_ID_LIST20 *) response;
	NULL_ERR(response, HDCP2_ERR_NULL_REQUEST);

	LOGI("entered 2.0");
	recidinfo->msg_id = 0x0c;

	/*
	 * hardcoding below parameters since present requirement
	 * of a single Tx-Repeater-Rx communication.
	 * can be updated real time when we can count the number
	 * for sessions created from repeater.
	 * */
	recidinfo->DEVICE_COUNT = 0x01;
	recidinfo->DEPTH = 0x01;

	//max 31 devices
	if ((recidinfo->DEVICE_COUNT) <= 0x1F) {
		recidinfo->MAX_DEVS_EXCEEDED = 0x00;
	} else {
		recidinfo->MAX_DEVS_EXCEEDED = 0x01;
	}

	*res_size =  HDCP_FIXED_SIZE_REPEATERAUTH_SEND_RECEIVERID_LIST20 + (recidinfo->DEVICE_COUNT)*RECEIVER_ID_SIZE;

	// max 4 level of devices
	if ((recidinfo->DEPTH) <= 0x04) {
		recidinfo->MAX_CASCADE_EXCEEDED = 0x00;
	} else {
		recidinfo->MAX_CASCADE_EXCEEDED = 0x01;
	}

/*
*  Receiver ID hardcoded for Repeater. Qualcomm API will provide receiver ID.
*  Request has been proposed to Qualcomm.
*  Waiting for their response.
*
*      recidinfo->RECEIVER_IDj[0]= 0x24 ;
*      recidinfo->RECEIVER_IDj[1]= 0x73 ;
*      recidinfo->RECEIVER_IDj[2]= 0xee ;
*      recidinfo->RECEIVER_IDj[3]= 0x29 ;
*      recidinfo->RECEIVER_IDj[4]= 0x1b ;
*/

	memcpy(input, recidinfo->RECEIVER_IDs[0].RECEIVER_IDj, (recidinfo->DEVICE_COUNT)*RECEIVER_ID_SIZE);
	offset = (recidinfo->DEVICE_COUNT)*RECEIVER_ID_SIZE;

	memcpy(input + offset, &(recidinfo->DEPTH), sizeof(recidinfo->DEPTH));
	memcpy(input + offset + sizeof(recidinfo->DEPTH),
			&(recidinfo->DEVICE_COUNT), sizeof(recidinfo->DEVICE_COUNT));
	memcpy(input + offset + sizeof(recidinfo->DEPTH) + sizeof(recidinfo->DEVICE_COUNT),
			&(recidinfo->MAX_DEVS_EXCEEDED), sizeof(recidinfo->MAX_DEVS_EXCEEDED));
	memcpy( input + offset + sizeof(recidinfo->DEPTH) + sizeof(recidinfo->DEVICE_COUNT)
					+ sizeof(recidinfo->MAX_DEVS_EXCEEDED),
			&(recidinfo->MAX_CASCADE_EXCEEDED), sizeof(recidinfo->MAX_CASCADE_EXCEEDED));

	totalSize = offset + sizeof(recidinfo->DEPTH)+ sizeof(recidinfo->DEVICE_COUNT) + sizeof(recidinfo->MAX_DEVS_EXCEEDED) + sizeof(recidinfo->MAX_CASCADE_EXCEEDED);

	TZ_HMAC_SHA256(V, hdcp2_ctx.kd, sizeof(hdcp2_ctx.kd), input, totalSize);

	memcpy(recidinfo->V_PRIME, V, sizeof(V));

	return HDCP2_OK;
}

/**
 * @fn int TZ_RepeaterAuth_Send_ReceiverId_List21_Rep(uint8_t *response, u32 *res_size)
 * @brief This function gets the message containing the information regarding the hdcp2.0 compliant receivers connected to the repeater
 * @param command - The command to be executed, sent from NWD
 * @param request- pointer to request
 * @param req_size - size of request
 * @param response- pointer to response containing REPEATERAUTH_SEND_RECEIVER_ID_LIST21 message
 * @param res_size - size of response
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_RepeaterAuth_Send_ReceiverId_List21_Rep(uint8_t *response, u32 *res_size)
{
	u8 V[32] = { 0 };
	u8 input[164] = { 0 };
	u8 totalSize = 0;
	u8 offset=0;

	REPEATERAUTH_SEND_RECEIVER_ID_LIST21 *recidinfo =
			(REPEATERAUTH_SEND_RECEIVER_ID_LIST21 *) response;
	NULL_ERR(response, HDCP2_ERR_NULL_REQUEST);
	*res_size = sizeof(REPEATERAUTH_SEND_RECEIVER_ID_LIST21);
	hdcp2_ctx.seq_num_V[2]++;

	recidinfo->msg_id = 0x0c;

	/*
	 * hardcoding below parameters since present requirement
	 * of a single Tx-Repeater-Rx communication.
	 * can be updated real time when we can count the number
	 * for sessions created from repeater.
	 * */
	recidinfo->DEVICE_COUNT = 0x01;
	recidinfo->DEPTH = 0x01;
	//Hardcoding end

	//max 31 devices
	if ((recidinfo->DEVICE_COUNT) <= 0x1F) {
		recidinfo->MAX_DEVS_EXCEEDED = 0x00;
	} else {
		recidinfo->MAX_DEVS_EXCEEDED = 0x01;
	}

	*res_size = HDCP_FIXED_SIZE_REPEATERAUTH_SEND_RECEIVERID_LIST21 + (recidinfo->DEVICE_COUNT)*RECEIVER_ID_SIZE;
	// max 4 level of devices
	if ((recidinfo->DEPTH) <= 0x04) {
		recidinfo->MAX_CASCADE_EXCEEDED = 0x00;
	} else {
		recidinfo->MAX_CASCADE_EXCEEDED = 0x01;
	}

	//not supporting this
	recidinfo->HDCP2_LEGACY_DEVICE_DOWNSTREAM = 0x00;
	// end device is HDMI compatible, hence acts as HDCP 1.x device
	recidinfo->HDCP1_DEVICE_DOWNSTREAM = 0x01;

	memcpy(recidinfo->seq_num_V, hdcp2_ctx.seq_num_V, sizeof(recidinfo->seq_num_V));

	memcpy(input, recidinfo->RECEIVER_IDs[0].RECEIVER_IDj, (recidinfo->DEVICE_COUNT)*RECEIVER_ID_SIZE);
	offset = (recidinfo->DEVICE_COUNT)*RECEIVER_ID_SIZE;

	memcpy(input + offset, &(recidinfo->DEPTH), sizeof(recidinfo->DEPTH));
	memcpy(input + offset + sizeof(recidinfo->DEPTH),
			&(recidinfo->DEVICE_COUNT), sizeof(recidinfo->DEVICE_COUNT));
	memcpy(input + offset + sizeof(recidinfo->DEPTH)
					+ sizeof(recidinfo->DEVICE_COUNT),
			&(recidinfo->MAX_DEVS_EXCEEDED), sizeof(recidinfo->MAX_DEVS_EXCEEDED));
	memcpy(input + offset + sizeof(recidinfo->DEPTH) + sizeof(recidinfo->DEVICE_COUNT)
					+ sizeof(recidinfo->MAX_DEVS_EXCEEDED),
			&(recidinfo->MAX_CASCADE_EXCEEDED), sizeof(recidinfo->MAX_CASCADE_EXCEEDED));
	memcpy(input + offset + sizeof(recidinfo->DEPTH) + sizeof(recidinfo->DEVICE_COUNT)
					+ sizeof(recidinfo->MAX_DEVS_EXCEEDED) + sizeof(recidinfo->MAX_CASCADE_EXCEEDED),
			&(recidinfo->HDCP2_LEGACY_DEVICE_DOWNSTREAM), sizeof(recidinfo->HDCP2_LEGACY_DEVICE_DOWNSTREAM));
	memcpy(input + offset + sizeof(recidinfo->DEPTH) + sizeof(recidinfo->DEVICE_COUNT)
					+ sizeof(recidinfo->MAX_DEVS_EXCEEDED) + sizeof(recidinfo->MAX_CASCADE_EXCEEDED)
					+ sizeof(recidinfo->HDCP2_LEGACY_DEVICE_DOWNSTREAM),
			&(recidinfo->HDCP1_DEVICE_DOWNSTREAM), sizeof(recidinfo->HDCP1_DEVICE_DOWNSTREAM));
	memcpy(input + offset + sizeof(recidinfo->DEPTH) + sizeof(recidinfo->DEVICE_COUNT)
					+ sizeof(recidinfo->MAX_DEVS_EXCEEDED) + sizeof(recidinfo->MAX_CASCADE_EXCEEDED)
					+ sizeof(recidinfo->HDCP2_LEGACY_DEVICE_DOWNSTREAM) + sizeof(recidinfo->HDCP1_DEVICE_DOWNSTREAM),
			recidinfo->seq_num_V, sizeof(recidinfo->seq_num_V));

	totalSize = offset + sizeof(recidinfo->DEPTH)+ sizeof(recidinfo->DEVICE_COUNT)+ sizeof(recidinfo->MAX_DEVS_EXCEEDED)+ sizeof(recidinfo->MAX_CASCADE_EXCEEDED)
					+ sizeof(recidinfo->HDCP2_LEGACY_DEVICE_DOWNSTREAM) + sizeof(recidinfo->HDCP1_DEVICE_DOWNSTREAM) + sizeof(recidinfo->seq_num_V);

	TZ_HMAC_SHA256(V, hdcp2_ctx.kd, sizeof(hdcp2_ctx.kd), input, totalSize);

	TZ_LOG_HEX("V", V, sizeof(V));
	memcpy(recidinfo->V_PRIME, V, 16);
	memcpy(hdcp2_ctx.repeater_ack, V + 16, 16);
	TZ_LOG_HEX("V' MSB to Tx", recidinfo->V_PRIME, sizeof(recidinfo->V_PRIME));

	return HDCP2_OK;
}

/**
 * @fn int TZ_RepeaterAuth_Send_Ack_Rep(const uint8_t command, uint8_t *request,const u32 req_size, uint8_t *response, uint32_t *res_size);
 * @brief This function receives an acknowledgment from the transmitter
 * @param command - The command to be executed, sent from NWD
 * @param request- pointer to request containing REPEATERAUTH_SEND_ACK message
 * @param req_size - size of request
 * @param response- pointer to response
 * @param res_size - size of response
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_RepeaterAuth_Send_Ack_Rep(const u8 command, u8 *request,
			const u32 req_size, u8 *response, u32 *res_size)
{
	REPEATERAUTH_SEND_ACK *send_ack = (REPEATERAUTH_SEND_ACK *) request;
	NULL_ERR(send_ack, HDCP2_ERR_NULL_RESPONSE);

	if (memcmp(send_ack->V, hdcp2_ctx.repeater_ack, 16))
		hdcp2_ctx.reauth_request = 0x01; //request for reauthentication

	return HDCP2_OK;
}

/**
 * @fn int TZ_Receiver_AuthStatus_Rep(const uint8_t command, uint8_t *request,const u32 req_size, uint8_t *response, uint32_t *res_size);
 * @brief This function lets the transmitter know about the authentication status of receiver with the repeater
 * @param command - The command to be executed, sent from NWD
 * @param request- pointer to request containing REPEATERAUTH_SEND_ACK message
 * @param req_size - size of request
 * @param response- pointer to response
 * @param res_size - size of response
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_Receiver_AuthStatus_Rep(const u8 command, u8 *request,
			const u32 req_size, u8 *response, u32 *res_size)
{
	u8 LENGTH[2] = { 0X00, 0x04 }; //default value in HDCP 2.1
	RECEIVER_AUTHSTATUS *status = (RECEIVER_AUTHSTATUS *) response;
	NULL_ERR(status, HDCP2_ERR_NULL_REQUEST);
	*res_size = sizeof(RECEIVER_AUTHSTATUS);

	status->msg_id = command; //0x12;
	memcpy(status->LENGTH, LENGTH, sizeof(status->LENGTH));
	status->REAUTH_Req = hdcp2_ctx.reauth_request;

	return HDCP2_OK;
}

/**
 * @fn int TZ_RepeaterAuth_Stream_Manage_Rep(const uint8_t command, uint8_t *request,const u32 req_size, uint8_t *response, uint32_t *res_size);
 * @brief This function propagates content stream management information, which includes Type values assigned to the content streams
 * @param command - The command to be executed, sent from NWD
 * @param request- pointer to request containing REPEATERAUTH_STREAM_MANAGE message
 * @param req_size - size of request
 * @param response- pointer to response
 * @param res_size - size of response
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_RepeaterAuth_Stream_Manage_Rep(const u8 command, u8 *request,
			const u32 req_size, u8 *response, u32 *res_size)
{
	if (req_size != sizeof(REPEATERAUTH_STREAM_MANAGE)) {
		LOGE("Error, RepeaterAuth data size (%d)\n", req_size);
		return HDCP2_ERR;
	}

	u8 kd[32] = { 0 };
	u8 input[115] = { 0 };
	u8* temp_ptr = input;
	int i = 0;
	int ret = 0;

	//hdcp2_ctx.type1_stream = 0;
	REPEATERAUTH_STREAM_MANAGE *send_msg = (REPEATERAUTH_STREAM_MANAGE *) request;
	NULL_ERR(send_msg, HDCP2_ERR_NULL_RESPONSE);

	if (send_msg->k[1] > 16) {
		LOGE("Error, K max size\n");
		return HDCP2_ERR;
	}

	//size is hdcp2_ctx.no_of_streams*7 + 3
	//calculate input
	for (i = 0; i < send_msg->k[1]; i++) {
		memcpy(temp_ptr, send_msg->max_k_info[i].streamCtrj,
				sizeof(send_msg->max_k_info[i].streamCtrj));
		memcpy(temp_ptr + sizeof(send_msg->max_k_info[i].streamCtrj),
				send_msg->max_k_info[i].ContentStreamIDj, sizeof(send_msg->max_k_info[i].ContentStreamIDj));
		memcpy( temp_ptr + sizeof(send_msg->max_k_info[i].streamCtrj)
						+ sizeof(send_msg->max_k_info[i].ContentStreamIDj),
				&(send_msg->max_k_info[i].Type), sizeof(send_msg->max_k_info[i].Type));
		temp_ptr = temp_ptr + sizeof(send_msg->max_k_info[i].streamCtrj)
				+ sizeof(send_msg->max_k_info[i].ContentStreamIDj) + sizeof(send_msg->max_k_info[i].Type);

		if (send_msg->max_k_info[i].Type == 0x01) {
			hdcp2_ctx.type1_stream = 1;
			hdcp2_ctx.type1_set = 1;
			//TZ_HDCP_LOG("hdcp2_ctx.type1_stream is set to 1");
		} else {
			hdcp2_ctx.type1_stream = 0;
		}
	}

	memcpy(temp_ptr, send_msg->seq_num_M, sizeof(send_msg->seq_num_M));

	ret = TZ_SHA256(kd, (uint8_t*) &hdcp2_ctx.kd, 32);
	if (ret < HDCP2_OK)
		return ret;

	TZ_HMAC_SHA256(hdcp2_ctx.M, kd, sizeof(kd), input, (send_msg->k[1] * 7 + 3));

	return HDCP2_OK;
}

/**
 * @fn int TZ_RepeaterAuth_Stream_Ready_Rep(const uint8_t command, uint8_t *request,const u32 req_size, uint8_t *response, uint32_t *res_size);
 * @brief This function sends M- prime to the transmitter
 * @param command - The command to be executed, sent from NWD
 * @param request- pointer to request
 * @param req_size - size of request
 * @param response- pointer to response containing REPEATERAUTH_STREAM_READY message
 * @param res_size - size of response
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_RepeaterAuth_Stream_Ready_Rep(const u8 command, u8 *request,
			const u32 req_size, u8 *response, u32 *res_size)
{
	REPEATERAUTH_STREAM_READY *status = (REPEATERAUTH_STREAM_READY *) response;
	NULL_ERR(status, HDCP2_ERR_NULL_REQUEST);
	*res_size = sizeof(REPEATERAUTH_STREAM_READY);

	status->msg_id = command;
	memcpy(status->M_PRIME, hdcp2_ctx.M, sizeof(hdcp2_ctx.M));

	if (hdcp2_ctx.type1_stream == 1) {
		return HDCP2_TYPE1_CONTENT;
	} else if ((hdcp2_ctx.type1_set == 1) && (hdcp2_ctx.type1_stream == 0)) {
	   hdcp2_ctx.type1_set = 0;
	   return HDCP2_TYPE0_CONTENT;
	} else {
		return HDCP2_OK;
	}
}

#define SCATTERED_SIZE 1024*1024
#define MAX_INP_CTR SCATTERED_SIZE / 16

/*
 * Copy input to output
 */
int TZ_SPSPPS_COPY_R(uint8_t command, uint8_t *request, const u32 req_size,
			uint8_t *response, uint32_t *res_size)
{
	int ret = 0;
	int dec_ret = HDCP2_ERR;
	unsigned char p[DECRYPT_BLK_SIZE] = {0};
	unsigned char *input = NULL;
	unsigned char *output = NULL;
	u64 input_ctr = 0;
	uint32 index = 0;
	int tmp_size = 0;
	tz_buf_array_s_t *in_addrs = NULL;
	tz_buf_array_s_t *out_addrs = NULL;
	u8 *in_addr = NULL;
	u8 *out_addr = NULL;
	int out_size = -1;
	int in_size = -1;
	int max_count = 0;

	NULL_ERR(request, HDCP2_TZ_NULL_REQUEST);
	*res_size = 0;

#ifdef CONFIG_HDCP_64BIT
	if (req_size != sizeof(CIP_DATA_INFO_ION_RX) - 8) {
		LOGE("Error, SPSPPS data size (%d)\n", req_size);
		return HDCP2_ERR;
	}
	CIP_DATA_INFO_ION_RX *data = (CIP_DATA_INFO_ION_RX *)(((uint8_t*)request));
#else
	if (req_size != sizeof(CIP_DATA_INFO_RX)) {
		LOGE("Error, SPSPPS data size (%d)\n", req_size);
		return HDCP2_ERR;
	}
	CIP_DATA_INFO_RX *data = (CIP_DATA_INFO_RX *)(((uint8_t*)request));
#endif /* CONFIG_HDCP_64BIT */

	max_count = (int)((data->length + SCATTERED_SIZE - 1) >> 20);  //(int)(data->length) / SCATTERED_SIZE;

#ifdef CONFIG_HDCP_64BIT
	in_addrs = (tz_buf_array_s_t *)(&(data->input_data));
	out_addrs = (tz_buf_array_s_t *)(&(data->output_data));
#else
	in_addrs = (tz_buf_array_s_t *)(&(data->input));
	out_addrs = (tz_buf_array_s_t *)(&(data->output));
#endif /* CONFIG_HDCP_64BIT */

	for (index = 0; index < max_count; index++) {
		if (in_size != 0) {   //0 means it is continous memory
			in_addr = (uint8*)SegAddress(MemSeg(in_addrs, index));
			in_size = SegSize(MemSeg(in_addrs, index));
		} else {
			in_addr = in_addr + SCATTERED_SIZE;
		}
		if (out_size != 0) {
			out_addr = (uint8*)SegAddress(MemSeg(out_addrs, index));
			out_size = SegSize(MemSeg(out_addrs, index));
		} else {
			out_addr = out_addr + SCATTERED_SIZE;
		}

		*res_size = data->length > SCATTERED_SIZE * (index + 1) ? SCATTERED_SIZE : data->length;
		if (index > 0)
			*res_size = data->length - SCATTERED_SIZE * index;
		tmp_size = *res_size;
		input_ctr = data->inp_ctr + (index * MAX_INP_CTR);

		input = in_addr;
		output = out_addr;

		//Input buffer
		if (qsee_register_shared_buffer(input, tmp_size) < 0) {
			LOGE("TZ_SPSPPS_COPY_R input qsee_register_shared_buffer failed");
			*res_size = 0;
			return HDCP2_ERR_BUFFER_PROTECTION;
		}

		if (qsee_prepare_shared_buf_for_secure_read(input, tmp_size) < 0) {
			LOGE("TZ_SPSPPS_COPY_R input qsee_prepare_shared_buf_for_secure_read failed");
			qsee_deregister_shared_buffer((void *)input);
			*res_size = 0;
			return HDCP2_ERR_BUFFER_PROTECTION;
		}

		//Output buffer
		if (qsee_register_shared_buffer(output, tmp_size) < 0) {
			LOGE("TZ_SPSPPS_COPY_R output qsee_register_shared_buffer failed");
			qsee_deregister_shared_buffer((void *)input);
			qsee_prepare_shared_buf_for_nosecure_read((void *)input, tmp_size);
			*res_size = 0;
			return HDCP2_ERR_BUFFER_PROTECTION;
		}
		if (qsee_prepare_shared_buf_for_secure_read(output, tmp_size) < 0) {
			LOGE("TZ_SPSPPS_COPY_R output qsee_prepare_shared_buf_for_secure_read failed");
			qsee_prepare_shared_buf_for_nosecure_read((void *)input, tmp_size);
			qsee_deregister_shared_buffer((void *)input);
			qsee_deregister_shared_buffer((void *)output);
			*res_size = 0;
			return HDCP2_ERR_BUFFER_PROTECTION;
		}

		// Chech output buffer is secure or not.
#ifdef CONFIG_MSM8996
		if (qsee_is_s_tag_area(AC_VM_CP_BITSTREAM, (uint64)output, (uint64)(output +  tmp_size)) == false)
#else
		if (qsee_is_s_tag_area(QSEE_MEM_TAG_USECASE_CP, (uint32)output, (uint32)(output +  tmp_size)) == false)
#endif /* CONFIG_MSM8996 */
		{
			ret = HDCP2_ERR_BUFFER_PROTECTION;
			LOGE("Output buffer is not secure");
			goto DEREGISTER;
		}

		memcpy(output , input, tmp_size);

		//Dumping the output
		TZ_LOG_HEX("inside spspps_copy: data recved on swd side : ", output, 50);
		LOGI("tmp_size is: %d", tmp_size);

		*res_size = 0; //For error return

DEREGISTER:
		//Input buffer
		if ((qsee_prepare_shared_buf_for_nosecure_read((void *)input, tmp_size)) < 0) {
			ret = HDCP2_ERR_BUFFER_PROTECTION;
			LOGE("TZ_SPSPPS_COPY_R input qsee_prepare_shared_buf_for_nosecure_read failed");
		}
		if (qsee_deregister_shared_buffer((void *)input) < 0) {
			ret = HDCP2_ERR_BUFFER_PROTECTION;
			LOGE("TZ_SPSPPS_COPY_R input qsee_deregister_shared_buffer failed");
		}

		//Output buffer
		if (qsee_prepare_shared_buf_for_nosecure_read((void *)output, tmp_size) < 0) {
			ret = HDCP2_ERR_BUFFER_PROTECTION;
			LOGE("TZ_SPSPPS_COPY_R output qsee_prepare_shared_buf_for_nosecure_read failed");
		}
		if (qsee_deregister_shared_buffer((void *)output) < 0) {
			ret = HDCP2_ERR_BUFFER_PROTECTION;
			LOGE("TZ_SPSPPS_COPY_R output qsee_deregister_shared_buffer failed");
		}
	}

	return ret;
}

/**
 * @fn int TZ_DEC_Data_ION(const uint8_t command, uint8_t *request,const u32 req_size, uint8_t *response, uint32_t *res_size);
 * @brief This function decrypts the encrypted data stream for ION memory.
 * @param command - The command to be executed, sent from NWD
 * @param request- pointer to request containing CIP_DATA_INFO_RX message
 * @param req_size - size of request
 * @param response- pointer to response
 * @param res_size - size of response
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_DEC_Data_ION(uint8_t command, uint8_t *request, const u32 req_size,
			uint8_t *response, uint32_t *res_size)
{
	int ret = 0;
	int dec_ret = HDCP2_ERR;
	unsigned char p[DECRYPT_BLK_SIZE] = {0};
	unsigned char *input = NULL;
	unsigned char *output = NULL;
	u64 input_ctr = 0;
	uint32 index = 0;
	int tmp_size = 0;
	tz_buf_array_s_t *in_addrs = NULL;
	tz_buf_array_s_t *out_addrs = NULL;
	u8 *in_addr = NULL;
	u8 *out_addr = NULL;
	int out_size = -1;
	int in_size = -1;
	int max_count = 0;
#ifdef CONFIG_HDCP_64BIT
	CIP_DATA_INFO_ION_RX *data = (CIP_DATA_INFO_ION_RX *)(((uint8_t*)request));
#else
	CIP_DATA_INFO_RX *data = (CIP_DATA_INFO_RX *)(((uint8_t*)request));
#endif /* CONFIG_HDCP_64BIT */

	*res_size = 0;

	if (!data || !data->length || !data->output) {
		LOGE("TZ_DEC_Data: Error, Invalid Input\n");
		return HDCP2_ERR_INVALID_INPUT;
	}

	if (data->length > MAX_ENCRYPT_BUFFER) {
		LOGE("Error, Data max size\n");
		return HDCP2_ERR_INVALID_INPUT;
	}

	if (req_size != ((TZ_OUT_BUF_MAX == 512) ? (sizeof(CIP_DATA_INFO_ION_RX)-8) : sizeof(CIP_DATA_INFO_RX))) {
		LOGE("TZ_ENC_Data: Error, Invalid Req size (%d)\n", req_size);
		return HDCP2_ERR_INVALID_INPUT;
	}

	max_count = (int)((data->length + SCATTERED_SIZE - 1) >> 20);	//(int)(data->length) / SCATTERED_SIZE;

#ifdef CONFIG_HDCP_64BIT
	in_addrs = (tz_buf_array_s_t *)(&(data->input_data));
	out_addrs = (tz_buf_array_s_t *)(&(data->output_data));
#else
	in_addrs = (tz_buf_array_s_t *)(&(data->input));
	out_addrs = (tz_buf_array_s_t *)(&(data->output));
#endif /* CONFIG_HDCP_64BIT */

	for (index = 0; index < max_count; index++) {
		if (in_size != 0) {   //0 means it is continous memory
			in_addr = (uint8*)SegAddress(MemSeg(in_addrs, index));
			in_size = SegSize(MemSeg(in_addrs, index));
		} else {
			in_addr = in_addr + SCATTERED_SIZE;
		}
		if (out_size != 0) {
			out_addr = (uint8*)SegAddress(MemSeg(out_addrs, index));
			out_size = SegSize(MemSeg(out_addrs, index));
		} else {
			out_addr = out_addr + SCATTERED_SIZE;
		}

		*res_size = data->length > SCATTERED_SIZE * (index + 1) ? SCATTERED_SIZE : data->length;
		if (index > 0)
			*res_size = data->length - SCATTERED_SIZE * index;
		tmp_size = *res_size;
		input_ctr = data->inp_ctr + (index * MAX_INP_CTR);

		input = in_addr;
		output = out_addr;

		p[0] = hdcp2_ctx.riv[0];
		p[1] = hdcp2_ctx.riv[1];
		p[2] = hdcp2_ctx.riv[2];
		p[3] = hdcp2_ctx.riv[3];
		p[4] = hdcp2_ctx.riv[4] ^ (u8)((data->str_ctr >> 24) & 0xff);
		p[5] = hdcp2_ctx.riv[5] ^ (u8)((data->str_ctr >> 16) & 0xff);
		p[6] = hdcp2_ctx.riv[6] ^ (u8)((data->str_ctr >> 8) & 0xff);
		p[7] = hdcp2_ctx.riv[7] ^ (u8)(data->str_ctr & 0xff);
		p[8] = (u8)((input_ctr>> 56) & 0xff);
		p[9] = (u8)((input_ctr >> 48) & 0xff);
		p[10] = (u8)((input_ctr>> 40) & 0xff);
		p[11] = (u8)((input_ctr >> 32) & 0xff);
		p[12] = (u8)((input_ctr >> 24) & 0xff);
		p[13] = (u8)((input_ctr>> 16) & 0xff);
		p[14] = (u8)((input_ctr>> 8) & 0xff);
		p[15] = (u8)(input_ctr& 0xff);

		//Input buffer
		if (qsee_register_shared_buffer(input, tmp_size) < 0) {
			LOGE("TZ_DEC_Data_ION input qsee_register_shared_buffer failed");
			return HDCP2_ERR_BUFFER_PROTECTION;
		}

		if (qsee_prepare_shared_buf_for_secure_read(input, tmp_size) < 0) {
			LOGE("TZ_DEC_Data_ION input qsee_prepare_shared_buf_for_secure_read failed");
			qsee_deregister_shared_buffer((void *)input);
			return HDCP2_ERR_BUFFER_PROTECTION;
		}

		//Output buffer
		if (qsee_register_shared_buffer(output, tmp_size) < 0) {
			LOGE("TZ_DEC_Data_ION output qsee_register_shared_buffer failed");
			qsee_deregister_shared_buffer((void *)input);
			qsee_prepare_shared_buf_for_nosecure_read((void *)input, tmp_size);
			return HDCP2_ERR_BUFFER_PROTECTION;
		}
		if (qsee_prepare_shared_buf_for_secure_read(output, tmp_size) < 0) {
			LOGE("TZ_DEC_Data_ION output qsee_prepare_shared_buf_for_secure_read failed");
			qsee_prepare_shared_buf_for_nosecure_read((void *)input, tmp_size);
			qsee_deregister_shared_buffer((void *)input);
			qsee_deregister_shared_buffer((void *)output);
			return HDCP2_ERR_BUFFER_PROTECTION;
		}

	/* Check output buffer is secure or not. */
#ifdef CONFIG_MSM8996
		if (qsee_is_s_tag_area(AC_VM_CP_BITSTREAM, (uint64)output, (uint64)(output + tmp_size)) == false)
#else
		if (qsee_is_s_tag_area(QSEE_MEM_TAG_USECASE_CP, (uint32)output, (uint32)(output + tmp_size)) == false)
#endif /* CONFIG_MSM8996 */
		{
			ret = HDCP2_ERR_BUFFER_PROTECTION;
			LOGE("Output buffer is not secure");
			goto DEREGISTER;
		}

		dec_ret = TZ_AES_decrypt(s_pKey, DECRYPT_BLK_SIZE, input, tmp_size,
						(u8*)(((char*)output)), res_size, p, QSEE_CIPHER_MODE_CTR);

		if (*res_size != tmp_size) {
			LOGE("TZ_DEC_Data_ION decrypted size is different from input size");
			ret = HDCP2_ERR_CRYPTO;
		}
		*res_size = 0; //For error return

		//TZ_LOG_HEX("TZ_DEC_Data_ION : output", output, 64);

DEREGISTER:
		//Input buffer
		if ((qsee_prepare_shared_buf_for_nosecure_read((void *)input, tmp_size)) < 0) {
			ret = HDCP2_ERR_BUFFER_PROTECTION;
			LOGE("TZ_DEC_Data_ION input qsee_prepare_shared_buf_for_nosecure_read failed");
		}
		if (qsee_deregister_shared_buffer((void *)input) < 0) {
			ret = HDCP2_ERR_BUFFER_PROTECTION;
			LOGE("TZ_DEC_Data_ION input qsee_deregister_shared_buffer failed");
		}

		//Output buffer
		if (qsee_prepare_shared_buf_for_nosecure_read((void *)output, tmp_size) < 0) {
			ret = HDCP2_ERR_BUFFER_PROTECTION;
			LOGE("TZ_DEC_Data_ION output qsee_prepare_shared_buf_for_nosecure_read failed");
		}
		if (qsee_deregister_shared_buffer((void *)output) < 0) {
			ret = HDCP2_ERR_BUFFER_PROTECTION;
			LOGE("TZ_DEC_Data_ION output qsee_deregister_shared_buffer failed");
		}
	}

	if (dec_ret != HDCP2_OK) {
		LOGE("TZ_DEC_Data_ION decryption failed");
		ret =  HDCP2_ERR_CRYPTO;
	}

	return ret;
}

/**
 * @fn void TZ_Free_Resource_R()
 * @brief This function free hdcp2_ctx and hdcp2_key
 */
void TZ_Free_Resource_R()
{
	memset(&hdcp2_ctx, 0, sizeof(hdcp2_ctx));
	memset(&hdcp2_key, 0, sizeof(hdcp2_key));
}

