#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "gd32f107c_eval.h"
#include "gd32f10x_gpio.h"
#include "stdio.h"


/* System application includes. */
#include "FreeRTOSIPConfig.h"

/* Demo application includes. */
#include "FreeRTOS_IP.h"
#include "FreeRTOS_Sockets.h"
#include "SimpleUDPClientAndServer.h"
#include "SimpleTCPEchoServer.h"
#include "TCPEchoClient_SingleTasks.h"
#include "rf_switch_driver.h"

#define BUTTON_USER GPIO_PIN_14

#define LED2_USER_PORT GPIOC
#define LED5_TICK_PORT GPIOE
#define RF_SWITCH_PORT GPIOE
#define RF_SWITCH_PIN_A GPIO_PIN_10
#define RF_SWITCH_PIN_B GPIO_PIN_11
#define RF_SWITCH_PIN_C GPIO_PIN_12
#define RF_SWITCH_PIN_EN GPIO_PIN_13

#define LED2_USER GPIO_PIN_0
#define LED5_TICK GPIO_PIN_1

#define TASK_HELLO_WORLD_DELAY 500
#define TASK_TOGGLE_LED_DELAY 125

#define OSC32_PORT			GPIOC
#define OSC32_IN  GPIO_PIN_14
#define OSC32_OUT GPIO_PIN_15
/*
*	RMII interface port & pin definitions
*/
#define RMII_INT_PORT				GPIOB
#define RMII_INT			GPIO_PIN_0

#define RMII_REF_CLK_PORT		GPIOA
#define RMII_REF_CLK	GPIO_PIN_1
#define RMII_MCO			GPIO_PIN_8

#define RMII_MDC_PORT				GPIOC
#define RMII_MDC			GPIO_PIN_1

#define RMII_MDIO_PORT			GPIOA
#define RMII_MDIO			GPIO_PIN_2

#define RMII_RXD_PORT				GPIOC
#define RMII_RXD0			GPIO_PIN_4
#define RMII_RXD1			GPIO_PIN_5

#define RMII_CRS_DV_PORT		GPIOA
#define RMII_CRS_DV		GPIO_PIN_7

#define RMII_TXD_PORT				GPIOB
#define RMII_TX_EN		GPIO_PIN_11
#define RMII_TXD0			GPIO_PIN_12
#define RMII_TXD1			GPIO_PIN_13


#define TEST_RUNNER_TASK_STACK_SIZE    512

#define mainCREATE_SIMPLE_UDP_CLIENT_SERVER_TASKS     0
#define mainCREATE_TCP_ECHO_TASKS_SINGLE              0
#define mainCREATE_TCP_ECHO_SERVER_TASK               1

/* Simple UDP client and server task parameters. */
#define mainSIMPLE_UDP_CLIENT_SERVER_TASK_PRIORITY    ( tskIDLE_PRIORITY + 1 )
#define mainSIMPLE_UDP_CLIENT_SERVER_PORT             ( 5005UL )

/* Echo client task parameters - used for both TCP and UDP echo clients. */
#define mainECHO_CLIENT_TASK_STACK_SIZE               ( configMINIMAL_STACK_SIZE * 2 )      /* Not used in the Windows port. */
#define mainECHO_CLIENT_TASK_PRIORITY                 ( tskIDLE_PRIORITY + 1 )

/* Echo server task parameters. */
#define mainECHO_SERVER_TASK_STACK_SIZE               ( configMINIMAL_STACK_SIZE * 2 )      /* Not used in the Windows port. */
#define mainECHO_SERVER_TASK_PRIORITY                 ( tskIDLE_PRIORITY + 1 )

/* Define a name that will be used for LLMNR and NBNS searches. */
#define mainHOST_NAME                                 "GD32F107C_TCP_UDP_SERVER"
#define mainDEVICE_NICK_NAME                          "GD32F107C"

#ifdef DEBUG
		#define BKPT_DEBUG() __BKPT();						
#else
		#define BKPT_DEBUG()
#endif

TaskHandle_t vTaskHelloWorld_Handle, vTaskToggleLed_Handle;
/* Default MAC address configuration.  The demo creates a virtual network
 * connection that uses this MAC address by accessing the raw Ethernet data
 * to and from a real network connection on the host PC.  See the
 * configNETWORK_INTERFACE_TO_USE definition for information on how to configure
 * the real network connection to use. */
const uint8_t ucMACAddress[ 6 ] =
{
    configMAC_ADDR0,
    configMAC_ADDR1,
    configMAC_ADDR2,
    configMAC_ADDR3,
    configMAC_ADDR4,
    configMAC_ADDR5
};

/* The default IP and MAC address used by the demo.  The address configuration
 * defined here will be used if ipconfigUSE_DHCP is 0, or if ipconfigUSE_DHCP is
 * 1 but a DHCP server could not be contacted.  See the online documentation for
 * more information.  In both cases the node can be discovered using
 * "ping RTOSDemo". */
static const uint8_t ucIPAddress[ 4 ] =
{
    configIP_ADDR0,
    configIP_ADDR1,
    configIP_ADDR2,
    configIP_ADDR3
};
static const uint8_t ucNetMask[ 4 ] =
{
    configNET_MASK0,
    configNET_MASK1,
    configNET_MASK2,
    configNET_MASK3
};
static const uint8_t ucGatewayAddress[ 4 ] =
{
    configGATEWAY_ADDR0,
    configGATEWAY_ADDR1,
    configGATEWAY_ADDR2,
    configGATEWAY_ADDR3
};
static const uint8_t ucDNSServerAddress[ 4 ] =
{
    configDNS_SERVER_ADDR0,
    configDNS_SERVER_ADDR1,
    configDNS_SERVER_ADDR2,
    configDNS_SERVER_ADDR3
};

/* Use by the pseudo random number generator. */
static UBaseType_t ulNextRand;
struct rf_switch_config rfSwitchConfig;
/*
 * Just seeds the simple pseudo random number generator.
 */
static void prvSRand( UBaseType_t ulSeed );

/*
 * Miscellaneous initialisation including preparing the logging and seeding the
 * random number generator.
 */
static void prvMiscInitialisation( void );

void vTaskHelloWorld( void *pvParameters);
void vTaskToggleLed( void *pvParameters);
int stdout_putchar (int ch);
int stdin_getchar (void);
void DelayMS(unsigned char ms);

int stdout_putchar (int ch)
{
	usart_data_transmit(EVAL_COM1, (uint16_t)ch);	
	while (!usart_flag_get(EVAL_COM1, USART_FLAG_TBE)){}
	
	return ch;
}
int stdin_getchar (void)
{	
	return usart_data_receive(EVAL_COM1);
}

/**! \brief ErrStatus xInitPLL2(void)
*		Enable PLL2 to generate 50MHz clocks 
*/
static ErrStatus xInitPLL2(void)
{		
		/*CK_PREDIV1 = (CK_HXTAL)/5 = 5 MHz */ 
		if (!(RCU_CFG1 & RCU_PREDV1_DIV5)) return ERROR;
		/* CK_PLL2 = (CK_PREDIV1) * 10 = 50 MHz */
		rcu_pll2_config(RCU_PLL2_MUL10);
    /* enable PLL2 */
    RCU_CTL |= RCU_CTL_PLL2EN;
    /* wait till PLL2 is ready */
    while(RESET == rcu_flag_get(RCU_FLAG_PLL2STB)){
    };
		return SUCCESS;
}

/**! \brief vInitMCU
*		Initial MCU configuration
*/
static void vInitMCU(void)
{
	SystemInit();	
	
	rcu_periph_clock_enable(RCU_GPIOA);
	rcu_periph_clock_enable(RCU_GPIOB);
	rcu_periph_clock_enable(RCU_GPIOC);
	rcu_periph_clock_enable(RCU_GPIOE);
	rcu_periph_clock_enable(RCU_AF);
	rcu_periph_clock_enable(RCU_ENET);
	rcu_periph_clock_enable(RCU_ENETTX);
	rcu_periph_clock_enable(RCU_ENETRX);		
	rcu_periph_clock_enable(RCU_PMU);
	rcu_periph_reset_enable(RCU_BKPIRST);
	rcu_periph_reset_disable(RCU_BKPIRST);		
	rcu_periph_clock_enable(RCU_BKPI);
	
	
	/* Configure RTC */
#ifndef DEBUG_RTC_LXTAL	
	gpio_init(OSC32_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_2MHZ, OSC32_IN); 
	gpio_init(OSC32_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_2MHZ, OSC32_OUT); 
	
	if (FALSE == rtc_counter_get() & FALSE == rtc_flag_get(RTC_CTL_OVIF))
	{			
		pmu_backup_write_enable();
		bkp_deinit();
		rcu_osci_on(RCU_LXTAL);				
		rcu_periph_clock_enable(RCU_RTC);		
		rcu_rtc_clock_config(RCU_RTCSRC_LXTAL);			
		
		while (RESET == (RCU_BDCTL & RCU_BDCTL_LXTALSTB)) {}
			
		rtc_lwoff_wait();				
		rtc_counter_set(0xA5A5);
		rtc_lwoff_wait();			
		rtc_prescaler_set(0);		
		rtc_lwoff_wait();
			
		pmu_backup_write_disable();		
	}	
	else if (TRUE == rtc_flag_get(RTC_CTL_OVIF))
		rtc_flag_clear(RTC_CTL_OVIF);		
#endif		
	
	/* Configure GPIO Alternate UART function */
	gd_eval_com_init(EVAL_COM1);	
	gpio_init(GPIOB, GPIO_MODE_IPU, GPIO_OSPEED_2MHZ, BUTTON_USER);
	
	/* Configure GPIO Output function for LEDs */	
	gpio_init(LED2_USER_PORT, GPIO_MODE_OUT_PP, GPIO_OSPEED_2MHZ, LED2_USER);		//gd_eval_led_init(LED2);
	gpio_bit_set(LED2_USER_PORT, LED2_USER);																		//gd_eval_led_on(LED2);
	gpio_init(LED5_TICK_PORT, GPIO_MODE_OUT_PP, GPIO_OSPEED_2MHZ, LED5_TICK);		
	gpio_bit_set(LED5_TICK_PORT, LED5_TICK);
	
	
	/* Enable PLL2 to generate 50MHz clocks */
	if (ERROR == xInitPLL2()) FreeRTOS_debug_printf(("PLL2 initialization failed\n"));		
	
	/* Put PLL2 clocks into CKOUT0(PA1) as ref clock for ethernet phy */
	rcu_ckout0_config(RCU_CKOUT0SRC_CKPLL2);	
	gpio_ethernet_phy_select(GPIO_ENET_PHY_RMII);
	
	/* Configure GPIO Alternate RMII function */
	gpio_init(RMII_TXD_PORT, 			GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, RMII_TX_EN);
	gpio_init(RMII_TXD_PORT, 			GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, RMII_TXD0);
	gpio_init(RMII_TXD_PORT, 			GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, RMII_TXD1);	
			
	gpio_init(RMII_MDC_PORT, 			GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, RMII_MDC);
	gpio_init(RMII_MDIO_PORT, 		GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, RMII_MDIO);	
	gpio_init(RMII_INT_PORT, 			GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, RMII_INT);	
		
	gpio_init(RMII_REF_CLK_PORT, 	GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, RMII_MCO);
	
	gpio_init(RMII_REF_CLK_PORT, 	GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, RMII_REF_CLK);	
	gpio_init(RMII_RXD_PORT, 			GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, RMII_RXD0);
	gpio_init(RMII_RXD_PORT, 			GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, RMII_RXD1);
	gpio_init(RMII_CRS_DV_PORT, 	GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, RMII_CRS_DV);	
	
	/* Enable Ethernet MAC */
	enet_descriptors_chain_init(ENET_DMA_TX);
	enet_descriptors_chain_init(ENET_DMA_RX); //enet_desc_receive_complete_bit_enable(dma_current_rxdesc);
	enet_enable();
	//nvic_irq_enable(ENET_IRQn, ipconfigMAC_INTERRUPT_PRIORITY, 0xFF);
	__disable_irq();
	NVIC_ClearPendingIRQ(ENET_IRQn);
  NVIC_SetPriority(ENET_IRQn, ipconfigMAC_INTERRUPT_PRIORITY);
	NVIC_EnableIRQ(ENET_IRQn);
	__enable_irq();
}

/**! \brief vTaskHelloWorld procedure
 *         
 *  \param Not Used.
 */
void vTaskHelloWorld( void *pvParameters)
{
	char ButtonState = 0;
	for( ;; )	
	{		
#ifdef DEBUG_ET_IRQ
			ulTaskNotifyTakeIndexed(1, pdFALSE, portMAX_DELAY );
			FreeRTOS_debug_printf(("ENET_DMA_INT_FLAG_ET\n"));			
#else		
			ButtonState = !gpio_input_bit_get(GPIOB, BUTTON_USER);
			if (ButtonState)
			{				
				FreeRTOS_debug_printf(("Key pressed\n"));	
				fflush( stdout );				
				vTaskDelay(TASK_HELLO_WORLD_DELAY);
			}			
#endif			
	}
}	
/**! \brief vTaskToggleLed procedure
 *         
 *  \param Not Used.
 */
void vTaskToggleLed( void *pvParameters)
{	
	char toggle = 1;
	for( ;; )	
	{			
#ifdef DEBUG_RBU_IRQ		
			ulTaskNotifyTakeIndexed(0, pdFALSE, portMAX_DELAY );
			FreeRTOS_debug_printf(("ENET_DMA_INT_FLAG_RBU\n"));
#else		
			if (toggle)
			{
				gpio_bit_reset(LED2_USER_PORT, LED2_USER);				
			}
			else
			{
				gpio_bit_set(LED2_USER_PORT, LED2_USER);				
			}	
			toggle = !toggle;
			vTaskDelay(TASK_TOGGLE_LED_DELAY);
#endif			
	}
}	

/**
* @brief Miscellaneous initialisation including preparing the logging and seeding
* the random number generator. 
*/
static void prvMiscInitialisation( void )
{
    UBaseType_t xTimeNow;
    uint32_t ulRandomNumbers[ 4 ];

    /* Seed the random number generator. */
		xTimeNow = rtc_counter_get();
    FreeRTOS_debug_printf( ("Seed for randomiser: 0x%08X\r\n", (unsigned)xTimeNow ) );		
    prvSRand( ( uint32_t ) xTimeNow );

    ( void ) xApplicationGetRandomNumber( &ulRandomNumbers[ 0 ] );
    ( void ) xApplicationGetRandomNumber( &ulRandomNumbers[ 1 ] );
    ( void ) xApplicationGetRandomNumber( &ulRandomNumbers[ 2 ] );
    ( void ) xApplicationGetRandomNumber( &ulRandomNumbers[ 3 ] );
    FreeRTOS_debug_printf(  ("Random numbers: 0x%08X 0x%08X 0x%08X 0x%08X\r\n",
                             ulRandomNumbers[ 0 ],
                             ulRandomNumbers[ 1 ],
                             ulRandomNumbers[ 2 ],
                             ulRandomNumbers[ 3 ] ) );
}

int main(void)
{	
	vInitMCU(); 			
	vRFSwitchInit(&rfSwitchConfig, RF_SWITCH_PORT, RF_SWITCH_PIN_A, RF_SWITCH_PORT, RF_SWITCH_PIN_B, RF_SWITCH_PORT, RF_SWITCH_PIN_C, RF_SWITCH_PORT, RF_SWITCH_PIN_EN);
  prvMiscInitialisation();
	xTaskCreate( vTaskToggleLed,  "ToggleLed",  configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY+1, &vTaskToggleLed_Handle);
	xTaskCreate( vTaskHelloWorld, "HelloWorld", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY+1, &vTaskHelloWorld_Handle);
	FreeRTOS_IPInit( ucIPAddress, ucNetMask, ucGatewayAddress, ucDNSServerAddress, ucMACAddress );
	
	vTaskStartScheduler();
	while(1);
}

BaseType_t xApplicationDNSQueryHook( const char * pcName )
{
#ifdef DEBUG	
	return TRUE;
#endif	
}
/**
 * @brief pcApplicationHostnameHook( void )
 */
const char * pcApplicationHostnameHook( void )	
{
	return mainHOST_NAME;
}

/**
 * @brief xApplicationDHCPHook( eDHCPCallbackPhase_t eDHCPPhase, uint32_t ulIPAddress )
 */
eDHCPCallbackAnswer_t xApplicationDHCPHook( eDHCPCallbackPhase_t eDHCPPhase, uint32_t ulIPAddress )
{
	eDHCPCallbackAnswer_t xResult = eDHCPUseDefaults;
	switch (eDHCPPhase)
	{
		case  eDHCPPhasePreDiscover:
			FreeRTOS_printf((" Driver is about to send a DHCP discovery.\n"));
			xResult = eDHCPContinue;
			break;
		case eDHCPPhasePreRequest:
			FreeRTOS_printf((" Driver is about to request DHCP an IP address.\n"));
			xResult = eDHCPContinue;
			break;
		default:
			FreeRTOS_printf((" Stop DHCP requests.\n"));
			xResult = eDHCPStopNoChanges;
			break;
	};
	return xResult;
};

/**
 * @brief vApplicationIPNetworkEventHook( eIPCallbackEvent_t eNetworkEvent )
 */
/* Called by FreeRTOS+TCP when the network connects or disconnects.  Disconnect
 * events are only received if implemented in the MAC driver. */
void vApplicationIPNetworkEventHook( eIPCallbackEvent_t eNetworkEvent )
{
    uint32_t ulIPAddress, ulNetMask, ulGatewayAddress, ulDNSServerAddress;
    char cBuffer[ 16 ];
    static BaseType_t xTasksAlreadyCreated = pdFALSE;

    /* If the network has just come up...*/
    if( eNetworkEvent == eNetworkUp )
    {
        /* Create the tasks that use the IP stack if they have not already been
         * created. */
        if( xTasksAlreadyCreated == pdFALSE )
        {
            /* See the comments above the definitions of these pre-processor
             * macros at the top of this file for a description of the individual
             * demo tasks. */
            #if ( mainCREATE_SIMPLE_UDP_CLIENT_SERVER_TASKS == 1 )
                {
                    vStartSimpleUDPClientServerTasks( configMINIMAL_STACK_SIZE, mainSIMPLE_UDP_CLIENT_SERVER_PORT, mainSIMPLE_UDP_CLIENT_SERVER_TASK_PRIORITY );
                }
            #endif /* mainCREATE_SIMPLE_UDP_CLIENT_SERVER_TASKS */

            #if ( mainCREATE_TCP_ECHO_TASKS_SINGLE == 1 )
                {
                    vStartTCPEchoClientTasks_SingleTasks( mainECHO_CLIENT_TASK_STACK_SIZE, mainECHO_CLIENT_TASK_PRIORITY );
                }
            #endif /* mainCREATE_TCP_ECHO_TASKS_SINGLE */

            #if ( mainCREATE_TCP_ECHO_SERVER_TASK == 1 )
                {
                    vStartSimpleTCPServerTasks( mainECHO_SERVER_TASK_STACK_SIZE, mainECHO_SERVER_TASK_PRIORITY );
                }
            #endif

            xTasksAlreadyCreated = pdTRUE;
        }

        /* Print out the network configuration, which may have come from a DHCP
         * server. */
        FreeRTOS_GetAddressConfiguration( &ulIPAddress, &ulNetMask, &ulGatewayAddress, &ulDNSServerAddress );
        FreeRTOS_inet_ntoa( ulIPAddress, cBuffer );
        FreeRTOS_printf( ( "\r\n\r\nIP Address: %s\r\n", cBuffer ) );

        FreeRTOS_inet_ntoa( ulNetMask, cBuffer );
        FreeRTOS_printf( ( "Subnet Mask: %s\r\n", cBuffer ) );

        FreeRTOS_inet_ntoa( ulGatewayAddress, cBuffer );
        FreeRTOS_printf( ( "Gateway Address: %s\r\n", cBuffer ) );

        FreeRTOS_inet_ntoa( ulDNSServerAddress, cBuffer );
        FreeRTOS_printf( ( "DNS Server Address: %s\r\n\r\n\r\n", cBuffer ) );
    }
}

static void prvSRand( UBaseType_t ulSeed )
{
    /* Utility function to seed the pseudo random number generator. */
    ulNextRand = ulSeed;
}

UBaseType_t uxRand( void )
{				
    const uint32_t ulMultiplier = 0x015a4e35UL, ulIncrement = 1UL;

    /* Utility function to generate a pseudo random number. */

    ulNextRand = ( ulMultiplier * ulNextRand ) + ulIncrement;
    return( ( int ) ( ulNextRand ) & 0x7fffUL );
}

BaseType_t xApplicationGetRandomNumber( uint32_t * pulNumber )
{
		BaseType_t xReturn; /* Return pdTRUE if successful */
#ifdef RANDOM_NUMBER		
    CK_RV xResult = 0;
    SemaphoreHandle_t xSessionLock = NULL;
    CK_SESSION_HANDLE xPkcs11Session = 0;
    CK_FUNCTION_LIST_PTR pxPkcs11FunctionList = NULL;
    uint32_t ulRandomValue = 0;
   

    xResult = prvSocketsGetCryptoSession( &xSessionLock,
                                          &xPkcs11Session,
                                          &pxPkcs11FunctionList );

    if( 0 == xResult )
    {
        /* Request a sequence of cryptographically random byte values using
         * PKCS#11. */
        xResult = pxPkcs11FunctionList->C_GenerateRandom( xPkcs11Session,
                                                          ( CK_BYTE_PTR ) &ulRandomValue,
                                                          sizeof( ulRandomValue ) );
    }

    /* Check if any of the API calls failed. */
    if( 0 == xResult )
    {
        xReturn = pdTRUE;
        *( pulNumber ) = ulRandomValue;
    }
    else
    {
        xReturn = pdFALSE;
        *( pulNumber ) = 0uL;
    }		
    return xReturn;
}
#else
    *pulNumber = uxRand();
		return pdTRUE;
#endif    
}


/**
 * @brief Generate a TCP Initial Sequence Number that is reasonably difficult
 * to predict, per https://tools.ietf.org/html/rfc6528. 
 */
extern uint32_t ulApplicationGetNextSequenceNumber( uint32_t ulSourceAddress,
                                                    uint16_t usSourcePort,
                                                    uint32_t ulDestinationAddress,
                                                    uint16_t usDestinationPort )
{
    ( void ) ulSourceAddress;
    ( void ) usSourcePort;
    ( void ) ulDestinationAddress;
    ( void ) usDestinationPort;
#ifdef RANDOM_NUMBER
	  CK_RV xResult = CKR_OK;
    SemaphoreHandle_t xSessionLock = NULL;
    CK_SESSION_HANDLE xPkcs11Session = 0;
    CK_FUNCTION_LIST_PTR pxPkcs11FunctionList = NULL;
    CK_MECHANISM xMechSha256 = { 0 };
    uint8_t ucSha256Result[ cryptoSHA256_DIGEST_BYTES ];
    CK_ULONG ulLength = sizeof( ucSha256Result );
    uint32_t ulNextSequenceNumber = 0;
    static uint64_t ullKey;
    static CK_BBOOL xKeyIsInitialized = CK_FALSE;
		
    /* Acquire a crypto session handle. */
    xResult = prvSocketsGetCryptoSession( &xSessionLock,
                                          &xPkcs11Session,
                                          &pxPkcs11FunctionList );

    if( CKR_OK == xResult )
    {
        xSemaphoreTake( xSessionLock, portMAX_DELAY );

        if( CK_FALSE == xKeyIsInitialized )
        {
            /* One-time initialization, per boot, of the random seed. */
            xResult = pxPkcs11FunctionList->C_GenerateRandom( xPkcs11Session,
                                                              ( CK_BYTE_PTR ) &ullKey,
                                                              sizeof( ullKey ) );

            if( xResult == CKR_OK )
            {
                xKeyIsInitialized = CK_TRUE;
            }
        }

        xSemaphoreGive( xSessionLock );
    }

    /* Lock the shared crypto session. */
    xSemaphoreTake( xSessionLock, portMAX_DELAY );

    /* Start a hash. */
    if( CKR_OK == xResult )
    {
        xMechSha256.mechanism = CKM_SHA256;
        xResult = pxPkcs11FunctionList->C_DigestInit( xPkcs11Session, &xMechSha256 );
    }

    /* Hash the seed. */
    if( CKR_OK == xResult )
    {
        xResult = pxPkcs11FunctionList->C_DigestUpdate( xPkcs11Session,
                                                        ( CK_BYTE_PTR ) &ullKey,
                                                        sizeof( ullKey ) );
    }

    /* Hash the source address. */
    if( CKR_OK == xResult )
    {
        xResult = pxPkcs11FunctionList->C_DigestUpdate( xPkcs11Session,
                                                        ( CK_BYTE_PTR ) &ulSourceAddress,
                                                        sizeof( ulSourceAddress ) );
    }

    /* Hash the source port. */
    if( CKR_OK == xResult )
    {
        xResult = pxPkcs11FunctionList->C_DigestUpdate( xPkcs11Session,
                                                        ( CK_BYTE_PTR ) &usSourcePort,
                                                        sizeof( usSourcePort ) );
    }

    /* Hash the destination address. */
    if( CKR_OK == xResult )
    {
        xResult = pxPkcs11FunctionList->C_DigestUpdate( xPkcs11Session,
                                                        ( CK_BYTE_PTR ) &ulDestinationAddress,
                                                        sizeof( ulDestinationAddress ) );
    }

    /* Hash the destination port. */
    if( CKR_OK == xResult )
    {
        xResult = pxPkcs11FunctionList->C_DigestUpdate( xPkcs11Session,
                                                        ( CK_BYTE_PTR ) &usDestinationPort,
                                                        sizeof( usDestinationPort ) );
    }

    /* Get the hash. */
    if( CKR_OK == xResult )
    {
        xResult = pxPkcs11FunctionList->C_DigestFinal( xPkcs11Session,
                                                       ucSha256Result,
                                                       &ulLength );
    }

    xSemaphoreGive( xSessionLock );

    /* Use the first four bytes of the hash result as the starting point for
     * all initial sequence numbers for connections based on the input 4-tuple. */
    if( CKR_OK == xResult )
    {
        memcpy( &ulNextSequenceNumber,
                ucSha256Result,
                sizeof( ulNextSequenceNumber ) );

        /* Add the tick count of four-tick intervals. In theory, per the RFC
         * (see above), this approach still allows server equipment to optimize
         * handling of connections from the same device that haven't fully timed out. */
        ulNextSequenceNumber += xTaskGetTickCount() / 4;
    }		
    return ulNextSequenceNumber;
#else
		/* THIS IS ONLY A DUMMY IMPLEMENTATION
		* THAT RETURNS A PSEUDO RANDOM NUMBER SO IS NOT INTENDED FOR USE IN PRODUCTION
		* SYSTEMS. */
		return uxRand();
#endif	    
}


/**! \func	 void vApplicationMallocFailedHook( void )
 * \brief This hook function is called when allocation failed.
 */
void vApplicationMallocFailedHook( void )
{
	FreeRTOS_debug_printf(("Malloc Failed Hook\n"));
	BKPT_DEBUG();
}

	
/**! \func	 vApplicationTickHook( void )
 *  \brief Toggles LED5_TICK to indicate RTOS running well         
 */
void vApplicationTickHook( void )
{
	static uint16_t tickCounter = 0;	
	if(++tickCounter == configTICK_RATE_HZ)	
	{
		gpio_bit_reset(LED5_TICK_PORT, LED5_TICK);	
		tickCounter = 0;		
	}
	else if(tickCounter == configTICK_RATE_HZ/2)
	{
		gpio_bit_set(LED5_TICK_PORT, LED5_TICK);						
	}	
}

/**! \func vApplicationIdleHook( void )
 *  \brief Idle task Hook
 */
void vApplicationIdleHook( void )
{	
#if defined (DEBUG)
		int32_t delta = (configTICK_RATE_HZ-(TASK_TOGGLE_LED_DELAY + TASK_HELLO_WORLD_DELAY)) % configTICK_RATE_HZ;
		if (delta < 0) delta = -delta;		
									 
		static uint32_t idleHookCounter = 0;
		uint32_t currentTick = xTaskGetTickCount();		
		if(currentTick > idleHookCounter + delta)
		{			
			FreeRTOS_debug_printf(("Idle Hook %d\n", idleHookCounter));//__ASM("BKPT #0\n");				
		}
		idleHookCounter = currentTick;
#endif		
}	

/**! \func	vApplicationStackOverflowHook( TaskHandle_t pxTask, char *pcTaskName )
 *  \brief	Stack overflow Hook
 */
void vApplicationStackOverflowHook( TaskHandle_t pxTask, char *pcTaskName )
{	
	FreeRTOS_debug_printf(("Stack Overflow Hook\n"));
	BKPT_DEBUG();	
}


__attribute__((used)) void prvGetRegistersFromStack( uint32_t *pulFaultStackAddress ) 
{
/* These are volatile to try and prevent the compiler/linker optimising them
away as the variables never actually get used.  If the debugger won't show the
values of the variables, make them global my moving their declaration outside
of this function. */
static volatile uint32_t r0;
static volatile uint32_t r1;
static volatile uint32_t r2;
static volatile uint32_t r3;
static volatile uint32_t r12;
static volatile uint32_t lr; /* Link register. */
static volatile uint32_t pc; /* Program counter. */
static volatile uint32_t psr;/* Program status register. */

    r0 = pulFaultStackAddress[ 0 ];
    r1 = pulFaultStackAddress[ 1 ];
    r2 = pulFaultStackAddress[ 2 ];
    r3 = pulFaultStackAddress[ 3 ];

    r12 = pulFaultStackAddress[ 4 ];
    lr = pulFaultStackAddress[ 5 ];
    pc = pulFaultStackAddress[ 6 ];
    psr = pulFaultStackAddress[ 7 ];
	
		FreeRTOS_debug_printf(("Hard Fault Hook\n"));
		BKPT_DEBUG();

		NVIC_SystemReset();
    /* When the following line is hit, the variables contain the register values. */
    for( ;; );
}

void DelayMS(unsigned char ms)
{
unsigned long us = 1000*ms;

while (us--)
  {
  __NOP();
  }
}

