diff --git a/inc/EtherShield.h b/Inc/EtherShield.h similarity index 84% rename from inc/EtherShield.h rename to Inc/EtherShield.h index d4701e8..3b96e3c 100644 --- a/inc/EtherShield.h +++ b/Inc/EtherShield.h @@ -1,36 +1,26 @@ -/* - EHTERSHIELD_H library for Arduino etherShield - Copyright (c) 2008 Xing Yu. All right reserved. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ +/** + * @file EtherShield.h + * @brief Header file containing definitions and macros for interfacing with the ENC28J60 Ethernet controller. + * + * This file includes control register definitions, chip enable/disable macros, and configurations for delays + * and chip select (CS) handling. + * + * @note For more information, refer to the `license.md` file located at the root of the project. + */ #ifndef ETHERSHIELD_H #define ETHERSHIELD_H -#include "stm32includes.h" - -#define bool _Bool -#define TRUE 1 -#define FALSE 0 - -#include +#include "main.h" +#include "defines.h" #include "enc28j60.h" -#include "ip_arp_udp_tcp.h" #include "net.h" +void ES_FullConnection(); +void ES_RenewCo(); +void ES_ProcessWebPacket(); +void ES_ProcessWebPacketFilter(); +void ES_init_network(); void ES_enc28j60SpiInit( SPI_HandleTypeDef *hspi ); void ES_enc28j60Init( uint8_t* macaddr); void ES_enc28j60clkout(uint8_t clk); @@ -76,7 +66,6 @@ uint16_t ES_packetloop_icmp_tcp(uint8_t *buf,uint16_t plen); // functions to fill the web pages with data: //uint16_t ES_fill_tcp_data_p(uint8_t *buf,uint16_t pos, const prog_char *progmem_s); uint16_t ES_fill_tcp_data(uint8_t *buf,uint16_t pos, const char *s); -uint16_t ES_fill_tcp_data_len(uint8_t *buf,uint16_t pos, const char *s, uint16_t len ); // send data from the web server to the client: void ES_www_server_reply(uint8_t *buf,uint16_t dlen); @@ -103,7 +92,6 @@ uint8_t *ES_dnslkup_getip( void ); void ES_dnslkup_set_dnsip(uint8_t *dnsipaddr); void ES_dnslkup_request(uint8_t *buf, uint8_t *hoststr ); uint8_t ES_udp_client_check_for_dns_answer(uint8_t *buf,uint16_t plen); -uint8_t resolveHostname(uint8_t *buf, uint16_t buffer_size, uint8_t *hostname ); #endif #ifdef DHCP_client @@ -113,7 +101,6 @@ void ES_dhcp_start(uint8_t *buf, uint8_t *macaddrin, uint8_t *ipaddrin, uint8_t *dnssvrin ); uint8_t ES_check_for_dhcp_answer(uint8_t *buf,uint16_t plen); -uint8_t allocateIPAddress(uint8_t *buf, uint16_t buffer_size, uint8_t *mymac, uint16_t myport, uint8_t *myip, uint8_t *mynetmask, uint8_t *gwip, uint8_t *dnsip, uint8_t *dhcpsvrip ); #endif #define HTTP_HEADER_START ((uint16_t)TCP_SRC_PORT_H_P+(buf[TCP_HEADER_LEN_P]>>4)*4) diff --git a/Inc/LogManager.h b/Inc/LogManager.h new file mode 100644 index 0000000..297dcc1 --- /dev/null +++ b/Inc/LogManager.h @@ -0,0 +1,31 @@ +/* + * @file logmanager.c + * @brief Source file + * + * This header file defines the interface for the Log Manager module, which handles logging + * operations for different types of logs such as UDP, Web, and UART. It includes an enumeration + * for log types, function prototypes for logging messages, initializing the log manager, and + * adding logs to the system. + */ + +#ifndef LOGMANAGER_H +#define LOGMANAGER_H + +#include "main.h" + +// Enumeration for log types +typedef enum { + LOG_UDP, + LOG_WEB, + LOG_UART + // Add more log types as needed +} LogType; + +// Function prototypes +void ethershieldDebug(char *message); +void udpLog2(char* alerte, char* text); +void add_log(const char *log); +void logMessage(LogType type, const char *message); +void initLogManager(LogType type); + +#endif /* LOGMANAGER_H */ diff --git a/Inc/RTC.h b/Inc/RTC.h new file mode 100644 index 0000000..aeca4a7 --- /dev/null +++ b/Inc/RTC.h @@ -0,0 +1,28 @@ +/** + * @file RTC.h + * @brief Real-Time Clock (RTC) interaction functions for STM32 microcontroller. + * + * This header file provides the declaration of the function used to obtain the current + * date and time from the RTC of the STM32 microcontroller. The function formats the + * date and time into a human-readable string for logging purposes. + */ + +#ifndef INC_RTC_H_ +#define INC_RTC_H_ + +#include "main.h" +//#include "defines.h" + + +typedef struct { + uint8_t day; + uint8_t month; + uint16_t year; + uint8_t hours; + uint8_t minutes; + uint8_t seconds; +} DateTime; + +DateTime getRTCDateTime(); + +#endif /* INC_RTC_H_ */ diff --git a/Inc/defines.h b/Inc/defines.h new file mode 100644 index 0000000..32d444c --- /dev/null +++ b/Inc/defines.h @@ -0,0 +1,89 @@ +/** + * @file defines.h + * @brief Header file containing global definitions and macros for configuring the STM32 and ENC28J60 Ethernet module. + * + * This file includes configurations for SPI settings, conditional compilation flags for enabling various + * networking clients (e.g., NTP, DNS, DHCP), and external declarations of global variables and function prototypes. + * + * @note For more information, refer to the `license.md` file located at the root of the project. + */ + +#ifndef INC_DEFINES_H_ +#define INC_DEFINESE_H_ + +#include "main.h" + +// Comment only one of this two. Depend of your SPI configuration. +// CS_only is simple (no option from SPI configuration). +// NSS is better. configuration details in enc28J60.h +//#define CS_Only +#define NSS_OutputSignal + +// # CS SECTION +#ifdef CS_Only +#define ETHERNET_LED_GPIO GPIOC +#define ETHERNET_LED_PIN GPIO_PIN_13 +#define ETHERNET_CS_GPIO GPIOA +#define ETHERNET_CS_PIN GPIO_PIN_4 +#define ETHERNET_CS_DELAY 10 // Start with 2, then 1 and 0. 1000 is an other step. if it work try 10, could be Unstable but fast. +#endif + +// # Parameters SECTION +// Comment or not depend on your need below : +#define ETHERSHIELD_DEBUG 1 +#define NTP_client +#define DNS_client +#define DHCP_client +#define HOSTNAME "STM32_ENC28J60" +#define DHCP_HOSTNAME_LEN 14 // here the len of the HOSTNAME define just before +#define DHCP_HOSTNAME "STM32_ENC28J60" +#define PING_client +//#define PINGPATTERN 0x42 (element needed in net.c - is it very usefull ? +#define TCP_client +#define WWW_client //uncoment next one too +#define WWW_USER_AGENT "MyUserAgent" +#define FROMDECODE_websrv_help +#define URLENCODE_websrv_help + + + +//extern volatile uint8_t dma_isr_called; + + +// Define this in defines.c file +extern uint8_t macaddrin[6]; +extern uint8_t ipaddrin[4]; +extern uint8_t maskin[4]; +extern uint8_t gwipin[4]; +extern uint8_t dhcpsvrin[4]; +extern uint8_t dnssvrin[4]; +extern uint8_t dnsipaddr[4]; +extern uint8_t ntpip[4]; +extern char domainName[]; +extern uint8_t dest_ip[4]; +extern uint16_t dest_port; +extern uint16_t srcport; +extern uint8_t dstport_h; +extern uint8_t dstport_l; +extern uint8_t dip[]; +extern uint16_t dport; +extern uint16_t sport; + +// Define the structure for command mapping +typedef void (*CommandFunction)(); +typedef void (*CommandFunctionWithArg)(char*); + +typedef struct { + const char *command; + CommandFunction function; + CommandFunctionWithArg function_with_arg; +} CommandMapping; + +// Declare the command table +extern CommandMapping commandTable[]; + +// Declare command functions here +void sendSTM32State(); // Function to send the STM32 state +void sendRTCDateTime(); // Function to send the RTC date and time + +#endif /* INC_DEFINES_H_ */ diff --git a/Inc/dhcp.h b/Inc/dhcp.h new file mode 100644 index 0000000..1a0845d --- /dev/null +++ b/Inc/dhcp.h @@ -0,0 +1,32 @@ +/** + * @file dhcp.h + * @brief Header file for DHCP client function prototypes and definitions. + * + * This file provides the function prototypes and necessary definitions for implementing DHCP client functionality. + * It includes declarations for initializing the DHCP process, sending requests, and handling DHCP responses + * such as DHCPOFFER and DHCPACK. + * + * @note For more information, refer to the `license.md` file located at the root of the project. + */ + + +#ifndef DHCP_H +#define DHCP_H + +#include "main.h" +#include "defines.h" + +extern void dhcp_start(uint8_t *buf, uint8_t *macaddrDCPin, uint8_t *ipaddrin, + uint8_t *maskin, uint8_t *gwipin, uint8_t *dhcpsvrin, + uint8_t *dnssvrin ); + +extern uint8_t dhcp_state( void ); + +void dhcp_send(uint8_t *buf, uint8_t requestType); +uint8_t check_for_dhcp_answer(uint8_t *buf,uint16_t plen); +uint8_t have_dhcpoffer(uint8_t *buf,uint16_t plen); +uint8_t have_dhcpack(uint8_t *buf,uint16_t plen); + +uint8_t allocateIPAddress(uint8_t *buf, uint16_t buffer_size, uint8_t *mymac, uint16_t myport, uint8_t *myip, uint8_t *mynetmask, uint8_t *gwip, uint8_t *dnsip, uint8_t *dhcpsvrip ); + +#endif /* DHCP_H */ diff --git a/inc/dnslkup.h b/Inc/dnslkup.h similarity index 62% rename from inc/dnslkup.h rename to Inc/dnslkup.h index b6d1202..88ea847 100644 --- a/inc/dnslkup.h +++ b/Inc/dnslkup.h @@ -1,22 +1,19 @@ -/********************************************* - * vim:sw=8:ts=8:si:et - * To use the above modeline in vim you must have "set modeline" in your .vimrc - * Author: Guido Socher - * Copyright: GPL V2 +/** + * @file dnslkup.h + * @brief Header file for DNS lookup function prototypes and related definitions. * - * DNS look-up functions based on the udp client + * This file provides the function prototypes for DNS client operations, such as sending DNS requests, + * processing DNS responses, and resolving hostnames to IP addresses. It also includes functions + * for setting the DNS server and checking for errors during DNS lookups. * - * Chip type : ATMEGA88/ATMEGA168/ATMEGA328p with ENC28J60 - *********************************************/ -//@{ + * @note For more information, refer to the `license.md` file located at the root of the project. + */ + #ifndef DNSLKUP_H #define DNSLKUP_H -#include "stm32includes.h" - -// to use this you need to enable UDP_client in the file ip_config.h -// -#if defined (UDP_client) +#include "main.h" +//#include "defines.h" // look-up a hostname (you should check client_waiting_gw() before calling this function): extern void dnslkup_request(uint8_t *buf, uint8_t *hostname); @@ -36,6 +33,6 @@ extern uint8_t *dnslkup_getip(void); // set DNS server to be used for lookups. extern void dnslkup_set_dnsip(uint8_t *dnsipaddr); -#endif /* UDP_client */ +uint8_t resolveHostname(uint8_t *buf, uint16_t buffer_size, uint8_t *hostname ); + #endif /* DNSLKUP_H */ -//@} diff --git a/inc/enc28j60.h b/Inc/enc28j60.h similarity index 77% rename from inc/enc28j60.h rename to Inc/enc28j60.h index 387235e..aa8edda 100644 --- a/inc/enc28j60.h +++ b/Inc/enc28j60.h @@ -1,38 +1,20 @@ +/** + * @file ENC28J60.h + * @brief Header file containing definitions, macros, and function prototypes for interfacing with the ENC28J60 Ethernet controller. + * + * This file includes control register definitions, chip enable/disable macros, configurations for delays, + * chip select (CS) handling, and various SPI operation codes. It also provides function prototypes + * for initializing and managing the ENC28J60 Ethernet controller. + * + * @note For more information, refer to the `license.md` file located at the root of the project. + */ + #ifndef __ENC28J60_H #define __ENC28J60_H -#include "stm32includes.h" -#define Delay HAL_Delay - -/* -#pragma GCC push_options -#pragma GCC optimize ("O0") -static inline void uDelay(uint32_t useconds) { - volatile int cycleCount = ? - while (cycleCount--); -} -#pragma GCC pop_options -*/ - -#ifndef ETHERNET_LED_GPIO -# error Please define ETHERNET_LED_GPIO, for example by gcc option -DETHERNET_LED_GPIO=GPIOA -#endif -#ifndef ETHERNET_LED_PIN -# error Please define ETHERNET_LED_PIN, for example by gcc option -DETHERNET_LED_PIN=GPIO_PIN_5 -#endif - -#ifndef ETHERNET_CS_GPIO -# error Please define chip-select GPIO port ETHERNET_CS_GPIO, for example by gcc option -DETHERNET_CS_GPIO=GPIOA -#endif -#ifndef ETHERNET_CS_PIN -# error Please define chip-select pin ETHERNET_CS_PIN, for example by gcc option -DETHERNET_CS_PIN=GPIO_PIN_4 -#endif - -#ifndef ETHERNET_CS_DELAY -# warning ETHERNET_CS_DELAY is not defined. Setting to "2" (mseconds). Adapter may work very slow or not properly. If the latency on "1" is too big, but on "0" the adapter is not working properly, you can try values >= 10: it will use other delay method (for example try value 1000). -# define ETHERNET_CS_DELAY 2 -#endif +#include "main.h" +#ifdef CS_Only #if ETHERNET_CS_DELAY >= 10 # define ETHERNET_CS_DELAY_PROC {volatile uint32_t i=ETHERNET_CS_DELAY; while(i--);} #else @@ -45,9 +27,12 @@ static inline void uDelay(uint32_t useconds) { #define enableChip ETHERNET_CS_GPIO->BSRR = ETHERNET_CS_PIN<<16;\ ETHERNET_LED_GPIO->BSRR = ETHERNET_LED_PIN;\ ETHERNET_CS_DELAY_PROC; -//#define disableChip {} -//#define enableChip {} +#endif +#ifdef NSS_OutputSignal +#define disableChip __HAL_SPI_DISABLE(hspi); +#define enableChip; +#endif // ENC28J60 Control Registers // Control register definitions are a combination of address, @@ -268,53 +253,49 @@ static inline void uDelay(uint32_t useconds) { #define ENC28J60_BIT_FIELD_CLR 0xA0 #define ENC28J60_SOFT_RESET 0xFF - // The RXSTART_INIT should be zero. See Rev. B4 Silicon Errata // buffer boundaries applied to internal 8K ram // the entire available packet buffer space is allocated // // start with RX buf at 0/ -#define RXSTART_INIT 0x0 +#define RXSTART_INIT 0x0 // RX buffer end -#define RXSTOP_INIT (0x1FFF-0x0600-1) +#define RXSTOP_INIT (0x1FFF-0x0600-1) // start TX buffer at 0x1FFF-0x0600, place for one full ethernet frame (~1500 bytes) -#define TXSTART_INIT (0x1FFF-0x0600) +#define TXSTART_INIT (0x1FFF-0x0600) // stop TX buffer at end of mem -#define TXSTOP_INIT 0x1FFF -// +#define TXSTOP_INIT 0x1FFF + // max frame length which the controller will accept: -#define MAX_FRAMELEN 1500 // (note: maximum ethernet frame length would be 1518) +#define MAX_FRAMELEN 1500 // (note: maximum ethernet frame length would be 1518) //#define MAX_FRAMELEN 600 -//void ENC28J60_SPI1_Configuration(void); -//void ENC28J60_GPIO_Configuration(void); void enc28j60_set_spi(SPI_HandleTypeDef *hspi_new); unsigned char ENC28J60_SendByte(unsigned char dt); -uint8_t enc28j60ReadOp(uint8_t op, uint8_t address); - // functions -extern uint8_t enc28j60ReadOp(uint8_t op, uint8_t address); -extern void enc28j60WriteOp(uint8_t op, uint8_t address, uint8_t data); -extern void enc28j60ReadBuffer(uint16_t len, uint8_t* data); -extern void enc28j60WriteBuffer(uint16_t len, uint8_t* data); -extern void enc28j60SetBank(uint8_t address); -extern uint8_t enc28j60Read(uint8_t address); -extern void enc28j60Write(uint8_t address, uint8_t data); -extern void enc28j60PhyWrite(uint8_t address, uint16_t data); -extern void enc28j60clkout(uint8_t clk); -extern void enc28j60SpiInit(void); -extern void enc28j60Init(uint8_t* macaddr); -extern void enc28j60PacketSend(uint16_t len, uint8_t* packet); -extern uint16_t enc28j60PacketReceive(uint16_t maxlen, uint8_t* packet); -extern uint8_t enc28j60getrev(void); -extern uint8_t enc28j60hasRxPkt(void); -extern uint8_t enc28j60linkup(void); -extern void enc28j60EnableBroadcast( void ); -extern void enc28j60DisableBroadcast( void ); -extern void enc28j60EnableMulticast( void ); -extern void enc28j60DisableMulticast( void ); -extern void enc28j60PowerDown(); -extern void enc28j60PowerUp(); +uint8_t enc28j60ReadOp(uint8_t op, uint8_t address); +void enc28j60WriteOp(uint8_t op, uint8_t address, uint8_t data); +void enc28j60ReadBuffer(uint16_t len, uint8_t* data); +void enc28j60WriteBuffer(uint16_t len, uint8_t* data); +void enc28j60SetBank(uint8_t address); +uint8_t enc28j60Read(uint8_t address); +void enc28j60Write(uint8_t address, uint8_t data); +void enc28j60PhyWrite(uint8_t address, uint16_t data); +static void enc28j60PhyWriteWord(uint8_t address, uint16_t data); +void enc28j60clkout(uint8_t clk); +void enc28j60SpiInit(void); +void enc28j60Init(uint8_t* macaddr); +void enc28j60PacketSend(uint16_t len, uint8_t* packet); +uint16_t enc28j60PacketReceive(uint16_t maxlen, uint8_t* packet); +uint8_t enc28j60getrev(void); +uint8_t enc28j60hasRxPkt(void); +uint8_t enc28j60linkup(void); +void enc28j60EnableBroadcast( void ); +void enc28j60DisableBroadcast( void ); +void enc28j60EnableMulticast( void ); +void enc28j60DisableMulticast( void ); +void enc28j60PowerDown(); +void enc28j60PowerUp(); #endif /* __ENC28J60_H */ diff --git a/inc/error_handler.h b/Inc/error_handler.h similarity index 73% rename from inc/error_handler.h rename to Inc/error_handler.h index 06cc560..78f1f16 100644 --- a/inc/error_handler.h +++ b/Inc/error_handler.h @@ -1,3 +1,8 @@ +/** +* @file error_handler.h + * @brief Header file + */ + #ifndef __ERROR_HANDLER_H #define __ERROR_HANDLER_H @@ -7,4 +12,4 @@ enum ENC28j60_Error { void __attribute__((weak)) ENC28j60_Error_Handler(enum ENC28j60_Error error); -#endif \ No newline at end of file +#endif diff --git a/Inc/ip_arp_udp_tcp.h b/Inc/ip_arp_udp_tcp.h new file mode 100644 index 0000000..d560626 --- /dev/null +++ b/Inc/ip_arp_udp_tcp.h @@ -0,0 +1,58 @@ +/** + * @file ip_arp_udp_tcp.h + * @brief Header file containing function prototypes and definitions for IP, ARP, UDP, and TCP protocols. + * + * This file includes function declarations for initializing and managing network communications using + * the ENC28J60 Ethernet controller. It provides functions for handling ARP, UDP, and TCP packets, + * as well as client-side networking tasks such as ARP requests and gateway management. + * + * @note For more information, refer to the `license.md` file located at the root of the project. + */ + +#ifndef IP_ARP_UDP_TCP_H +#define IP_ARP_UDP_TCP_H + +#include "main.h" + +// -- web server functions -- +// you must call this function once before you use any of the other server functions: +void init_ip_arp_udp_tcp(uint8_t *mymac,uint8_t *myip,uint16_t port); +// for a UDP server: +uint8_t eth_type_is_arp_and_my_ip(uint8_t *buf,uint16_t len); +uint8_t eth_type_is_ip_and_my_ip(uint8_t *buf,uint16_t len); +uint8_t eth_type_is_ip_and_my_ip(uint8_t *buf,uint16_t len); +void make_udp_reply_from_request(uint8_t *buf,char *data,uint16_t datalen,uint16_t port); + +void make_arp_answer_from_request(uint8_t *buf); +void init_len_info(uint8_t *buf); + +void fill_buf_p(uint8_t *buf, uint16_t len, const char *s); +void fill_ip_hdr_checksum(uint8_t *buf); +uint16_t checksum(uint8_t *buf, uint16_t len,uint8_t type); + +// -- client functions -- +#if defined (WWW_client) || defined (NTP_client) || defined (UDP_client) || defined (TCP_client) || defined (PING_client) +uint8_t client_store_gw_mac(uint8_t *buf); +void client_set_gwip(uint8_t *gwipaddr); +void client_gw_arp_refresh(void); +void client_arp_whohas(uint8_t *buf,uint8_t *gwipaddr); +uint8_t client_waiting_gw(void); // 1 no GW mac yet, 0 have a gw mac + +//#if defined (WWW_client) || defined (TCP_client) +//#define client_set_wwwip client_tcp_set_serverip +// set the ip of the next tcp session that we open to a server +//#endif +#endif // WWW_client TCP_client etc + +#ifdef TCP_client +void tcp_client_send_packet(uint8_t *buf,uint16_t dest_port, uint16_t src_port, uint8_t flags, uint8_t max_segment_size, + uint8_t clear_seqck, uint16_t next_ack_num, uint16_t dlength, uint8_t *dest_mac, uint8_t *dest_ip); +uint16_t tcp_get_dlength ( uint8_t *buf ); + +#endif // TCP_client + +void send_udp_prepare(uint8_t *buf,uint16_t sport, uint8_t *dip, uint16_t dport); +void send_udp_transmit(uint8_t *buf,uint16_t datalen); +void send_udp(uint8_t *buf,char *data,uint16_t datalen,uint16_t sport, uint8_t *dip, uint16_t dport); +#endif /* IP_ARP_UDP_TCP_H */ +//@} diff --git a/Inc/net.h b/Inc/net.h new file mode 100644 index 0000000..8981ae3 --- /dev/null +++ b/Inc/net.h @@ -0,0 +1,260 @@ +/** + * @file net.h + * @brief Header file for network-related definitions and macros for ENC28J60 Ethernet controller. + * + * This file contains the definitions and macros necessary for interacting with the ENC28J60 Ethernet controller, + * including structures and constants for handling Ethernet, IP, TCP, UDP, and DNS protocols. It also defines + * various states for DHCP and DNS clients, as well as structures for managing TCP connections. + * + * @note The file is configured to support different STM32 series, including STM32F0, STM32F1, and STM32F4. + */ + +#ifndef NET_H +#define NET_H + +#include +#include +#include +#include +#include + + +#include "main.h" + +#if defined(STM32F0) +/*# if STM32F091xC +# include "stm32f091xc.h" +# endif*/ +# include "stm32f0xx.h" +# include "stm32f0xx_hal_def.h" +# include "stm32f0xx_hal_spi.h" +#elif defined(STM32F1) +//# include "stm32f103xb.h" +# include "stm32f1xx.h" +# include "stm32f1xx_hal_def.h" +# include "stm32f1xx_hal_spi.h" +#elif defined(STM32F4) +# include "stm32f4xx.h" +# include "stm32f4xx_hal.h" +# include "stm32f4xx_hal_gpio.h" +# include "stm32f4xx_hal_def.h" +# include "stm32f4xx_hal_spi.h" +#endif + + +// Ethernet Header +#define ETH_HEADER_LEN 14 +#define ETHTYPE_ARP_H_V 0x08 +#define ETHTYPE_ARP_L_V 0x06 +#define ETHTYPE_IP_H_V 0x08 +#define ETHTYPE_IP_L_V 0x00 +#define ETH_TYPE_H_P 12 +#define ETH_TYPE_L_P 13 +#define ETH_DST_MAC 0 +#define ETH_SRC_MAC 6 +// ARP Header +#define ETH_ARP_OPCODE_REPLY_H_V 0x0 +#define ETH_ARP_OPCODE_REPLY_L_V 0x02 +#define ETH_ARP_OPCODE_REQ_H_V 0x0 +#define ETH_ARP_OPCODE_REQ_L_V 0x01 +#define ETH_ARP_P 0xe +#define ETH_ARP_DST_IP_P 0x26 +#define ETH_ARP_OPCODE_H_P 0x14 +#define ETH_ARP_OPCODE_L_P 0x15 +#define ETH_ARP_SRC_MAC_P 0x16 +#define ETH_ARP_SRC_IP_P 0x1c +#define ETH_ARP_DST_MAC_P 0x20 +#define ETH_ARP_DST_IP_P 0x26 +// IP Header +#define IP_HEADER_LEN 20 +#define IP_PROTO_ICMP_V 0x01 +#define IP_PROTO_TCP 0x06 // Protocol number for TCP +#define IP_PROTO_TCP_V 0x06 +#define IP_PROTO_UDP_V 0x11 +#define IP_V4_V 0x40 +#define IP_HEADER_LENGTH_V 0x05 +#define IP_P 0x0E +#define IP_HEADER_VER_LEN_P 0x0E +#define IP_TOS_P 0x0F +#define IP_TOTLEN_H_P 0x10 +#define IP_TOTLEN_L_P 0x11 +#define IP_ID_H_P 0x12 +#define IP_ID_L_P 0x13 +#define IP_FLAGS_H_P 0x14 +#define IP_FLAGS_L_P 0x15 +#define IP_TTL_P 0x16 +#define IP_PROTO_P 0x17 +#define IP_CHECKSUM_H_P 0x18 +#define IP_CHECKSUM_L_P 0x19 +#define IP_SRC_IP_P 0x1A +#define IP_DST_IP_P 0x1E +#define IP_HEADER_LEN_VER_P 0xe +// ICMP Header +#define ICMP_TYPE_ECHOREPLY_V 0 +#define ICMP_TYPE_ECHOREQUEST_V 8 +#define ICMP_TYPE_P 0x22 +#define ICMP_CHECKSUM_H_P 0x24 +#define ICMP_CHECKSUM_L_P 0x25 +#define ICMP_IDENT_H_P 0x26 +#define ICMP_IDENT_L_P 0x27 +#define ICMP_DATA_P 0x2a +// UDP Header +#define UDP_HEADER_LEN 8 +#define UDP_SRC_PORT_H_P 0x22 +#define UDP_SRC_PORT_L_P 0x23 +#define UDP_DST_PORT_H_P 0x24 +#define UDP_DST_PORT_L_P 0x25 +#define UDP_LEN_H_P 0x26 +#define UDP_LEN_L_P 0x27 +#define UDP_CHECKSUM_H_P 0x28 +#define UDP_CHECKSUM_L_P 0x29 +#define UDP_DATA_P 0x2a +// TCP Header +#define TCP_STATE_CLOSED 0 +#define TCP_STATE_SYN_SENT 1 +#define TCP_STATE_SYN_RECEIVED 2 +#define TCP_STATE_ESTABLISHED 3 +#define TCP_STATE_FIN_WAIT_1 4 +#define TCP_STATE_FIN_WAIT_2 5 +#define TCP_STATE_CLOSE_WAIT 6 +#define TCP_STATE_CLOSING 7 +#define TCP_STATE_LAST_ACK 8 +#define TCP_STATE_TIME_WAIT 9 +#define TCP_FLAGS_FIN_V 0x01 +#define TCP_FLAGS_SYN_V 0x02 +#define TCP_FLAGS_RST_V 0x04 +#define TCP_FLAGS_PUSH_V 0x08 +#define TCP_FLAGS_ACK_V 0x10 +#define TCP_FLAGS_SYNACK_V 0x12 +#define TCP_FLAGS_PSHACK_V 0x18 +#define TCP_SRC_PORT_H_P 0x22 +#define TCP_SRC_PORT_L_P 0x23 +#define TCP_DST_PORT_H_P 0x24 +#define TCP_DST_PORT_L_P 0x25 +#define TCP_SEQ_P 0x26 +#define TCP_SEQ_H_P 0x26 +#define TCP_SEQ_L_P (TCP_SEQ_H_P + 1) +#define TCP_SEQACK_P 0x2a +#define TCP_SEQACK_H_P 0x2a +#define TCP_FLAGS_P 0x2f +#define TCP_WINDOWSIZE_H_P 0x30 +#define TCP_WINDOWSIZE_L_P 0x31 +#define TCP_CHECKSUM_H_P 0x32 +#define TCP_CHECKSUM_L_P 0x33 +#define TCP_URGENT_PTR_H_P 0x34 +#define TCP_URGENT_PTR_L_P 0x35 +#define TCP_OPTIONS_P 0x36 +#define TCP_DATA_P 0x36 +#define TCP_ACK_H_P 0x2C +#define TCP_ACK_L_P (TCP_ACK_H_P + 1) +#define TCP_HEADER_LEN_PLAIN 20 +#define TCP_HEADER_LEN_P 0x2e +#define TCP_WIN_SIZE 0x30 +#define TCP_LEN_H_P 0x2C // Position of TCP length high byte +#define TCP_LEN_L_P 0x2D // Position of TCP length low byte +// DNS +#define DNSCLIENT_SRC_PORT_H 0xe0 + + +//TCP personalyse ? +#define TCPCLIENT_SRC_PORT_H 11 +#define WGW_INITIAL_ARP 1 +#define WGW_HAVE_GW_MAC 2 +#define WGW_REFRESHING 4 +#define WGW_ACCEPT_ARP_REPLY 8 +#define CLIENTMSS 550 +#define TCP_DATA_START ((uint16_t)TCP_SRC_PORT_H_P+(buf[TCP_HEADER_LEN_P]>>4)*4) + +// DNS States for access in applications +#define DNS_STATE_INIT 0 +#define DNS_STATE_REQUESTED 1 +#define DNS_STATE_ANSWER 2 +// DHCP States for access in applications +#define DHCP_STATE_INIT 0 +#define DHCP_STATE_DISCOVER 1 +#define DHCP_STATE_OFFER 2 +#define DHCP_STATE_REQUEST 3 +#define DHCP_STATE_ACK 4 +#define DHCP_STATE_OK 5 +#define DHCP_STATE_RENEW 6 + +#define bool _Bool +#define true 1 +#define false 0 + +#define PINGPATTERN 0x42 + +extern uint16_t len; +extern uint16_t plen; + +// Web server port, used when implementing webserver +extern uint8_t wwwport_l; // server port +extern uint8_t wwwport_h; // Note: never use same as TCPCLIENT_SRC_PORT_H + +extern uint8_t www_fd; +extern uint8_t browsertype; // 0 = get, 1 = post +extern char *client_additionalheaderline; +extern char *client_method; // for POST or PUT, no trailing space needed +extern char *client_urlbuf; +extern char *client_hoststr; +extern char *client_postval; +extern char *client_urlbuf_var; +extern uint8_t *bufptr; // ugly workaround for backward compatibility + +// just lower byte, the upper byte is TCPCLIENT_SRC_PORT_H: +extern uint8_t tcpclient_src_port_l; +extern uint8_t tcp_fd; // a file descriptor, will be encoded into the port +extern uint8_t tcp_client_state; +// TCP client Destination port +extern uint8_t tcp_client_port_h; +extern uint8_t tcp_client_port_l; + +#define MAX_TCP_CONNECTIONS 10 + +typedef struct { + uint8_t state; + uint32_t seq_num; + uint32_t ack_num; + uint8_t remote_ip[4]; + uint16_t remote_port; + uint16_t local_port; +} tcp_connection_t; + +extern tcp_connection_t tcp_connections[MAX_TCP_CONNECTIONS]; + + +extern int16_t delaycnt; +extern uint8_t gwip[4]; +extern uint8_t gwmacaddr[6]; +extern uint8_t tcpsrvip[4]; +extern uint8_t macaddr[6]; +extern uint8_t ipaddr[4]; +extern uint16_t info_data_len; +extern uint16_t info_hdr_len; +extern uint8_t seqnum; +extern const char arpreqhdr[]; +extern const char iphdr[]; +extern const char ntpreqhdr[]; + +extern uint8_t waitgwmac; + + +extern void (*icmp_callback)(uint8_t *ip); + + +// TCP functions on packet.c +extern uint8_t send_fin; +extern uint16_t tcpstart; +extern uint16_t save_len; + + +void client_icmp_request(uint8_t *buf,uint8_t *destip); +void register_ping_rec_callback(void (*callback)(uint8_t *srcip)); +uint8_t packetloop_icmp_checkreply(uint8_t *buf,uint8_t *ip_monitoredhost); +void make_echo_reply_from_request(uint8_t *buf,uint16_t len); +void __attribute__((weak)) pingCallback(void); + +void send_wol(uint8_t *buf,uint8_t *wolmac); + + +#endif diff --git a/Inc/ntp.h b/Inc/ntp.h new file mode 100644 index 0000000..a22cc6e --- /dev/null +++ b/Inc/ntp.h @@ -0,0 +1,19 @@ +/** + * @file ntp.h + * @brief NTP (Network Time Protocol) functions for the STM32 using ENC28J60 Ethernet controller. + * + * This header file provides the necessary function declarations for sending NTP requests and processing + * NTP responses. It facilitates synchronization of the STM32 microcontroller's Real-Time Clock (RTC) + * with a remote NTP server. + */ + +#ifndef NTP_H +#define NTP_H + +#include "main.h" + +void client_ntp_request(uint8_t *buf, uint8_t *ntpip, uint8_t srcport); +void client_ntp_process_answer(uint8_t *buf, uint16_t plen); + + +#endif diff --git a/Inc/packet.h b/Inc/packet.h new file mode 100644 index 0000000..92bfd64 --- /dev/null +++ b/Inc/packet.h @@ -0,0 +1,29 @@ +/** + * @file packet.h + * @brief Header file containing function prototypes for processing Ethernet packets, including TCP, UDP, ICMP, and HTTP protocols. + * + * This file declares functions for handling various types of network packets, including Ethernet, TCP, UDP, + * ICMP, and HTTP. It provides the necessary prototypes for managing packet processing, handling protocol-specific + * tasks, and maintaining network communication states. + * + * @note For more information, refer to the `license.md` file located at the root of the project. + */ + +#ifndef PACKET_H +#define PACKET_H + +#include "main.h" +#include "defines.h" +#include "net.h" + + + +void handle_AllPacket(uint8_t *buf, uint16_t len); + +void handle_icmp_packet(uint8_t *buf, uint16_t len); +void handle_tcp_response(uint8_t *buf); +void loop(); +void handle_tcp_packet(uint8_t *buf, uint16_t len); +void handle_udp_packet(uint8_t *buf, uint16_t len); +void handle_http_request(uint8_t *buf, uint16_t data_offset, uint16_t data_len, tcp_connection_t *tcp_conn); +#endif diff --git a/Inc/tcp.h b/Inc/tcp.h new file mode 100644 index 0000000..7ba03ba --- /dev/null +++ b/Inc/tcp.h @@ -0,0 +1,68 @@ +/* + * @file tcp.h + * @brief Header file + * + * This header file contains definitions and macros for interfacing with the ENC28J60 Ethernet controller. + * It includes control register definitions, chip enable/disable macros, and configurations for delays and + * chip select (CS) handling. + */ + +#ifndef TCP_H +#define TCP_H + +#include "main.h" +//#include "defines.h" +#include "net.h" + +// Callback function types for handling TCP results and data fill +typedef uint8_t (*tcp_result_callback_t)(uint8_t fd, uint8_t statuscode, uint16_t data_start_pos_in_buf, uint16_t len_of_data); +typedef uint16_t (*tcp_datafill_callback_t)(uint8_t fd); + +// Static variables to store the callbacks +extern tcp_result_callback_t client_tcp_result_callback; +extern tcp_datafill_callback_t client_tcp_datafill_callback; + +//-------------------------------------- STEP1 +void client_tcp_set_serverip(uint8_t *ipaddr); +void initialize_tcp_sequence_number(); +void init_tcp_connections(); +tcp_connection_t* find_free_connection(); +tcp_connection_t* find_connection(uint8_t *ip, uint16_t port); +//-------------------------------------- STEP2 +void tcp_open_connection(uint8_t *src_ip, uint16_t src_port, uint8_t *dst_ip, uint16_t dst_port); +void make_tcphead(uint8_t *buf, uint16_t len, uint8_t mss, uint8_t win_size); +static uint16_t TCP_htons(uint16_t hostshort); +void make_ip_tcp_new(uint8_t *buf, uint16_t len, uint8_t *dst_ip); +void create_tcp_synack_packet(uint8_t *buf, tcp_connection_t *tcp_conn); +void create_tcp_ack_packet(uint8_t *buf, tcp_connection_t *tcp_conn, uint8_t mss, uint8_t win_size); +void create_tcp_ack_with_data_packet(uint8_t *buf,uint16_t len); +//void tcp_close_connection(tcp_connection_t *conn); +//-------------------------------------- STEP3 +uint16_t get_tcp_data_len(uint8_t *buf); +uint16_t fill_tcp_data(uint8_t *buf, uint16_t pos, const uint8_t *data, uint16_t len); +uint16_t build_tcp_data(uint8_t *buf, uint16_t srcPort); +void send_tcp_data(uint8_t *buf, uint16_t dlen); +void send_data_packet(uint8_t *buf, uint16_t srcPort, const uint8_t *data, uint16_t len); +void send_tcp_fin_packet(uint8_t *buf); +void tcp_send_packet(uint32_t seq_num, uint32_t ack_num); +//-------------------------------------- STEP4 +uint16_t prepare_tcp_data(uint8_t *buf, uint16_t pos, const uint8_t *data, uint16_t len); +void transmit_tcp_data(uint8_t *buf, uint16_t dlen); +//-------------------------------------- STEP6 +void tcp_handle_retransmissions(); +void tcp_state_machine(uint8_t *buf, uint16_t len); +//-------------------------------------- OTHERS +void client_syn(uint8_t *buf, uint8_t srcport, uint8_t dstport_h, uint8_t dstport_l); +void make_tcp_ack_with_data_noflags(uint8_t *buf,uint16_t dlen); + +uint8_t client_tcp_req(uint8_t (*result_callback)(uint8_t fd,uint8_t statuscode,uint16_t data_start_pos_in_buf, uint16_t len_of_data),uint16_t (*datafill_callback)(uint8_t fd),uint16_t port); + +void fill_ip_tcp_checksum(uint8_t *buf, uint16_t len); +void fill_tcp_ip_hdr_checksum(uint8_t *buf); +uint16_t calculate_tcp_checksum(uint8_t *buf, uint16_t len); +void send_tcp_syn_packet(uint8_t *buf, uint16_t srcPort, uint16_t dstPort, uint8_t *dst_ip); + +void log_ip_header(uint8_t *buf); +void log_tcp_header(uint8_t *buf); + +#endif diff --git a/Inc/timers.h b/Inc/timers.h new file mode 100644 index 0000000..4812027 --- /dev/null +++ b/Inc/timers.h @@ -0,0 +1,27 @@ +/** + * @file timers.h + * @brief Header file for timer-related functions and declarations for the STM32 platform. + * + * This file contains the declarations of functions for initializing, logging, and handling + * timer interrupts, specifically for the TIM4 timer. It also includes the necessary external + * references to the TIM handle and function prototypes. + * + * @note For more information, refer to the `license.md` file located at the root of the project. + * + * @created May 30, 2024 + * @author dtneo + */ + +#ifndef TIMERS_H +#define TIMERS_H + +#include "main.h" + +extern TIM_HandleTypeDef htim4; + +void timerLog(TIM_HandleTypeDef *htim); +void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim); +void generalTimersInit(); + + +#endif // TIMERS_H diff --git a/Inc/websrv_help_functions.h b/Inc/websrv_help_functions.h new file mode 100644 index 0000000..64e6f5c --- /dev/null +++ b/Inc/websrv_help_functions.h @@ -0,0 +1,70 @@ +/* + * @file websrv_help_functions.h + * @brief Header file + * + * This header file contains definitions and macros for interfacing with the ENC28J60 Ethernet controller. + * It includes control register definitions, chip enable/disable macros, and configurations for delays and + * chip select (CS) handling. + */ + +#ifndef WEBSRV_HELP_FUNCTIONS_H +#define WEBSRV_HELP_FUNCTIONS_H + +#include "main.h" +#include "net.h" + + +uint16_t send_logs_page(uint8_t *buf); +uint16_t send_test_page(uint8_t *buf); +void www_server_reply(uint8_t *buf, uint16_t dlen, tcp_connection_t *tcp_conn); +uint16_t www_client_internal_datafill_callback(uint8_t fd); +uint8_t www_client_internal_result_callback(uint8_t fd, uint8_t statuscode, uint16_t datapos, uint16_t len_of_data); +void client_http_get(char *urlbuf, char *urlbuf_varpart, char *hoststr, void (*callback)(uint8_t,uint16_t,uint16_t)); +void client_http_post(char *urlbuf, char *hoststr, char *additionalheaderline, char *method, char *postval,void (*callback)(uint8_t,uint16_t)); + +// Function declarations + +/** + * Find the value for a given key in a query string. + * @param str The input query string. + * @param strbuf The buffer to store the value. + * @param maxlen The maximum length of the value to store. + * @param key The key to search for. + * @return Length of the value found. + */ +uint8_t find_key_val(char *str, char *strbuf, uint8_t maxlen, char *key); + +/** + * Decode a URL-encoded string. + * @param urlbuf The URL-encoded string to decode. + */ +void urldecode(char *urlbuf); + +/** + * Encode a string to URL-encoded format. + * @param str The input string to encode. + * @param urlbuf The buffer to store the URL-encoded string. + */ +void urlencode(char *str, char *urlbuf); + +/** + * Parse an IP address string into a byte array. + * @param bytestr The byte array to store the parsed IP address. + * @param str The input IP address string. + * @return 1 if successful, 0 otherwise. + */ +uint8_t parse_ip(uint8_t *bytestr, char *str); + +/** + * Create a network string from a byte array. + * @param resultstr The buffer to store the resulting string. + * @param bytestr The input byte array. + * @param len The length of the byte array. + * @param separator The character to use as a separator. + * @param base The numerical base for conversion (e.g., 10 for decimal). + */ +void mk_net_str(char *resultstr, uint8_t *bytestr, uint8_t len, char separator, uint8_t base); + +void get_logs(char *buffer, size_t size); + +#endif /* WEBSRV_HELP_FUNCTIONS_H */ diff --git a/README.md b/README.md index fba821f..8c8518d 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,141 @@ -# stm32-enc28j60 +# TOOLS for STM32 +## ENC28J60 +ENC28J60 ethernet module for STM32 MCU -STM32 ENC28J60 ethernet driver, ported for CMake using [stm32-cmake](https://github.com/ObKo/stm32-cmake) and [stm32-base](https://github.com/DVALight/stm32-base) +Search terms for `ENC28J60/HR911105A datasheet` and `enc28j60 schematic` for detail about hardware. -An implementation of ENC28J60 driver for STM32 MCU. Tested only on sending UDP packets and only on MCUs STM32F091xC, STM32F030x6, STM32F103xB and STM32F401VE. +About Cmake, this code was ported using [stm32-cmake](https://github.com/ObKo/stm32-cmake) and [stm32-base](https://github.com/DVALight/stm32-base) -You need to copy `stm32*_hal_conf.h` of your MCU to `inc` folder to make it work. +### UDP Command Hnadler +Domotics ! -If you need to use another STM32 MCU then don't forget to edit file [`inc/stm32includes.h`](inc/stm32includes.h). +### RTC +Active the code on file [`inc/define.h`](inc/defines.h). +Add the code needed in main.c `between USER code begin` and `User code end` of the section RTC created by your STM32cubeIDE, you should find examples files in this repo. -## How to use it +### Logs functions +via udp +via html +... -* Either just copy it into your project and set up your project to be built with this library. -* Or use the [stm32-cmake](https://github.com/ObKo/stm32-cmake) (see the next section). +## MCU tested (please share your results for update this list): +- STM32F091xC (v2 Tested only on sending UDP packets) +- STM32F030x6 (v2 Tested only on sending UDP packets) +- STM32F103xB (v2 Tested only on sending UDP packets) +- STM32F401VE (v2 Tested only on sending UDP packets) +- STM32F407 (v3 complete network interface) +- STM32F446 (v3 complete network interface) + +## News on branch V3 +// Contact of this changes @Dtneo + +Quick Start for Full Network Connection +To quickly set up a complete network connection (DHCP, DNS, Gateway, etc.), use the following function: +`ES_FullConnection();` from the file: `EtherShield.c` +Call this fonction just before the while and ping your device ! + +and in your while think to use this for treat everything from your network : +` ES_ProcessWebPacket();` + +A lot of code was delete, few functions were added, and a lot of AI comment was generated. If any function deseaper please create a Issue + + +# How to use this project + +Copy it into your project and set up your project to be built with this library. +copy all files /inc and /src respectibly to the folder. + +Main.c and Main.h examples should be present. Shared your files and create folder for each MCU for help the community. + +About CMake, see the section Cmake below. + +## Expected code from STM32cubeIDE configuration + +You have to configure via STM32cubeIDE your SPI, automaticaly this code should be create in your main.c + +`void ENC28J60_hspi->Instance_Configuration(void) +{ + SPI_InitTypeDef SPI_InitStructure; + + /* Enable the SPI periph */ + RCC_APB2PeriphClockCmd(RCC_APB2Periph_hspi->Instance, ENABLE); + + /* SPI configuration -------------------------------------------------------*/ + SPI_I2S_DeInit(hspi->Instance); + SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; + SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; + SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; + SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; + SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; + SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_64; + SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; + SPI_InitStructure.SPI_CRCPolynomial = 7; + SPI_InitStructure.SPI_Mode = SPI_Mode_Master; + SPI_Init(hspi->Instance, &SPI_InitStructure); + + /* Enable hspi->Instance */ + SPI_Cmd(hspi->Instance, ENABLE); +}` + +Suggestion of GPIO Configuration + +`void ENC28J60_GPIO_Configuration(void) +{ + GPIO_InitTypeDef GPIO_InitStructure; + + /* Enable SCK, MOSI and MISO GPIO clocks */ + RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); + + /* Enable CS GPIO clock */ + RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); + + GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_hspi->Instance); + GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_hspi->Instance); + GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_hspi->Instance); + + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_25MHz; + + /* SPI SCK pin configuration */ + GPIO_InitStructure.GPIO_Pin = GPIO_PIN_3; + GPIO_Init(GPIOB, &GPIO_InitStructure); + + /* SPI MOSI pin configuration */ + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; + GPIO_Init(GPIOA, &GPIO_InitStructure); + + /* SPI MISO pin configuration */ + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; + GPIO_Init(GPIOA, &GPIO_InitStructure); + + /* Configure GPIO PIN for Chip select */ + GPIO_InitStructure.GPIO_Pin = GPIO_PIN_4; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_Init(GPIOA, &GPIO_InitStructure); + + /* Deselect : Chip Select high */ + GPIO_SetBits(GPIOA, GPIO_PIN_4); +}` + + +### FILE [`inc/define.h`](inc/defines.h) Hardware SECTION + +If you need to use another STM32 MCU then don't forget to edit file in Hardware section. + + +### FILE [`inc/define.h`](inc/defines.h) Parameters SECTION + +Chose what you want, depend on your needs. + +### FILE [`inc/define.h`](inc/defines.h) CS SECTION +Very important part if you choose CS_Only, you have to specify this correcty. + +### About CMake +Use the [stm32-cmake](https://github.com/ObKo/stm32-cmake) (see the next section). ## CMake @@ -26,3 +150,37 @@ To make it work for your MCU family and model you must change `MCU_FAMILY` and ` * [dc-thermal-logger](https://github.com/mephi-ut/dc-thermal-logger/blob/master/collector/firmware/Src/main.c) * [STM32_Devel](https://github.com/mephi-ut/STM32_Devel) * [nucleo-f091rc-rs232-enc28j60](https://github.com/mephi-ut/nucleo-f091rc-rs232-enc28j60/blob/master/Src/main.c) + +# License +Mainpage Arduino ENC28J60 EtherShield Library +## section Introduction +This library is derived from original code by Guido Socher and Pascal Stang, and hence licensed as GPL2. See http://www.gnu.org/licenses/gpl.html +It comprises a C++ class wrapper and a number of C files. It still follows pretty much the same structure as the original code that it was based on. +The Arduino EtherShield Library was initially created by Xing Yu of Nuelectronics, http://www.nuelectronics.com/estore/index.php?main_page=product_info&cPath=1&products_id=4 +The library was heavily modified and improved by Andrew D. Lindsay (http://blog.thiseldo.co.uk) with extra code from the Tuxgraphics.org ethernet library (http://www.tuxgraphics.org/electronics/200905/embedded-tcp-ip-stack.shtml), which also originated from the Pascal Stang code. +Further additions include the DHCP implementation with some assistance from JCW at http://jeelabs.org which is now used in their own version of the library for their EtherCard at http://jeelabs.net/projects/hardware/wiki/Ether_Card. +The library is now being used successfully with the Nanode, as minimal Ethernet connected Arduino compatible board, details available from http://wiki.london.hackspace.org.uk/view/Project:Nanode + +## section Download +Download the latest library and examples from https://github.com/thiseldo/EtherShield +To fully utilise the Nanode board, you will also require a library that can access the onboard MAC address chip. +One such library is available from https://github.com/thiseldo/NanodeMAC and is used in the examples provided with this library. + +## section Instalation +The library .zip file downloaded from https://github.com/thiseldo/EtherShield should be renamed to EtherShield.zip or EtherShield.tar.gz depending on the archive file you're downloading. +The file should be extracted to the sketchbook/libraries/ folder so that there is a subdirectory called EtherSheild containing all the files from the archive. + +## section License + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + A copy of the GNU Lesser General Public + License is available from http://www.gnu.org/licenses/gpl.html; or write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA \ No newline at end of file diff --git a/src/EtherShield.c b/Src/EtherShield.c similarity index 58% rename from src/EtherShield.c rename to Src/EtherShield.c index 38d7d6b..38ca795 100644 --- a/src/EtherShield.c +++ b/Src/EtherShield.c @@ -1,64 +1,114 @@ -/*! \mainpage Arduino ENC28J60 EtherShield Library +/** + * @file EtherShield.c + * @brief Implementation of functions for establishing a full connection using the EtherShield + * library with the ENC28J60 Ethernet module. + * + * This file contains functions to initialize the connection, allocate IP addresses via DHCP, + * resolve DNS hostnames, and request NTP time updates. It also processes incoming packets + * for ICMP and TCP protocols. + * + * @note For more information, refer to the `license.md` file located at the root of the project. + */ -\section Introduction +#include "EtherShield.h" -This library is derived from original code by Guido Socher and Pascal Stang, and hence licensed as GPL2. See http://www.gnu.org/licenses/gpl.html +#define BUFFER_SIZE 400 -It comprises a C++ class wrapper and a number of C files. It still follows pretty much the same structure as the original code that it was based on. +#define DHCP_BUFFER_SIZE 300 +#define DNS_BUFFER_SIZE 200 +#define NTP_BUFFER_SIZE 150 -The Arduino EtherShield Library was initially created by Xing Yu of Nuelectronics, http://www.nuelectronics.com/estore/index.php?main_page=product_info&cPath=1&products_id=4 +uint8_t bufDHCP[DHCP_BUFFER_SIZE]; +uint8_t bufDNS[DNS_BUFFER_SIZE]; +uint8_t bufNTP[NTP_BUFFER_SIZE]; -The library was heavily modified and improved by Andrew D. Lindsay (http://blog.thiseldo.co.uk) with extra code from the Tuxgraphics.org ethernet library (http://www.tuxgraphics.org/electronics/200905/embedded-tcp-ip-stack.shtml), which also originated from the Pascal Stang code. +uint8_t hostname[] = "www.google.com"; -Further additions include the DHCP implementation with some assistance from JCW at http://jeelabs.org which is now used in their own version of the library for their EtherCard at http://jeelabs.net/projects/hardware/wiki/Ether_Card. +// Function to initialize and establish a full connection +void ES_FullConnection(SPI_HandleTypeDef *hspi) { + // Set the SPI handle for the ENC28J60 + enc28j60_set_spi(hspi); -The library is now being used successfully with the Nanode, as minimal Ethernet connected Arduino compatible board, details available from http://wiki.london.hackspace.org.uk/view/Project:Nanode + // Initialize the ENC28J60 with the MAC address + enc28j60Init(macaddrin); -\section Download + // Configure the ENC28J60 clock + enc28j60clkout(3); -Download the latest library and examples from https://github.com/thiseldo/EtherShield + // Configure the ENC28J60 PHY LEDs + enc28j60PhyWrite(PHLCON, 0x3880); + enc28j60PhyWrite(PHLCON, 0x3990); + enc28j60PhyWrite(PHLCON, 0x3476); -To fully utilise the Nanode board, you will also require a library that can access the onboard MAC address chip. -One such library is available from https://github.com/thiseldo/NanodeMAC and is used in the examples provided with this library. + // Allocate IP address via DHCP + allocateIPAddress(bufDHCP, BUFFER_SIZE, macaddrin, 80, ipaddrin, maskin, gwipin, dnssvrin, dhcpsvrin); -\section Instalation + // Set the gateway IP for the client + client_set_gwip(gwipin); -The library .zip file downloaded from https://github.com/thiseldo/EtherShield should be renamed to EtherShield.zip or EtherShield.tar.gz depending on the archive file you're downloading. + // Resolve the DNS hostname + dnslkup_request(bufDNS, domainName); + udp_client_check_for_dns_answer(bufDNS, plen); + dnslkup_set_dnsip(dnsipaddr); + resolveHostname(bufDNS, BUFFER_SIZE, hostname); -The file should be extracted to the sketchbook/libraries/ folder so that there is a subdirectory called EtherSheild containing all the files from the archive. + // Send NTP request to get the current time + client_ntp_request(bufNTP, ntpip, 123); -\section License - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + // Log the success of the full connection setup + udpLog2("ES_FullConnection", "WORKS!"); - A copy of the GNU Lesser General Public - License is available from http://www.gnu.org/licenses/gpl.html; or write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +} - */ +// Function to initialize and establish a full connection +void ES_RenewCo() { + // Allocate IP address via DHCP + allocateIPAddress(bufDHCP, BUFFER_SIZE, macaddrin, 80, ipaddrin, maskin, gwipin, dnssvrin, dhcpsvrin); -#include "enc28j60.h" -#include "ip_arp_udp_tcp.h" -#include "websrv_help_functions.h" + // Set the gateway IP for the client + client_set_gwip(gwipin); -#ifdef DNS_client - #include "dnslkup.h" -#endif + // Resolve the DNS hostname + dnslkup_request(bufDNS, domainName); + udp_client_check_for_dns_answer(bufDNS, plen); + dnslkup_set_dnsip(dnsipaddr); + resolveHostname(bufDNS, BUFFER_SIZE, hostname); -#ifdef DHCP_client - #include "dhcp.h" -#endif + // Send NTP request to get the current time + client_ntp_request(bufNTP, ntpip, 123); + + // Log the success of the full connection setup + udpLog2("ES_RenewCo", "WORKS!"); +} + +// Function to process incoming web packets +void ES_ProcessWebPacket() { + static uint8_t packet[500]; + handle_AllPacket(packet, enc28j60PacketReceive(500, packet)); +} + +// Function to process incoming web packets +void ES_ProcessWebPacketFilter() { +// enc28j60SetBank(ERXFCON); +// write_control_register(0x18, 0b10100001); +// enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, EIE, EIE_INTIE|EIE_PKTIE); +// enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, EIR, EIR_PKTIF); + static uint8_t packet[500]; + uint16_t len = enc28j60PacketReceive(500, packet); + if (len > 0) { + handle_AllPacket(packet, len); + } + handle_AllPacket(packet, enc28j60PacketReceive(500, packet)); +} -#include "EtherShield.h" +void ES_init_network() { + init_ip_arp_udp_tcp(macaddrin, ipaddrin, 80); + initialize_tcp_sequence_number(); + init_tcp_connections(); +} /** * Initialise SPI, separate from main initialisation so that * multiple SPI devices can be used together @@ -68,7 +118,6 @@ void ES_enc28j60SpiInit(SPI_HandleTypeDef *hspi){ enc28j60_set_spi(hspi); } - /** * Initialise the ENC28J60 using default chip select pin * Flash the 2 MagJack LEDs @@ -228,10 +277,6 @@ uint16_t ES_fill_tcp_data(uint8_t *buf,uint16_t pos, const char *s){ return fill_tcp_data(buf,pos, s); } -uint16_t ES_fill_tcp_data_len(uint8_t *buf,uint16_t pos, const char *s, uint16_t len ){ - return fill_tcp_data_len(buf,pos, s, len); -} - void ES_www_server_reply(uint8_t *buf,uint16_t dlen) { www_server_reply(buf,dlen); } @@ -294,10 +339,6 @@ void ES_client_http_post(char *urlbuf, char *hoststr, char *additionalheaderline void ES_client_ntp_request(uint8_t *buf,uint8_t *ntpip,uint8_t srcport) { client_ntp_request(buf,ntpip,srcport); } - -uint8_t ES_client_ntp_process_answer(uint8_t *buf,uint32_t *time,uint8_t dstport_l) { - return client_ntp_process_answer(buf,time,dstport_l); -} #endif // NTP_client void ES_register_ping_rec_callback(void (*callback)(uint8_t *srcip)) { @@ -314,11 +355,9 @@ uint8_t ES_packetloop_icmp_checkreply(uint8_t *buf,uint8_t *ip_monitoredhost) { } #endif // PING_client -#ifdef WOL_client void ES_send_wol(uint8_t *buf,uint8_t *wolmac) { send_wol(buf,wolmac); } -#endif // WOL_client #ifdef FROMDECODE_websrv_help @@ -377,65 +416,6 @@ void ES_dnslkup_request(uint8_t *buf,uint8_t *hostname) { uint8_t ES_udp_client_check_for_dns_answer(uint8_t *buf,uint16_t plen){ return( udp_client_check_for_dns_answer( buf, plen) ); } - - -// Perform all processing to resolve a hostname to IP address. -// Returns 1 for successful Name resolution, 0 otherwise -uint8_t resolveHostname(uint8_t *buf, uint16_t buffer_size, uint8_t *hostname ) { - uint16_t dat_p; - int plen = 0; - long lastDnsRequest = HAL_GetTick(); - uint8_t dns_state = DNS_STATE_INIT; - bool gotAddress = FALSE; - uint8_t dnsTries = 3; // After 10 attempts fail gracefully so other action can be carried out - - while( !gotAddress ) { - // handle ping and wait for a tcp packet - plen = enc28j60PacketReceive(buffer_size, buf); - dat_p=packetloop_icmp_tcp(buf,plen); - - // We have a packet - // Check if IP data - if (dat_p == 0) { - if (client_waiting_gw() ) { - // No ARP received for gateway - continue; - } - // It has IP data - if (dns_state==DNS_STATE_INIT) { - dns_state=DNS_STATE_REQUESTED; - lastDnsRequest = HAL_GetTick(); - dnslkup_request(buf,hostname); - continue; - } - if (dns_state!=DNS_STATE_ANSWER){ - // retry every minute if dns-lookup failed: - if (HAL_GetTick() > (lastDnsRequest + 60000L) ){ - if( --dnsTries <= 0 ) - return 0; // Failed to allocate address - - dns_state=DNS_STATE_INIT; - lastDnsRequest = HAL_GetTick(); - } - // don't try to use client before - // we have a result of dns-lookup - - continue; - } - } - else { - if (dns_state==DNS_STATE_REQUESTED && udp_client_check_for_dns_answer( buf, plen ) ){ - dns_state=DNS_STATE_ANSWER; - //client_set_wwwip(dnslkup_getip()); - client_tcp_set_serverip(dnslkup_getip()); - gotAddress = TRUE; - } - } - } - - return 1; -} - #endif // DNS_client #ifdef DHCP_client @@ -451,61 +431,6 @@ uint8_t ES_dhcp_state(void) uint8_t ES_check_for_dhcp_answer(uint8_t *buf,uint16_t plen){ return( check_for_dhcp_answer( buf, plen) ); } - -// Utility functions - -// Perform all processing to get an IP address plus other addresses returned, e.g. gw, dns, dhcp server. -// Returns 1 for successful IP address allocation, 0 otherwise -uint8_t allocateIPAddress(uint8_t *buf, uint16_t buffer_size, uint8_t *mymac, uint16_t myport, uint8_t *myip, uint8_t *mynetmask, uint8_t *gwip, uint8_t *dnsip, uint8_t *dhcpsvrip ) { - uint16_t dat_p; - int plen = 0; - long lastDhcpRequest = HAL_GetTick(); - uint8_t dhcpState = 0; - bool gotIp = FALSE; - uint8_t dhcpTries = 10; // After 10 attempts fail gracefully so other action can be carried out - - dhcp_start( buf, mymac, myip, mynetmask,gwip, dnsip, dhcpsvrip ); - - while( !gotIp ) { - // handle ping and wait for a tcp packet - plen = enc28j60PacketReceive(buffer_size, buf); - dat_p=packetloop_icmp_tcp(buf,plen); - if(dat_p==0) { - check_for_dhcp_answer( buf, plen); - dhcpState = dhcp_state(); - // we are idle here - if( dhcpState != DHCP_STATE_OK ) { - if (HAL_GetTick() > (lastDhcpRequest + 10000L) ){ - lastDhcpRequest = HAL_GetTick(); - if( --dhcpTries <= 0 ) - return 0; // Failed to allocate address - // send dhcp - dhcp_start( buf, mymac, myip, mynetmask,gwip, dnsip, dhcpsvrip ); - } - } else { - if( !gotIp ) { - gotIp = TRUE; - - //init the ethernet/ip layer: - init_ip_arp_udp_tcp(mymac, myip, myport); - - // Set the Router IP - client_set_gwip(gwip); // e.g internal IP of dsl router - -#ifdef DNS_client - // Set the DNS server IP address if required, or use default - dnslkup_set_dnsip( dnsip ); -#endif - - } - } - } - } - - return 1; - -} - #endif // DHCP_client void ES_enc28j60PowerUp(){ diff --git a/Src/LogManager.c b/Src/LogManager.c new file mode 100644 index 0000000..fe7c1aa --- /dev/null +++ b/Src/LogManager.c @@ -0,0 +1,84 @@ +/* + * @file logmanager.c + * @brief Source file + * + * This source file implements the Log Manager module, which handles logging operations + * for different types of logs such as UDP, Web, and UART. It includes functions to log + * messages, initialize the log manager, and store logs in a circular buffer. + */ + +#include "LogManager.h" +#include "EtherShield.h" // Assuming this is where udplog2 is declared + + +#if ETHERSHIELD_DEBUG +void ethershieldDebug(char *message) { + printf("%s\r\n", message); + udpLog2("ETHERSHIELD_DEBUG", message); +} +#endif + +void udpLog2(char* alerte, char* text) { + static uint8_t udp[500]; + char data[450]; + snprintf(data, 450, "Project %s - %s", alerte, text); + send_udp(udp, data, strlen(data), sport, dip, dport); +} + +#define MAX_LOGS 50 +#define LOG_LENGTH 80 + +char logs[MAX_LOGS][LOG_LENGTH]; +int log_index = 0; + +// Fonction pour ajouter un log +void add_log(const char *log) { + strncpy(logs[log_index], log, LOG_LENGTH); + log_index = (log_index + 1) % MAX_LOGS; +} + +// Fonction pour récupérer les logs en format HTML +//void get_logs(char *buffer, size_t size) { +// size_t offset = 0; +// for (int i = 0; i < MAX_LOGS; i++) { +// int index = (log_index + i) % MAX_LOGS; +// offset += snprintf(buffer + offset, size - offset, "%s\n", logs[index]); +// if (offset >= size) break; +// } +//} + +// Function to log messages based on log type +void logMessage(LogType type, const char *message) { + switch (type) { + case LOG_UDP: + udplog2("Log", message); + break; + case LOG_WEB: + // Implement web logging + break; + case LOG_UART: + // Implement UART logging + break; + // Add more cases as needed + default: + break; + } +} + +// Function to initialize the log manager for a specific log type +void initLogManager(LogType type) { + switch (type) { + case LOG_UDP: + // Initialize UDP logging if needed + break; + case LOG_WEB: + // Initialize web logging if needed + break; + case LOG_UART: + // Initialize UART logging if needed + break; + // Add more cases as needed + default: + break; + } +} diff --git a/Src/RTC.c b/Src/RTC.c new file mode 100644 index 0000000..f6ba394 --- /dev/null +++ b/Src/RTC.c @@ -0,0 +1,43 @@ +/* + * @file RTC.c + * @brief Source file + * + * + * This file contains the implementation for interacting with the Real-Time Clock (RTC) + * on the STM32 microcontroller. It provides a function to obtain the current date and + * time from the RTC and formats it into a human-readable string for logging purposes. + */ + +#include "RTC.h" + +// Function to obtain the current date and time from the RTC +DateTime getRTCDateTime() { + DateTime currentDateTime; + RTC_TimeTypeDef sTime; + RTC_DateTypeDef sDate; + + // Get the current time and date + HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN); + HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN); // Necessary to unlock the time registers + + // Fill the DateTime structure + currentDateTime.hours = sTime.Hours; + currentDateTime.minutes = sTime.Minutes; + currentDateTime.seconds = sTime.Seconds; + currentDateTime.day = sDate.Date; + currentDateTime.month = sDate.Month; + currentDateTime.year = sDate.Year + 1970; // Convert to full year + + // Format the date and time into a string + char timeStr[32]; // Adjusted size to hold formatted date and time + sprintf(timeStr, "%02d/%02d/%04d %02d:%02d:%02d", + currentDateTime.day, + currentDateTime.month, + currentDateTime.year, + currentDateTime.hours, + currentDateTime.minutes, + currentDateTime.seconds); + udpLog2("DateTime_RTC", timeStr); + + return currentDateTime; // Return the filled DateTime structure +} diff --git a/Src/defines.c b/Src/defines.c new file mode 100644 index 0000000..25034ba --- /dev/null +++ b/Src/defines.c @@ -0,0 +1,52 @@ +/** + * @file defines.c + * @brief Source file containing the implementation of global variables and command functions for the STM32 and ENC28J60 Ethernet module. + * + * This file defines the network configuration variables such as MAC address, IP address, subnet mask, + * gateway, and DNS server addresses. It also includes the implementation of command functions for + * interacting with the ENC28J60 and RTC modules. + * + * @note For more information, refer to the `license.md` file located at the root of the project. + */ + + +#include "defines.h" + +uint8_t macaddrin[6] = {00, 00, 00, 00, 00, 01}; // MAC address of ENC28J60 +uint8_t ipaddrin[4] = {192, 168, 0, 100}; // IP address, prioritize DHCP server assignment +uint8_t maskin[4] = {255, 255, 255, 0}; // Subnet mask +uint8_t gwipin[4] = {192, 168, 0, 1}; // Default gateway +uint8_t dhcpsvrin[4] = {192, 168, 0, 1}; // DHCP server +uint8_t dnssvrin[4] = {192, 168, 0, 1}; // DNS server confirmed by DHCP +uint8_t dnsipaddr[4] = {192, 168, 0, 1}; // DNS server confirmed by DHCP +uint8_t ntpip[4] = {192, 168, 0, 1}; // NTP server IP address +char domainName[] = "www.google.com"; +uint8_t dest_ip[4] = {192, 168, 0, 254}; // Destination IP address, corresponding to a syslog server running on Debian (syslog-ng) +uint16_t dest_port = 10001; // Destination port +uint16_t srcport = 10002; // Source port +uint8_t dstport_h = 0x27; // High byte of port 10001 +uint8_t dstport_l = 0x11; // Low byte of port 10001 +uint8_t dip[] = {192, 168, 0, 254}; // Destination IP address, corresponding to a syslog server running on Debian (syslog-ng) +uint16_t dport = 514; // UDP port of the syslog server running on Debian (syslog-ng) +uint16_t sport = 8; // Source port + + + +// Define the command table +CommandMapping commandTable[] = { + {"sendSTM32State", sendSTM32State, NULL}, + {"sendRTCDateTime", sendRTCDateTime, NULL}, + {NULL, NULL, NULL} // End marker +}; + + +// Implement the command functions +void sendSTM32State() { + //enc28j60getrev(void); +} + +void sendRTCDateTime() { + getRTCDateTime(); +} + + diff --git a/Src/dhcp.c b/Src/dhcp.c new file mode 100644 index 0000000..5d3aa43 --- /dev/null +++ b/Src/dhcp.c @@ -0,0 +1,367 @@ +/** + * @file dhcp.c + * @brief Source file implementing DHCP client functionality for IP address allocation and network configuration. + * + * This file contains the implementation of the DHCP client, responsible for obtaining an IP address, + * subnet mask, gateway, and DNS server information from a DHCP server. It includes functions for + * sending DHCP requests, processing DHCP responses, and managing the DHCP lease. + * + * @note For more information, refer to the `license.md` file located at the root of the project. + */ + +#include "dhcp.h" +#include "net.h" + +#define DHCP_BOOTREQUEST 1 +#define DHCP_BOOTREPLY 2 +#define DHCPDISCOVER 0x01 +#define DHCPOFFER 0x02 +#define DHCPREQUEST 0x03 +#define DHCPACK 0x05 +// #define DHCPNACK // Uncomment if DHCPNACK is needed + +// Source port high byte must be a0 or higher: +#define DHCPCLIENT_SRC_PORT_H 0xe0 +#define DHCP_SRC_PORT 67 +#define DHCP_DEST_PORT 68 + +// DHCP data structure (size 236 bytes) +typedef struct dhcpData { + uint8_t op; // 0-3: op, htype, hlen, hops + uint8_t htype; + uint8_t hlen; + uint8_t hops; + uint32_t xid; // 4-7: xid + uint16_t secs; // 8-9: secs + uint16_t flags; // 10-11: flags + uint8_t ciaddr[4]; // 12-15: ciaddr + uint8_t yiaddr[4]; // 16-19: yiaddr + uint8_t siaddr[4]; // 20-23: siaddr + uint8_t giaddr[4]; // 24-27: giaddr + uint8_t chaddr[16]; // 28-43: chaddr + uint8_t sname[64]; // 44-107: sname + uint8_t file[128]; // 108-235: file +} __attribute__((packed)) dhcpData; + +// Static variables for DHCP +static uint8_t dhcptid_l = 0; // Counter for transaction ID +static uint8_t dhcpState = DHCP_STATE_INIT; // DHCP state + +// Pointers to values we have set or need to set +static uint8_t *macaddrDHCP; // MAC address pointer +uint8_t *dhcpip; // DHCP IP address pointer +uint8_t *dhcpmask; // DHCP subnet mask pointer +uint8_t *dhcpserver; // DHCP server address pointer +uint8_t *dnsserver; // DNS server address pointer +uint8_t *gwaddr; // Gateway address pointer + +// Hostname definitions +#define HOSTNAME_LEN DHCP_HOSTNAME_LEN +#define HOSTNAME_SIZE (HOSTNAME_LEN + 3) // Length + 3 for additional characters + +static char hostname[HOSTNAME_SIZE]; // Hostname buffer + +// DHCP response variables +static uint8_t haveDhcpAnswer = 0; // Flag for DHCP answer +static uint8_t dhcp_ansError = 0; // DHCP answer error flag +uint32_t currentXid = 0; // Current transaction ID +uint16_t currentSecs = 0; // Current seconds since DHCP start +static uint32_t leaseStart = 0; // Lease start time +static uint32_t leaseTime = 0; // Lease duration +static uint8_t *bufPtr; // Buffer pointer + +static void addToBuf(uint8_t b) { + *bufPtr++ = b; +} + +uint8_t dhcp_state(void) { + // Check lease and request renew if currently OK and time + // leaseStart - start time in millis + // leaseTime - length of lease in millis + if (dhcpState == DHCP_STATE_OK && (leaseStart + leaseTime) <= HAL_GetTick()) { + // Calling app needs to detect this and initiate renewal + dhcpState = DHCP_STATE_RENEW; + } + return dhcpState; +} + +// Start request sequence, send DHCPDISCOVER +// Wait for DHCPOFFER +// Send DHCPREQUEST +// Wait for DHCPACK +// All configured +void dhcp_start(uint8_t *buf, uint8_t *macaddrDHCPin, uint8_t *ipaddrin, uint8_t *maskin, + uint8_t *gwipin, uint8_t *dhcpsvrin, uint8_t *dnssvrin) { + macaddrDHCP = macaddrDHCPin; + dhcpip = ipaddrin; + dhcpmask = maskin; + gwaddr = gwipin; + dhcpserver = dhcpsvrin; + dnsserver = dnssvrin; + + // Initialize random seed and transaction ID + srand(0x13); + currentXid = 0x00654321 + rand(); + currentSecs = 0; + + // Clear IP, mask, gateway, DHCP server, and DNS server addresses + memset(dhcpip, 0, 4); + memset(dhcpmask, 0, 4); + memset(gwaddr, 0, 4); + memset(dhcpserver, 0, 4); + memset(dnsserver, 0, 4); + + // Set a unique hostname, use DHCP_HOSTNAME- plus last octet of MAC address + strncpy(hostname, DHCP_HOSTNAME, HOSTNAME_LEN); + hostname[HOSTNAME_LEN] = '-'; + hostname[HOSTNAME_LEN + 1] = 'A' + (macaddrDHCP[5] >> 4); + hostname[HOSTNAME_LEN + 2] = 'A' + (macaddrDHCP[5] & 0x0F); + hostname[HOSTNAME_LEN + 3] = '\0'; + + // Enable reception of broadcast packets + enc28j60EnableBroadcast(); + + // Send DHCPDISCOVER packet + dhcp_send(buf, DHCPDISCOVER); + dhcpState = DHCP_STATE_DISCOVER; +} + +void dhcp_request_ip(uint8_t *buf) { + // Send DHCPREQUEST packet + dhcp_send(buf, DHCPREQUEST); + + // Update the DHCP state to REQUEST + dhcpState = DHCP_STATE_REQUEST; +} + +// Main DHCP message sending function, either DHCPDISCOVER or DHCPREQUEST +void dhcp_send(uint8_t *buf, uint8_t requestType) { + haveDhcpAnswer = 0; + dhcp_ansError = 0; + dhcptid_l++; // Increment for next request, finally wrap + + // Clear the buffer + memset(buf, 0, 400); + + // Prepare the UDP packet + send_udp_prepare(buf, (DHCPCLIENT_SRC_PORT_H << 8) | (dhcptid_l & 0xff), dhcpip, DHCP_DEST_PORT); + + // Set Ethernet source and destination MAC addresses + memcpy(buf + ETH_SRC_MAC, macaddrDHCP, 6); + memset(buf + ETH_DST_MAC, 0xFF, 6); + + // Set IP total length, protocol, and destination + buf[IP_TOTLEN_L_P] = 0x82; + buf[IP_PROTO_P] = IP_PROTO_UDP_V; + memset(buf + IP_DST_IP_P, 0xFF, 4); + + // Set UDP source and destination ports + buf[UDP_DST_PORT_L_P] = DHCP_SRC_PORT; + buf[UDP_SRC_PORT_H_P] = 0; + buf[UDP_SRC_PORT_L_P] = DHCP_DEST_PORT; + + // Build DHCP packet from buf[UDP_DATA_P] + dhcpData *dhcpPtr = (dhcpData *)&buf[UDP_DATA_P]; + + // 0-3: op, htype, hlen, hops + dhcpPtr->op = DHCP_BOOTREQUEST; + dhcpPtr->htype = 1; + dhcpPtr->hlen = 6; + dhcpPtr->hops = 0; + + // 4-7: xid + memcpy(&dhcpPtr->xid, ¤tXid, sizeof(currentXid)); + + // 8-9: secs + memcpy(&dhcpPtr->secs, ¤tSecs, sizeof(currentSecs)); + + // 16-19: yiaddr + memset(dhcpPtr->yiaddr, 0, 4); + + // 28-43: chaddr (16) + memcpy(dhcpPtr->chaddr, macaddrDHCP, 6); + + // Options defined as option, length, value + bufPtr = buf + UDP_DATA_P + sizeof(dhcpData); + + // Magic cookie: 99, 130, 83, 99 + const uint8_t magicCookie[] = {99, 130, 83, 99}; + memcpy(bufPtr, magicCookie, sizeof(magicCookie)); + bufPtr += sizeof(magicCookie); + + // Option 1 - DHCP message type + addToBuf(53); // Option: DHCP Message Type + addToBuf(1); // Length: 1 + addToBuf(requestType); // Value: DHCPDISCOVER or DHCPREQUEST + + // Client Identifier Option: client MAC address + addToBuf(61); // Option: Client Identifier + addToBuf(7); // Length: 7 + addToBuf(0x01); // Hardware type: Ethernet + for (int i = 0; i < 6; i++) addToBuf(macaddrDHCP[i]); + + // Host name Option + addToBuf(12); // Option: Host Name + addToBuf(HOSTNAME_SIZE); // Length + for (int i = 0; i < HOSTNAME_SIZE; i++) addToBuf(hostname[i]); + + if (requestType == DHCPREQUEST) { + // Request IP address + addToBuf(50); // Option: Requested IP Address + addToBuf(4); // Length: 4 + for (int i = 0; i < 4; i++) addToBuf(dhcpip[i]); + + // Request using server IP address + addToBuf(54); // Option: Server Identifier + addToBuf(4); // Length: 4 + for (int i = 0; i < 4; i++) addToBuf(dhcpserver[i]); + } + + // Additional information in parameter request list + addToBuf(55); // Option: Parameter Request List + addToBuf(3); // Length: 3 + addToBuf(1); // Parameter: Subnet Mask + addToBuf(3); // Parameter: Router + addToBuf(6); // Parameter: DNS Server + + // End option + addToBuf(255); + + // Transmit the UDP packet + send_udp_transmit(buf, bufPtr - buf - UDP_DATA_P); +} + +// Examine packet, if DHCP then process, otherwise exit. +// Perform lease expiry checks and initiate renewal. +// Process the answer from the DHCP server: +// Return 1 on successful processing of the answer. +// We also set the variable haveDhcpAnswer to 1 if we process an answer. +// Either DHCPOFFER, DHCPACK, or DHCPNACK. +// Return 0 for nothing processed, 1 for something processed. +uint8_t check_for_dhcp_answer(uint8_t *buf, uint16_t plen) { + // Map struct onto payload + dhcpData *dhcpPtr = (dhcpData *)&buf[UDP_DATA_P]; + + // Check if the packet is a DHCP reply with the correct transaction ID + if (plen >= 70 && + buf[UDP_SRC_PORT_L_P] == DHCP_SRC_PORT && + dhcpPtr->op == DHCP_BOOTREPLY && + !memcmp(&dhcpPtr->xid, ¤tXid, sizeof(currentXid))) { + + // Check for DHCP message type option + int optionIndex = UDP_DATA_P + sizeof(dhcpData) + 4; + if (buf[optionIndex] == 53) { // Option 53 is DHCP message type + uint8_t messageType = buf[optionIndex + 2]; + switch (messageType) { + case DHCPOFFER: + return have_dhcpoffer(buf, plen); + case DHCPACK: + return have_dhcpack(buf, plen); + // You can add more cases here if needed, e.g., DHCPNACK + } + } + } + + return 0; // No DHCP answer processed +} + +uint8_t have_dhcpoffer(uint8_t *buf, uint16_t plen) { + // Map struct onto payload + dhcpData *dhcpPtr = (dhcpData *)&buf[UDP_DATA_P]; + + // Offered IP address is in yiaddr + memcpy(dhcpip, dhcpPtr->yiaddr, 4); + + // Scan through variable length option list identifying options we want + uint8_t *ptr = (uint8_t *)(dhcpPtr + 1) + 4; // Move past fixed DHCP data and magic cookie + + while (ptr < buf + plen) { + uint8_t option = *ptr++; + uint8_t optionLen = *ptr++; + + switch (option) { + case 1: // Subnet Mask + memcpy(dhcpmask, ptr, 4); + break; + case 3: // Router/Gateway + memcpy(gwaddr, ptr, 4); + break; + case 6: // DNS Server + memcpy(dnsserver, ptr, 4); + break; + case 51: // IP Address Lease Time + leaseTime = 0; + for (uint8_t i = 0; i < 4; i++) { + leaseTime = (leaseTime << 8) + ptr[i]; + } + leaseTime *= 1000; // Convert to milliseconds + break; + case 54: // DHCP Server Identifier + memcpy(dhcpserver, ptr, 4); + break; + default: + break; + } + ptr += optionLen; + } + + dhcp_request_ip(buf); + return 1; +} + +uint8_t have_dhcpack(uint8_t *buf, uint16_t plen) { + // Set DHCP state to OK + dhcpState = DHCP_STATE_OK; + + // Record the lease start time + leaseStart = HAL_GetTick(); + + // Turn off broadcast. Application can re-enable if needed + enc28j60DisableBroadcast(); + + // Return 2 to indicate DHCPACK processed + return 2; +} + +uint8_t allocateIPAddress(uint8_t *buf, uint16_t buffer_size, uint8_t *mymac, uint16_t myport, uint8_t *myip, uint8_t *mynetmask, uint8_t *gwip, uint8_t *dnsip, uint8_t *dhcpsvrip) { + uint16_t dat_p; + int plen = 0; + long lastDhcpRequest = HAL_GetTick(); + uint8_t dhcpState = 0; + bool gotIp = false; + uint8_t dhcpTries = 10; + + dhcp_start(buf, mymac, myip, mynetmask, gwip, dnsip, dhcpsvrip); + + while (!gotIp) { + plen = enc28j60PacketReceive(buffer_size, buf); + dat_p = handle_AllPacket(buf, plen); + + if (dat_p == 0) { + check_for_dhcp_answer(buf, plen); + dhcpState = dhcp_state(); + + if (dhcpState != DHCP_STATE_OK) { + if (HAL_GetTick() > (lastDhcpRequest + 10000L)) { + lastDhcpRequest = HAL_GetTick(); + if (--dhcpTries <= 0) { + return 0; + } + dhcp_start(buf, mymac, myip, mynetmask, gwip, dnsip, dhcpsvrip); + } + } else if (!gotIp) { + gotIp = true; + + init_ip_arp_udp_tcp(mymac, myip, myport); + client_set_gwip(gwip); + +#ifdef DNS_client + dnslkup_set_dnsip(dnsip); +#endif + } + } + } + + return 1; +} +/* end of dhcp.c */ diff --git a/src/dnslkup.c b/Src/dnslkup.c similarity index 69% rename from src/dnslkup.c rename to Src/dnslkup.c index de6de16..2b3cc6e 100644 --- a/src/dnslkup.c +++ b/Src/dnslkup.c @@ -1,41 +1,35 @@ -/********************************************* - * vim:sw=8:ts=8:si:et - * To use the above modeline in vim you must have "set modeline" in your .vimrc +/** + * @file dnslkup.c + * @brief Source file implementing DNS lookup functionality for resolving hostnames to IP addresses. * - * Author: Guido Socher - * Copyright: GPL V2 - * See http://www.gnu.org/licenses/gpl.html + * This file contains the implementation of DNS client functionality, including sending DNS queries, + * processing DNS responses, and handling DNS errors. It provides functions for setting the DNS server, + * sending DNS requests, and resolving hostnames to IP addresses. * - * DNS look-up functions based on the udp client - * - *********************************************/ -#include -#include + * @note For more information, refer to the `license.md` file located at the root of the project. + */ + +#include "defines.h" #include "net.h" #include "ip_arp_udp_tcp.h" -#if defined (UDP_client) static uint8_t dnstid_l=0; // a counter for transaction ID // src port high byte must be a a0 or higher: -#define DNSCLIENT_SRC_PORT_H 0xe0 uint8_t dnsip[]={8,8,8,8}; // the google public DNS. To be used if DNS server is not set by user. static uint8_t haveDNSanswer=0; static uint8_t dns_answerip[4]; static uint8_t dns_ansError=0; -uint8_t dnslkup_haveanswer(void) -{ +uint8_t dnslkup_haveanswer(void) { return(haveDNSanswer); } -uint8_t dnslkup_get_error_info(void) -{ +uint8_t dnslkup_get_error_info(void) { return(dns_ansError); } -uint8_t *dnslkup_getip() -{ +uint8_t *dnslkup_getip() { return(dns_answerip); } @@ -44,8 +38,7 @@ uint8_t *dnslkup_getip() // and http://www.ietf.org/rfc/rfc1035.txt // //void dnslkup_request(uint8_t *buf,const prog_char *progmem_hostname) -void dnslkup_request(uint8_t *buf, uint8_t *hostname) -{ +void dnslkup_request(uint8_t *buf, uint8_t *domainName) { uint8_t i,lenpos,lencnt; char c; haveDNSanswer=0; @@ -73,7 +66,7 @@ void dnslkup_request(uint8_t *buf, uint8_t *hostname) i=13; lencnt=1; // while ((c = pgm_read_byte(progmem_hostname++))) { - while ((c = *hostname++)) { + while ((c = *domainName++)) { if (c=='.'){ buf[UDP_DATA_P+lenpos]=lencnt-1; lencnt=0; @@ -104,7 +97,7 @@ void dnslkup_request(uint8_t *buf, uint8_t *hostname) // process the answer from the dns server: // return 1 on sucessful processing of answer. // We set also the variable haveDNSanswer -uint8_t udp_client_check_for_dns_answer(uint8_t *buf,uint16_t plen){ +uint8_t udp_client_check_for_dns_answer(uint8_t *buf,uint16_t plen) { uint8_t j,i; if (plen<70){ return(0); @@ -185,8 +178,7 @@ uint8_t udp_client_check_for_dns_answer(uint8_t *buf,uint16_t plen){ // set DNS server to be used for lookups. // defaults to Google DNS server if not called. -void dnslkup_set_dnsip(uint8_t *dnsipaddr) -{ +void dnslkup_set_dnsip(uint8_t *dnsipaddr) { uint8_t i=0; while(i<4){ dnsip[i]=dnsipaddr[i]; @@ -194,7 +186,50 @@ void dnslkup_set_dnsip(uint8_t *dnsipaddr) } } -#endif +// Perform all processing to resolve a hostname to IP address. +// Returns 1 for successful name resolution, 0 otherwise +uint8_t resolveHostname(uint8_t *buf, uint16_t buffer_size, uint8_t *hostname) { + uint16_t dat_p; + int plen = 0; + long lastDnsRequest = HAL_GetTick(); + uint8_t dns_state = DNS_STATE_INIT; + bool gotAddress = false; + uint8_t dnsTries = 3; // After 3 attempts fail gracefully + + while (!gotAddress) { + plen = enc28j60PacketReceive(buffer_size, buf); + dat_p = handle_AllPacket(buf, plen); + + if (dat_p == 0) { + if (client_waiting_gw()) { + continue; + } -/* end of dnslkup.c */ + if (dns_state == DNS_STATE_INIT) { + dns_state = DNS_STATE_REQUESTED; + lastDnsRequest = HAL_GetTick(); + dnslkup_request(buf, hostname); + continue; + } + if (dns_state != DNS_STATE_ANSWER) { + if (HAL_GetTick() > (lastDnsRequest + 60000L)) { + if (--dnsTries <= 0) { + return 0; // Failed to resolve address + } + dns_state = DNS_STATE_INIT; + lastDnsRequest = HAL_GetTick(); + } + continue; + } + } else { + if (dns_state == DNS_STATE_REQUESTED && udp_client_check_for_dns_answer(buf, plen)) { + dns_state = DNS_STATE_ANSWER; + client_tcp_set_serverip(dnslkup_getip()); + gotAddress = true; + } + } + } + + return 1; +} diff --git a/Src/enc28j60.c b/Src/enc28j60.c new file mode 100644 index 0000000..398b6ad --- /dev/null +++ b/Src/enc28j60.c @@ -0,0 +1,417 @@ +/** + * @file ENC28J60.c + * @brief Source file containing the implementation for interfacing with the ENC28J60 Ethernet controller. + * + * This file includes functions for initializing the ENC28J60, handling SPI communication, + * reading and writing registers, managing buffers, and controlling power states. It also + * contains functions for sending and receiving Ethernet packets, as well as configuring + * various network settings. + * + * @note For more information, refer to the `license.md` file located at the root of the project. + */ + +#include "defines.h" +#include "enc28j60.h" +#include "error_handler.h" + +static uint8_t Enc28j60Bank; +static uint16_t gNextPacketPtr; +static uint8_t erxfcon; +static SPI_HandleTypeDef *hspi = NULL; + + + +void enc28j60_set_spi(SPI_HandleTypeDef *hspi_new) { + hspi = hspi_new; +} + +void error (float error_num, char infinite); + +unsigned char ENC28J60_SendByte(uint8_t tx) { + if (hspi == NULL) + return 0; + + uint8_t rx = 0; + int r; + + // Use a reasonable timeout value + const uint32_t timeout = 1000; // 1 second timeout + + r = HAL_SPI_TransmitReceive(hspi, &tx, &rx, 1, timeout); + + if (r != HAL_OK) { + ENC28j60_Error_Handler(SPI_ERROR); + return 0; // Return a default value in case of an error + } + + return rx; +} + +uint8_t enc28j60ReadOp(uint8_t op, uint8_t address) { + uint8_t temp; + + // Enable the chip (CS low) + enableChip; + + // Issue read command + ENC28J60_SendByte(op | (address & ADDR_MASK)); + + // Read the data + temp = ENC28J60_SendByte(0xFF); + + // If this is a MAC or MII register, read one more byte + if (address & 0x80) + temp = ENC28J60_SendByte(0xFF); + + // Disable the chip (CS high) + disableChip; + + return temp; +} + +void enc28j60WriteOp(uint8_t op, uint8_t address, uint8_t data) { + // Enable the chip (CS low) + enableChip; + + // Issue write command + ENC28J60_SendByte(op | (address & ADDR_MASK)); + + // Write the data + ENC28J60_SendByte(data); + + // Disable the chip (CS high) + disableChip; +} + +void enc28j60PowerDown() { + // Disable packet reception + enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_RXEN); + + // Wait for any ongoing receive operation to complete + while (enc28j60Read(ESTAT) & ESTAT_RXBUSY); + + // Wait for any ongoing transmission to complete + while (enc28j60Read(ECON1) & ECON1_TXRTS); + + // Enter power save mode + enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PWRSV); +} + +void enc28j60PowerUp() { + // Exit power save mode + enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, ECON2, ECON2_PWRSV); + + // Wait for the clock to become ready + while (!(enc28j60Read(ESTAT) & ESTAT_CLKRDY)); +} + +void enc28j60ReadBuffer(uint16_t len, uint8_t* data) { + // Enable the chip (CS low) + enableChip; + + // Issue read buffer memory command + ENC28J60_SendByte(ENC28J60_READ_BUF_MEM); + + // Read data from buffer + while (len--) { + *data++ = ENC28J60_SendByte(0x00); + } + + // Disable the chip (CS high) + disableChip; +} + +static uint16_t enc28j60ReadBufferWord() { + uint16_t result; + // Read 2 bytes from the buffer and store them in result + enc28j60ReadBuffer(2, (uint8_t*)&result); + return result; +} + +void enc28j60WriteBuffer(uint16_t len, uint8_t* data) { + // Enable the chip (CS low) + enableChip; + + // Issue write buffer memory command + ENC28J60_SendByte(ENC28J60_WRITE_BUF_MEM); + + // Write data to buffer + while (len--) { + ENC28J60_SendByte(*data++); + } + + // Disable the chip (CS high) + disableChip; +} + +void enc28j60SetBank(uint8_t address) { + uint8_t bank = address & BANK_MASK; + + if (bank != Enc28j60Bank) { + // Clear the bank select bits in ECON1 + enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_BSEL1 | ECON1_BSEL0); + + // Set the new bank + Enc28j60Bank = bank; + + // Set the bank select bits in ECON1 + enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, bank >> 5); + } +} + +uint8_t enc28j60Read(uint8_t address) { + // Set the bank for the given address + enc28j60SetBank(address); + + // Read the value from the specified register + return enc28j60ReadOp(ENC28J60_READ_CTRL_REG, address); +} + + +void enc28j60WriteWord(uint8_t address, uint16_t data) { + // Write the low byte to the specified address + enc28j60Write(address, data & 0xFF); + + // Write the high byte to the next address + enc28j60Write(address + 1, data >> 8); +} + +// Read upper 8 bits from PHY register +uint16_t enc28j60PhyReadH(uint8_t address) { + // Set the address and start the register read operation + enc28j60Write(MIREGADR, address); + enc28j60Write(MICMD, MICMD_MIIRD); + + // Wait for the read operation to complete + HAL_Delay(15); + + // Wait until the PHY read completes + while (enc28j60Read(MISTAT) & MISTAT_BUSY); + + // Reset the reading bit + enc28j60Write(MICMD, 0x00); + + // Return the read value + return enc28j60Read(MIRDH); +} + +void enc28j60Write(uint8_t address, uint8_t data) { + // Set the bank for the given address + enc28j60SetBank(address); + + // Write the data to the specified address + enc28j60WriteOp(ENC28J60_WRITE_CTRL_REG, address, data); +} + +void enc28j60PhyWrite(uint8_t address, uint16_t data) { + // Set the PHY register address + enc28j60Write(MIREGADR, address); + + // Write the low byte of PHY data + enc28j60Write(MIWRL, data & 0xFF); + + // Write the high byte of PHY data + enc28j60Write(MIWRH, data >> 8); + + // Wait until the PHY write completes + while (enc28j60Read(MISTAT) & MISTAT_BUSY) { + HAL_Delay(15); + } +} + +static void enc28j60PhyWriteWord(uint8_t address, uint16_t data) { + // Set the PHY register address + enc28j60Write(MIREGADR, address); + + // Write the PHY data + enc28j60WriteWord(MIWRL, data); + + // Wait until the PHY write completes, with a timeout + uint32_t startTick = HAL_GetTick(); + while (enc28j60Read(MISTAT) & MISTAT_BUSY) { + if ((HAL_GetTick() - startTick) > 100) { // 100ms timeout + // Handle timeout (e.g., log an error, reset the PHY, etc.) + return; + } + HAL_Delay(1); + } +} + +void enc28j60clkout(uint8_t clk) { + // Set the ECOCON register with the lower 3 bits of clk + // 0: Output clock disabled + // 1: 6.25 MHz + // 2: 12.5 MHz + // 3: 25 MHz + // 4-7: Reserved + enc28j60Write(ECOCON, clk & 0x07); +} + +void enc28j60Init(uint8_t* macaddr) { + // Enable the chip (CS low) + enableChip; + + // Perform system reset + enc28j60WriteOp(ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET); + HAL_Delay(50); + + // Initialize receive buffer + gNextPacketPtr = RXSTART_INIT; + enc28j60WriteWord(ERXSTL, RXSTART_INIT); // Rx start + enc28j60WriteWord(ERXRDPTL, RXSTART_INIT); // Set receive pointer address + enc28j60WriteWord(ERXNDL, RXSTOP_INIT); // Rx end + enc28j60WriteWord(ETXSTL, TXSTART_INIT); // Tx start + enc28j60WriteWord(ETXNDL, TXSTOP_INIT); // Tx end + + // Packet filter: Allow only ARP packets for broadcast, and unicast for our MAC + erxfcon = ERXFCON_UCEN | ERXFCON_CRCEN | ERXFCON_PMEN | ERXFCON_BCEN; + enc28j60Write(ERXFCON, erxfcon); + enc28j60WriteWord(EPMM0, 0x303f); + enc28j60WriteWord(EPMCSL, 0xf7f9); + + // Enable MAC receive + enc28j60Write(MACON1, MACON1_MARXEN | MACON1_TXPAUS | MACON1_RXPAUS); + // Bring MAC out of reset + enc28j60Write(MACON2, 0x00); + // Enable automatic padding to 60 bytes and CRC operations + enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, MACON3, MACON3_PADCFG0 | MACON3_TXCRCEN | MACON3_FRMLNEN); + + // Set inter-frame gap (non-back-to-back) + enc28j60WriteWord(MAIPGL, 0x0C12); + // Set inter-frame gap (back-to-back) + enc28j60Write(MABBIPG, 0x12); + + // Set the maximum packet size which the controller will accept + enc28j60WriteWord(MAMXFLL, MAX_FRAMELEN); + + // Write MAC address (note: MAC address in ENC28J60 is byte-backward) + enc28j60Write(MAADR5, macaddr[0]); + enc28j60Write(MAADR4, macaddr[1]); + enc28j60Write(MAADR3, macaddr[2]); + enc28j60Write(MAADR2, macaddr[3]); + enc28j60Write(MAADR1, macaddr[4]); + enc28j60Write(MAADR0, macaddr[5]); + + // No loopback of transmitted frames + enc28j60PhyWrite(PHCON2, PHCON2_HDLDIS); + + // Switch to bank 0 + enc28j60SetBank(ECON1); + + // Enable interrupts + enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, EIE, EIE_INTIE | EIE_PKTIE); + + // Enable packet reception + enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN); +} + +uint8_t enc28j60getrev(void) { + uint8_t rev = enc28j60Read(EREVID); + // Microchip forgot to step the number on the silicon when they released revision B7. + // 6 is now rev B7. If rev is greater than 5, increment it. + if (rev > 5) rev++; + return rev; +} + +void enc28j60EnableBroadcast(void) { + erxfcon |= ERXFCON_BCEN; + enc28j60Write(ERXFCON, erxfcon); +} + +void enc28j60DisableBroadcast(void) { + erxfcon &= ~ERXFCON_BCEN; + enc28j60Write(ERXFCON, erxfcon); +} + +void enc28j60EnableMulticast(void) { + erxfcon |= ERXFCON_MCEN; + enc28j60Write(ERXFCON, erxfcon); +} + +void enc28j60DisableMulticast(void) { + erxfcon &= ~ERXFCON_MCEN; + enc28j60Write(ERXFCON, erxfcon); +} + +uint8_t enc28j60linkup(void) { + // Check bit 3 in the upper PHSTAT2 register (bit 10 overall) + return (enc28j60PhyReadH(PHSTAT2) & 0x04) ? 1 : 0; +} + +void enc28j60PacketSend(uint16_t len, uint8_t* packet) { + // Check no transmit in progress + while (enc28j60ReadOp(ENC28J60_READ_CTRL_REG, ECON1) & ECON1_TXRTS) { + // Reset the transmit logic problem. See Rev. B4 Silicon Errata point 12. + if (enc28j60Read(EIR) & EIR_TXERIF) { + enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRST); + enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRST); + } + } + + // Set the write pointer to start of the transmit buffer area + enc28j60WriteWord(EWRPTL, TXSTART_INIT); + + // Set the TXND pointer to correspond to the packet size given + enc28j60WriteWord(ETXNDL, TXSTART_INIT + len); + + // Write per-packet control byte (0x00 means use macon3 settings) + enc28j60WriteOp(ENC28J60_WRITE_BUF_MEM, 0, 0x00); + + // Copy the packet into the transmit buffer + enc28j60WriteBuffer(len, packet); + + // Send the contents of the transmit buffer onto the network + enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRTS); +} + +uint8_t enc28j60hasRxPkt(void) { + return enc28j60Read(EPKTCNT) > 0; +} + +uint16_t enc28j60PacketReceive(uint16_t maxlen, uint8_t* packet) { + uint16_t rxstat; + uint16_t len; + + // Check if a packet has been received and buffered + if (enc28j60Read(EPKTCNT) == 0) { + return 0; + } + + // Set the read pointer to the start of the received packet + enc28j60WriteWord(ERDPTL, gNextPacketPtr); + + // Read the next packet pointer + gNextPacketPtr = enc28j60ReadBufferWord(); + + // Read the packet length (excluding the CRC length) + len = enc28j60ReadBufferWord() - 4; + + // Read the receive status + rxstat = enc28j60ReadBufferWord(); + + // Limit retrieve length to maxlen - 1 + if (len > maxlen - 1) { + len = maxlen - 1; + } + + // Check CRC and symbol errors + if ((rxstat & 0x80) == 0) { + len = 0; + } else { + // Copy the packet from the receive buffer + enc28j60ReadBuffer(len, packet); + } + + // Move the RX read pointer to the start of the next received packet + if ((gNextPacketPtr - 1 < RXSTART_INIT) || (gNextPacketPtr - 1 > RXSTOP_INIT)) { + enc28j60WriteWord(ERXRDPTL, RXSTOP_INIT); + } else { + enc28j60WriteWord(ERXRDPTL, gNextPacketPtr - 1); + } + + // Decrement the packet counter to indicate we are done with this packet + enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PKTDEC); + + return len; +} +/* end of enc28j60.c */ diff --git a/src/error_handler.c b/Src/error_handler.c similarity index 67% rename from src/error_handler.c rename to Src/error_handler.c index 96096f6..4e5ef00 100644 --- a/src/error_handler.c +++ b/Src/error_handler.c @@ -1,6 +1,11 @@ +/** + * @file error_handler.c + * @brief Source file + */ + #include "error_handler.h" void __attribute__((weak)) ENC28j60_Error_Handler(enum ENC28j60_Error error) { while(1) {} -} \ No newline at end of file +} diff --git a/Src/ip_arp_udp_tcp.c b/Src/ip_arp_udp_tcp.c new file mode 100644 index 0000000..aa00830 --- /dev/null +++ b/Src/ip_arp_udp_tcp.c @@ -0,0 +1,339 @@ +/** + * @file ip_arp_udp_tcp.c + * @brief Source file containing the implementation of IP, ARP, UDP, and TCP protocols for network communication. + * + * This file includes functions for handling IP, ARP, UDP, and TCP packets, managing network communication, + * and processing checksums. It also provides implementations for ARP requests, UDP packet transmission, + * and basic network initialization. + * + * @note For more information, refer to the `license.md` file located at the root of the project. + */ + + +#include "defines.h" +#include "net.h" +#include "enc28j60.h" +#include "ip_arp_udp_tcp.h" +#include "websrv_help_functions.h" + +// The checksum function calculates checksums for IP, UDP, and TCP packets. +// For IP and ICMP, the checksum is calculated over the IP header only. +// For UDP and TCP, the checksum includes a pseudo-header using the source and destination IP fields. +uint16_t checksum(uint8_t *buf, uint16_t len, uint8_t type) { + uint32_t sum = 0; + // Ajout des valeurs spécifiques au protocole pour UDP et TCP + if (type == 1) { + sum += IP_PROTO_UDP_V; // Protocole UDP + sum += len - 8; // Longueur UDP (données + en-tête) + } else if (type == 2) { + sum += IP_PROTO_TCP_V; // Protocole TCP + sum += len - 8; // Longueur TCP (données + en-tête) + } + + // Sommation des mots de 16 bits + while (len > 1) { + sum += 0xFFFF & (*buf << 8 | *(buf + 1)); + buf += 2; + len -= 2; + } + + // Si un octet reste, l'ajouter (complété par zéro) + if (len) { + sum += (0xFF & *buf) << 8; + } + + // Réduction de la somme de 32 bits à 16 bits + while (sum >> 16) { + sum = (sum & 0xFFFF) + (sum >> 16); + } + + // Retourner le complément à un de la somme + return (uint16_t)(sum ^ 0xFFFF); +} +// This function initializes the web server. +// You must call this function once before you use any of the other functions. +void init_ip_arp_udp_tcp(uint8_t *mymac, uint8_t *myip, uint16_t port) { + wwwport_h = (port >> 8) & 0xFF; + wwwport_l = port & 0xFF; + memcpy(ipaddr, myip, 4); + memcpy(macaddr, mymac, 6); +} + +// Check if an IP message is from a specified IP address +uint8_t check_ip_message_is_from(uint8_t *buf, uint8_t *ip) { + return memcmp(&buf[IP_SRC_IP_P], ip, 4) == 0; +} + +// Check if the Ethernet frame is an ARP request for my IP address +uint8_t eth_type_is_arp_and_my_ip(uint8_t *buf, uint16_t len) { + if (len < 41) { + return 0; + } + if (buf[ETH_TYPE_H_P] != ETHTYPE_ARP_H_V || buf[ETH_TYPE_L_P] != ETHTYPE_ARP_L_V) { + return 0; + } + return memcmp(&buf[ETH_ARP_DST_IP_P], ipaddr, 4) == 0; +} + +// Check if the Ethernet frame is an IP packet addressed to my IP address +uint8_t eth_type_is_ip_and_my_ip(uint8_t *buf, uint16_t len) { + // eth + ip header is 34 bytes + udp header is 8 bytes + if (len < 42) { + return 0; + } + if (buf[ETH_TYPE_H_P] != ETHTYPE_IP_H_V || buf[ETH_TYPE_L_P] != ETHTYPE_IP_L_V) { + return 0; + } + // must be IP V4 and 20 byte header + if (buf[IP_HEADER_LEN_VER_P] != 0x45) { + return 0; + } + return memcmp(&buf[IP_DST_IP_P], ipaddr, 4) == 0; +} + +// Make a return Ethernet header from a received Ethernet packet +void make_eth(uint8_t *buf) { + memcpy(&buf[ETH_DST_MAC], &buf[ETH_SRC_MAC], 6); + memcpy(&buf[ETH_SRC_MAC], macaddr, 6); +} + +// Make a new Ethernet header for an IP packet +void make_eth_ip_new(uint8_t *buf, uint8_t *dst_mac) { + memcpy(&buf[ETH_DST_MAC], dst_mac, 6); + memcpy(&buf[ETH_SRC_MAC], macaddr, 6); + buf[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; + buf[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; +} + +// Function to calculate IP header checksum +void fill_ip_hdr_checksum(uint8_t *buf) { + uint16_t ck; + + // Zero out IP checksum field + buf[0x18] = 0; + buf[0x19] = 0; + + // Calculate IP header checksum + ck = calculate_tcp_checksum(&buf[0x0E], 20); // IP_P = 0x0E, IP_HEADER_LEN = 20 + + // Set IP header checksum + buf[0x18] = ck >> 8; + buf[0x19] = ck & 0xFF; +} + + +// Check if it's an ARP reply +uint8_t eth_type_is_arp_reply(uint8_t *buf) { + return buf[ETH_ARP_OPCODE_L_P] == ETH_ARP_OPCODE_REPLY_L_V; +} + +// Check if it's an ARP request +uint8_t eth_type_is_arp_req(uint8_t *buf) { + return buf[ETH_ARP_OPCODE_L_P] == ETH_ARP_OPCODE_REQ_L_V; +} + +// Make a return IP header from a received IP packet +void make_ip(uint8_t *buf) { + memcpy(&buf[IP_DST_IP_P], &buf[IP_SRC_IP_P], 4); + memcpy(&buf[IP_SRC_IP_P], ipaddr, 4); + fill_ip_hdr_checksum(buf); +} + +// Swap seq and ack number and count ack number up +void step_seq(uint8_t *buf, uint16_t rel_ack_num, uint8_t cp_seq) { + uint8_t i = 4; + uint8_t tseq; + + // Sequence numbers: add the relative ack num to SEQACK + while (i > 0) { + rel_ack_num += buf[TCP_SEQ_H_P + i - 1]; + tseq = buf[TCP_SEQACK_H_P + i - 1]; + buf[TCP_SEQACK_H_P + i - 1] = rel_ack_num & 0xFF; + + if (cp_seq) { + // Copy the acknum sent to us into the sequence number + buf[TCP_SEQ_H_P + i - 1] = tseq; + } else { + buf[TCP_SEQ_H_P + i - 1] = 0; // Some preset value + } + + rel_ack_num >>= 8; + i--; + } +} + + +// Make an ARP reply from a received ARP request +void make_arp_answer_from_request(uint8_t *buf) { + make_eth(buf); + buf[ETH_ARP_OPCODE_H_P] = ETH_ARP_OPCODE_REPLY_H_V; + buf[ETH_ARP_OPCODE_L_P] = ETH_ARP_OPCODE_REPLY_L_V; + memcpy(&buf[ETH_ARP_DST_MAC_P], &buf[ETH_ARP_SRC_MAC_P], 6); + memcpy(&buf[ETH_ARP_SRC_MAC_P], macaddr, 6); + memcpy(&buf[ETH_ARP_DST_IP_P], &buf[ETH_ARP_SRC_IP_P], 4); + memcpy(&buf[ETH_ARP_SRC_IP_P], ipaddr, 4); + enc28j60PacketSend(42, buf); +} + +// Make a UDP reply from a received UDP request, sending a max of 220 bytes of data +void make_udp_reply_from_request(uint8_t *buf, char *data, uint16_t datalen, uint16_t port) { + if (datalen > 220) { + datalen = 220; + } + + make_eth(buf); + + // Total length field in the IP header + uint16_t total_len = IP_HEADER_LEN + UDP_HEADER_LEN + datalen; + buf[IP_TOTLEN_H_P] = total_len >> 8; + buf[IP_TOTLEN_L_P] = total_len & 0xff; + + make_ip(buf); + + // Swap source and destination ports + buf[UDP_DST_PORT_H_P] = buf[UDP_SRC_PORT_H_P]; + buf[UDP_DST_PORT_L_P] = buf[UDP_SRC_PORT_L_P]; + buf[UDP_SRC_PORT_H_P] = port >> 8; + buf[UDP_SRC_PORT_L_P] = port & 0xff; + + // Calculate UDP length + uint16_t udp_len = UDP_HEADER_LEN + datalen; + buf[UDP_LEN_H_P] = udp_len >> 8; + buf[UDP_LEN_L_P] = udp_len & 0xff; + + // Zero the checksum + buf[UDP_CHECKSUM_H_P] = 0; + buf[UDP_CHECKSUM_L_P] = 0; + + // Copy data + memcpy(&buf[UDP_DATA_P], data, datalen); + + // Calculate checksum + uint16_t ck = checksum(&buf[IP_SRC_IP_P], 16 + datalen, 1); + buf[UDP_CHECKSUM_H_P] = ck >> 8; + buf[UDP_CHECKSUM_L_P] = ck & 0xff; + + enc28j60PacketSend(UDP_HEADER_LEN + IP_HEADER_LEN + ETH_HEADER_LEN + datalen, buf); +} + + +// Do some basic length calculations and store the result in static variables +void init_len_info(uint8_t *buf) { + info_data_len = (((int16_t)buf[IP_TOTLEN_H_P]) << 8) | (buf[IP_TOTLEN_L_P] & 0xff); + info_data_len -= IP_HEADER_LEN; + info_hdr_len = (buf[TCP_HEADER_LEN_P] >> 4) * 4; // Generate length in bytes + info_data_len -= info_hdr_len; + if (info_data_len <= 0) { + info_data_len = 0; + } +} + +#if defined(NTP_client) || defined(WOL_client) || defined(UDP_client) || defined(TCP_client) || defined(PING_client) +// Fills the buffer with a string of specified length +void fill_buf_p(uint8_t *buf, uint16_t len, const char *s) { + memcpy(buf, s, len); +} + +// Prepare a spontaneous UDP packet to a server +void send_udp_prepare(uint8_t *buf, uint16_t sport, uint8_t *dip, uint16_t dport) { + memcpy(&buf[ETH_DST_MAC], gwmacaddr, 6); + memcpy(&buf[ETH_SRC_MAC], macaddr, 6); + buf[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; + buf[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; + fill_buf_p(&buf[IP_P], 9, iphdr); + + buf[IP_TOTLEN_H_P] = 0; + buf[IP_PROTO_P] = IP_PROTO_UDP_V; + memcpy(&buf[IP_DST_IP_P], dip, 4); + memcpy(&buf[IP_SRC_IP_P], ipaddr, 4); + + buf[UDP_DST_PORT_H_P] = (dport >> 8); + buf[UDP_DST_PORT_L_P] = dport & 0xff; + buf[UDP_SRC_PORT_H_P] = (sport >> 8); + buf[UDP_SRC_PORT_L_P] = sport & 0xff; + buf[UDP_LEN_H_P] = 0; + buf[UDP_CHECKSUM_H_P] = 0; + buf[UDP_CHECKSUM_L_P] = 0; +} + +// Transmit a prepared UDP packet +void send_udp_transmit(uint8_t *buf, uint16_t datalen) { + buf[IP_TOTLEN_H_P] = (IP_HEADER_LEN + UDP_HEADER_LEN + datalen) >> 8; + buf[IP_TOTLEN_L_P] = (IP_HEADER_LEN + UDP_HEADER_LEN + datalen) & 0xff; + fill_ip_hdr_checksum(buf); + + buf[UDP_LEN_H_P] = (UDP_HEADER_LEN + datalen) >> 8; + buf[UDP_LEN_L_P] = (UDP_HEADER_LEN + datalen) & 0xff; + + uint16_t ck = checksum(&buf[IP_SRC_IP_P], 16 + datalen, 1); + buf[UDP_CHECKSUM_H_P] = ck >> 8; + buf[UDP_CHECKSUM_L_P] = ck & 0xff; + + enc28j60PacketSend(UDP_HEADER_LEN + IP_HEADER_LEN + ETH_HEADER_LEN + datalen, buf); +} + +// Send a UDP packet +void send_udp(uint8_t *buf, char *data, uint16_t datalen, uint16_t sport, uint8_t *dip, uint16_t dport) { + send_udp_prepare(buf, sport, dip, dport); + memcpy(&buf[UDP_DATA_P], data, datalen); + send_udp_transmit(buf, datalen); +} + +#if defined (NTP_client) || defined (UDP_client) || defined (TCP_client) +// Make an ARP request +void client_arp_whohas(uint8_t *buf, uint8_t *ip_we_search) { + // Prepare Ethernet header + memset(&buf[ETH_DST_MAC], 0xFF, 6); + memcpy(&buf[ETH_SRC_MAC], macaddr, 6); + buf[ETH_TYPE_H_P] = ETHTYPE_ARP_H_V; + buf[ETH_TYPE_L_P] = ETHTYPE_ARP_L_V; + + // Prepare ARP request header + fill_buf_p(&buf[ETH_ARP_P], 8, arpreqhdr); + memcpy(&buf[ETH_ARP_SRC_MAC_P], macaddr, 6); + memset(&buf[ETH_ARP_DST_MAC_P], 0, 6); + memcpy(&buf[ETH_ARP_DST_IP_P], ip_we_search, 4); + memcpy(&buf[ETH_ARP_SRC_IP_P], ipaddr, 4); + + // Set the flag to accept ARP reply + waitgwmac |= WGW_ACCEPT_ARP_REPLY; + + // Send the packet + enc28j60PacketSend(0x2a, buf); // 0x2a = 42 = length of the packet +} + +// Check if the client is waiting for the gateway MAC address +uint8_t client_waiting_gw(void) { + return (waitgwmac & WGW_HAVE_GW_MAC) ? 0 : 1; +} + +// Store the MAC address from an ARP reply +uint8_t client_store_gw_mac(uint8_t *buf) { + if (memcmp(&buf[ETH_ARP_SRC_IP_P], gwip, 4) == 0) { + memcpy(gwmacaddr, &buf[ETH_ARP_SRC_MAC_P], 6); + return 1; + } + return 0; +} + +// Refresh the gateway ARP entry +void client_gw_arp_refresh(void) { + if (waitgwmac & WGW_HAVE_GW_MAC) { + waitgwmac |= WGW_REFRESHING; + } +} + +//void client_set_wwwip(uint8_t *wwwipaddr) { +// memcpy(wwwip, wwwipaddr, 4); +//} + +void client_set_gwip(uint8_t *gwipaddr) +{ + waitgwmac = WGW_INITIAL_ARP; // causes an arp request in the packet loop + memcpy(gwip, gwipaddr, 4); +} +#endif // defined (NTP_client) || defined (UDP_client) || defined (TCP_client) + +#endif + +/* end of ip_arp_udp.c */ diff --git a/Src/net.c b/Src/net.c new file mode 100644 index 0000000..ecf7ce6 --- /dev/null +++ b/Src/net.c @@ -0,0 +1,186 @@ +/** + * @file net.c + * @brief Implementation of network-related functions for ENC28J60 Ethernet controller. + * + * This file implements various functions to interact with the ENC28J60 Ethernet controller, including + * handling ICMP echo requests (ping), registering callbacks for ICMP replies, checking for ICMP replies, + * making ICMP echo replies, and sending Wake-on-LAN (WOL) packets. It also includes the management of TCP + * client connections and settings related to a web server port. + */ + +#include "net.h" + +uint16_t len; +uint16_t plen; + +// Web server port, used when implementing webserver +uint8_t wwwport_l=80; // server port +uint8_t wwwport_h=0; // Note: never use same as TCPCLIENT_SRC_PORT_H + +uint8_t www_fd=0; +uint8_t browsertype=0; // 0 = get, 1 = post +char *client_additionalheaderline; +char *client_method; // for POST or PUT, no trailing space needed +char *client_urlbuf; +char *client_hoststr; +char *client_postval; +char *client_urlbuf_var; +uint8_t *bufptr=0; // ugly workaround for backward compatibility + +// just lower byte, the upper byte is TCPCLIENT_SRC_PORT_H: +uint8_t tcpclient_src_port_l=1; +uint8_t tcp_fd=0; // a file descriptor, will be encoded into the port +uint8_t tcp_client_state=0; +// TCP client Destination port +uint8_t tcp_client_port_h=0; +uint8_t tcp_client_port_l=0; + +int16_t delaycnt=0; +uint8_t gwip[4]; +uint8_t gwmacaddr[6]; +uint8_t tcpsrvip[4]; +uint8_t macaddr[6]; +uint8_t ipaddr[4]; +uint16_t info_data_len=0; +uint16_t info_hdr_len = 0; +uint8_t seqnum=0xa; // my initial tcp sequence number +const char arpreqhdr[] ={0,1,8,0,6,4,0,1}; +const char iphdr[] ={0x45,0,0,0x82,0,0,0x40,0,0x20}; // 0x82 is the total len on ip, 0x20 is ttl (time to live) +const char ntpreqhdr[] ={0xe3,0,4,0xfa,0,1,0,1,0,0}; + +uint8_t waitgwmac=WGW_INITIAL_ARP; + +void (*icmp_callback)(uint8_t *ip) = NULL; + + + +// TCP functions on packet.c +uint8_t send_fin = 0; +uint16_t tcpstart; +uint16_t save_len; + + +// Sends an ICMP echo request (ping) to the specified destination IP using the gateway MAC address. +void client_icmp_request(uint8_t *buf, uint8_t *destip) { + // Fill Ethernet header + memcpy(&buf[ETH_DST_MAC], gwmacaddr, 6); // Gateway MAC address in local LAN or host MAC address + memcpy(&buf[ETH_SRC_MAC], macaddr, 6); + buf[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; + buf[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; + + // Fill IP header + fill_buf_p(&buf[IP_P], 9, iphdr); + buf[IP_TOTLEN_L_P] = 0x82; // Total length + buf[IP_PROTO_P] = IP_PROTO_ICMP_V; // ICMP protocol + memcpy(&buf[IP_DST_IP_P], destip, 4); + memcpy(&buf[IP_SRC_IP_P], ipaddr, 4); + fill_ip_hdr_checksum(buf); + + // Fill ICMP header and data + buf[ICMP_TYPE_P] = ICMP_TYPE_ECHOREQUEST_V; + buf[ICMP_TYPE_P + 1] = 0; // Code + buf[ICMP_CHECKSUM_H_P] = 0; // Zero the checksum + buf[ICMP_CHECKSUM_L_P] = 0; + buf[ICMP_IDENT_H_P] = 5; // Some number for identification + buf[ICMP_IDENT_L_P] = ipaddr[3]; // Last byte of my IP for identification + buf[ICMP_IDENT_L_P + 1] = 0; // Sequence number, high byte + buf[ICMP_IDENT_L_P + 2] = 1; // Sequence number, low byte + + // Copy the data pattern + memset(&buf[ICMP_DATA_P], PINGPATTERN, 56); + + // Calculate checksum + uint16_t ck = checksum(&buf[ICMP_TYPE_P], 56 + 8, 0); + buf[ICMP_CHECKSUM_H_P] = ck >> 8; + buf[ICMP_CHECKSUM_L_P] = ck & 0xff; + + // Send the packet + enc28j60PacketSend(98, buf); +} + +/** + * @brief Register a callback function for receiving ping replies. + * + * @param callback The callback function to be called when a ping reply is received. + */ +void register_ping_rec_callback(void (*callback)(uint8_t *srcip)) { + icmp_callback = callback; +} + +/** + * @brief Check for an ICMP ping reply packet. + * + * This function should be called in a loop to check if a ping reply has been received. + * + * @param buf The buffer containing the packet data. + * @param ip_monitoredhost The IP address of the monitored host. + * @return 1 if the ping reply is from the monitored host, 0 otherwise. + */ +uint8_t packetloop_icmp_checkreply(uint8_t *buf, uint8_t *ip_monitoredhost) { + if (buf[IP_PROTO_P] == IP_PROTO_ICMP_V && + buf[ICMP_TYPE_P] == ICMP_TYPE_ECHOREPLY_V && + buf[ICMP_DATA_P] == PINGPATTERN && + check_ip_message_is_from(buf, ip_monitoredhost)) { + return 1; + } + return 0; +} + +// Make an ICMP echo reply from a received echo request +void make_echo_reply_from_request(uint8_t *buf, uint16_t len) { + make_eth(buf); + make_ip(buf); + buf[ICMP_TYPE_P] = ICMP_TYPE_ECHOREPLY_V; + if (buf[ICMP_CHECKSUM_H_P] > (0xff - 0x08)) { + buf[ICMP_CHECKSUM_H_P + 1]++; + } + buf[ICMP_CHECKSUM_H_P] += 0x08; + enc28j60PacketSend(len, buf); +} + +void __attribute__((weak)) pingCallback(void) {} + + +// A WOL (Wake on Lan) packet is a UDP packet to the broadcast +// address and UDP port 9. The data part contains 6x FF followed by +// 16 times the mac address of the host to wake-up +void send_wol(uint8_t *buf,uint8_t *wolmac) +{ + uint16_t ck; + // + memset(&buf[ETH_DST_MAC], 0xff, 6); + memcpy(&buf[ETH_SRC_MAC], macaddr, 6); + buf[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; + buf[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; + fill_buf_p(&buf[IP_P],9,iphdr); + + buf[IP_TOTLEN_L_P]=0x82; + buf[IP_PROTO_P]=IP_PROTO_UDP_V; + memcpy(&buf[IP_SRC_IP_P], ipaddr, 4); + memset(&buf[IP_DST_IP_P], 0xff, 4); + fill_ip_hdr_checksum(buf); + buf[UDP_DST_PORT_H_P]=0; + buf[UDP_DST_PORT_L_P]=0x9; // wol=normally 9 + buf[UDP_SRC_PORT_H_P]=10; + buf[UDP_SRC_PORT_L_P]=0x42; // source port does not matter + buf[UDP_LEN_H_P]=0; + buf[UDP_LEN_L_P]=110; // fixed len + // zero the checksum + buf[UDP_CHECKSUM_H_P]=0; + buf[UDP_CHECKSUM_L_P]=0; + // copy the data (102 bytes): + + // first mac - 0xFFs + memset(&buf[UDP_DATA_P], 0xff, 6); + // next 16 macs - wolmac repeated + // TODO: may need testing + for (unsigned i = 1; i <= 16; i++) { + memcpy(&buf[UDP_DATA_P + i*6], wolmac, 6); + } + + ck=checksum(&buf[IP_SRC_IP_P], 118,1); + buf[UDP_CHECKSUM_H_P]=ck>>8; + buf[UDP_CHECKSUM_L_P]=ck& 0xff; + + enc28j60PacketSend(UDP_HEADER_LEN+IP_HEADER_LEN+ETH_HEADER_LEN+102,buf); +} diff --git a/Src/ntp.c b/Src/ntp.c new file mode 100644 index 0000000..d2d204c --- /dev/null +++ b/Src/ntp.c @@ -0,0 +1,147 @@ +/** + * @file packet.c + * @brief Functions for handling network packets, including NTP requests and responses. + * + * This file contains functions to send NTP UDP packets and process NTP server responses. It also + * includes the logic to update the RTC (Real-Time Clock) of the STM32 microcontroller based on the NTP + * timestamp received from the server. The NTP implementation follows the specifications from + * RFC 958. Additional utility functions for handling various network packets are provided. + */ + +#include "ntp.h" +#include "packet.h" +#include "tcp.h" +#include "net.h" +#include "enc28j60.h" +#include "ip_arp_udp_tcp.h" +#include "websrv_help_functions.h" + +// Sends an NTP UDP packet +// See http://tools.ietf.org/html/rfc958 for details +void client_ntp_request(uint8_t *buf, uint8_t *ntpip, uint8_t srcport) { + // Fill Ethernet header + memcpy(&buf[ETH_DST_MAC], gwmacaddr, 6); + memcpy(&buf[ETH_SRC_MAC], macaddr, 6); + buf[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; + buf[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; + + // Fill IP header + fill_buf_p(&buf[IP_P], 9, iphdr); + buf[IP_TOTLEN_L_P] = 0x4c; // Total length + buf[IP_PROTO_P] = IP_PROTO_UDP_V; // UDP protocol + memcpy(&buf[IP_DST_IP_P], ntpip, 4); + memcpy(&buf[IP_SRC_IP_P], ipaddr, 4); + fill_ip_hdr_checksum(buf); + + // Fill UDP header + buf[UDP_DST_PORT_H_P] = 0; + buf[UDP_DST_PORT_L_P] = 0x7b; // NTP port = 123 + buf[UDP_SRC_PORT_H_P] = 10; + buf[UDP_SRC_PORT_L_P] = srcport; // Lower 8 bits of source port + buf[UDP_LEN_H_P] = 0; + buf[UDP_LEN_L_P] = 56; // Fixed length + buf[UDP_CHECKSUM_H_P] = 0; // Zero the checksum + buf[UDP_CHECKSUM_L_P] = 0; + + // Fill NTP data + memset(&buf[UDP_DATA_P], 0, 48); + fill_buf_p(&buf[UDP_DATA_P], 10, ntpreqhdr); + + // Calculate and fill UDP checksum + uint16_t ck = checksum(&buf[IP_SRC_IP_P], 16 + 48, 1); + buf[UDP_CHECKSUM_H_P] = ck >> 8; + buf[UDP_CHECKSUM_L_P] = ck & 0xff; + + // Send the packet + enc28j60PacketSend(90, buf); +} + +/* + * This function processes the response from an NTP server and updates the RTC of the STM32 microcontroller. + * It extracts the NTP timestamp from the received buffer, converts it to a UNIX timestamp, and then + * updates the RTC with the current date and time. + */ +void client_ntp_process_answer(uint8_t *buf, uint16_t plen) { + uint32_t unix_timestamp; + + // Extract the NTP timestamp (seconds since 1900) + unix_timestamp = ((uint32_t)buf[0x52] << 24) | ((uint32_t)buf[0x53] << 16) | ((uint32_t)buf[0x54] << 8) | (uint32_t)buf[0x55]; + + // Convert the NTP timestamp to UNIX timestamp (seconds since 1970) + // by subtracting the seconds between 1900 and 1970. + if (unix_timestamp > 2208988800UL) { + unix_timestamp -= 2208988800UL; + } else { + // Handle the case where the NTP timestamp is before the UNIX epoch (very unlikely, but for robustness) + unix_timestamp = 0; + } + + // Create structures for time and date + RTC_TimeTypeDef sTime = {0}; + RTC_DateTypeDef sDate = {0}; + + // Calculate the time components + uint32_t seconds = unix_timestamp % 60; + uint32_t minutes = (unix_timestamp / 60) % 60; + uint32_t hours = (unix_timestamp / 3600) % 24; + uint32_t days = unix_timestamp / 86400; // Total number of days since 1970 + + // The start of 1970 was a Thursday (day 4 of the week) + uint32_t dayOfWeek = (4 + days) % 7 + 1; // 1 = Monday, ..., 7 = Sunday + + // Determine the year + uint32_t year = 1970; + while (days >= 365) { + if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) { + if (days < 366) break; + days -= 366; + } else { + if (days < 365) break; + days -= 365; + } + year++; + } + + // Determine the month and the day of the month + uint32_t month_lengths[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) { + month_lengths[1] = 29; // February has 29 days in leap years + } + + uint32_t month = 0; + for (month = 0; month < 12; month++) { + if (days < month_lengths[month]) { + break; + } + days -= month_lengths[month]; + } + month++; // months are 1-12 + uint32_t dayOfMonth = days + 1; // days are 1-31 + + // Set the time + sTime.Hours = hours; + sTime.Minutes = minutes; + sTime.Seconds = seconds; + sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE; + sTime.StoreOperation = RTC_STOREOPERATION_RESET; + + // Set the date + sDate.WeekDay = dayOfWeek; + sDate.Month = month; + sDate.Date = dayOfMonth; + sDate.Year = year - 1970; // Year in the RTC structure is the year since 1970 + + // Update the RTC + HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN); + HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BIN); + + char timeStr[32]; // Adjusted size to hold formatted date and time + sprintf(timeStr, "%02d/%02d/%04d %02d:%02d:%02d UTC", + sDate.Date, + sDate.Month, + year, + sTime.Hours, + sTime.Minutes, + sTime.Seconds); + udpLog2("NTP indicates the following date and time:", timeStr); +} diff --git a/Src/packet.c b/Src/packet.c new file mode 100644 index 0000000..590d1e7 --- /dev/null +++ b/Src/packet.c @@ -0,0 +1,230 @@ +/** + * @file packet.c + * @brief Source file implementing packet processing functions for handling Ethernet, TCP, UDP, ICMP, and HTTP protocols. + * + * This file includes functions to process incoming Ethernet packets, including handling ARP, IP, TCP, UDP, + * and ICMP protocols. It also manages TCP connection states, processes HTTP requests, and handles UDP-based + * commands and NTP responses. + * + * @note For more information, refer to the `license.md` file located at the root of the project. + */ + +#include "packet.h" + +// Function to process all incoming packets +void handle_AllPacket(uint8_t *buf, uint16_t plen) { + // Check if the packet is an ARP packet + if (buf[ETH_TYPE_H_P] == ETHTYPE_ARP_H_V && buf[ETH_TYPE_L_P] == ETHTYPE_ARP_L_V) { + // Handle ARP packet + if ((waitgwmac & WGW_INITIAL_ARP || waitgwmac & WGW_REFRESHING) && delaycnt == 0 && enc28j60linkup()) { + client_arp_whohas(buf, gwip); // Send ARP request for gateway IP + } + delaycnt++; + if (buf[ETH_ARP_OPCODE_L_P] == ETH_ARP_OPCODE_REQ_L_V) { + make_arp_answer_from_request(buf); // Respond to ARP request + } + if (waitgwmac & WGW_ACCEPT_ARP_REPLY && buf[ETH_ARP_OPCODE_L_P] == ETH_ARP_OPCODE_REPLY_L_V) { + if (client_store_gw_mac(buf)) { + waitgwmac = WGW_HAVE_GW_MAC; // Store gateway MAC address + } + } + return; + } + + // Check if the packet is an IP packet addressed to us + if (!eth_type_is_ip_and_my_ip(buf, plen)) { + return; + } + + // Check Ethernet type + if (buf[ETH_TYPE_H_P] == ETHTYPE_IP_H_V && buf[ETH_TYPE_L_P] == ETHTYPE_IP_L_V) { + // It's an IP packet, check the IP protocol + uint8_t ip_proto = buf[IP_PROTO_P]; + + if (ip_proto == IP_PROTO_ICMP_V) { + // Handle ICMP packet + handle_icmp_packet(buf, plen); + } else if (ip_proto == IP_PROTO_TCP_V) { + // Handle TCP packet + handle_tcp_packet(buf, plen); + } else if (ip_proto == IP_PROTO_UDP_V) { + // Handle UDP packet + handle_udp_packet(buf, plen); + } + } +} + +// Function to handle ICMP packets +void handle_icmp_packet(uint8_t *buf, uint16_t plen) { + // Process ICMP packet (e.g., reply to ping) + if (icmp_callback) { + icmp_callback(&buf[IP_SRC_IP_P]); + } + make_echo_reply_from_request(buf, plen); + pingCallback(); +} + +// Déclarations Globales +static uint8_t tcp_state = TCP_STATE_CLOSED; + +void handle_tcp_response(uint8_t *buf) { + uint8_t connection_id = 0; // Assuming single connection for simplicity + tcp_connection_t *conn = &tcp_connections[connection_id]; + + if (conn->state == TCP_STATE_SYN_SENT && (buf[33] & TCP_FLAGS_SYN_V) && (buf[33] & TCP_FLAGS_ACK_V)) { + conn->ack_num = ((uint32_t)buf[24] << 24) | ((uint32_t)buf[25] << 16) | ((uint32_t)buf[26] << 8) | buf[27]; + conn->seq_num++; + conn->state = TCP_STATE_ESTABLISHED; + udpLog2("TCP_client", "Connection Established"); + + // Send HTTP GET request + const char *http_get = "GET / HTTP/1.1\r\nHost: 192.168.1.1\r\n\r\n"; + uint8_t buf[200]; + memcpy(buf + 38, http_get, strlen(http_get)); + send_tcp_ack_packet(buf, conn, conn->remote_ip, strlen(http_get)); + } +} + +void loop() { + uint8_t buf[60]; + // Simulate receiving a TCP response packet (this should be replaced with actual packet receiving code) + buf[33] = TCP_FLAGS_SYN_V | TCP_FLAGS_ACK_V; + buf[24] = 0; buf[25] = 0; buf[26] = 0; buf[27] = 2; // ACK number + handle_tcp_response(buf); +} + + +// Function to handle TCP packets +void handle_tcp_packet(uint8_t *buf, uint16_t plen) { + uint8_t flags = buf[TCP_FLAGS_P]; + uint32_t seq_num = (buf[TCP_SEQ_H_P] << 24) | (buf[TCP_SEQ_H_P + 1] << 16) | (buf[TCP_SEQ_H_P + 2] << 8) | buf[TCP_SEQ_H_P + 3]; + uint32_t ack_num = (buf[TCP_ACK_H_P] << 24) | (buf[TCP_ACK_H_P + 1] << 16) | (buf[TCP_ACK_H_P + 2] << 8) | buf[TCP_ACK_H_P + 3]; + uint8_t src_ip[4] = { buf[IP_SRC_IP_P], buf[IP_SRC_IP_P + 1], buf[IP_SRC_IP_P + 2], buf[IP_SRC_IP_P + 3] }; + uint16_t src_port = (buf[TCP_SRC_PORT_H_P] << 8) | buf[TCP_SRC_PORT_L_P]; + + char log_buffer[100]; + snprintf(log_buffer, sizeof(log_buffer), "TCP packet received: SEQ=%u, ACK=%u, FLAGS=%02X", seq_num, ack_num, flags); + udpLog2("TCP", log_buffer); + + tcp_connection_t *tcp_conn = find_connection(src_ip, src_port); + + if (tcp_conn == NULL) { + if (flags & TCP_FLAGS_SYN_V) { + tcp_conn = find_free_connection(); + if (tcp_conn != NULL) { + tcp_conn->state = TCP_STATE_SYN_RECEIVED; + tcp_conn->seq_num = seq_num; + tcp_conn->ack_num = ack_num; + memcpy(tcp_conn->remote_ip, src_ip, 4); + tcp_conn->remote_port = src_port; + create_tcp_synack_packet(buf, tcp_conn); + udpLog2("TCP", "SYN received, SYN-ACK sent"); + } else { + udpLog2("TCP", "No free connection slot available"); + } + } + return; + } + + switch (tcp_conn->state) { + case TCP_STATE_SYN_RECEIVED: + if (flags & TCP_FLAGS_ACK_V) { + tcp_conn->state = TCP_STATE_ESTABLISHED; + udpLog2("TCP", "Connection established"); + } + break; + + case TCP_STATE_ESTABLISHED: + if (flags & TCP_FLAGS_FIN_V) { + tcp_conn->state = TCP_STATE_CLOSED; + create_tcp_ack_packet(buf, tcp_conn, 0, 0); + udpLog2("TCP", "FIN received, ACK sent, connection closed"); + } else if (flags & TCP_FLAGS_ACK_V) { + if (flags & TCP_FLAGS_PUSH_V) { + handle_http_request(buf, TCP_DATA_P, plen - TCP_DATA_P, tcp_conn); + } else { + create_tcp_ack_packet(buf, tcp_conn, 0, 0); + udpLog2("TCP", "ACK received, no data to process"); + } + } + break; + + default: + udpLog2("TCP", "Unknown state, resetting connection"); + tcp_conn->state = TCP_STATE_CLOSED; + create_tcp_ack_packet(buf, tcp_conn, 0, TCP_FLAGS_RST_V); + break; + } +} + + +// Function to handle UDP packets +void handle_udp_packet(uint8_t *buf, uint16_t plen) { + if (buf[UDP_SRC_PORT_H_P] == 0 && buf[UDP_SRC_PORT_L_P] == 0x7b) { + client_ntp_process_answer(buf, plen); + return; + } + + if (buf[UDP_SRC_PORT_H_P] == 0 && buf[UDP_SRC_PORT_L_P] == 53) { + return; + } + + uint16_t data_pos = UDP_DATA_P; + uint8_t *data = &buf[data_pos]; + uint16_t data_len = plen - data_pos; + + char msg[data_len + 1]; + memcpy(msg, data, data_len); + msg[data_len] = '\0'; + + // Vérifier si le message reçu correspond à une commande dans la table des commandes + for (CommandMapping *cmd = commandTable; cmd->command != NULL; cmd++) { + if (strncmp(msg, cmd->command, strlen(cmd->command)) == 0) { + udpLog2("handle_udp_packet", cmd->command); // Log de la commande reconnue + if (cmd->function_with_arg != NULL) { + cmd->function_with_arg(msg); // Passer la commande entière pour traitement + } else if (cmd->function != NULL) { + cmd->function(); + } + return; + } + } + + udpLog2("Error", "Unexpected UDP message"); +} + + +// Function to handle HTTP requests +void handle_http_request(uint8_t *buf, uint16_t data_offset, uint16_t data_len, tcp_connection_t *tcp_conn) { + // Extract the HTTP request from the TCP payload + char *http_request = (char *)(buf + data_offset); + udpLog2("HTTP", "HTTP request received"); + + // Log the received HTTP request + char request_log[100]; + snprintf(request_log, sizeof(request_log), "HTTP Request: %.50s", http_request); + udpLog2("HTTP", request_log); + + // Check if the HTTP request is a GET request for "/logs" + if (strncmp(http_request, "GET /logs", 9) == 0) { + uint16_t response_len = send_logs_page(buf); + www_server_reply(buf, response_len, tcp_conn); + udpLog2("HTTP", "Logs page sent"); + } else if (strncmp(http_request, "GET /test", 9) == 0) { + uint16_t response_len = send_test_page(buf); + www_server_reply(buf, response_len, tcp_conn); + udpLog2("HTTP", "Test page sent"); + } else { + // Handle other HTTP requests or send a 404 Not Found response + const char *not_found_response = "HTTP/1.1 404 Not Found\r\n" + "Content-Type: text/html\r\n" + "Content-Length: 13\r\n" + "Connection: close\r\n" + "\r\n" + "404 Not Found"; + uint16_t response_len = strlen(not_found_response); + memcpy(buf + TCP_DATA_P, not_found_response, response_len); + www_server_reply(buf, response_len, tcp_conn); + udpLog2("HTTP", "404 Not Found response sent"); + } +} diff --git a/Src/tcp.c b/Src/tcp.c new file mode 100644 index 0000000..a8b0001 --- /dev/null +++ b/Src/tcp.c @@ -0,0 +1,699 @@ +/* + * @file tcp.c + * @brief Source file + * + * This source file provides the implementation of the TCP protocol for the ENC28J60 Ethernet controller. + * It includes functions for handling TCP connections, creating and sending TCP packets, managing TCP states, + * and performing TCP-related tasks such as checksum calculations and logging. + */ + +#include "defines.h" +#include "tcp.h" +#include "net.h" +#include "enc28j60.h" +#include "ip_arp_udp_tcp.h" +#include "websrv_help_functions.h" + +//-------------------------------------- STEP1 +// Initialisation by ES_init_network() for this 2 next function +void client_tcp_set_serverip(uint8_t *ipaddr) { + memcpy(tcpsrvip, ipaddr, 4); +} + +void initialize_tcp_sequence_number() { + seqnum = random(); // Assurez-vous d'avoir une fonction pour générer un nombre aléatoire +} + +// Définition de la variable globale +tcp_connection_t tcp_connections[MAX_TCP_CONNECTIONS]; + +// Function to initialize all TCP connections to the CLOSED state +void init_tcp_connections() { + for (int i = 0; i < MAX_TCP_CONNECTIONS; i++) { + tcp_connections[i].state = TCP_STATE_CLOSED; + tcp_connections[i].seq_num = 0; + tcp_connections[i].ack_num = 0; + memset(tcp_connections[i].remote_ip, 0, sizeof(tcp_connections[i].remote_ip)); + tcp_connections[i].remote_port = 0; + tcp_connections[i].local_port = 0; + } +} +void initiate_tcp_connection(uint8_t connection_id, uint8_t *remote_ip, uint16_t remote_port, uint16_t local_port) { + tcp_connections[connection_id].state = TCP_STATE_SYN_SENT; + tcp_connections[connection_id].local_port = local_port; + tcp_connections[connection_id].remote_port = remote_port; + memcpy(tcp_connections[connection_id].remote_ip, remote_ip, 4); + tcp_connections[connection_id].seq_num = 1; + tcp_connections[connection_id].ack_num = 0; + + uint8_t buf[60]; + send_tcp_syn_packet(buf, local_port, remote_port, remote_ip); +} + + +// Function to find a free TCP connection (one that is in the CLOSED state) +tcp_connection_t* find_free_connection() { + for (int i = 0; i < MAX_TCP_CONNECTIONS; i++) { + if (tcp_connections[i].state == TCP_STATE_CLOSED) { + return &tcp_connections[i]; // Return the first free connection found + } + } + return NULL; // No free connection found +} + +// Function to find an existing TCP connection based on remote IP and port +tcp_connection_t* find_connection(uint8_t *ip, uint16_t port) { + for (int i = 0; i < MAX_TCP_CONNECTIONS; i++) { + if (tcp_connections[i].state != TCP_STATE_CLOSED && + memcmp(tcp_connections[i].remote_ip, ip, sizeof(tcp_connections[i].remote_ip)) == 0 && + tcp_connections[i].remote_port == port) { + return &tcp_connections[i]; // Return the matching connection + } + } + return NULL; // No matching connection found +} + +//-------------------------------------- STEP2 +void make_tcphead(uint8_t *buf, uint16_t len, uint8_t mss, uint8_t win_size) { + uint16_t ck; + + // Set IP length + buf[0x10] = len >> 8; // IP_TOTLEN_H_P = 0x10 + buf[0x11] = len & 0xff; // IP_TOTLEN_L_P = 0x11 + + // Set TCP header length + buf[0x2C] = (20 / 4) << 4; // TCP_HEADER_LEN_P = 0x2C, TCP_HEADER_LEN_PLAIN = 20 + + // Set MSS option if present + if (mss) { + buf[0x32] = 0x02; + buf[0x33] = 0x04; + buf[0x34] = (mss >> 8) & 0xFF; + buf[0x35] = mss & 0xFF; + } + + // Zero out checksum fields + buf[0x2E] = 0; // TCP_CHECKSUM_H_P = 0x2E + buf[0x2F] = 0; // TCP_CHECKSUM_L_P = 0x2F + + // Calculate and set TCP checksum + ck = calculate_tcp_checksum(&buf[0x1A], len - 20); // IP_SRC_IP_P = 0x1A, TCP_HEADER_LEN_PLAIN = 20 + buf[0x2E] = ck >> 8; // TCP_CHECKSUM_H_P = 0x2E + buf[0x2F] = ck & 0xff; // TCP_CHECKSUM_L_P = 0x2F + + // Calculate and set IP checksum + fill_tcp_ip_hdr_checksum(buf); +} + +// htons function: convert host byte order to network byte order (short) +static uint16_t TCP_htons(uint16_t hostshort) { + return ((hostshort << 8) & 0xFF00) | ((hostshort >> 8) & 0x00FF); +} + +static uint16_t ip_identifier = 1; + +// Helper function to create a new IP + TCP packet +void make_ip_tcp_new(uint8_t *buf, uint16_t len, uint8_t *dst_ip) { + static uint16_t ip_identifier = 1; + + buf[0] = IP_V4_V | IP_HEADER_LENGTH_V; + buf[1] = 0x00; + buf[2] = (len >> 8) & 0xFF; + buf[3] = len & 0xFF; + buf[4] = (ip_identifier >> 8) & 0xFF; + buf[5] = ip_identifier & 0xFF; + ip_identifier++; + buf[6] = 0x00; + buf[7] = 0x00; + buf[8] = 128; + buf[9] = IP_PROTO_TCP_V; + memcpy(&buf[12], ipaddr, 4); + memcpy(&buf[16], dst_ip, 4); + fill_ip_hdr_checksum(buf); +} + +void create_tcp_synack_packet(uint8_t *buf, tcp_connection_t *tcp_conn) { + memset(buf + 0x22, 0, 20); // TCP_SRC_PORT_H_P = 0x22, TCP_HEADER_LEN_PLAIN = 20 + buf[0x2F] = 0x12; // TCP_FLAGS_P = 0x2F, TCP_FLAGS_SYNACK_V + + buf[0x22] = (tcp_conn->local_port >> 8) & 0xFF; // TCP_SRC_PORT_H_P = 0x22 + buf[0x23] = tcp_conn->local_port & 0xFF; // TCP_SRC_PORT_L_P = 0x23 + buf[0x24] = (tcp_conn->remote_port >> 8) & 0xFF; // TCP_DST_PORT_H_P = 0x24 + buf[0x25] = tcp_conn->remote_port & 0xFF; // TCP_DST_PORT_L_P = 0x25 + + uint32_t seq_num = tcp_conn->seq_num; + uint32_t ack_num = tcp_conn->ack_num + 1; + buf[0x26] = (seq_num >> 24) & 0xFF; // TCP_SEQ_P = 0x26 + buf[0x27] = (seq_num >> 16) & 0xFF; + buf[0x28] = (seq_num >> 8) & 0xFF; + buf[0x29] = seq_num & 0xFF; + buf[0x2A] = (ack_num >> 24) & 0xFF; // TCP_SEQACK_P = 0x2A + buf[0x2B] = (ack_num >> 16) & 0xFF; + buf[0x2C] = (ack_num >> 8) & 0xFF; + buf[0x2D] = ack_num & 0xFF; + + buf[0x30] = 0x05; // TCP_WINDOWSIZE_H_P = 0x30 + buf[0x31] = 0xB4; // TCP_WINDOWSIZE_L_P = 0x31, 1460 bytes + + // Calculate and set checksums + fill_ip_tcp_checksum(buf, 20 + 20); // IP_HEADER_LEN = 20, TCP_HEADER_LEN_PLAIN = 20 + + // Update connection state + tcp_conn->state = TCP_STATE_SYN_RECEIVED; + tcp_conn->seq_num += 1; +} + +// Function to create and send a TCP ACK packet +void create_tcp_ack_packet(uint8_t *buf, tcp_connection_t *tcp_conn, uint8_t mss, uint8_t win_size) { + uint16_t len = 20 + (mss ? 4 : 0); // TCP_HEADER_LEN_PLAIN = 20 + + // Configure the TCP header with necessary information + buf[0x2F] = 0x10; // TCP_FLAGS_ACK_V + + // Set source and destination ports + buf[0x22] = tcp_conn->local_port >> 8; // TCP_SRC_PORT_H_P = 0x22 + buf[0x23] = tcp_conn->local_port & 0xFF; // TCP_SRC_PORT_L_P = 0x23 + buf[0x24] = tcp_conn->remote_port >> 8; // TCP_DST_PORT_H_P = 0x24 + buf[0x25] = tcp_conn->remote_port & 0xFF; // TCP_DST_PORT_L_P = 0x25 + + // Set sequence and acknowledgment numbers + buf[0x26] = tcp_conn->seq_num >> 24; // TCP_SEQ_H_P = 0x26 + buf[0x27] = tcp_conn->seq_num >> 16; + buf[0x28] = tcp_conn->seq_num >> 8; + buf[0x29] = tcp_conn->seq_num & 0xFF; + + buf[0x2A] = tcp_conn->ack_num >> 24; // TCP_ACK_H_P = 0x2A + buf[0x2B] = tcp_conn->ack_num >> 16; + buf[0x2C] = tcp_conn->ack_num >> 8; + buf[0x2D] = tcp_conn->ack_num & 0xFF; + + // Set window size + buf[0x30] = win_size >> 8; // TCP_WINDOWSIZE_H_P = 0x30 + buf[0x31] = win_size & 0xFF; // TCP_WINDOWSIZE_L_P = 0x31 + + // Zero out the checksum fields + buf[0x32] = 0; // TCP_CHECKSUM_H_P = 0x32 + buf[0x33] = 0; // TCP_CHECKSUM_L_P = 0x33 + + // Fill MSS option if necessary + if (mss) { + buf[0x34] = mss; // TCP_OPTIONS_P = 0x34 + } + + // Calculate TCP checksum + uint16_t checksum = calculate_tcp_checksum(buf, len); + buf[0x32] = checksum >> 8; // TCP_CHECKSUM_H_P = 0x32 + buf[0x33] = checksum & 0xFF; // TCP_CHECKSUM_L_P = 0x33 + + // Calculate IP checksum + fill_tcp_ip_hdr_checksum(buf); + + // Send the packet + enc28j60PacketSend(20 + len + 14, buf); // IP_HEADER_LEN = 20, ETH_HEADER_LEN = 14 + + udpLog2("TCP", "ACK packet sent"); +} + +// Function to create and send a TCP ACK packet with data +void create_tcp_ack_with_data_packet(uint8_t *buf, uint16_t len) { + uint16_t total_len = 20 + 20 + len; // IP_HEADER_LEN = 20, TCP_HEADER_LEN_PLAIN = 20 + + // Update the total length field in the IP header + buf[0x10] = total_len >> 8; // IP_TOTLEN_H_P = 0x10 + buf[0x11] = total_len & 0xff; // IP_TOTLEN_L_P = 0x11 + + // Update the TCP header length field + buf[0x2E] = (20 / 4) << 4; // TCP_HEADER_LEN_P = 0x2E, TCP_HEADER_LEN_PLAIN = 20 + + // Calculate and set the TCP checksum + buf[0x32] = 0; // TCP_CHECKSUM_H_P = 0x32 + buf[0x33] = 0; // TCP_CHECKSUM_L_P = 0x33 + uint16_t tcp_ck = calculate_tcp_checksum(&buf[0x1A], 8 + 20 + len); // IP_SRC_IP_P = 0x1A, TCP_HEADER_LEN_PLAIN = 20 + buf[0x32] = tcp_ck >> 8; // TCP_CHECKSUM_H_P = 0x32 + buf[0x33] = tcp_ck & 0xff; // TCP_CHECKSUM_L_P = 0x33 + + // Update the TCP flags to include ACK and PUSH + buf[0x2F] = 0x18; // TCP_FLAGS_ACK_V = 0x10, TCP_FLAGS_PUSH_V = 0x08 + + // Calculate and set the IP header checksum + buf[0x18] = 0; // IP_CHECKSUM_H_P = 0x18 + buf[0x19] = 0; // IP_CHECKSUM_L_P = 0x19 + uint16_t ip_ck = calculate_tcp_checksum(&buf[0x0E], 20); // IP_P = 0x0E, IP_HEADER_LEN = 20 + buf[0x18] = ip_ck >> 8; // IP_CHECKSUM_H_P = 0x18 + buf[0x19] = ip_ck & 0xff; // IP_CHECKSUM_L_P = 0x19 + + // Log the IP and TCP headers + log_ip_header(buf); + log_tcp_header(buf); + + // Send the packet + enc28j60PacketSend(total_len + 14, buf); // ETH_HEADER_LEN = 14 +} +//-------------------------------------- STEP3 +// Function to calculate the length of TCP data +uint16_t get_tcp_data_len(uint8_t *buf) { + int16_t total_len = ((int16_t)buf[0x10] << 8) | (buf[0x11] & 0xff); // IP_TOTLEN_H_P, IP_TOTLEN_L_P + int16_t header_len = 20 + ((buf[0x2E] >> 4) * 4); // IP_HEADER_LEN, TCP_HEADER_LEN_P (0x2E) + int16_t data_len = total_len - header_len; + + // Return the data length if it is greater than 0, otherwise return 0 + return (data_len > 0) ? (uint16_t)data_len : 0; +} + +// Function to fill TCP data into the buffer +uint16_t fill_tcp_data(uint8_t *buf, uint16_t pos, const uint8_t *data, uint16_t len) { + memcpy(&buf[0x35 + 3 + pos], data, len); // TCP_CHECKSUM_L_P (0x35) + return pos + len; // Return the new position after the data +} + + +// Function to build TCP data packet +uint16_t build_tcp_data(uint8_t *buf, uint16_t srcPort) { + udpLog2("TCP_client", "Entering build_tcp_data"); + + // Build Ethernet header + memcpy(&buf[0x00], gwmacaddr, 6); // ETH_DST_MAC + memcpy(&buf[0x06], macaddr, 6); // ETH_SRC_MAC + buf[0x0C] = 0x08; // ETH_TYPE_H_P (ETHTYPE_IP_H_V) + buf[0x0D] = 0x00; // ETH_TYPE_L_P (ETHTYPE_IP_L_V) + + // Build IP header + fill_buf_p(&buf[0x0E], 9, iphdr); // IP_P + buf[0x10] = 0x00; // IP_TOTLEN_H_P + buf[0x11] = 40; // IP_TOTLEN_L_P (IP total length field) + buf[0x17] = 0x06; // IP_PROTO_P (IP_PROTO_TCP_V) + memcpy(&buf[0x1E], tcpsrvip, 4); // IP_DST_IP_P + memcpy(&buf[0x22], ipaddr, 4); // IP_SRC_IP_P + fill_ip_hdr_checksum(buf); + + // Build TCP header + buf[0x24] = tcp_client_port_h; // TCP_DST_PORT_H_P + buf[0x25] = tcp_client_port_l; // TCP_DST_PORT_L_P + buf[0x26] = srcPort >> 8; // TCP_SRC_PORT_H_P + buf[0x27] = srcPort & 0xff; // TCP_SRC_PORT_L_P + + // Zero out sequence number and acknowledgement number + memset(&buf[0x28], 0, 8); // TCP_SEQ_H_P + + // Set initial sequence number + buf[0x2A] = seqnum; // TCP_SEQ_H_P + 2 + seqnum += 3; + + // Set TCP header length and flags + buf[0x2E] = 0x60; // TCP_HEADER_LEN_P (Header length (5 words) and reserved bits) + buf[0x2F] = 0x08; // TCP_FLAGS_P (TCP_FLAGS_PUSH_V) + + // Set TCP window size + buf[0x30] = 0x4; // TCP_WIN_SIZE + buf[0x31] = 0x0; // TCP_WIN_SIZE + 1 + + // Zero out TCP checksum and urgent pointer + buf[0x34] = 0; // TCP_CHECKSUM_H_P + buf[0x35] = 0; // TCP_CHECKSUM_L_P + buf[0x36] = 0; // TCP_CHECKSUM_L_P + 1 + buf[0x37] = 0; // TCP_CHECKSUM_L_P + 2 + + // Set MSS option + buf[0x38] = 2; // TCP_OPTIONS_P + buf[0x39] = 4; // TCP_OPTIONS_P + 1 + buf[0x3A] = CLIENTMSS >> 8; // TCP_OPTIONS_P + 2 + buf[0x3B] = CLIENTMSS & 0xff; // TCP_OPTIONS_P + 3 + + // Calculate and set TCP checksum + uint16_t ck = checksum(&buf[0x1E], 8 + 20 + 4, 2); // IP_SRC_IP_P + 8 + TCP_HEADER_LEN_PLAIN + 4 + buf[0x34] = ck >> 8; // TCP_CHECKSUM_H_P + buf[0x35] = ck & 0xff; // TCP_CHECKSUM_L_P + + udpLog2("TCP_client", "Built TCP data"); + + // Return the total length of the packet + return 20 + 20 + 14 + 4; // IP_HEADER_LEN + TCP_HEADER_LEN_PLAIN + ETH_HEADER_LEN + 4 +} + +// Function to send TCP data +void send_tcp_data(uint8_t *buf, uint16_t dlen) { + udpLog2("TCP_client", "Entering send_tcp_data"); + + // Set TCP flags to PUSH + buf[0x2F] = 0x08; // TCP_FLAGS_P (TCP_FLAGS_PUSH_V) + + // Calculate total length of the IP packet (IP header + TCP header + data length) + uint16_t total_len = 20 + 20 + dlen; // IP_HEADER_LEN + TCP_HEADER_LEN_PLAIN + dlen + buf[0x10] = total_len >> 8; // IP_TOTLEN_H_P + buf[0x11] = total_len & 0xff; // IP_TOTLEN_L_P + + // Fill in the IP header checksum + fill_ip_hdr_checksum(buf); + + // Set TCP checksum to zero before calculation + buf[0x34] = 0; // TCP_CHECKSUM_H_P + buf[0x35] = 0; // TCP_CHECKSUM_L_P + + // Calculate TCP checksum + uint16_t ck = checksum(&buf[0x1E], 8 + 20 + dlen, 2); // IP_SRC_IP_P + 8 + TCP_HEADER_LEN_PLAIN + dlen + buf[0x34] = ck >> 8; // TCP_CHECKSUM_H_P + buf[0x35] = ck & 0xff; // TCP_CHECKSUM_L_P + + // Send the packet + enc28j60PacketSend(total_len + 14, buf); // total_len + ETH_HEADER_LEN + + udpLog2("TCP_client", "Sent TCP data"); +} + +// Function to build and send TCP data packet +void send_data_packet(uint8_t *buf, uint16_t srcPort, const uint8_t *data, uint16_t len) { + // Build TCP data packet + uint16_t pos = build_tcp_data(buf, srcPort); + + // Fill TCP data into the packet + pos = fill_tcp_data(buf, pos, data, len); + + // Send the TCP data packet + send_tcp_data(buf, pos - (20 + 20 + 14)); // IP_HEADER_LEN + TCP_HEADER_LEN_PLAIN + ETH_HEADER_LEN +} + +// Function to send a FIN packet to close the TCP connection +void send_tcp_fin_packet(uint8_t *buf) { + // Set TCP flags to ACK and FIN + buf[0x2F] = 0x11; // TCP_FLAGS_P offset (TCP_FLAGS_ACK_V | TCP_FLAGS_FIN_V) + + // Set TCP checksum to zero before calculation + buf[0x34] = 0; // TCP_CHECKSUM_H_P offset + buf[0x35] = 0; // TCP_CHECKSUM_L_P offset + + // Calculate TCP checksum + uint16_t tcp_ck = checksum(&buf[0x1E], 8 + 20, 2); // IP_SRC_IP_P offset + 8 + TCP_HEADER_LEN_PLAIN + buf[0x34] = tcp_ck >> 8; // TCP_CHECKSUM_H_P offset + buf[0x35] = tcp_ck & 0xff; // TCP_CHECKSUM_L_P offset + + // Set IP checksum to zero before calculation + buf[0x18] = 0; // IP_CHECKSUM_H_P offset + buf[0x19] = 0; // IP_CHECKSUM_L_P offset + + // Calculate IP checksum + uint16_t ip_ck = checksum(&buf[0x0E], 20, 0); // IP_P offset + IP_HEADER_LEN + buf[0x18] = ip_ck >> 8; // IP_CHECKSUM_H_P offset + buf[0x19] = ip_ck & 0xff; // IP_CHECKSUM_L_P offset + + // Log sending FIN packet + char log_buffer[100]; + sprintf(log_buffer, "Sending FIN packet. TCP Checksum: %04x", tcp_ck); + udpLog2("TCP", log_buffer); + + // Send the FIN packet + enc28j60PacketSend(20 + 20 + 14, buf); // IP_HEADER_LEN + TCP_HEADER_LEN_PLAIN + ETH_HEADER_LEN +} + +//-------------------------------------- STEP4 +// Function to prepare TCP data at a specified position and return the new position +uint16_t prepare_tcp_data(uint8_t *buf, uint16_t pos, const uint8_t *data, uint16_t len) { + // Copy data to the specified position in the buffer + memcpy(&buf[0x37 + pos], data, len); // TCP_CHECKSUM_L_P + 3 offset + + // Return the new position after adding the data + return pos + len; +} + +// Function to transmit prepared TCP data +void transmit_tcp_data(uint8_t *buf, uint16_t dlen) { + udpLog2("TCP_client", "Entering transmit_tcp_data"); + + // Set TCP flags to indicate PUSH operation + buf[0x2F] = 0x18; // TCP_FLAGS_P offset (TCP_FLAGS_PUSH_V) + + // Calculate total length of the IP packet (IP header + TCP header + data length) + uint16_t total_len = 20 + 20 + dlen; // IP_HEADER_LEN + TCP_HEADER_LEN_PLAIN + data length + buf[0x10] = total_len >> 8; // IP_TOTLEN_H_P offset + buf[0x11] = total_len & 0xff; // IP_TOTLEN_L_P offset + + // Fill in the IP header checksum + fill_ip_hdr_checksum(buf); + + // Set TCP checksum to zero before calculation + buf[0x34] = 0; // TCP_CHECKSUM_H_P offset + buf[0x35] = 0; // TCP_CHECKSUM_L_P offset + + // Calculate TCP checksum + uint16_t ck = checksum(&buf[0x1E], 8 + 20 + dlen, 2); // IP_SRC_IP_P offset + 8 + TCP_HEADER_LEN_PLAIN + data length + buf[0x34] = ck >> 8; // TCP_CHECKSUM_H_P offset + buf[0x35] = ck & 0xff; // TCP_CHECKSUM_L_P offset + + // Send the packet + enc28j60PacketSend(total_len + 14, buf); // total length + ETH_HEADER_LEN + + udpLog2("TCP_client", "Sent TCP data"); +} + +//-------------------------------------- OTHERS + +// Function to create and send a TCP SYN packet +void client_syn(uint8_t *buf, uint8_t srcport, uint8_t dstport_h, uint8_t dstport_l) { + udpLog2("TCP_client", "Entering client_syn"); + + // Copy MAC addresses to the buffer + memcpy(&buf[0x00], gwmacaddr, 6); // ETH_DST_MAC offset + memcpy(&buf[0x06], macaddr, 6); // ETH_SRC_MAC offset + + // Set Ethernet type to IP + buf[0x0C] = 0x08; // ETH_TYPE_H_P offset (ETHTYPE_IP_H_V) + buf[0x0D] = 0x00; // ETH_TYPE_L_P offset (ETHTYPE_IP_L_V) + + // Fill IP header + fill_buf_p(&buf[0x0E], 9, iphdr); // IP_P offset + + // Set IP total length for SYN packet + buf[0x10] = 0x00; // IP_TOTLEN_H_P offset + buf[0x11] = 44; // IP_TOTLEN_L_P offset (20 bytes IP header + 24 bytes TCP header) + buf[0x17] = 0x06; // IP_PROTO_P offset (IP_PROTO_TCP_V) + + // Set source and destination IP addresses + memcpy(&buf[0x1A], tcpsrvip, 4); // IP_DST_IP_P offset + memcpy(&buf[0x1E], ipaddr, 4); // IP_SRC_IP_P offset + + // Calculate and set IP header checksum + fill_ip_hdr_checksum(buf); + + // Set TCP source and destination ports + buf[0x22] = dstport_h; // TCP_DST_PORT_H_P offset + buf[0x23] = dstport_l; // TCP_DST_PORT_L_P offset + buf[0x20] = TCPCLIENT_SRC_PORT_H; // TCP_SRC_PORT_H_P offset + buf[0x21] = srcport; // TCP_SRC_PORT_L_P offset + + // Zero out sequence number and acknowledgment number + memset(&buf[0x26], 0, 4); // TCP_SEQ_H_P offset + memset(&buf[0x2A], 0, 4); // TCP_ACK_H_P offset + + // Set initial sequence number + buf[0x28] = seqnum >> 8; // TCP_SEQ_H_P offset + 2 + buf[0x29] = seqnum & 0xff; // TCP_SEQ_H_P offset + 3 + seqnum += 3; // Increment sequence number for next use + + // Set TCP header length and flags + buf[0x2E] = 0x60; // TCP_HEADER_LEN_P offset (24 bytes TCP header) + buf[0x2F] = 0x02; // TCP_FLAGS_P offset (TCP_FLAGS_SYN_V) + + // Set TCP window size + buf[0x30] = 0x04; // TCP_WIN_SIZE offset + buf[0x31] = 0x00; // TCP_WIN_SIZE offset + 1 + + // Zero out TCP checksum + buf[0x34] = 0x00; // TCP_CHECKSUM_H_P offset + buf[0x35] = 0x00; // TCP_CHECKSUM_L_P offset + + // Set urgent pointer + buf[0x36] = 0x00; // TCP_CHECKSUM_L_P offset + 1 + buf[0x37] = 0x00; // TCP_CHECKSUM_L_P offset + 2 + + // Set TCP options for MSS (Maximum Segment Size) + buf[0x38] = 0x02; // TCP_OPTIONS_P offset (Option kind = MSS) + buf[0x39] = 0x04; // TCP_OPTIONS_P offset + 1 (Option length) + buf[0x3A] = (CLIENTMSS >> 8); // TCP_OPTIONS_P offset + 2 (MSS value high byte) + buf[0x3B] = CLIENTMSS & 0xff; // TCP_OPTIONS_P offset + 3 (MSS value low byte) + + // Calculate and set TCP checksum + uint16_t ck = checksum(&buf[0x1E], 8 + 20 + 4, 2); // IP_SRC_IP_P offset + 8 + TCP_HEADER_LEN_PLAIN + 4 + buf[0x34] = ck >> 8; // TCP_CHECKSUM_H_P offset + buf[0x35] = ck & 0xff; // TCP_CHECKSUM_L_P offset + + // Send the SYN packet + enc28j60PacketSend(20 + 20 + 14 + 4, buf); // IP_HEADER_LEN + TCP_HEADER_LEN_PLAIN + ETH_HEADER_LEN + 4 + + udpLog2("TCP_client", "Sent TCP Syn"); +} + +// Function to create and send a TCP ACK packet with data, without setting TCP flags +void make_tcp_ack_with_data_noflags(uint8_t *buf, uint16_t dlen) { + uint16_t total_len = 20 + 20 + dlen; // IP_HEADER_LEN + TCP_HEADER_LEN_PLAIN + data length + + // Set the total length field in the IP header + buf[0x10] = total_len >> 8; // IP_TOTLEN_H_P offset + buf[0x11] = total_len & 0xff; // IP_TOTLEN_L_P offset + + // Calculate and set the IP header checksum + fill_ip_hdr_checksum(buf); + + // Zero out the TCP checksum field + buf[0x34] = 0; // TCP_CHECKSUM_H_P offset + buf[0x35] = 0; // TCP_CHECKSUM_L_P offset + + // Calculate the TCP checksum + uint16_t tcp_ck = checksum(&buf[0x1E], 8 + 20 + dlen, 2); // IP_SRC_IP_P offset + 8 + TCP_HEADER_LEN_PLAIN + data length + buf[0x34] = tcp_ck >> 8; // TCP_CHECKSUM_H_P offset + buf[0x35] = tcp_ck & 0xff; // TCP_CHECKSUM_L_P offset + + // Send the packet + enc28j60PacketSend(total_len + 14, buf); // total length + ETH_HEADER_LEN + + udpLog2("TCP_client", "Sent TCP ACK with data (no flags)"); +} + + +// Function to initiate a TCP request +uint8_t client_tcp_req( + uint8_t (*result_callback)(uint8_t fd, uint8_t statuscode, uint16_t data_start_pos_in_buf, uint16_t len_of_data), + uint16_t (*datafill_callback)(uint8_t fd), + uint16_t port) { + + udpLog2("TCP_client", "Entering client_tcp_req"); + + // Set the callback functions + client_tcp_result_callback = result_callback; + client_tcp_datafill_callback = datafill_callback; + + // Set the destination port + tcp_client_port_h = (port >> 8) & 0xff; + tcp_client_port_l = port & 0xff; + + // Initialize TCP client state + tcp_client_state = 1; + + // Increment and wrap around the file descriptor + if (++tcp_fd > 7) { + tcp_fd = 0; + } + + udpLog2("TCP_client", "TCP Request initialized"); + + return tcp_fd; +} + + +void fill_ip_tcp_checksum(uint8_t *buf, uint16_t len) { + // IP header length + uint8_t ip_header_len = 0x14; // 20 bytes + + // IP checksum offsets + uint8_t ip_checksum_h_p = 0x18; + uint8_t ip_checksum_l_p = 0x19; + + // TCP checksum offsets + uint8_t tcp_checksum_h_p = 0x2E; + uint8_t tcp_checksum_l_p = 0x2F; + + // Source IP and destination IP offsets + uint8_t ip_src_ip_p = 0x1A; + uint8_t ip_dst_ip_p = 0x1E; + + // Zero out IP checksum field + buf[ip_checksum_h_p] = 0; + buf[ip_checksum_l_p] = 0; + + // Calculate IP header checksum + uint16_t ip_ck = calculate_tcp_checksum(&buf[0x0E], ip_header_len); // Starting at IP header + + // Set IP header checksum + buf[ip_checksum_h_p] = ip_ck >> 8; + buf[ip_checksum_l_p] = ip_ck & 0xFF; + + // Zero out TCP checksum field + buf[tcp_checksum_h_p] = 0; + buf[tcp_checksum_l_p] = 0; + + // Calculate TCP checksum + uint16_t tcp_ck = calculate_tcp_checksum(&buf[ip_src_ip_p], len); // Starting at source IP + + // Set TCP checksum + buf[tcp_checksum_h_p] = tcp_ck >> 8; + buf[tcp_checksum_l_p] = tcp_ck & 0xFF; +} + +void fill_tcp_ip_hdr_checksum(uint8_t *buf) { + uint16_t ck; + + // Zero out IP checksum field + buf[0x18] = 0; + buf[0x19] = 0; + + // Calculate IP header checksum + ck = calculate_tcp_checksum(&buf[0x0E], 20); // IP_P = 0x0E, IP_HEADER_LEN = 20 + + // Set IP header checksum + buf[0x18] = ck >> 8; + buf[0x19] = ck & 0xff; +} + +uint16_t calculate_tcp_checksum(uint8_t *buf, uint16_t len) { + uint32_t sum = 0; + + // Sum up 2-byte values from the buffer + for (int i = 0; i < len; i += 2) { + sum += (buf[i] << 8) | buf[i + 1]; + } + + // Add carry bits to the lower 16 bits + while (sum >> 16) { + sum = (sum & 0xFFFF) + (sum >> 16); + } + + // One's complement + return ~sum; +} + +void log_ip_header(uint8_t *buf) { + char log_buffer[100]; + udpLog2("IP Header", "-----------------"); + sprintf(log_buffer, "IP Version/IHL: 0x%02x", buf[IP_P]); + udpLog2("IP Header", log_buffer); + sprintf(log_buffer, "Type of Service: 0x%02x", buf[IP_TOS_P]); + udpLog2("IP Header", log_buffer); + sprintf(log_buffer, "Total Length: %d", (buf[IP_TOTLEN_H_P] << 8) | buf[IP_TOTLEN_L_P]); + udpLog2("IP Header", log_buffer); + sprintf(log_buffer, "ID: 0x%04x", (buf[IP_ID_H_P] << 8) | buf[IP_ID_L_P]); + udpLog2("IP Header", log_buffer); + sprintf(log_buffer, "Flags/Fragment Offset: 0x%04x", (buf[IP_FLAGS_H_P] << 8) | buf[IP_FLAGS_L_P]); + udpLog2("IP Header", log_buffer); + sprintf(log_buffer, "TTL: %d", buf[IP_TTL_P]); + udpLog2("IP Header", log_buffer); + sprintf(log_buffer, "Protocol: %d", buf[IP_PROTO_P]); + udpLog2("IP Header", log_buffer); + sprintf(log_buffer, "Header Checksum: 0x%04x", (buf[IP_CHECKSUM_H_P] << 8) | buf[IP_CHECKSUM_L_P]); + udpLog2("IP Header", log_buffer); + sprintf(log_buffer, "Source IP: %d.%d.%d.%d", buf[IP_SRC_IP_P], buf[IP_SRC_IP_P + 1], buf[IP_SRC_IP_P + 2], buf[IP_SRC_IP_P + 3]); + udpLog2("IP Header", log_buffer); + sprintf(log_buffer, "Destination IP: %d.%d.%d.%d", buf[IP_DST_IP_P], buf[IP_DST_IP_P + 1], buf[IP_DST_IP_P + 2], buf[IP_DST_IP_P + 3]); + udpLog2("IP Header", log_buffer); +} + + +void log_tcp_header(uint8_t *buf) { + char log_buffer[100]; + udpLog2("TCP Header", "-----------------"); + sprintf(log_buffer, "Source Port: %d", (buf[TCP_SRC_PORT_H_P] << 8) | buf[TCP_SRC_PORT_L_P]); + udpLog2("TCP Header", log_buffer); + sprintf(log_buffer, "Destination Port: %d", (buf[TCP_DST_PORT_H_P] << 8) | buf[TCP_DST_PORT_L_P]); + udpLog2("TCP Header", log_buffer); + sprintf(log_buffer, "Sequence Number: 0x%08x", (buf[TCP_SEQ_H_P] << 24) | (buf[TCP_SEQ_H_P + 1] << 16) | (buf[TCP_SEQ_H_P + 2] << 8) | buf[TCP_SEQ_H_P + 3]); + udpLog2("TCP Header", log_buffer); + sprintf(log_buffer, "Acknowledgment Number: 0x%08x", (buf[TCP_ACK_H_P] << 24) | (buf[TCP_ACK_H_P + 1] << 16) | (buf[TCP_ACK_H_P + 2] << 8) | buf[TCP_ACK_H_P + 3]); + udpLog2("TCP Header", log_buffer); + sprintf(log_buffer, "Data Offset: %d", (buf[TCP_HEADER_LEN_P] >> 4) * 4); + udpLog2("TCP Header", log_buffer); + sprintf(log_buffer, "Flags: 0x%02x", buf[TCP_FLAGS_P]); + udpLog2("TCP Header", log_buffer); + sprintf(log_buffer, "Window Size: %d", (buf[TCP_WINDOWSIZE_H_P] << 8) | buf[TCP_WINDOWSIZE_L_P]); + udpLog2("TCP Header", log_buffer); + sprintf(log_buffer, "Checksum: 0x%04x", (buf[TCP_CHECKSUM_H_P] << 8) | buf[TCP_CHECKSUM_L_P]); + udpLog2("TCP Header", log_buffer); + sprintf(log_buffer, "Urgent Pointer: %d", (buf[TCP_URGENT_PTR_H_P] << 8) | buf[TCP_URGENT_PTR_L_P]); + udpLog2("TCP Header", log_buffer); +} diff --git a/Src/timers.c b/Src/timers.c new file mode 100644 index 0000000..adf1d4e --- /dev/null +++ b/Src/timers.c @@ -0,0 +1,68 @@ +/** + * @file timers.c + * @brief Source file implementing timer-related functions for STM32, including initialization, logging, and interrupt handling. + * + * This file contains functions to initialize and manage timers on the STM32 platform. It includes + * interrupt handling for the TIM4 timer and logging of timer states. The functions ensure that + * timers are properly initialized, started, and managed during runtime. + * + * @note For more information, refer to the `license.md` file located at the root of the project. + */ + +#include "timers.h" +#include +#include +#include "LogManager.h" + +// Counter for the number of timer interrupts +static uint32_t int_counterTIM4 = 0; + +_Bool isInitializationDone = false; + +const char* getTimerName(TIM_TypeDef *instance) { + if (instance == TIM4) return "Timer 4"; + return "Unknown Timer"; +} + +void timerLog(TIM_HandleTypeDef *htim) { + const char* timerName = getTimerName(htim->Instance); + char logMessage[128]; + sprintf(logMessage, "%s, Counter=%lu, Period=%lu", timerName, (unsigned long)__HAL_TIM_GET_COUNTER(htim), (unsigned long)htim->Init.Period); + udpLog2("Initialisation_Timers", logMessage); +} + +void timerInit(TIM_HandleTypeDef *htim) { + HAL_TIM_Base_Start_IT(htim); + HAL_TIM_Base_Stop_IT(htim); + __HAL_TIM_SET_COUNTER(htim, 0); + timerLog(htim); +} + +void generalTimersInit() { + TIM4Flag = 0; + + timerInit(&htim4); + + isInitializationDone = true; + + // Timers initialisation is done, now active them needed permanently. + HAL_TIM_Base_Start_IT(&htim4); +} + +void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { + + if (!isInitializationDone) { + return; // no - the initialization of timer is not done - we skip this function + } + + if (htim->Instance == TIM4) { + HAL_TIM_Base_Stop_IT(htim); + int_counterTIM4++; + if (int_counterTIM4 >= 10) // + { + TIM4Flag = 1; + int_counterTIM4 = 0; + } + HAL_TIM_Base_Start_IT(htim); + } +} diff --git a/Src/websrv_help_functions.c b/Src/websrv_help_functions.c new file mode 100644 index 0000000..49483bd --- /dev/null +++ b/Src/websrv_help_functions.c @@ -0,0 +1,363 @@ +/* + * @file websrv_help_functions.c + * @brief Source file + * + * This header file contains definitions and macros for interfacing with the ENC28J60 Ethernet controller. + * It includes control register definitions, chip enable/disable macros, and configurations for delays and + * chip select (CS) handling. + */ + +#include "defines.h" +#include "websrv_help_functions.h" +#include "tcp.h" +#include "net.h" +#include "enc28j60.h" +#include "ip_arp_udp_tcp.h" + +#ifdef FROMDECODE_websrv_help + +typedef void (*browser_callback_t)(uint8_t, uint16_t, uint16_t); +static browser_callback_t client_browser_callback = NULL; + +// Function to send the logs page +uint16_t send_logs_page(uint8_t *buf) { + const char *logs_page = "Logs

System Logs

Log content goes here

"; + uint16_t len = strlen(logs_page); + memcpy(buf + TCP_DATA_P, logs_page, len); + return len; +} + +// Function to send a test page +uint16_t send_test_page(uint8_t *buf) { + const char *test_page = "Test

Test Page

Test content goes here

"; + uint16_t len = strlen(test_page); + memcpy(buf + TCP_DATA_P, test_page, len); + return len; +} + +// Function to send an HTTP response +void www_server_reply(uint8_t *buf, uint16_t dlen, tcp_connection_t *tcp_conn) { + uint16_t ck; + uint8_t i; + + // Setup the TCP header + buf[TCP_FLAGS_P] = TCP_FLAGS_ACK_V | TCP_FLAGS_PUSH_V | TCP_FLAGS_FIN_V; // FIN, ACK, and PSH flags + buf[TCP_SEQACK_P] = (tcp_conn->ack_num >> 24) & 0xFF; + buf[TCP_SEQACK_P + 1] = (tcp_conn->ack_num >> 16) & 0xFF; + buf[TCP_SEQACK_P + 2] = (tcp_conn->ack_num >> 8) & 0xFF; + buf[TCP_SEQACK_P + 3] = tcp_conn->ack_num & 0xFF; + tcp_conn->seq_num += dlen; + buf[TCP_SEQ_P] = (tcp_conn->seq_num >> 24) & 0xFF; + buf[TCP_SEQ_P + 1] = (tcp_conn->seq_num >> 16) & 0xFF; + buf[TCP_SEQ_P + 2] = (tcp_conn->seq_num >> 8) & 0xFF; + buf[TCP_SEQ_P + 3] = tcp_conn->seq_num & 0xFF; + + // TCP header length is 20 bytes + buf[TCP_HEADER_LEN_P] = 0x50; + + // Set the window size + buf[TCP_WINDOWSIZE_H_P] = 0x05; + buf[TCP_WINDOWSIZE_L_P] = 0xB4; + + // Calculate and set the checksum + ck = 0; + // Pseudo header part 1: source and destination IPs + for (i = 0; i < 4; i++) { + ck += buf[IP_SRC_IP_P + i]; + ck += buf[IP_DST_IP_P + i]; + } + // Pseudo header part 2: protocol and TCP length + ck += IP_PROTO_TCP_V + (TCP_HEADER_LEN_PLAIN + dlen); + // TCP header and data + for (i = IP_HEADER_LEN + ETH_HEADER_LEN; i < IP_HEADER_LEN + ETH_HEADER_LEN + TCP_HEADER_LEN_PLAIN + dlen; i += 2) { + ck += (uint16_t)((buf[i] << 8) | buf[i + 1]); + } + while (ck >> 16) { + ck = (ck & 0xFFFF) + (ck >> 16); + } + ck = ~ck; + buf[TCP_CHECKSUM_H_P] = ck >> 8; + buf[TCP_CHECKSUM_L_P] = ck & 0xFF; + + // Send the packet + enc28j60PacketSend(IP_HEADER_LEN + ETH_HEADER_LEN + TCP_HEADER_LEN_PLAIN + dlen, buf); +} + +uint16_t www_client_internal_datafill_callback(uint8_t fd) { + if (fd != www_fd) { + return 0; + } + + char strbuf[6]; + uint16_t len = 0; + + if (browsertype == 0) { + // GET Request + len = fill_tcp_data(bufptr, len, (uint8_t *)"GET ", strlen("GET ")); + len = fill_tcp_data(bufptr, len, (uint8_t *)client_urlbuf, strlen(client_urlbuf)); + if (client_urlbuf_var) { + len = fill_tcp_data(bufptr, len, (uint8_t *)client_urlbuf_var, strlen(client_urlbuf_var)); + } + len = fill_tcp_data(bufptr, len, (uint8_t *)" HTTP/1.1\r\nHost: ", strlen(" HTTP/1.1\r\nHost: ")); + len = fill_tcp_data(bufptr, len, (uint8_t *)client_hoststr, strlen(client_hoststr)); + len = fill_tcp_data(bufptr, len, (uint8_t *)"\r\nUser-Agent: " WWW_USER_AGENT "\r\nAccept: text/html\r\nConnection: close\r\n\r\n", strlen("\r\nUser-Agent: " WWW_USER_AGENT "\r\nAccept: text/html\r\nConnection: close\r\n\r\n")); + } else { + // POST Request + len = fill_tcp_data(bufptr, len, (uint8_t *)(client_method ? client_method : "POST "), strlen(client_method ? client_method : "POST ")); + len = fill_tcp_data(bufptr, len, (uint8_t *)client_urlbuf, strlen(client_urlbuf)); + len = fill_tcp_data(bufptr, len, (uint8_t *)" HTTP/1.1\r\nHost: ", strlen(" HTTP/1.1\r\nHost: ")); + len = fill_tcp_data(bufptr, len, (uint8_t *)client_hoststr, strlen(client_hoststr)); + + if (client_additionalheaderline) { + len = fill_tcp_data(bufptr, len, (uint8_t *)"\r\n", strlen("\r\n")); + len = fill_tcp_data(bufptr, len, (uint8_t *)client_additionalheaderline, strlen(client_additionalheaderline)); + } + + len = fill_tcp_data(bufptr, len, (uint8_t *)"\r\nUser-Agent: " WWW_USER_AGENT "\r\nAccept: */*\r\nConnection: close\r\n", strlen("\r\nUser-Agent: " WWW_USER_AGENT "\r\nAccept: */*\r\nConnection: close\r\n")); + len = fill_tcp_data(bufptr, len, (uint8_t *)"Content-Length: ", strlen("Content-Length: ")); + snprintf(strbuf, sizeof(strbuf), "%zu", strlen(client_postval)); + len = fill_tcp_data(bufptr, len, (uint8_t *)strbuf, strlen(strbuf)); + len = fill_tcp_data(bufptr, len, (uint8_t *)"\r\nContent-Type: application/x-www-form-urlencoded\r\n\r\n", strlen("\r\nContent-Type: application/x-www-form-urlencoded\r\n\r\n")); + len = fill_tcp_data(bufptr, len, (uint8_t *)client_postval, strlen(client_postval)); + } + + // Logging the final request for debugging + udpLog2("WWW_client", "Generated HTTP request:"); + udpLog2("WWW_client", (char *)bufptr); + + return len; +} + + +/** + * @brief Internal result callback for the WWW client. + * + * This function processes the result of the HTTP request. + * + * @param fd The file descriptor. + * @param statuscode The status code. + * @param datapos The position of the data in the buffer. + * @param len_of_data The length of the data. + * @return 0 Always returns 0. + */ +uint8_t www_client_internal_result_callback(uint8_t fd, uint8_t statuscode, uint16_t datapos, uint16_t len_of_data) { + char log_msg[50]; + + // Log the entry into the function + udpLog2("WWW_client", "Entering www_client_internal_result_callback"); + + // Log the parameters received + snprintf(log_msg, sizeof(log_msg), "FD: %d, Status: %d, DataPos: %d, Len: %d", fd, statuscode, datapos, len_of_data); + udpLog2("WWW_client", log_msg); + + if (fd != www_fd) { + udpLog2("WWW_client", "File descriptor mismatch"); + client_browser_callback(4, 0, 0); + return 0; + } + + if (statuscode == 0 && len_of_data > 12 && client_browser_callback) { + uint16_t offset = TCP_SRC_PORT_H_P + ((bufptr[TCP_HEADER_LEN_P] >> 4) * 4); + uint8_t *status_code_pos = &bufptr[datapos + 9]; + + // Log the status code + snprintf(log_msg, sizeof(log_msg), "Status Code: %.3s", status_code_pos); + udpLog2("WWW_client", log_msg); + + if (strncmp("200", (char *)status_code_pos, 3) == 0) { + udpLog2("WWW_client", "HTTP 200 OK"); + client_browser_callback(0, offset, len_of_data); + } else { + udpLog2("WWW_client", "HTTP Error"); + client_browser_callback(1, offset, len_of_data); + } + } else { + udpLog2("WWW_client", "Invalid status or data length"); + } + + udpLog2("WWW_client", "Exiting www_client_internal_result_callback"); + return 0; +} + + +void client_http_get(char *urlbuf, char *urlbuf_varpart, char *hoststr, void (*callback)(uint8_t, uint16_t, uint16_t)) { + client_urlbuf = urlbuf; + client_urlbuf_var = urlbuf_varpart; + client_hoststr = hoststr; + browsertype = 0; + client_browser_callback = callback; + www_fd = client_tcp_req(www_client_internal_result_callback, www_client_internal_datafill_callback, 80); +} + +void client_http_post(char *urlbuf, char *hoststr, char *additionalheaderline, char *method, char *postval, void (*callback)(uint8_t, uint16_t)) { + client_urlbuf = urlbuf; + client_hoststr = hoststr; + client_additionalheaderline = additionalheaderline; + client_method = method; + client_postval = postval; + browsertype = 1; + client_browser_callback = (void (*)(uint8_t, uint16_t, uint16_t))callback; + www_fd = client_tcp_req(www_client_internal_result_callback, www_client_internal_datafill_callback, 80); +} + +// search for a string of the form key=value in +// a string that looks like q?xyz=abc&uvw=defgh HTTP/1.1\r\n +// +// The returned value is stored in strbuf. You must allocate +// enough storage for strbuf, maxlen is the size of strbuf. +// I.e the value it is declated with: strbuf[5]-> maxlen=5 +uint8_t find_key_val(char *str, char *strbuf, uint8_t maxlen, char *key) { + uint8_t found = 0; + uint8_t i = 0; + char *kp = key; + + // Find the key in the string + while (*str && *str != ' ' && *str != '\n' && !found) { + if (*str == *kp) { + kp++; + if (*kp == '\0' && *(str + 1) == '=') { + found = 1; + str += 2; // Move past '=' + } + } else { + kp = key; + } + str++; + } + + // Copy the value to the buffer if key is found + if (found) { + while (*str && *str != ' ' && *str != '\n' && *str != '&' && i < maxlen - 1) { + strbuf[i++] = *str++; + } + strbuf[i] = '\0'; + } + + // Return the length of the value + return i; +} + +// Convert a single hex digit character to its integer value +unsigned char h2int(char c) { + if (c >= '0' && c <= '9') { + return (unsigned char)(c - '0'); + } else if (c >= 'a' && c <= 'f') { + return (unsigned char)(c - 'a' + 10); + } else if (c >= 'A' && c <= 'F') { + return (unsigned char)(c - 'A' + 10); + } else { + return 0; + } +} + + +// Decode a URL string, e.g., "hello%20joe" or "hello+joe" becomes "hello joe" +void urldecode(char *urlbuf) { + char c; + char *dst = urlbuf; + + while ((c = *urlbuf)) { + if (c == '+') { + c = ' '; + } else if (c == '%') { + urlbuf++; + char high = *urlbuf++; + char low = *urlbuf; + c = (h2int(high) << 4) | h2int(low); + } + *dst++ = c; + urlbuf++; + } + *dst = '\0'; +} +#endif // FROMDECODE_websrv_help + +#ifdef URLENCODE_websrv_help +// Convert a single character to a 2-digit hex string +// A terminating '\0' is added +void int2h(char c, char *hstr) { + // Convert the lower 4 bits + hstr[1] = (c & 0xF) + '0'; + if ((c & 0xF) > 9) { + hstr[1] = (c & 0xF) - 10 + 'a'; + } + + // Convert the upper 4 bits + c = (c >> 4) & 0xF; + hstr[0] = c + '0'; + if (c > 9) { + hstr[0] = c - 10 + 'a'; + } + + // Null-terminate the string + hstr[2] = '\0'; +} + +// There must be enough space in urlbuf. In the worst case, that is +// 3 times the length of str. +void urlencode(char *str, char *urlbuf) { + char c; + while ((c = *str)) { + if (c == ' ' || isalnum((unsigned char)c)) { + if (c == ' ') { + c = '+'; + } + *urlbuf++ = c; + } else { + *urlbuf++ = '%'; + int2h(c, urlbuf); + urlbuf += 2; + } + str++; + } + *urlbuf = '\0'; +} +#endif // URLENCODE_websrv_help +// Parse a string and extract the IP to bytestr +uint8_t parse_ip(uint8_t *bytestr, char *str) { + char *sptr = NULL; + uint8_t i = 0; + + // Initialize bytestr to zero + for (i = 0; i < 4; i++) { + bytestr[i] = 0; + } + + i = 0; + while (*str && i < 4) { + if (sptr == NULL && isdigit((unsigned char)*str)) { + sptr = str; + } + if (*str == '.') { + *str = '\0'; + bytestr[i] = (uint8_t)(atoi(sptr) & 0xff); + i++; + sptr = NULL; + } + str++; + } + + if (i == 3 && sptr != NULL) { + bytestr[i] = (uint8_t)(atoi(sptr) & 0xff); + return 0; + } + return 1; +} + +// Convert a byte string to a human-readable display string +// (base is 10 for IP and 16 for MAC address), len is 4 for IP address and 6 for MAC. +void mk_net_str(char *resultstr, uint8_t *bytestr, uint8_t len, char separator, uint8_t base) { + uint8_t i, j = 0; + + for (i = 0; i < len; i++) { + if (base == 10) { + j += sprintf(&resultstr[j], "%d", bytestr[i]); + } else if (base == 16) { + j += sprintf(&resultstr[j], "%02x", bytestr[i]); + } + resultstr[j++] = separator; + } + resultstr[j - 1] = '\0'; // Replace the last separator with null terminator +} + +// end of websrv_help_functions.c diff --git a/inc/dhcp.h b/inc/dhcp.h deleted file mode 100644 index f3ff66c..0000000 --- a/inc/dhcp.h +++ /dev/null @@ -1,40 +0,0 @@ -/********************************************* - * vim:sw=8:ts=8:si:et - * To use the above modeline in vim you must have "set modeline" in your .vimrc - * Author: - * Copyright: GPL V2 - * - * DHCP look-up functions based on the udp client - * - * Chip type : ATMEGA88/ATMEGA168/ATMEGA328p with ENC28J60 - *********************************************/ -//@{ -#ifndef DHCP_H -#define DHCP_H - -#include "stm32includes.h" - -// to use this you need to enable UDP_client in the file ip_config.h -#if defined (UDP_client) - -// Some defines - -#define DHCP_BOOTREQUEST 1 -#define DHCP_BOOTRESPONSE 2 - -extern void dhcp_start(uint8_t *buf, uint8_t *macaddrin, uint8_t *ipaddrin, - uint8_t *maskin, uint8_t *gwipin, uint8_t *dhcpsvrin, - uint8_t *dnssvrin ); - -extern uint8_t dhcp_state( void ); - -void dhcp_send(uint8_t *buf, uint8_t requestType); - -uint8_t check_for_dhcp_answer(uint8_t *buf,uint16_t plen); - -uint8_t have_dhcpoffer(uint8_t *buf,uint16_t plen); -uint8_t have_dhcpack(uint8_t *buf,uint16_t plen); - -#endif /* UDP_client */ -#endif /* DHCP_H */ -//@} diff --git a/inc/ip_arp_udp_tcp.h b/inc/ip_arp_udp_tcp.h deleted file mode 100644 index 1c2b75f..0000000 --- a/inc/ip_arp_udp_tcp.h +++ /dev/null @@ -1,209 +0,0 @@ -/********************************************* - * vim:sw=8:ts=8:si:et - * To use the above modeline in vim you must have "set modeline" in your .vimrc - * Author: Guido Socher - * Copyright: GPL V2 - * - * IP/ARP/UDP/TCP functions - * - * Chip type : ATMEGA88 with ENC28J60 - *********************************************/ - /********************************************* - * Modified: nuelectronics.com -- Ethershield for Arduino - *********************************************/ -//@{ -#ifndef IP_ARP_UDP_TCP_H -#define IP_ARP_UDP_TCP_H - -#include "stm32includes.h" -#include - -void __attribute__((weak)) ES_PingCallback(void); - -// -- web server functions -- -// you must call this function once before you use any of the other server functions: -void init_ip_arp_udp_tcp(uint8_t *mymac,uint8_t *myip,uint16_t port); -// for a UDP server: -uint8_t eth_type_is_arp_and_my_ip(uint8_t *buf,uint16_t len); -uint8_t eth_type_is_ip_and_my_ip(uint8_t *buf,uint16_t len); -uint8_t eth_type_is_arp_reply(uint8_t *buf); -uint8_t eth_type_is_arp_req(uint8_t *buf); - -void make_udp_reply_from_request(uint8_t *buf,char *data,uint16_t datalen,uint16_t port); -void make_echo_reply_from_request(uint8_t *buf,uint16_t len); - -void make_arp_answer_from_request(uint8_t *buf); -void make_tcp_synack_from_syn(uint8_t *buf); -void init_len_info(uint8_t *buf); -uint16_t get_tcp_data_pointer(void); - -void make_tcp_ack_from_any(uint8_t *buf,int16_t datlentoack,uint8_t addflags); -void make_tcp_ack_with_data(uint8_t *buf,uint16_t dlen); -void make_tcp_ack_with_data_noflags(uint8_t *buf,uint16_t dlen); - -//extern void fill_buf_p(uint8_t *buf,uint16_t len, const prog_char *progmem_s); -void fill_ip_hdr_checksum(uint8_t *buf); -uint16_t checksum(uint8_t *buf, uint16_t len,uint8_t type); - -// for a UDP server: -uint8_t eth_type_is_ip_and_my_ip(uint8_t *buf,uint16_t len); -void make_udp_reply_from_request(uint8_t *buf,char *data,uint16_t datalen,uint16_t port); - -// return 0 to just continue in the packet loop and return the position -// of the tcp data if there is tcp data part -uint16_t packetloop_icmp_tcp(uint8_t *buf,uint16_t plen); -// functions to fill the web pages with data: -//extern uint16_t fill_tcp_data_p(uint8_t *buf,uint16_t pos, const prog_char *progmem_s); -uint16_t fill_tcp_data(uint8_t *buf,uint16_t pos, const char *s); -uint16_t fill_tcp_data_len(uint8_t *buf,uint16_t pos, const char *s, uint16_t len); -// send data from the web server to the client: -void www_server_reply(uint8_t *buf,uint16_t dlen); - -// -- client functions -- -#if defined (WWW_client) || defined (NTP_client) || defined (UDP_client) || defined (TCP_client) || defined (PING_client) -uint8_t client_store_gw_mac(uint8_t *buf); - -void client_set_gwip(uint8_t *gwipaddr); -// do a continues refresh until found: -void client_gw_arp_refresh(void); -// do an arp request once (call this function only if enc28j60PacketReceive returned zero: -void client_arp_whohas(uint8_t *buf,uint8_t *gwipaddr); -uint8_t client_waiting_gw(void); // 1 no GW mac yet, 0 have a gw mac - -uint16_t build_tcp_data(uint8_t *buf, uint16_t srcPort ); -void send_tcp_data(uint8_t *buf,uint16_t dlen ); -#endif // WWW_client TCP_client etc - - -//#if defined (WWW_client) || defined (TCP_client) -//#define client_set_wwwip client_tcp_set_serverip -// set the ip of the next tcp session that we open to a server -void client_tcp_set_serverip(uint8_t *ipaddr); -//#endif - - -#ifdef TCP_client -// To use the tcp client you need to: -// -// Declare a callback function to get the result (tcp data from the server): -// -// uint8_t your_client_tcp_result_callback(uint8_t fd, uint8_t statuscode,uint16_t data_start_pos_in_buf, uint16_t len_of_data){...your code;return(close_tcp_session);} -// -// statuscode=0 means the buffer has valid data, otherwise len and pos_in_buf -// are invalid. That is: do to use data_start_pos_in_buf and len_of_data -// if statuscode!=0. -// -// This callback gives you access to the TCP data of the first -// packet returned from the server. You should aim to minimize the server -// output such that this will be the only packet. -// -// close_tcp_session=1 means close the session now. close_tcp_session=0 -// read all data and leave it to the other side to close it. -// If you connect to a web server then you want close_tcp_session=0. -// If you connect to a modbus/tcp equipment then you want close_tcp_session=1 -// -// Declare a callback function to be called in order to fill in the -// -// request (tcp data sent to the server): -// uint16_t your_client_tcp_datafill_callback(uint8_t fd){...your code;return(len_of_data_filled_in);} -// -// Now call: -// fd=client_tcp_req(&your_client_tcp_result_callback,&your_client_tcp_datafill_callback,portnumber); -// -// fd is a file descriptor like number that you get back in the fill and result -// function so you know to which call of client_tcp_req this callback belongs. -// -// You can not start different clients (e.g modbus and web) at the -// same time but you can start them one after each other. That is -// when the request has timed out or when the result_callback was -// executed then you can start a new one. The fd makes it still possible to -// distinguish in the callback code the different types you started. -// -// Note that you might never get called back if the other side does -// not answer. A timer would be needed to recongnize such a condition. -// -// We use callback functions because that is the best implementation -// given the fact that we have very little RAM memory. -// -uint8_t client_tcp_req(uint8_t (*result_callback)(uint8_t fd,uint8_t statuscode,uint16_t data_start_pos_in_buf, uint16_t len_of_data),uint16_t (*datafill_callback)(uint8_t fd),uint16_t port); -void tcp_client_send_packet(uint8_t *buf,uint16_t dest_port, uint16_t src_port, uint8_t flags, uint8_t max_segment_size, - uint8_t clear_seqck, uint16_t next_ack_num, uint16_t dlength, uint8_t *dest_mac, uint8_t *dest_ip); -uint16_t tcp_get_dlength ( uint8_t *buf ); - -#endif // TCP_client - -#define HTTP_HEADER_START ((uint16_t)TCP_SRC_PORT_H_P+(buf[TCP_HEADER_LEN_P]>>4)*4) - -#ifdef WWW_client - -// ----- http get -void client_http_get(char *urlbuf, char *urlbuf_varpart, char *hoststr, void (*callback)(uint8_t,uint16_t,uint16_t)); - -// The callback is a reference to a function which must look like this: -// void browserresult_callback(uint8_t statuscode,uint16_t datapos) -// statuscode=0 means a good webpage was received, with http code 200 OK -// statuscode=1 an http error was received -// statuscode=2 means the other side in not a web server and in this case datapos is also zero -// ----- http post -// client web browser using http POST operation: -// additionalheaderline must be set to NULL if not used. -// postval is a string buffer which can only be de-allocated by the caller -// when the post operation was really done (e.g when callback was executed). -// postval must be urlencoded. - -void client_http_post(char *urlbuf, char *hoststr, char *additionalheaderline, char *method, char *postval,void (*callback)(uint8_t,uint16_t)); - -// The callback is a reference to a function which must look like this: -// void browserresult_callback(uint8_t statuscode,uint16_t datapos) -// statuscode=0 means a good webpage was received, with http code 200 OK -// statuscode=1 an http error was received -// statuscode=2 means the other side in not a web server and in this case datapos is also zero -#endif // WWW_client - -#ifdef NTP_client -void client_ntp_request(uint8_t *buf,uint8_t *ntpip,uint8_t srcport); -uint8_t client_ntp_process_answer(uint8_t *buf,uint32_t *time,uint8_t dstport_l); -#endif - -#ifdef UDP_client -// There are two ways of using this UDP client: -// -// 1) you call send_udp_prepare, you fill the data yourself into buf starting at buf[UDP_DATA_P], -// you send the packet by calling send_udp_transmit -// -// 2) You just allocate a large enough buffer for you data and you call send_udp and nothing else -// needs to be done. -// -void send_udp_prepare(uint8_t *buf,uint16_t sport, uint8_t *dip, uint16_t dport); -void send_udp_transmit(uint8_t *buf,uint16_t datalen); - -// send_udp sends via gwip, you must call client_set_gwip at startup, datalen must be less than 220 bytes -void send_udp(uint8_t *buf,char *data,uint16_t datalen,uint16_t sport, uint8_t *dip, uint16_t dport); -#endif // UDP_client - - -// you can find out who ping-ed you if you want: -void register_ping_rec_callback(void (*callback)(uint8_t *srcip)); - -#ifdef PING_client -void client_icmp_request(uint8_t *buf,uint8_t *destip); -// you must loop over this function to check if there was a ping reply: -uint8_t packetloop_icmp_checkreply(uint8_t *buf,uint8_t *ip_monitoredhost); -#endif // PING_client - -#ifdef WOL_client -void send_wol(uint8_t *buf,uint8_t *wolmac); -#endif // WOL_client - -uint8_t nextTcpState( uint8_t *buf,uint16_t plen ); -uint8_t currentTcpState( ); -uint8_t tcpActiveOpen( uint8_t *buf,uint16_t plen, - uint8_t (*result_callback)(uint8_t fd,uint8_t statuscode,uint16_t data_start_pos_in_buf, uint16_t len_of_data), - uint16_t (*datafill_callback)(uint8_t fd), - uint16_t port); - -void tcpPassiveOpen( uint8_t *buf,uint16_t plen ); -void tcpClose( uint8_t *buf,uint16_t plen ); - -#endif /* IP_ARP_UDP_TCP_H */ -//@} diff --git a/inc/net.h b/inc/net.h deleted file mode 100644 index 14b85e1..0000000 --- a/inc/net.h +++ /dev/null @@ -1,193 +0,0 @@ -/********************************************* - * vim:sw=8:ts=8:si:et - * To use the above modeline in vim you must have "set modeline" in your .vimrc - * Author: Guido Socher - * Copyright: GPL V2 - * - * Based on the net.h file from the AVRlib library by Pascal Stang. - * For AVRlib See http://www.procyonengineering.com/ - * Used with explicit permission of Pascal Stang. - * - * Chip type : ATMEGA88 with ENC28J60 - *********************************************/ - -// notation: _P = position of a field -// _V = value of a field - -//@{ - -#ifndef NET_H -#define NET_H - -#include "stm32includes.h" - -// ******* ETH ******* -#define ETH_HEADER_LEN 14 -// values of certain bytes: -#define ETHTYPE_ARP_H_V 0x08 -#define ETHTYPE_ARP_L_V 0x06 -#define ETHTYPE_IP_H_V 0x08 -#define ETHTYPE_IP_L_V 0x00 -// byte positions in the ethernet frame: -// -// Ethernet type field (2bytes): -#define ETH_TYPE_H_P 12 -#define ETH_TYPE_L_P 13 -// -#define ETH_DST_MAC 0 -#define ETH_SRC_MAC 6 - - -// ******* ARP ******* -#define ETH_ARP_OPCODE_REPLY_H_V 0x0 -#define ETH_ARP_OPCODE_REPLY_L_V 0x02 -#define ETH_ARP_OPCODE_REQ_H_V 0x0 -#define ETH_ARP_OPCODE_REQ_L_V 0x01 -// start of arp header: -#define ETH_ARP_P 0xe -// -#define ETHTYPE_ARP_L_V 0x06 -// arp.dst.ip -#define ETH_ARP_DST_IP_P 0x26 -// arp.opcode -#define ETH_ARP_OPCODE_H_P 0x14 -#define ETH_ARP_OPCODE_L_P 0x15 -// arp.src.mac -#define ETH_ARP_SRC_MAC_P 0x16 -#define ETH_ARP_SRC_IP_P 0x1c -#define ETH_ARP_DST_MAC_P 0x20 -#define ETH_ARP_DST_IP_P 0x26 - -// ******* IP ******* -#define IP_HEADER_LEN 20 - -#define IP_PROTO_ICMP_V 0x01 -#define IP_PROTO_TCP_V 0x06 -#define IP_PROTO_UDP_V 0x11 -#define IP_V4_V 0x40 -#define IP_HEADER_LENGTH_V 0x05 - -#define IP_P 0x0E -#define IP_HEADER_VER_LEN_P 0x0E -#define IP_TOS_P 0x0F -#define IP_TOTLEN_H_P 0x10 -#define IP_TOTLEN_L_P 0x11 -#define IP_ID_H_P 0x12 -#define IP_ID_L_P 0x13 -#define IP_FLAGS_P 0x14 -#define IP_FLAGS_H_P 0x14 -#define IP_FLAGS_L_P 0x15 -#define IP_TTL_P 0x16 -#define IP_PROTO_P 0x17 -#define IP_CHECKSUM_P 0x18 -#define IP_CHECKSUM_H_P 0x18 -#define IP_CHECKSUM_L_P 0x19 -#define IP_SRC_IP_P 0x1A -#define IP_DST_IP_P 0x1E - -#define IP_SRC_P 0x1a -#define IP_DST_P 0x1e -#define IP_HEADER_LEN_VER_P 0xe - -// ip.src -/* -#define IP_SRC_P 0x1a -#define IP_DST_P 0x1e -#define IP_P 0xe -#define IP_HEADER_LEN_VER_P 0xe -#define IP_CHECKSUM_P 0x18 -#define IP_TTL_P 0x16 -#define IP_FLAGS_P 0x14 -#define IP_TOTLEN_H_P 0x10 -#define IP_TOTLEN_L_P 0x11 - -#define IP_PROTO_P 0x17 - -#define IP_PROTO_ICMP_V 1 -#define IP_PROTO_TCP_V 6 -// 17=0x11 -#define IP_PROTO_UDP_V 17 -#define IP_V4_V 0x40 -*/ -// ******* ICMP ******* -#define ICMP_TYPE_ECHOREPLY_V 0 -#define ICMP_TYPE_ECHOREQUEST_V 8 -// -#define ICMP_TYPE_P 0x22 -#define ICMP_CHECKSUM_P 0x24 -#define ICMP_CHECKSUM_H_P 0x24 -#define ICMP_CHECKSUM_L_P 0x25 -#define ICMP_IDENT_H_P 0x26 -#define ICMP_IDENT_L_P 0x27 -#define ICMP_DATA_P 0x2a - -// ******* UDP ******* -#define UDP_HEADER_LEN 8 -// -#define UDP_SRC_PORT_H_P 0x22 -#define UDP_SRC_PORT_L_P 0x23 -#define UDP_DST_PORT_H_P 0x24 -#define UDP_DST_PORT_L_P 0x25 -// -#define UDP_LEN_H_P 0x26 -#define UDP_LEN_L_P 0x27 -#define UDP_CHECKSUM_H_P 0x28 -#define UDP_CHECKSUM_L_P 0x29 -#define UDP_DATA_P 0x2a - -// ******* TCP ******* -#define TCP_FLAGS_FIN_V 0x01 -#define TCP_FLAGS_SYN_V 0x02 -#define TCP_FLAGS_RST_V 0x04 -#define TCP_FLAGS_PUSH_V 0x08 -#define TCP_FLAGS_ACK_V 0x10 -#define TCP_FLAGS_SYNACK_V 0x12 -#define TCP_FLAGS_PSHACK_V 0x18 - -#define TCP_SRC_PORT_H_P 0x22 -#define TCP_SRC_PORT_L_P 0x23 -#define TCP_DST_PORT_H_P 0x24 -#define TCP_DST_PORT_L_P 0x25 -#define TCP_SEQ_P 0x26 // the tcp seq number is 4 bytes 0x26-0x29 -#define TCP_SEQ_H_P 0x26 -#define TCP_SEQACK_P 0x2a -#define TCP_SEQACK_H_P 0x2a -// flags: SYN=2 -#define TCP_FLAGS_P 0x2f -//#define TCP_FLAG_P 0x2f -#define TCP_WINDOWSIZE_H_P 0x30 // 2 bytes -#define TCP_WINDOWSIZE_L_P 0x31 -#define TCP_CHECKSUM_H_P 0x32 -#define TCP_CHECKSUM_L_P 0x33 -#define TCP_URGENT_PTR_H_P 0x34 // 2 bytes -#define TCP_URGENT_PTR_L_P 0x35 -#define TCP_OPTIONS_P 0x36 -#define TCP_DATA_P 0x36 -// plain len without the options: -#define TCP_HEADER_LEN_PLAIN 20 -#define TCP_HEADER_LEN_P 0x2e -#define TCP_WIN_SIZE 0x30 -#define TCP_CHECKSUM_H_P 0x32 -#define TCP_CHECKSUM_L_P 0x33 -#define TCP_OPTIONS_P 0x36 -// -// - -// DNS States for access in applications -#define DNS_STATE_INIT 0 -#define DNS_STATE_REQUESTED 1 -#define DNS_STATE_ANSWER 2 - -// DHCP States for access in applications -#define DHCP_STATE_INIT 0 -#define DHCP_STATE_DISCOVER 1 -#define DHCP_STATE_OFFER 2 -#define DHCP_STATE_REQUEST 3 -#define DHCP_STATE_ACK 4 -#define DHCP_STATE_OK 5 -#define DHCP_STATE_RENEW 6 - - -#endif -//@} - diff --git a/inc/stm32includes.h b/inc/stm32includes.h deleted file mode 100644 index d837da1..0000000 --- a/inc/stm32includes.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef __STM32INCLUDES_H -#define __STM32INCLUDES_H - -#if STM32F0 -/*# if STM32F091xC -# include "stm32f091xc.h" -# endif*/ -# include "stm32f0xx.h" -# include "stm32f0xx_hal_def.h" -# include "stm32f0xx_hal_spi.h" -#elif STM32F1 -//# include "stm32f103xb.h" -# include "stm32f1xx.h" -# include "stm32f1xx_hal_def.h" -# include "stm32f1xx_hal_spi.h" -#elif STM32F4 -# include "stm32f4xx.h" -# include "stm32f4xx_hal.h" -# include "stm32f4xx_hal_gpio.h" -# include "stm32f4xx_hal_def.h" -# include "stm32f4xx_hal_spi.h" -#endif - -#include - -#endif diff --git a/inc/websrv_help_functions.h b/inc/websrv_help_functions.h deleted file mode 100644 index e8d1bf8..0000000 --- a/inc/websrv_help_functions.h +++ /dev/null @@ -1,26 +0,0 @@ -/********************************************* - * vim:sw=8:ts=8:si:et - * To use the above modeline in vim you must have "set modeline" in your .vimrc - * Author: Guido Socher - * Copyright: GPL V2 - *********************************************/ -//@{ -#ifndef WEBSRV_HELP_FUNCTIONS_H -#define WEBSRV_HELP_FUNCTIONS_H - -#include "stm32includes.h" -#include -#include -#include -#include - -// These functions are documented in websrv_help_functions.c -extern uint8_t find_key_val(char *str,char *strbuf, uint8_t maxlen,char *key); -extern void urldecode(char *urlbuf); -extern void urlencode(char *str,char *urlbuf); -extern uint8_t parse_ip(uint8_t *bytestr,char *str); -extern void mk_net_str(char *resultstr,uint8_t *bytestr,uint8_t len,char separator,uint8_t base); - - -#endif /* WEBSRV_HELP_FUNCTIONS_H */ -//@} diff --git a/src/dhcp.c b/src/dhcp.c deleted file mode 100644 index 2d9a528..0000000 --- a/src/dhcp.c +++ /dev/null @@ -1,314 +0,0 @@ -/********************************************* - * vim:sw=8:ts=8:si:et - * To use the above modeline in vim you must have "set modeline" in your .vimrc - * - * Author: Andrew Lindsay - * Rewritten and optimized by Jean-Claude Wippler, http://jeelabs.org/ - * Please visit Jeelabs for tons of cool stuff. - * - * Copyright: GPL V2 - * See http://www.gnu.org/licenses/gpl.html - * - * DHCP look-up functions based on the udp client - * http://www.ietf.org/rfc/rfc2131.txt - *********************************************/ -#include -#include - -#include "dhcp.h" -#include "enc28j60.h" -#include "ip_arp_udp_tcp.h" -#include "net.h" - -#if defined(UDP_client) - -#define DHCP_BOOTREQUEST 1 -#define DHCP_BOOTREPLY 2 -#define DHCPDISCOVER 0x01 -#define DHCPOFFER 0x02 -#define DHCPREQUEST 0x03 -#define DHCPACK 0x05 -//#define DHCPNACK - -// size 236 -typedef struct dhcpData { - // 0-3 op, htype, hlen, hops - uint8_t op; - uint8_t htype; - uint8_t hlen; - uint8_t hops; - // 4-7 xid - uint32_t xid; - // 8-9 secs - uint16_t secs; - // 10-11 flags - uint16_t flags; - // 12-15 ciaddr - uint8_t ciaddr[4]; - // 16-19 yiaddr - uint8_t yiaddr[4]; - // 20-23 siaddr - uint8_t siaddr[4]; - // 24-27 giaddr - uint8_t giaddr[4]; - // 28-43 chaddr(16) - uint8_t chaddr[16]; - // 44-107 sname (64) - uint8_t sname[64]; - // 108-235 file(128) - uint8_t file[128]; - // options - // dont include as its variable size, - // just tag onto end of structure -} __attribute__((packed)) dhcpData; - -static uint8_t dhcptid_l = 0; // a counter for transaction ID -// src port high byte must be a a0 or higher: -#define DHCPCLIENT_SRC_PORT_H 0xe0 -#define DHCP_SRC_PORT 67 -#define DHCP_DEST_PORT 68 - -static uint8_t dhcpState = DHCP_STATE_INIT; - -// Pointers to values we have set or need to set -static uint8_t *macaddr; -uint8_t *dhcpip; -uint8_t *dhcpmask; -uint8_t *dhcpserver; -uint8_t *dnsserver; -uint8_t *gwaddr; - -// For DHCP hostnames we don't need any zero-terminators -#define HOSTNAME_LEN DHCP_HOSTNAME_LEN -#define HOSTNAME_SIZE HOSTNAME_LEN + 3 - -static char hostname[HOSTNAME_SIZE]; -static uint8_t haveDhcpAnswer = 0; -static uint8_t dhcp_ansError = 0; -uint32_t currentXid = 0; -uint16_t currentSecs = 0; -static uint32_t leaseStart = 0; -static uint32_t leaseTime = 0; -static uint8_t *bufPtr; - -static void addToBuf(uint8_t b) { *bufPtr++ = b; } - -uint8_t dhcp_state(void) { - // Check lease and request renew if currently OK and time - // leaseStart - start time in millis - // leaseTime - length of lease in millis - // - if (dhcpState == DHCP_STATE_OK && (leaseStart + leaseTime) <= HAL_GetTick()) { - // Calling app needs to detect this and init renewal - dhcpState = DHCP_STATE_RENEW; - } - return (dhcpState); -} - -// Start request sequence, send DHCPDISCOVER -// Wait for DHCPOFFER -// Send DHCPREQUEST -// Wait for DHCPACK -// All configured -void dhcp_start(uint8_t *buf, uint8_t *macaddrin, uint8_t *ipaddrin, uint8_t *maskin, - uint8_t *gwipin, uint8_t *dhcpsvrin, uint8_t *dnssvrin) { - macaddr = macaddrin; - dhcpip = ipaddrin; - dhcpmask = maskin; - gwaddr = gwipin; - dhcpserver = dhcpsvrin; - dnsserver = dnssvrin; - /*srand(analogRead(0));*/ srand(0x13); - currentXid = 0x00654321 + rand(); - currentSecs = 0; - int n; - for (n = 0; n < 4; n++) { - dhcpip[n] = 0; - dhcpmask[n] = 0; - gwaddr[n] = 0; - dhcpserver[n] = 0; - dnsserver[n] = 0; - } - // Set a unique hostname, use DHCP_HOSTNAME- plus last octet of mac address - // hostname[8] = 'A' + (macaddr[5] >> 4); - // hostname[9] = 'A' + (macaddr[5] & 0x0F); - for (int i = 0; i < HOSTNAME_LEN; i++) { - hostname[i] = DHCP_HOSTNAME[i]; - } - - hostname[HOSTNAME_LEN] = '-'; - hostname[HOSTNAME_LEN + 1] = 'A' + (macaddr[5] >> 4); - hostname[HOSTNAME_LEN + 2] = 'A' + (macaddr[5] & 0x0F); - hostname[HOSTNAME_LEN + 3] = '\0'; - - // Reception of broadcast packets turned off by default, but - // it has been shown that some routers send responses as - // broadcasts. Enable here and disable later - enc28j60EnableBroadcast(); - dhcp_send(buf, DHCPDISCOVER); - dhcpState = DHCP_STATE_DISCOVER; -} - -void dhcp_request_ip(uint8_t *buf) { - dhcp_send(buf, DHCPREQUEST); - dhcpState = DHCP_STATE_REQUEST; -} - -// Main DHCP message sending function, either DHCPDISCOVER or DHCPREQUEST -void dhcp_send(uint8_t *buf, uint8_t requestType) { - int i = 0; - haveDhcpAnswer = 0; - dhcp_ansError = 0; - dhcptid_l++; // increment for next request, finally wrap - // destination IP gets replaced after this call - - memset(buf, 0, 400); // XXX OUCH! - send_udp_prepare(buf, (DHCPCLIENT_SRC_PORT_H << 8) | (dhcptid_l & 0xff), dhcpip, DHCP_DEST_PORT); - - memcpy(buf + ETH_SRC_MAC, macaddr, 6); - memset(buf + ETH_DST_MAC, 0xFF, 6); - buf[IP_TOTLEN_L_P] = 0x82; - buf[IP_PROTO_P] = IP_PROTO_UDP_V; - memset(buf + IP_DST_P, 0xFF, 4); - buf[UDP_DST_PORT_L_P] = DHCP_SRC_PORT; - buf[UDP_SRC_PORT_H_P] = 0; - buf[UDP_SRC_PORT_L_P] = DHCP_DEST_PORT; - - // Build DHCP Packet from buf[UDP_DATA_P] - // Make dhcpPtr start of UDP data buffer - dhcpData *dhcpPtr = (dhcpData *)&buf[UDP_DATA_P]; - // 0-3 op, htype, hlen, hops - dhcpPtr->op = DHCP_BOOTREQUEST; - dhcpPtr->htype = 1; - dhcpPtr->hlen = 6; - dhcpPtr->hops = 0; - // 4-7 xid - memcpy(&dhcpPtr->xid, ¤tXid, sizeof(currentXid)); - // 8-9 secs - memcpy(&dhcpPtr->secs, ¤tSecs, sizeof(currentSecs)); - // 16-19 yiaddr - memset(dhcpPtr->yiaddr, 0, 4); - // 28-43 chaddr(16) - memcpy(dhcpPtr->chaddr, macaddr, 6); - - // options defined as option, length, value - bufPtr = buf + UDP_DATA_P + sizeof(dhcpData); - // Magic cookie 99, 130, 83 and 99 - addToBuf(99); - addToBuf(130); - addToBuf(83); - addToBuf(99); - - // Set correct options - // Option 1 - DHCP message type - addToBuf(53); // DHCPDISCOVER, DHCPREQUEST - addToBuf(1); // Length - addToBuf(requestType); // Value - - // Client Identifier Option, this is the client mac address - addToBuf(61); // Client identifier - addToBuf(7); // Length - addToBuf(0x01); // Value - for (i = 0; i < 6; i++) addToBuf(macaddr[i]); - - // Host name Option - addToBuf(12); // Host name - addToBuf(HOSTNAME_SIZE); // Length - for (i = 0; i < HOSTNAME_SIZE; i++) addToBuf(hostname[i]); - - if (requestType == DHCPREQUEST) { - // Request IP address - addToBuf(50); // Requested IP address - addToBuf(4); // Length - for (i = 0; i < 4; i++) addToBuf(dhcpip[i]); - - // Request using server ip address - addToBuf(54); // Server IP address - addToBuf(4); // Length - for (i = 0; i < 4; i++) addToBuf(dhcpserver[i]); - } - - // Additional information in parameter list - minimal list for what we need - addToBuf(55); // Parameter request list - addToBuf(3); // Length - addToBuf(1); // Subnet mask - addToBuf(3); // Route/Gateway - addToBuf(6); // DNS Server - - // payload len should be around 300 - addToBuf(255); // end option - send_udp_transmit(buf, bufPtr - buf - UDP_DATA_P); -} - -// Examine packet, if dhcp then process, if just exit. -// Perform lease expiry checks and initiate renewal -// process the answer from the dhcp server: -// return 1 on sucessful processing of answer. -// We set also the variable haveDhcpAnswer -// Either DHCPOFFER, DHCPACK or DHCPNACK -// Return 0 for nothing processed, 1 for done soemthing -uint8_t check_for_dhcp_answer(uint8_t *buf, uint16_t plen) { - // Map struct onto payload - dhcpData *dhcpPtr = (dhcpData *)&buf[UDP_DATA_P]; - if (plen >= 70 && buf[UDP_SRC_PORT_L_P] == DHCP_SRC_PORT && dhcpPtr->op == DHCP_BOOTREPLY && - !memcmp(&dhcpPtr->xid, ¤tXid, 4)) { - // Check for lease expiry - // uint32_t currentSecs = millis(); - int optionIndex = UDP_DATA_P + sizeof(dhcpData) + 4; - if (buf[optionIndex] == 53) switch (buf[optionIndex + 2]) { - case DHCPOFFER: - return have_dhcpoffer(buf, plen); - case DHCPACK: - return have_dhcpack(buf, plen); - } - } - return 0; -} - -uint8_t have_dhcpoffer(uint8_t *buf, uint16_t plen) { - // Map struct onto payload - dhcpData *dhcpPtr = (dhcpData *)((uint8_t *)buf + UDP_DATA_P); - // Offered IP address is in yiaddr - memcpy(dhcpip, dhcpPtr->yiaddr, 4); - // Scan through variable length option list identifying options we want - uint8_t *ptr = (uint8_t *)(dhcpPtr + 1) + 4; - do { - uint8_t option = *ptr++; - uint8_t optionLen = *ptr++; - uint8_t i; - switch (option) { - case 1: - memcpy(dhcpmask, ptr, 4); - break; - case 3: - memcpy(gwaddr, ptr, 4); - break; - case 6: - memcpy(dnsserver, ptr, 4); - break; - case 51: - leaseTime = 0; - for (i = 0; i < 4; i++) leaseTime = (leaseTime + ptr[i]) << 8; - leaseTime *= 1000; // milliseconds - break; - case 54: - memcpy(dhcpserver, ptr, 4); - break; - } - ptr += optionLen; - } while (ptr < buf + plen); - dhcp_request_ip(buf); - return 1; -} - -uint8_t have_dhcpack(uint8_t *buf, uint16_t plen) { - dhcpState = DHCP_STATE_OK; - leaseStart = HAL_GetTick(); - // Turn off broadcast. Application if it needs it can re-enable it - enc28j60DisableBroadcast(); - return 2; -} - -#endif - -/* end of dhcp.c */ diff --git a/src/enc28j60.c b/src/enc28j60.c deleted file mode 100644 index 0e5c1a4..0000000 --- a/src/enc28j60.c +++ /dev/null @@ -1,533 +0,0 @@ -#include "enc28j60.h" -#include "error_handler.h" - -static uint8_t Enc28j60Bank; -static uint16_t gNextPacketPtr; -static uint8_t erxfcon; -static SPI_HandleTypeDef *hspi = NULL; - -#if 0 -void ENC28J60_hspi->Instance_Configuration(void) -{ - SPI_InitTypeDef SPI_InitStructure; - - /* Enable the SPI periph */ - RCC_APB2PeriphClockCmd(RCC_APB2Periph_hspi->Instance, ENABLE); - - /* SPI configuration -------------------------------------------------------*/ - SPI_I2S_DeInit(hspi->Instance); - SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; - SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; - SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; - SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; - SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; - SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_64; - SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; - SPI_InitStructure.SPI_CRCPolynomial = 7; - SPI_InitStructure.SPI_Mode = SPI_Mode_Master; - SPI_Init(hspi->Instance, &SPI_InitStructure); - - /* Enable hspi->Instance */ - SPI_Cmd(hspi->Instance, ENABLE); -} - -#endif - -/******************************************************************************* -* Function Name : GPIO_Configuration -* Description : Configures the different GPIO ports. -* Input : None -* Output : None -* Return : None -*******************************************************************************/ -#if 0 -void ENC28J60_GPIO_Configuration(void) -{ - GPIO_InitTypeDef GPIO_InitStructure; - - /* Enable SCK, MOSI and MISO GPIO clocks */ - RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); - - /* Enable CS GPIO clock */ - RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); - - GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_hspi->Instance); - GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_hspi->Instance); - GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_hspi->Instance); - - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; - GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; - GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_25MHz; - - /* SPI SCK pin configuration */ - GPIO_InitStructure.GPIO_Pin = GPIO_PIN_3; - GPIO_Init(GPIOB, &GPIO_InitStructure); - - /* SPI MOSI pin configuration */ - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; - GPIO_Init(GPIOA, &GPIO_InitStructure); - - /* SPI MISO pin configuration */ - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; - GPIO_Init(GPIOA, &GPIO_InitStructure); - - /* Configure GPIO PIN for Chip select */ - GPIO_InitStructure.GPIO_Pin = GPIO_PIN_4; - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; - GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; - GPIO_Init(GPIOA, &GPIO_InitStructure); - - /* Deselect : Chip Select high */ - GPIO_SetBits(GPIOA, GPIO_PIN_4); -} -#endif - -void enc28j60_set_spi(SPI_HandleTypeDef *hspi_new) -{ - hspi = hspi_new; -} - -void error (float error_num, char infinite); -unsigned char ENC28J60_SendByte(uint8_t tx) -{ - if (hspi == NULL) - return 0; -// while(SPI_I2S_GetFlagStatus(hspi->Instance, SPI_I2S_FLAG_TXE) == RESET); -// SPI_WaitFlagStateUntilTimeout(hspi, SPI_FLAG_TXE, RESET, 0xffffffff); - -/* - //SPI_I2S_SendData(hspi->Instance, dt); - int r; - r = HAL_SPI_Transmit(hspi, &dt, 1, 0xffffffff); - - if (r != HAL_OK) - error(r, 0); - //while(SPI_I2S_GetFlagStatus(hspi->Instance, SPI_I2S_FLAG_RXNE) == RESET); -// SPI_WaitFlagStateUntilTimeout(hspi, SPI_FLAG_RXNE, RESET, 0xffffffff); - - //return SPI_I2S_ReceiveData(hspi->Instance); - r = HAL_SPI_Receive(hspi, &dt, 1, 0xffffffff); - - if (r != HAL_OK) - error(r, 0); -*/ - - uint8_t rx = 0; - int r; - - r = HAL_SPI_TransmitReceive(hspi, &tx, &rx, 1, 0xffffffff); - - if (r != HAL_OK) - ENC28j60_Error_Handler(SPI_ERROR); - - return rx; -} - -uint8_t enc28j60ReadOp(uint8_t op, uint8_t address) -{ - uint8_t temp; - enableChip; - // issue read command - ENC28J60_SendByte(op | (address & ADDR_MASK)); - temp = ENC28J60_SendByte(0xFF); - if (address & 0x80) - temp = ENC28J60_SendByte(0xFF); - - // release CS - disableChip; - return temp; -} - -void enc28j60WriteOp(uint8_t op, uint8_t address, uint8_t data) -{ - enableChip; - ENC28J60_SendByte(op | (address & ADDR_MASK)); - ENC28J60_SendByte(data); - disableChip; -} - -void enc28j60PowerDown() { - enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_RXEN); - while(enc28j60Read(ESTAT) & ESTAT_RXBUSY); - while(enc28j60Read(ECON1) & ECON1_TXRTS); - enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PWRSV); -} - -void enc28j60PowerUp() { - enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, ECON2, ECON2_PWRSV); - while(!enc28j60Read(ESTAT) & ESTAT_CLKRDY); -} - - -void enc28j60ReadBuffer(uint16_t len, uint8_t* data) -{ - enableChip; - ENC28J60_SendByte(ENC28J60_READ_BUF_MEM); - while (len--) { - *data++ = ENC28J60_SendByte(0x00); - } - disableChip; - // Remove next line suggested by user epam - not needed -// *data='\0'; -} - -static uint16_t enc28j60ReadBufferWord() { - uint16_t result; - enc28j60ReadBuffer(2, (uint8_t*) &result); - return result; -} - - -void enc28j60WriteBuffer(uint16_t len, uint8_t* data) -{ - enableChip; - ENC28J60_SendByte(ENC28J60_WRITE_BUF_MEM); - while (len--) - ENC28J60_SendByte(*data++); - - disableChip; -} - -void enc28j60SetBank(uint8_t address) -{ - if ((address & BANK_MASK) != Enc28j60Bank) { - enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_BSEL1|ECON1_BSEL0); - Enc28j60Bank = address & BANK_MASK; - enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, Enc28j60Bank>>5); - } -} - -uint8_t enc28j60Read(uint8_t address) -{ - // set the bank - enc28j60SetBank(address); - // do the read - return enc28j60ReadOp(ENC28J60_READ_CTRL_REG, address); -} - -void enc28j60WriteWord(uint8_t address, uint16_t data) { - enc28j60Write(address, data & 0xff); - enc28j60Write(address + 1, data >> 8); -} - -// read upper 8 bits -uint16_t enc28j60PhyReadH(uint8_t address) -{ - // Set the right address and start the register read operation - enc28j60Write(MIREGADR, address); - enc28j60Write(MICMD, MICMD_MIIRD); - Delay(15); - - // wait until the PHY read completes - while(enc28j60Read(MISTAT) & MISTAT_BUSY); - - // reset reading bit - enc28j60Write(MICMD, 0x00); - - return (enc28j60Read(MIRDH)); -} - - -void enc28j60Write(uint8_t address, uint8_t data) -{ - // set the bank - enc28j60SetBank(address); - // do the write - enc28j60WriteOp(ENC28J60_WRITE_CTRL_REG, address, data); -} - - -void enc28j60PhyWrite(uint8_t address, uint16_t data) -{ - // set the PHY register address - enc28j60Write(MIREGADR, address); - // write the PHY data - enc28j60Write(MIWRL, data); - enc28j60Write(MIWRH, data>>8); - // wait until the PHY write completes - while(enc28j60Read(MISTAT) & MISTAT_BUSY){ - Delay(15); - } -} -/* -static void enc28j60PhyWriteWord(byte address, word data) { - enc28j60Write(MIREGADR, address); - //enc28j60WriteByte(MIREGADR, address); - enc28j60WriteWord(MIWRL, data); - while (enc28j60ReadByte(MISTAT) & MISTAT_BUSY) - ; -} -*/ -void enc28j60clkout(uint8_t clk) -{ - //setup clkout: 2 is 12.5MHz: - enc28j60Write(ECOCON, clk & 0x7); -} - -void enc28j60Init( uint8_t* macaddr ) -{ - enableChip; // ss=0 - - // perform system reset - enc28j60WriteOp(ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET); - HAL_Delay(50); - // check CLKRDY bit to see if reset is complete - // The CLKRDY does not work. See Rev. B4 Silicon Errata point. Just wait. - //while(!(enc28j60Read(ESTAT) & ESTAT_CLKRDY)); - // do bank 0 stuff - // initialize receive buffer - // 16-bit transfers, must write low byte first - // set receive buffer start address - gNextPacketPtr = RXSTART_INIT; - // Rx start - enc28j60WriteWord(ERXSTL, RXSTART_INIT); - // set receive pointer address - enc28j60WriteWord(ERXRDPTL, RXSTART_INIT); - // RX end - enc28j60WriteWord(ERXNDL, RXSTOP_INIT); - // TX start - enc28j60WriteWord(ETXSTL, TXSTART_INIT); - // TX end - enc28j60WriteWord(ETXNDL, TXSTOP_INIT); - // do bank 1 stuff, packet filter: - // For broadcast packets we allow only ARP packtets - // All other packets should be unicast only for our mac (MAADR) - // - // The pattern to match on is therefore - // Type ETH.DST - // ARP BROADCAST - // 06 08 -- ff ff ff ff ff ff -> ip checksum for theses bytes=f7f9 - // in binary these poitions are:11 0000 0011 1111 - // This is hex 303F->EPMM0=0x3f,EPMM1=0x30 - - //enc28j60Write(ERXFCON, ERXFCON_UCEN|ERXFCON_CRCEN|ERXFCON_PMEN); - //Change to add ERXFCON_BCEN recommended by epam - //enc28j60Write(ERXFCON, ERXFCON_UCEN|ERXFCON_CRCEN|ERXFCON_PMEN|ERXFCON_BCEN); - erxfcon = ERXFCON_UCEN|ERXFCON_CRCEN|ERXFCON_PMEN|ERXFCON_BCEN; - enc28j60Write(ERXFCON, erxfcon ); - enc28j60WriteWord(EPMM0, 0x303f); - enc28j60WriteWord(EPMCSL, 0xf7f9); - // - // do bank 2 stuff - // enable MAC receive - enc28j60Write(MACON1, MACON1_MARXEN|MACON1_TXPAUS|MACON1_RXPAUS); - // bring MAC out of reset - enc28j60Write(MACON2, 0x00); - // enable automatic padding to 60bytes and CRC operations - enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, MACON3, MACON3_PADCFG0|MACON3_TXCRCEN|MACON3_FRMLNEN); //|MACON3_FULDPX); - // set inter-frame gap (non-back-to-back) - enc28j60WriteWord(MAIPGL, 0x0C12); - // set inter-frame gap (back-to-back) - enc28j60Write(MABBIPG, 0x12); - // Set the maximum packet size which the controller will accept - // Do not send packets longer than MAX_FRAMELEN: - enc28j60WriteWord(MAMXFLL, MAX_FRAMELEN); - // do bank 3 stuff - // write MAC address - // NOTE: MAC address in ENC28J60 is byte-backward - enc28j60Write(MAADR5, macaddr[0]); - enc28j60Write(MAADR4, macaddr[1]); - enc28j60Write(MAADR3, macaddr[2]); - enc28j60Write(MAADR2, macaddr[3]); - enc28j60Write(MAADR1, macaddr[4]); - enc28j60Write(MAADR0, macaddr[5]); - // no loopback of transmitted frames - enc28j60PhyWrite(PHCON2, PHCON2_HDLDIS); - // switch to bank 0 - enc28j60SetBank(ECON1); - // enable interrutps - enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, EIE, EIE_INTIE|EIE_PKTIE); - // enable packet reception - enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN); -} - -// read the revision of the chip: -uint8_t enc28j60getrev(void) -{ - uint8_t rev; - rev=enc28j60Read(EREVID); - // microchip forgot to step the number on the silcon when they - // released the revision B7. 6 is now rev B7. We still have - // to see what they do when they release B8. At the moment - // there is no B8 out yet - if (rev>5) rev++; - return(rev); -} - -// A number of utility functions to enable/disable broadcast and multicast bits -void enc28j60EnableBroadcast( void ) { - erxfcon |= ERXFCON_BCEN; - enc28j60Write(ERXFCON, erxfcon); -} - -void enc28j60DisableBroadcast( void ) { - erxfcon &= ~ERXFCON_BCEN; - enc28j60Write(ERXFCON, erxfcon); -} - -void enc28j60EnableMulticast( void ) { - erxfcon |= ERXFCON_MCEN; - enc28j60Write(ERXFCON, erxfcon); -} - -void enc28j60DisableMulticast( void ) { - erxfcon &= ~ERXFCON_MCEN; - enc28j60Write(ERXFCON, erxfcon); -} - - -// link status -uint8_t enc28j60linkup(void) -{ - // bit 10 (= bit 3 in upper reg) - return(enc28j60PhyReadH(PHSTAT2) & 4); -} - -void enc28j60PacketSend(uint16_t len, uint8_t* packet) -{ - // Check no transmit in progress - while (enc28j60ReadOp(ENC28J60_READ_CTRL_REG, ECON1) & ECON1_TXRTS) - { - // Reset the transmit logic problem. See Rev. B4 Silicon Errata point 12. - if( (enc28j60Read(EIR) & EIR_TXERIF) ) { - enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRST); - enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRST); - } - } - - // Set the write pointer to start of transmit buffer area - enc28j60WriteWord(EWRPTL, TXSTART_INIT); - // Set the TXND pointer to correspond to the packet size given - enc28j60WriteWord(ETXNDL, (TXSTART_INIT+len)); - // write per-packet control byte (0x00 means use macon3 settings) - enc28j60WriteOp(ENC28J60_WRITE_BUF_MEM, 0, 0x00); - // copy the packet into the transmit buffer - enc28j60WriteBuffer(len, packet); - // send the contents of the transmit buffer onto the network - enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRTS); - // Reset the transmit logic problem. See Rev. B4 Silicon Errata point 12. -} - -// just probe if there might be a packet -//uint8_t enc28j60hasRxPkt(void) -//{ -// return enc28j60ReadByte(EPKTCNT) > 0; -//} - -// Gets a packet from the network receive buffer, if one is available. -// The packet will by headed by an ethernet header. -// maxlen The maximum acceptable length of a retrieved packet. -// packet Pointer where packet data should be stored. -// Returns: Packet length in bytes if a packet was retrieved, zero otherwise. -uint16_t enc28j60PacketReceive(uint16_t maxlen, uint8_t* packet) -{ - uint16_t rxstat; - uint16_t len; - // check if a packet has been received and buffered - //if( !(enc28j60Read(EIR) & EIR_PKTIF) ){ - // The above does not work. See Rev. B4 Silicon Errata point 6. - if( enc28j60Read(EPKTCNT) ==0 ){ - return(0); - } - - // Set the read pointer to the start of the received packet - enc28j60WriteWord(ERDPTL, gNextPacketPtr); - //enc28j60Write(ERDPTL, (gNextPacketPtr &0xFF)); - //enc28j60Write(ERDPTH, (gNextPacketPtr)>>8); - // read the next packet pointer - gNextPacketPtr = enc28j60ReadBufferWord(); - //gNextPacketPtr = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0); - //gNextPacketPtr |= enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0)<<8; - // read the packet length (see datasheet page 43) - len = enc28j60ReadBufferWord() - 4; - //len = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0); - //len |= enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0)<<8; - //len-=4; //remove the CRC count - // read the receive status (see datasheet page 43) - rxstat = enc28j60ReadBufferWord(); - //rxstat = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0); - //rxstat |= ((uint16_t)enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0))<<8; - // limit retrieve length - if (len>maxlen-1){ - len=maxlen-1; - } - // check CRC and symbol errors (see datasheet page 44, table 7-3): - // The ERXFCON.CRCEN is set by default. Normally we should not - // need to check this. - if ((rxstat & 0x80)==0){ - // invalid - len=0; - }else{ - // copy the packet from the receive buffer - enc28j60ReadBuffer(len, packet); - } - // Move the RX read pointer to the start of the next received packet - // This frees the memory we just read out - enc28j60WriteWord(ERXRDPTL, gNextPacketPtr ); - //enc28j60Write(ERXRDPTL, (gNextPacketPtr &0xFF)); - //enc28j60Write(ERXRDPTH, (gNextPacketPtr)>>8); - // However, compensate for the errata point 13, rev B4: enver write an even address! - if ((gNextPacketPtr - 1 < RXSTART_INIT) - || (gNextPacketPtr -1 > RXSTOP_INIT)) { - enc28j60WriteWord(ERXRDPTL, RXSTOP_INIT); - //enc28j60Write(ERXRDPTL, (RXSTOP_INIT)&0xFF); - //enc28j60Write(ERXRDPTH, (RXSTOP_INIT)>>8); - } else { - enc28j60WriteWord(ERXRDPTL, (gNextPacketPtr-1)); - //enc28j60Write(ERXRDPTL, (gNextPacketPtr-1)&0xFF); - //enc28j60Write(ERXRDPTH, (gNextPacketPtr-1)>>8); - } - // decrement the packet counter indicate we are done with this packet - enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PKTDEC); - return(len); - -/* - uint16_t rxstat; - uint16_t len; - // check if a packet has been received and buffered - //if( !(enc28j60Read(EIR) & EIR_PKTIF) ){ - // The above does not work. See Rev. B4 Silicon Errata point 6. - if( enc28j60Read(EPKTCNT) ==0 ){ - return(0); - } - - // Set the read pointer to the start of the received packet - enc28j60WriteWord(ERDPTL, gNextPacketPtr); - // read the next packet pointer - gNextPacketPtr = enc28j60ReadBufferWord(); - // read the packet length (see datasheet page 43) - len = enc28j60ReadBufferWord() - 4; - // read the receive status (see datasheet page 43) - rxstat = enc28j60ReadBufferWord(); - // limit retrieve length - if (len>maxlen-1){ - len=maxlen-1; - } - // check CRC and symbol errors (see datasheet page 44, table 7-3): - // The ERXFCON.CRCEN is set by default. Normally we should not - // need to check this. - if ((rxstat & 0x80)==0){ - // invalid - len=0; - }else{ - // copy the packet from the receive buffer - enc28j60ReadBuffer(len, packet); - } - // Move the RX read pointer to the start of the next received packet - // This frees the memory we just read out -// enc28j60WriteWord(ERXRDPTL, gNextPacketPtr ); - // However, compensate for the errata point 13, rev B4: enver write an even address! - if ((gNextPacketPtr - 1 < RXSTART_INIT) - || (gNextPacketPtr -1 > RXSTOP_INIT)) { - enc28j60WriteWord(ERXRDPTL, RXSTOP_INIT); - } else { - enc28j60WriteWord(ERXRDPTL, (gNextPacketPtr-1)); - } - // decrement the packet counter indicate we are done with this packet - enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PKTDEC); - return(len); -*/ -} - diff --git a/src/ip_arp_udp_tcp.c b/src/ip_arp_udp_tcp.c deleted file mode 100644 index c882c30..0000000 --- a/src/ip_arp_udp_tcp.c +++ /dev/null @@ -1,1584 +0,0 @@ -/********************************************* - * vim:sw=8:ts=8:si:et - * To use the above modeline in vim you must have "set modeline" in your .vimrc - * - * Author: Guido Socher - * Copyright: GPL V2 - * See http://www.gnu.org/licenses/gpl.html - * - * IP, Arp, UDP and TCP functions. - * - * The TCP implementation uses some size optimisations which are valid - * only if all data can be sent in one single packet. This is however - * not a big limitation for a microcontroller as you will anyhow use - * small web-pages. The web server must send the entire web page in one - * packet. The client "web browser" as implemented here can also receive - * large pages. - * - * Chip type : ATMEGA88/168/328 with ENC28J60 - *********************************************/ -#include -#include -#include -#include "net.h" -#include "enc28j60.h" -#include "ip_arp_udp_tcp.h" - - -#if ETHERSHIELD_DEBUG -void ethershieldDebug(char *message) { - printf("%s\r\n", message); -} -#endif - -// Web server port, used when implementing webserver -static uint8_t wwwport_l=80; // server port -static uint8_t wwwport_h=0; // Note: never use same as TCPCLIENT_SRC_PORT_H - -#if defined (WWW_client) || defined (TCP_client) - -// just lower byte, the upper byte is TCPCLIENT_SRC_PORT_H: -static uint8_t tcpclient_src_port_l=1; -static uint8_t tcp_fd=0; // a file descriptor, will be encoded into the port -static uint8_t tcp_client_state=0; -// TCP client Destination port -static uint8_t tcp_client_port_h=0; -static uint8_t tcp_client_port_l=0; -// This function will be called if we ever get a result back from the -// TCP connection to the sever: -// close_connection= your_client_tcp_result_callback(uint8_t fd, uint8_t statuscode,uint16_t data_start_pos_in_buf, uint16_t len_of_data){...your code} -// statuscode=0 means the buffer has valid data -static uint8_t (*client_tcp_result_callback)(uint8_t,uint8_t,uint16_t,uint16_t); -// len_of_data_filled_in=your_client_tcp_datafill_callback(uint8_t fd){...your code} -static uint16_t (*client_tcp_datafill_callback)(uint8_t); -#endif -#define TCPCLIENT_SRC_PORT_H 11 -#if defined (WWW_client) -// WWW_client uses TCP_client -#if ! defined(TCP_client) -#define TCP_client 1 -#endif -static uint8_t www_fd=0; -static uint8_t browsertype=0; // 0 = get, 1 = post -static void (*client_browser_callback)(uint8_t,uint16_t,uint16_t); -static char *client_additionalheaderline; -static char *client_method; // for POST or PUT, no trailing space needed -static char *client_urlbuf; -static char *client_hoststr; -static char *client_postval; -static char *client_urlbuf_var; -static uint8_t *bufptr=0; // ugly workaround for backward compatibility -#endif -static void (*icmp_callback)(uint8_t *ip); -// 0=wait, 1=first req no anser, 2=have gwmac, 4=refeshing but have gw mac, 8=accept an arp reply -#define WGW_INITIAL_ARP 1 -#define WGW_HAVE_GW_MAC 2 -#define WGW_REFRESHING 4 -#define WGW_ACCEPT_ARP_REPLY 8 -static int16_t delaycnt=0; -static uint8_t gwip[4]; -static uint8_t gwmacaddr[6]; -static uint8_t tcpsrvip[4]; -static volatile uint8_t waitgwmac=WGW_INITIAL_ARP; - -uint8_t macaddr[6]; -static uint8_t ipaddr[4]; -static uint16_t info_data_len=0; -static uint8_t seqnum=0xa; // my initial tcp sequence number - -#define CLIENTMSS 550 -#define TCP_DATA_START ((uint16_t)TCP_SRC_PORT_H_P+(buf[TCP_HEADER_LEN_P]>>4)*4) - -const char arpreqhdr[] ={0,1,8,0,6,4,0,1}; -#if defined (NTP_client) || defined (WOL_client) || defined (UDP_client) || defined (TCP_client) || defined (PING_client) -const char iphdr[] ={0x45,0,0,0x82,0,0,0x40,0,0x20}; // 0x82 is the total len on ip, 0x20 is ttl (time to live) -#endif -#ifdef NTP_client -const char ntpreqhdr[] ={0xe3,0,4,0xfa,0,1,0,1,0,0}; -#endif - -#ifndef DISABLE_IP_STACK - -// The Ip checksum is calculated over the ip header only starting -// with the header length field and a total length of 20 bytes -// unitl ip.dst -// You must set the IP checksum field to zero before you start -// the calculation. -// len for ip is 20. -// -// For UDP/TCP we do not make up the required pseudo header. Instead we -// use the ip.src and ip.dst fields of the real packet: -// The udp checksum calculation starts with the ip.src field -// Ip.src=4bytes,Ip.dst=4 bytes,Udp header=8bytes + data length=16+len -// In other words the len here is 8 + length over which you actually -// want to calculate the checksum. -// You must set the checksum field to zero before you start -// the calculation. -// The same algorithm is also used for udp and tcp checksums. -// len for udp is: 8 + 8 + data length -// len for tcp is: 4+4 + 20 + option len + data length -// -// For more information on how this algorithm works see: -// http://www.netfor2.com/checksum.html -// http://www.msc.uky.edu/ken/cs471/notes/chap3.htm -// The RFC has also a C code example: http://www.faqs.org/rfcs/rfc1071.html -uint16_t checksum(uint8_t *buf, uint16_t len,uint8_t type) -{ - // type 0=ip , icmp - // 1=udp - // 2=tcp - uint32_t sum = 0; - - //if(type==0){ - // // do not add anything, standard IP checksum as described above - // // Usable for ICMP and IP header - //} - if(type==1){ - sum+=IP_PROTO_UDP_V; // protocol udp - // the length here is the length of udp (data+header len) - // =length given to this function - (IP.scr+IP.dst length) - sum+=len-8; // = real udp len - } - if(type==2){ - sum+=IP_PROTO_TCP_V; - // the length here is the length of tcp (data+header len) - // =length given to this function - (IP.scr+IP.dst length) - sum+=len-8; // = real tcp len - } - // build the sum of 16bit words - while(len >1){ - sum += 0xFFFF & (((uint32_t)*buf<<8)|*(buf+1)); - buf+=2; - len-=2; - } - // if there is a byte left then add it (padded with zero) - if (len){ - sum += ((uint32_t)(0xFF & *buf))<<8; - } - // now calculate the sum over the bytes in the sum - // until the result is only 16bit long - while (sum>>16){ - sum = (sum & 0xFFFF)+(sum >> 16); - } - // build 1's complement: - return( (uint16_t) sum ^ 0xFFFF); -} - -#endif - -// This initializes the web server -// you must call this function once before you use any of the other functions: -void init_ip_arp_udp_tcp(uint8_t *mymac,uint8_t *myip,uint16_t port) -{ - wwwport_h=(port>>8)&0xff; - wwwport_l=(port&0xff); - memcpy(ipaddr, myip, 4); - memcpy(macaddr, mymac, 6); -} - -#ifndef DISABLE_IP_STACK - -uint8_t check_ip_message_is_from(uint8_t *buf,uint8_t *ip) -{ - return !memcmp(&buf[IP_SRC_P], ip, 4); -} - -uint8_t eth_type_is_arp_and_my_ip(uint8_t *buf,uint16_t len){ - // - if (len<41){ - return(0); - } - if(buf[ETH_TYPE_H_P] != ETHTYPE_ARP_H_V || - buf[ETH_TYPE_L_P] != ETHTYPE_ARP_L_V){ - return(0); - } - - if (memcmp(&buf[ETH_ARP_DST_IP_P], ipaddr, 4)) { - return 0; - } - - return(1); -} - -uint8_t eth_type_is_ip_and_my_ip(uint8_t *buf,uint16_t len){ - //eth+ip+udp header is 42 - if (len<42){ - return(0); - } - if(buf[ETH_TYPE_H_P]!=ETHTYPE_IP_H_V || - buf[ETH_TYPE_L_P]!=ETHTYPE_IP_L_V){ - return(0); - } - if (buf[IP_HEADER_LEN_VER_P]!=0x45){ - // must be IP V4 and 20 byte header - return(0); - } - if (memcmp(&buf[IP_DST_P], ipaddr, 4)) { - return 0; - } - return(1); -} - -// make a return eth header from a received eth packet -void make_eth(uint8_t *buf) -{ - //copy the destination mac from the source and fill my mac into src - memcpy(&buf[ETH_DST_MAC], &buf[ETH_SRC_MAC], 6); - memcpy(&buf[ETH_SRC_MAC], macaddr, 6); -} - -// make a new eth header for IP packet -void make_eth_ip_new(uint8_t *buf, uint8_t* dst_mac) -{ - //copy the destination mac from the source and fill my mac into src - memcpy(&buf[ETH_DST_MAC], dst_mac, 6); - memcpy(&buf[ETH_SRC_MAC], macaddr, 6); - - buf[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; - buf[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; -} - - -void fill_ip_hdr_checksum(uint8_t *buf) -{ - uint16_t ck; - // clear the 2 byte checksum - buf[IP_CHECKSUM_P]=0; - buf[IP_CHECKSUM_P+1]=0; - buf[IP_FLAGS_P]=0x40; // don't fragment - buf[IP_FLAGS_P+1]=0; // fragement offset - buf[IP_TTL_P]=64; // ttl - // calculate the checksum: - ck=checksum(&buf[IP_P], IP_HEADER_LEN,0); - buf[IP_CHECKSUM_P]=ck>>8; - buf[IP_CHECKSUM_P+1]=ck & 0xff; -} - -// is it an arp reply (no len check here, you must first call eth_type_is_arp_and_my_ip) -uint8_t eth_type_is_arp_reply(uint8_t *buf){ - return (buf[ETH_ARP_OPCODE_L_P]==ETH_ARP_OPCODE_REPLY_L_V); -} - -// is it an arp request (no len check here, you must first call eth_type_is_arp_and_my_ip) -uint8_t eth_type_is_arp_req(uint8_t *buf){ - return (buf[ETH_ARP_OPCODE_L_P]==ETH_ARP_OPCODE_REQ_L_V); -} - -// make a return ip header from a received ip packet -static uint16_t ip_identifier = 1; - -// make a new ip header for tcp packet - -// make a return ip header from a received ip packet -void make_ip_tcp_new(uint8_t *buf, uint16_t len,uint8_t *dst_ip) -{ - // set ipv4 and header length - buf[ IP_P ] = IP_V4_V | IP_HEADER_LENGTH_V; - - // set TOS to default 0x00 - buf[ IP_TOS_P ] = 0x00; - - // set total length - buf[ IP_TOTLEN_H_P ] = (len >>8)& 0xff; - buf[ IP_TOTLEN_L_P ] = len & 0xff; - - // set packet identification - buf[ IP_ID_H_P ] = (ip_identifier >>8) & 0xff; - buf[ IP_ID_L_P ] = ip_identifier & 0xff; - ip_identifier++; - - // set fragment flags - buf[ IP_FLAGS_H_P ] = 0x00; - buf[ IP_FLAGS_L_P ] = 0x00; - - // set Time To Live - buf[ IP_TTL_P ] = 128; - - // set ip packettype to tcp/udp/icmp... - buf[ IP_PROTO_P ] = IP_PROTO_TCP_V; - - // set source and destination ip address - memcpy(&buf[IP_DST_P], dst_ip, 4); - memcpy(&buf[IP_SRC_P], ipaddr, 4); - fill_ip_hdr_checksum(buf); -} - - -void make_ip(uint8_t *buf) -{ - memcpy(&buf[IP_DST_P], &buf[IP_SRC_P], 4); - memcpy(&buf[IP_SRC_P], ipaddr, 4); - fill_ip_hdr_checksum(buf); -} - -// swap seq and ack number and count ack number up -void step_seq(uint8_t *buf,uint16_t rel_ack_num,uint8_t cp_seq) -{ - uint8_t i; - uint8_t tseq; - i=4; - // sequence numbers: - // add the rel ack num to SEQACK - while(i>0) { - rel_ack_num=buf[TCP_SEQ_H_P+i-1]+rel_ack_num; - tseq=buf[TCP_SEQACK_H_P+i-1]; - buf[TCP_SEQACK_H_P+i-1]=0xff&rel_ack_num; - if (cp_seq){ - // copy the acknum sent to us into the sequence number - buf[TCP_SEQ_H_P+i-1]=tseq; - }else{ - buf[TCP_SEQ_H_P+i-1]= 0; // some preset vallue - } - rel_ack_num=rel_ack_num>>8; - i--; - } -} - -// make a return tcp header from a received tcp packet -// rel_ack_num is how much we must step the seq number received from the -// other side. We do not send more than 765 bytes of text (=data) in the tcp packet. -// No mss is included here. -// -// After calling this function you can fill in the first data byte at TCP_OPTIONS_P+4 -// If cp_seq=0 then an initial sequence number is used (should be use in synack) -// otherwise it is copied from the packet we received -void make_tcphead(uint8_t *buf,uint16_t rel_ack_num,uint8_t cp_seq) -{ - uint8_t i; - // copy ports: - i=buf[TCP_DST_PORT_H_P]; - buf[TCP_DST_PORT_H_P]=buf[TCP_SRC_PORT_H_P]; - buf[TCP_SRC_PORT_H_P]=i; - // - i=buf[TCP_DST_PORT_L_P]; - buf[TCP_DST_PORT_L_P]=buf[TCP_SRC_PORT_L_P]; - buf[TCP_SRC_PORT_L_P]=i; - step_seq(buf,rel_ack_num,cp_seq); - // zero the checksum - buf[TCP_CHECKSUM_H_P]=0; - buf[TCP_CHECKSUM_L_P]=0; - // no options: - // 20 bytes: - // The tcp header length is only a 4 bit field (the upper 4 bits). - // It is calculated in units of 4 bytes. - // E.g 20 bytes: 20/4=6 => 0x50=header len field - buf[TCP_HEADER_LEN_P]=0x50; -} - -void make_arp_answer_from_request(uint8_t *buf) -{ - // - make_eth(buf); - buf[ETH_ARP_OPCODE_H_P]=ETH_ARP_OPCODE_REPLY_H_V; - buf[ETH_ARP_OPCODE_L_P]=ETH_ARP_OPCODE_REPLY_L_V; - // fill the mac addresses: - memcpy(&buf[ETH_ARP_DST_MAC_P], &buf[ETH_ARP_SRC_MAC_P], 6); - memcpy(&buf[ETH_ARP_SRC_MAC_P], macaddr, 6); - // fill the ip addresses - memcpy(&buf[ETH_ARP_DST_IP_P], &buf[ETH_ARP_SRC_IP_P], 4); - memcpy(&buf[ETH_ARP_SRC_IP_P], ipaddr, 4); - // eth+arp is 42 bytes: - enc28j60PacketSend(42,buf); -} - -void make_echo_reply_from_request(uint8_t *buf,uint16_t len) -{ - make_eth(buf); - make_ip(buf); - buf[ICMP_TYPE_P]=ICMP_TYPE_ECHOREPLY_V; - // we changed only the icmp.type field from request(=8) to reply(=0). - // we can therefore easily correct the checksum: - if (buf[ICMP_CHECKSUM_P] > (0xff-0x08)){ - buf[ICMP_CHECKSUM_P+1]++; - } - buf[ICMP_CHECKSUM_P]+=0x08; - // - enc28j60PacketSend(len,buf); -} - -// you can send a max of 220 bytes of data -void make_udp_reply_from_request(uint8_t *buf,char *data,uint16_t datalen,uint16_t port) -{ - uint16_t ck; - make_eth(buf); - if (datalen>220){ - datalen=220; - } - // total length field in the IP header must be set: - buf[IP_TOTLEN_H_P]=(IP_HEADER_LEN+UDP_HEADER_LEN+datalen) >>8; - buf[IP_TOTLEN_L_P]=(IP_HEADER_LEN+UDP_HEADER_LEN+datalen) & 0xff; - make_ip(buf); - // send to port: - //buf[UDP_DST_PORT_H_P]=port>>8; - //buf[UDP_DST_PORT_L_P]=port & 0xff; - // sent to port of sender and use "port" as own source: - buf[UDP_DST_PORT_H_P]=buf[UDP_SRC_PORT_H_P]; - buf[UDP_DST_PORT_L_P]= buf[UDP_SRC_PORT_L_P]; - buf[UDP_SRC_PORT_H_P]=port>>8; - buf[UDP_SRC_PORT_L_P]=port & 0xff; - // calculte the udp length: - buf[UDP_LEN_H_P]=(UDP_HEADER_LEN+datalen) >> 8; - buf[UDP_LEN_L_P]=(UDP_HEADER_LEN+datalen) & 0xff; - // zero the checksum - buf[UDP_CHECKSUM_H_P]=0; - buf[UDP_CHECKSUM_L_P]=0; - // copy the data: - memcpy(&buf[UDP_DATA_P], data, datalen); - - ck=checksum(&buf[IP_SRC_P], 16 + datalen,1); - buf[UDP_CHECKSUM_H_P]=ck>>8; - buf[UDP_CHECKSUM_L_P]=ck & 0xff; - enc28j60PacketSend(UDP_HEADER_LEN+IP_HEADER_LEN+ETH_HEADER_LEN+datalen,buf); -} - -// this is for the server not the client: -void make_tcp_synack_from_syn(uint8_t *buf) -{ - uint16_t ck; - make_eth(buf); - // total length field in the IP header must be set: - // 20 bytes IP + 24 bytes (20tcp+4tcp options) - buf[IP_TOTLEN_H_P]=0; - buf[IP_TOTLEN_L_P]=IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+4; - make_ip(buf); - buf[TCP_FLAGS_P]=TCP_FLAGS_SYNACK_V; - make_tcphead(buf,1,0); - // put an inital seq number - buf[TCP_SEQ_H_P+0]= 0; - buf[TCP_SEQ_H_P+1]= 0; - // we step only the second byte, this allows us to send packts - // with 255 bytes, 512 or 765 (step by 3) without generating - // overlapping numbers. - buf[TCP_SEQ_H_P+2]= seqnum; - buf[TCP_SEQ_H_P+3]= 0; - // step the inititial seq num by something we will not use - // during this tcp session: - seqnum+=3; - // add an mss options field with MSS to 1280: - // 1280 in hex is 0x500 - buf[TCP_OPTIONS_P]=2; - buf[TCP_OPTIONS_P+1]=4; - buf[TCP_OPTIONS_P+2]=0x05; - buf[TCP_OPTIONS_P+3]=0x0; - // The tcp header length is only a 4 bit field (the upper 4 bits). - // It is calculated in units of 4 bytes. - // E.g 24 bytes: 24/4=6 => 0x60=header len field - buf[TCP_HEADER_LEN_P]=0x60; - // here we must just be sure that the web browser contacting us - // will send only one get packet - buf[TCP_WIN_SIZE]=0x5; // 1400=0x578 - buf[TCP_WIN_SIZE+1]=0x78; - // calculate the checksum, len=8 (start from ip.src) + TCP_HEADER_LEN_PLAIN + 4 (one option: mss) - ck=checksum(&buf[IP_SRC_P], 8+TCP_HEADER_LEN_PLAIN+4,2); - buf[TCP_CHECKSUM_H_P]=ck>>8; - buf[TCP_CHECKSUM_L_P]=ck& 0xff; - // add 4 for option mss: - enc28j60PacketSend(IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+4+ETH_HEADER_LEN,buf); -} - -// do some basic length calculations and store the result in static variables -uint16_t get_tcp_data_len(uint8_t *buf) -{ - int16_t i; - i=(((int16_t)buf[IP_TOTLEN_H_P])<<8)|(buf[IP_TOTLEN_L_P]&0xff); - i-=IP_HEADER_LEN; - i-=(buf[TCP_HEADER_LEN_P]>>4)*4; // generate len in bytes; - if (i<=0){ - i=0; - } - return((uint16_t)i); -} - -// get a pointer to the start of tcp data in buf -// Returns 0 if there is no data -// You must call init_len_info once before calling this function -// Not used? -/* - uint16_t get_tcp_data_pointer(void) -{ - if (info_data_len){ - return((uint16_t)TCP_SRC_PORT_H_P+info_hdr_len); - }else{ - return(0); - } -} -*/ - - -// do some basic length calculations and store the result in static varibales -// Not used? -/* -void init_len_info(uint8_t *buf) -{ - info_data_len=(((int16_t)buf[IP_TOTLEN_H_P])<<8)|(buf[IP_TOTLEN_L_P]&0xff); - info_data_len-=IP_HEADER_LEN; - info_hdr_len=(buf[TCP_HEADER_LEN_P]>>4)*4; // generate len in bytes; - info_data_len-=info_hdr_len; - if (info_data_len<=0){ - info_data_len=0; - } -} -*/ - -// fill a binary string of len data into the tcp packet -uint16_t fill_tcp_data_len(uint8_t *buf,uint16_t pos, const char *s, uint16_t len) -{ - // fill in tcp data at position pos - // - // with no options the data starts after the checksum + 2 more bytes (urgent ptr) - memcpy(&buf[TCP_CHECKSUM_L_P+3], s, len); - return(pos); -} - -// fill in tcp data at position pos. pos=0 means start of -// tcp data. Returns the position at which the string after -// this string could be filled. -uint16_t fill_tcp_data(uint8_t *buf,uint16_t pos, const char *s) -{ - return(fill_tcp_data_len(buf,pos,(char*)s,strlen(s))); -} - -// Make just an ack packet with no tcp data inside -// This will modify the eth/ip/tcp header -void make_tcp_ack_from_any(uint8_t *buf,int16_t datlentoack,uint8_t addflags) -{ - uint16_t j; - make_eth(buf); - // fill the header: - buf[TCP_FLAGS_P]=TCP_FLAGS_ACK_V|addflags; - if (addflags==TCP_FLAGS_RST_V){ - make_tcphead(buf,datlentoack,1); - } else { - if (datlentoack==0){ - // if there is no data then we must still acknoledge one packet - datlentoack = 1; - } - make_tcphead(buf,datlentoack,1); // no options - } - // total length field in the IP header must be set: - // 20 bytes IP + 20 bytes tcp (when no options) - j=IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN; - buf[IP_TOTLEN_H_P]=j >> 8; - buf[IP_TOTLEN_L_P]=j & 0xff; - make_ip(buf); - // use a low window size otherwise we have to have - // timers and can not just react on every packet. - buf[TCP_WIN_SIZE]=0x4; // 1024=0x400 - buf[TCP_WIN_SIZE+1]=0x0; - // calculate the checksum, len=8 (start from ip.src) + TCP_HEADER_LEN_PLAIN + data len - j=checksum(&buf[IP_SRC_P], 8+TCP_HEADER_LEN_PLAIN,2); - buf[TCP_CHECKSUM_H_P]=j>>8; - buf[TCP_CHECKSUM_L_P]=j& 0xff; - enc28j60PacketSend(IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+ETH_HEADER_LEN,buf); -} - - -// dlen is the amount of tcp data (http data) we send in this packet -// You can use this function only immediately after make_tcp_ack_from_any -// This is because this function will NOT modify the eth/ip/tcp header except for -// length and checksum -// You must set TCP_FLAGS before calling this -void make_tcp_ack_with_data_noflags(uint8_t *buf,uint16_t dlen) -{ - uint16_t j; - // total length field in the IP header must be set: - // 20 bytes IP + 20 bytes tcp (when no options) + len of data - j=IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+dlen; - buf[IP_TOTLEN_H_P]=j>>8; - buf[IP_TOTLEN_L_P]=j& 0xff; - fill_ip_hdr_checksum(buf); - // zero the checksum - buf[TCP_CHECKSUM_H_P]=0; - buf[TCP_CHECKSUM_L_P]=0; - // calculate the checksum, len=8 (start from ip.src) + TCP_HEADER_LEN_PLAIN + data len - j=checksum(&buf[IP_SRC_P], 8+TCP_HEADER_LEN_PLAIN+dlen,2); - buf[TCP_CHECKSUM_H_P]=j>>8; - buf[TCP_CHECKSUM_L_P]=j& 0xff; - enc28j60PacketSend(IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+dlen+ETH_HEADER_LEN,buf); -} - - -// you must have called init_len_info at some time before calling this function -// dlen is the amount of tcp data (http data) we send in this packet -// You can use this function only immediately after make_tcp_ack_from_any -// This is because this function will NOT modify the eth/ip/tcp header except for -// length and checksum -// Used? -void make_tcp_ack_with_data(uint8_t *buf,uint16_t dlen) -{ - uint16_t j; - // fill the header: - // This code requires that we send only one data packet - // because we keep no state information. We must therefore set - // the fin here: - buf[TCP_FLAGS_P]=TCP_FLAGS_ACK_V|TCP_FLAGS_PUSH_V|TCP_FLAGS_FIN_V; - - // total length field in the IP header must be set: - // 20 bytes IP + 20 bytes tcp (when no options) + len of data - j=IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+dlen; - buf[IP_TOTLEN_H_P]=j>>8; - buf[IP_TOTLEN_L_P]=j& 0xff; - fill_ip_hdr_checksum(buf); - // zero the checksum - buf[TCP_CHECKSUM_H_P]=0; - buf[TCP_CHECKSUM_L_P]=0; - // calculate the checksum, len=8 (start from ip.src) + TCP_HEADER_LEN_PLAIN + data len - j=checksum(&buf[IP_SRC_P], 8+TCP_HEADER_LEN_PLAIN+dlen,2); - buf[TCP_CHECKSUM_H_P]=j>>8; - buf[TCP_CHECKSUM_L_P]=j& 0xff; - enc28j60PacketSend(IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+dlen+ETH_HEADER_LEN,buf); -} - - -// you must have initialized info_data_len at some time before calling this function -// -// This info_data_len initialisation is done automatically if you call -// packetloop_icmp_tcp(buf,enc28j60PacketReceive(BUFFER_SIZE, buf)); -// and test the return value for non zero. -// -// dlen is the amount of tcp data (http data) we send in this packet -// You can use this function only immediately after make_tcp_ack_from_any -// This is because this function will NOT modify the eth/ip/tcp header except for -// length and checksum -void www_server_reply(uint8_t *buf,uint16_t dlen) -{ - make_tcp_ack_from_any(buf,info_data_len,0); // send ack for http get - // fill the header: - // This code requires that we send only one data packet - // because we keep no state information. We must therefore set - // the fin here: - buf[TCP_FLAGS_P]=TCP_FLAGS_ACK_V|TCP_FLAGS_PUSH_V|TCP_FLAGS_FIN_V; - make_tcp_ack_with_data_noflags(buf,dlen); // send data -} - -#if defined (NTP_client) || defined (WOL_client) || defined (UDP_client) || defined (TCP_client) || defined (PING_client) -// fill buffer with a prog-mem string - CHANGED TO NON PROGMEM! -void fill_buf_p(uint8_t *buf,uint16_t len, const char *s) -{ - // fill in tcp data at position pos - // - // with no options the data starts after the checksum + 2 more bytes (urgent ptr) - memcpy(buf, s, len); - -} -#endif - -#ifdef PING_client -// icmp echo, matchpat is a pattern that has to be sent back by the -// host answering the ping. -// The ping is sent to destip and mac gwmacaddr -void client_icmp_request(uint8_t *buf,uint8_t *destip) -{ - uint16_t ck; - // - memcpy(&buf[ETH_DST_MAC], gwmacaddr, 6); // gw mac in local lan or host mac - memcpy(&buf[ETH_SRC_MAC], macaddr, 6); - buf[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; - buf[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; - fill_buf_p(&buf[IP_P],9,iphdr); - - buf[IP_TOTLEN_L_P]=0x82; // TUX Code has 0x54, here has 0x82 - buf[IP_PROTO_P]=IP_PROTO_UDP_V; - memcpy(&buf[IP_DST_P], destip, 4); - memcpy(&buf[IP_SRC_P], ipaddr, 4); - fill_ip_hdr_checksum(buf); - - buf[ICMP_TYPE_P]=ICMP_TYPE_ECHOREQUEST_V; - buf[ICMP_TYPE_P+1]=0; // code - // zero the checksum - buf[ICMP_CHECKSUM_H_P]=0; - buf[ICMP_CHECKSUM_L_P]=0; - // a possibly unique id of this host: - buf[ICMP_IDENT_H_P]=5; // some number - buf[ICMP_IDENT_L_P]=ipaddr[3]; // last byte of my IP - // - buf[ICMP_IDENT_L_P+1]=0; // seq number, high byte - buf[ICMP_IDENT_L_P+2]=1; // seq number, low byte, we send only 1 ping at a time - // copy the data: - memset(&buf[ICMP_DATA_P], PINGPATTERN, 56); - // - ck=checksum(&buf[ICMP_TYPE_P], 56+8,0); - buf[ICMP_CHECKSUM_H_P]=ck>>8; - buf[ICMP_CHECKSUM_L_P]=ck& 0xff; - enc28j60PacketSend(98,buf); -} -#endif // PING_client - -void __attribute__((weak)) ES_PingCallback(void) -{ -} - -#ifdef NTP_client -// ntp udp packet -// See http://tools.ietf.org/html/rfc958 for details -// -void client_ntp_request(uint8_t *buf,uint8_t *ntpip,uint8_t srcport) -{ - uint16_t ck; - // - memcpy(&buf[ETH_DST_MAC], gwmacaddr, 6); - memcpy(&buf[ETH_SRC_MAC], macaddr, 6); - buf[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; - buf[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; - fill_buf_p(&buf[IP_P],9,iphdr); - - buf[IP_TOTLEN_L_P]=0x4c; - buf[IP_PROTO_P]=IP_PROTO_UDP_V; - memcpy(&buf[IP_DST_P], ntpip, 4); - memcpy(&buf[IP_SRC_P], ipaddr, 4); - fill_ip_hdr_checksum(buf); - - buf[UDP_DST_PORT_H_P]=0; - buf[UDP_DST_PORT_L_P]=0x7b; // ntp=123 - buf[UDP_SRC_PORT_H_P]=10; - buf[UDP_SRC_PORT_L_P]=srcport; // lower 8 bit of src port - buf[UDP_LEN_H_P]=0; - buf[UDP_LEN_L_P]=56; // fixed len - // zero the checksum - buf[UDP_CHECKSUM_H_P]=0; - buf[UDP_CHECKSUM_L_P]=0; - // copy the data: - // most fields are zero, here we zero everything and fill later - memset(&buf[UDP_DATA_P], 0, 48); - fill_buf_p(&buf[UDP_DATA_P],10,ntpreqhdr); - // - ck=checksum(&buf[IP_SRC_P], 16 + 48,1); - buf[UDP_CHECKSUM_H_P]=ck>>8; - buf[UDP_CHECKSUM_L_P]=ck& 0xff; - enc28j60PacketSend(90,buf); -} -// process the answer from the ntp server: -// if dstport==0 then accept any port otherwise only answers going to dstport -// return 1 on sucessful processing of answer -uint8_t client_ntp_process_answer(uint8_t *buf,uint32_t *time,uint8_t dstport_l){ - if (dstport_l){ - if (buf[UDP_DST_PORT_L_P]!=dstport_l){ - return(0); - } - } - if (buf[UDP_LEN_H_P]!=0 || buf[UDP_LEN_L_P]!=56 || buf[UDP_SRC_PORT_L_P]!=0x7b){ - // not ntp - return(0); - } - // copy time from the transmit time stamp field: - *time=((uint32_t)buf[0x52]<<24)|((uint32_t)buf[0x53]<<16)|((uint32_t)buf[0x54]<<8)|((uint32_t)buf[0x55]); - return(1); -} -#endif - -#ifdef UDP_client -// -------------------- send a spontanious UDP packet to a server -// There are two ways of using this: -// 1) you call send_udp_prepare, you fill the data yourself into buf starting at buf[UDP_DATA_P], -// you send the packet by calling send_udp_transmit -// -// 2) You just allocate a large enough buffer for you data and you call send_udp and nothing else -// needs to be done. -// -// send_udp sends via gwip, you must call client_set_gwip at startup -void send_udp_prepare(uint8_t *buf,uint16_t sport, uint8_t *dip, uint16_t dport) -{ - memcpy(&buf[ETH_DST_MAC], gwmacaddr, 6); - memcpy(&buf[ETH_SRC_MAC], macaddr, 6); - buf[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; - buf[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; - fill_buf_p(&buf[IP_P],9,iphdr); - - // total length field in the IP header must be set: - buf[IP_TOTLEN_H_P]=0; - // done in transmit: buf[IP_TOTLEN_L_P]=IP_HEADER_LEN+UDP_HEADER_LEN+datalen; - buf[IP_PROTO_P]=IP_PROTO_UDP_V; - memcpy(&buf[IP_DST_P], dip, 4); - memcpy(&buf[IP_SRC_P], ipaddr, 4); - - // done in transmit: fill_ip_hdr_checksum(buf); - buf[UDP_DST_PORT_H_P]=(dport>>8); - buf[UDP_DST_PORT_L_P]=0xff&dport; - buf[UDP_SRC_PORT_H_P]=(sport>>8); - buf[UDP_SRC_PORT_L_P]=sport&0xff; - buf[UDP_LEN_H_P]=0; - // done in transmit: buf[UDP_LEN_L_P]=UDP_HEADER_LEN+datalen; - // zero the checksum - buf[UDP_CHECKSUM_H_P]=0; - buf[UDP_CHECKSUM_L_P]=0; - // copy the data: - // now starting with the first byte at buf[UDP_DATA_P] -} - -void send_udp_transmit(uint8_t *buf,uint16_t datalen) -{ - uint16_t ck; - buf[IP_TOTLEN_H_P]=(IP_HEADER_LEN+UDP_HEADER_LEN+datalen) >> 8; - buf[IP_TOTLEN_L_P]=(IP_HEADER_LEN+UDP_HEADER_LEN+datalen) & 0xff; - fill_ip_hdr_checksum(buf); - //buf[UDP_LEN_L_P]=UDP_HEADER_LEN+datalen; - buf[UDP_LEN_H_P]=(UDP_HEADER_LEN+datalen) >>8; - buf[UDP_LEN_L_P]=(UDP_HEADER_LEN+datalen) & 0xff; - - // - ck=checksum(&buf[IP_SRC_P], 16 + datalen,1); - buf[UDP_CHECKSUM_H_P]=ck>>8; - buf[UDP_CHECKSUM_L_P]=ck& 0xff; - enc28j60PacketSend(UDP_HEADER_LEN+IP_HEADER_LEN+ETH_HEADER_LEN+datalen,buf); -} - -void send_udp(uint8_t *buf,char *data,uint16_t datalen,uint16_t sport, uint8_t *dip, uint16_t dport) -{ - send_udp_prepare(buf,sport, dip, dport); - // limit the length: Why??? ADL - // NOTE: We dont need such silly limits on powerful STM32s - // if (datalen>220){ - // datalen=220; - // } - // copy the data: - memcpy(&buf[UDP_DATA_P], data, datalen); - // - send_udp_transmit(buf,datalen); -} -#endif // UDP_client - -#ifdef WOL_client -// -------------------- special code to make a WOL packet - -// A WOL (Wake on Lan) packet is a UDP packet to the broadcast -// address and UDP port 9. The data part contains 6x FF followed by -// 16 times the mac address of the host to wake-up -// -void send_wol(uint8_t *buf,uint8_t *wolmac) -{ - uint16_t ck; - // - memset(&buf[ETH_DST_MAC], 0xff, 6); - memcpy(&buf[ETH_SRC_MAC], macaddr, 6); - buf[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; - buf[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; - fill_buf_p(&buf[IP_P],9,iphdr); - - buf[IP_TOTLEN_L_P]=0x82; - buf[IP_PROTO_P]=IP_PROTO_UDP_V; - memcpy(&buf[IP_SRC_P], ipaddr, 4); - memset(&buf[IP_DST_P], 0xff, 4); - fill_ip_hdr_checksum(buf); - buf[UDP_DST_PORT_H_P]=0; - buf[UDP_DST_PORT_L_P]=0x9; // wol=normally 9 - buf[UDP_SRC_PORT_H_P]=10; - buf[UDP_SRC_PORT_L_P]=0x42; // source port does not matter - buf[UDP_LEN_H_P]=0; - buf[UDP_LEN_L_P]=110; // fixed len - // zero the checksum - buf[UDP_CHECKSUM_H_P]=0; - buf[UDP_CHECKSUM_L_P]=0; - // copy the data (102 bytes): - - // first mac - 0xFFs - memset(&buf[UDP_DATA_P], 0xff, 6); - // next 16 macs - wolmac repeated - // TODO: may need testing - for (unsigned i = 1; i <= 16; i++) { - memcpy(&buf[UDP_DATA_P + i*6], wolmac, 6); - } - - ck=checksum(&buf[IP_SRC_P], 118,1); - buf[UDP_CHECKSUM_H_P]=ck>>8; - buf[UDP_CHECKSUM_L_P]=ck& 0xff; - - enc28j60PacketSend(UDP_HEADER_LEN+IP_HEADER_LEN+ETH_HEADER_LEN+102,buf); -} -#endif // WOL_client - -#if defined (NTP_client) || defined (UDP_client) || defined (TCP_client) || defined (PING_client) -// make a arp request -void client_arp_whohas(uint8_t *buf,uint8_t *ip_we_search) -{ - // - memset(&buf[ETH_DST_MAC], 0xFF, 6); - memcpy(&buf[ETH_SRC_MAC], macaddr, 6); - buf[ETH_TYPE_H_P] = ETHTYPE_ARP_H_V; - buf[ETH_TYPE_L_P] = ETHTYPE_ARP_L_V; - fill_buf_p(&buf[ETH_ARP_P],8,arpreqhdr); - - memcpy(&buf[ETH_ARP_SRC_MAC_P], macaddr, 6); - memset(&buf[ETH_ARP_DST_MAC_P], 0, 6); - - memcpy(&buf[ETH_ARP_DST_IP_P], ip_we_search, 4); - memcpy(&buf[ETH_ARP_SRC_IP_P], ipaddr, 4); - - waitgwmac|=WGW_ACCEPT_ARP_REPLY; - - // 0x2a=42=len of packet - enc28j60PacketSend(0x2a,buf); -} - -uint8_t client_waiting_gw(void) -{ - if (waitgwmac & WGW_HAVE_GW_MAC){ - return(0); - } - return(1); -} - - -// store the mac addr from an arp reply -// no len check here, you must first call eth_type_is_arp_and_my_ip -uint8_t client_store_gw_mac(uint8_t *buf) -{ - if (memcmp(&buf[ETH_ARP_SRC_IP_P], gwip, 4)) { - return 0; - } - - memcpy(gwmacaddr, &buf[ETH_ARP_SRC_MAC_P], 6); - return 1; -} - -void client_gw_arp_refresh(void) { - if (waitgwmac & WGW_HAVE_GW_MAC){ - waitgwmac|=WGW_REFRESHING; - } -} - -// Used? -/* -void client_set_wwwip(uint8_t *wwwipaddr) -{ - uint8_t i=0; - while(i<4){ - wwwip[i]=wwwipaddr[i]; - i++; - } -} -*/ - -void client_set_gwip(uint8_t *gwipaddr) -{ - waitgwmac=WGW_INITIAL_ARP; // causes an arp request in the packet loop - memcpy(gwip, gwipaddr, 4); -} -#endif - -void client_tcp_set_serverip(uint8_t *ipaddr) -{ - memcpy(tcpsrvip, ipaddr, 4); -} - -#if defined (TCP_client) -// Make a tcp syn packet -void client_syn(uint8_t *buf,uint8_t srcport,uint8_t dstport_h,uint8_t dstport_l) -{ - uint16_t ck; - // -- make the main part of the eth/IP/tcp header: - memcpy(&buf[ETH_DST_MAC], gwmacaddr, 6); - memcpy(&buf[ETH_SRC_MAC], macaddr, 6); - buf[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; - buf[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; - fill_buf_p(&buf[IP_P],9,iphdr); - - buf[IP_TOTLEN_L_P]=44; // good for syn - buf[IP_PROTO_P]=IP_PROTO_TCP_V; - memcpy(&buf[IP_DST_P], tcpsrvip, 4); - memcpy(&buf[IP_SRC_P], ipaddr, 4); - fill_ip_hdr_checksum(buf); - - buf[TCP_DST_PORT_H_P]=dstport_h; - buf[TCP_DST_PORT_L_P]=dstport_l; - buf[TCP_SRC_PORT_H_P]=TCPCLIENT_SRC_PORT_H; - buf[TCP_SRC_PORT_L_P]=srcport; // lower 8 bit of src port - // zero out sequence number and acknowledgement number - memset(&buf[TCP_SEQ_H_P], 0, 8); - // -- header ready - // put inital seq number - // we step only the second byte, this allows us to send packts - // with 255 bytes 512 (if we step the initial seqnum by 2) - // or 765 (step by 3) - buf[TCP_SEQ_H_P+2]= seqnum; - // step the inititial seq num by something we will not use - // during this tcp session: - seqnum+=3; - buf[TCP_HEADER_LEN_P]=0x60; // 0x60=24 len: (0x60>>4) * 4 - buf[TCP_FLAGS_P]=TCP_FLAGS_SYN_V; - // use a low window size otherwise we have to have - // timers and can not just react on every packet. - buf[TCP_WIN_SIZE]=0x4; // 1024=0x400, 768 = 0x300, initial window - buf[TCP_WIN_SIZE+1]=0x0; - // zero the checksum - buf[TCP_CHECKSUM_H_P]=0; - buf[TCP_CHECKSUM_L_P]=0; - // urgent pointer - buf[TCP_CHECKSUM_L_P+1]=0; - buf[TCP_CHECKSUM_L_P+2]=0; - // MSS=768, must be more than 50% of the window size we use - // 768 in hex is 0x300 - buf[TCP_OPTIONS_P]=2; - buf[TCP_OPTIONS_P+1]=4; - buf[TCP_OPTIONS_P+2]=(CLIENTMSS>>8); - buf[TCP_OPTIONS_P+3]=CLIENTMSS & 0xff; - ck=checksum(&buf[IP_SRC_P], 8 +TCP_HEADER_LEN_PLAIN+4,2); - buf[TCP_CHECKSUM_H_P]=ck>>8; - buf[TCP_CHECKSUM_L_P]=ck& 0xff; - // 4 is the tcp mss option: - enc28j60PacketSend(IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+ETH_HEADER_LEN+4,buf); -#if ETHERSHIELD_DEBUG - ethershieldDebug( "Sent TCP Syn\n"); -#endif -} - -// Make a tcp data packet with next sequence number -// XXXXXXXXXXX -uint16_t build_tcp_data(uint8_t *buf, uint16_t srcPort ) -{ - uint16_t ck; - // -- make the main part of the eth/IP/tcp header: - memcpy(&buf[ETH_DST_MAC], gwmacaddr, 6); - memcpy(&buf[ETH_SRC_MAC], macaddr, 6); - buf[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; - buf[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; - fill_buf_p(&buf[IP_P],9,iphdr); - - buf[IP_TOTLEN_L_P]=40; - buf[IP_PROTO_P]=IP_PROTO_TCP_V; - memcpy(&buf[IP_DST_P], tcpsrvip, 4); - memcpy(&buf[IP_SRC_P], ipaddr, 4); - fill_ip_hdr_checksum(buf); - - buf[TCP_DST_PORT_H_P]=tcp_client_port_h; - buf[TCP_DST_PORT_L_P]=tcp_client_port_l; - buf[TCP_SRC_PORT_H_P]= (srcPort >> 8); - buf[TCP_SRC_PORT_L_P]= srcPort & 0x00ff; - // zero out sequence number and acknowledgement number - memset(&buf[TCP_SEQ_H_P], 0, 8); - // -- header ready - // put inital seq number - // we step only the second byte, this allows us to send packts - // with 255 bytes 512 (if we step the initial seqnum by 2) - // or 765 (step by 3) - buf[TCP_SEQ_H_P+2]= seqnum; - // step the inititial seq num by something we will not use - // during this tcp session: - seqnum+=3; - buf[TCP_HEADER_LEN_P]=0x60; // 0x60=24 len: (0x60>>4) * 4 - buf[TCP_FLAGS_P]=TCP_FLAGS_PUSH_V; - // use a low window size otherwise we have to have - // timers and can not just react on every packet. - buf[TCP_WIN_SIZE]=0x4; // 1024=0x400, 768 = 0x300, initial window - buf[TCP_WIN_SIZE+1]=0x0; - // zero the checksum - buf[TCP_CHECKSUM_H_P]=0; - buf[TCP_CHECKSUM_L_P]=0; - // urgent pointer - buf[TCP_CHECKSUM_L_P+1]=0; - buf[TCP_CHECKSUM_L_P+2]=0; - // MSS=768, must be more than 50% of the window size we use - // 768 in hex is 0x300 - buf[TCP_OPTIONS_P]=2; - buf[TCP_OPTIONS_P+1]=4; - buf[TCP_OPTIONS_P+2]=(CLIENTMSS>>8); - buf[TCP_OPTIONS_P+3]=CLIENTMSS & 0xff; - ck=checksum(&buf[IP_SRC_P], 8 +TCP_HEADER_LEN_PLAIN+4,2); - buf[TCP_CHECKSUM_H_P]=ck>>8; - buf[TCP_CHECKSUM_L_P]=ck& 0xff; - // 4 is the tcp mss option: -// enc28j60PacketSend(IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+ETH_HEADER_LEN+4,buf); - return IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+ETH_HEADER_LEN+4; -} - -// Send the data -void send_tcp_data(uint8_t *buf,uint16_t dlen) -{ - uint16_t j; - // fill the header: - // This code requires that we send only one data packet - // because we keep no state information. We must therefore set - // the fin here: - buf[TCP_FLAGS_P]=TCP_FLAGS_PUSH_V; - - // total length field in the IP header must be set: - // 20 bytes IP + 20 bytes tcp (when no options) + len of data - j=IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+dlen; - buf[IP_TOTLEN_H_P]=j>>8; - buf[IP_TOTLEN_L_P]=j& 0xff; - fill_ip_hdr_checksum(buf); - // zero the checksum - buf[TCP_CHECKSUM_H_P]=0; - buf[TCP_CHECKSUM_L_P]=0; - // calculate the checksum, len=8 (start from ip.src) + TCP_HEADER_LEN_PLAIN + data len - j=checksum(&buf[IP_SRC_P], 8+TCP_HEADER_LEN_PLAIN+dlen,2); - buf[TCP_CHECKSUM_H_P]=j>>8; - buf[TCP_CHECKSUM_L_P]=j& 0xff; - enc28j60PacketSend(IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+dlen+ETH_HEADER_LEN,buf); -} - - -// This is how to use the tcp client: -// -// Declare a callback function to get the result (tcp data from the server): -// -// uint8_t your_client_tcp_result_callback(uint8_t fd, uint8_t statuscode,uint16_t data_start_pos_in_buf, uint16_t len_of_data){...your code;return(close_tcp_session);} -// -// statuscode=0 means the buffer has valid data, otherwise len and pos_in_buf -// are invalid. That is: do to use data_start_pos_in_buf and len_of_data -// if statuscode!=0. -// -// This callback gives you access to the TCP data of the first -// packet returned from the server. You should aim to minimize the server -// output such that this will be the only packet. -// -// close_tcp_session=1 means close the session now. close_tcp_session=0 -// read all data and leave it to the other side to close it. -// If you connect to a web server then you want close_tcp_session=0. -// If you connect to a modbus/tcp equipment then you want close_tcp_session=1 -// -// Declare a callback function to be called in order to fill in the -// request (tcp data sent to the server): -// uint16_t your_client_tcp_datafill_callback(uint8_t fd){...your code;return(len_of_data_filled_in);} -// -// Now call: -// fd=client_tcp_req(&your_client_tcp_result_callback,&your_client_tcp_datafill_callback,portnumber); -// -// fd is a file descriptor like number that you get back in the fill and result -// function so you know to which call of client_tcp_req this callback belongs. -// -// You can not start different clients (e.g modbus and web) at the -// same time but you can start them one after each other. That is -// when the request has timed out or when the result_callback was -// executed then you can start a new one. The fd makes it still possible to -// distinguish in the callback code the different types you started. -// -// Note that you might never get called back if the other side does -// not answer. A timer would be needed to recongnize such a condition. -// -// We use callback functions because that saves memory and a uC is very -// limited in memory -// -uint8_t client_tcp_req(uint8_t (*result_callback)(uint8_t fd,uint8_t statuscode,uint16_t data_start_pos_in_buf, uint16_t len_of_data),uint16_t (*datafill_callback)(uint8_t fd),uint16_t port) -{ - client_tcp_result_callback=result_callback; - client_tcp_datafill_callback=datafill_callback; - tcp_client_port_h=(port>>8) & 0xff; - tcp_client_port_l=(port & 0xff); - tcp_client_state=1; - tcp_fd++; - if (tcp_fd>7){ - tcp_fd=0; - } - return(tcp_fd); -} -#endif // TCP_client - -#if defined (WWW_client) -uint16_t www_client_internal_datafill_callback(uint8_t fd){ - char strbuf[5]; - uint16_t len=0; - if (fd==www_fd){ - if (browsertype==0){ - // GET - len=fill_tcp_data(bufptr,0,"GET "); - len=fill_tcp_data(bufptr,len,client_urlbuf); - if( client_urlbuf_var ) - len=fill_tcp_data(bufptr,len,client_urlbuf_var); - // I would prefer http/1.0 but there is a funny - // bug in some apache webservers which causes - // them to send two packets (fragmented PDU) - // if we don't use HTTP/1.1 + Connection: close - len=fill_tcp_data(bufptr,len," HTTP/1.1\r\nHost: "); - len=fill_tcp_data(bufptr,len,client_hoststr); - len=fill_tcp_data(bufptr,len,"\r\nUser-Agent: " WWW_USER_AGENT "\r\nAccept: text/html\r\nConnection: close\r\n\r\n"); - }else{ - // POST - if( client_method ) { - len=fill_tcp_data(bufptr,0, client_method ); - len=fill_tcp_data(bufptr,len, " "); - } else { - len=fill_tcp_data(bufptr,0, "POST "); - } - len=fill_tcp_data(bufptr,len,client_urlbuf); - len=fill_tcp_data(bufptr,len," HTTP/1.1\r\nHost: "); - len=fill_tcp_data(bufptr,len,client_hoststr); - if (client_additionalheaderline){ - len=fill_tcp_data(bufptr,len,"\r\n"); - len=fill_tcp_data(bufptr,len,client_additionalheaderline); - } - len=fill_tcp_data(bufptr,len,"\r\nUser-Agent: " WWW_USER_AGENT "\r\nAccept: */*\r\nConnection: close\r\n"); - len=fill_tcp_data(bufptr,len,"Content-Length: "); - //itoa(strlen(client_postval),strbuf,10); - sprintf(strbuf, "%d", strlen(client_postval)); - len=fill_tcp_data(bufptr,len,strbuf); - len=fill_tcp_data(bufptr,len,"\r\nContent-Type: application/x-www-form-urlencoded\r\n\r\n"); - len=fill_tcp_data(bufptr,len,client_postval); - } - return(len); - } - return(0); -} - -uint8_t www_client_internal_result_callback(uint8_t fd, uint8_t statuscode, uint16_t datapos, uint16_t len_of_data){ - if (fd!=www_fd){ - (*client_browser_callback)(4,0,0); - return(0); - } - if (statuscode==0 && len_of_data>12){ - // we might have a http status code - if (client_browser_callback){ - if (strncmp("200",(char *)&(bufptr[datapos+9]),3)==0){ - (*client_browser_callback)(0,((uint16_t)TCP_SRC_PORT_H_P+(bufptr[TCP_HEADER_LEN_P]>>4)*4),len_of_data); - }else{ - (*client_browser_callback)(1,((uint16_t)TCP_SRC_PORT_H_P+(bufptr[TCP_HEADER_LEN_P]>>4)*4),len_of_data); - } - } - } - return(0); -} - - -// call this function externally like this: -// -// Declare a callback function: void browserresult(uint8_t statuscode){...your code} -// client_browser_url(PSTR("/cgi-bin/checkip"),NULL,PSTR("tuxgraphics.org"),&browserresult); -// urlbuf_varpart is a pointer to a string buffer that contains the second -// non constant part of the url. You must keep this buffer allocated until the -// callback function is executed or until you can be sure that the server side -// has timed out. -// hoststr is the name of the host. This is needed because many sites host several -// sites on the same physical machine with only one IP address. The web server needs -// to know to which site you want to go. -// additionalheaderline must be set to NULL if not used. -// statuscode is zero if the answer from the web server is 200 OK (e.g HTTP/1.1 200 OK) -// -// -void client_http_get(char *urlbuf, char *urlbuf_varpart, char *hoststr, void (*callback)(uint8_t,uint16_t,uint16_t)) -{ - client_urlbuf=urlbuf; - client_urlbuf_var=urlbuf_varpart; - client_hoststr=hoststr; - browsertype=0; - client_browser_callback=callback; - www_fd=client_tcp_req(&www_client_internal_result_callback,&www_client_internal_datafill_callback,80); -} - -// client web browser using http POST operation: -// additionalheaderline must be set to NULL if not used. -// method set to NULL if using default POST alternative is PUT -// postval is a string buffer which can only be de-allocated by the caller -// when the post operation was really done (e.g when callback was executed). -// postval must be urlencoded. -void client_http_post(char *urlbuf, char *hoststr, char *additionalheaderline, char *method, char *postval, void (*callback)(uint8_t,uint16_t)) -{ - client_urlbuf=urlbuf; - client_hoststr=hoststr; - client_additionalheaderline=additionalheaderline; - client_method=method; - client_postval=postval; - browsertype=1; - client_browser_callback=(void (*)(uint8_t,uint16_t,uint16_t))callback; - www_fd=client_tcp_req(&www_client_internal_result_callback,&www_client_internal_datafill_callback,80); -} -#endif // WWW_client - -void register_ping_rec_callback(void (*callback)(uint8_t *srcip)) -{ - icmp_callback=callback; -} - -#ifdef PING_client -// loop over this to check if we get a ping reply: -uint8_t packetloop_icmp_checkreply(uint8_t *buf,uint8_t *ip_monitoredhost) -{ - if(buf[IP_PROTO_P]==IP_PROTO_ICMP_V && buf[ICMP_TYPE_P]==ICMP_TYPE_ECHOREPLY_V){ - if (buf[ICMP_DATA_P]== PINGPATTERN){ - if (check_ip_message_is_from(buf,ip_monitoredhost)){ - return(1); - // ping reply is from monitored host and ping was from us - } - } - } - return(0); -} -#endif // PING_client - -// return 0 to just continue in the packet loop and return the position -// of the tcp/udp data if there is tcp/udp data part -uint16_t packetloop_icmp_tcp(uint8_t * buf, uint16_t plen) { - uint16_t len; - #if defined(TCP_client) - uint8_t send_fin = 0; - uint16_t tcpstart; - uint16_t save_len; - #endif - - //plen will be unequal to zero if there is a valid - // packet (without crc error): - #if defined(NTP_client) || defined(UDP_client) || defined(TCP_client) || defined(PING_client) - if (plen == 0) { - if ((waitgwmac & WGW_INITIAL_ARP || waitgwmac & WGW_REFRESHING) && delaycnt == 0 && enc28j60linkup()) { - client_arp_whohas(buf, gwip); - } - delaycnt++; - #if defined(TCP_client) - if (tcp_client_state == 1 && (waitgwmac & WGW_HAVE_GW_MAC)) { // send a syn - tcp_client_state = 2; - tcpclient_src_port_l++; // allocate a new port - // we encode our 3 bit fd into the src port this - // way we get it back in every message that comes - // from the server: - client_syn(buf, ((tcp_fd << 5) | (0x1f & tcpclient_src_port_l)), tcp_client_port_h, tcp_client_port_l); - } - #endif - return (0); - } - #endif // NTP_client||UDP_client||TCP_client||PING_client - // arp is broadcast if unknown but a host may also - // verify the mac address by sending it to - // a unicast address. - if (eth_type_is_arp_and_my_ip(buf, plen)) { - if (buf[ETH_ARP_OPCODE_L_P] == ETH_ARP_OPCODE_REQ_L_V) { - // is it an arp request - make_arp_answer_from_request(buf); - } - #if defined(NTP_client) || defined(UDP_client) || defined(TCP_client) || defined(PING_client) - if (waitgwmac & WGW_ACCEPT_ARP_REPLY && (buf[ETH_ARP_OPCODE_L_P] == ETH_ARP_OPCODE_REPLY_L_V)) { - // is it an arp reply - if (client_store_gw_mac(buf)) { - waitgwmac = WGW_HAVE_GW_MAC; - } - } - #endif // NTP_client||UDP_client||TCP_client||PING_client - return (0); - - } - // check if ip packets are for us: - if (eth_type_is_ip_and_my_ip(buf, plen) == 0) { - return (0); - } - #ifdef NTP_client - // TODO - does this work? - // If NTP response, drop out to have it processed elsewhere - if (buf[IP_PROTO_P] == IP_PROTO_UDP_V && buf[UDP_SRC_PORT_H_P] == 0 && buf[UDP_SRC_PORT_L_P] == 0x7b) { - return (UDP_DATA_P); - } - #endif // NTP_client - #ifdef DNS_client - // TODO - does this work? - // If DNS response, drop out to have it processed elsewhere - if (buf[IP_PROTO_P] == IP_PROTO_UDP_V && buf[UDP_SRC_PORT_H_P] == 0 && buf[UDP_SRC_PORT_L_P] == 53) { - return (UDP_DATA_P); - } - #endif - - if (buf[IP_PROTO_P] == IP_PROTO_ICMP_V && buf[ICMP_TYPE_P] == ICMP_TYPE_ECHOREQUEST_V) { - if (icmp_callback) { - ( * icmp_callback)( & (buf[IP_SRC_P])); - } - // a ping packet, let's send pong - make_echo_reply_from_request(buf, plen); - ES_PingCallback(); - return (0); - } - if (plen < 54 && buf[IP_PROTO_P] != IP_PROTO_TCP_V) { - // smaller than the smallest TCP packet and not tcp port - return (0); - } - #if defined(TCP_client) - // a message for the tcp client, client_state is zero if client was never used - if (buf[TCP_DST_PORT_H_P] == TCPCLIENT_SRC_PORT_H) { - #if defined(WWW_client) - // workaround to pass pointer to www_client_internal.. - bufptr = buf; - #endif // WWW_client - if (check_ip_message_is_from(buf, tcpsrvip) == 0) { - return (0); - } - // if we get a reset: - if (buf[TCP_FLAGS_P] & TCP_FLAGS_RST_V) { - if (client_tcp_result_callback) { - #if ETHERSHIELD_DEBUG - ethershieldDebug("RST: Calling tcp client callback\n"); - #endif - // parameters in client_tcp_result_callback: fd, status, buf_start, len - ( * client_tcp_result_callback)((buf[TCP_DST_PORT_L_P] >> 5) & 0x7, 3, 0, 0); - } - tcp_client_state = 5; - return (0); - } - - // Determine what to do with packed depending on state - - len = get_tcp_data_len(buf); - if (tcp_client_state == 2) { - if ((buf[TCP_FLAGS_P] & TCP_FLAGS_SYN_V) && (buf[TCP_FLAGS_P] & TCP_FLAGS_ACK_V)) { - #if ETHERSHIELD_DEBUG - ethershieldDebug("Got SYNACK\n"); - #endif - // synack, answer with ack - make_tcp_ack_from_any(buf, 0, 0); - buf[TCP_FLAGS_P] = TCP_FLAGS_ACK_V | TCP_FLAGS_PUSH_V; - - // Make a tcp message with data. When calling this function we must - // still have a valid tcp-ack in the buffer. In other words - // you have just called make_tcp_ack_from_any(buf,0). - if (client_tcp_datafill_callback) { - #if ETHERSHIELD_DEBUG - ethershieldDebug("Datafill Callback\n"); - #endif - // in this case it is src port because the above - // make_tcp_ack_from_any swaps the dst and src port: - len = ( * client_tcp_datafill_callback)((buf[TCP_SRC_PORT_L_P] >> 5) & 0x7); - } else { - // this is just to prevent a crash - len = 0; - } - tcp_client_state = 3; - make_tcp_ack_with_data_noflags(buf, len); - #if ETHERSHIELD_DEBUG - ethershieldDebug("Send ACK\n"); - #endif - return (0); - } else { - // reset only if we have sent a syn and don't get syn-ack back. - // If we connect to a non listen port then we get a RST - // which will be handeled above. In other words there is - // normally no danger for an endless loop. - tcp_client_state = 1; // retry - // do not inform application layer as we retry. - len++; - if (buf[TCP_FLAGS_P] & TCP_FLAGS_ACK_V) { - // if packet was an ack then do not step the ack number - len = 0; - } - // refuse and reset the connection - make_tcp_ack_from_any(buf, len, TCP_FLAGS_RST_V); - return (0); - } - } - // in tcp_client_state==3 we will normally first get an empty - // ack-packet and then a ack-packet with data. - if (tcp_client_state == 4) { //&& len>0){ - // our first real data packet - #if ETHERSHIELD_DEBUG - // ethershieldDebug( "First Data Packet\n"); - #endif - // Removed this as there is no code to handle state 4. Only 1st packet will be available. - //tcp_client_state=4; - // return the data we received - if (client_tcp_result_callback) { - tcpstart = TCP_DATA_START; // TCP_DATA_START is a formula - // out of buffer bounds check, needed in case of fragmented IP packets - if (tcpstart > plen - 8) { - tcpstart = plen - 8; // dummy but save - } - save_len = len; - if (tcpstart + len > plen) { - save_len = plen - tcpstart; - } - #if ETHERSHIELD_DEBUG - ethershieldDebug("Calling Result callback\n"); - #endif - send_fin = ( * client_tcp_result_callback)((buf[TCP_DST_PORT_L_P] >> 5) & 0x7, 0, tcpstart, save_len); - - } - if (send_fin) { - #if ETHERSHIELD_DEBUG - ethershieldDebug("Send FIN\n"); - #endif - make_tcp_ack_from_any(buf, len, TCP_FLAGS_PUSH_V | TCP_FLAGS_FIN_V); - tcp_client_state = 5; - return (0); - } - make_tcp_ack_from_any(buf, len, 0); - return (0); - } - if (tcp_client_state == 3) { // && len>0){ - // our first real data packet - #if ETHERSHIELD_DEBUG - // ethershieldDebug( "First Data Packet\n"); - #endif - // Removed this as there is no code to handle state 4. Only 1st packet will be available. - tcp_client_state = 4; - // return the data we received - if (client_tcp_result_callback) { - tcpstart = TCP_DATA_START; // TCP_DATA_START is a formula - // out of buffer bounds check, needed in case of fragmented IP packets - if (tcpstart > plen - 8) { - tcpstart = plen - 8; // dummy but save - } - save_len = len; - if (tcpstart + len > plen) { - save_len = plen - tcpstart; - } - #if ETHERSHIELD_DEBUG - ethershieldDebug("Calling Result callback\n"); - #endif - send_fin = ( * client_tcp_result_callback)((buf[TCP_DST_PORT_L_P] >> 5) & 0x7, 0, tcpstart, save_len); - - } - if (send_fin) { - #if ETHERSHIELD_DEBUG - ethershieldDebug("Send FIN\n"); - #endif - make_tcp_ack_from_any(buf, len, TCP_FLAGS_PUSH_V | TCP_FLAGS_FIN_V); - tcp_client_state = 5; - return (0); - } - make_tcp_ack_from_any(buf, len, 0); - return (0); - } - if (tcp_client_state == 5) { - // no more ack - #if ETHERSHIELD_DEBUG - ethershieldDebug("No more ACK\n"); - #endif - return (0); - } - if (buf[TCP_FLAGS_P] & TCP_FLAGS_FIN_V) { - #if ETHERSHIELD_DEBUG - ethershieldDebug("Terminated\n"); - #endif - make_tcp_ack_from_any(buf, len + 1, TCP_FLAGS_PUSH_V | TCP_FLAGS_FIN_V); - tcp_client_state = 5; // connection terminated - return (0); - } - // ack all data (the web page may be long): - // if we just get a fragment then len will be zero - // and we ack only once we have the full packet - if (len > 0) { - make_tcp_ack_from_any(buf, len, 0); - #if ETHERSHIELD_DEBUG - ethershieldDebug("Send ACK\n"); - #endif - } - return (0); - } - #endif // WWW_client||TCP_client - // - // tcp port web server start - if (buf[TCP_DST_PORT_H_P] == wwwport_h && buf[TCP_DST_PORT_L_P] == wwwport_l) { - if (buf[TCP_FLAGS_P] & TCP_FLAGS_SYN_V) { - make_tcp_synack_from_syn(buf); - // make_tcp_synack_from_syn does already send the syn,ack - return (0); - } - if (buf[TCP_FLAGS_P] & TCP_FLAGS_ACK_V) { - info_data_len = get_tcp_data_len(buf); - // we can possibly have no data, just ack: - // Here we misuse plen for something else to save a variable. - // plen is now the position of start of the tcp user data. - if (info_data_len == 0) { - if (buf[TCP_FLAGS_P] & TCP_FLAGS_FIN_V) { - // finack, answer with ack - make_tcp_ack_from_any(buf, 0, 0); - } - // just an ack with no data, wait for next packet - return (0); - } - // Here we misuse len for something else to save a variable - len = TCP_DATA_START; // TCP_DATA_START is a formula - // check for data corruption - if (len > plen - 8) { - return (0); - } - return (len); - } - } - return (0); -} - -#endif - -/* end of ip_arp_udp.c */ diff --git a/src/websrv_help_functions.c b/src/websrv_help_functions.c deleted file mode 100644 index a4e71a7..0000000 --- a/src/websrv_help_functions.c +++ /dev/null @@ -1,199 +0,0 @@ -/********************************************* - * vim:sw=8:ts=8:si:et - * To use the above modeline in vim you must have "set modeline" in your .vimrc - * Author: Guido Socher - * Copyright: GPL V2 - * - * Some common utilities needed for IP and web applications - *********************************************/ - -#include "websrv_help_functions.h" - -#ifdef FROMDECODE_websrv_help -// search for a string of the form key=value in -// a string that looks like q?xyz=abc&uvw=defgh HTTP/1.1\r\n -// -// The returned value is stored in strbuf. You must allocate -// enough storage for strbuf, maxlen is the size of strbuf. -// I.e the value it is declated with: strbuf[5]-> maxlen=5 -uint8_t find_key_val(char *str,char *strbuf, uint8_t maxlen,char *key) -{ - uint8_t found=0; - uint8_t i=0; - char *kp; - kp=key; - while(*str && *str!=' ' && *str!='\n' && found==0){ - if (*str == *kp){ - kp++; - if (*kp == '\0'){ - str++; - kp=key; - if (*str == '='){ - found=1; - } - } - }else{ - kp=key; - } - str++; - } - if (found==1){ - // copy the value to a buffer and terminate it with '\0' - while(*str && *str!=' ' && *str!='\n' && *str!='&' && i= '0' && c <='9'){ - return((unsigned char)c - '0'); - } - if (c >= 'a' && c <='f'){ - return((unsigned char)c - 'a' + 10); - } - if (c >= 'A' && c <='F'){ - return((unsigned char)c - 'A' + 10); - } - return(0); -} - -// decode a url string e.g "hello%20joe" or "hello+joe" becomes "hello joe" -void urldecode(char *urlbuf) -{ - char c; - char *dst; - dst=urlbuf; - while ((c = *urlbuf)) { - if (c == '+') c = ' '; - if (c == '%') { - urlbuf++; - c = *urlbuf; - urlbuf++; - c = (h2int(c) << 4) | h2int(*urlbuf); - } - *dst = c; - dst++; - urlbuf++; - } - *dst = '\0'; -} - -#endif // FROMDECODE_websrv_help - -#ifdef URLENCODE_websrv_help - -// convert a single character to a 2 digit hex str -// a terminating '\0' is added -void int2h(char c, char *hstr) -{ - hstr[1]=(c & 0xf)+'0'; - if ((c & 0xf) >9){ - hstr[1]=(c & 0xf) - 10 + 'a'; - } - c=(c>>4)&0xf; - hstr[0]=c+'0'; - if (c > 9){ - hstr[0]=c - 10 + 'a'; - } - hstr[2]='\0'; -} - -// there must be enoug space in urlbuf. In the worst case that is -// 3 times the length of str -void urlencode(char *str,char *urlbuf) -{ - char c; - while ((c = *str)) { - if (c == ' '||isalnum((unsigned char)c)){ - if (c == ' '){ - c = '+'; - } - *urlbuf=c; - str++; - urlbuf++; - continue; - } - *urlbuf='%'; - urlbuf++; - int2h(c,urlbuf); - urlbuf++; - urlbuf++; - str++; - } - *urlbuf='\0'; -} - -#endif // URLENCODE_websrv_help - -// parse a string an extract the IP to bytestr -uint8_t parse_ip(uint8_t *bytestr,char *str) -{ - char *sptr; - uint8_t i=0; - sptr=NULL; - while(i<4){ - bytestr[i]=0; - i++; - } - i=0; - while(*str && i<4){ - // if a number then start - if (sptr==NULL && isdigit((unsigned char)*str)){ - sptr=str; - } - if (*str == '.'){ - *str ='\0'; - bytestr[i]=(atoi(sptr)&0xff); - i++; - sptr=NULL; - } - str++; - } - *str ='\0'; - if (i==3){ - bytestr[i]=(atoi(sptr)&0xff); - return(0); - } - return(1); -} - -// take a byte string and convert it to a human readable display string (base is 10 for ip and 16 for mac addr), len is 4 for IP addr and 6 for mac. -void mk_net_str(char *resultstr,uint8_t *bytestr,uint8_t len,char separator,uint8_t base) -{ - uint8_t i=0; - uint8_t j=0; - if (base == 10) { - while(i