  /*
   *  Object %CRYS_HASH.c    : %
   *  State           :  %state%
   *  Creation date   :  Wed Nov 17 17:25:55 2004
   *  Last modified   :  %modify_time%
   */
  /** @file
   *  \brief A brief description of this module
   *
   *  \version CRYS_HASH.c#1:csrc:6
   *  \author adams
   *  \remarks Copyright (C) 2004 by Discretix Technologies Ltd.
   *           All Rights reserved
   */



/************* Include Files ****************/

/* .............. CRYS level includes ................. */

#include "DX_VOS_Mem.h"
#include "DX_VOS_Memmap.h"
#include "CRYS_HASH_error.h"
#include "CRYS_HASH.h"
#include "error.h"
#include "crys_host_op_code.h"
#include "SEPDriver.h"
#include "gen.h"



/************************ Defines ******************************/


/************************ Enums ******************************/


/************************ macros ******************************/


/************************ Global Data ******************************/

#ifndef CRYS_NO_EXT_IF_MODE_SUPPORT
extern CRYSError_t  CRYS_CALLBACK_Handler();
#else
#define CRYS_CALLBACK_Handler DX_NULL
#endif

/************* Private function prototype ****************/


/************************ Public Functions ******************************/

/**
 * This function initializes the HASH machine on the CRYS level.
 *
 * This function allocates and initializes the HASH Context .
 * The function receives as input a pointer to store the context handle to HASH Context , 
 * it initializes the 
 * HASH Context with the cryptographic attributes that are needed for 
 * the HASH block operation ( initialize H's value for the HASH algorithm ).
 *
 * The function flow:
 *
 * 1) checking the validity of the arguments - returnes an error on an illegal argument case.
 * 2) Aquiring the working context from the CCM manager.
 * 3) Initializing the context with the parameters passed by the user and with the init values
 *    of the HASH.
 * 4) loading the user tag to the context.
 * 5) release the CCM context.
 * 
 * @param[in] ContextID_ptr - a pointer to the HASH context buffer allocated by the user that
 *                       is used for the HASH machine operation.
 *
 * @param[in] OperationMode - The operation mode : MD5 or SHA1.
 *
 * @return CRYSError_t on success the function returns CRYS_OK else non ZERO error.
 *      
 */
CEXPORT_C CRYSError_t CRYS_HASH_Init(CRYS_HASHUserContext_t*    ContextID_ptr,
                                     CRYS_HASH_OperationMode_t  OperationMode)
{
  /* The return error identifier */
  CRYSError_t   Error;
  
  /* offset */
  DxUint32_t    sramOffset;
  
  /* word parameter */
  DxUint32_t    messageParam[2];
  
  /* maximum parameter length */
  DxUint32_t    maxLength;
  
  
  /*---------------------------
      CODE
  -----------------------------*/
   
  /* initializing the Error to O.K */
  Error = CRYS_OK;

                 
  #ifndef CRYS_NO_HASH_SUPPORT                                      
      
  /* ............... checking the parameters validity ................... */
  /* -------------------------------------------------------------------- */
   
  /* if the users context ID pointer is DX_NULL return an error */
  if( ContextID_ptr == DX_NULL )
  {
    Error = CRYS_HASH_INVALID_USER_CONTEXT_POINTER_ERROR;
    goto end_function;
  }

  /* check if the operation mode is legal */
  if( OperationMode >= CRYS_HASH_NumOfModes )
  {
    Error = CRYS_HASH_ILLEGAL_OPERATION_MODE_ERROR;
    goto end_function;
  }
  
  
   /* lock access to the SEP */	  
   Error = SEPDriver_Lock();
   
   if(Error != DX_OK)
   {
       goto end_function;
   }
     
  /*----------------------------
      start sending message to SEP 
  -----------------------------*/
  sramOffset = 0;
   
  /* start the message */
  SEPDriver_StartMessage(&sramOffset);
  
  /* prepare message  */
  messageParam[0] = DX_SEP_HOST_SEP_PROTOCOL_HOST_HASH_INIT_OP_CODE;
  messageParam[1] = OperationMode;
  
  /* send message */
  Error = SEPDriver_WriteParamater((DxUint32_t)messageParam,
                           sizeof(DxUint32_t) * 2,
                           sizeof(DxUint32_t) * 2,
                           &sramOffset , 
                           DX_FALSE);
  if(Error != DX_OK)
  {
      goto end_function_unlock;
  }
   
  SEPDriver_EndMessage(sramOffset);
            
  /* wait for the response */
  Error = SEPDriver_POLL_FOR_REPONSE();
  if(Error != DX_OK)
  {
  	goto end_function_unlock;
  }
   
  /*-------------------
    start reading message from the SEP 
  ---------------------*/
   
  /* start the message */
  Error = SEPDriver_StartIncomingMessage(&sramOffset);
  if(Error != DX_OK)
  {
  	goto end_function_unlock;
  }
   
  /* read opcode + status */
  Error = SEPDriver_ReadParamater((DxUint32_t)messageParam,
                          sizeof(DxUint32_t) * 2,
                          sizeof(DxUint32_t) * 2, 
                          &sramOffset , 
                          DX_FALSE);
  if(Error != DX_OK)
  {
      goto end_function_unlock;
  }
   
  /* check the opcode */
  if(messageParam[0] != DX_SEP_HOST_SEP_PROTOCOL_HOST_HASH_INIT_OP_CODE)
  {
    Error = DX_WRONG_OPCODE_FROM_SEP_ERR;
    goto end_function_unlock;
  }
   
  /* check the status */
  if(messageParam[1] != CRYS_OK)
  {
    Error = messageParam[1];
    goto end_function_unlock;
  }
   
  /* read the context */
  maxLength = ((sizeof(CRYS_HASHUserContext_t) + 3) / sizeof(DxUint32_t)) * sizeof(DxUint32_t);
  Error = SEPDriver_ReadParamater((DxUint32_t)ContextID_ptr,
                           sizeof(CRYS_HASHUserContext_t),
                           maxLength,
                           &sramOffset,
                           DX_FALSE);
  if(Error != DX_OK)
  {
      goto end_function_unlock;
  }
                                           
  /* ...................... end of function ................................ */   

end_function_unlock:   

  /* lock access to the SEP */
  SEPDriver_Unlock();
  
                       
end_function:

  return Error;

  #endif /*!CRYS_NO_HASH_SUPPORT*/

}/* END OF CRYS_HASH_Init */                           

/**
 * This function process a block of data via the HASH Hardware.
 * The function receives as input an handle to the  HASH Context , that was initialized before
 * by an CRYS_HASH_Init function or by other CRYS_HASH_Update function. \n
 * The function Sets the hardware with the last H's value that where stored in
 * the CRYS HASH context and then process the data block using the hardware 
 * and in the end of the process stores in the HASH context the H's value 
 * HASH Context with the cryptographic attributes that are needed for 
 * the HASH block operation ( initialize H's value for the HASH algorithm ).
 * This function is used in cases not all the data is arrange in one 
 * continues buffer . \n
 *
 * The function flow:
 *
 * 1) checking the parameters validty if there is an error the function shall exit with an error code. 
 * 2) Aquiring the working context from the CCM manager.
 * 3) If there isnt enouth data in the previous update data buff in the context plus the received data
 *    load it to the context buffer and exit the function.
 * 4) fill the previous update data buffer to contain an entire block. 
 * 5) Calling the hardware low level function to execute the update.
 * 6) fill the previous update data buffer with the data not processed at the end of the received data.
 * 7) release the CCM context.
 * 
 * @param[in] ContextID_ptr - a pointer to the HASH context buffer allocated by the user that
 *                       is used for the HASH machine operation.
 *
 * @param DataIn_ptr a pointer to the buffer that stores the data to be 
 *                       hashed .
 * 
 * @param DataInSize  The size of the data to be hashed in bytes. 
 *
 * @return CRYSError_t on success the function returns CRYS_OK else non ZERO error.
 *
 */

CEXPORT_C CRYSError_t CRYS_HASH_Update(CRYS_HASHUserContext_t*    ContextID_ptr,
                                       DxUint8_t*                 DataIn_ptr,
                                       DxUint32_t                 DataInSize )
{                             
  /* The return error identifier */
  CRYSError_t         Error;
  
  /* address of the first table in the input list */
  DxUint32_t          inputLLITableAddr = 0;
   
  /* number of entries in each of the first tables */
  DxUint32_t          numEntriesInTable = 0;
  
  /* first table data size */
  DxUint32_t          firstTableDataSize = 0;
  
  /* offset */
  DxUint32_t          sramOffset;
  
  /* message parameter */
  DxUint32_t          messageParam[4]; 
  
  /* maximum parameter length */
  DxUint32_t          maxLength;  
  
  /* length of the head hash to complete */
  DxUint32_t 		  headBytesLen;
  
  /* length of the tail hash reminder */
  DxUint32_t 		  tailBytesLen;
   
  /* block size */
  DxUint32_t          blockSize;
  
      
   
  /*-----------------------------------
      CODE
  --------------------------------------*/
  
  /* initializing the Error to O.K */
  Error = CRYS_OK;
 
                       
  #ifndef CRYS_NO_HASH_SUPPORT                                      
      
  /* ............... checking the parameters validity ................... */
 
  /* if the users context ID pointer is DX_NULL return an error */
  if( ContextID_ptr == DX_NULL )
  {
    Error = CRYS_HASH_INVALID_USER_CONTEXT_POINTER_ERROR;
    goto end_function; 
  }
   
  /* if the data size is zero return CRYS_OK - we just do not need to process any data */
  if( DataInSize == 0 ) 
  {
    Error = CRYS_OK;
    goto end_function;
  }
   
  /* if the users Data In pointer is illegal and the data size is larger then 0 return an error */
  if( DataIn_ptr == DX_NULL )
  {
    Error = CRYS_HASH_DATA_IN_POINTER_INVALID_ERROR;
    goto end_function;
  }
           
  /* larger then 2^29 (to prevant an overflow on the transition to bits ) 
     return error */
  if( DataInSize >= (1 << 29) )
  {
    Error = CRYS_HASH_DATA_SIZE_ILLEGAL;
    goto end_function;
  }
  
  /* lock access to the SEP */	  
  Error = SEPDriver_Lock();
   
  if(Error != DX_OK)
  {
      goto end_function;
  }

  /* get the block size */
  blockSize = ((HASHContext_t*)(ContextID_ptr->context_buff))->OperationModeBlockSizeInBytes;

  /* calculate the data reminder length of the head part (may be zero) */
  headBytesLen = (blockSize - ((HASHContext_t*)(ContextID_ptr->context_buff))->NumOfBytesOnThePreviousUpdateBuff) % blockSize;
  
  /* copy the data to a local buffer only if not modulus of CRYS_HASH_BLOCK_SIZE_IN_BYTES*/
  if (headBytesLen > DataInSize)
  {
    headBytesLen = DataInSize;
  }
    
  /* calculate the context reminder length of the tail part (may be zero) */
  tailBytesLen = (DataInSize - headBytesLen) % blockSize;

  /* prepare tables for the input only*/
  Error = SEPDriver_PREPARE_INPUT_SYM_DMA_TABLE( (DxUint32_t)(DataIn_ptr + headBytesLen),
                                                 DataInSize - (headBytesLen + tailBytesLen),
                                                 blockSize,
                                                 &inputLLITableAddr,
                                                 &numEntriesInTable,
                                                 &firstTableDataSize );
  if(Error != DX_OK)
  {
    goto end_function_unlock;
  }
    
  
  /*----------------------------
      start sending message to SEP 
  -----------------------------*/
  sramOffset = 0;
   
  /* start the message */
  SEPDriver_StartMessage(&sramOffset);
  
  /* prepare part of message */
  messageParam[0] = DX_SEP_HOST_SEP_PROTOCOL_HOST_HASH_UPDATE_OP_CODE;
  messageParam[1] = inputLLITableAddr;
  messageParam[2] = numEntriesInTable;
  messageParam[3] = firstTableDataSize;
  
  /* send part of message  */
  Error = SEPDriver_WriteParamater((DxUint32_t)messageParam,
                           sizeof(DxUint32_t) * 4,
                           sizeof(DxUint32_t) * 4,
                           &sramOffset, 
                           DX_FALSE);
  if(Error != DX_OK)
  {
      goto end_function_unlock;
  }
  
  /* send head data buffer actual size */ 
  Error = SEPDriver_WriteParamater((DxUint32_t)&headBytesLen,
  						   sizeof(DxUint32_t),
                           sizeof(DxUint32_t),
                           &sramOffset ,
                           DX_FALSE);

  if(Error != DX_OK)
  {
      goto end_function_unlock;
  }

  /* send head data buffer completion */ 
  Error = SEPDriver_WriteParamater((DxUint32_t)DataIn_ptr,
  						   headBytesLen,
                           sizeof(DxUint32_t) * 32,
                           &sramOffset ,
                           DX_FALSE);
  
  if(Error != DX_OK)
  {
      goto end_function_unlock;
  }

  /* send tail data buffer actual size */ 
  Error = SEPDriver_WriteParamater((DxUint32_t)&tailBytesLen,
  						   sizeof(DxUint32_t),
                           sizeof(DxUint32_t),
                           &sramOffset ,
                           DX_FALSE);

  if(Error != DX_OK)
  {
      goto end_function_unlock;
  }

  /* send tail data buffer completion */ 
  Error = SEPDriver_WriteParamater((DxUint32_t)(DataIn_ptr + DataInSize - tailBytesLen),
  						   tailBytesLen,
                           sizeof(DxUint32_t) * 32,
                           &sramOffset,
                           DX_FALSE);
   
  if(Error != DX_OK)
  {
      goto end_function_unlock;
  }

  /* send context */
  maxLength = ((sizeof(CRYS_HASHUserContext_t) + 3) / sizeof(DxUint32_t)) * sizeof(DxUint32_t);
  Error = SEPDriver_WriteParamater((DxUint32_t)ContextID_ptr,
                           sizeof(CRYS_HASHUserContext_t),
                           maxLength,
                           &sramOffset ,
                           DX_FALSE);
  if(Error != DX_OK)
  {
      goto end_function_unlock;
  }

   
  SEPDriver_EndMessage(sramOffset);
            
  /* wait for the response */
  Error = SEPDriver_WaitForResponse(SEP_DRIVER_POLLING , 0 , CRYS_CALLBACK_Handler , SEP_DRIVER_TIMEOUT_WAIT_FOR_INIFINITY);
  if(Error != DX_OK)
  {
  	goto end_function_unlock;
  }
   
  /*-------------------
    start reading message from the SEP 
  ---------------------*/
  
  /* free allocated tables */
  Error = SEPDriver_FreeDMATables();
  if(Error != DX_OK)
  {
  	goto end_function_unlock;
  }
  
  /* start the message */
  Error = SEPDriver_StartIncomingMessage(&sramOffset);
  if(Error != DX_OK)
  {
  	goto end_function_unlock;
  }
   
  /* read opcode and status */
  Error = SEPDriver_ReadParamater((DxUint32_t)messageParam , 
                          sizeof(DxUint32_t) * 2,
                          sizeof(DxUint32_t) * 2,
                          &sramOffset , 
                          DX_FALSE);
  if(Error != DX_OK)
  {
      goto end_function_unlock;
  }
   
  /* check the opcode */
  if(messageParam[0] != DX_SEP_HOST_SEP_PROTOCOL_HOST_HASH_UPDATE_OP_CODE)
  {
    Error = DX_WRONG_OPCODE_FROM_SEP_ERR;
    goto end_function_unlock;
  }
   
  /* check status */
  if(messageParam[1] != CRYS_OK)
  {
    Error = messageParam[1];
    goto end_function_unlock;
  }
   
  /* read the context */
  maxLength = ((sizeof(CRYS_HASHUserContext_t) + 3) / sizeof(DxUint32_t)) * sizeof(DxUint32_t);
  Error = SEPDriver_ReadParamater((DxUint32_t)ContextID_ptr,
                           sizeof(CRYS_HASHUserContext_t),
                           maxLength,
                           &sramOffset,
                           DX_FALSE);
  if(Error != DX_OK)
  {
      goto end_function_unlock;
  }
                                           
end_function_unlock:

  /* unlock access to the SEP */
  SEPDriver_Unlock();                                          
                                                 
end_function:

  return Error;
   
   #endif /* !CRYS_NO_HASH_SUPPORT */   
 
}/* END OF CRYS_HASH_Update */ 

/**
 * This function finalize the hashing process of data block.
 * The function receives as input an handle to the HASH Context , that was initialized before
 * by an CRYS_HASH_Init function or by CRYS_HASH_Update function.
 * The function "adds" an header to the data block as the specific hash standard 
 * specifics , then it loads the hardware and reads the final message digest.
 *
 *  the function flow:
 *
 * 1) checking the parameters validty if there is an error the function shall exit with an error code. 
 * 2) Aquiring the working context from the CCM manager.
 * 3) Setting the padding buffer to load.
 * 4) Calling the hardware low level function to execute the finish.
 * 5) fill the previous update data buffer with the data not processed at the end of the received data.
 * 6) release the CCM context.
 *
 *  @param[in] ContextID_ptr - a pointer to the HASH context buffer allocated by the user that
 *                       is used for the HASH machine operation.
 *
 *  @retval HashResultBuff a pointer to the target buffer where the 
 *                       HASE result stored in the context is loaded to.
 *
 *  @return CRYSError_t on success the function returns CRYS_OK else non ZERO error.
 */

CEXPORT_C CRYSError_t CRYS_HASH_Finish( CRYS_HASHUserContext_t*   ContextID_ptr ,
                                        CRYS_HASH_Result_t        HashResultBuff )
{  
  /* The return error identifier */
  CRYSError_t   Error;
  
  /* offset */
  DxUint32_t    sramOffset;
  
  /* message params */
  DxUint32_t    messageParam[2];
  
  /* maximum parameter length */
  DxUint32_t    maxLength;
  
  

  /*---------------------------------
      CODE
  -----------------------------------*/                            
  
  Error = CRYS_OK;
                        
  #ifndef CRYS_NO_HASH_SUPPORT                                      
  
  /* ............... checking the parameters validity ................... */
 
  /* if the users context ID pointer is DX_NULL return an error */
  if( ContextID_ptr == DX_NULL )
  {
    Error = CRYS_HASH_INVALID_USER_CONTEXT_POINTER_ERROR;
    goto end_function;
  }
   
  /* if the result buffer DX_NULL return an error */
  if( HashResultBuff == DX_NULL )
  {
    Error = CRYS_HASH_INVALID_RESULT_BUFFER_POINTER_ERROR;
    goto end_function;
  }

   /* lock access to the SEP */	  
   Error = SEPDriver_Lock();
   
   if(Error != DX_OK)
   {
       goto end_function;
   }
     
  /*----------------------------
      start sending message to SEP 
  -----------------------------*/
  sramOffset = 0;
   
  /* start the message */
  SEPDriver_StartMessage(&sramOffset);
  
  /* send opcode */
  messageParam[0] = DX_SEP_HOST_SEP_PROTOCOL_HOST_HASH_FINISH_OP_CODE;
  Error = SEPDriver_WriteParamater((DxUint32_t)messageParam ,
                           sizeof(DxUint32_t),
                           sizeof(DxUint32_t),
                           &sramOffset , 
                           DX_FALSE);
  if(Error != DX_OK)
  {
      goto end_function_unlock;
  }
  
  /* send context */
  maxLength = ((sizeof(CRYS_HASHUserContext_t) + 3) / sizeof(DxUint32_t)) * sizeof(DxUint32_t);
  Error = SEPDriver_WriteParamater((DxUint32_t)ContextID_ptr , 
                            sizeof(CRYS_HASHUserContext_t) ,
                            maxLength,
                            &sramOffset ,
                            DX_FALSE); 
  if(Error != DX_OK)
  {
      goto end_function_unlock;
  }
  
  SEPDriver_EndMessage(sramOffset);
            
  /* wait for the response */
  Error = SEPDriver_POLL_FOR_REPONSE();
  if(Error != DX_OK)
  {
  	goto end_function_unlock;
  }
   
  /*-------------------
    start reading message from the SEP 
  ---------------------*/
   
  /* start the message */
  Error = SEPDriver_StartIncomingMessage(&sramOffset);
  if(Error != DX_OK)
  {
  	goto end_function_unlock;
  }
   
  /* read opcode +  status  */
  Error = SEPDriver_ReadParamater((DxUint32_t)messageParam ,
                          sizeof(DxUint32_t) * 2,
                          sizeof(DxUint32_t) * 2, 
                          &sramOffset , 
                          DX_FALSE);
  if(Error != DX_OK)
  {
      goto end_function_unlock;
  }
   
  /* check the opcode */
  if(messageParam[0] != DX_SEP_HOST_SEP_PROTOCOL_HOST_HASH_FINISH_OP_CODE)
  {
    Error = DX_WRONG_OPCODE_FROM_SEP_ERR;
    goto end_function_unlock;
  }
   
  if(messageParam[1] != CRYS_OK)
  {
    Error = messageParam[1];
    goto end_function_unlock;
  }
   
  /* read the result */
  maxLength = ((sizeof(CRYS_HASH_Result_t) + 3) / sizeof(DxUint32_t)) * sizeof(DxUint32_t);
  Error = SEPDriver_ReadParamater((DxUint32_t)HashResultBuff,
                           sizeof(CRYS_HASH_Result_t),
                           maxLength,
                           &sramOffset,
                            DX_FALSE);
  if(Error != DX_OK)
  {
      goto end_function_unlock;
  }
                                           
  /* ...................... end of function ................................ */   

end_function_unlock:   

  /* lock access to the SEP */
  SEPDriver_Unlock();
  
                       
end_function:

  return Error; 
 
  #endif /* !CRYS_NO_HASH_SUPPORT*/

}/* END OF CRYS_HASH_Finish */

/**
 * @brief This function is a service function that frees the context if the operation has
 *        failed. 
 *
 *        The function executes the following major steps:
 *
 *        1. Checks the validity of all of the inputs of the function. 
 *           If one of the received parameters is not valid it shall return an error.
 *
 *           The major checkers that are run over the received parameters:
 *           - verifying that the context pointer is not DX_NULL (*ContextID_ptr). 
 *        2. Clearing the users context.
 *        3. Exit the handler with the OK code.
 *      
 *
 * @param[in] ContextID_ptr - a pointer to the HASH context buffer allocated by the user that
 *                       is used for the HASH machine operation. this should be the same context that was
 *                       used on the previous call of this session.
 *
 *
 * @return CRYSError_t - On success CRYS_OK is returned, on failure a
 *                        value MODULE_* CRYS_HASH_error.h
 */
CEXPORT_C CRYSError_t  CRYS_HASH_Free(CRYS_HASHUserContext_t  *ContextID_ptr )
{
  /* The return error identifier */
  CRYSError_t Error;
  
  

  /*---------------------------
      CODE
  -----------------------------*/
 
  /* ............... local initializations .............................. */
   
  Error = CRYS_OK;
                      
  #ifndef CRYS_NO_HASH_SUPPORT                                      
  
  /* ............... checking the parameters validity ................... */
  
  /* if the users context ID pointer is DX_NULL return an error */
  if( ContextID_ptr == DX_NULL )
  {
    Error = CRYS_HASH_INVALID_USER_CONTEXT_POINTER_ERROR;
    goto end_function;
  }

  /* .............. clearing the users context .......................... */
  DX_VOS_MemSet( ContextID_ptr , 0 , sizeof(CRYS_HASHUserContext_t) );   

   /* ................. end of function ..................................... */
   /* ----------------------------------------------------------------------- */

end_function:
       
  return Error;
   
  #endif /* !CRYS_NO_HASH_SUPPORT */   
 
}/* END OF CRYS_HASH_Free */
                     
/**
 * This function provide HASH function to process one buffer of data.
 * The function allocates an internal HASH Context , it initializes the 
 * HASH Context with the cryptographic attributes that are needed for 
 * the HASH block operation ( initialize H's value for the HASH algorithm ).
 * Then the function loads the Hardware with the initializing values and after 
 * that process the data block using the hardware to do hash .
 * At the end the function return the message digest of the data buffer .
 *
 *
 * @param[in] OperationMode - The operation mode : MD5 or SHA1.
 * 
 * @param DataIn_ptr a pointer to the buffer that stores the data to be 
 *                       hashed .
 * 
 * @param DataInSize  The size of the data to be hashed in bytes. 
 *
 * @retval HashResultBuff a pointer to the target buffer where the 
 *                      HASE result stored in the context is loaded to.
 *
 * @return CRYSError_t on success the function returns CRYS_OK else non ZERO error.
 *
 */
CEXPORT_C CRYSError_t CRYS_HASH  ( CRYS_HASH_OperationMode_t    OperationMode,
                                   DxUint8_t*                   DataIn_ptr,
                                   DxUint32_t                   DataSize,
                                   CRYS_HASH_Result_t           HashResultBuff )
{    
  /* The return error identifier */
  CRYSError_t         Error;
  
  /* address of the first table in the input list */
  DxUint32_t          inputLLITableAddr = 0;
   
  /* number of entries in each of the first tables */
  DxUint32_t          numEntriesInTable = 0;
  
  /* first table data size */
  DxUint32_t          firstTableDataSize = 0;
  
  /* offset */
  DxUint32_t          sramOffset;
  
  /* message parameters */
  DxUint32_t          messageParam[5];
  
  /* maximum parameter length */
  DxUint32_t          maxLength;
  
  /* length of the tail hash data reminder */
  DxUint32_t 		  tailBytesLen;
   
  /* block size */
  DxUint32_t		  blockSize;


   
  /*-----------------------------
      CODE 
  -------------------------------*/
   
  /* ............... local initializations .............................. */
  Error = CRYS_OK;
  
  /* check if the operation mode is legal */
  if( OperationMode >= CRYS_HASH_NumOfModes )
  {
    Error = CRYS_HASH_ILLEGAL_OPERATION_MODE_ERROR;
    goto end_function;
  }
   
  /* if the users Data In pointer is illegal and the data size is larger then 0 return an error */
  if ((DataIn_ptr == DX_NULL) &&
      (DataSize != 0))
  {
    Error = CRYS_HASH_DATA_IN_POINTER_INVALID_ERROR;
    goto end_function;
  }
           
  /* larger then 2^29 (to prevant an overflow on the transition to bits ) 
     return error */
  if( DataSize >= (1 << 29) )
  {
    Error = CRYS_HASH_DATA_SIZE_ILLEGAL;
    goto end_function;
  }
  
  /* if the result buffer DX_NULL return an error */
  if( HashResultBuff == DX_NULL )
  {
    Error = CRYS_HASH_INVALID_RESULT_BUFFER_POINTER_ERROR;
    goto end_function;
  }
                             
  #ifndef CRYS_NO_HASH_SUPPORT  
  
  /* lock access to the SEP */	  
  Error = SEPDriver_Lock();
   
  if(Error != DX_OK)
  {
      goto end_function;
  }
  
  /* calculate block size */
  switch(OperationMode)
  {
  	case CRYS_HASH_SHA1_mode:
  	case CRYS_HASH_SHA224_mode:
  	case CRYS_HASH_SHA256_mode:
  	case CRYS_HASH_MD5_mode:
  		blockSize = CRYS_HASH_BLOCK_SIZE_IN_BYTES;
  		break;
  	case CRYS_HASH_SHA384_mode:
  	case CRYS_HASH_SHA512_mode:
  		blockSize = CRYS_HASH_SHA2_BLOCK_SIZE_IN_BYTES;
  		break;
  	default:
  		return CRYS_HASH_ILLEGAL_OPERATION_MODE_ERROR;
  }

  /* get the number of bytes from the beginning of the user buffer into the message */
  tailBytesLen = DataSize % blockSize;

  /* prepare tables for the input only */
  Error = SEPDriver_PREPARE_INPUT_SYM_DMA_TABLE( (DxUint32_t)DataIn_ptr,
                                                 DataSize - tailBytesLen,
                                                 blockSize,
                                                 &inputLLITableAddr,
                                                 &numEntriesInTable,
                                                 &firstTableDataSize );
  if(Error != DX_OK)
  {
    goto end_function_unlock;
  }
    
  
  /*----------------------------
      start sending message to SEP 
  -----------------------------*/
  sramOffset = 0;
   
  /* start the message */
  SEPDriver_StartMessage(&sramOffset);
  
  /* prepare message params */
  messageParam[0] = DX_SEP_HOST_SEP_PROTOCOL_HOST_HASH_OP_CODE;
  messageParam[1] = OperationMode;
  messageParam[2] = inputLLITableAddr;
  messageParam[3] = numEntriesInTable;
  messageParam[4] = firstTableDataSize;
  
  /* send message */
  Error = SEPDriver_WriteParamater((DxUint32_t)messageParam,
                           sizeof(DxUint32_t) * 5,
                           sizeof(DxUint32_t) * 5,
                           &sramOffset , 
                           DX_FALSE);
  if(Error != DX_OK)
  {
      goto end_function_unlock;
  }


  /* send tail data buffer actual size */ 
  Error = SEPDriver_WriteParamater((DxUint32_t)&tailBytesLen,
  						   sizeof(DxUint32_t),
                           sizeof(DxUint32_t),
                           &sramOffset ,
                           DX_FALSE);

  if(Error != DX_OK)
  {
      goto end_function_unlock;
  }

  /* send tail data buffer completion */ 
  Error = SEPDriver_WriteParamater((DxUint32_t)(DataIn_ptr + DataSize - tailBytesLen),
  						   tailBytesLen,
                           sizeof(DxUint32_t) * 32,
                           &sramOffset ,
                           DX_FALSE);
   
  if(Error != DX_OK)
  {
      goto end_function_unlock;
  }
  
  /* end message */ 
  SEPDriver_EndMessage(sramOffset);
            
  /* wait for the response */
  Error = SEPDriver_WaitForResponse(SEP_DRIVER_POLLING , 0 , CRYS_CALLBACK_Handler , SEP_DRIVER_TIMEOUT_WAIT_FOR_INIFINITY);
  if(Error != DX_OK)
  {
  	goto end_function_unlock;
  }
   
  /*-------------------
    start reading message from the SEP 
  ---------------------*/
  
  /* free allocated tables */
  Error = SEPDriver_FreeDMATables();
  if(Error != DX_OK)
  {
  	goto end_function_unlock;
  }
   
  /* start the message */
  Error = SEPDriver_StartIncomingMessage(&sramOffset);
  if(Error != DX_OK)
  {
  	goto end_function_unlock;
  }
   
  /* read opcode + status  */
  Error = SEPDriver_ReadParamater((DxUint32_t)messageParam , 
                          sizeof(DxUint32_t) * 2,
                          sizeof(DxUint32_t) * 2,
                          &sramOffset , DX_FALSE);
  if(Error != DX_OK)
  {
      goto end_function_unlock;
  }
   
  /* check the opcode */
  if(messageParam[0] != DX_SEP_HOST_SEP_PROTOCOL_HOST_HASH_OP_CODE)
  {
    Error = DX_WRONG_OPCODE_FROM_SEP_ERR;
    goto end_function_unlock;
  }
   
  /* check the status */
  if(messageParam[1] != CRYS_OK)
  {
    Error = messageParam[1];
    goto end_function_unlock;
  }
   
  /* read the result */
  maxLength = ((sizeof(CRYS_HASH_Result_t) + 3) / sizeof(DxUint32_t)) * sizeof(DxUint32_t);
  Error = SEPDriver_ReadParamater((DxUint32_t)HashResultBuff,
                          sizeof(CRYS_HASH_Result_t),
                          maxLength,
                          &sramOffset,
                          DX_FALSE);
  if(Error != DX_OK)
  {
      goto end_function_unlock;
  }
                                           
end_function_unlock:

  /* unlock access to the SEP */
  SEPDriver_Unlock();                                          
                                                 
end_function:

  return Error;
   
  #endif /* !CRYS_NO_HASH_SUPPORT */
   
}/* END OF CRYS_HASH */   
