text: //*****************************************************************************
  //
  //! \file dhcp.c
  //! \brief DHCP APIs implement file.
  //! \details Processing DHCP protocol as DISCOVER, OFFER, REQUEST, ACK, NACK and DECLINE.
  //! \version 1.1.1
  //! \date 2019/10/08
  //! \par  Revision history
  //!       <2019/10/08> compare DHCP server ip address
  //!       <2013/11/18> 1st Release
  //!       <2012/12/20> V1.1.0
  //!         1. Optimize code
  //!         2. Add DHCP_register_callback_functions()
  //!         3. Add DHCP_stop()
  //!         4. Integrate check_DHCP_state() & DHCP_run() to DHCP_run()
  //!         5. Don't care system endian
  //!         6. Add comments
  //!       <2012/12/26> V1.1.1
  //!         1. Modify variable declaration: dhcp_tick_1s is declared volatile for code optimization
  //! \author Eric Jung & MidnightCow
  //! \copyright
  //!
  //! Copyright (c)  2013, WIZnet Co., LTD.
  //! All rights reserved.
  //!
  //! Redistribution and use in source and binary forms, with or without
  //! modification, are permitted provided that the following conditions
  //! are met:
  //!
  //!     * Redistributions of source code must retain the above copyright
  //! notice, this list of conditions and the following disclaimer.
  //!     * Redistributions in binary form must reproduce the above copyright
  //! notice, this list of conditions and the following disclaimer in the
  //! documentation and/or other materials provided with the distribution.
  //!     * Neither the name of the <ORGANIZATION> nor the names of its
  //! contributors may be used to endorse or promote products derived
  //! from this software without specific prior written permission.
  //!
  //! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  //! AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  //! IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  //! ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  //! LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  //! CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  //! SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  //! INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  //! CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  //! ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  //! THE POSSIBILITY OF SUCH DAMAGE.
  //
  //*****************************************************************************
  
  #include "dhcp.h"
  #include "socket.h"
  
  /* If you want to display debug & processing message, Define _DHCP_DEBUG_ in dhcp.h */
  #ifdef _DHCP_DEBUG_
  #include <stdio.h>
  #endif
  
  /* DHCP state machine. */
  #define STATE_DHCP_INIT 0			 // < Initialize
  #define STATE_DHCP_DISCOVER 1	 // < send DISCOVER and wait OFFER
  #define STATE_DHCP_REQUEST 2	 // < send REQEUST and wait ACK or NACK
  #define STATE_DHCP_LEASED 3		 // < ReceiveD ACK and IP leased
  #define STATE_DHCP_REREQUEST 4 // < send REQUEST for maintaining leased IP
  #define STATE_DHCP_RELEASE 5	 // < No use
  #define STATE_DHCP_STOP 6			 // < Stop processing DHCP
  
  #define DHCP_FLAG_BROADCAST 0x8000 // < The broadcast value of flags in @ref RIP_MSG
  #define DHCP_FLAG_UNICAST 0x0000	 // < The unicast   value of flags in @ref RIP_MSG
  
  /* DHCP message OP code */
  #define DHCP_BOOTREQUEST 1 // < Request Message used in op of @ref RIP_MSG
  #define DHCP_BOOTREPLY 2	 // < Reply Message used i op of @ref RIP_MSG
  
  /* DHCP message type */
  #define DHCP_DISCOVER 1 // < DISCOVER message in OPT of @ref RIP_MSG
  #define DHCP_OFFER 2		// < OFFER message in OPT of @ref RIP_MSG
  #define DHCP_REQUEST 3	// < REQUEST message in OPT of @ref RIP_MSG
  #define DHCP_DECLINE 4	// < DECLINE message in OPT of @ref RIP_MSG
  #define DHCP_ACK 5			// < ACK message in OPT of @ref RIP_MSG
  #define DHCP_NAK 6			// < NACK message in OPT of @ref RIP_MSG
  #define DHCP_RELEASE 7	// < RELEASE message in OPT of @ref RIP_MSG. No use
  #define DHCP_INFORM 8		// < INFORM message in OPT of @ref RIP_MSG. No use
  
  #define DHCP_HTYPE10MB 1	// < Used in type of @ref RIP_MSG
  #define DHCP_HTYPE100MB 2 // < Used in type of @ref RIP_MSG
  
  #define DHCP_HLENETHERNET 6 // < Used in hlen of @ref RIP_MSG
  #define DHCP_HOPS 0					// < Used in hops of @ref RIP_MSG
  #define DHCP_SECS 0					// < Used in secs of @ref RIP_MSG
  
  #define INFINITE_LEASETIME 0xffffffff ///< Infinite lease time
  
  #define OPT_SIZE 312									/// Max OPT size of @ref RIP_MSG
  #define RIP_MSG_SIZE (236 + OPT_SIZE) /// Max size of @ref RIP_MSG
  
  /*
   * @brief DHCP option and value (cf. RFC1533)
   */
  enum {
  	padOption = 0,
  	subnetMask = 1,
  	routersOnSubnet = 3,
  	hostName = 12,
  	performRouterDiscovery = 31,
  	staticRoute = 33,
  	dhcpRequestedIPaddr = 50,
  	dhcpIPaddrLeaseTime = 51,
  	dhcpMessageType = 53,
  	dhcpServerIdentifier = 54,
  	dhcpParamRequest = 55,
  	dhcpMsg = 56,
  	dhcpClientIdentifier = 61,
  	endOption = 255
  };
  
  /*
   * @brief DHCP message format
   */
  typedef struct {
  	uint8_t op;						 ///< @ref DHCP_BOOTREQUEST or @ref DHCP_BOOTREPLY
  	uint8_t htype;				 ///< @ref DHCP_HTYPE10MB or @ref DHCP_HTYPE100MB
  	uint8_t hlen;					 ///< @ref DHCP_HLENETHERNET
  	uint8_t hops;					 ///< @ref DHCP_HOPS
  	uint32_t xid;					 ///< @ref dhcp_xid_transaction_id  This increase one every DHCP transaction.
  	uint16_t secs;				 ///< @ref DHCP_SECS
  	uint16_t flags;				 ///< @ref DHCP_FLAG_BROADCAST or @ref DHCP_FLAG_UNICAST
  	uint8_t ciaddr[4];		 ///< @ref Request IP to DHCP sever
  	uint8_t yiaddr[4];		 ///< @ref Offered IP from DHCP server
  	uint8_t siaddr[4];		 ///< No use
  	uint8_t giaddr[4];		 ///< No use
  	uint8_t chaddr[16];		 ///< DHCP client 6bytes MAC address. Others is filled to zero
  	uint8_t sname[64];		 ///< No use
  	uint8_t file[128];		 ///< No use
  	uint8_t OPT[OPT_SIZE]; ///< Option
  } RIP_MSG;
  
  uint8_t DHCP_SOCKET; // Socket number for DHCP
  uint8_t DHCP_DEVICE_TYPE; // Device type for DHCP
  
  uint8_t DHCP_SIP[4];			// DHCP Server IP address
  uint8_t DHCP_REAL_SIP[4]; // For extract my DHCP server in a few DHCP server
  
  // Network information from DHCP Server
  // Previous IP address
  uint8_t OLD_allocated_ip[4] = {
  		0,
  };
  // IP address from DHCP
  uint8_t DHCP_allocated_ip[4] = {
  		0,
  };
  // Gateway address from DHCP
  uint8_t DHCP_allocated_gw[4] = {
  		0,
  };
  // Subnet mask from DHCP
  uint8_t DHCP_allocated_sn[4] = {
  		0,
  };
  
  int8_t dhcp_state = STATE_DHCP_INIT; // DHCP state
  int8_t dhcp_retry_count = 0;
  
  uint32_t dhcp_lease_time = INFINITE_LEASETIME;
  volatile uint32_t dhcp_tick_1s = 0; // unit 1 second
  uint32_t dhcp_tick_next = DHCP_WAIT_TIME;
  
  uint32_t dhcp_xid_transaction_id = 0x00000000; // Any number
  
  RIP_MSG *pDHCPMSG; // Buffer pointer for DHCP processing
  
  uint8_t dhcp_mac_address[6]; // DHCP Client MAC address.
  
  /* The default callback function */
  void default_ip_assign(void);
  void default_ip_update(void);
  void default_ip_conflict(void);
  
  /* Callback handler */
  void (*dhcp_ip_assign)(void) = default_ip_assign;			/* handler to be called when the IP address from DHCP server is first assigned */
  void (*dhcp_ip_update)(void) = default_ip_update;			/* handler to be called when the IP address from DHCP server is updated */
  void (*dhcp_ip_conflict)(void) = default_ip_conflict; /* handler to be called when the IP address from DHCP server is conflict */
  
  void DHCP_register_callback_functions(void (*ip_assign)(void), void (*ip_update)(void), void (*ip_conflict)(void));
  
  char NibbleToHex(uint8_t nibble);
  
  /* send DISCOVER message to DHCP server */
  void send_DHCP_DISCOVER(void);
  
  /* send REQEUST message to DHCP server */
  void send_DHCP_REQUEST(void);
  
  /* send DECLINE message to DHCP server */
  void send_DHCP_DECLINE(void);
  
  /* IP conflict check by sending ARP-request to leased IP and wait ARP-response. */
  int8_t check_DHCP_leasedIP(void);
  
  /* check the timeout in DHCP process */
  uint8_t check_DHCP_timeout(void);
  
  /* Initialize to timeout process.  */
  void reset_DHCP_timeout(void);
  
  /* Parse message as OFFER and ACK and NACK from DHCP server.*/
  int8_t parseDHCPCMSG(void);
  
  /* The default handler of ip assign first */
  void default_ip_assign(void) {
  	setSIPR(DHCP_allocated_ip);
  	setSUBR(DHCP_allocated_sn);
  	setGAR(DHCP_allocated_gw);
  }
  
  /* The default handler of ip changed */
  void default_ip_update(void) {
  	/* WIZchip Software Reset */
  	setMR(MR_RST);
  	getMR(); // for delay
  	default_ip_assign();
  	setSHAR(dhcp_mac_address);
  }
  
  /* The default handler of ip changed */
  void default_ip_conflict(void) {
  	// WIZchip Software Reset
  	setMR(MR_RST);
  	getMR(); // for delay
  	setSHAR(dhcp_mac_address);
  }
  
  /* register the call back func. */
  void DHCP_register_callback_functions(void (*ip_assign)(void), void (*ip_update)(void), void (*ip_conflict)(void)) {
  	dhcp_ip_assign = ip_assign;
  	dhcp_ip_update = ip_update;
  	dhcp_ip_conflict = ip_conflict;
  }
  
  /* make the common DHCP message */
  void makeDHCPMSG(void) {
  	uint8_t bk_mac[6];
  	uint8_t *ptmp;
  	uint8_t i;
  	getSHAR(bk_mac);
  	pDHCPMSG->op = DHCP_BOOTREQUEST;
  	pDHCPMSG->htype = DHCP_HTYPE10MB;
  	pDHCPMSG->hlen = DHCP_HLENETHERNET;
  	pDHCPMSG->hops = DHCP_HOPS;
  	ptmp = (uint8_t *)(&pDHCPMSG->xid);
  	*(ptmp + 0) = (uint8_t)((dhcp_xid_transaction_id & 0xFF000000) >> 24);
  	*(ptmp + 1) = (uint8_t)((dhcp_xid_transaction_id & 0x00FF0000) >> 16);
  	*(ptmp + 2) = (uint8_t)((dhcp_xid_transaction_id & 0x0000FF00) >> 8);
  	*(ptmp + 3) = (uint8_t)((dhcp_xid_transaction_id & 0x000000FF) >> 0);
  	pDHCPMSG->secs = DHCP_SECS;
  	ptmp = (uint8_t *)(&pDHCPMSG->flags);
  	*(ptmp + 0) = (uint8_t)((DHCP_FLAG_BROADCAST & 0xFF00) >> 8);
  	*(ptmp + 1) = (uint8_t)((DHCP_FLAG_BROADCAST & 0x00FF) >> 0);
  
  	pDHCPMSG->ciaddr[0] = 0;
  	pDHCPMSG->ciaddr[1] = 0;
  	pDHCPMSG->ciaddr[2] = 0;
  	pDHCPMSG->ciaddr[3] = 0;
  
  	pDHCPMSG->yiaddr[0] = 0;
  	pDHCPMSG->yiaddr[1] = 0;
  	pDHCPMSG->yiaddr[2] = 0;
  	pDHCPMSG->yiaddr[3] = 0;
  
  	pDHCPMSG->siaddr[0] = 0;
  	pDHCPMSG->siaddr[1] = 0;
  	pDHCPMSG->siaddr[2] = 0;
  	pDHCPMSG->siaddr[3] = 0;
  
  	pDHCPMSG->giaddr[0] = 0;
  	pDHCPMSG->giaddr[1] = 0;
  	pDHCPMSG->giaddr[2] = 0;
  	pDHCPMSG->giaddr[3] = 0;
  
  	pDHCPMSG->chaddr[0] = dhcp_mac_address[0];
  	pDHCPMSG->chaddr[1] = dhcp_mac_address[1];
  	pDHCPMSG->chaddr[2] = dhcp_mac_address[2];
  	pDHCPMSG->chaddr[3] = dhcp_mac_address[3];
  	pDHCPMSG->chaddr[4] = dhcp_mac_address[4];
  	pDHCPMSG->chaddr[5] = dhcp_mac_address[5];
  
  	for (i = 6; i < 16; i++)
  		pDHCPMSG->chaddr[i] = 0;
  	for (i = 0; i < 64; i++)
  		pDHCPMSG->sname[i] = 0;
  	for (i = 0; i < 128; i++)
  		pDHCPMSG->file[i] = 0;
  
  	// MAGIC_COOKIE
  	pDHCPMSG->OPT[0] = (uint8_t)((MAGIC_COOKIE & 0xFF000000) >> 24);
  	pDHCPMSG->OPT[1] = (uint8_t)((MAGIC_COOKIE & 0x00FF0000) >> 16);
  	pDHCPMSG->OPT[2] = (uint8_t)((MAGIC_COOKIE & 0x0000FF00) >> 8);
  	pDHCPMSG->OPT[3] = (uint8_t)(MAGIC_COOKIE & 0x000000FF) >> 0;
  }
  
  /* SEND DHCP DISCOVER */
  void send_DHCP_DISCOVER(void) {
  	uint16_t i;
  	uint8_t ip[4];
  	uint16_t k = 0;
  
  	makeDHCPMSG();
  	DHCP_SIP[0] = 0;
  	DHCP_SIP[1] = 0;
  	DHCP_SIP[2] = 0;
  	DHCP_SIP[3] = 0;
  	DHCP_REAL_SIP[0] = 0;
  	DHCP_REAL_SIP[1] = 0;
  	DHCP_REAL_SIP[2] = 0;
  	DHCP_REAL_SIP[3] = 0;
  
  	k = 4; // because MAGIC_COOKIE already made by makeDHCPMSG()
  
  	// Option Request Param
  	pDHCPMSG->OPT[k++] = dhcpMessageType;
  	pDHCPMSG->OPT[k++] = 0x01;
  	pDHCPMSG->OPT[k++] = DHCP_DISCOVER;
  
  	// Client identifier
  	pDHCPMSG->OPT[k++] = dhcpClientIdentifier;
  	pDHCPMSG->OPT[k++] = 0x07;
  	pDHCPMSG->OPT[k++] = 0x01;
  	pDHCPMSG->OPT[k++] = dhcp_mac_address[0];
  	pDHCPMSG->OPT[k++] = dhcp_mac_address[1];
  	pDHCPMSG->OPT[k++] = dhcp_mac_address[2];
  	pDHCPMSG->OPT[k++] = dhcp_mac_address[3];
  	pDHCPMSG->OPT[k++] = dhcp_mac_address[4];
  	pDHCPMSG->OPT[k++] = dhcp_mac_address[5];
  
  	// host name
  	pDHCPMSG->OPT[k++] = hostName;
  	pDHCPMSG->OPT[k++] = 0; // fill zero length of hostname
  	// Print fAnchor or fCharger as hostname
  	if (DHCP_DEVICE_TYPE == 0) {
  		for (i = 0; DHCP_FANCHOR_HOSTNAME[i] != 0; i++)
  			pDHCPMSG->OPT[k++] = DHCP_FANCHOR_HOSTNAME[i];
  	} else {
  		for (i = 0; DHCP_FCHARGER_HOSTNAME[i] != 0; i++)
  			pDHCPMSG->OPT[k++] = DHCP_FCHARGER_HOSTNAME[i];
  	}
  	pDHCPMSG->OPT[k++] = '_';
  	pDHCPMSG->OPT[k++] = NibbleToHex(dhcp_mac_address[0] >> 4);
  	pDHCPMSG->OPT[k++] = NibbleToHex(dhcp_mac_address[0]);
  	pDHCPMSG->OPT[k++] = '-';
  	pDHCPMSG->OPT[k++] = NibbleToHex(dhcp_mac_address[1] >> 4);
  	pDHCPMSG->OPT[k++] = NibbleToHex(dhcp_mac_address[1]);
  	pDHCPMSG->OPT[k++] = '-';
  	pDHCPMSG->OPT[k++] = NibbleToHex(dhcp_mac_address[2] >> 4);
  	pDHCPMSG->OPT[k++] = NibbleToHex(dhcp_mac_address[2]);
  	pDHCPMSG->OPT[k++] = '-';
  	pDHCPMSG->OPT[k++] = NibbleToHex(dhcp_mac_address[3] >> 4);
  	pDHCPMSG->OPT[k++] = NibbleToHex(dhcp_mac_address[3]);
  	pDHCPMSG->OPT[k++] = '-';
  	pDHCPMSG->OPT[k++] = NibbleToHex(dhcp_mac_address[4] >> 4);
  	pDHCPMSG->OPT[k++] = NibbleToHex(dhcp_mac_address[4]);
  	pDHCPMSG->OPT[k++] = '-';
  	pDHCPMSG->OPT[k++] = NibbleToHex(dhcp_mac_address[5] >> 4);
  	pDHCPMSG->OPT[k++] = NibbleToHex(dhcp_mac_address[5]);
  	pDHCPMSG->OPT[k - (i + 13 + 1)] = i + 13; // length of hostname
  
  	pDHCPMSG->OPT[k++] = dhcpParamRequest;
  	pDHCPMSG->OPT[k++] = 0x02; // length of request
  	pDHCPMSG->OPT[k++] = subnetMask;
  	pDHCPMSG->OPT[k++] = routersOnSubnet;
  	pDHCPMSG->OPT[k++] = endOption;
  
  	for (i = k; i < OPT_SIZE; i++)
  		pDHCPMSG->OPT[i] = 0;
  
  	// send broadcasting packet
  	ip[0] = 255;
  	ip[1] = 255;
  	ip[2] = 255;
  	ip[3] = 255;
  
  #ifdef _DHCP_DEBUG_
  	printf("> Send DHCP_DISCOVER\r\n");
  #endif
  
  	sendto(DHCP_SOCKET, (uint8_t *)pDHCPMSG, RIP_MSG_SIZE, ip, DHCP_SERVER_PORT);
  }
  
  /* SEND DHCP REQUEST */
  void send_DHCP_REQUEST(void) {
  	int i;
  	uint8_t ip[4];
  	uint16_t k = 0;
  
  	makeDHCPMSG();
  
  	if (dhcp_state == STATE_DHCP_LEASED || dhcp_state == STATE_DHCP_REREQUEST) {
  		*((uint8_t *)(&pDHCPMSG->flags)) = ((DHCP_FLAG_BROADCAST & 0xFF00) >> 8);
  		*((uint8_t *)(&pDHCPMSG->flags) + 1) = (DHCP_FLAG_BROADCAST & 0x00FF);
  		pDHCPMSG->ciaddr[0] = DHCP_allocated_ip[0];
  		pDHCPMSG->ciaddr[1] = DHCP_allocated_ip[1];
  		pDHCPMSG->ciaddr[2] = DHCP_allocated_ip[2];
  		pDHCPMSG->ciaddr[3] = DHCP_allocated_ip[3];
  		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;
  	}
  
  	k = 4; // because MAGIC_COOKIE already made by makeDHCPMSG()
  
  	// Option Request Param.
  	pDHCPMSG->OPT[k++] = dhcpMessageType;
  	pDHCPMSG->OPT[k++] = 0x01;
  	pDHCPMSG->OPT[k++] = DHCP_REQUEST;
  
  	pDHCPMSG->OPT[k++] = dhcpClientIdentifier;
  	pDHCPMSG->OPT[k++] = 0x07;
  	pDHCPMSG->OPT[k++] = 0x01;
  	pDHCPMSG->OPT[k++] = dhcp_mac_address[0];
  	pDHCPMSG->OPT[k++] = dhcp_mac_address[1];
  	pDHCPMSG->OPT[k++] = dhcp_mac_address[2];
  	pDHCPMSG->OPT[k++] = dhcp_mac_address[3];
  	pDHCPMSG->OPT[k++] = dhcp_mac_address[4];
  	pDHCPMSG->OPT[k++] = dhcp_mac_address[5];
  
  	if (ip[3] == 255) // if(dchp_state == STATE_DHCP_LEASED || dchp_state == DHCP_REREQUEST_STATE)
  	{
  		pDHCPMSG->OPT[k++] = dhcpRequestedIPaddr;
  		pDHCPMSG->OPT[k++] = 0x04;
  		pDHCPMSG->OPT[k++] = DHCP_allocated_ip[0];
  		pDHCPMSG->OPT[k++] = DHCP_allocated_ip[1];
  		pDHCPMSG->OPT[k++] = DHCP_allocated_ip[2];
  		pDHCPMSG->OPT[k++] = DHCP_allocated_ip[3];
  
  		pDHCPMSG->OPT[k++] = dhcpServerIdentifier;
  		pDHCPMSG->OPT[k++] = 0x04;
  		pDHCPMSG->OPT[k++] = DHCP_SIP[0];
  		pDHCPMSG->OPT[k++] = DHCP_SIP[1];
  		pDHCPMSG->OPT[k++] = DHCP_SIP[2];
  		pDHCPMSG->OPT[k++] = DHCP_SIP[3];
  	}
  
  	// host name
  	pDHCPMSG->OPT[k++] = hostName;
  	pDHCPMSG->OPT[k++] = 0; // length of hostname
  	// Print fAnchor or fCharger as hostname
  	if (DHCP_DEVICE_TYPE == 0) {
  		for (i = 0; DHCP_FANCHOR_HOSTNAME[i] != 0; i++)
  			pDHCPMSG->OPT[k++] = DHCP_FANCHOR_HOSTNAME[i];
  	} else {
  		for (i = 0; DHCP_FCHARGER_HOSTNAME[i] != 0; i++)
  			pDHCPMSG->OPT[k++] = DHCP_FCHARGER_HOSTNAME[i];
  	}
  	pDHCPMSG->OPT[k++] = '_';
  	pDHCPMSG->OPT[k++] = NibbleToHex(dhcp_mac_address[0] >> 4);
  	pDHCPMSG->OPT[k++] = NibbleToHex(dhcp_mac_address[0]);
  	pDHCPMSG->OPT[k++] = NibbleToHex(dhcp_mac_address[1] >> 4);
  	pDHCPMSG->OPT[k++] = NibbleToHex(dhcp_mac_address[1]);
  	pDHCPMSG->OPT[k++] = NibbleToHex(dhcp_mac_address[2] >> 4);
  	pDHCPMSG->OPT[k++] = NibbleToHex(dhcp_mac_address[2]);
  	pDHCPMSG->OPT[k++] = NibbleToHex(dhcp_mac_address[3] >> 4);
  	pDHCPMSG->OPT[k++] = NibbleToHex(dhcp_mac_address[3]);
  	pDHCPMSG->OPT[k++] = NibbleToHex(dhcp_mac_address[4] >> 4);
  	pDHCPMSG->OPT[k++] = NibbleToHex(dhcp_mac_address[4]);
  	pDHCPMSG->OPT[k++] = NibbleToHex(dhcp_mac_address[5] >> 4);
  	pDHCPMSG->OPT[k++] = NibbleToHex(dhcp_mac_address[5]);
  	pDHCPMSG->OPT[k - (i + 13 + 1)] = i + 13; // length of hostname
  
  	pDHCPMSG->OPT[k++] = dhcpParamRequest;
  	pDHCPMSG->OPT[k++] = 0x04;
  	pDHCPMSG->OPT[k++] = subnetMask;
  	pDHCPMSG->OPT[k++] = routersOnSubnet;
  	pDHCPMSG->OPT[k++] = performRouterDiscovery;
  	pDHCPMSG->OPT[k++] = staticRoute;
  	pDHCPMSG->OPT[k++] = endOption;
  
  	for (i = k; i < OPT_SIZE; i++)
  		pDHCPMSG->OPT[i] = 0;
  
  #ifdef _DHCP_DEBUG_
  	printf("> Send DHCP_REQUEST\r\n");
  #endif
  
  	sendto(DHCP_SOCKET, (uint8_t *)pDHCPMSG, RIP_MSG_SIZE, ip, DHCP_SERVER_PORT);
  }
  
  /* SEND DHCP DHCPDECLINE */
  void send_DHCP_DECLINE(void) {
  	int i;
  	uint8_t ip[4];
  	uint16_t k = 0;
  
  	makeDHCPMSG();
  
  	k = 4; // because MAGIC_COOKIE already made by makeDHCPMSG()
  
  	*((uint8_t *)(&pDHCPMSG->flags)) = ((DHCP_FLAG_BROADCAST & 0xFF00) >> 8);
  	*((uint8_t *)(&pDHCPMSG->flags) + 1) = (DHCP_FLAG_BROADCAST & 0x00FF);
  
  	// Option Request Param.
  	pDHCPMSG->OPT[k++] = dhcpMessageType;
  	pDHCPMSG->OPT[k++] = 0x01;
  	pDHCPMSG->OPT[k++] = DHCP_DECLINE;
  
  	pDHCPMSG->OPT[k++] = dhcpClientIdentifier;
  	pDHCPMSG->OPT[k++] = 0x07;
  	pDHCPMSG->OPT[k++] = 0x01;
  	pDHCPMSG->OPT[k++] = dhcp_mac_address[0];
  	pDHCPMSG->OPT[k++] = dhcp_mac_address[1];
  	pDHCPMSG->OPT[k++] = dhcp_mac_address[2];
  	pDHCPMSG->OPT[k++] = dhcp_mac_address[3];
  	pDHCPMSG->OPT[k++] = dhcp_mac_address[4];
  	pDHCPMSG->OPT[k++] = dhcp_mac_address[5];
  
  	pDHCPMSG->OPT[k++] = dhcpRequestedIPaddr;
  	pDHCPMSG->OPT[k++] = 0x04;
  	pDHCPMSG->OPT[k++] = DHCP_allocated_ip[0];
  	pDHCPMSG->OPT[k++] = DHCP_allocated_ip[1];
  	pDHCPMSG->OPT[k++] = DHCP_allocated_ip[2];
  	pDHCPMSG->OPT[k++] = DHCP_allocated_ip[3];
  
  	pDHCPMSG->OPT[k++] = dhcpServerIdentifier;
  	pDHCPMSG->OPT[k++] = 0x04;
  	pDHCPMSG->OPT[k++] = DHCP_SIP[0];
  	pDHCPMSG->OPT[k++] = DHCP_SIP[1];
  	pDHCPMSG->OPT[k++] = DHCP_SIP[2];
  	pDHCPMSG->OPT[k++] = DHCP_SIP[3];
  
  	pDHCPMSG->OPT[k++] = endOption;
  
  	for (i = k; i < OPT_SIZE; i++)
  		pDHCPMSG->OPT[i] = 0;
  
  	// send broadcasting packet
  	ip[0] = 0xFF;
  	ip[1] = 0xFF;
  	ip[2] = 0xFF;
  	ip[3] = 0xFF;
  
  #ifdef _DHCP_DEBUG_
  	printf("\r\n> Send DHCP_DECLINE\r\n");
  #endif
  
  	sendto(DHCP_SOCKET, (uint8_t *)pDHCPMSG, RIP_MSG_SIZE, ip, DHCP_SERVER_PORT);
  }
  
  /* PARSE REPLY pDHCPMSG */
  int8_t parseDHCPMSG(void) {
  	uint8_t svr_addr[6];
  	uint16_t svr_port;
  	uint16_t len;
  
  	uint8_t *p;
  	uint8_t *e;
  	uint8_t type = 0;
  	uint8_t opt_len;
  
  	if ((len = getSn_RX_RSR(DHCP_SOCKET)) > 0) {
  		len = recvfrom(DHCP_SOCKET, (uint8_t *)pDHCPMSG, len, svr_addr, &svr_port);
  #ifdef _DHCP_DEBUG_
  		printf("DHCP message: %d.%d.%d.%d(%d) %d received. \r\n", svr_addr[0], svr_addr[1], svr_addr[2], svr_addr[3], svr_port, len);
  #endif
  	} else {
  		return 0;
  	}
  	if (svr_port == DHCP_SERVER_PORT) {
  		// compare mac address
  		if ((pDHCPMSG->chaddr[0] != dhcp_mac_address[0]) || (pDHCPMSG->chaddr[1] != dhcp_mac_address[1]) ||
  				(pDHCPMSG->chaddr[2] != dhcp_mac_address[2]) || (pDHCPMSG->chaddr[3] != dhcp_mac_address[3]) ||
  				(pDHCPMSG->chaddr[4] != dhcp_mac_address[4]) || (pDHCPMSG->chaddr[5] != dhcp_mac_address[5])) {
  #ifdef _DHCP_DEBUG_
  			printf("No My DHCP Message. This message is ignored.\r\n");
  #endif
  			return 0;
  		}
  		// compare DHCP server ip address
  		if ((DHCP_SIP[0] != 0) || (DHCP_SIP[1] != 0) || (DHCP_SIP[2] != 0) || (DHCP_SIP[3] != 0)) {
  			if (((svr_addr[0] != DHCP_SIP[0]) || (svr_addr[1] != DHCP_SIP[1]) || (svr_addr[2] != DHCP_SIP[2]) || (svr_addr[3] != DHCP_SIP[3])) &&
  					((svr_addr[0] != DHCP_REAL_SIP[0]) || (svr_addr[1] != DHCP_REAL_SIP[1]) || (svr_addr[2] != DHCP_REAL_SIP[2]) || (svr_addr[3] != DHCP_REAL_SIP[3]))) {
  #ifdef _DHCP_DEBUG_
  				printf("Another DHCP sever send a response message. This is ignored.\r\n");
  #endif
  				return 0;
  			}
  		}
  		p = (uint8_t *)(&pDHCPMSG->op);
  		p = p + 240; // 240 = sizeof(RIP_MSG) + MAGIC_COOKIE size in RIP_MSG.opt - sizeof(RIP_MSG.opt)
  		e = p + (len - 240);
  
  		while (p < e) {
  			switch (*p) {
  			case endOption:
  				p = e; // for break while(p < e)
  				break;
  			case padOption:
  				p++;
  				break;
  			case dhcpMessageType:
  				p++;
  				p++;
  				type = *p++;
  				break;
  			case subnetMask:
  				p++;
  				p++;
  				DHCP_allocated_sn[0] = *p++;
  				DHCP_allocated_sn[1] = *p++;
  				DHCP_allocated_sn[2] = *p++;
  				DHCP_allocated_sn[3] = *p++;
  				break;
  			case routersOnSubnet:
  				p++;
  				opt_len = *p++;
  				DHCP_allocated_gw[0] = *p++;
  				DHCP_allocated_gw[1] = *p++;
  				DHCP_allocated_gw[2] = *p++;
  				DHCP_allocated_gw[3] = *p++;
  				p = p + (opt_len - 4);
  				break;
  			case dhcpIPaddrLeaseTime:
  				p++;
  				opt_len = *p++;
  				dhcp_lease_time = *p++;
  				dhcp_lease_time = (dhcp_lease_time << 8) + *p++;
  				dhcp_lease_time = (dhcp_lease_time << 8) + *p++;
  				dhcp_lease_time = (dhcp_lease_time << 8) + *p++;
  #ifdef _DHCP_DEBUG_
  				dhcp_lease_time = 20;
  #endif
  				break;
  			case dhcpServerIdentifier:
  				p++;
  				opt_len = *p++;
  				DHCP_SIP[0] = *p++;
  				DHCP_SIP[1] = *p++;
  				DHCP_SIP[2] = *p++;
  				DHCP_SIP[3] = *p++;
  				DHCP_REAL_SIP[0] = svr_addr[0];
  				DHCP_REAL_SIP[1] = svr_addr[1];
  				DHCP_REAL_SIP[2] = svr_addr[2];
  				DHCP_REAL_SIP[3] = svr_addr[3];
  				break;
  			default:
  				p++;
  				opt_len = *p++;
  				p += opt_len;
  				break;
  			} // switch
  		}		// while
  	}			// if
  	return type;
  }
  
  uint8_t DHCP_run(void) {
  	uint8_t type;
  	uint8_t ret;
  
  	if (dhcp_state == STATE_DHCP_STOP) return DHCP_STOPPED;
  
  	if (getSn_SR(DHCP_SOCKET) != SOCK_UDP)
  		socket(DHCP_SOCKET, Sn_MR_UDP, DHCP_CLIENT_PORT, 0x00);
  
  	ret = DHCP_RUNNING;
  	type = parseDHCPMSG();
  
  	switch (dhcp_state) {
  	case STATE_DHCP_INIT:
  		DHCP_allocated_ip[0] = 0;
  		DHCP_allocated_ip[1] = 0;
  		DHCP_allocated_ip[2] = 0;
  		DHCP_allocated_ip[3] = 0;
  		send_DHCP_DISCOVER();
  		dhcp_state = STATE_DHCP_DISCOVER;
  		break;
  	case STATE_DHCP_DISCOVER:
  		if (type == DHCP_OFFER) {
  #ifdef _DHCP_DEBUG_
  			printf("> Receive DHCP_OFFER\r\n");
  #endif
  			DHCP_allocated_ip[0] = pDHCPMSG->yiaddr[0];
  			DHCP_allocated_ip[1] = pDHCPMSG->yiaddr[1];
  			DHCP_allocated_ip[2] = pDHCPMSG->yiaddr[2];
  			DHCP_allocated_ip[3] = pDHCPMSG->yiaddr[3];
  
  			send_DHCP_REQUEST();
  			dhcp_state = STATE_DHCP_REQUEST;
  		} else
  			ret = check_DHCP_timeout();
  		break;
  
  	case STATE_DHCP_REQUEST:
  		if (type == DHCP_ACK) {
  #ifdef _DHCP_DEBUG_
  			printf("> Receive DHCP_ACK\r\n");
  #endif
  			if (check_DHCP_leasedIP()) {
  				// Network info assignment from DHCP
  				dhcp_ip_assign();
  				reset_DHCP_timeout();
  				dhcp_state = STATE_DHCP_LEASED;
  			} else {
  				// IP address conflict occurred
  				reset_DHCP_timeout();
  				dhcp_ip_conflict();
  				dhcp_state = STATE_DHCP_INIT;
  			}
  		} else if (type == DHCP_NAK) {
  #ifdef _DHCP_DEBUG_
  			printf("> Receive DHCP_NACK\r\n");
  #endif
  			reset_DHCP_timeout();
  			dhcp_state = STATE_DHCP_DISCOVER;
  		} else
  			ret = check_DHCP_timeout();
  		break;
  
  	case STATE_DHCP_LEASED:
  		ret = DHCP_IP_LEASED;
  		if ((dhcp_lease_time != INFINITE_LEASETIME) && ((dhcp_lease_time / 2) < dhcp_tick_1s)) {
  #ifdef _DHCP_DEBUG_
  			printf("> Maintains the IP address \r\n");
  #endif
  			type = 0;
  			OLD_allocated_ip[0] = DHCP_allocated_ip[0];
  			OLD_allocated_ip[1] = DHCP_allocated_ip[1];
  			OLD_allocated_ip[2] = DHCP_allocated_ip[2];
  			OLD_allocated_ip[3] = DHCP_allocated_ip[3];
  
  			dhcp_xid_transaction_id++;
  
  			send_DHCP_REQUEST();
  			reset_DHCP_timeout();
  			dhcp_state = STATE_DHCP_REREQUEST;
  		}
  		break;
  
  	case STATE_DHCP_REREQUEST:
  		ret = DHCP_IP_LEASED;
  		if (type == DHCP_ACK) {
  			dhcp_retry_count = 0;
  			if (OLD_allocated_ip[0] != DHCP_allocated_ip[0] ||
  					OLD_allocated_ip[1] != DHCP_allocated_ip[1] ||
  					OLD_allocated_ip[2] != DHCP_allocated_ip[2] ||
  					OLD_allocated_ip[3] != DHCP_allocated_ip[3]) {
  				ret = DHCP_IP_CHANGED;
  				dhcp_ip_update();
  #ifdef _DHCP_DEBUG_
  				printf(">IP changed.\r\n");
  #endif
  			}
  #ifdef _DHCP_DEBUG_
  			else
  				printf(">IP is continued.\r\n");
  #endif
  			reset_DHCP_timeout();
  			dhcp_state = STATE_DHCP_LEASED;
  		} else if (type == DHCP_NAK) {
  #ifdef _DHCP_DEBUG_
  			printf("> Receive DHCP_NACK, Failed to maintain ip\r\n");
  #endif
  			reset_DHCP_timeout();
  			dhcp_state = STATE_DHCP_DISCOVER;
  		} else
  			ret = check_DHCP_timeout();
  		break;
  	default:
  		break;
  	}
  
  	return ret;
  }
  
  void DHCP_stop(void) {
  	close(DHCP_SOCKET);
  	dhcp_state = STATE_DHCP_STOP;
  }
  
  uint8_t check_DHCP_timeout(void) {
  	uint8_t ret = DHCP_RUNNING;
  
  	if (dhcp_retry_count < MAX_DHCP_RETRY) {
  		if (dhcp_tick_next < dhcp_tick_1s) {
  			switch (dhcp_state) {
  			case STATE_DHCP_DISCOVER:
  				// printf("<<timeout>> state: STATE_DHCP_DISCOVER\r\n");
  				send_DHCP_DISCOVER();
  				break;
  			case STATE_DHCP_REQUEST:
  				// printf("<<timeout>> state: STATE_DHCP_REQUEST\r\n");
  				send_DHCP_REQUEST();
  				break;
  			case STATE_DHCP_REREQUEST:
  				// printf("<<timeout>> state: STATE_DHCP_REREQUEST\r\n");
  				send_DHCP_REQUEST();
  				break;
  			default:
  				break;
  			}
  			dhcp_tick_1s = 0;
  			dhcp_tick_next = dhcp_tick_1s + DHCP_WAIT_TIME;
  			dhcp_retry_count++;
  		}
  	} else { // timeout occurred
  
  		switch (dhcp_state) {
  		case STATE_DHCP_DISCOVER:
  			dhcp_state = STATE_DHCP_INIT;
  			ret = DHCP_FAILED;
  			break;
  		case STATE_DHCP_REQUEST:
  		case STATE_DHCP_REREQUEST:
  			send_DHCP_DISCOVER();
  			dhcp_state = STATE_DHCP_DISCOVER;
  			break;
  		default:
  			break;
  		}
  		reset_DHCP_timeout();
  	}
  	return ret;
  }
  
  int8_t check_DHCP_leasedIP(void) {
  	uint8_t tmp;
  	int32_t ret;
  
  	// WIZchip RCR value changed for ARP Timeout count control
  	tmp = getRCR();
  	setRCR(0x03);
  
  	// IP conflict detection: ARP request - ARP reply
  	// Broadcasting ARP Request for check the IP conflict using UDP sendto() function
  	ret = sendto(DHCP_SOCKET, (uint8_t *)"CHECK_IP_CONFLICT", 17, DHCP_allocated_ip, 5000);
  
  	// RCR value restore
  	setRCR(tmp);
  
  	if (ret == SOCKERR_TIMEOUT) {
  		// UDP send Timeout occurred: allocated IP address is unique, DHCP Success
  #ifdef _DHCP_DEBUG_
  		printf("\r\n> Check leased IP - OK\r\n");
  #endif
  		return 1;
  	} else {
  		// Received ARP reply or etc: IP address conflict occur, DHCP Failed
  		send_DHCP_DECLINE();
  
  		ret = dhcp_tick_1s;
  		while ((dhcp_tick_1s - ret) < 2)
  			; // wait for 1s over; wait to complete to send DECLINE message;
  
  		return 0;
  	}
  }
  
  void DHCP_init(uint8_t s, uint8_t t, uint8_t *buf) {
  	uint8_t zeroip[4] = {0, 0, 0, 0};
  	getSHAR(dhcp_mac_address);
  	if ((dhcp_mac_address[0] | dhcp_mac_address[1] | dhcp_mac_address[2] | dhcp_mac_address[3] | dhcp_mac_address[4] | dhcp_mac_address[5]) == 0x00) {
  		printf("DHCP: MAC address was not defined before DHCP initialisation!");
  	}
  
  	DHCP_SOCKET = s; // SOCK_DHCP
  	DHCP_DEVICE_TYPE = t; // DHCP_DEVICE_TYPE
  	pDHCPMSG = (RIP_MSG *)buf;
  
  	// Make initial dhcp_xid_transaction_id unique based on mac address
  	dhcp_xid_transaction_id += dhcp_mac_address[3];
  	dhcp_xid_transaction_id += dhcp_mac_address[4];
  	dhcp_xid_transaction_id += dhcp_mac_address[5];
  	dhcp_xid_transaction_id += (dhcp_mac_address[3] ^ dhcp_mac_address[4] ^ dhcp_mac_address[5]);
  	
  	// WIZchip Netinfo Clear
  	setSIPR(zeroip);
  	setGAR(zeroip);
  
  	reset_DHCP_timeout();
  	dhcp_state = STATE_DHCP_INIT;
  }
  
  /* Reset the DHCP timeout count and retry count. */
  void reset_DHCP_timeout(void) {
  	dhcp_tick_1s = 0;
  	dhcp_tick_next = DHCP_WAIT_TIME;
  	dhcp_retry_count = 0;
  }
  
  void dhcpTimeHandler(void) {
  	dhcp_tick_1s++;
  }
  
  void getIPfromDHCP(uint8_t *ip) {
  	ip[0] = DHCP_allocated_ip[0];
  	ip[1] = DHCP_allocated_ip[1];
  	ip[2] = DHCP_allocated_ip[2];
  	ip[3] = DHCP_allocated_ip[3];
  }
  
  void getGWfromDHCP(uint8_t *ip) {
  	ip[0] = DHCP_allocated_gw[0];
  	ip[1] = DHCP_allocated_gw[1];
  	ip[2] = DHCP_allocated_gw[2];
  	ip[3] = DHCP_allocated_gw[3];
  }
  
  void getSNfromDHCP(uint8_t *ip) {
  	ip[0] = DHCP_allocated_sn[0];
  	ip[1] = DHCP_allocated_sn[1];
  	ip[2] = DHCP_allocated_sn[2];
  	ip[3] = DHCP_allocated_sn[3];
  }
  
  uint32_t getDHCPLeasetime(void) {
  	return dhcp_lease_time;
  }
  
  char NibbleToHex(uint8_t nibble) {
  	nibble &= 0x0F;
  	if (nibble <= 9) {
  		return nibble + '0';
  	} else {
  		return nibble + ('A' - 0x0A);
  	}
  }