/*******************************************************************************
 * (c) Copyright 2010 Microsemi Corporation.  All rights reserved.
 *
 *  Modbus TCP Server on lwIP TCP/IP stack with freeRTOS Application demo
 *  for SmartFusion
 *
 *
 * Author : Corporate Application Team
 * Rev    : 1.0
 *
 *******************************************************************************/

/**************************************************************************/
/* Standard Includes */
/**************************************************************************/

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

/**************************************************************************/
/* RTOS Includes */
/**************************************************************************/
#include "FreeRTOS.h"
#include "task.h"



/* ------------------------ FreeModbus includes ------------------------- */
#include "mb.h"

/**************************************************************************/
/* lwIP TCP/IP Stack Includes */
/**************************************************************************/
#include "cpu_types.h"
#include "conf_eth.h"
#include "lwip/api.h"

/**************************************************************************/
/* Firmware Includes */
/**************************************************************************/

#include "./drivers/mss_watchdog/mss_watchdog.h"
#include "./drivers/mss_gpio/mss_gpio.h"
#include "./drivers/mss_rtc/mss_rtc.h"
#include "./drivers/mss_watchdog/mss_watchdog.h"
#include "./drivers/mss_ethernet_mac/mss_ethernet_mac.h"

#include "oled.h"

#define PPP_AUTH_ENABLED        0
#define PPP_USER                "freemodbus"
#define PPP_PASS                "insecure"

/* Modbus discrete input registers (1 bit read-only) */
#ifndef REG_DISCRETE_START
#define REG_DISCRETE_START 		1
#endif
#ifndef REG_DISCRETE_NREGS
#define REG_DISCRETE_NREGS 		2
#endif

/* Modbus coils registers (1 bit read-write) */
#ifndef REG_COILS_START
#define REG_COILS_START 		1
#endif
#ifndef REG_COILS_NREGS
#define REG_COILS_NREGS 		1
#endif

/* Modbus input registers (16 bit read-only) */
#ifndef REG_INPUT_START
#define REG_INPUT_START 		1
#endif
#ifndef REG_INPUT_NREGS
#define REG_INPUT_NREGS 		2
#endif

/* Modbus holding registers (16 bit read-write) */
#ifndef REG_HOLDING_START
#define REG_HOLDING_START		1
#endif
#ifndef REG_HOLDING_NREGS
#define REG_HOLDING_NREGS		( OLED_NCHARS )
#endif

/* Register details */
#define REG_DISCRETE_PBS		0
#define REG_DISCRETE_DIPS		1
#define REG_DISCRETE_PBS_SHIFT	4
#define REG_DISCRETE_PBS_MASK	0x1F
#define REG_DISCRETE_DIPS_SHIFT	9
#define REG_DISCRETE_DIPS_MASK	0x0F

#define REG_COILS_LEDS			0
#define REG_COILS_LEDS_MASK 	0x0F
#define REG_COILS_LEDS_DEFAULT	0x05

#define REG_INPUT_RTC			0
#define REG_INPUT_RV1			1


/* OLED details */
#define OLED_NROWS				2
#define OLED_NCOLS				19
#define OLED_NCHARS				( ( OLED_NROWS ) * ( OLED_NCOLS ) )
#define OLED_GREETING			"Microsemi Corp v1.xSmartFusion Modbus "

/* ----------------------- Static variables ---------------------------------*/
static UCHAR	g_usRegDiscreteBuf[REG_DISCRETE_NREGS];
static UCHAR	g_usRegCoilsBuf[REG_COILS_NREGS];
static USHORT   g_usRegInputBuf[REG_INPUT_NREGS];
static USHORT	g_usRegHoldingBuf[REG_HOLDING_NREGS];
//static UCHAR	g_aucSlaveId[] = { MODBUS_SLAVEID };

/* ----------------------- Static functions ---------------------------------*/
static void init_ace( void );
static void init_rtc( void );
static void init_modbus_regs( void );
static void init_oled( void );
static void init_gpios( void );
static void init_systick( void );
static void init_modbus( void );




/* ----------------------- Type definitions ---------------------------------*/

#define PHY_ADDRESS			       1
/* Demo file headers. */
extern unsigned char               my_ip[4];
uint32_t mac_cfg;
extern unsigned int                num_pkt_rx;
extern unsigned char               ip_known;
extern unsigned char               dhcp_ip_found;

/* Priority definitions for most of the tasks in the demo application. */
#define USER_RX_BUFF_SIZE	       1600
#define OPEN_IP 1
#define PROG                    "FreeModbus"


extern void prvlwIPInit( void );

void show_ip()
{
    printf( "Successfully requested IP addr \n\r" );

    if( (my_ip[0] == 192) && (my_ip[1]==168)&& (my_ip[2]==0) && (my_ip[3]==14))
    {
        printf( "Static IP address: " );
    }
    else
    {
        printf( "Dynamic/Open IP address: " ); 
    }
    printf( "%d.%d.%d.%d \n\r", my_ip[0], my_ip[1], my_ip[2], my_ip[3] );
}

void init_mac()
{
	uint32_t time_out = 0;
	int32_t i;
	int32_t rx_size;
    uint8_t rx_buffer[USER_RX_BUFF_SIZE];
    
	portCHAR MacAddress[6];

	   /* Default MAC addr. */
    MacAddress[0] = ETHERNET_CONF_ETHADDR0;
    MacAddress[1] = ETHERNET_CONF_ETHADDR1;
	MacAddress[2] = ETHERNET_CONF_ETHADDR2;
	MacAddress[3] = ETHERNET_CONF_ETHADDR3;
	MacAddress[4] = ETHERNET_CONF_ETHADDR4;
	MacAddress[5] = ETHERNET_CONF_ETHADDR5;
   

    MSS_MAC_init(PHY_ADDRESS );
	/*
	 * Configure the MAC.
	 */
	mac_cfg = MSS_MAC_get_configuration();

	mac_cfg &= ~(MSS_MAC_CFG_STORE_AND_FORWARD | MSS_MAC_CFG_PASS_BAD_FRAMES );

	mac_cfg |=
          MSS_MAC_CFG_RECEIVE_ALL |
          MSS_MAC_CFG_PROMISCUOUS_MODE |
          MSS_MAC_CFG_FULL_DUPLEX_MODE |
          MSS_MAC_CFG_TRANSMIT_THRESHOLD_MODE |
          MSS_MAC_CFG_THRESHOLD_CONTROL_00;

	MSS_MAC_configure(mac_cfg );

	MSS_MAC_set_mac_address((uint8_t *)MacAddress);

	ip_known = 0;
	num_pkt_rx = 0;
	time_out = 0;
	for (i = 0; i < 1600; i++)
	{
		rx_buffer[i] = 0;
	}
	
#ifdef OPEN_IP
	do{

    	send_bootp_packet(0);
    	do {		    		
			rx_size = MSS_MAC_rx_pckt_size();
			time_out++;
			if(dhcp_ip_found)
			   break;//goto here;
    	}while ( rx_size == 0 && (time_out < 3000000));
		MSS_MAC_rx_packet( rx_buffer, USER_RX_BUFF_SIZE, MSS_MAC_BLOCKING );
		num_pkt_rx++;		
		process_packet( rx_buffer );
    }while((!dhcp_ip_found) && (time_out < 7000000));
#endif
    show_ip();
} // End of ethernet


void vMBServerTask( void *arg )
{
    eMBErrorCode    xStatus;

    for( ;; )
    {
        if( eMBTCPInit( MB_TCP_PORT_USE_DEFAULT ) != MB_ENOERR )
        {
            printf("can't initialize modbus stack!\r\n");
        }
        else if( eMBEnable(  ) != MB_ENOERR )
        {
            printf("can't enable modbus stack!\r\n");
        }
        else
        {
            do
            {
                xStatus = eMBPoll(  );
            }
            while( xStatus == MB_ENOERR );
        }
        printf("Disabling modbus stack!\r\n");
        /* An error occured. Maybe we can restart. */
        ( void )eMBDisable(  );
        printf("Closing modbus stack!\r\n");
        ( void )eMBClose(  );

    }
}

void synchronizing_modbus_regs(void)
{
#if 1
	ULONG	gpios;
	UCHAR	oled_row;
	UCHAR 	oled_col;

	/* Flush holding registers (lower 8 bits only) to OLED */
	for ( oled_row = FIRST_LINE;
		  oled_row < ( FIRST_LINE + OLED_NROWS );
		  oled_row++ )
	{
		( void )OLED_set_cursor( oled_row, FIRST_CHARACTER );
		for ( oled_col = FIRST_CHARACTER;
			  oled_col < ( FIRST_CHARACTER + OLED_NCOLS );
			  oled_col++ )
		{
			( void )OLED_write_char(
				g_usRegHoldingBuf[( oled_row * OLED_NCOLS ) + oled_col]
			);
		}
	}

	/* Flush RTC value (in seconds) to 1st input register  */
	g_usRegInputBuf[REG_INPUT_RTC] = MSS_RTC_get_seconds_count(  );

	/* Flush RV1 3.3V pot voltage in mV to 2nd input register */
	g_usRegInputBuf[REG_INPUT_RV1] =
		ACE_convert_to_mV(
			ACE_get_first_channel(),
			ACE_get_ppe_sample( ACE_get_first_channel() ) );

	/* Flush pushbuttons & DIP switches to discrete input registers */
	gpios = MSS_GPIO_get_inputs(  );
	g_usRegDiscreteBuf[REG_DISCRETE_PBS] =
		( gpios >> REG_DISCRETE_PBS_SHIFT ) & REG_DISCRETE_PBS_MASK;
	g_usRegDiscreteBuf[REG_DISCRETE_DIPS] =
		( gpios >> REG_DISCRETE_DIPS_SHIFT ) & REG_DISCRETE_DIPS_MASK;

	/* Flush coils register to LEDs */
	MSS_GPIO_set_outputs(
		g_usRegCoilsBuf[REG_COILS_LEDS] & REG_COILS_LEDS_MASK
	);
#endif
}


/*
 * Modbus 16 bit read-only input registers callback
 */

eMBErrorCode
eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
{
    eMBErrorCode	eStatus = MB_ENOERR;
    USHORT			iRegIndex;

    if( ( usAddress >= REG_INPUT_START )
        && ( usAddress + usNRegs <= REG_INPUT_START + REG_INPUT_NREGS ) )
    {
        iRegIndex = ( int )( usAddress - REG_INPUT_START );
        while( usNRegs > 0 )
        {
            *pucRegBuffer++ =
                ( unsigned char )( g_usRegInputBuf[iRegIndex] >> 8 );
            *pucRegBuffer++ =
                ( unsigned char )( g_usRegInputBuf[iRegIndex] & 0xFF );
            iRegIndex++;
            usNRegs--;
        }
    }
    else
    {
        eStatus = MB_ENOREG;
    }

    return eStatus;
}


/*
 * Modbus 16 bit read-write holding registers callback
 */

eMBErrorCode
eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs,
                 eMBRegisterMode eMode )
{
    eMBErrorCode    eStatus = MB_ENOERR;
    USHORT			iRegIndex;

    if( ( usAddress >= REG_HOLDING_START )
        && ( usAddress + usNRegs <= REG_HOLDING_START + REG_HOLDING_NREGS ) )
    {
        iRegIndex = ( int )( usAddress - REG_HOLDING_START );
        while( usNRegs > 0 )
        {
        	switch ( eMode )
        	{
				case MB_REG_WRITE:
				{
					g_usRegHoldingBuf[iRegIndex] = ( *pucRegBuffer ) << 8;
					g_usRegHoldingBuf[iRegIndex] |=  *( pucRegBuffer + 1 );
					pucRegBuffer += 2;
				}
				break;

				case MB_REG_READ:
				default:
				{
		            *pucRegBuffer++ =
		                ( unsigned char )( g_usRegHoldingBuf[iRegIndex] >> 8 );
		            *pucRegBuffer++ =
		                ( unsigned char )( g_usRegHoldingBuf[iRegIndex] & 0xFF );
				}
				break;
        	}

            iRegIndex++;
            usNRegs--;
        }
    }
    else
    {
        eStatus = MB_ENOREG;
    }

    return eStatus;
}


/*
 * Modbus 1 bit read-write coils registers callback
 */

eMBErrorCode
eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils,
               eMBRegisterMode eMode )
{
    eMBErrorCode    eStatus = MB_ENOERR;

    if( ( usAddress >= REG_COILS_START ) &&
        ( usAddress + usNCoils <= REG_COILS_START + ( REG_COILS_NREGS * 8 ) ) )
    {
    	usAddress--;

        while( usNCoils > 0 )
        {
        	switch( eMode )
        	{
				case MB_REG_WRITE:
				{
					xMBUtilSetBits(
						g_usRegCoilsBuf,
						usAddress,
						( usNCoils > 8 ) ? 8 : usNCoils,
						*pucRegBuffer);

				}
				break;

				case MB_REG_READ:
				default:
				{
		            *pucRegBuffer =
		            	xMBUtilGetBits(
		            		g_usRegCoilsBuf,
		            		usAddress,
		            		( usNCoils > 8 ) ? 8 : usNCoils
		            	);
				}
				break;
        	}

        	pucRegBuffer++;
        	usNCoils -= ( usNCoils > 8) ? 8 : usNCoils;
            usAddress += 8;
        }
    }
    else
    {
        eStatus = MB_ENOREG;
    }

    return eStatus;
}

/*
 * Modbus 1 bit read-only discrete input registers callback
 */

eMBErrorCode
eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
{
    eMBErrorCode    eStatus = MB_ENOERR;

    if( ( usAddress >= REG_INPUT_START ) &&
        ( usAddress + usNDiscrete <= REG_INPUT_START + ( REG_INPUT_NREGS * 8 ) ) )
    {
        while( usNDiscrete > 0 )
        {
            *pucRegBuffer++ =
            	xMBUtilGetBits(
            		g_usRegDiscreteBuf,
            		usAddress - 1,
            		( usNDiscrete > 8 ) ? 8 : usNDiscrete
            	);

            usNDiscrete -= ( usNDiscrete > 8) ? 8 : usNDiscrete;
            usAddress += 8;
        }
    }
    else
    {
        eStatus = MB_ENOREG;
    }

    return eStatus;
}


/*
 * Initialize Modbus slave registers
 */

static void init_modbus_regs( void )
{
	UCHAR i;

	/* OLED holding registers */
	for ( i = 0; i < OLED_NCHARS; i++ )
	{
		g_usRegHoldingBuf[i] = OLED_GREETING[i];
	}

	/* LEDs coils register */
	g_usRegCoilsBuf[REG_COILS_LEDS] = REG_COILS_LEDS_DEFAULT;

}


/*
 * Initialize GPIOs
 */

static void init_gpios( void )
{
	( void )MSS_GPIO_init(  );

	/* LEDs D1-D4 */
	( void )MSS_GPIO_config( MSS_GPIO_0,  MSS_GPIO_OUTPUT_MODE );
	( void )MSS_GPIO_config( MSS_GPIO_1,  MSS_GPIO_OUTPUT_MODE );
	( void )MSS_GPIO_config( MSS_GPIO_2,  MSS_GPIO_OUTPUT_MODE );
	( void )MSS_GPIO_config( MSS_GPIO_3,  MSS_GPIO_OUTPUT_MODE );

	/* SW1-SW5 (A2F-DEV-KIT)/SW1-SW2 (A2F-EVAL-KIT) */
	( void )MSS_GPIO_config( MSS_GPIO_4,  MSS_GPIO_INPUT_MODE );
	( void )MSS_GPIO_config( MSS_GPIO_5,  MSS_GPIO_INPUT_MODE );
	( void )MSS_GPIO_config( MSS_GPIO_6,  MSS_GPIO_INPUT_MODE );
	( void )MSS_GPIO_config( MSS_GPIO_7,  MSS_GPIO_INPUT_MODE );
	( void )MSS_GPIO_config( MSS_GPIO_8,  MSS_GPIO_INPUT_MODE );

	/* A2F-DIP DIP1-DIP4 (A2F-DEV-KIT) */
	( void )MSS_GPIO_config( MSS_GPIO_9,  MSS_GPIO_INPUT_MODE );
	( void )MSS_GPIO_config( MSS_GPIO_10, MSS_GPIO_INPUT_MODE );
	( void )MSS_GPIO_config( MSS_GPIO_11, MSS_GPIO_INPUT_MODE );
	( void )MSS_GPIO_config( MSS_GPIO_12, MSS_GPIO_INPUT_MODE );
}


/*
 * Initialize OLED
 */

static void init_oled( void )
{
	( void )OLED_init(  );
}


/*
 * Initialize MSS SysTick timer (100ms period)
 */

static void init_systick( void )
{
	( void )SystemCoreClockUpdate(  );
	( void )SysTick_Config( SystemCoreClock / 10 );
}



/*
 * Initialize RTC (Real Time Counter)
 */
static void init_rtc( void )
{
    ( void )MSS_RTC_init(  );
    ( void )MSS_RTC_configure( MSS_RTC_NO_COUNTER_RESET );
    ( void )MSS_RTC_start(  );
}


int main( void )
{
	MSS_WD_disable();
    printf("SmartFusion Modbus Demo\n\r");
    printf("Initializing the MAC and getting IP Address\n\r");

    ACE_init();
    ( void )init_rtc(  );
	( void )init_modbus_regs(  );
	( void )init_oled(  );
	( void )init_gpios(  );


    init_mac();

	/* Setup lwIP. & Starts the vMBServerTask after TCP Task is created */
	prvlwIPInit();

	/* Enabling the M3 SysTick TImer in SF */ 
	*(volatile unsigned long *)0xE000E010 = 0x7;    
    *(volatile unsigned long *)0xE0042038 = 0x31000000;    
    /* Starts the FreeRTOS Scheduler */
	vTaskStartScheduler();
    printf("Should Never Come here\n\r");
	return 0;
}

