/*
   *  Object %name    : FVOS.c
   *  State           :  %state%
   *  Creation date   :  Wed Nov 17 17:39:24 2004
   *  Last modified   :  %modify_time%
   */
  /** @file
   *  \brief Secure Boot implemtation of hash queues
   *
   *  \version 
   *  \author yevgenys
   *  \remarks Copyright (C) 2004 by Discretix Technologies Ltd.
   *           All Rights reserved
   */
#include <string.h>
#include <sys/mman.h>
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <poll.h>
#include <unistd.h>
#include <errno.h>
#include "DX_VOS_BaseTypes.h"
#include "FVOS_Types.h"
#include "FVOS_API.h"
#include "FVOS_Config.h"
#include "FVOS_HwDefs.h"
#include "FVOS_Error.h"



/*-------------------------
  GLOBAL variables
--------------------------*/

/* the opened file descriptor for the device driver */
DxInt32_t     g_FD;


/* address of the SEP shared area */
DxUint32_t    g_FVOS_SharedAreaAddress;

/* start mmap address */
void*         g_StartMapAddress;


FVOS_FlowContext_t  g_FlowsContexts[FVOS_NUM_FLOWS];
/*-------------------------
  DEFINES
--------------------------*/


/*-----------------------------
    prototypes
-------------------------------*/


/* function to be called as callback for NVS transactions */
extern FVOS_Error_t NVSTreadParser(void);



/*--------------------------
    FUNCTIONS
----------------------------*/

/**
 * @brief     This functionsconfigurates the FVOS - shared area address
 *
 * @param[in] sharedAreaAddr - address of the shared area
 * @return     DxError_t_t:  
 *                        
 */
FVOS_Error_t  FVOS_Config(DxUint32_t sharedAreaAddr)
{
  return FVOS_OK;
}


/**
 * @brief     This functions start the transaction to the SEP - this is the first function to be called during transaction to SEP
 *            In NoOS configuration no action needs to be performed
 * 
 * @return     DxError_t_t:  
 *                        
 */
FVOS_Error_t FVOS_StartTransaction()
{
  /* error */
  FVOS_Error_t  error;
  
  /*-----------------------------
      CODE
  ---------------------------------*/
  
  error = FVOS_OK;
  
  /* open device driver */
  g_FD = open(SEP_DRIVER_PATH , O_RDWR|O_SYNC);
  if(g_FD < 0)
  {
    error = FVOS_ACCESS_DENIED_ERR;
  }
  
  return error;
}

/**
 * @brief     This function end the transaction to SEP - this is the last function in the transaction
 *            In NoOS configuration no action needs to be performed
 *
 * @return     DxError_t_t:  
 *                        
 */
FVOS_Error_t FVOS_EndTransaction(DxUint32_t mappedAddr)
{
  /* error */
  FVOS_Error_t  error;
  
  /* status of the linux system call */
  DxInt32_t     status;
  
  /*-----------------------------
      CODE
  ---------------------------------*/
  
  error = FVOS_OK;
  
  /* unmap the shared message area */
  munmap((void*)g_StartMapAddress , SEP_DRIVER_MMAP_AREA_SIZE);
  
  /* reset the map address */
  g_StartMapAddress = 0;
  
  /* signal the end of the transaction - relase mutex. can not do this during close, since the release method of driver might not be called */
  status = ioctl(g_FD , SEP_DRIVER_END_TRANSACTION_CMD);
  if(status < 0)
  {
    errno = errno;
    perror("Errno is:");
    error = FVOS_SYS_CALL_FAIL_ERR;
    goto end_function;
  }
  
  /* close device driver */
  status = close(g_FD);
  if(status < 0)
  {
    error = FVOS_ACCESS_DENIED_ERR;
  }

end_function:
  
  return error;
}

/**
 * @brief     This function maps the shared message area to the application memory. It returns a pointer,
 *            trough which application can directly write message data into message shared area
 * 
 * @param[out] mappedAddr_ptr - the mapped address will be returned into this pointer
 *
 * @return     DxError_t_t:  
 *                        
 */
FVOS_Error_t FVOS_Mmap(DxUint32_t* mappedAddr_ptr)
{
  /* error */
  FVOS_Error_t  error;
  
  /* start address of mapping */
  void*         startAddress;
  
  int           s_error;
  
  /*-----------------------------
      CODE
  ---------------------------------*/
  
  error = FVOS_OK;
  g_StartMapAddress = mmap(0 , SEP_DRIVER_MMAP_AREA_SIZE , PROT_WRITE|PROT_READ , MAP_SHARED , g_FD , 0);
  if(g_StartMapAddress == (void*)-1)
  {
    error = FVOS_MESSAGE_AREA_ACCESS_ERR;
  }

  *mappedAddr_ptr = (DxUint32_t)g_StartMapAddress + sizeof(DxUint32_t) * 2;
  
  return error;
}


/**
 * @brief     This function performs a number of commands according to the CMD paramater:
 *            1) Signal to SEP that the message is ready
 *            2) Allocate memory continues locked memory
 *            3) Read from the allocated memory
 *            4) Write into allocated memory
 * 
 * @param[in] commandId - the id of command to execute
 * @param[in] cmdData_ptr - pointer to the strucutre that contains the paramaters for command execution
 *
 * @return     DxError_t_t:  
 *                        
 */
FVOS_Error_t FVOS_Cntrl(FVOS_Cntrl_cmd_t commandId , FVOS_Cntrl_info_t* cmdData_ptr)
{
  /* error */
  FVOS_Error_t                error;
  
  /* the linux ioctl struct */
  FVOS_Driver_Ioctl_Params_t  params;
  
  /*-----------------------------
      CODE
  ---------------------------------*/
  
  error = FVOS_OK;
  
  switch(commandId)
  {
    case FVOS_CNTRL_SEND_MSG_RDY_CMD:
    
      msync(g_StartMapAddress , SEP_DRIVER_MMAP_AREA_SIZE , MS_SYNC);
      
      /* send command to SEP */
      error = ioctl(g_FD , SEP_DRIVER_SEND_MSG_IOCTL_CMD);
      
      break;
    
    case FVOS_CNTRL_SEND_RPLY_MSG_RDY_CMD:
      
      /* send reply message */
      error = ioctl(g_FD , SEP_DRIVER_SEND_RPLY_MSG_IOCTL_CMD);
      break;
      
    case FVOS_CNTRL_ALLOC_MEM_CMD:
      
	    params.data.fvos_driver_alloc_t.num_bytes = cmdData_ptr->data.allocCmdData.allocSize; 

      /* send allocate request */
      error = ioctl(g_FD , SEP_DRIVER_ALLOC_MEM_IOCTL_CMD , &params);
      
      /* update the output */
      cmdData_ptr->data.allocCmdData.virtAddr = (DxUint32_t)(g_StartMapAddress + params.data.fvos_driver_alloc_t.offset);
      cmdData_ptr->data.allocCmdData.physAddr = params.data.fvos_driver_alloc_t.phys_address;
      
      break;
      
    case FVOS_CNTRL_WRITE_DATA_CMD:
    
      /* data is direct memory mapped - copy */
      memcpy((void*)cmdData_ptr->data.writeCmdData.destFvosAddr,
             (void*)cmdData_ptr->data.writeCmdData.srcAppAddr,
             cmdData_ptr->data.writeCmdData.dataSize);
             
      break;
      
    case FVOS_CNTRL_READ_DATA_CMD:
    
      /* data is direct memory mapped - copy */
      memcpy((void*)cmdData_ptr->data.readCmdData.destAppAddr,
             (void*)cmdData_ptr->data.readCmdData.srcFvosAddr,
             cmdData_ptr->data.readCmdData.dataSize);
      
      break;
      
    case FVOS_CNTRL_BUILD_SYM_DMA_TABLES_CMD:
      
      /* set parameters */
      params.data.fvos_driver_build_sym_table_t.app_in_address		= cmdData_ptr->data.buildSymDMATables.dataInAddr;
      params.data.fvos_driver_build_sym_table_t.data_in_size		= cmdData_ptr->data.buildSymDMATables.dataInSize;
      params.data.fvos_driver_build_sym_table_t.app_out_address		= cmdData_ptr->data.buildSymDMATables.dataOutAddr;
      params.data.fvos_driver_build_sym_table_t.block_size			= cmdData_ptr->data.buildSymDMATables.blockSize;
      
	    /* send write request */
      error = ioctl(g_FD , SEP_DRIVER_CREATE_SYM_DMA_TABLE_CMD , &params);

	    /* update the output */
	    cmdData_ptr->data.buildSymDMATables.firstInTableAddr        = params.data.fvos_driver_build_sym_table_t.in_table_address;
      cmdData_ptr->data.buildSymDMATables.firstInTableNumEntries	= params.data.fvos_driver_build_sym_table_t.in_table_num_entries;
	    cmdData_ptr->data.buildSymDMATables.firstOutTableAddr			  = params.data.fvos_driver_build_sym_table_t.out_table_address;
	    cmdData_ptr->data.buildSymDMATables.firstOutTableNumEntries = params.data.fvos_driver_build_sym_table_t.out_table_num_entries;
	    cmdData_ptr->data.buildSymDMATables.firstTableDataSize      = params.data.fvos_driver_build_sym_table_t.table_data_size;
    
      break;
      
	case FVOS_CNTRL_BUILD_FLOW_DMA_TABLES_CMD:
	  
  	  params.data.fvos_driver_build_flow_table_t.flow_type				= cmdData_ptr->data.buildFlowDMATables.flowType;
  	  params.data.fvos_driver_build_flow_table_t.input_output_flag		= cmdData_ptr->data.buildFlowDMATables.inputOutputFlag;
  	  params.data.fvos_driver_build_flow_table_t.virt_buff_data_addr	= cmdData_ptr->data.buildFlowDMATables.buffersArrayAddr;
  	  params.data.fvos_driver_build_flow_table_t.num_virtual_buffers	= cmdData_ptr->data.buildFlowDMATables.buffersArraySize;
	
	    /* send build flow request */
      error = ioctl(g_FD , SEP_DRIVER_CREATE_FLOW_DMA_TABLE_CMD , &params);

  	  /* update the output */
  	  cmdData_ptr->data.buildFlowDMATables.firstInTableAddr			= params.data.fvos_driver_build_flow_table_t.first_table_addr;
  	  cmdData_ptr->data.buildFlowDMATables.firstInTableNumEntries	= params.data.fvos_driver_build_flow_table_t.first_table_num_entries;
  	  cmdData_ptr->data.buildFlowDMATables.firstTableDataSize		= params.data.fvos_driver_build_flow_table_t.first_table_data_size;

	    break;

	case FVOS_CNTRL_ADD_FLOW_DMA_TABLES_CMD:
	  
      params.data.fvos_driver_add_flow_table_t.flow_id				= cmdData_ptr->data.addTables.flowId;
  	  params.data.fvos_driver_add_flow_table_t.input_output_flag	= cmdData_ptr->data.addTables.inputOutputFlag;
  	  params.data.fvos_driver_add_flow_table_t.virt_buff_data_addr	= cmdData_ptr->data.addTables.buffersArrayAddr;
  	  params.data.fvos_driver_add_flow_table_t.num_virtual_buffers	= cmdData_ptr->data.addTables.buffersArraySize;
		
	    /* send add flow tables request */
      error = ioctl(g_FD , SEP_DRIVER_ADD_FLOW_TABLE_CMD , &params);

  	  /* update the output */
  	  cmdData_ptr->data.addTables.firstTableAddr			= params.data.fvos_driver_add_flow_table_t.first_table_addr;
  	  cmdData_ptr->data.addTables.firstTableNumEntries	= params.data.fvos_driver_add_flow_table_t.first_table_num_entries;
  	  cmdData_ptr->data.addTables.firstTableDataSize		= params.data.fvos_driver_add_flow_table_t.first_table_data_size;

	    break;

	case FVOS_CNTRL_ADD_FLOW_TABLE_MSG_CMD:
	  
  	  params.data.fvos_driver_add_message_t.flow_id					= cmdData_ptr->data.addTablesMessage.flowId;
  	  params.data.fvos_driver_add_message_t.message_address			= cmdData_ptr->data.addTablesMessage.messageAddress;
  	  params.data.fvos_driver_add_message_t.message_size_in_bytes	= cmdData_ptr->data.addTablesMessage.messageSizeInWords * 4 /* DX_BYTES_IN_WORD :) ??*/ ;

  	  /* send add flow message request */
      error = ioctl(g_FD , SEP_DRIVER_ADD_FLOW_MESSAGE_CMD , &params);
	    
	    break;

	case FVOS_CNTRL_SET_FLOW_ID_IN_TABLES_CMD:
		
	    params.data.fvos_driver_set_flow_id_t.flow_id	= cmdData_ptr->data.setFlowId.flowId;

	    /* send set flow id request */
      error = ioctl(g_FD , SEP_DRIVER_SET_FLOW_ID_CMD , &params);
	    
	    break;

    case FVOS_CNTRL_FREE_DMA_RESOURSES:
      
      /* send free dma request */
      error = ioctl(g_FD , SEP_DRIVER_FREE_SYM_DMA_TABLE_CMD);
      
      break;
    
	case FVOS_CNTRL_SEP_INIT:

  	  params.data.fvos_driver_init_t.message_addr = cmdData_ptr->data.sepInit.messageAddr;
  	  params.data.fvos_driver_init_t.message_size_in_words = cmdData_ptr->data.sepInit.messageSizeInWords;
  	  params.data.fvos_driver_init_t.sep_sram_addr = cmdData_ptr->data.sepInit.SepSramAddr;
  	 
  	  /* send sep init request */
      error = ioctl(g_FD , SEP_DRIVER_SEP_INIT_CMD , &params);
	  
	    break;
	    
  case FVOS_REALLOC_CACHE_RESIDENT:
  
      params.data.fvos_driver_realloc_cache_resident_t.base_addr = cmdData_ptr->data.reallocCachResident.baseAddr;
      params.data.fvos_driver_realloc_cache_resident_t.cache_addr = cmdData_ptr->data.reallocCachResident.cacheAddr;
      params.data.fvos_driver_realloc_cache_resident_t.cache_size_in_bytes = cmdData_ptr->data.reallocCachResident.cacheSize;
      params.data.fvos_driver_realloc_cache_resident_t.resident_addr = cmdData_ptr->data.reallocCachResident.residentAddr;
      params.data.fvos_driver_realloc_cache_resident_t.resident_size_in_bytes = cmdData_ptr->data.reallocCachResident.residentSize;
      params.data.fvos_driver_realloc_cache_resident_t.dcache_addr = cmdData_ptr->data.reallocCachResident.dcacheAddr;
      params.data.fvos_driver_realloc_cache_resident_t.dcache_size_in_bytes = cmdData_ptr->data.reallocCachResident.dcacheSize;
      params.data.fvos_driver_realloc_cache_resident_t.extcache_addr = cmdData_ptr->data.reallocCachResident.extCacheAddr;
      params.data.fvos_driver_realloc_cache_resident_t.extcache_size_in_bytes = cmdData_ptr->data.reallocCachResident.extCacheSize;
      
      error = ioctl(g_FD , SEP_DRIVER_REALLOC_CACHE_RESIDENT_CMD , &params);
      
      cmdData_ptr->data.reallocCachResident.newBaseAddr = params.data.fvos_driver_realloc_cache_resident_t.new_base_addr;
      cmdData_ptr->data.reallocCachResident.newSharedAreaAddr = params.data.fvos_driver_realloc_cache_resident_t.new_shared_area_addr;
      cmdData_ptr->data.reallocCachResident.newResidentAddr = params.data.fvos_driver_realloc_cache_resident_t.new_resident_addr;
      cmdData_ptr->data.reallocCachResident.newCacheAddr = params.data.fvos_driver_realloc_cache_resident_t.new_cache_addr;
      cmdData_ptr->data.reallocCachResident.newDCacheAddr = params.data.fvos_driver_realloc_cache_resident_t.new_dcache_addr;
      
      break;
      
  case FVOS_REALLOC_EXT_CACHE_RESIDENT:
  
      params.data.fvos_driver_realloc_ext_cache_t.ext_cache_addr = cmdData_ptr->data.reallocExtCache.cacheAddr;
      params.data.fvos_driver_realloc_ext_cache_t.ext_cache_size_in_bytes = cmdData_ptr->data.reallocExtCache.cacheSize;
      
      error = ioctl(g_FD , SEP_DRIVER_REALLOC_EXT_CACHE_CMD , &params);
      
      cmdData_ptr->data.reallocExtCache.newCacheAddr = params.data.fvos_driver_realloc_ext_cache_t.new_ext_cache_addr;
      
      break;

	case FVOS_CNTRL_GET_STATIC_POOL_ADDR:
	  
  	  /* send get static pool addresses request */
  	  error = ioctl(g_FD , SEP_DRIVER_GET_STATIC_POOL_ADDR_CMD , &params);

  	  /* update the output */
  	  cmdData_ptr->data.staticPoolInfo.physAddr		= params.data.fvos_driver_static_pool_addr_t.physical_static_address;
  	  cmdData_ptr->data.staticPoolInfo.fvosAddr		= params.data.fvos_driver_static_pool_addr_t.virtual_static_address;

	    break;
	    
  case FVOS_CNTRL_CONV_STATIC_PHYS_TO_VIRT:
  
      params.data.fvos_driver_get_phys_addr_offset_t.physical_address = cmdData_ptr->data.convStatPhysToVirt.physAddr;
      
      /* send get static pool addresses request */
  	  error = ioctl(g_FD , SEP_DRIVER_GET_PHYS_ADDR_OFFET_FROM_MAP_START_CMD , &params);
      
      cmdData_ptr->data.convStatPhysToVirt.virtAddr = (DxUint32_t)(g_StartMapAddress + params.data.fvos_driver_get_phys_addr_offset_t.offset);
  	  
  	  break;

  case FVOS_CNTRL_SEP_API_MODE:
      
      params.data.fvos_driver_set_api_mode_t.mode = cmdData_ptr->data.setAPIMode.blockingMode;
    
      error = ioctl(g_FD , SEP_DRIVER_SET_API_MODE_CMD , &params);
    
      break;
      
    case FVOS_CNTRL_GET_TIME:
    
      error = ioctl(g_FD , SEP_DRIVER_GET_TIME_CMD , &params);
      
      cmdData_ptr->data.getTime.timeAddress = params.data.fvos_driver_get_time_t.time_addr;
      cmdData_ptr->data.getTime.timeValue = params.data.fvos_driver_get_time_t.time_val;
      
      break;
      
    case FVOS_CHECK_CONFIGURATION:
    
      /* in OS, the configuration is defined by the driver */
      error = 0;
      
      break;
    
    default:
      
      error = FVOS_SYS_CALL_FAIL_ERR;
    
      break;
  }
  
  return error;
}


/**
 * @brief     This function polls for the for the response from SEP
 *            Polling can be blocking or busy wait,and can be regulated by timeout
 * @param[in] Timeout - timeout for polling
 * @param[out] pollSource_ptr - the source of the poll interrupt (regular response, or MVS request from SEP
 * @return     DxError_t_t:  
 *                        
 */
FVOS_Error_t FVOS_Poll(DxUint32_t  timeout , DxUint32_t* pollSource_ptr)
{
  /* error */
  FVOS_Error_t    error;
  
  /* polling error */
  DxInt32_t       poll_error;
  
  /* array of FDs for the poll */
  struct pollfd   fd_array[1];

  /*-----------------------
      CODE
  -------------------------*/
  
  error = FVOS_OK;
  
  /* set paramaters */
  fd_array[0].fd = g_FD;
  fd_array[0].events = POLLIN | POLLOUT | POLLHUP;
  
  /* call the driver */
  if(timeout == FVOS_TIMEOUT_INFINITE)
  {
    poll_error = poll(fd_array , 1 , -1);
  }
  else
  {
    poll_error = poll(fd_array , 1 , timeout);
  }
  
  /* check if timeout occured */
  if(poll_error == 0)
  {
    error = FVOS_TIMEOUT_ERR;
    goto end_function;
  }
  if(poll_error == -1)
  {
    error = FVOS_SYS_CALL_FAIL_ERR;
    goto end_function;
  }
  
  if(fd_array[0].revents & POLLIN)
  {
    *pollSource_ptr = FVOS_SEP_REPLY;
    goto end_function;
  }
  
  if(fd_array[0].revents & POLLOUT)
  {
    *pollSource_ptr = FVOS_SEP_REQUEST;
    goto end_function;
  }
  
  if(fd_array[0].revents & POLLHUP)
  {
    *pollSource_ptr = FVOS_SEP_PRINTF;
  }
  
end_function:

  msync(g_StartMapAddress , SEP_DRIVER_MMAP_AREA_SIZE , MS_SYNC);
  
  return error;
}


/**
 * @brief     This function return the printf buffer used by the SEP,
 *            
 * 
 * @param[out] printfBuff_ptr - the mapped address will be returned into this pointer
 *
 * @return     DxError_t_t:  
 *                        
 */
FVOS_Error_t FVOS_GetPrintfBuffer(DxUint32_t* printfBuff_ptr)
{
  /*-----------------------------
      CODE
  ---------------------------------*/
  
  if(g_StartMapAddress == 0)
  {
    /* the memory is not mapped - access denied */
    printfBuff_ptr = 0;
    
    return FVOS_ACCESS_DENIED_ERR;
  }
  
  /* The printf buffer is located at the end of the shared area for the message (last 256 bytes from 6K) */
  *printfBuff_ptr = g_StartMapAddress + 5888; /*5888 = 6K-256*/
  
  return FVOS_OK;
}
