/**
 * 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.
 *
 */

#include <tee_internal_api.h>
#include <unistd.h>

#include <tees_extension.h>
#include "tz_hdcp2_resources.h"
#include "tz_hdcp2.h"
#include "tz_hdcp2_transmitter.h"


// Variables to verify that the TA is initialized.
static bool g_initialized = false;

static bool isInitialized(const uint8_t commandID)
{	
	switch (commandID) {
		case (CMD_TRUSTZONE_INIT + CMD_RECEIVER) :
		case (CMD_TRUSTZONE_INIT + CMD_TRANSMITTER) :
		case (CMD_HDCP2_WRAPKEY + CMD_RECEIVER) :
		case (CMD_HDCP2_LOADKEY + CMD_RECEIVER) :
			return true;
		default :
			return g_initialized;
	}
}

/**
 * @fn int TZ_COMMAND(const uint8_t command, uint8_t *request,const u32 req_size, uint8_t *response, uint32_t *res_size);
 * @brief The control comes to this function when secure world is entered and the command to be executed next is decided here only.
 * @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_COMMAND(const uint8_t command, uint8_t *request, const u32 req_size,
				uint8_t *response, uint32_t *res_size)
{
	int ret = HDCP2_ERR_NOT_SUPPORTED_COMMAND;

	switch (command) {
		case CMD_NULL_MESSAGE + CMD_RECEIVER:
			break;
		case CMD_HDCP2_WRAPKEY + CMD_RECEIVER:
			ret = TZ_HDCP2_WRAPKEY(command - CMD_RECEIVER, request, req_size,
						response, res_size);
			break;
		case CMD_HDCP2_LOADKEY + CMD_RECEIVER:
			ret = TZ_HDCP2_LOADKEY_R(command - CMD_RECEIVER, request, req_size,
						response, res_size);
			break;
		case CMD_AKE_INIT + CMD_RECEIVER:
			ret = TZ_AKE_Init_R(command - CMD_RECEIVER, request, req_size,
						response, res_size);
			break;
		case CMD_AKE_SEND_CERT + CMD_RECEIVER:
			ret = TZ_AKE_Send_Cert_R(command - CMD_RECEIVER, request, req_size,
						response, res_size);
			break;
		case CMD_AKE_NO_STORED_KM + CMD_RECEIVER:
			ret = TZ_AKE_No_Store_km_R(command - CMD_RECEIVER, request,
						req_size, response, res_size);
			break;
		case CMD_AKE_STORED_KM + CMD_RECEIVER:
			ret = TZ_AKE_Store_km_R(command - CMD_RECEIVER, request, req_size,
						response, res_size);
			break;
		case CMD_AKE_SEND_RRX + CMD_RECEIVER:
			ret = TZ_AKE_Send_rrx_R(command - CMD_RECEIVER, request, req_size,
						response, res_size);
			break;
		case CMD_AKE_SEND_H_PRIME + CMD_RECEIVER:
			ret = TZ_AKE_Send_h_prime_R(command - CMD_RECEIVER, request,
						req_size, response, res_size);
			break;
		case CMD_AKE_SEND_PAIRING_INFO + CMD_RECEIVER:
			ret = TZ_AKE_Send_Pairing_Info_R(command - CMD_RECEIVER, request,
						req_size, response, res_size);
			break;
		case CMD_LC_INIT + CMD_RECEIVER:
			ret = TZ_LC_Init_R(command - CMD_RECEIVER, request, req_size,
						response, res_size);
			break;
		case CMD_RTT_READY + CMD_RECEIVER:
			ret = TZ_RTT_READY_R(command - CMD_RECEIVER, request,
						req_size, response, res_size);
			break;
		case CMD_RTT_CHALLENGE + CMD_RECEIVER:
			ret = TZ_RTT_CHALLENGE_R(command - CMD_RECEIVER, request, req_size,
						response, res_size);
			break;
		case CMD_LC_SEND_L_PRIME + CMD_RECEIVER:
			ret = TZ_LC_Send_L_prime_R(command - CMD_RECEIVER, request,
						req_size, response, res_size);
			break;
		case CMD_SKE_SEND_EKS + CMD_RECEIVER:
			ret = TZ_SKE_Send_Eks_R(command - CMD_RECEIVER, request, req_size,
						response, res_size);
			break;
		case CMD_AKE_TRANSMITTER_INFO + CMD_RECEIVER:
			ret = TZ_AKE_Transmitter_Info_R(command - CMD_RECEIVER, request,
						req_size, response, res_size);
			break;
		case CMD_AKE_RECEIVER_INFO + CMD_RECEIVER:
			ret = TZ_AKE_Receiver_Info_R(command - CMD_RECEIVER, request,
						req_size, response, res_size);
			break;
		case CMD_REPEATERAUTH_SEND_RECEIVER_ID_LIST + CMD_RECEIVER:
			ret = TZ_RepeaterAuth_Send_ReceiverId_List_Rep(command - CMD_RECEIVER,
						request, req_size, response, res_size);
			break;
		case CMD_REPEATERAUTH_SEND_ACK + CMD_RECEIVER:
			ret = TZ_RepeaterAuth_Send_Ack_Rep(command - CMD_RECEIVER, request,
						req_size, response, res_size);
			break;
		case CMD_RECEIVER_AUTHSTATUS + CMD_RECEIVER:
			ret = TZ_Receiver_AuthStatus_Rep(command - CMD_RECEIVER, request,
						req_size, response, res_size);
			break;
		case CMD_REPEATERAUTH_STREAM_MANAGE+CMD_RECEIVER:
			ret = TZ_RepeaterAuth_Stream_Manage_Rep(command - CMD_RECEIVER, request,
						req_size, response, res_size);
			break;
		case CMD_REPEATERAUTH_STREAM_READY+CMD_RECEIVER:
			ret = TZ_RepeaterAuth_Stream_Ready_Rep(command - CMD_RECEIVER, request,
						req_size, response, res_size);
			break;
		case CMD_CIP_DEC_DATA + CMD_RECEIVER:
			ret = TZ_DEC_Data(command - CMD_RECEIVER, request, req_size,
						response, res_size);
			break;
		case CMD_SET_HDCP_VERSION + CMD_RECEIVER:
			ret = TZ_SET_HDCP_VERSION_R(command - CMD_RECEIVER, request, req_size,
						response, res_size);
			break;
		case CMD_TRUSTZONE_INIT + CMD_RECEIVER:
			ret = TZ_HW_Init_R(command - CMD_RECEIVER, request, req_size,
						response, res_size);
			if (ret == HDCP2_OK)
				g_initialized = true;
			else
				g_initialized = false;
			break;
		case CMD_TRUSTZONE_CLOSE + CMD_RECEIVER:
			ret = TZ_HW_Close_R(command - CMD_RECEIVER, request, req_size,
						response, res_size);
			g_initialized = false;
			break;
		case CMD_CIP_SPS_PPS_DATA + CMD_RECEIVER:
			ret = TZ_SPSPPS_COPY(command - CMD_RECEIVER, request, req_size,
						response, res_size);
			break;
		case CMD_NULL_MESSAGE + CMD_TRANSMITTER:
			break;
		case CMD_HDCP2_LOADKEY + CMD_TRANSMITTER:
			ret = TZ_HDCP2_LOADKEY_T(command - CMD_RECEIVER, request, req_size,
						response, res_size);
			break;
		case CMD_AKE_SET_PAIRING_INFO + CMD_TRANSMITTER:
			ret = TZ_AKE_SET_PAIRING_INFO_T(command - CMD_TRANSMITTER, request,
						req_size, response, res_size);
			break;
		case CMD_AKE_INIT + CMD_TRANSMITTER:
			ret = TZ_AKE_Init_T(command - CMD_TRANSMITTER, request, req_size,
						response, res_size);
			break;
		case CMD_AKE_SEND_CERT + CMD_TRANSMITTER:
			ret = TZ_AKE_Send_Cert_T(command - CMD_TRANSMITTER, request,
						req_size, response, res_size);
			break;
		case CMD_AKE_NO_STORED_KM + CMD_TRANSMITTER:
			ret = TZ_AKE_No_Store_km_T(command - CMD_TRANSMITTER, request,
						req_size, response, res_size);
			break;
		case CMD_AKE_STORED_KM + CMD_TRANSMITTER:
			ret = TZ_AKE_Store_km_T(command - CMD_TRANSMITTER, request,
						req_size, response, res_size);
			break;
		case CMD_AKE_SEND_RRX + CMD_TRANSMITTER:
			ret = TZ_AKE_Send_rrx_T(command - CMD_TRANSMITTER, request,
						req_size, response, res_size);
			break;
		case CMD_AKE_SEND_H_PRIME + CMD_TRANSMITTER:
			ret = TZ_AKE_Send_h_prime_T(command - CMD_TRANSMITTER, request,
						req_size, response, res_size);
			break;
		case CMD_AKE_SEND_PAIRING_INFO + CMD_TRANSMITTER:
			ret = TZ_AKE_Send_Pairing_Info_T(command - CMD_TRANSMITTER,
						request, req_size, response, res_size);
			break;
		case CMD_LC_INIT + CMD_TRANSMITTER:
			ret = TZ_LC_Init_T(command - CMD_TRANSMITTER, request, req_size,
						response, res_size);
			break;
		case CMD_RTT_CHALLENGE + CMD_TRANSMITTER:
			ret = TZ_RTT_Challenge_T(command - CMD_TRANSMITTER, request, req_size,
						response, res_size);
			break;
		case CMD_LC_SEND_L_PRIME + CMD_TRANSMITTER:
			ret = TZ_LC_Send_L_prime_T(command - CMD_TRANSMITTER, request,
						req_size, response, res_size);
			break;
		case CMD_SKE_SEND_EKS + CMD_TRANSMITTER:
			ret = TZ_SKE_Send_Eks_T(command - CMD_TRANSMITTER, request,
						req_size, response, res_size);
			break;
		case CMD_AKE_TRANSMITTER_INFO + CMD_TRANSMITTER:
			ret = TZ_AKE_Transmitter_Info_T(command - CMD_TRANSMITTER, request,
						req_size, response, res_size);
			break;
		case CMD_AKE_RECEIVER_INFO + CMD_TRANSMITTER:
			ret = TZ_AKE_Receiver_Info_T(command - CMD_TRANSMITTER, request,
						req_size, response, res_size);
			break;
		case CMD_REPEATERAUTH_SEND_RECEIVER_ID_LIST + CMD_TRANSMITTER:
			ret = TZ_RepeaterAuth_Send_ReceiverId_List_T(command - CMD_TRANSMITTER, 
						request, req_size, response, res_size);
			break;
		case CMD_REPEATERAUTH_SEND_ACK + CMD_TRANSMITTER:
			ret = TZ_RepeaterAuth_Send_Ack_T(command - CMD_TRANSMITTER,
						request, req_size, response, res_size);
			break;
		case CMD_RECEIVER_AUTHSTATUS + CMD_TRANSMITTER:
			ret = TZ_Receiver_AuthStatus_T(command - CMD_TRANSMITTER, request,
						req_size, response, res_size);
			break;
		case CMD_REPEATERAUTH_STREAM_MANAGE + CMD_TRANSMITTER:
			ret = TZ_RepeaterAuth_Stream_Manage_T(command - CMD_TRANSMITTER,
						request, req_size, response, res_size);
			break;
		case CMD_REPEATERAUTH_STREAM_READY + CMD_TRANSMITTER:
			ret = TZ_RepeaterAuth_Stream_Ready_T(command - CMD_TRANSMITTER,
						request, req_size, response, res_size);
			break;
		case CMD_CIP_ENC_DATA + CMD_TRANSMITTER:
			ret = TZ_ENC_Data(command - CMD_TRANSMITTER, request, req_size,
						response, res_size);
			break;
		case CMD_SET_HDCP_VERSION + CMD_TRANSMITTER:
			ret = TZ_SET_HDCP_VERSION_T(command - CMD_TRANSMITTER, request, req_size,
						response, res_size);
			break;
		case CMD_TRUSTZONE_INIT + CMD_TRANSMITTER:
			ret = TZ_HW_Init_T(command - CMD_TRANSMITTER, request, req_size,
						response, res_size);
			if (ret == HDCP2_OK)
				g_initialized = true;
			else
				g_initialized = false;
			break;
		case CMD_TRUSTZONE_CLOSE + CMD_TRANSMITTER:
			ret = TZ_HW_Close_T(command - CMD_RECEIVER, request, req_size,
						response, res_size);
			g_initialized = false;
			break;
		default:
			break;
	}

	TZ_HDCP_DEBUG("HDCP : returning from TZ_Command with ret = %d\n", ret);

	return ret;
}

TEE_Result TA_CreateEntryPoint(void)
{
	TZ_HDCP_LOG("HDCP : TA_CreateEntryPoint\n");
	return TEE_SUCCESS;
}

void TA_DestroyEntryPoint(void)
{

}

unsigned int serial = 0;

TEE_Result TA_OpenSessionEntryPoint(uint32_t paramTypes,
				TEE_Param params[4], void **sessionContext)
{
	(void) paramTypes;
	(void) params;
	*sessionContext = (void *)(serial++);
	TZ_HDCP_LOG("HDCP : TA_OpenSessionEntryPoint\n");
	
	if (TZ_HW_Get_Oem_Flag() != HDCP2_OK) {
		TZ_HDCP_LOG("Integrity check fail\n");
		return HDCP2_ERR_INTEGRITY;
	}

	return TEE_SUCCESS;
}

void TA_CloseSessionEntryPoint(void *sessionContext)
{
	(void) sessionContext;
}

TEE_Result TA_InvokeCommandEntryPoint(void *sessionContext,
				uint32_t commandID, uint32_t paramTypes, TEE_Param params[4])
{
	(void) sessionContext;
	(void) commandID;
	(void) paramTypes;
	(void) params;

	int returnCode = 0;
	u32 req_size = 0;
	uint8_t *request = NULL;

	if (TEE_PARAM_TYPE_GET(paramTypes, 0) != TEE_PARAM_TYPE_MEMREF_INOUT) {
		TZ_HDCP_LOG("HDCP : Error, Invalid parameter\n");
		return HDCP2_ERR_INVALID_INPUT;
	} else {
		if ((params[0].memref.buffer == NULL) || (params[0].memref.size == 0)) {
			TZ_HDCP_LOG("HDCP : Error, Invalid input data\n");
			return HDCP2_ERR_INVALID_INPUT;
		}

#if defined(TEEGRIS_V3) || defined(TEEGRIS_V4)
		if (TEES_IsREESharedMemory(TEE_MEMORY_ACCESS_READ,
						params[0].memref.buffer, params[0].memref.size) != TEE_SUCCESS) {
			TZ_HDCP_LOG("HDCP : Error, Memory access check error\n");
			return HDCP2_ERR;
		}
#endif /* TEEGRIS_V3 || TEEGRIS_V4*/

		req_size = params[0].memref.size;
		request = TEE_Malloc(req_size, 0);
		if (request == NULL) {
			TZ_HDCP_LOG("HDCP : Error, TEE malloc failed\n");
			return HDCP2_ERR;
		} else {
			TZ_HDCP_DEBUG("HDCP : TZ COMMAND OK, TEE malloc\n");
			TEE_MemMove((uint8_t*)request, (uint8_t*)params[0].memref.buffer, req_size);
		}
	}

	if (TEE_PARAM_TYPE_GET(paramTypes, 1) != TEE_PARAM_TYPE_MEMREF_INOUT) {
		TZ_HDCP_LOG("HDCP : Error, invalid parameter\n");
		returnCode = HDCP2_ERR;
		goto err;
	}
#if defined(TEEGRIS_V3) || defined(TEEGRIS_V4)
	else {
		if (TEES_IsREESharedMemory(TEE_MEMORY_ACCESS_WRITE,
						params[1].memref.buffer, params[1].memref.size) != TEE_SUCCESS) {
			TZ_HDCP_LOG("HDCP : Error, memory access check error\n");
			returnCode = HDCP2_ERR;
			goto err;
		}
	}
#endif /* TEEGRIS_V3 || TEEGRIS_V4*/

	TZ_HDCP_DEBUG("HDCP : TA_InvokeCommandEntryPoint [command: %d]\n", commandID);

	if (isInitialized(commandID)) {
		returnCode = TZ_COMMAND(commandID, request, req_size,
						params[1].memref.buffer, &params[1].memref.size);
	} else {
		TZ_HDCP_LOG("HDCP::doesn't initialize secure world & memory\n");
		returnCode = HDCP2_ERR;
	}
err :
	if (request != NULL)
		TEE_Free(request);

	return returnCode;
}
