/*
 * FreeModbus Libary: Microsemi SmartFusion Port
 * Copyright (C) 2011 Microsemi Corporation <soc_tech@microsemi.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * SVN $Revision: 15145 $
 * SVN $Date: 2011-03-11 11:36:10 +0000 (Fri, 11 Mar 2011) $
 * File: $Id: demo.c 15145 2011-03-11 11:36:10Z tommy.murphy $
 */

/* ----------------------- Platform includes --------------------------------*/
/* A2F-DEV-KIT/A2F-EVAL-KIT board OLED driver */
#include "../../oled/oled.h"
/* SmartFusion Microcontroller Subsystem (MSS) drivers */
#include "../../drivers/mss_ace/mss_ace.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"

/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbutils.h"

#if 0
/* ----------------------- Defines ------------------------------------------*/

/* Modbus configuration */

#ifndef MODBUS_SERIAL_MODE
/* Modbus serial mode: MB_RTU or MB_ASCII */
#define MODBUS_SERIAL_MODE		MB_RTU
#endif

#ifndef MODBUS_SLAVE_ADDR
/* Slave address: 1-247 */
#define MODBUS_SLAVE_ADDR		0x1
#endif

#ifndef MODBUS_PORT
/* Modbus serial port: 0 = MSS UART_0/RS-232, 1 = MSS UART_1/RS-485 */
#define MODBUS_PORT				0
#endif

#ifndef MODBUS_BAUD_RATE
/* Modbus baud rate */
#define MODBUS_BAUD_RATE		19200
#endif

#ifndef MODBUS_PARITY
/* Modbus parity: MB_PAR_EVEN, MB_PAR_ODD, MB_PAR_NONE */
#define MODBUS_PARITY			MB_PAR_EVEN
#endif

#endif

#ifndef MODBUS_SLAVEID
/* Modbus slave id + optional additional bytes */
#define MODBUS_SLAVEID			0x55, 0xC0, 0xFF, 0xEE
#endif

/* 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 "

/* 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


/* ----------------------- 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(  );

    }
}

/* ----------------------- Start implementation -----------------------------*/

int
main( void )
{
	/* Disable watchdog (should be disabled in MSS configuration anyway) */
    ( void )MSS_WD_disable(  );

    /* Initialization */
    ( void )init_ace(  );
    ( void )init_rtc(  );
	( void )init_modbus_regs(  );
	( void )init_oled(  );
	( void )init_gpios(  );
	( void )init_systick(  );
	( void )init_modbus(  );

    init_mac();

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

    return 0;
}


/*
 * 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 & enable the Modbus protocol stack
 */

static void init_modbus( void )
{
	eMBErrorCode eStatus;

    eStatus = eMBSetSlaveID(
    	g_aucSlaveId[0],
    	TRUE,
    	&( g_aucSlaveId[1] ),
    	sizeof( g_aucSlaveId ) - 1
    );

    eStatus = eMBEnable(  );
}


/*
 * 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(  );
}


/*
 * Initialize Analog Compute Engine (ACE)
 */
static void init_ace( void )
{
    ( void )ACE_init(  );
}


/*
 * SysTick ISR for synchronizing Modbus registers & hardware resources
 */

//__attribute__((__interrupt__)) void SysTick_Handler( void )
void synchronizing_modbus_regs(void)
{
	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
	);
}
