/**
 * 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 <string.h>
#include <unistd.h>
#include <limits.h>

#include "tz_hdcp2_crypto.h"
#include "tz_hdcp2.h"

#ifdef USE_MTK
#include "drSecMemApi.h"
#endif /* USE_MTK */

#ifdef TEEGRIS_V4
#define PHYS_DEV_NAME "/dev/phys"
#else
#define PHYS_DEV_NAME "phys://"
#endif

/**
 * @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);

/**
 * @def MAX_SPS_PPS_BUFFER_SIZE
 * This macro is assigned a value of 512 to be maintained everywhere.
 */
#define MAX_SPS_PPS_BUFFER_SIZE	512

static TZ_HDCP2_CTX hdcp2_ctx;
static HDCP2_KEY hdcp2_key;


/**
 * @fn int TZ_HW_Init_R(const uint8_t command, uint8_t *request,const u32 req_size, uint8_t *response, uint32_t *res_size);
 * @brief This function opens the crypto dev used later during Decryption
 * @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_HW_Init_R(const u8 command, u8 *request, const u32 req_size,
			u8 *response, u32 *res_size)
{
	return TZ_HW_Init_T(command, request, req_size, response, res_size);
}

int TZ_HW_Close_R(const u8 command, u8 *request, const u32 req_size,
			u8 *response, u32 *res_size)
{
	return TZ_HW_Close_T(command, request, req_size, response, res_size);
}

/**
 * @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)
{
	return TZ_HDCP2_LOADKEY(request, req_size, &hdcp2_key);
}

/**
 * @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_SET_HDCP_VERSION_R(const u8 command, u8 *request, const u32 req_size,
			u8 *response, u32 *res_size)
{
	NULL_ERR(request, HDCP2_TZ_NULL_REQUEST);
	if (req_size != 2 /* the buffer size for hdcp_version */) {
		LOGE("TZ_SET_HDCP_VERSION_R: Error, Invalid Req size (%d)\n", req_size);
		return HDCP2_ERR_INVALID_INPUT;
	}

	hdcp2_ctx.version = request[1];
	LOGI("TZ_SET_HDCP_VERSION_R : HDCP %d.%d version is setuped\n",
			(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)
{
	if (req_size != sizeof(AKE_INIT)) {
		LOGE("Error,AKE_INIT data size (%d)\n", req_size);
		return HDCP2_ERR;
	}
	AKE_INIT *p = (AKE_INIT *) request;
	NULL_ERR(p, HDCP2_TZ_NULL_RESPONSE);

	/* Reset context */
	TEE_MemFill(&hdcp2_ctx, 0, sizeof(TZ_HDCP2_CTX));
	TEE_MemMove(hdcp2_ctx.rtx, p->rtx, sizeof(hdcp2_ctx.rtx));

	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)
{
	if (req_size != sizeof(AKE_TRANSMITTER_INFO)) {
		LOGE("Error,AKE_TRANSMITTER_INFO data size (%d)\n", req_size);
		return HDCP2_ERR;
	}
	AKE_TRANSMITTER_INFO *p = (AKE_TRANSMITTER_INFO *) request;
	NULL_ERR(p, HDCP2_TZ_NULL_REQUEST);

	TEE_MemMove(&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;

	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)
{
	if (*res_size != sizeof(AKE_SEND_CERT)) {
		LOGE("Error, AKE CERT data size (%d)\n", *res_size);
		return HDCP2_ERR;
	}
	
	if(!TZ_HDCP2_Get_Status_UnwrapKey()) {
		LOGE("TZ_AKE_Send_Cert_R: TZ_HDCP2_Get_Status_UnwrapKey return failed");
		return HDCP2_ERR_UNWRAPKEY;
	}
	AKE_SEND_CERT *p = (AKE_SEND_CERT *) response;
	*res_size = sizeof(AKE_SEND_CERT);

	NULL_ERR(p, HDCP2_TZ_NULL_RESPONSE);
	p->msg_id = command;
	TEE_MemMove(p->certrx, &hdcp2_key.cert, sizeof(p->certrx));

	/*
	 * To support repeater we must check this value
	 */
	if (*request == 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)
{
	if (*res_size != sizeof(AKE_RECEIVER_INFO)) {
		LOGE("Error, AKE_RECEIVER_INFO data size (%d)\n", *res_size);
		return HDCP2_ERR;
	}
	AKE_RECEIVER_INFO *p = (AKE_RECEIVER_INFO *) response;

	NULL_ERR(p, HDCP2_TZ_NULL_RESPONSE);
	TEE_MemFill(p, 0, *res_size);
	p->msg_id = command;
	p->LENGTH[1] = 6; // HDCP 2.1
	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;

	LOGI("TZ_AKE_Receiver_Info_R : receiver_info.VERSION : 0x%02x\n",
			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] ;
	int ret = HDCP2_OK;

	if (req_size != sizeof(AKE_NO_STORED_KM)) {
		LOGE("Error, AKE_NO_STORED_KM data size (%d)\n", req_size);
		return HDCP2_ERR;
	}
	if(!TZ_HDCP2_Get_Status_UnwrapKey()) {
		LOGE("TZ_AKE_No_Store_km_R: TZ_HDCP2_Get_Status_UnwrapKey return failed");
		return HDCP2_ERR_UNWRAPKEY;
	}
	
	AKE_NO_STORED_KM *p = (AKE_NO_STORED_KM *) request;
	NULL_ERR(p, HDCP2_TZ_NULL_REQUEST);
	TEE_MemFill(dec,0,HDCP2_SIZE_RECEIVER_PUBKEY);

	// 1. Decrypt Ekpub_km
	if ((ret = TZ_RSA_OAEP_decrypt(&hdcp2_ctx, &hdcp2_key, p->Ekpub_km, dec)) < 0)
		return ret;

	// 2. Copy decrypted buffer to hdcp2_ctx.pairing_info.km
	TEE_MemMove(hdcp2_ctx.pairing_info.km, dec, sizeof(hdcp2_ctx.pairing_info.km));

	TZ_LOG_HEX("km", hdcp2_ctx.pairing_info.km, sizeof(hdcp2_ctx.pairing_info.km));
	TEE_MemFill(dec, 0x0, HDCP2_SIZE_RECEIVER_PUBKEY);

	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)
{
	int ret = HDCP2_OK;
	u32 length = 16;
	uint8_t kh[32] = {0, };
	AKE_STORED_KM *p = (AKE_STORED_KM *) request;
	NULL_ERR(p, HDCP2_TZ_NULL_REQUEST);

	if (req_size != sizeof(AKE_STORED_KM)){
		LOGE("Error, AKE_STORED_KM data size (%d)\n", req_size);
		return HDCP2_ERR;
	}
	if(!TZ_HDCP2_Get_Status_UnwrapKey()) {
		LOGE("TZ_AKE_Store_km_R: TZ_HDCP2_Get_Status_UnwrapKey return failed");
		return HDCP2_ERR_UNWRAPKEY;
	}
	
	// 1. Copy payload
	TEE_MemMove(hdcp2_ctx.pairing_info.m, p->m, sizeof(p->m));
	TEE_MemMove(hdcp2_ctx.pairing_info.Ekm, p->Ekh_km, sizeof(p->Ekh_km));

	// 2. Decrypt and get km
	TZ_SHA256(kh, (uint8_t *) &hdcp2_key.private_key, 128);
	ret = TZ_AES_ECB_decrypt(kh, 16, p->Ekh_km, 16, hdcp2_ctx.pairing_info.km, &length);

	TZ_LOG_HEX("kh", kh, 32);
	TZ_LOG_HEX("km", (uint8_t *) hdcp2_ctx.pairing_info.km, sizeof(hdcp2_ctx.pairing_info.km));
	TEE_MemFill(kh, 0x0, 32);

	return ret;
}

/**
 * @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)
{
	if (*res_size != sizeof(AKE_SEND_RRX)) {
		LOGE("Error, AKE_SEND_RRX data size (%d)\n", *res_size);
		return HDCP2_ERR;
	}
	AKE_SEND_RRX *p = (AKE_SEND_RRX *) response;

	NULL_ERR(p, HDCP2_TZ_NULL_RESPONSE);
	TZ_rand(hdcp2_ctx.rrx, sizeof(hdcp2_ctx.rrx));

	// Make payload
	p->msg_id = command;
	TEE_MemMove(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, };

	if (*res_size != sizeof(AKE_SEND_H_PRIME)) {
		LOGE("Error, AKE_SEND_H_PRIME data size (%d)\n", *res_size);
		return HDCP2_ERR;
	}
	if(!TZ_HDCP2_Get_Status_UnwrapKey()) {
		LOGE("TZ_AKE_Send_h_prime_R: TZ_HDCP2_Get_Status_UnwrapKey return failed");
		return HDCP2_ERR_UNWRAPKEY;
	}
	
	AKE_SEND_H_PRIME *p = (AKE_SEND_H_PRIME *) response;

	NULL_ERR(p, HDCP2_TZ_NULL_RESPONSE);

	// Derivate kd = dkey0 || dkey1
	TZ_Derivate_dkey(&hdcp2_ctx);
	TEE_MemMove(hdcp2_ctx.kd, hdcp2_ctx.dkey, 16);
	TZ_Derivate_dkey(&hdcp2_ctx);
	TEE_MemMove(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, };
		TEE_MemFill(temp_input, 0, sizeof(temp_input));
		TEE_MemMove(temp_input, hdcp2_ctx.rtx, sizeof(hdcp2_ctx.rtx));
		temp_input[7] ^= hdcp2_ctx.REPEATER;
		temp_input[8] = hdcp2_ctx.receiver_info.VERSION;
		TEE_MemMove(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;
		TEE_MemMove(temp_input + 12, hdcp2_ctx.transmitter_info.TRANSMITTER_CAPABILITY_MASK, sizeof(hdcp2_ctx.transmitter_info.TRANSMITTER_CAPABILITY_MASK));
		TZ_LOG_HEX("temp_input", temp_input, sizeof(temp_input));

		//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 {
		TEE_MemMove(input, hdcp2_ctx.rtx, sizeof(hdcp2_ctx.rtx));
		input[7] ^= hdcp2_ctx.REPEATER;
		TZ_LOG_HEX("input", input, 8);

		// HMAC-SHA256(rtx XOR REPEATER, kd)
		TZ_HMAC_SHA256(p->H, hdcp2_ctx.kd, sizeof(hdcp2_ctx.kd), input, sizeof(input));
	}

	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));
	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;

	if (*res_size != sizeof(AKE_SEND_PAIRING_INFO)) {
		LOGE("Error, AKE_SEND_PAIRING_INFO data size (%d)\n", *res_size);
		return HDCP2_ERR;
	}
	if(!TZ_HDCP2_Get_Status_UnwrapKey()) {
		LOGE("TZ_AKE_Send_Pairing_Info_R: TZ_HDCP2_Get_Status_UnwrapKey return failed");
		return HDCP2_ERR_UNWRAPKEY;
	}
	
	AKE_SEND_PAIRING_INFO *p = (AKE_SEND_PAIRING_INFO *) response;
	uint8_t kh[32] = {0, };

	NULL_ERR(p, HDCP2_TZ_NULL_RESPONSE);

	// make Ekh_km
	p->msg_id = command;
	TZ_SHA256(kh, (uint8_t *) &hdcp2_key.private_key, 128);

	// encrypt
	TZ_AES_ECB_encrypt(kh, 16, hdcp2_ctx.pairing_info.km, 16, p->Ekh_Km, &length);
	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, };
	if (req_size != sizeof(LC_INIT)) {
		LOGE("Error, LC_INIT data size (%d)\n", req_size);
		return HDCP2_ERR;
	}
	LC_INIT *p = (LC_INIT *) request;

	NULL_ERR(p, HDCP2_TZ_NULL_REQUEST);

	// Copy payload
	TEE_MemMove(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
		TEE_MemMove(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};
			TEE_MemMove(temp_rn, hdcp2_ctx.rn, sizeof(hdcp2_ctx.rn));
			TEE_MemMove(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));
		}

		TEE_MemMove(hdcp2_ctx.L_msb, L , 16); //L msb
		TEE_MemMove(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)
{
	if (*res_size != sizeof(RTT_READY)) {
		LOGE("Error, RTT_READY data size (%d)\n", *res_size);
		return HDCP2_ERR;
	}
	RTT_READY *p = (RTT_READY *) response;
	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)
{
	if (req_size != sizeof(RTT_CHALLENGE)) {
		LOGE("Error, RTT_CHALLENGE data size (%d)\n", req_size);
		return HDCP2_ERR;
	}
	RTT_CHALLENGE *rtt = (RTT_CHALLENGE *)request;
	NULL_ERR(rtt, HDCP2_TZ_NULL_REQUEST);

	if (TEE_MemCompare(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) {
		if (*res_size != sizeof(LC_SEND_L_PRIME_PC)) {
			LOGE("Error, LC_SEND_L_PRIME_PC data size (%d)\n", *res_size);
			return HDCP2_ERR;
		}
		LC_SEND_L_PRIME_PC *p = (LC_SEND_L_PRIME_PC *) response;
		NULL_ERR(p, HDCP2_TZ_NULL_RESPONSE);
		TEE_MemMove(p->L_msb, hdcp2_ctx.L_msb, 16);

		// Make payload
		p->msg_id = command;
	} else {
		if (*res_size != sizeof(LC_SEND_L_PRIME)) {
			LOGE("Error, LC_SEND_L_PRIME data size (%d)\n", *res_size);
			return HDCP2_ERR;
		}
		LC_SEND_L_PRIME *p = (LC_SEND_L_PRIME *) response;
		NULL_ERR(p, HDCP2_TZ_NULL_RESPONSE);
		// key = kd ^ rrx
		TEE_MemMove(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;
		TZ_HMAC_SHA256(p->L, key, sizeof(key), hdcp2_ctx.rn, sizeof(hdcp2_ctx.rn));
	}

	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)
{
	int i = 0;
	uint8_t H[32] = {0, };

	NULL_ERR(request, HDCP2_TZ_NULL_REQUEST);

	if (hdcp2_ctx.version == HDCP2_VERSION_2_3) {
		if (req_size != sizeof(SKE_SEND_EKS_VER23)) {
			LOGE("Error, SKE_SEND_EKS_VER23[%d] != req_size[%d]\n", sizeof(SKE_SEND_EKS_VER23), req_size);
			return HDCP2_ERR;
		}
		SKE_SEND_EKS_VER23 *p = (SKE_SEND_EKS_VER23 *) request;

		// 1. Copy request
		TEE_MemMove(hdcp2_ctx.riv, p->riv, sizeof(hdcp2_ctx.riv));

		// HDCP v2.3: Calculate HMAC of riv and compare with received H
		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)
		TZ_Derivate_dkey(&hdcp2_ctx);
		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 {
		if (req_size != sizeof(SKE_SEND_EKS)) {
			LOGE("Error, SKE_SEND_EKS[%d] != req_size[%d]\n", sizeof(SKE_SEND_EKS), req_size);
			return HDCP2_ERR;
		}
		SKE_SEND_EKS *p = (SKE_SEND_EKS *) request;

		// 1. Copy request
		TEE_MemMove(hdcp2_ctx.riv, p->riv, sizeof(hdcp2_ctx.riv));

		// 2. Key Derivation (dkey2 <= ctr=2)
		TZ_Derivate_dkey(&hdcp2_ctx);
		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));

	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 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, int length, int32_t *bIsKeyFrame, u32 codec_type)
{
	int ret = HDCP2_OK;
	int i;

	if (length > MAX_ENCRYPT_BUFFER) {
		LOGE("Error, Input max data size\n");
		return HDCP2_ERR;
	}

	if (codec_type == 1) { // MPEG4
		for (i = 0; i < (length - 5); 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
				// 2 bit mask : 0 means I FRAME
				if (((output_data[i + 4] >> 6) & 0x3) == 0) {
					*bIsKeyFrame = 1;
					break;
				} else {
					*bIsKeyFrame = 0;
					break;
				}
			}
		}
	} else if (codec_type == 2) { // 264
		for (i = 0; i < length - 5; i++) {
			// start code = 0x00 0x00 0x00 0x01
			if (output_data[i] == 0x00 && output_data[i + 1] == 0x00
				&& output_data[i + 2] == 0x00 && output_data[i + 3] == 0x01) {
				if ((output_data[i + 4] & 0x1f) == 5) { // 5 bit mask : 5 means I FRAME
					*bIsKeyFrame = 2;
					break;
				} else {
					*bIsKeyFrame = 0;
				}
			} else if (output_data[i] == 0x00 && output_data[i + 1] == 0x00
						&& output_data[i + 2] == 0x01) { // start code = 0x00 0x00 0x01
				// 5 bit mask : 5 means I FRAME
				if ((output_data[i + 3] & 0x1f) == 5) {
					*bIsKeyFrame = 3;
					break;
				} else {
					*bIsKeyFrame = 0;
				}
			}
		}
	}

	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 ret = 0;
	unsigned char pKey[16] = {0, };
	unsigned char p[16];
	u8 *output_data = NULL;
	int ion_fd = 0;
	uint64_t phys_fd = 0;
	u8 *input_data = NULL;
	int bIsKeyFrame = 0;

	if (req_size < sizeof(CIP_DATA_INFO_RX) - (ADJUST_POINTER_SIZE * 2)) {
		LOGE("TZ_DEC_Data: Error, Invalid Input (req_size == %d)\n", req_size);
		return HDCP2_ERR_INVALID_INPUT;
	}

	CIP_DATA_INFO_RX *data = (CIP_DATA_INFO_RX *) request;
#ifdef CONFIG_HDCP_64BIT
	CIP_DATA_INFO_RX tmp_data;
	TEE_MemMove(&tmp_data, request, req_size);
	TEE_MemFill(request, 0, req_size);
#endif /* CONFIG_HDCP_64BIT */

#ifdef USE_MTK
	tlApimem_t meminfo;
	uint64_t output_pa;
#endif /* USE_MTK */

	if (!data) {
		LOGE("TZ_DEC_Data: Error, Invalid Input (data == null)\n");
		return HDCP2_ERR_INVALID_INPUT;
	}
	
	if(!TZ_HDCP2_Get_Status_UnwrapKey()) {
		LOGE("TZ_DEC_Data: TZ_HDCP2_Get_Status_UnwrapKey return failed");
		return HDCP2_ERR_UNWRAPKEY;
	}	

#ifdef CONFIG_HDCP_64BIT
	data->str_ctr = tmp_data.str_ctr;
	data->inp_ctr = tmp_data.inp_ctr;

	data->codec_type = *(&(tmp_data.str_ctr) + 7);
	data->dec_type = *(&(tmp_data.str_ctr) + 6);
	data->length = *(&(tmp_data.str_ctr) + 5);
	data->output = *(&(tmp_data.str_ctr) + 4);
	data->input = *(&(tmp_data.str_ctr) + 3);

//	TZ_HDCP_DEBUG("data->dec_type: %d\n", data->dec_type);
//	TZ_HDCP_DEBUG("data->length: %d\n", data->length);
	LOGD("data->input: 0x%llx\n", data->input);
	LOGD("data->output: 0x%llx\n", data->output);
#endif /* CONFIG_HDCP_64BIT */

	if (!data->input || !(&(data->length)) || !data->output || !(&(data->dec_type))) {
		LOGE("TZ_DEC_Data: Error, Invalid Input parameters\n");
		return HDCP2_ERR_INVALID_INPUT;
	}

	if (data->length > MAX_ENCRYPT_BUFFER) {
		LOGE("Error, Data max size\n");
		return HDCP2_ERR_INVALID_INPUT;
	}

#ifndef USE_MTK
	if(data->length != *res_size) {
		LOGE("Data size : %d, Response size : %d\n", data->length, *res_size);
		LOGE("Error, data size is not the same as reponse size\n");
		return HDCP2_ERR_INVALID_INPUT;
	}
#endif /* !USE_MTK */

	if (data->dec_type == DEC_TYPE_VIDEO){
		if (req_size != (sizeof(CIP_DATA_INFO_RX) - (ADJUST_POINTER_SIZE * 2))) {
			LOGE("TZ_DEC_Data: Error, Invalid Req size (%d)\n", req_size);
			return HDCP2_ERR_INVALID_INPUT;
		}
	} else {
		if (req_size != (sizeof(CIP_DATA_INFO_RX) - (ADJUST_POINTER_SIZE * 2) + data->length)) {
			LOGE("TZ_DEC_Data: Error, Invalid Req size (%d)\n", req_size);
			return HDCP2_ERR_INVALID_INPUT;
		}
	}

	*res_size = data->length;

	if (data->dec_type == DEC_TYPE_VIDEO) {
		ion_fd = open(PHYS_DEV_NAME, O_RDWR);
		if (ion_fd < 0) {
			LOGE("failed to open %s : %d....\n",PHYS_DEV_NAME, errno);
			return HDCP2_ERR_BUFFER_PROTECTION;
		}

		phys_fd = (uint64_t)data->input;
		input_data = (u8 *)mmap(NULL, data->length, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_PHYS_NON_SECURE, ion_fd, phys_fd*PAGE_SIZE);

		if (input_data == MAP_FAILED) {
			LOGE("input_data mmap failed : %d\n", errno );
			close(ion_fd);
			return HDCP2_ERR;
		}

		phys_fd = (uint64_t)data->output;

#ifdef USE_MTK
		if (drMemPAQueryByType(phys_fd, &output_pa, MEM_HAPP_SVP) != 0) {
			LOGE("TZ_DEC_Data: Error, mapping error\n");
			close(ion_fd);
			return HDCP2_ERR_INVALID_INPUT;
		}
		if (output_pa == 0) {
			LOGE("TZ_DEC_Data: Error, output address error (%llx)\n", output_pa);
			close(ion_fd);
			return HDCP2_ERR_INVALID_INPUT;
		}
		LOGD("phys_fd: %llx, output_pa : %llx\n", phys_fd, output_pa);
		output_data = (u8 *)mmap(NULL, data->length, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_PHYS_NON_CACHED, ion_fd, output_pa);
#else
		output_data = (u8 *)mmap(NULL, data->length, PROT_READ|PROT_WRITE,  MAP_SHARED|MAP_PHYS_NON_CACHED, ion_fd, phys_fd*PAGE_SIZE);
#endif /* USE_MTK */

		if (output_data == MAP_FAILED) {
			LOGE("mmap failed : %d\n", errno);
			munmap(input_data, data->length);
			close(ion_fd);
			return HDCP2_ERR;
		}
		
		LOGD("output_data : 0x%x\n", (unsigned int)output_data);
	} else {
		NULL_ERR(response, HDCP2_TZ_NULL_RESPONSE);
		input_data = request + sizeof(CIP_DATA_INFO_RX) - (ADJUST_POINTER_SIZE * 2);
		output_data = response;
	}

	if (TZ_Get_ContentKey(hdcp2_ctx.ks, hdcp2_key.lc128, pKey) != 0) {
		ret = HDCP2_ERR_CRYPTO;
		goto err;
	}

//	TZ_LOG_HEX("[Input Data]", input_data, 32);

	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)((data->inp_ctr >> 56) & 0xff);
	p[9] = (u8)((data->inp_ctr >> 48) & 0xff);
	p[10] = (u8)((data->inp_ctr >> 40) & 0xff);
	p[11] = (u8)((data->inp_ctr >> 32) & 0xff);
	p[12] = (u8)((data->inp_ctr >> 24) & 0xff);
	p[13] = (u8)((data->inp_ctr >> 16) & 0xff);
	p[14] = (u8)((data->inp_ctr >> 8) & 0xff);
	p[15] = (u8)(data->inp_ctr & 0xff);

//	TZ_LOG_HEX("pkey===", pKey, 16);

	ret = TZ_Cipher_AES_CTR_Decrypt_HW(input_data, data->length, output_data, res_size, pKey, p);

#ifdef USE_DUMP_RAW_DATA
	for (int i = 0; i < data->length; i++)
		*(input_data+i) = *(output_data+i);
#endif /* USE_DUMP_RAW_DATA */

//	TZ_LOG_HEX("[Output Data]", output_data, 32);

	if (ret != TEE_SUCCESS) {
		LOGE("TZ_Cipher_AES_CTR_Decrypt fail : %d\n", ret);
		goto err;
	}

	LOGD("Decrypted.. type=%d, inlen=%d, ret=%d\n", data->dec_type, data->length, ret);

	if (data->dec_type == DEC_TYPE_CHECK_KEY_FRAME) {
		TZ_Is_Key_Frame((u8 *) output_data, data->length, &bIsKeyFrame, data->codec_type);
		TEE_MemFill(output_data, 0x00, data->length); // erase decrypted data
		*res_size = bIsKeyFrame;
	}

err:
	if (data->dec_type == DEC_TYPE_VIDEO) {
		munmap(output_data, data->length);
		munmap(input_data, data->length);
		close(ion_fd);
	}

	return ret;
}

/**
 * @fn int TZ_SPSPPS_COPY(const uint8_t command, uint8_t *request,const u32 req_size, uint8_t *response, uint32_t *res_size);
 * @brief sps_pps header is copied to secure memory
 * @param command - The command to be executed, sent from NWD
 * @param request- pointer to request containing CIP_DATA_SPSPPS 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_SPSPPS_COPY(const u8 command, u8 *request, u32 req_size,
			u8 *response, u32 *res_size)
{
	NULL_ERR(request, HDCP2_TZ_NULL_REQUEST);
	if (req_size != sizeof(CIP_DATA_SPSPPS) || response != NULL || res_size == NULL || *res_size != 0) {
		LOGE("Error, SPSPPS data size (%d)\n", req_size);
		return HDCP2_ERR;
	}
	int ret = HDCP2_OK;
	CIP_DATA_SPSPPS *data = (CIP_DATA_SPSPPS *) request;
	u8 *output_data = NULL;
	int ion_fd = 0;
	uint64_t phys_fd = 0;
	int length = 0;

	if (data->size > MAX_SPS_BUFFER - 1 || data->size <= 0) {
		LOGE("Error, SPS data size (%d)\n", data->size);
		return -1;
	}

	if (data->split_ctr > INT_MAX - data->size || data->split_ctr < 0) {
		LOGE("Error, SPS data split_ctr size (%d)\n", data->split_ctr);
		return -1;
	}

	length = data->size + data->split_ctr;

	//TZ_HDCP_DEBUG("TZ_SPSPPS_COPY input: %d, size: %d\n", request, req_size);

	ion_fd = open(PHYS_DEV_NAME, O_RDWR);
	if (ion_fd < 0) {
		LOGE("HDCP2: TZ_SPSPPS_COPY : failed to open %s : %d....\n", PHYS_DEV_NAME, errno);
		return -1;
	}

	phys_fd = (uint64_t)data->phy_out;

	output_data = (u8 *)mmap(NULL, length, PROT_READ|PROT_WRITE,
						MAP_SHARED|MAP_PHYS_NON_CACHED/*shared+secure+noncached*/, ion_fd, phys_fd*PAGE_SIZE);
	if (output_data == MAP_FAILED) {
		LOGE("TZ_SPSPPS_COPY : mmap failed : %d\n", errno );
		close(ion_fd);
		return -1;
	}

	// copy data to output
	memcpy(output_data + data->split_ctr, data->spspps, data->size);

	//TZ_HDCP_DEBUG("SPSPPS DATA: ", output_data, data->size);

	munmap(output_data, length);
	close(ion_fd);

	return ret;
}

/**
 * @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 req_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] ;
	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);
	TEE_MemFill(input,0,159);
	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;
	}

	TEE_MemMove(input, recidinfo->RECEIVER_IDs[0].RECEIVER_IDj, (recidinfo->DEVICE_COUNT)*RECEIVER_ID_SIZE);
	offset = (recidinfo->DEVICE_COUNT)*RECEIVER_ID_SIZE;

	TEE_MemMove(input + offset, &(recidinfo->DEPTH), sizeof(recidinfo->DEPTH));
	TEE_MemMove(input + offset + sizeof(recidinfo->DEPTH),
			&(recidinfo->DEVICE_COUNT), sizeof(recidinfo->DEVICE_COUNT));
	TEE_MemMove( input + offset + sizeof(recidinfo->DEPTH) + sizeof(recidinfo->DEVICE_COUNT),
			&(recidinfo->MAX_DEVS_EXCEEDED), sizeof(recidinfo->MAX_DEVS_EXCEEDED));
	TEE_MemMove( 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);

	TEE_MemMove(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] ;
	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);
	TEE_MemFill(input,0,164);
	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;

	TEE_MemMove(recidinfo->seq_num_V, hdcp2_ctx.seq_num_V, sizeof(recidinfo->seq_num_V));

	TEE_MemMove(input, recidinfo->RECEIVER_IDs[0].RECEIVER_IDj, (recidinfo->DEVICE_COUNT)*RECEIVER_ID_SIZE);
	offset = (recidinfo->DEVICE_COUNT)*RECEIVER_ID_SIZE;

	TEE_MemMove(input + offset, &(recidinfo->DEPTH), sizeof(recidinfo->DEPTH));
	TEE_MemMove(input + offset + sizeof(recidinfo->DEPTH),
			&(recidinfo->DEVICE_COUNT), sizeof(recidinfo->DEVICE_COUNT));
	TEE_MemMove(input + offset + sizeof(recidinfo->DEPTH) + sizeof(recidinfo->DEVICE_COUNT),
			&(recidinfo->MAX_DEVS_EXCEEDED), sizeof(recidinfo->MAX_DEVS_EXCEEDED));
	TEE_MemMove(input + offset + sizeof(recidinfo->DEPTH) + sizeof(recidinfo->DEVICE_COUNT) 
					+ sizeof(recidinfo->MAX_DEVS_EXCEEDED),
			&(recidinfo->MAX_CASCADE_EXCEEDED), sizeof(recidinfo->MAX_CASCADE_EXCEEDED));
	TEE_MemMove(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));
	TEE_MemMove(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));
	TEE_MemMove(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));
	TEE_MemMove(recidinfo->V_PRIME, V, 16);
	TEE_MemMove(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 (req_size != sizeof(REPEATERAUTH_SEND_ACK)) {
		LOGE("Error, RepeaterAuth_Send_Ack data size (%d)\n", req_size);
		return HDCP2_ERR;
	}

	if (TEE_MemCompare(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);

	if (*res_size != sizeof(RECEIVER_AUTHSTATUS)) {
		LOGE("Error, Receiver_AuthStatus_Rep size (%d)\n", *res_size);
		return HDCP2_ERR;
	}

	status->msg_id = command; //0x12;
	TEE_MemMove(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] ;
	u8* temp_ptr = input;
	int i = 0;

	//hdcp2_ctx.type1_stream = 0;
	REPEATERAUTH_STREAM_MANAGE *send_msg = (REPEATERAUTH_STREAM_MANAGE *) request;
	NULL_ERR(send_msg, HDCP2_ERR_NULL_RESPONSE);
	TEE_MemFill(input, 0, 115);

	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++) {
		TEE_MemMove(temp_ptr, send_msg->max_k_info[i].streamCtrj,
				sizeof(send_msg->max_k_info[i].streamCtrj));
		TEE_MemMove(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));
		TEE_MemMove(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;
			LOGI("hdcp2_ctx.type1_stream is set to 1\n");
		} else {
			hdcp2_ctx.type1_stream = 0;
		}
	}

	TEE_MemMove(temp_ptr, send_msg->seq_num_M, sizeof(send_msg->seq_num_M));
	TZ_SHA256(kd, (uint8_t*) &hdcp2_ctx.kd, 32);
	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)
{
	if (*res_size != sizeof(REPEATERAUTH_STREAM_READY)) {
		LOGE("Error, RepeaterAuth data size (%d)\n", *res_size);
		return HDCP2_ERR;
	}
	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;
	TEE_MemMove(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;
	}
}
