/* 
 * AUV Battery Management Server (BatMan)
 *
 *
 *               ++
 *               ||
 *               ||
 *   --     .----++-------------+----------O---------+-----------------+---.
 *    )\  /                     |                    |                 |    \
 *    >-<|                      |        MBARI       |                 |    |
 *   ( /  \                     |                    |                 |    /
 *   --     `-------------------+--------------------+-----------------+---'
 *
 * 
 *
 *
 * Copyright MBARI 2018
 */

#include "board.h"
#include "iap_driver.h"
#include "uart.h"
#include "smbus.h"
#include "srv_ui.h"
#include "utils.h"


#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#include "FreeRTOS_Sockets.h"
#include "FreeRTOS_IP_Private.h"

void panic(char *msg);

xSemaphoreHandle bat_semphr;
char str_data[2500];

APP_CFG serial0_app;
APP_CFG serial3_app;
APP_CFG control_app;
APP_CFG battery_app;

SERIAL_CFG serial0_cfg;
SERIAL_CFG serial3_cfg;
CONTROL_CFG control_cfg;
BATTERY_CFG battery_cfg;

TASK_CFG maintask, serialtask;

/*
 * network config
 *
 * Some parameters like IP addr, etc. are overwritten from flash
 * at boot so they can't be local defines.
 */

#define	TCP_BUFSIZE			(256)
/* Timeout for recv(): 100ms */
#define TCP_RECV_TIMEOUT	(100)
/* minimum free heap to accept a new connection, 8kB */
#define TCP_MIN_FREE_HEAP		(8192)

uint8_t ucMACAddress[]			= { 0x00, 0x0c, 0x6a, 0x01, 0x03, 0x10 };
char tcp_srv_ip[16]			= "10.0.0.2";		/* IP address */
char tcp_srv_gw[16]  		= "10.0.0.1";		/* gateway */
char tcp_srv_nm[16] 		= "255.255.255.0";	/* netmask */
const uint16_t base_port = 10000;

const char version[] = "3.1.5";

#define WATCHDOG_ON		0
#define WATCHDOG_TIMEOUT	120
bool wdt_running = false;

/* bypass store/load of variables into flash for debug reasons */
#define FLASH_BYPASS	0

/* global task handles */
xTaskHandle to_ts_comm = NULL;
xTaskHandle to_ts_sercomm = NULL;
xTaskHandle to_ts_battery = NULL;

FLASH_CFG flash_cfg;

/* local functions */
static void commTask(void *parm);
Socket_t tcp_srv_init(uint16_t port);
void tcp_srv_recv(void);
int tcp_srv_respond_term(Socket_t sock, char *terminated_str);
int tcp_srv_respond(Socket_t sock, char *bufp, int bytestosend);
void init_wdt(uint32_t timeout);
int send_to_uart(void *arg, int sockidx, int len);
void decode_ip(char *ip, char *ipstr);
int load_flash_config(void);
void store_flash_config(void);


void
init_wdt(uint32_t timeout) {

	uint32_t wdtFreq;
	/*	Initialize WWDT and event router */
	Chip_WWDT_Init(LPC_WWDT);
	Chip_WWDT_SelClockSource(LPC_WWDT, WWDT_CLKSRC_WATCHDOG_PCLK);
	wdtFreq = Chip_Clock_GetPeripheralClockRate(SYSCTL_PCLK_WDT) / 4;

	/* Set watchdog feed time constant to timeout */
	Chip_WWDT_SetTimeOut(LPC_WWDT, wdtFreq * timeout);

	/* Configure WWDT to reset on timeout */
	Chip_WWDT_SetOption(LPC_WWDT, WWDT_WDMOD_WDEN | WWDT_WDMOD_WDRESET);

	/* Clear watchdog warning and timeout interrupts */
	Chip_WWDT_ClearStatusFlag(LPC_WWDT, WWDT_WDMOD_WDTOF | WWDT_WDMOD_WDINT);

	/* Start watchdog */
	Chip_WWDT_Start(LPC_WWDT);

	/* Enable watchdog interrupt */
	NVIC_ClearPendingIRQ(WDT_IRQn);
	NVIC_EnableIRQ(WDT_IRQn);
	wdt_running = true;
}


int
send_to_uart(void *arg, int sockidx, int len)
{
	APP_CFG *app = (APP_CFG *)arg;
	int sent, free, n;
	SERIAL_CFG *ext = (SERIAL_CFG *)app->ext_app_cfg;
	char *data_buf = app->tcp_buffer;
	
	n = sent = 0;
	while (sent < len) {
		data_buf += n;
		free = RingBuffer_GetFree(&ext->txring);
		if (free < 32) {
			n = 0;
			utils_task_sleep(25);
			continue;
		}
		if (free > len - sent) {
			free = len - sent;
		}
		n = Chip_UART_SendRB(ext->uart, &ext->txring, data_buf, free);
		sent += n;
	}
	ext->tx_bytes += sent;
	return (0);
}


void
recv_from_uart(APP_CFG *app)
{
	int i, n, skip;
	int len = 0;
	const int chunksz = 256;
	int rounds = 0;
	uint8_t bytes[chunksz + 1];
	uint8_t *byteptr = bytes;
	portTickType start, dur;
	SERIAL_CFG *ext = (SERIAL_CFG *)app->ext_app_cfg;

	/*
	 * We are trying to avoid sending TCP packets for every single UART byte
	 * we receive. So cycle over Chip_UART_ReadRB() until we have enough bytes
	 * to send out (set by chunksz). However, we don't want to cause
	 * unnecessary delays so we have a timeout of 2 ticks (~100ms) for sending
	 * out whatever we have.
	 */
	start = xTaskGetTickCount();
	while (1) {
		skip = 0;
		n = Chip_UART_ReadRB(ext->uart, &ext->rxring, byteptr, chunksz-len);
		len += n;
		ext->rx_bytes += n;

		/* nothing here */
		if (!len) break;

		/* skip character caching if we are in newline mode and saw a newline */
		for (i = 0; (i < n) && (ext->cache_mode == CACHE_TYPE_NEWLINE); i++) {
			if (*(byteptr + i) == '\n') {
				skip = 1;
				break;
			}
		}
		if (!skip) {
			/* not enough bytes so try again if we didn't wait too long already */
			dur = xTaskGetTickCount() - start;
			if (len < chunksz && dur < 2) {
				byteptr += n;
				continue;
			}
		}

		bytes[len] = 0;
		/* send serial data to all connected sockets */
		for (i = 0; i < MAX_APP_CLIENTS; i++) {
			if (!app->clientsocks[i]) continue;
			tcp_srv_respond(app->clientsocks[i], (char*)bytes, len);
		}

		/* we sent out all the data or we spent too much time in this loop */
		if ((!skip && len < chunksz) || ++rounds >= 5) break;

		/* otherwise reset counters and start over */
		len = 0;
		byteptr = bytes;
		start = xTaskGetTickCount();
	}
}


int
handle_ctl_requests(void *arg, int sockidx, int len)
{
	
	APP_CFG *app = (APP_CFG *)arg;
	CONTROL_CFG *ctlcfg = (CONTROL_CFG *)app->ext_app_cfg;
	Socket_t sock = app->clientsocks[sockidx];
	char response[32];

	int ret;

	ret = parse_ctl_cmd(app, sockidx, len);
	if (ret) return (ret);
	
	if (!ctlcfg->sockapp[sockidx]) ctlcfg->sockapp[sockidx] = app;

	/* show prompt based on what app we are configuring */
	sprintf(response, "%s> ", ctlcfg->sockapp[sockidx]->name);
	tcp_srv_respond_term(sock, response);

	return ret;
}

int
handle_battery_requests(void *arg, int sockidx, int len)
{
	
	APP_CFG *app = (APP_CFG *)arg;
	Socket_t sock = app->clientsocks[sockidx];
	char response[32];

	int ret;
	
	ret = parse_battery_cmd(app, sockidx, len);
	if (ret) return (ret);
	
	sprintf(response, "battery> ");
	tcp_srv_respond_term(sock, response);

	return ret;
}

static void
commTask(void *parm) {

	TASK_CFG *task = (TASK_CFG *)parm;
	APP_CFG *app;
	struct freertos_sockaddr client;
	int i, j;
	BaseType_t res, sres;
	socklen_t len;
	Socket_t newsock;
	char response[64];
	const TickType_t xReceiveTimeOut = TCP_RECV_TIMEOUT / portTICK_RATE_MS;
	TickType_t xSelectTimeout = 100 / portTICK_RATE_MS;
	
	/*
	 * In a serial task we don't want to waste time waiting for
	 * TCP activity since we might drop data on the UART RX end.
	 */
	if (task->serial) xSelectTimeout = 0;

	task->active_fds = FreeRTOS_CreateSocketSet();
	
	/* create listener sockets for all apps in task */
	for (i = 0; i < task->appnum; i++) {
		app = task->apps[i];
		app->master_sock = tcp_srv_init(app->tcp_port);
		FreeRTOS_FD_SET(app->master_sock, task->active_fds, eSELECT_READ | eSELECT_EXCEPT);
	}
	
	while (1) {
		/* the serial task checks for any bytes in the UART queue to send out */
		if (task->serial) {
			for (i = 0; i < task->appnum; i++) {
				recv_from_uart(task->apps[i]);
			}
		}

		/*
		 * TODO: The FreeRTOS version of select() returns the socket which
		 * triggered the event. We might be able to rewrite this to be more
		 * efficient. For now we keep the logic we used with the Interniche
		 * stack.
		 */
		sres = FreeRTOS_select(task->active_fds, xSelectTimeout);
		if (sres  < 0) {
			panic ("select");
		} else if (sres == 0) {
			// nothing happening on any socket
			continue;
		}
		
		/* cycle through configured apps and check for incoming requests */ 
		for (i = 0; i < task->appnum; i++) {
			app = task->apps[i];

			/* check for requests on established connections */
			for (j = 0; j < MAX_APP_CLIENTS; j++) {
				if (!app->clientsocks[j] || !FreeRTOS_FD_ISSET(app->clientsocks[j],
						task->active_fds))
					continue;

				/* data arriving on an already-connected socket. */
				res = FreeRTOS_recv(app->clientsocks[j], app->tcp_buffer,
				  TCP_BUFSIZE, 0);
				if (res <= 0 || app->request_handler(app, j, res) < 0) {
					/* Read error (-1) or EOF (0) */
					utils_socket_close(app->clientsocks[j]);
					FreeRTOS_FD_CLR(app->clientsocks[j], task->active_fds, eSELECT_READ | eSELECT_EXCEPT);
					app->clientsocks[j] = 0;
					continue;
				}

			if (app->app_type != SERIAL_APP) taskYIELD();
			}

			/* check for incoming new connections on master sock */
			if (!app->master_sock || !FreeRTOS_FD_ISSET(app->master_sock, task->active_fds))
				continue;

			len = sizeof (client);
			newsock = FreeRTOS_accept(app->master_sock, &client, &len);
			if (newsock < 0) {
				perror ("accept");
				panic ("EXIT_FAILURE");
			}
			FreeRTOS_setsockopt(newsock, 0, FREERTOS_SO_RCVTIMEO, &xReceiveTimeOut,
			  sizeof( xReceiveTimeOut ) );

			for (j = 0; j < MAX_APP_CLIENTS; j++) {
				if (!app->clientsocks[j]) {
					FreeRTOS_FD_SET(newsock, task->active_fds, eSELECT_READ | eSELECT_EXCEPT);
					app->clientsocks[j] = newsock;
					break;
				}
			}
			if (j >= MAX_APP_CLIENTS) {
				tcp_srv_respond_term(newsock,
				  "\r\nToo many active connections, closing!\r\n\n");
				utils_socket_close(newsock);
				continue;

			}
			if (xPortGetFreeHeapSize() < TCP_MIN_FREE_HEAP) {
				tcp_srv_respond_term(newsock,
				  "\r\nRunning low on memory, closing!\r\nConsider closing unused connections.\r\n\n");
				utils_socket_close(newsock);
				continue;
			}
			if (app->app_type != SERIAL_APP) {
				sprintf(response, "\xff\xfd\x22\xff\xfa\x22\x01\x01\xff\xf0\r\n\nConnected to %s \r\n%s> ",
				  app->name, app->name);
				tcp_srv_respond_term(newsock, response);
			}
			if (app->app_type != SERIAL_APP) taskYIELD();
		}
		if (!task->serial) utils_task_sleep(50);
	}
}


static void
batteryTask(void *parm) {
	
	LED_STATE led_mode;
	init_batteries();

	for (;;) {
		
		/* check state of CHG HS switch to determine LED status */
		if (Chip_GPIO_GetPinState(LPC_GPIO, 0, CHG_ENABLE_PIN))
			led_mode = LED_PULSE_LEVEL;
		else
			led_mode = LED_SOLID_LEVEL;
		set_led_state(led_mode);
		
		if (xSemaphoreTake(bat_semphr, 500 / portTICK_RATE_MS) == pdFALSE) {			
			continue;
		}
		update_battery_status();
		xSemaphoreGive(bat_semphr);	

		if (WATCHDOG_ON) Chip_WWDT_Feed(LPC_WWDT);
		chgwd_check();

		/* blink once to show we're still running */
		set_led_state(LED_BLINK);
		
		/* sleep for 5s to next update */
		vTaskDelay(5000 / portTICK_RATE_MS);
	}
}	


/* setup tcp socket for server */
Socket_t 
tcp_srv_init(uint16_t port)
{
	struct freertos_sockaddr me;
	const TickType_t xReceiveTimeOut = TCP_RECV_TIMEOUT / portTICK_RATE_MS;
	Socket_t elisten_sock;

	/* open TCP socket */
	me.sin_family = FREERTOS_AF_INET;
	me.sin_port = FreeRTOS_htons(port);

	if (((elisten_sock = FreeRTOS_socket(FREERTOS_AF_INET, FREERTOS_SOCK_STREAM,
	  FREERTOS_IPPROTO_TCP)) == FREERTOS_INVALID_SOCKET) ||
	  (FreeRTOS_bind(elisten_sock, &me, sizeof(me)) != 0) ||
	  (FreeRTOS_listen(elisten_sock, 4) != 0)) {
		return (NULL);
	}
	
	/* put listen socket into non-blocking mode */
	FreeRTOS_setsockopt(elisten_sock, 0, FREERTOS_SO_RCVTIMEO, &xReceiveTimeOut,
	  sizeof( xReceiveTimeOut ) );

	return (elisten_sock);
}


/*
 * Wrapper for tcp_srv_respond() to pass in strings without a length.
 * Use ONLY! for 0-terminated strings!!!
 */
int
tcp_srv_respond_term(Socket_t sock, char *terminated_str)
{
	return tcp_srv_respond(sock, terminated_str, strlen(terminated_str));
}


/* send response to client */
int
tcp_srv_respond(Socket_t sock, char *bufp, int bytestosend)
{   
   int bytessent;

   while (bytestosend) {
	  bytessent = FreeRTOS_send(sock, bufp, bytestosend, 0);

      if (bytessent < 0) {
         return (-1);
      } else if (bytessent == 0) {
            return (0);
      } else if (bytessent < bytestosend) {
         /* Only part of the data was written */
         bufp += bytessent;
         bytestosend -= bytessent;
         taskYIELD();       /* Let other tasks run before we try again */
         continue;
      } else {
         break;     /* All bytes sent  */
	  }
   }
   return (0);
}

int
load_flash_config(void)
{
	void *fptr = (void *) FLASH_CFG_LOCATION;
	int i;
	uint16_t sum = 1;

	if (FLASH_BYPASS) return (1);

	/* sync in-ram copy */
	memcpy(&flash_cfg, fptr, FLASH_CFG_SIZE);
	
	/* verify data is valid */
	for (i = 0; i < sizeof(FLASH_CFG); i++) {
		sum = (sum + ((uint8_t *)&flash_cfg)[i]) % 256;
	}
	/* chksum field is not part of the checksum */
	sum = (sum - flash_cfg.chksum) % 256;
	sum &= 0xff;
	
	if (sum != flash_cfg.chksum) {
		/* flash location is not a valid config */
		memset(&flash_cfg, 0, FLASH_CFG_SIZE);
		return (1);
	}
	return (0);
}

void
store_flash_config(void)
{
	int i;
	uint16_t sum = 1;
	void *fptr = (void *) FLASH_CFG_LOCATION;
	
	if (FLASH_BYPASS) return;

	flash_cfg.chksum = 0;
	/* calculate checksum before storing data */
	for (i = 0; i < sizeof(FLASH_CFG); i++) {
		sum = (sum + ((uint8_t *)&flash_cfg)[i]) % 256;
	}
	flash_cfg.chksum = sum;
		
	(void) iap_prepare_sector(29, 29);
	(void) iap_erase_sector(29, 29);
	(void) iap_prepare_sector(29, 29);
	(void) iap_copy_ram_to_flash(&flash_cfg, fptr, FLASH_CFG_SIZE);
}


static void
main_task_setup(TASK_CFG *maintask, TASK_CFG *serialtask) {
	
	
	int id;
	bool flash_valid = false;
	char *srv_inbuf, *serial_inbuf;

	/* alloc TCP recv buffers for main and serial tasks */
	if ((srv_inbuf = (char *)pvPortMalloc(TCP_BUFSIZE)) == NULL) panic(NULL);
	if ((serial_inbuf = (char *)pvPortMalloc(TCP_BUFSIZE)) == NULL) panic(NULL);
	memset(srv_inbuf, 0, TCP_BUFSIZE);
	memset(serial_inbuf, 0, TCP_BUFSIZE);
	
	/* zero task and app cnfiguration structs */
	memset(maintask, 0, sizeof(TASK_CFG));
	memset(serialtask, 0, sizeof(TASK_CFG));

	memset(&control_app, 0 , sizeof(control_app));
	memset(&serial0_app, 0 , sizeof(serial0_app));
	memset(&serial3_app, 0 , sizeof(serial3_app));
	memset(&battery_app, 0 , sizeof(battery_app));

	memset(&serial0_cfg, 0, sizeof(serial0_cfg));
	memset(&serial3_cfg, 0, sizeof(serial3_cfg));
	memset(&control_cfg, 0, sizeof(control_cfg));

	/* load network config from flash */
	if (load_flash_config() == 0) {
		memcpy(ucMACAddress, flash_cfg.macaddr, sizeof(ucMACAddress));
		strcpy(tcp_srv_ip, flash_cfg.ipstr);
		strcpy(tcp_srv_gw, flash_cfg.gwstr);
		strcpy(tcp_srv_nm, flash_cfg.nmstr);
		flash_valid = true;
	} else {
		/* there are no valid values in flash, store defaults for next time */
		memcpy(flash_cfg.macaddr, ucMACAddress, sizeof(ucMACAddress));
		strcpy(flash_cfg.ipstr, tcp_srv_ip);
		strcpy(flash_cfg.gwstr, tcp_srv_gw);
		strcpy(flash_cfg.nmstr, tcp_srv_nm);
	}

	/* setup for control app */
	id = 0;
	control_app.id = id;
	strcpy(control_app.name, "control");
	control_app.app_type = CTL_APP;
	control_app.request_handler = &handle_ctl_requests;
	control_app.tcp_buffer = srv_inbuf;
	control_app.parent_task = maintask;
	control_app.ext_app_cfg = &control_cfg;
	if (flash_valid && flash_cfg.app_cfgs[id].port > 0) {
		control_app.tcp_port = flash_cfg.app_cfgs[id].port;
	} else {
		flash_valid = false;
		control_app.tcp_port = base_port + id;
		flash_cfg.app_cfgs[id].port = base_port + id;
	}
	flash_cfg.app_cfgs[id].flags = FLASH_CFG_TYPE_MASK(control_app.app_type);
	

	control_cfg.apps[0] = &serial0_app;
	control_cfg.apps[1] = &serial3_app;
	control_cfg.apps[2] = &control_app;
	control_cfg.apps[3] = &battery_app;
	control_cfg.appnum = 4;
	
	/* setup for serial app on UART0 */
	id = 1;
	serial0_app.id = id;
	strcpy(serial0_app.name, "uart0");
	serial0_app.app_type = SERIAL_APP;
	serial0_app.request_handler = send_to_uart;
	serial0_app.tcp_buffer = serial_inbuf;
	serial0_app.parent_task = serialtask;
	serial0_app.ext_app_cfg = &serial0_cfg;
	if (flash_valid && flash_cfg.app_cfgs[id].port > 0) {
		serial0_app.tcp_port = flash_cfg.app_cfgs[id].port;
	} else {
		flash_valid = false;
		serial0_app.tcp_port = base_port + id;
		flash_cfg.app_cfgs[id].port = base_port + id;
	}
	flash_cfg.app_cfgs[id].flags = FLASH_CFG_TYPE_MASK(serial0_app.app_type);

	setup_uart0(&serial0_cfg, flash_cfg.app_cfgs[id].baud,
	  flash_cfg.app_cfgs[id].mode);


	/* setup for serial app on UART3 */
	id = 2;
	serial3_app.id = id;
	strcpy(serial3_app.name, "uart3");
	serial3_app.app_type = SERIAL_APP;
	serial3_app.request_handler = send_to_uart;
	serial3_app.tcp_buffer = serial_inbuf;
	serial3_app.parent_task = serialtask;
	serial3_app.ext_app_cfg = &serial3_cfg;
	if (flash_valid) {
		serial3_app.tcp_port = flash_cfg.app_cfgs[id].port;
	} else {
		serial3_app.tcp_port = base_port + id;
		flash_cfg.app_cfgs[id].port = base_port + id;
	}
	flash_cfg.app_cfgs[id].flags = FLASH_CFG_TYPE_MASK(serial3_app.app_type);

	setup_uart3(&serial3_cfg, flash_cfg.app_cfgs[id].baud,
	  flash_cfg.app_cfgs[id].mode);
	
	/* setup for battery app */
	id = 3;
	battery_app.id = id;
	strcpy(battery_app.name, "battery");
	battery_app.app_type = BAT_APP;
	battery_app.request_handler = &handle_battery_requests;
	battery_app.tcp_buffer = srv_inbuf;
	battery_app.parent_task = maintask;
	if (flash_valid && flash_cfg.app_cfgs[id].port > 0) {
		battery_app.tcp_port = flash_cfg.app_cfgs[id].port;
	} else {
		flash_valid = false;
		battery_app.tcp_port = base_port + id;
		flash_cfg.app_cfgs[id].port = base_port + id;
	}
	flash_cfg.app_cfgs[id].flags = FLASH_CFG_TYPE_MASK(battery_app.app_type);


	/* if we had to use default values for anything, store new values */
	if (!flash_valid) {
		store_flash_config();
	}
	
	/* assign apps to tasks */
	maintask->appnum = 2;
	maintask->apps[0] = &control_app;
	maintask->apps[1] = &battery_app;
	
	serialtask->serial = true;
	serialtask->appnum = 2;
	serialtask->apps[0] = &serial0_app;
	serialtask->apps[1] = &serial3_app;
	
	return;
}

/* Sets up system hardware */
static void prvSetupHardware(void)
{
	SystemCoreClockUpdate();
	Board_Init();
	smbus_setup();
}


int
main(void)
{
	/*
	 * Set up the heap for FreeRTOS.
	 * We are using all of the AHB RAM but there is still some space
	 * left in the local RAM so we make some use of that too by creating
	 * an uint8_t array and pass this along as additional heap space.
	 * See: https://docs.aws.amazon.com/freertos-kernel/latest/dg/heap-management.html#vportdefineheapregions-api-function
	 */
#define MAIN_ADDITIONAL_HEAP_SIZE	0x3000

	static uint8_t ucHeap[MAIN_ADDITIONAL_HEAP_SIZE];
	const HeapRegion_t xHeapRegions[] =
	{
		{ ucHeap, MAIN_ADDITIONAL_HEAP_SIZE },
		{ ( uint8_t * ) 0x2007C000UL, 0x8000 },
		{ NULL, 0 } /* Terminates the array. */
	};
	vPortDefineHeapRegions( xHeapRegions );

	prvSetupHardware();
	main_task_setup(&maintask, &serialtask);
	
	vSemaphoreCreateBinary(bat_semphr);

	/*
	 * FreeRTOS_IPInit wants the IP addr, etc in a weird number array
	 * format so we just convert the internally used uint32_t value
	 * and cast it to an uint8_t array.
	 * Note: this might not work on big-endian platforms
	 */
	uint32_t sip = (uint32_t) FreeRTOS_inet_addr(tcp_srv_ip);
	uint32_t snm = (uint32_t) FreeRTOS_inet_addr(tcp_srv_nm);
	uint32_t sgw = (uint32_t) FreeRTOS_inet_addr(tcp_srv_gw);
	uint32_t dns = 0UL;

	FreeRTOS_IPInit(
	  (uint8_t *)&sip,	// server IP address
	  (uint8_t *)&snm,	// netmask
	  (uint8_t *)&sgw,	// gateway
	  (uint8_t *)&dns,	// DNS server
	  ucMACAddress);	// MAC address

	/* start battery query task */
	xTaskCreate(batteryTask, "battery",
	  configMINIMAL_STACK_SIZE, NULL, 2, &to_ts_battery);

	/*
	 * The comm tasks are started after the network is up
	 * in vApplicationIPNetworkEventHook() below.
	 */

	/* enable watchdog */
	if (WATCHDOG_ON) init_wdt(WATCHDOG_TIMEOUT);

	vTaskStartScheduler();

	for( ;; ) {
	};

}



/*----------------- FreeRTOS Application Hooks ----------------------------*/

/* FUNCTION: vApplicationTickHook()
 *
 * Called by the FreeRTOS timer interrupt function.
 */
void
vApplicationTickHook(void)
{
}


#if (configUSE_IDLE_HOOK == 1)

/* FUNCTION: vApplicationIdleHook()
 *
 * Called from the "idle" task.
 *
 * PARAMS: none
 *
 * RETURN: none
 */
void
vApplicationIdleHook(void)
{
   const unsigned long ulMSToSleep = 5;

   /* Sleep to reduce CPU load, but don't sleep indefinitely in case
    * there are tasks waiting to be terminated by the idle task.
    */
   Sleep(ulMSToSleep);
}

#endif

#if (configUSE_MALLOC_FAILED_HOOK == 1)

/* FUNCITON: vApplicationMallocFailedHook()
 *
 * Called by FreeRTOS when a memory allocation request fails.
 *
 * PARAMS: none
 *
 * RETURN: none
 */
void
vApplicationMallocFailedHook(void)
{
   //panic("malloc");
	for(;;) {
	}
}

#endif

#if (configCHECK_FOR_STACK_OVERFLOW > 0)

/* FUNCTION: vApplicationStackOverflowHook()
 *
 * Called if the OS detects a task stack overflow.
 *
 * PARAM1: xTaskHandle        current FreeRTOS task
 * PARAM2: char *             name of current task
 *
 * RETURN: none
 */
void
vApplicationStackOverflowHook(xTaskHandle curTCB, char *name)
{
	//panic("stack overflow");
	for(;;) {
	}
}
#endif


/* Called by FreeRTOS+UDP when the network connects. */
void vApplicationIPNetworkEventHook( eIPCallbackEvent_t eNetworkEvent )
{
static BaseType_t xTaskAlreadyCreated = pdFALSE;

	if( eNetworkEvent == eNetworkUp )
	{
		/* Create the tasks that transmit to and receive from a standard
		echo server (see the web documentation for this port) in both
		standard and zero copy mode. */
		if( xTaskAlreadyCreated == pdFALSE )
		{
			/* Telnet comm task */
			xTaskCreate(commTask, "comm",
			  configMINIMAL_STACK_SIZE, &maintask, 2, &to_ts_comm);
			/* Serial comm task */
			xTaskCreate(commTask, "sercomm",
			  configMINIMAL_STACK_SIZE, &serialtask, 2, &to_ts_sercomm);

		}
	}
}
/*-----------------------------------------------------------*/


#if (ipconfigSUPPORT_OUTGOING_PINGS != 0)
/* Called by FreeRTOS+TCP when a reply is received to an outgoing ping request. */
void vApplicationPingReplyHook( ePingReplyStatus_t eStatus, uint16_t usIdentifier )
{
}
#endif


/* FUNCTION: vAssertCalled()
 *
 * Called if "configAssert(x)" is defined in FreeRTOSConfig.h.
 *
 * PARAMS: none
 *
 * RETURN: none
 */
void
vAssertCalled(void)
{
	for(;;) {
	}
}

void
panic(char *msg) {
	for(;;) {
	}
}
