#include <string.h>
#include "../iinchip/socket.h"
#include "../iinchip/w5100.h"
#include "../util/sockutil.h"
#include "../util/myprintf.h"
#include "../mcu/timer.h"
#include "../mcu/delay.h"
#include "../main/main.h"

#include "dhcp.h"

//#define DHCP_DEBUG

u_char SRC_MAC_ADDR[6];				// Local MAC address
u_char GET_SN_MASK[4];				// Subnet mask received from the DHCP server
u_char GET_GW_IP[4];				// Gateway ip address received from the DHCP server
u_char GET_DNS_IP[4] = "\x00\x00\x00\x00";	// DNS server ip address received from the DHCP server

char HOST_NAME[30] = "WIZnet\0";		// Host Name

u_char GET_SIP[4] = {0,};				// Local ip address received from the DHCP server
u_char DHCP_SIP[4] = {0,};				// DNS server ip address is discovered

u_char DHCP_REAL_SIP[4] = {0,};			// For extract my DHCP server in a few DHCP servers

u_char OLD_SIP[4];				// Previous local ip address received from DHCP server

char dhcp_state;				// DHCP client status
char retry_count;				// retry count

u_char DHCP_timeout;				// DHCP Timeout flag

un_l2cval lease_time;				// Leased time

u_long dhcp_time, next_dhcp_time;			// DHCP Timer tick count

u_long DHCP_XID;				

SOCKET DHCPC_SOCK;				// Socket for the DHCP client

RIP_MSG* pRIPMSG;					// Pointer for the DHCP message

void (*dhcp_ip_update)(void) = 0;		// handler to be called when the IP address from DHCP server is updated
void (*dhcp_ip_conflict)(void) = 0;		// handler to be called when the IP address from DHCP server is conflict
void send_DHCP_DISCOVER(SOCKET s);		// Send the discovery message to the DHCP server
void send_DHCP_REQUEST(SOCKET s);		// Send the request message to the DHCP server
void send_DHCP_REREQUEST(SOCKET s);		// send the re-request message to the DHCP server
void send_DHCP_RELEASE_DECLINE(SOCKET s,char msgtype);		// send the release message to the DHCP server
char parseDHCPMSG(SOCKET s, u_int length);	// Receive the message from DHCP server and parse it.
void reset_DHCP_time(void);			// Initialize DHCP Timer
void DHCP_timer_handler(void);			// DHCP Timer handler
char check_leasedIP(void);			// Check the leased IP address
void check_DHCP_Timeout(void);			// Check DHCP Timeout 
void set_DHCP_network(void);			// Apply the leased IP address to LP-NetCAM II
void proc_ip_conflict(void);			// called when the leased IP address is conflict

void reset_DHCP_time(void)
{
	dhcp_time = 0;
	next_dhcp_time = dhcp_time + DHCP_WAIT_TIME;
	retry_count = 0;
}

void send_DHCP_DISCOVER(SOCKET s)
{
	u_char ip[4];
	u_int i=0;

	u_char data_buf[DHCP_BUF] = {0};
	pRIPMSG = (RIP_MSG*)data_buf;

	*((u_long*)DHCP_SIP)=0;
	*((u_long*)DHCP_REAL_SIP)=0;
	
	pRIPMSG->op = DHCP_BOOTREQUEST;
	pRIPMSG->htype = DHCP_HTYPE10MB;
	pRIPMSG->hlen = DHCP_HLENETHERNET;
	pRIPMSG->hops = DHCP_HOPS;
	pRIPMSG->xid = htonl(DHCP_XID);
	pRIPMSG->secs = htons(DHCP_SECS);
	pRIPMSG->flags = htons(DHCP_FLAGSBROADCAST);
	pRIPMSG->chaddr[0] = SRC_MAC_ADDR[0];
	pRIPMSG->chaddr[1] = SRC_MAC_ADDR[1];
	pRIPMSG->chaddr[2] = SRC_MAC_ADDR[2];
	pRIPMSG->chaddr[3] = SRC_MAC_ADDR[3];
	pRIPMSG->chaddr[4] = SRC_MAC_ADDR[4];
	pRIPMSG->chaddr[5] = SRC_MAC_ADDR[5];

	/* MAGIC_COOKIE */
	pRIPMSG->OPT[i++] = (char)((MAGIC_COOKIE >> 24)& 0xFF);
	pRIPMSG->OPT[i++] = (char)((MAGIC_COOKIE >> 16)& 0xFF);
	pRIPMSG->OPT[i++] = (char)((MAGIC_COOKIE >> 8)& 0xFF);
	pRIPMSG->OPT[i++] = (char)(MAGIC_COOKIE& 0xFF);

	/* Option Request Param. */
	pRIPMSG->OPT[i++] = dhcpMessageType;
	pRIPMSG->OPT[i++] = 0x01;
	pRIPMSG->OPT[i++] = DHCP_DISCOVER;

	// Client identifier
	pRIPMSG->OPT[i++] = dhcpClientIdentifier;
	pRIPMSG->OPT[i++] = 0x07;
	pRIPMSG->OPT[i++] = 0x01;
	pRIPMSG->OPT[i++] = SRC_MAC_ADDR[0];
	pRIPMSG->OPT[i++] = SRC_MAC_ADDR[1];
	pRIPMSG->OPT[i++] = SRC_MAC_ADDR[2];
	pRIPMSG->OPT[i++] = SRC_MAC_ADDR[3];
	pRIPMSG->OPT[i++] = SRC_MAC_ADDR[4];
	pRIPMSG->OPT[i++] = SRC_MAC_ADDR[5];
	
	// host name
	pRIPMSG->OPT[i++] = hostName;
	pRIPMSG->OPT[i++] = strlen(HOST_NAME)+3; // length of hostname + 3
	strcpy((char*)&(pRIPMSG->OPT[i]),HOST_NAME);

	i+=strlen(HOST_NAME);

	pRIPMSG->OPT[i++] = SRC_MAC_ADDR[3];
	pRIPMSG->OPT[i++] = SRC_MAC_ADDR[4];
	pRIPMSG->OPT[i++] = SRC_MAC_ADDR[5];
	
	pRIPMSG->OPT[i++] = dhcpParamRequest;
	pRIPMSG->OPT[i++] = 0x06;
	pRIPMSG->OPT[i++] = subnetMask;
	pRIPMSG->OPT[i++] = routersOnSubnet;
	pRIPMSG->OPT[i++] = dns;
	pRIPMSG->OPT[i++] = domainName;
	pRIPMSG->OPT[i++] = dhcpT1value;
	pRIPMSG->OPT[i++] = dhcpT2value;
	pRIPMSG->OPT[i++] = endOption;

	/* send broadcasting packet */
	ip[0] = 255;
	ip[1] = 255;
	ip[2] = 255;
	ip[3] = 255;
	if(0 == sendto(s, (u_char *)pRIPMSG, sizeof(RIP_MSG), ip, DHCP_SERVER_PORT))
	{
		printf("DHCP : Fatal Error(0).\r\n");
		(*dhcp_ip_conflict)();
	}
	
	DPRINTLN("sent DHCP_DISCOVER");
}

void send_DHCP_REQUEST(SOCKET s)
{
	u_char ip[4];
	u_int i = 0;

	u_char data_buf[DHCP_BUF] = {0};
	pRIPMSG = (RIP_MSG*)data_buf;

	pRIPMSG->op = DHCP_BOOTREQUEST;
	pRIPMSG->htype = DHCP_HTYPE10MB;
	pRIPMSG->hlen = DHCP_HLENETHERNET;
	pRIPMSG->hops = DHCP_HOPS;
	pRIPMSG->xid = htonl(DHCP_XID);
	pRIPMSG->secs = htons(DHCP_SECS);

	if(dhcp_state < STATE_DHCP_LEASED)
		pRIPMSG->flags = htons(DHCP_FLAGSBROADCAST);
	else
	{
		pRIPMSG->flags = 0;		// For Unicast
		pRIPMSG->ciaddr[0] = GET_SIP[0];
		pRIPMSG->ciaddr[1] = GET_SIP[1];
		pRIPMSG->ciaddr[2] = GET_SIP[2];
		pRIPMSG->ciaddr[3] = GET_SIP[3];
	}		

	pRIPMSG->chaddr[0] = SRC_MAC_ADDR[0];
	pRIPMSG->chaddr[1] = SRC_MAC_ADDR[1];
	pRIPMSG->chaddr[2] = SRC_MAC_ADDR[2];
	pRIPMSG->chaddr[3] = SRC_MAC_ADDR[3];
	pRIPMSG->chaddr[4] = SRC_MAC_ADDR[4];
	pRIPMSG->chaddr[5] = SRC_MAC_ADDR[5];

	/* MAGIC_COOKIE */
	pRIPMSG->OPT[i++] = (u_char)((MAGIC_COOKIE >> 24) & 0xFF);
	pRIPMSG->OPT[i++] = (u_char)((MAGIC_COOKIE >> 16) & 0xFF);
	pRIPMSG->OPT[i++] = (u_char)((MAGIC_COOKIE >> 8) & 0xFF);
	pRIPMSG->OPT[i++] = (u_char)(MAGIC_COOKIE & 0xFF);

	/* Option Request Param. */
	pRIPMSG->OPT[i++] = dhcpMessageType;
	pRIPMSG->OPT[i++] = 0x01;
	pRIPMSG->OPT[i++] = DHCP_REQUEST;

	pRIPMSG->OPT[i++] = dhcpClientIdentifier;
	pRIPMSG->OPT[i++] = 0x07;
	pRIPMSG->OPT[i++] = 0x01;
	pRIPMSG->OPT[i++] = SRC_MAC_ADDR[0];
	pRIPMSG->OPT[i++] = SRC_MAC_ADDR[1];
	pRIPMSG->OPT[i++] = SRC_MAC_ADDR[2];
	pRIPMSG->OPT[i++] = SRC_MAC_ADDR[3];
	pRIPMSG->OPT[i++] = SRC_MAC_ADDR[4];
	pRIPMSG->OPT[i++] = SRC_MAC_ADDR[5];	

	if(dhcp_state < STATE_DHCP_LEASED)
	{
		pRIPMSG->OPT[i++] = dhcpRequestedIPaddr;
		pRIPMSG->OPT[i++] = 0x04;
		pRIPMSG->OPT[i++] = GET_SIP[0];
		pRIPMSG->OPT[i++] = GET_SIP[1];
		pRIPMSG->OPT[i++] = GET_SIP[2];
		pRIPMSG->OPT[i++] = GET_SIP[3];
	
		pRIPMSG->OPT[i++] = dhcpServerIdentifier;
		pRIPMSG->OPT[i++] = 0x04;
		pRIPMSG->OPT[i++] = DHCP_SIP[0];
		pRIPMSG->OPT[i++] = DHCP_SIP[1];
		pRIPMSG->OPT[i++] = DHCP_SIP[2];
		pRIPMSG->OPT[i++] = DHCP_SIP[3];
	}
	
	// host name
	pRIPMSG->OPT[i++] = hostName;
	pRIPMSG->OPT[i++] = strlen(HOST_NAME)+3; // length of hostname + 3
	strcpy((char*)&(pRIPMSG->OPT[i]),HOST_NAME);

	i+=strlen(HOST_NAME);

	pRIPMSG->OPT[i++] = SRC_MAC_ADDR[3];
	pRIPMSG->OPT[i++] = SRC_MAC_ADDR[4];
	pRIPMSG->OPT[i++] = SRC_MAC_ADDR[5];		
	
	pRIPMSG->OPT[i++] = dhcpParamRequest;
	pRIPMSG->OPT[i++] = 0x08;
	pRIPMSG->OPT[i++] = subnetMask;
	pRIPMSG->OPT[i++] = routersOnSubnet;
	pRIPMSG->OPT[i++] = dns;
	pRIPMSG->OPT[i++] = domainName;
	pRIPMSG->OPT[i++] = dhcpT1value;
	pRIPMSG->OPT[i++] = dhcpT2value;
	pRIPMSG->OPT[i++] = performRouterDiscovery;
	pRIPMSG->OPT[i++] = staticRoute;
	pRIPMSG->OPT[i++] = endOption;

	/* send broadcasting packet */
	if(dhcp_state < STATE_DHCP_LEASED)
	{
		ip[0] = 255;
		ip[1] = 255;
		ip[2] = 255;
		ip[3] = 255;
	}
	else
	{
		ip[0] = DHCP_SIP[0];
		ip[1] = DHCP_SIP[1];
		ip[2] = DHCP_SIP[2];
		ip[3] = DHCP_SIP[3];
	}

	if(0 == sendto(s, (u_char*)pRIPMSG, sizeof(RIP_MSG), ip, DHCP_SERVER_PORT))
	{
		printf("DHCP : Fatal Error(1).\r\n");
		(*dhcp_ip_conflict)();
	}

	DPRINTLN("sent DHCP_REQUEST");

}

void send_DHCP_RELEASE_DECLINE(SOCKET s,char msgtype)
{
	u_int i =0;
	u_char ip[4];
	
	u_char data_buf[DHCP_BUF] = {0};
	pRIPMSG = (RIP_MSG*)data_buf;

	pRIPMSG->op = DHCP_BOOTREQUEST;
	pRIPMSG->htype = DHCP_HTYPE10MB;
	pRIPMSG->hlen = DHCP_HLENETHERNET;
	pRIPMSG->hops = DHCP_HOPS;
	pRIPMSG->xid = htonl(DHCP_XID);
	pRIPMSG->secs = htons(DHCP_SECS);
	pRIPMSG->flags = 0;	//DHCP_FLAGSBROADCAST;

	pRIPMSG->chaddr[0] = SRC_MAC_ADDR[0];
	pRIPMSG->chaddr[1] = SRC_MAC_ADDR[1];
	pRIPMSG->chaddr[2] = SRC_MAC_ADDR[2];
	pRIPMSG->chaddr[3] = SRC_MAC_ADDR[3];
	pRIPMSG->chaddr[4] = SRC_MAC_ADDR[4];
	pRIPMSG->chaddr[5] = SRC_MAC_ADDR[5];


	/* MAGIC_COOKIE */
	pRIPMSG->OPT[i++] = (u_char)((MAGIC_COOKIE >> 24) & 0xFF);
	pRIPMSG->OPT[i++] = (u_char)((MAGIC_COOKIE >> 16) & 0xFF);
	pRIPMSG->OPT[i++] = (u_char)((MAGIC_COOKIE >> 8) & 0xFF);
	pRIPMSG->OPT[i++] = (u_char)(MAGIC_COOKIE & 0xFF);

	/* Option Request Param. */
	pRIPMSG->OPT[i++] = dhcpMessageType;
	pRIPMSG->OPT[i++] = 0x01;
	pRIPMSG->OPT[i++] = ((!msgtype) ? DHCP_RELEASE : DHCP_DECLINE);

	pRIPMSG->OPT[i++] = dhcpClientIdentifier;
	pRIPMSG->OPT[i++] = 0x07;
	pRIPMSG->OPT[i++] = 0x01;
	pRIPMSG->OPT[i++] = SRC_MAC_ADDR[0];
	pRIPMSG->OPT[i++] = SRC_MAC_ADDR[1];
	pRIPMSG->OPT[i++] = SRC_MAC_ADDR[2];
	pRIPMSG->OPT[i++] = SRC_MAC_ADDR[3];
	pRIPMSG->OPT[i++] = SRC_MAC_ADDR[4];
	pRIPMSG->OPT[i++] = SRC_MAC_ADDR[5];	

	pRIPMSG->OPT[i++] = dhcpServerIdentifier;
	pRIPMSG->OPT[i++] = 0x04;
	pRIPMSG->OPT[i++] = DHCP_SIP[0];
	pRIPMSG->OPT[i++] = DHCP_SIP[1];
	pRIPMSG->OPT[i++] = DHCP_SIP[2];
	pRIPMSG->OPT[i++] = DHCP_SIP[3];

	if(msgtype)
	{
		pRIPMSG->OPT[i++] = dhcpRequestedIPaddr;
		pRIPMSG->OPT[i++] = 0x04;
		pRIPMSG->OPT[i++] = GET_SIP[0];
		pRIPMSG->OPT[i++] = GET_SIP[1];
		pRIPMSG->OPT[i++] = GET_SIP[2];
		pRIPMSG->OPT[i++] = GET_SIP[3];		
		pRIPMSG->OPT[i++] = endOption;
		DPRINTLN("sent DHCP_DECLINE");
	}
	else
	{
		pRIPMSG->OPT[i++] = endOption;
		DPRINTLN("sent DHCP_RELEASE");
	}
	
	if(!msgtype)
	{
		ip[0] = DHCP_SIP[0];
		ip[1] = DHCP_SIP[1];
		ip[2] = DHCP_SIP[2];
		ip[3] = DHCP_SIP[3];
	}
	else
	{
		ip[0] = 255;
		ip[1] = 255;
		ip[2] = 255;
		ip[3] = 255;
	}

	if(0 == sendto(s, (u_char *)pRIPMSG, sizeof(RIP_MSG), ip, DHCP_SERVER_PORT))
	{
		printf("DHCP : Fatal Error(2).\r\n");
		(*dhcp_ip_conflict)();
	}	
}

char parseDHCPMSG(SOCKET s, u_int length)
{
	u_char svr_addr[6];
	u_int  svr_port;

	u_char data_buf[DHCP_BUF] = {0};
	pRIPMSG = (RIP_MSG*)data_buf;

	u_int len;
	u_char * p;
	u_char * e;
	u_char type, opt_len = 0;

	len = recvfrom(s, (u_char *)pRIPMSG, length, svr_addr, &svr_port);

#ifdef DHCP_DEBUG
	u_int j;
	for(j=0; j<len; j++) {
		DPRINT1("%.2X", data_buf[j]);
		if((j%0x10)==15) DPRINTLN("");
	}
	DPRINTLN("");
#endif	

#ifdef DHCP_DUBUG
	printf("DHCP_SIP:%d.%d.%d.%d\r\n",DHCP_SIP[0],DHCP_SIP[1],DHCP_SIP[2],DHCP_SIP[3]);
	printf("DHCP_RIP:%d.%d.%d.%d\r\n",DHCP_REAL_SIP[0],DHCP_REAL_SIP[1],DHCP_REAL_SIP[2],DHCP_REAL_SIP[3]);
	printf("svr_addr:%d.%d.%d.%d\r\n",svr_addr[0],svr_addr[1],svr_addr[2],svr_addr[3]);
#endif	

	if(pRIPMSG->op != DHCP_BOOTREPLY)
	{
		DPRINTLN("DHCP : NO DHCP MSG");
	}
	else
	{
		if (svr_port == DHCP_SERVER_PORT)
		{
			if(memcmp(pRIPMSG->chaddr,SRC_MAC_ADDR,6) != 0 || pRIPMSG->xid != htonl(DHCP_XID))
			{
				printf("No My DHCP Message. This message is ignored.\r\n");

#ifdef DHCP_DEBUG
				printf("\tSRC_MAC_ADDR(%02X.%02X.%02X.",SRC_MAC_ADDR[0],SRC_MAC_ADDR[1],SRC_MAC_ADDR[2]);
				printf("%02X.%02X.%02X)",SRC_MAC_ADDR[3],SRC_MAC_ADDR[4],SRC_MAC_ADDR[5]);
				printf(", pRIPMSG->chaddr(%02X.%02X.%02X.",pRIPMSG->chaddr[0],pRIPMSG->chaddr[1],pRIPMSG->chaddr[2]);
				printf("%02X.%02X.%02X)\r\n",pRIPMSG->chaddr[3],pRIPMSG->chaddr[4],pRIPMSG->chaddr[5]);
				printf("\tpRIPMSG->xid(%08lX), DHCP_XID(%08lX)\r\n",pRIPMSG->xid,htonl(DHCP_XID));
				printf("\tpRIMPMSG->yiaddr:%d.%d.%d.%d\r\n",pRIPMSG->yiaddr[0],pRIPMSG->yiaddr[1],pRIPMSG->yiaddr[2],pRIPMSG->yiaddr[3]);
#endif
				return 0;
			}
			if( *((u_long*)DHCP_SIP) != 0x00000000 )
			{
				if( *((u_long*)DHCP_REAL_SIP) != *((u_long*)svr_addr) && 
					*((u_long*)DHCP_SIP) != *((u_long*)svr_addr) ) 
				{
#ifdef DHCP_DEBUG		
					printf("Another DHCP sever send a response message. This is ignored.\r\n");
					printf("\tIP:%d.%d.%d.%d\r\n",svr_addr[0],svr_addr[1],svr_addr[2],svr_addr[3]);
#endif				
					return 0;								
				}
			}
			
			memcpy(GET_SIP,pRIPMSG->yiaddr,4);
	
			DPRINTLN("DHCP MSG received..");
			DPRINTLN4("yiaddr : %d.%d.%d.%d",GET_SIP[0],GET_SIP[1],GET_SIP[2],GET_SIP[3]);
	
			type = 0;
			p = (u_char *)(&pRIPMSG->op);
			p = p + 240;
			e = p + (len - 240);
	
			DPRINTLN3("p : 0x%08X  e : 0x%08X  len : %d\r\n", (u_int)p, (u_int)e, len);
	
			while ( p < e ) 
			{
				switch ( *p++ ) 
				{
				case endOption :
				 	return type;
					break;	
	       		case padOption :
					break;
				case dhcpMessageType :
					opt_len = *p++;
					type = *p;
					DPRINTLN1("dhcpMessageType : %x", type);
	
					break;
				case subnetMask :
					opt_len =* p++;
					memcpy(GET_SN_MASK,p,4);
	
					DPRINT("subnetMask : ");
					DPRINTLN4("%d.%d.%d.%d",GET_SN_MASK[0],GET_SN_MASK[1],GET_SN_MASK[2],GET_SN_MASK[3]);
	
					break;
				case routersOnSubnet :
					opt_len = *p++;
					memcpy(GET_GW_IP,p,4);
	
					DPRINT("routersOnSubnet : ");
					DPRINTLN4("%d.%d.%d.%d",GET_GW_IP[0],GET_GW_IP[1],GET_GW_IP[2],GET_GW_IP[3]);
	
					break;
				case dns :
					opt_len = *p++;
					memcpy(GET_DNS_IP,p,4);
					break;
				case dhcpIPaddrLeaseTime :
					opt_len = *p++;


#ifdef LITTLE_ENDIAN	
					lease_time.cVal[3] = *p;
					lease_time.cVal[2] = *(p+1);
					lease_time.cVal[1] = *(p+2);
					lease_time.cVal[0] = *(p+3);
#else
					lease_time.cVal[0] = *p;
					lease_time.cVal[1] = *(p+1);
					lease_time.cVal[2] = *(p+2);
					lease_time.cVal[3] = *(p+3);
#endif

					DPRINTLN1("dhcpIPaddrLeaseTime : %08lX", lease_time.lVal);
	
					break;
	
				case dhcpServerIdentifier :
					opt_len = *p++;
					printf("DHCP_SIP : %d.%d.%d.%d\r\n", DHCP_SIP[0], DHCP_SIP[1], DHCP_SIP[2], DHCP_SIP[3]);
					
					// The first response DHCP server is regarded as my DHCH server
					if( *((u_long*)DHCP_SIP) == 0 || 
					    *((u_long*)DHCP_REAL_SIP) == *((u_long*)svr_addr) || 
					    *((u_long*)DHCP_SIP) == *((u_long*)svr_addr) )
					{
						memcpy(DHCP_SIP,p,4);
						memcpy(DHCP_REAL_SIP,svr_addr,4);	// Copy the real ip address of my DHCP server
#ifdef DHCP_DEBUG						
						printf("My dhcpServerIdentifier : ");
						printf("%d.%d.%d.%d\r\n", DHCP_SIP[0], DHCP_SIP[1], DHCP_SIP[2], DHCP_SIP[3]);
						printf("My DHCP server real IP address : ");
						printf("%d.%d.%d.%d\r\n", DHCP_REAL_SIP[0], DHCP_REAL_SIP[1], DHCP_REAL_SIP[2], DHCP_REAL_SIP[3]);
#endif						
					}
					else
					{
						printf("Another dhcpServerIdentifier : ");
						printf("\tMY(%d.%d.%d.%d) \r\n", DHCP_SIP[0], DHCP_SIP[1], DHCP_SIP[2], DHCP_SIP[3]);
						printf("Another(%d.%d.%d.%d) : \r\n", svr_addr[0], svr_addr[1], svr_addr[2], svr_addr[3]);
					}
					break;
	
				default :
					opt_len = *p++;
	
					DPRINTLN1("opt_len : %d", opt_len);
	
					break;
				} // switch
				p+=opt_len;
			} // while
		} // if
	}
	return 0;
}

void check_DHCP_state(SOCKET s) 
{
	u_int len;
	u_char type;

	type = 0;
	if( (s !=(SOCKET)-1) && getSn_SR(s) !=SOCK_CLOSED)
	{
		if ((len = getSn_RX_RSR(s)) > 0)
		{
			 type = parseDHCPMSG(s, len);
		}
	}

	else if(!socket(s, Sn_MR_UDP, DHCP_CLIENT_PORT, 0x00))
	{
		DPRINTLN("Fail to create the DHCPC_SOCK");
	}
	switch ( dhcp_state )
	{
	case STATE_DHCP_DISCOVER :
		if (type == DHCP_OFFER) 
		{
			reset_DHCP_time();
			send_DHCP_REQUEST(s);
			dhcp_state = STATE_DHCP_REQUEST;
			DPRINTLN("state : STATE_DHCP_REQUEST");
		}
		else
		{
 			check_DHCP_Timeout();
		}
		break;

	case STATE_DHCP_REQUEST :
		if (type == DHCP_ACK) 
		{
			reset_DHCP_time();
			if (check_leasedIP()) 
			{
				set_DHCP_network();
				dhcp_state = STATE_DHCP_LEASED;
				DPRINTLN("state : STATE_DHCP_LEASED");
			} 
			else 
			{
				dhcp_state = STATE_DHCP_DISCOVER;
				DPRINTLN("state : STATE_DHCP_DISCOVER");
			}
		}
		else if (type == DHCP_NAK) 
		{
			reset_DHCP_time();
			dhcp_state = STATE_DHCP_DISCOVER;
			DPRINTLN("state : STATE_DHCP_DISCOVER");
		}
		else check_DHCP_Timeout();
		break;

	case STATE_DHCP_LEASED :
		if ((lease_time.lVal != 0xffffffff) && ((lease_time.lVal/2) < dhcp_time)) 
		{
			type = 0;
			memcpy(OLD_SIP,GET_SIP,4);
			DHCP_XID++;
			send_DHCP_REQUEST(s);
			dhcp_state = STATE_DHCP_REREQUEST;
			DPRINTLN("state : STATE_DHCP_REREQUEST");
			reset_DHCP_time();
		}
		break;

	case STATE_DHCP_REREQUEST :
		if (type == DHCP_ACK) 
		{
			if(memcmp(OLD_SIP,GET_SIP,4)!=0)	
			{
				DPRINTLN2("OLD_SIP=%s,GET_SIP=%s",inet_ntoa(ntohl(*((u_long*)OLD_SIP))), inet_ntoa(ntohl(*((u_long*)GET_SIP))));
				(*dhcp_ip_update)();
				printf("The IP address from the DHCP server is updated.\r\n");				
			}
			else
			{
				DPRINTLN("state : STATE_DHCP_LEASED : same IP");
			}
			reset_DHCP_time();
			dhcp_state = STATE_DHCP_LEASED;
		} 
		else if (type == DHCP_NAK) 
		{
			reset_DHCP_time();
			dhcp_state = STATE_DHCP_DISCOVER;
			DPRINTLN("state : STATE_DHCP_DISCOVER");
		} 
		else check_DHCP_Timeout();
		break;

	case STATE_DHCP_RELEASE :
		break;
	default :
		break;
	}
}

void check_DHCP_Timeout(void)
{
	if (retry_count < MAX_DHCP_RETRY) 
	{
		if (next_dhcp_time < dhcp_time) 
		{
			dhcp_time = 0;
			next_dhcp_time = dhcp_time + DHCP_WAIT_TIME;
			retry_count++;
			switch ( dhcp_state ) 
			{
			case STATE_DHCP_DISCOVER :
				DPRINTLN("<<timeout>> state : STATE_DHCP_DISCOVER");
				send_DHCP_DISCOVER(DHCPC_SOCK);
				break;
	
			case STATE_DHCP_REQUEST :
				DPRINTLN("<<timeout>> state : STATE_DHCP_REQUEST");
				send_DHCP_REQUEST(DHCPC_SOCK);
				break;

			case STATE_DHCP_REREQUEST :
				DPRINTLN("<<timeout>> state : STATE_DHCP_REREQUEST");
				send_DHCP_REQUEST(DHCPC_SOCK);
				break;
	
			default :
				break;
			}
		}
	} 
	else 
	{
		reset_DHCP_time();
		DHCP_timeout = 1;
		
		send_DHCP_DISCOVER(DHCPC_SOCK);
		dhcp_state = STATE_DHCP_DISCOVER;
		DPRINTLN("timeout\r\nstate : STATE_DHCP_DISCOVER");
	}
}

void set_DHCP_network(void)
{
	iinchip_init();
	setGAR(GET_GW_IP);
	setSUBR(GET_SN_MASK);
	setSIPR(GET_SIP);
#ifdef __DEF_IINCHIP_INT__
	setIMR(0xEF);
#endif	
	sysinit(0x55, 0x55);
	DPRINTLN4("DHCP Set IP OK. %d.%d.%d.%d\r\n", GET_SIP[0], GET_SIP[1], GET_SIP[2], GET_SIP[3]);
}

char check_leasedIP(void)
{

	printf("<Check the IP Conflict : ");
	// sendto is complete. that means there is a node which has a same IP.
	if (sendto(DHCPC_SOCK, (u_char*)"CHECK_IP_CONFLICT", 17, GET_SIP, 5000) > 0)
	{
		printf(" Conflict>\r\n");
		send_DHCP_RELEASE_DECLINE(DHCPC_SOCK,1);
		(*dhcp_ip_conflict)();
		return 0;
	}
	printf(" No Conflict>\r\n");
	return 1;
}	

u_int getIP_DHCPS() 
{
	DPRINTLN("DHCP SetIP..");

	send_DHCP_DISCOVER(DHCPC_SOCK);
	dhcp_state = STATE_DHCP_DISCOVER;

	reset_DHCP_time();
	set_timer(DHCP_CHECK_TIMER2, DHCP_timer_handler);
	DHCP_timeout = 0;

	while (dhcp_state != STATE_DHCP_LEASED)
	{
		if (DHCP_timeout == 1)
		{
			kill_timer(DHCP_CHECK_TIMER2);
			return 0;
		}
		check_DHCP_state(DHCPC_SOCK);
	}
	
	return 1;
}

void DHCP_timer_handler(void)
{
	dhcp_time++;
}

void init_dhcp_client(SOCKET s,void(*ip_update)(void),void(*ip_conflict)(void))
{
	if(!ip_update)	dhcp_ip_update = set_DHCP_network;
	else		dhcp_ip_update = ip_update;

	if(!ip_conflict) dhcp_ip_conflict = proc_ip_conflict;
	else		 dhcp_ip_conflict = ip_conflict;

	init_dhcpc_ch(s);
}

void proc_ip_conflict(void)
{
	printf("The IP Address from DHCP server is CONFLICT!!!\r\n"); 
	printf("Retry to get a IP address from DHCP server\r\n");
}

u_int init_dhcpc_ch(SOCKET s)
{
	u_int ret;

	DHCP_XID = 0x12345678;
	memset(GET_SIP,0,4);
	memset(GET_GW_IP,0,4);
	memset(GET_SN_MASK,0,4);
	
	iinchip_init();
	setSIPR(GET_SIP);
	setSHAR(SRC_MAC_ADDR);
#ifdef __DEF_IINCHIP_INT__
	setIMR(0xEF);
#endif	
	sysinit(0x55, 0x55);

#ifdef DHCP_DEBUG
{
	u_int i;
	PRINT("MAC : ");
	for (i = 0; i < 5; i++) PRINT1("0x%02X.", SRC_MAC_ADDR[i]);
	printf("0x%02X\r\n",SRC_MAC_ADDR[5]);
}	
#endif

	DPRINT1("DHCP socket %d ",s);
	if(!socket(s, Sn_MR_UDP, DHCP_CLIENT_PORT, 0x00))
	{
		DPRINTLN("fail..");
		ret = 0;
	}
	else
	{
		DPRINTLN("ok..");
		ret = 1;
	}
	DHCPC_SOCK = s;
	return ret;
}
