#include "archinc.h"
#include "eventq.h"
#include "timer0.h"
#include "pll.h"
#include "board.h"
#include "uart0.h"
#include "spi.h"
#include "i2cmaster.h"
#include "i2cswitch.h"
#include "i2cport.h"
#include "cli.h"
#include "clichar.h"
#include "clivar.h"
#include "sfp.h"
#include "sfpdiag.h"
#include "zarlink.h"
#include "zl_event.h"
#include "zl_rmon.h"
#include "icmp.h"
#include "ip.h"
#include "watchdog.h"
#include "ee24lc256.h"
#include "nvstore.h"
#include "pm.h"

#include "uip/uip.h"
#include "uip/uip_arp.h"

#define BUF ((struct uip_eth_hdr *)&uip_buf[0])

#include <stdio.h>

static char buffer[128];

static char *buildDate = __DATE__;
static char *buildTime = __TIME__;

static char *appVersion = "1.10";

#ifdef CONFIG_TOOLCHAIN_ROWLEY
static char *toolchain = "Rowley gcc";
static char *toolVersion = __VERSION__;
#endif

#ifdef CONFIG_TOOLCHAIN_IAR
static char *toolchain = "IAR ICC";
static char *toolVersion = "4.1.1A";
#endif

char cpuMacAddr[6] = {0x00, 0x0c, 0x6a, 0x01, 0x00, 0x00};

extern boolean frameDataReady;

int main(void)
{
  volatile int wdogCount, arptimer;
  unsigned int current, start;
  volatile int i, j;

  // first, disable interrupts
  IRQ_DISABLE();

  // initialize the event (work) queue
  eventqInit();

  // initialize the hardware
  boardInit();

  // initialize the VIC
  armvicInit();

  // initialize Memory subsystem first (cacheability, flash timing)
  MAMCR  = (unsigned long)0x00; // disable MAM, so we can change MAMTIM
  MAMTIM = (unsigned long)0x03; // flash access = 3 CPU clock cycles
  MAMCR  = (unsigned long)0x01; // partially enable MAM operation (prefetch on)

  // before all else, initialize PLL and VPB peripheral divider
  pllInit();

  // run VPB bus at 1/2 processor clock
  pllSetPeriphDivider(2);

  // Initialize periodic timer
#ifdef CONFIG_INCLUDE_LPC210X_TIMER0
  timer0Init();
#endif

  //
  // Initialize on-chip peripherals
  //

  // console UART
  uart0Init(38400);

  sprintf(buffer, "\r\nMBARI Medusa v %s (%s %s) %s %s\r\n", 
    appVersion, toolchain, toolVersion, buildDate, buildTime);
  uart0Puts(buffer);

  // initialize the CLI
  cliInit();
  clicharInit();
  clivarInit();

  // non-critical initialization
  armvicInit2();
  uart0Init2();
  boardInit2();

  /* Initialize SPI controller */

#if defined(CONFIG_INCLUDE_LPC210X_SPI_SLAVE)
  spiInit(false);
#endif

  /* Initialize I2C */
 
#ifdef CONFIG_INCLUDE_LPC210X_I2C
  i2cmasterInit(0x10);
#endif

  // lastly enable interrupts
  IRQ_ENABLE();


#ifdef CONFIG_INCLUDE_EEPROM_24LC256
  if (ee24lc256Init((unsigned char)0) == 0) {
    nvstoreInit();
    }
#endif

#ifdef CONFIG_INCLUDE_I2C_PORT
  i2cportInit();

  // configure port 0 as all inputs
  i2cportDirConfig(0, ((unsigned short)0xffff));

  // initialize port 1 to all zeroes
  i2cportAssign(1, ((unsigned short)0x000f));

  // configure port 1 as all outputs
  i2cportDirConfig(1, ((unsigned short)0x0000));

#endif /* CONFIG_INCLUDE_I2C_PORT */

#ifdef CONFIG_INCLUDE_I2C_SWITCH
  i2cswitchInit(0xe0);
#endif /* CONFIG_INCLUDE_I2C_SWITCH */

#ifdef CONFIG_INCLUDE_I2C_SFP
  sfpInit();
#ifdef CONFIG_INCLUDE_I2C_SFPDIAG
  sfpdiagInit();
#endif
#endif

#ifdef CONFIG_INCLUDE_ZARLINK

    if (zarlinkInit() == 0) {
      zarlinkMiiInit();
      zarlinkRmonInit();
      zarlinkUtilInit();
      zarlinkMirrorInit();
      zarlinkEventInit(true);

      zarlinkRxFrameInit();
      zarlinkCtrl1Init();
      zarlinkCtrl2Init();
      zarlinkResetInit();

      // enable link change and stat counter overflow interrupts on all ports
      zarlinkEventMaskPorts(0x44);

      // unmask events to handle
      zarlinkEventUnmaskIrq(INT_ALL);
      }
#endif

  // initialize the stack
  uip_init();
  uip_arp_init();

  // set a default mac address for the CPU port
  zarlinkCpuMacAddrSetUip(cpuMacAddr);

#if 0
  // IP address
  uip_ipaddr(addr, 192,168,27,2);
  uip_sethostaddr(addr);
 
  // Default router
  uip_ipaddr(addr, 192,168,27,1);
  uip_setdraddr(addr);
 
  // netmask
  uip_ipaddr(addr, 255,255,255,0);
  uip_setnetmask(addr);
#endif

  httpd_init();
  // telnetd_init();

  arptimer = 0;
  start = timer0GetTicks();

#ifdef CONFIG_INCLUDE_LPC210X_WATCHDOG
  watchdogInit();
#endif

  while(1) {

    // Give the event dispatcher a thread of execution
    eventqDispatch();

      /* The poll function returned 0, so no packet was
	 received. Instead we check if there is time that we do the
	 periodic processing. */

      current = timer0GetTicks();
      
      if((current - start) >= 10) {
	start = current;
	for(i = 0; i < UIP_CONNS; i++) {
	  uip_periodic(i);
	  /* If the above function invocation resulted in data that
	     should be sent out on the network, the global variable
	     uip_len is set to a value > 0. */
	  if(uip_len > 0) {
	    uip_arp_out();
	    zarlinkEthFrameWrite();
	  }
	}

#if UIP_UDP
	for(i = 0; i < UIP_UDP_CONNS; i++) {
	  uip_udp_periodic(i);
	  /* If the above function invocation resulted in data that
	     should be sent out on the network, the global variable
	     uip_len is set to a value > 0. */
	  if(uip_len > 0) {
	    uip_arp_out();
	    zarlinkEthFrameWrite();
	  }
	}
#endif /* UIP_UDP */
      
	/* Call the ARP timer function every 10 seconds. */
	if(++arptimer == 20) {	
	  uip_arp_timer();
	  arptimer = 0;
	}
      } // timer    
  } // while(1)

} // main
