/**
 * Copyright (C) 2011 Samsung Electronics Co., Ltd. All rights reserved.
 *
 * Mobile Communication Division,
 * Digital Media & Communications Business, Samsung Electronics Co., Ltd.
 *
 * This software and its documentation are confidential and proprietary
 * information of Samsung Electronics Co., Ltd.  No part of the software and
 * documents may be copied, reproduced, transmitted, translated, or reduced to
 * any electronic medium or machine-readable form without the prior written
 * consent of Samsung Electronics.
 *
 * Samsung Electronics makes no representations with respect to the contents,
 * and assumes no responsibility for any errors that might appear in the
 * software and documents. This publication and the contents hereof are subject
 * to change without notice.
 *
 */
 /**
 * @file hdcp2_common.cpp
 * @author
 * @date
 * @brief This file contains the functions to do initialization of HDCP system,
 *        to set AKE retry policy, to store/load/remove pairing info, log functions and functions related to wrapped key.
 */

#include <hdcp2.h>
#include <time.h>
#include <stdio.h>
#include <memory.h>
#include <sys/time.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdarg.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <android/log.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>

#include "log.h"
#include "properties.h"

#ifdef HDCP2_PROFILE_ENCRYPT_TIME
unsigned long long total_duration = 0;
unsigned long long enc_count = 0;
unsigned long long total_lengh = 0;
#endif /* HDCP2_PROFILE_ENCRYPT_TIME */

const char drm_dir[] = "/efs/drm";
const char h2k_key_sfs_dir[] = "/efs/drm/h2k";

/**
 * @fn void HDCP2_Log(const char* format, ...)
 * @brief prints the data into the log file
 * @param format pointer to data that needs to be logged
 * @return void
 */
void HDCP2_Log(const char* format, ...)
{
	va_list args;
	char log_buffer[4096] = {0, };

	va_start(args, format);
	vsprintf(log_buffer, format, args);
	va_end(args);

	__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "%s", log_buffer);
}

/**
 * @fn void HDCP2_LogHex(const char *title, const unsigned char *data, int length);
 * @brief prints the data in hexadecimal form into the log file
 * @param title - pointer to the title
 * @param data - pointer to the actual data to be printed in hexadecimal form
 * @param length - length of data to be printed
 * @return void
 */
void HDCP2_LogHex(const char *title, const unsigned char *data, int length)
{
	char dt[32] = {0, };

#ifdef DEBUG
	int i;
	char buffer[4096] = {0, };
	char dest_buffer[4096] = {0, };

	// print title
	HDCP2_Log("%s[%d] [%s] = ", title, length, dt);

	// print binary data
	for (i = 0; i < length; i++) {
		if (i % 16 == 0 && i != 0) {
			HDCP2_Log("%s\n", buffer);
			buffer[0] = 0;
		}
		sprintf(dest_buffer, "%s%.2x ", buffer, data[i]);
		sprintf(buffer, "%s", dest_buffer);
	}
	sprintf(dest_buffer, "%s\n", buffer);
	sprintf(buffer, "%s\n", dest_buffer);
	HDCP2_Log("%s\n", buffer);
#endif /* DEBUG */
}

/**
 * @fn int HDCP2_Remove_Pair(HDCP2_Ctx *hdcp)
 * @brief deletes pairing info and sets hdcp->is_paired=0
 * @param hdcp - pointer to HDCP context
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int HDCP2_Remove_Pair(HDCP2_Ctx *hdcp)
{
	if (!hdcp)
		return HDCP2_ERR_INVALID_INPUT;

	HDCP2_Log("HDCP2_Remove_Pair: %s\n", hdcp->pair_info_path);
	unlink(hdcp->pair_info_path);
	hdcp->is_paired = 0;

	return HDCP2_OK;
}

/**
 * @fn int HDCP2_Chmod644(const char *path)
 * @brief This function changes the access permissions to 644 of directory path of which is given as argument.
 * @param path - pointer to the path String for the directory.
 * @return int - 0 if success and error code in case of failure.
 */
int HDCP2_Chmod644(const char *path)
{
	int ret = 0;

	ret = chmod(path, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); // chmod 644
	if (ret != 0)
		HDCP2_Log("chmod in HDCP2_Chmod644(): %s", strerror(errno));

	return ret;
}

#ifndef USE_QSEE_WRAP_WITH_SFS
/**
 * @fn int HDCP2_LoadPairingInfo(HDCP2_Ctx *hdcp)
 * @brief loads the pairing info from the system (/data/system/hdcp2)
 * @param hdcp - pointer to HDCP context
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int HDCP2_LoadPairingInfo(HDCP2_Ctx *hdcp)
{
	int ret = 0;
	FILE *fp = 0;
	u8 *receiver_id = NULL;

	if (!hdcp)
		return HDCP2_ERR_INVALID_INPUT;

	receiver_id = hdcp->receiver_id;

	snprintf(hdcp->pair_info_path, MAX_PATH_LEN, "%s/%02x%02x%02x%02x%02x_%d.hdcp2",
			hdcp->pair_info_dir, receiver_id[0], receiver_id[1],
			receiver_id[2], receiver_id[3], receiver_id[4], getuid());

	HDCP2_DEBUG("Load pairing information from %s\n", hdcp->pair_info_path);

	/*if (access(hdcp->pair_info_path, R_OK | W_OK))
	 return HDCP2_NO_PAIRING_INFO;*/
	fp = fopen(hdcp->pair_info_path, "rb");
	if (!fp) {
		HDCP2_Log("fopen() error in opening file. %s ,", strerror(errno));
		return HDCP2_ERR_LOAD_PAIRING_INFO1;
	}
	ret = fread(hdcp->encrypted_pairing, 1, PAIRING_INFO_LENGTH, fp);
	if (ret != PAIRING_INFO_LENGTH) {
		HDCP2_Log("read len (%d) !=  PAIRING_INFO_LENGTH (%d)\n", ret,PAIRING_INFO_LENGTH);
		HDCP2_Log("FAILED Loading PairingInfo. %s ,", strerror(errno));
		fclose(fp);
		return HDCP2_ERR_LOAD_PAIRING_INFO2;
	}
	fclose(fp);
	hdcp->is_paired = 1;

	return HDCP2_OK;
}

/**
 * @fn int HDCP2_Store_Pairing_Info(HDCP2_Ctx *hdcp)
 * @brief stores the pairing info in the system (/data/system/hdcp2)
 * @param hdcp - pointer to HDCP context
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int HDCP2_Store_Pairing_Info(HDCP2_Ctx *hdcp)
{
	int ret = 0;
	FILE *fp = 0;
	u8 *receiver_id = NULL;

	if(!hdcp)
		return HDCP2_ERR_INVALID_INPUT;

	receiver_id = hdcp->receiver_id;

	snprintf(hdcp->pair_info_path, MAX_PATH_LEN, "%s/%02x%02x%02x%02x%02x_%d.hdcp2",
			hdcp->pair_info_dir, receiver_id[0], receiver_id[1],
			receiver_id[2], receiver_id[3], receiver_id[4], getuid());
	//HDCP2_Log("Store pairing information on %s\n", hdcp->pair_info_path);

	fp = fopen(hdcp->pair_info_path, "wb");
	if (!fp) {
		HDCP2_Log("fopen() error in HDCP2_Store_Pairing_Info(): %s", strerror(errno));
		return HDCP2_ERR_WRITE_PAIRING_INFO1;
	}
	ret = fwrite(hdcp->encrypted_pairing, 1, PAIRING_INFO_LENGTH, fp);
	if (ret != PAIRING_INFO_LENGTH) {
		HDCP2_Log("written len (%d) !=  PAIRING_INFO_LENGTH (%d)\n", ret,PAIRING_INFO_LENGTH);
		HDCP2_Log("FAILED writing PairingInfo to the file. %s ,", strerror(errno));
		fclose(fp);
		return HDCP2_ERR_WRITE_PAIRING_INFO2;
	}
	//sync();
	fclose(fp);
	hdcp->is_paired = 1;
	gettimeofday((struct timeval *) &hdcp->time_pairing_done, 0);

	HDCP2_Chmod644(hdcp->pair_info_path);
	return ret;
}
#endif /* !USE_QSEE_WRAP_WITH_SFS */

/**
 * @fn int HDCP2_LoadKey(HDCP2_Ctx *hdcp)
 * @brief This function loads the key
 * @param hdcp - pointer to HDCP context
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int HDCP2_LoadKey(HDCP2_Ctx *hdcp)
{
	int ret = 0, i = 0, odm_flag = 0;
	FILE *fp = 0;

	unsigned char h2k_path_cpk[16] = {0x11, 0x5a, 0x26, 0x32, 0x6d, 0x20, 0x34, 0x2e, 0x69, 0x2f, 0x7a, 0x22, 0x64, 0x2f, 0x2d, 0x39};
	unsigned char h2k_path_odm[19] = {0x6e, 0x27, 0x25, 0x37, 0x6a, 0x25, 0x37, 0x23, 0x66, 0x25, 0x2f, 0x21, 0x25, 0x7c, 0x24, 0x7e,  0x35, 0x33, 0x27};
	unsigned char h2k_path_efs[12] = {0x15, 0x5e, 0x5a, 0x4e, 0x11, 0x57, 0x72, 0x2a, 0x6c, 0x27, 0x25, 0x31};

	char wrappedkey_path_cpk[17] = {0, };
	char wrappedkey_path_odm[20] = {0, };
	char wrappedkey_path_efs[13] = {0, };

	u8 hdcp_key[WRAPPED_KEY_SIZE] = {0, };

	if (!hdcp)
		return HDCP2_ERR_INVALID_INPUT;

#ifdef USE_QSEE_WRAP_WITH_SFS
	if (hdcp->entity == HDCP2_TRANSMITTER)
		ret = TZ_COMMAND_T(CMD_HDCP2_LOADKEY, NULL, 0, NULL, 0);
	else
		ret = TZ_COMMAND_R(CMD_HDCP2_LOADKEY, NULL, 0, NULL, 0);
#else
	for (i=0; i<sizeof(h2k_path_cpk); i++)
		wrappedkey_path_cpk[i] = h2k_path_cpk[i] ^ (char)(0x2e + i + sizeof(h2k_path_cpk));

	fp = fopen(wrappedkey_path_cpk, "rb");
	if (!fp) {
		for (i=0; i<sizeof(h2k_path_odm); i++)
			wrappedkey_path_odm[i] = h2k_path_odm[i] ^ (char)(0x2e + i + sizeof(h2k_path_odm));

		fp = fopen(wrappedkey_path_odm, "rb");
		if (!fp) {
			for (i=0; i<sizeof(h2k_path_efs); i++)
				wrappedkey_path_efs[i] = h2k_path_efs[i] ^ (char)(0x2e + i + sizeof(h2k_path_efs));

			fp = fopen(wrappedkey_path_efs, "rb");
			if (!fp) {
				HDCP2_Log("fopen(rb/load) error in opening wrappedkey_path_xxx file. %s", strerror(errno));
				return HDCP2_ERR_READ_WRAPPED_KEYBOX1;
			}
		}
		HDCP2_Log("try to load for ODM");
		odm_flag = 1;
	}

	if ((ret = fread(hdcp_key, 1, WRAPPED_KEY_SIZE, fp)) != WRAPPED_KEY_SIZE) {
		HDCP2_Log("read len (%d) != HDCP2_KEY (%d)\n", ret, WRAPPED_KEY_SIZE);
		HDCP2_Log("FAILED Loading key. %s ,", strerror(errno));
		fclose(fp);
		return HDCP2_ERR_READ_WRAPPED_KEYBOX2;
	}
	fclose(fp);

	if (hdcp->entity == HDCP2_TRANSMITTER)
		ret = TZ_COMMAND_T((odm_flag == 0)?CMD_HDCP2_LOADKEY:CMD_HDCP2_LOADKEY_ODM, hdcp_key, WRAPPED_KEY_SIZE, NULL, 0);
	else
		ret = TZ_COMMAND_R((odm_flag == 0)?CMD_HDCP2_LOADKEY:CMD_HDCP2_LOADKEY_ODM, hdcp_key, WRAPPED_KEY_SIZE, NULL, 0);
#endif /* !USE_QSEE_WRAP_WITH_SFS */

	//HDCP2_Log("HDCP2_LoadKey(%d)", ret);
	return ret;
}

/**
 * @fn int HDCP2_IsKeyboxValid(HDCP2_Ctx *hdcp)
 * @brief Checks if the keybox is valid
 * @param hdcp - pointer to HDCP context
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int HDCP2_IsKeyboxValid(HDCP2_Ctx *hdcp)
{
	if(!hdcp)
		return HDCP2_ERR_INVALID_INPUT;

	if (HDCP2_LoadKey(hdcp) != HDCP2_OK)
		return HDCP2_ERR_INVALID_KEYBOX;

	return HDCP2_OK;
}

/**
 * @fn int HDCP2_WrapKey(HDCP2_Ctx *hdcp)
 * @brief This function wraps key data for encryption
 * @param hdcp - pointer to HDCP context
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int HDCP2_WrapKey(HDCP2_Ctx *hdcp)
{
	int ret = -1, i, odm_flag = 0;
	FILE *fp = 0;

	u8 redata_path_cpk[19] = {0x6e, 0x27, 0x25, 0x37, 0x6a, 0x25, 0x37, 0x23, 0x66, 0x38, 0x2e, 0x28, 0x2c, 0x3a, 0x2e, 0x7e, 0x33, 0x3b, 0x3d};
	u8 tedata_path_odm[22] = {0x6b, 0x20, 0x20, 0x34, 0x67, 0x2a, 0x3a, 0x20, 0x63, 0x22, 0x2a, 0x22, 0x24, 0x34, 0x36, 0x32, 0x20, 0x34, 0x78, 0x35, 0x31, 0x37};
	u8 redata_path_efs[15] = {0x12, 0x5b, 0x59, 0x33, 0x6e, 0x30, 0x26, 0x20, 0x24, 0x32, 0x26, 0x66, 0x2b, 0x23, 0x25};

	char in_path_cpk[20] = {0, };
	char in_path_odm[23] = {0, };
	char in_path_efs[16] = {0, };

	char *in_path_addr = NULL;

	u8 key[PROVISIONING_KEY_SIZE] = {0, };

	if (!hdcp)
		return HDCP2_ERR_INVALID_INPUT;

	for (i=0; i<sizeof(redata_path_cpk); i++)
		in_path_cpk[i] = redata_path_cpk[i] ^ (char)(0x2e + i + sizeof(redata_path_cpk));

	// 1. Check Installation
	if (HDCP2_IsKeyboxValid(hdcp) == HDCP2_OK) {
		HDCP2_Log("Pass...");
		return HDCP2_OK;
	}

	fp = fopen(in_path_cpk, "rb");
	if (!fp) {
		for (i=0; i<sizeof(tedata_path_odm); i++)
			in_path_odm[i] = tedata_path_odm[i] ^ (char)(0x2e + i + sizeof(tedata_path_odm));

		fp = fopen(in_path_odm, "rb");
		if (!fp) {
			for (i=0; i<sizeof(redata_path_efs); i++)
				in_path_efs[i] = redata_path_efs[i] ^ (char)(0x2e + i + sizeof(redata_path_efs));

			fp = fopen(in_path_efs, "rb");
			if (!fp) {
				HDCP2_Log("fopen(rb/load) error in opening wrappedkey_path_xxx file. %s", strerror(errno));
				ret = HDCP2_ERR_WRITE_WRAPPED_KEYBOX1;
				goto err;
			} else {
				in_path_addr = in_path_efs;
			}
		} else {
			in_path_addr = in_path_odm;
			odm_flag = 1;
		}
	} else {
		in_path_addr = in_path_cpk;
	}

	if ((ret=fread(key, 1, PROVISIONING_KEY_SIZE, fp)) != PROVISIONING_KEY_SIZE) {
		HDCP2_Log("read len (%d) != HDCP2_KEY (%d)\n", ret,PROVISIONING_KEY_SIZE);
		HDCP2_Log("FAILED Loading key. %s ,", strerror(errno));
		fclose(fp);
		ret = HDCP2_ERR_WRITE_WRAPPED_KEYBOX2;
		goto err;
	}
	fclose(fp);

	// b. check key
	if (key[PROVISIONING_KEY_SIZE-17] == 1) {
		ret = HDCP2_ERR_WRITE_WRAPPED_KEYBOX7;
		HDCP2_Log(" check key.failse %s ,", strerror(errno));
		goto err;
	}

	// c. wrap key
	if ((ret = HDCP2_WrapKeyData(hdcp, key, PROVISIONING_KEY_SIZE, odm_flag)) != HDCP2_OK) {
		HDCP2_Log(" HDCP2_WrapKeyData failse %s ,", strerror(errno));
		goto err;
	}

	// 3. Wipe redata.bin
	memset(key, 0x00, PROVISIONING_KEY_SIZE);
	fp = fopen(in_path_addr, "wb");
	if (!fp) {
		HDCP2_Log("fopen() error in opening file. %s ,", strerror(errno));
		ret = HDCP2_ERR_WRITE_WRAPPED_KEYBOX5;
		HDCP2_Log("Cannot open input");
	} else {
		if ((ret = fwrite(key, 1, PROVISIONING_KEY_SIZE, fp)) != PROVISIONING_KEY_SIZE) {
			HDCP2_Log("Written len (%d) != HDCP2_KEY (%d)\n", ret,PROVISIONING_KEY_SIZE);
			HDCP2_Log("FAILED writing key. %s ,", strerror(errno));
			HDCP2_Log("Wipe input error (%d)", ret);
		}
		fclose(fp);
	}

	// Remove redata.bin file
	if (!unlink(in_path_addr))
		HDCP2_Log("Removed...");

	HDCP2_Log("Installed...");

	return HDCP2_OK;

err:
	HDCP2_Log("Wrapkey Failed with %d", ret);
	return ret;
}

/**
 * @fn int HDCP2_WrapKeyData(HDCP2_Ctx *hdcp, const u8 *keyData, int keyDataLen, int odm_flag)
 * @brief This function wraps key data for encryption and is called by hdcp2_Wrap_key
 * @param hdcp - pointer to HDCP context
 * @param keyData - pointer to the key
 * @param keyDataLen - length of key
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int HDCP2_WrapKeyData(HDCP2_Ctx *hdcp, const u8 *keyData, int keyDataLen, int odm_flag)
{
	int ret = -1;

#ifndef USE_QSEE_WRAP_WITH_SFS
	int i, length = 0;
	FILE *fp = NULL;

	unsigned char h2k_path_cpk[16] = {0x11, 0x5a, 0x26, 0x32, 0x6d, 0x20, 0x34, 0x2e, 0x69, 0x2f, 0x7a, 0x22, 0x64, 0x2f, 0x2d, 0x39};
	unsigned char h2k_path_odm[19] = {0x6e, 0x27, 0x25, 0x37, 0x6a, 0x25, 0x37, 0x23, 0x66, 0x25, 0x2f, 0x21, 0x25, 0x7c, 0x24, 0x7e, 0x35, 0x33, 0x27};
	unsigned char h2k_path_efs[12] = {0x15, 0x5e, 0x5a, 0x4e, 0x11, 0x57, 0x72, 0x2a, 0x6c, 0x27, 0x25, 0x31};

	char wrappedkey_path_cpk[17] = {0, };
	char wrappedkey_path_odm[20] = {0, };
	char wrappedkey_path_efs[13] = {0, };

	u8 wrapped_key[WRAPPED_KEY_SIZE] = {0, };
#endif /* !USE_QSEE_WRAP_WITH_SFS */

	u8 key[PROVISIONING_KEY_SIZE] = {0, };

#ifdef USE_QUALCOMM
	DELIVER_KEY_DATA key_data = {0, };
#endif /* USE_QUALCOMM */

	if (!hdcp || !keyData) {
		HDCP2_Log("Invalid parameter in HDCP2_WrapKeyData");
		return HDCP2_ERR_INVALID_INPUT;
	}

	if (keyDataLen != PROVISIONING_KEY_SIZE) {
		ret = HDCP2_ERR_WRITE_WRAPPED_KEYBOX1;
		goto err;
	}
	memcpy(key, keyData, PROVISIONING_KEY_SIZE);

	if (key[PROVISIONING_KEY_SIZE-17] == 1) {
		ret = HDCP2_ERR_WRITE_WRAPPED_KEYBOX7;
		goto err;
	}

#ifndef USE_QUALCOMM
	length = TZ_COMMAND_R(CMD_HDCP2_WRAPKEY, key, PROVISIONING_KEY_SIZE, wrapped_key, WRAPPED_KEY_SIZE);

	if (length == WRAPPED_KEY_SIZE) {
		for (i=0; i<sizeof(h2k_path_cpk); i++)
			wrappedkey_path_cpk[i] = h2k_path_cpk[i] ^ (char)(0x2e + i + sizeof(h2k_path_cpk));

		fp = fopen(wrappedkey_path_cpk, "wb");
		if (!fp) {
			for (i=0; i<sizeof(h2k_path_efs); i++)
				wrappedkey_path_efs[i] = h2k_path_efs[i] ^ (char)(0x2e + i + sizeof(h2k_path_efs));

			fp = fopen(wrappedkey_path_efs, "wb");
			if (!fp) {
				HDCP2_Log("fopen(wb/wrap) error in opening wrappedkey_path_xxx file. %s", strerror(errno));
				ret = HDCP2_ERR_WRITE_WRAPPED_KEYBOX3;
				goto err;
			}
		}
		ret = fwrite(wrapped_key, 1, WRAPPED_KEY_SIZE, fp);
		if (ret != WRAPPED_KEY_SIZE) {
			HDCP2_Log("Written len (%d) != HDCP2_KEY (%d)\n", ret, WRAPPED_KEY_SIZE);
			HDCP2_Log("FAILED writing key. %s ,", strerror(errno));
			ret = HDCP2_ERR_WRITE_WRAPPED_KEYBOX4;
			fclose(fp);
			goto err;
		}
		fflush(fp);
		sync();
		fclose(fp);
	} else {
		HDCP2_Log("Size of Wrappedkey by TZ (%d bytes) is Wrong", length);
		goto err;
	}
#else /* USE_QUALCOMM */
	memcpy(key_data.key, key, PROVISIONING_KEY_SIZE);

#ifdef USE_QSEE_WRAP_WITH_SFS
	ret = mkdir(drm_dir, 0700);
	if (ret != 0) {
		if (errno != EEXIST) {
			HDCP2_Log("mkdir() - %s: FAILED! %s(%d)", drm_dir, strerror(errno), errno);
			return HDCP2_ERR_WRITE_WRAPPED_KEYBOX8;
		}
	}

	ret = mkdir(h2k_key_sfs_dir, 0700);
	if (ret != 0) {
		if (errno != EEXIST) {
			HDCP2_Log("mkdir() - %s: FAILED! %s(%d)", h2k_key_sfs_dir, strerror(errno), errno);
			return HDCP2_ERR_WRITE_WRAPPED_KEYBOX3;
		}
	}

	ret = chown(h2k_key_sfs_dir, 1000, 1000);
	if (ret != 0) {
		HDCP2_Log("chown() - %s: FAILED! %s(%d)", h2k_key_sfs_dir, strerror(errno), errno);
		return HDCP2_ERR_WRITE_WRAPPED_KEYBOX4;
	}

	ret = TZ_COMMAND_R(CMD_HDCP2_STOREKEY, (uint8_t*)&key_data, sizeof(DELIVER_KEY_DATA), NULL, 0);
	if (ret != HDCP2_OK) {
		ret = HDCP2_ERR_WRITE_WRAPPED_KEYBOX3;
		goto err;
	}
#else
	length = TZ_COMMAND_R(CMD_HDCP2_STOREKEY, (uint8_t*)&key_data, sizeof(DELIVER_KEY_DATA), wrapped_key, WRAPPED_KEY_SIZE);

	if (length == WRAPPED_KEY_SIZE) {
		if (odm_flag == 0) {
			for (i=0; i<sizeof(h2k_path_cpk); i++)
				wrappedkey_path_cpk[i] = h2k_path_cpk[i] ^ (char)(0x2e + i + sizeof(h2k_path_cpk));

			fp = fopen(wrappedkey_path_cpk, "wb");
		} else {
			for (i=0; i<sizeof(h2k_path_odm); i++)
				wrappedkey_path_odm[i] = h2k_path_odm[i] ^ (char)(0x2e + i + sizeof(h2k_path_odm));

			fp = fopen(wrappedkey_path_odm, "wb");
		}

		if (!fp) {
			for (i=0; i<sizeof(h2k_path_efs); i++)
				wrappedkey_path_efs[i] = h2k_path_efs[i] ^ (char)(0x2e + i + sizeof(h2k_path_efs));

			fp = fopen(wrappedkey_path_efs, "wb");
			if (!fp) {
				HDCP2_Log("fopen(wb/wrap) error in opening wrappedkey_path_xxx file. %s", strerror(errno));
				ret = HDCP2_ERR_WRITE_WRAPPED_KEYBOX3;
				goto err;
			}
		}

		ret = fwrite(wrapped_key, 1, WRAPPED_KEY_SIZE, fp);
		if (ret != WRAPPED_KEY_SIZE) {
			HDCP2_Log("Written len (%d) != HDCP2_KEY (%d)\n", ret, WRAPPED_KEY_SIZE);
			HDCP2_Log("FAILED writing key. %s ,", strerror(errno));
			ret = HDCP2_ERR_WRITE_WRAPPED_KEYBOX4;
			fclose(fp);
			goto err;
		}
		fflush(fp);
		sync();
		fclose(fp);
	} else {
		HDCP2_Log("Size of Wrappedkey by TZ (%d bytes) is Wrong", length);
		goto err;
	}
#endif /* USE_QSEE_WRAP_WITH_SFS */
#endif /* !USE_QUALCOMM */

done:
#ifndef USE_QSEE_WRAP_WITH_SFS
	if (HDCP2_Chmod644(wrappedkey_path_cpk) != 0)
		if (HDCP2_Chmod644(wrappedkey_path_odm) != 0)
			HDCP2_Chmod644(wrappedkey_path_efs);
#endif /* !USE_QSEE_WRAP_WITH_SFS */

	return HDCP2_OK;

err:
	return ret;
}

/**
 * @fn int HDCP2_Init(HDCP2_Ctx *hdcp, u8 entity, int version)
 * @brief Initializes hdcp context
 * @param hdcp - pointer to HDCP context
 * @param entity - specifies if the devices is a transmitter or receiver or a repeater.
 * @param version - specifies the Hdcp version (2.0/2.1/2.2)
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int HDCP2_Init(HDCP2_Ctx *hdcp, u8 entity, int version)
{
	char flag[256] = {0, };
	int ret = 0;

	// Check arguments
	if (entity > HDCP2_FACTORY || entity < HDCP2_TRANSMITTER)
		return HDCP2_ERR_UNSUPPRTED_PARTY;

	if (!hdcp)
		return HDCP2_ERR_INVALID_INPUT;

	// Set system property about installing HDCP key
	if (entity == HDCP2_FACTORY) {
#if defined(USE_MOBICORE)
		property_set(INSTHK_SYS_PROP, PROP_LSI_WRAP); // LSI wrapping key
#elif defined(USE_QSEE_WRAP_WITH_SFS)
		property_set(INSTHK_SYS_PROP, PROP_QC_SFS); // QC using sfs
#elif defined(USE_QUALCOMM)
		property_set(INSTHK_SYS_PROP, PROP_QC_WRAP); // QC wrapping key
#endif /* USE_MOBICORE */
	}

	// Reset Context
	memset(hdcp, 0, sizeof(hdcp));

	// Set default value
	hdcp->entity = entity;
#ifdef USE_TEEGRIS
	hdcp->tztype = TZ_TYPE_TEEGRIS;
#else
	hdcp->tztype = TZ_TYPE_MOBICORE;
#endif /* USE_TEEGRIS */

	hdcp->version = version;
	snprintf(hdcp->pair_info_dir, MAX_PATH_LEN, "%s_%d", HDCP2_PAIRING_INFO_DIR, getuid());

	// Set Retry Policy
	hdcp->timeout = HDCP2_AKE_TIMEOUT; // default 15 sec
	hdcp->ake_retry_count = HDCP2_AKE_RETRY_COUNT;
	hdcp->lc_retry_count = HDCP2_LC_RETRY_COUNT;
	hdcp->sd = -1;
	hdcp->csd = -1;

	// Set log info and version
	HDCP2_Log(HDCP2_INFO_1);
	HDCP2_Log(HDCP2_INFO_2);
	HDCP2_Log(HDCP2_INFO_3);

	// Open Trustlet Session
	if (HDCP2_TZ_Open(hdcp, entity) != HDCP2_OK) {
		HDCP2_Log("Cannot open trustlet session\n");
		return HDCP2_ERR_TRUSTZONE_OPEN;
	}

	hdcp->opened = 1;

	if (entity != HDCP2_FACTORY) {
		/*
		 * Load Key (lc128 is required)
		 */
		if ((ret=HDCP2_LoadKey(hdcp)) != HDCP2_OK) {
			HDCP2_Log("Cannot load...(%d)", ret);
			return HDCP2_ERR_INVALID_KEYBOX;
		}

		// Set Current Mode
		if (entity == HDCP2_TRANSMITTER) {
			property_set("wlan.hdcp2.cur", "tx");

			if(hdcp->version == HDCP2_VERSION_2_1)
				property_set("wlan.hdcp2.ver", "2.1");
			else if(hdcp->version == HDCP2_VERSION_2_0)
				property_set("wlan.hdcp2.ver", "2.0");
			else if(hdcp->version == HDCP2_VERSION_2_2)
				property_set("wlan.hdcp2.ver", "2.2");
			else if(hdcp->version == HDCP2_VERSION_2_3)
				property_set("wlan.hdcp2.ver", "2.3");
		} else if ((entity == HDCP2_RECEIVER) || (entity == HDCP2_REPEATER)) {
			property_set("wlan.hdcp2.cur", "rx");

			if(hdcp->version == HDCP2_VERSION_2_1)
				property_set("wlan.hdcp2.ver", "2.1");
			else if(hdcp->version == HDCP2_VERSION_2_0)
				property_set("wlan.hdcp2.ver", "2.0");
			else if(hdcp->version == HDCP2_VERSION_2_2)
				property_set("wlan.hdcp2.ver", "2.2");
			else if(hdcp->version == HDCP2_VERSION_2_3)
				property_set("wlan.hdcp2.ver", "2.3");
		}
	}

	HDCP2_Log("USE HDCP GOOGLE STACK");

	switch (entity) {
	case HDCP2_TRANSMITTER:
		HDCP2_Log("HDCP%d.%d Transmitter Loaded", hdcp->version / 10, hdcp->version % 10);
		break;
	case HDCP2_RECEIVER:
		HDCP2_Log("HDCP%d.%d Receiver Loaded", hdcp->version / 10, hdcp->version % 10);
		break;
	case HDCP2_REPEATER:
		HDCP2_Log("HDCP%d.%d Repeater Loaded", hdcp->version / 10, hdcp->version % 10);
		break;
	case HDCP2_FACTORY:
		HDCP2_Log("HDCP%d.%d Factory Loaded", hdcp->version / 10, hdcp->version % 10);
		break;
	}

	return HDCP2_OK;
}

/**
 * @fn int HDCP2_Close(HDCP2_Ctx *hdcp)
 * @brief Closes the hdcp connection by unintializing hdcp2 context
 * @param hdcp - pointer to HDCP context
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int HDCP2_Close(HDCP2_Ctx *hdcp)
{
	// let Tx or Rx can terminate AKE
	if(!hdcp || !hdcp->opened)
		return HDCP2_OK;

	hdcp->opened = 0;
	hdcp->running_ake = 0;
	// wait for remained encryptions
	if (hdcp->entity != HDCP2_FACTORY){
		usleep(100000);

	//close socket
	if (hdcp->sd > 0)
		HDCP2_Close_Socket(hdcp, "sd");

#ifndef USE_QUALCOMM
	// close socket for server
	if (hdcp->csd > 0)
		HDCP2_Close_Socket(hdcp, "csd");
#endif /* USE_QUALCOMM */
	}

	HDCP2_TZ_Close(hdcp);

	if (hdcp->entity != HDCP2_FACTORY) {
		property_set("wlan.hdcp2.cur", "none");
		property_set("wlan.hdcp2.ver", "none");
	}

#ifdef HDCP2_PROFILE_ENCRYPT_TIME
	if (hdcp->entity == HDCP2_TRANSMITTER) {
		HDCP2_Log("total_duration = %llu, enc_count = %llu, total_lengh = %llu", total_duration, enc_count, total_lengh);
		HDCP2_Log("avg = %llu(msec/frame), %llu(bytes/msec)", total_duration/enc_count, total_lengh/total_duration);
	}
#endif /* HDCP2_PROFILE_ENCRYPT_TIME */

	memset(hdcp, 0, sizeof(hdcp));

	HDCP2_Log("Closed");
	return HDCP2_OK;
}

/**
 * @fn int HDCP2_IsClosed(HDCP2_Ctx *hdcp)
 * @brief checks if hdcp is opened or closed
 * @param hdcp - pointer to HDCP context
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int HDCP2_IsClosed(HDCP2_Ctx *hdcp)
{
	if(hdcp)
		return !hdcp->opened;
	else
		return HDCP2_ERR;
}

/**
 * int HDCP2_Set_AKE_RetryPolicy(HDCP2_Ctx *hdcp, long timeout, int ake_retry_count, int lc_retry_count)
 * @brief This function checks with retry policy if anymore retries for AKE_Init can be performed in case of error in connection.
 * @param hdcp - pointer to HDCP context.
 * @param timeout - Time elapsed since Ake_init was first called.
 * @param ake_retry_count - indicates the number times AKE_Init has been retried.
 * @param lc_retry_count - indicates the number of times lc_init has been retried.
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error.
 */
int HDCP2_Set_AKE_RetryPolicy(HDCP2_Ctx *hdcp, long timeout, int ake_retry_count, int lc_retry_count)
{
#ifdef USE_AKE_POLICY
	if(!hdcp)
		return HDCP2_ERR_INVALID_INPUT;

	if (timeout < 0 || timeout > 60000)
		HDCP2_Log("Timout value is invalid (0ms <= t <= 60,000ms)");
	else
		hdcp->timeout = timeout;

	if (ake_retry_count < 1 || ake_retry_count > 128)
		HDCP2_Log("AKE Retry Count is invalid (1 <= c <= 128)");
	else
		hdcp->ake_retry_count = ake_retry_count;

	if (lc_retry_count < 1 || lc_retry_count > 1024)
		HDCP2_Log("LC Retry Count is invalid (1 <= c <= 128)");
	else
		hdcp->lc_retry_count = lc_retry_count;

	HDCP2_Log("AKE_RetryPolicy (timeout: %dms, ake: %dth, lc: %dth)",
			hdcp->timeout, hdcp->ake_retry_count, hdcp->lc_retry_count);
#endif /* USE_AKE_POLICY */

	return HDCP2_OK;
}

/**
 * int HDCP2_SetVersion(HDCP2_Ctx *hdcp)
 * @brief This function update the version of HDCP.
 * @param hdcp - pointer to HDCP context.
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error.
 */
int HDCP2_SetVersion(HDCP2_Ctx *hdcp)
{
	int ret = 0;

	u8 hdcp_version[2] = {0, };
	hdcp_version[0] = CMD_SET_HDCP_VERSION;
	hdcp_version[1] = hdcp->version;

	//updating the version from nwd to swd
	if (hdcp->entity == HDCP2_TRANSMITTER)
		ret = TZ_COMMAND_T(CMD_SET_HDCP_VERSION, hdcp_version, sizeof(hdcp_version), NULL, 0);
	else if ((hdcp->entity == HDCP2_RECEIVER) || (hdcp->entity == HDCP2_REPEATER))
		ret = TZ_COMMAND_R(CMD_SET_HDCP_VERSION, hdcp_version, sizeof(hdcp_version), NULL, 0);

	return ret;
}

