+/*\r
+ LUFA Library\r
+ Copyright (C) Dean Camera, 2010.\r
+ \r
+ dean [at] fourwalledcubicle [dot] com\r
+ www.fourwalledcubicle.com\r
+*/\r
+\r
+/*\r
+ Copyright 2010 Dean Camera (dean [at] fourwalledcubicle [dot] com)\r
+\r
+ Permission to use, copy, modify, distribute, and sell this \r
+ software and its documentation for any purpose is hereby granted\r
+ without fee, provided that the above copyright notice appear in \r
+ all copies and that both that the copyright notice and this\r
+ permission notice and warranty disclaimer appear in supporting \r
+ documentation, and that the name of the author not be used in \r
+ advertising or publicity pertaining to distribution of the \r
+ software without specific, written prior permission.\r
+\r
+ The author disclaim all warranties with regard to this\r
+ software, including all implied warranties of merchantability\r
+ and fitness. In no event shall the author be liable for any\r
+ special, indirect or consequential damages or any damages\r
+ whatsoever resulting from loss of use, data or profits, whether\r
+ in an action of contract, negligence or other tortious action,\r
+ arising out of or in connection with the use or performance of\r
+ this software.\r
+*/\r
+\r
+/** \file\r
+ *\r
+ * DHCP Client Application. When connected to the uIP stack, this will retrieve IP configuration settings from the\r
+ * DHCP server on the network.\r
+ */\r
+ \r
+#include "DHCPClientApp.h"\r
+\r
+#if defined(ENABLE_DHCP) || defined(__DOXYGEN__)\r
+/** Timer for managing the timeout period for a DHCP server to respond */\r
+struct timer DHCPTimer;\r
+\r
+/** Initialization function for the DHCP client. */\r
+void DHCPClientApp_Init(void)\r
+{\r
+ uip_udp_appstate_t* const AppState = &uip_udp_conn->appstate;\r
+ \r
+ /* Create a new UDP connection to the DHCP server port for the DHCP solicitation */\r
+ uip_ipaddr_t DHCPServerIPAddress;\r
+ uip_ipaddr(&DHCPServerIPAddress, 255, 255, 255, 255);\r
+ AppState->DHCPClient.Connection = uip_udp_new(&DHCPServerIPAddress, HTONS(DHCPC_SERVER_PORT));\r
+ \r
+ /* If the connection was sucessfully created, bind it to the local DHCP client port */\r
+ if(AppState->DHCPClient.Connection != NULL)\r
+ {\r
+ uip_udp_bind(AppState->DHCPClient.Connection, HTONS(DHCPC_CLIENT_PORT));\r
+ AppState->DHCPClient.CurrentState = DHCP_STATE_SendDiscover;\r
+ }\r
+\r
+ /* Set timeout period to half a second for a DHCP server to respond */\r
+ timer_set(&DHCPTimer, CLOCK_SECOND / 2);\r
+}\r
+ \r
+/** uIP stack application callback for the DHCP client. This function must be called each time the TCP/IP stack \r
+ * needs a UDP packet to be processed.\r
+ */\r
+void DHCPClientApp_Callback(void)\r
+{\r
+ uip_udp_appstate_t* const AppState = &uip_udp_conn->appstate;\r
+ DHCP_Header_t* const AppData = (DHCP_Header_t*)uip_appdata;\r
+ uint16_t AppDataSize = 0;\r
+ \r
+ switch (AppState->DHCPClient.CurrentState)\r
+ {\r
+ case DHCP_STATE_SendDiscover:\r
+ /* Clear all DHCP settings, reset client IP address */\r
+ memset(&AppState->DHCPClient.DHCPOffer_Data, 0x00, sizeof(AppState->DHCPClient.DHCPOffer_Data));\r
+ uip_sethostaddr((uip_ipaddr_t*)&AppState->DHCPClient.DHCPOffer_Data.AllocatedIP);\r
+\r
+ /* Fill out the DHCP response header */\r
+ AppDataSize += DHCPClientApp_FillDHCPHeader(AppData, DHCP_DISCOVER, AppState);\r
+ \r
+ /* Add the required DHCP options list to the packet */\r
+ uint8_t RequiredOptionList[] = {DHCP_OPTION_SUBNET_MASK, DHCP_OPTION_ROUTER, DHCP_OPTION_DNS_SERVER};\r
+ AppDataSize += DHCPClientApp_SetOption(AppData->Options, DHCP_OPTION_REQ_LIST, sizeof(RequiredOptionList),\r
+ RequiredOptionList); \r
+ \r
+ /* Send the DHCP DISCOVER packet */\r
+ uip_udp_send(AppDataSize);\r
+\r
+ /* Reset the timeout timer, progress to next state */\r
+ timer_reset(&DHCPTimer);\r
+ AppState->DHCPClient.CurrentState = DHCP_STATE_WaitForOffer; \r
+ \r
+ break;\r
+ case DHCP_STATE_WaitForOffer:\r
+ if (!(uip_newdata()))\r
+ {\r
+ /* Check if the DHCP timeout period has expired while waiting for a response */\r
+ if (timer_expired(&DHCPTimer))\r
+ AppState->DHCPClient.CurrentState = DHCP_STATE_SendDiscover;\r
+ \r
+ break;\r
+ }\r
+ \r
+ uint8_t OfferResponse_MessageType;\r
+ if ((AppData->TransactionID == DHCP_TRANSACTION_ID) &&\r
+ DHCPClientApp_GetOption(AppData->Options, DHCP_OPTION_MSG_TYPE, &OfferResponse_MessageType) &&\r
+ (OfferResponse_MessageType == DHCP_OFFER))\r
+ {\r
+ /* Received a DHCP offer for an IP address, copy over values for later request */\r
+ memcpy(&AppState->DHCPClient.DHCPOffer_Data.AllocatedIP, &AppData->YourIP, sizeof(uip_ipaddr_t));\r
+ DHCPClientApp_GetOption(AppData->Options, DHCP_OPTION_SUBNET_MASK, &AppState->DHCPClient.DHCPOffer_Data.Netmask);\r
+ DHCPClientApp_GetOption(AppData->Options, DHCP_OPTION_ROUTER, &AppState->DHCPClient.DHCPOffer_Data.GatewayIP);\r
+ DHCPClientApp_GetOption(AppData->Options, DHCP_OPTION_SERVER_ID, &AppState->DHCPClient.DHCPOffer_Data.ServerIP);\r
+ \r
+ timer_reset(&DHCPTimer);\r
+ AppState->DHCPClient.CurrentState = DHCP_STATE_SendRequest;\r
+ }\r
+\r
+ break;\r
+ case DHCP_STATE_SendRequest:\r
+ /* Fill out the DHCP response header */\r
+ AppDataSize += DHCPClientApp_FillDHCPHeader(AppData, DHCP_REQUEST, AppState);\r
+\r
+ /* Add the DHCP REQUESTED IP ADDRESS option to the packet */\r
+ AppDataSize += DHCPClientApp_SetOption(AppData->Options, DHCP_OPTION_REQ_IPADDR, sizeof(uip_ipaddr_t),\r
+ &AppState->DHCPClient.DHCPOffer_Data.AllocatedIP);\r
+\r
+ /* Add the DHCP SERVER IP ADDRESS option to the packet */\r
+ AppDataSize += DHCPClientApp_SetOption(AppData->Options, DHCP_OPTION_SERVER_ID, sizeof(uip_ipaddr_t),\r
+ &AppState->DHCPClient.DHCPOffer_Data.ServerIP);\r
+\r
+ /* Send the DHCP REQUEST packet */\r
+ uip_udp_send(AppDataSize);\r
+ \r
+ /* Reset the timeout timer, progress to next state */\r
+ timer_reset(&DHCPTimer);\r
+ AppState->DHCPClient.CurrentState = DHCP_STATE_WaitForACK;\r
+\r
+ break;\r
+ case DHCP_STATE_WaitForACK:\r
+ if (!(uip_newdata()))\r
+ {\r
+ /* Check if the DHCP timeout period has expired while waiting for a response */\r
+ if (timer_expired(&DHCPTimer))\r
+ AppState->DHCPClient.CurrentState = DHCP_STATE_SendDiscover;\r
+ \r
+ break;\r
+ }\r
+ \r
+ uint8_t RequestResponse_MessageType;\r
+ if ((AppData->TransactionID == DHCP_TRANSACTION_ID) &&\r
+ DHCPClientApp_GetOption(AppData->Options, DHCP_OPTION_MSG_TYPE, &RequestResponse_MessageType) &&\r
+ (RequestResponse_MessageType == DHCP_ACK))\r
+ {\r
+ /* Set the new network parameters from the DHCP server */\r
+ uip_sethostaddr((uip_ipaddr_t*)&AppState->DHCPClient.DHCPOffer_Data.AllocatedIP);\r
+ uip_setnetmask((uip_ipaddr_t*)&AppState->DHCPClient.DHCPOffer_Data.Netmask);\r
+ uip_setdraddr((uip_ipaddr_t*)&AppState->DHCPClient.DHCPOffer_Data.GatewayIP);\r
+ \r
+ AppState->DHCPClient.CurrentState = DHCP_STATE_AddressLeased;\r
+ }\r
+ \r
+ break;\r
+ }\r
+}\r
+\r
+/** Fills the DHCP packet response with the appropriate BOOTP header for DHCP. This fills out all the required\r
+ * fields, leaving only the additional DHCP options to be added to the packet before it is sent to the DHCP server.\r
+ *\r
+ * \param[out] DHCPHeader Location in the packet buffer where the BOOTP header should be written to\r
+ * \param[in] DHCPMessageType DHCP Message type, such as DHCP_DISCOVER\r
+ * \param[in] AppState Application state of the current UDP connection\r
+ *\r
+ * \return Size in bytes of the created DHCP packet\r
+ */\r
+uint16_t DHCPClientApp_FillDHCPHeader(DHCP_Header_t* DHCPHeader, uint8_t DHCPMessageType, uip_udp_appstate_t* AppState)\r
+{\r
+ /* Erase existing packet data so that we start will all 0x00 DHCP header data */\r
+ memset(DHCPHeader, 0, sizeof(DHCP_Header_t));\r
+\r
+ /* Fill out the DHCP packet header */\r
+ DHCPHeader->Operation = DHCP_OP_BOOTREQUEST;\r
+ DHCPHeader->HardwareType = DHCP_HTYPE_ETHERNET;\r
+ DHCPHeader->HardwareAddressLength = sizeof(MACAddress);\r
+ DHCPHeader->Hops = 0;\r
+ DHCPHeader->TransactionID = DHCP_TRANSACTION_ID;\r
+ DHCPHeader->ElapsedSeconds = 0;\r
+ DHCPHeader->Flags = HTONS(BOOTP_BROADCAST);\r
+ memcpy(&DHCPHeader->ClientIP, &uip_hostaddr, sizeof(uip_ipaddr_t));\r
+ memcpy(&DHCPHeader->YourIP, &AppState->DHCPClient.DHCPOffer_Data.AllocatedIP, sizeof(uip_ipaddr_t));\r
+ memcpy(&DHCPHeader->NextServerIP, &AppState->DHCPClient.DHCPOffer_Data.ServerIP, sizeof(uip_ipaddr_t));\r
+ memcpy(&DHCPHeader->ClientHardwareAddress, &MACAddress, sizeof(struct uip_eth_addr));\r
+ DHCPHeader->Cookie = DHCP_MAGIC_COOKIE;\r
+ \r
+ /* Add a DHCP type and terminator options to the start of the DHCP options field */\r
+ DHCPHeader->Options[0] = DHCP_OPTION_MSG_TYPE;\r
+ DHCPHeader->Options[1] = 1;\r
+ DHCPHeader->Options[2] = DHCPMessageType;\r
+ DHCPHeader->Options[3] = DHCP_OPTION_END;\r
+ \r
+ /* Calculate the total number of bytes added to the outgoing packet */\r
+ return (sizeof(DHCP_Header_t) + 4);\r
+}\r
+\r
+/** Sets the given DHCP option in the DHCP packet's option list. This automatically moves the\r
+ * end of options terminator past the new option in the options list.\r
+ *\r
+ * \param[in,out] DHCPOptionList Pointer to the start of the DHCP packet's options list\r
+ * \param[in] Option DHCP option to add to the list\r
+ * \param[in] DataLen Size in bytes of the option data to add\r
+ * \param[in] OptionData Buffer where the option's data is to be sourced from\r
+ *\r
+ * \return Number of bytes added to the DHCP packet\r
+ */\r
+uint8_t DHCPClientApp_SetOption(uint8_t* DHCPOptionList, uint8_t Option, uint8_t DataLen, void* OptionData)\r
+{\r
+ /* Skip through the DHCP options list until the terminator option is found */\r
+ while (*DHCPOptionList != DHCP_OPTION_END)\r
+ DHCPOptionList += (DHCPOptionList[1] + 2);\r
+\r
+ /* Overwrite the existing terminator with the new option, add a new terminator at the end of the list */\r
+ DHCPOptionList[0] = Option;\r
+ DHCPOptionList[1] = DataLen;\r
+ memcpy(&DHCPOptionList[2], OptionData, DataLen);\r
+ DHCPOptionList[2 + DataLen] = DHCP_OPTION_END;\r
+ \r
+ /* Calculate the total number of bytes added to the outgoing packet */\r
+ return (2 + DataLen);\r
+}\r
+\r
+/** Retrieves the given option's data (if present) from the DHCP packet's options list.\r
+ *\r
+ * \param[in,out] DHCPOptionList Pointer to the start of the DHCP packet's options list\r
+ * \param[in] Option DHCP option to retrieve to the list\r
+ * \param[out] Destination Buffer where the option's data is to be written to if found\r
+ *\r
+ * \return Boolean true if the option was found in the DHCP packet's options list, false otherwise\r
+ */\r
+bool DHCPClientApp_GetOption(uint8_t* DHCPOptionList, uint8_t Option, void* Destination)\r
+{\r
+ /* Look through the incomming DHCP packet's options list for the requested option */\r
+ while (*DHCPOptionList != DHCP_OPTION_END)\r
+ {\r
+ /* Check if the current DHCP option in the packet is the one requested */\r
+ if (DHCPOptionList[0] == Option)\r
+ {\r
+ /* Copy request option's data to the destination buffer */\r
+ memcpy(Destination, &DHCPOptionList[2], DHCPOptionList[1]);\r
+ \r
+ /* Indicate that the requested option data was sucessfully retrieved */\r
+ return true;\r
+ }\r
+ \r
+ /* Skip to next DHCP option in the options list */\r
+ DHCPOptionList += (DHCPOptionList[1] + 2);\r
+ }\r
+ \r
+ /* Requested option not found in the incomming packet's DHCP options list */\r
+ return false;\r
+}\r
+#endif\r