This post was last edited by Yin Xiaozhou on 2024-6-5 14:05
LWIP (Lightweight IP) is an open source TCP/IP protocol stack implementation, specifically designed for embedded systems, which focuses on reducing memory usage and code size to adapt to resource-constrained environments. The following is a detailed description of some of the main features of LWIP you mentioned:
- Support IP forwarding under multiple network interfaces :
- LWIP allows a device to have multiple network interfaces and be able to forward IP packets between these interfaces. This is particularly useful in routers or gateway devices.
- Support IP data fragmentation transmission function :
- For larger packets, LWIP can split them into smaller fragments for transmission on the network. This allows a device to handle packets larger than its MTU (Maximum Transmission Unit).
- Support ICMP protocol :
- ICMP (Internet Control Message Protocol) is used to send control messages, such as error reports and diagnostic messages, between IP hosts and routers.
- Support UDP protocol :
- UDP is a connectionless protocol that provides best-effort data transmission services but does not guarantee the order or reliability of data. UDP is suitable for applications that do not require reliable transmission, such as real-time video streaming or audio streaming.
- Support TCP protocol :
- TCP (Transmission Control Protocol) is a connection-oriented protocol that provides reliable data transmission services. TCP includes features such as congestion control, RTT (Round-Trip Time) estimation, fast recovery, and fast forwarding to ensure reliable delivery of data packets.
- Support PPP point-to-point communication protocol :
- PPP allows TCP/IP connections to be established over a serial link (such as a telephone line or ISDN line). It is commonly used for dial-up Internet access and VPN connections.
- Support DHCP protocol :
- DHCP (Dynamic Host Configuration Protocol) allows devices to automatically obtain IP addresses and other network configuration information, such as subnet masks and default gateways. This simplifies the network configuration process.
- Support IPv6 protocol :
- IPv6 is the next generation Internet Protocol, which provides more address space and other improvements over IPv4. LWIP supports IPv6 allowing devices to operate on IPv6 networks.
- Provides a dedicated internal callback function interface :
- LWIP allows users to define callback functions to perform custom actions when specific events occur, such as packet arrival or timer expiration. This improves application performance and flexibility.
- Provides optional Berkeley interface functions :
- Berkeley Sockets API (also known as BSD Sockets) is a widely used network programming interface. LWIP provides an alternative Berkeley Sockets API implementation, allowing developers to develop network applications using a familiar socket programming model.
Due to its lightweight design, LWIP is well suited for use in embedded systems, especially on devices with limited memory and processor resources. In addition, LWIP's high configurability allows developers to tailor the functionality and size of the protocol stack to the needs of specific applications.
1. Memory Management
Each layer in the TCP/IP protocol stack needs to process packets with a specific format and add or remove header and trailer information as the packets pass through each layer. This frequent operation of the data buffer may affect the performance and stability of the embedded system, especially when resources are limited. Therefore, for the TCP/IP protocol stack, memory management is always the most important part. The choice of memory management will fundamentally determine the efficiency of memory allocation and recycling, and ultimately determine the performance of the system.
In LwIP, the dynamic memory management mechanism includes several different ways, such as using standard C library functions (such as malloc() and free()), custom dynamic memory heap allocation, and dynamic memory pool allocation. The specific mechanism to be used is usually determined by configuring the macro definition in opt.h (or similar configuration header file) of LwIP.
The following are macro definition examples and explanations related to LwIP dynamic memory management:
-
Standard C library memory allocation :
If LwIP is configured to use standard C library functions for memory allocation, it will directly call malloc() and free(). This configuration is more common in operating system environments, but may not be suitable for embedded systems because the memory management functions of the standard C library may not be suitable for the specific needs of embedded systems.
Usually, no specific macro definitions are needed for this configuration, since LwIP will probably use them by default (unless they are explicitly disabled).
-
LwIP dynamic memory heap allocation :
LwIP provides its own memory heap management implementation, which is usually used in the absence of an operating system or when more fine-grained control of memory allocation is required. This heap management implementation is usually defined in the mem.c and memp.c files.
To enable LwIP memory heap allocation, you need to define the relevant macros in opt.h, for example:
#define MEM_LIBC_MALLOC 0 // 禁用标准C库malloc
#define MEM_USE_POOLS 0 // 禁用内存池分配
// 可能还需要配置MEM_SIZE等宏来定义堆的大小
-
LwIP dynamic memory pool allocation :
Memory pool allocation is a strategy used in LwIP to optimize performance and reduce memory fragmentation. It pre-allocates fixed-size memory blocks and allocates and releases these blocks from the pool when needed.
To enable memory pool allocation, you need to define the relevant macros in opt.h, for example:
#define MEM_LIBC_MALLOC 0 // 禁用标准C库malloc
#define MEM_USE_POOLS 1 // 启用内存池分配
// 还需要配置一些其他的宏来定义内存池的大小和数量,例如MEMP_NUM_PBUF等
In opt.h, you will see many macro definitions related to memory management, which allow you to fine-tune LwIP's memory usage. These macros usually include:
- MEM_LIBC_MALLOC: Defines whether to use the standard C library for memory allocation.
- MEM_USE_POOLS: Defines whether to use LwIP's memory pool allocation.
- MEM_SIZE: Defines the memory size used for heap allocation (if heap allocation is used).
- Various MEMP_NUM_ macros: define the number of memory blocks in various memory pools.
- PBUF_POOL_SIZE: Defines the buffer size used for the pbuf memory pool.
Please note that the specific macro definitions and configurations may vary between different versions of LwIP. Therefore, when configuring LwIP's memory management, it is best to refer to the official documentation or comments in the source code of the LwIP version you are using.
2. Network interface
In lwIP, struct netif is a data structure used to describe a hardware network interface. For a single network card system, there is usually only one netif structure instance. However, for a multi-network card system, multiple netif structure instances can be created, each corresponding to a physical network interface.
These netif structure instances can be organized in a linked list so that lwIP can traverse all network interfaces. In struct netif, if the LWIP_SINGLE_NETIF macro is defined to 0 (or undefined), it will contain a pointer to the next netif structure for building a linked list.
/** ETHARP_TABLE_MATCH_NETIF==1: Match netif for ARP table entries.
* If disabled, duplicate IP address on multiple netifs are not supported
* (but this should only occur for AutoIP).
*/
#if !defined ETHARP_TABLE_MATCH_NETIF || defined __DOXYGEN__
#define ETHARP_TABLE_MATCH_NETIF !LWIP_SINGLE_NETIF
#endif
When lwIP needs to send or receive data, it will traverse this linked list and find the correct network interface to handle the data. For example, when a packet is received, lwIP will check the source address or destination address of the packet to determine which network interface should be used to handle the packet. Similarly, when lwIP needs to send a packet, it will select an appropriate network interface to send the data.
/** Generic data structure used for all lwIP network interfaces.
* The following fields should be filled in by the initialization
* function for the device driver: hwaddr_len, hwaddr[], mtu, flags */
struct netif {
#if !LWIP_SINGLE_NETIF
/** pointer to next in linked list */
struct netif *next;
#endif
#if LWIP_IPV4
/** IP address configuration in network byte order */
ip_addr_t ip_addr;
ip_addr_t netmask;
ip_addr_t gw;
#endif /* LWIP_IPV4 */
#if LWIP_IPV6
/** Array of IPv6 addresses for this netif. */
ip_addr_t ip6_addr[LWIP_IPV6_NUM_ADDRESSES];
/** The state of each IPv6 address (Tentative, Preferred, etc).
* @see ip6_addr.h */
u8_t ip6_addr_state[LWIP_IPV6_NUM_ADDRESSES];
#if LWIP_IPV6_ADDRESS_LIFETIMES
/** Remaining valid and preferred lifetime of each IPv6 address, in seconds.
* For valid lifetimes, the special value of IP6_ADDR_LIFE_STATIC (0)
* indicates the address is static and has no lifetimes. */
u32_t ip6_addr_valid_life[LWIP_IPV6_NUM_ADDRESSES];
u32_t ip6_addr_pref_life[LWIP_IPV6_NUM_ADDRESSES];
#endif /* LWIP_IPV6_ADDRESS_LIFETIMES */
#endif /* LWIP_IPV6 */
/** This function is called by the network device driver
* to pass a packet up the TCP/IP stack. */
netif_input_fn input;
#if LWIP_IPV4
/** This function is called by the IP module when it wants
* to send a packet on the interface. This function typically
* first resolves the hardware address, then sends the packet.
* For ethernet physical layer, this is usually etharp_output() */
netif_output_fn output;
#endif /* LWIP_IPV4 */
/** This function is called by ethernet_output() when it wants
* to send a packet on the interface. This function outputs
* the pbuf as-is on the link medium. */
netif_linkoutput_fn linkoutput;
#if LWIP_IPV6
/** This function is called by the IPv6 module when it wants
* to send a packet on the interface. This function typically
* first resolves the hardware address, then sends the packet.
* For ethernet physical layer, this is usually ethip6_output() */
netif_output_ip6_fn output_ip6;
#endif /* LWIP_IPV6 */
#if LWIP_NETIF_STATUS_CALLBACK
/** This function is called when the netif state is set to up or down
*/
netif_status_callback_fn status_callback;
#endif /* LWIP_NETIF_STATUS_CALLBACK */
#if LWIP_NETIF_LINK_CALLBACK
/** This function is called when the netif link is set to up or down
*/
netif_status_callback_fn link_callback;
#endif /* LWIP_NETIF_LINK_CALLBACK */
#if LWIP_NETIF_REMOVE_CALLBACK
/** This function is called when the netif has been removed */
netif_status_callback_fn remove_callback;
#endif /* LWIP_NETIF_REMOVE_CALLBACK */
/** This field can be set by the device driver and could point
* to state information for the device. */
void *state;
#ifdef netif_get_client_data
void* client_data[LWIP_NETIF_CLIENT_DATA_INDEX_MAX + LWIP_NUM_NETIF_CLIENT_DATA];
#endif
#if LWIP_NETIF_HOSTNAME
/* the hostname for this netif, NULL is a valid value */
const char* hostname;
#endif /* LWIP_NETIF_HOSTNAME */
#if LWIP_CHECKSUM_CTRL_PER_NETIF
u16_t chksum_flags;
#endif /* LWIP_CHECKSUM_CTRL_PER_NETIF*/
/** maximum transfer unit (in bytes) */
u16_t mtu;
#if LWIP_IPV6 && LWIP_ND6_ALLOW_RA_UPDATES
/** maximum transfer unit (in bytes), updated by RA */
u16_t mtu6;
#endif /* LWIP_IPV6 && LWIP_ND6_ALLOW_RA_UPDATES */
/** link level hardware address of this interface */
u8_t hwaddr[NETIF_MAX_HWADDR_LEN];
/** number of bytes used in hwaddr */
u8_t hwaddr_len;
/** flags (@see @ref netif_flags) */
u8_t flags;
/** descriptive abbreviation */
char name[2];
/** number of this interface. Used for @ref if_api and @ref netifapi_netif,
* as well as for IPv6 zones */
u8_t num;
#if LWIP_IPV6_AUTOCONFIG
/** is this netif enabled for IPv6 autoconfiguration */
u8_t ip6_autoconfig_enabled;
#endif /* LWIP_IPV6_AUTOCONFIG */
#if LWIP_IPV6_SEND_ROUTER_SOLICIT
/** Number of Router Solicitation messages that remain to be sent. */
u8_t rs_count;
#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */
#if MIB2_STATS
/** link type (from "snmp_ifType" enum from snmp_mib2.h) */
u8_t link_type;
/** (estimate) link speed */
u32_t link_speed;
/** timestamp at last change made (up/down) */
u32_t ts;
/** counters */
struct stats_mib2_netif_ctrs mib2_counters;
#endif /* MIB2_STATS */
#if LWIP_IPV4 && LWIP_IGMP
/** This function could be called to add or delete an entry in the multicast
filter table of the ethernet MAC.*/
netif_igmp_mac_filter_fn igmp_mac_filter;
#endif /* LWIP_IPV4 && LWIP_IGMP */
#if LWIP_IPV6 && LWIP_IPV6_MLD
/** This function could be called to add or delete an entry in the IPv6 multicast
filter table of the ethernet MAC. */
netif_mld_mac_filter_fn mld_mac_filter;
#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
#if LWIP_NETIF_USE_HINTS
struct netif_hint *hints;
#endif /* LWIP_NETIF_USE_HINTS */
#if ENABLE_LOOPBACK
/* List of packets to be queued for ourselves. */
struct pbuf *loop_first;
struct pbuf *loop_last;
#if LWIP_LOOPBACK_MAX_PBUFS
u16_t loop_cnt_current;
#endif /* LWIP_LOOPBACK_MAX_PBUFS */
#endif /* ENABLE_LOOPBACK */
};
From the definition of this structure, we can see that netif is a linked list structure, and the elements in the linked list are connected through the next pointer. Each network hardware interface corresponds to a variable of the netif structure. When there are multiple network hardware interfaces, these variables of the netif structure are connected to each other through the next pointer to form a linked list. Therefore, the user program should establish a global variable to maintain the linked list.
3. LwIP API
LwIP provides a set of application programming interfaces (APIs) that enable applications to easily use the TCP/IP protocol stack for network communications.
The following is a brief introduction to LwIP's main application programming interfaces and their functions:
- Socket API
- Function : Encapsulates the underlying network operations and provides an interface similar to Berkeley sockets for the application layer.
- File :Socket.c
- Features : Provides standard socket programming interfaces, such as socket(), bind(), listen(), accept(), connect(), send(), recv(), etc., to facilitate application porting.
- Netconn API
- Function : Provides a higher-level abstraction layer on top of the Socket API, allowing applications to use the TCP/IP protocol stack more conveniently.
- File : Api_lib.c
- Features : Encapsulates the underlying socket operations and provides a simpler interface, such as netconn_new(), netconn_bind(), netconn_listen(), netconn_accept(), netconn_connect(), netconn_send(), netconn_recv(), etc.
- Raw/Callback API
- Function : Provides a lower-level interface that allows applications to directly register callback functions to handle network events, such as data reception, sending completion, etc.
- Features : Suitable for applications that require more sophisticated control over network operations, such as systems with high real-time requirements.
- netif API
- Function : Used to manage network interfaces, such as adding, deleting, setting the default network interface, etc.
- Functions : netif_add(), netif_remove(), netif_set_default(), etc.
- Features : Abstracts the differences in network interfaces on different hardware platforms, allowing LwIP to support multiple network interface types.
- Pbuf API
- Function : Memory buffer for processing network packets.
- Features : It provides management functions such as allocation, release, and reference counting of data packet memory, supports chain memory structure, and improves memory usage efficiency.
- IP/ICMP/IGMP/UDP/TCP and other protocol APIs
- Function : Used to process operations of network protocols such as IP, ICMP, IGMP, UDP, TCP, etc.
- Features : These APIs provide a direct operation interface for the underlying network protocols, allowing applications to directly handle network protocol-related operations as needed.
In embedded systems, due to limited resources (such as memory, processor speed, etc.), using traditional BSD APIs (such as socket APIs in UNIX or Linux) may result in excessive resource consumption and is not suitable for these environments. For this reason, LwIP (Lightweight IP) is designed as a lightweight TCP/IP protocol stack, especially optimized for embedded systems.
Advantages of LwIP API
LwIP API在设计上充分考虑了嵌入式系统的特点,它与BSD API类似,但操作更为低级,能够充分利用LwIP的内部结构来避免BSD API对系统资源的过度依赖。这一特点使得LwIP API在嵌入式系统中具有显著的优势:
- 减少数据复制:LwIP API的设计避免了在应用程序和协议栈之间频繁地复制数据。通过直接处理内部缓冲区,LwIP API显著减少了数据复制带来的额外开销,提高了系统性能和响应速度。
- Efficient memory management : LwIP uses a unique buffer pool (pbuf) system to manage network packets, which makes memory usage more efficient. Through carefully designed memory allocation and release strategies, LwIP can minimize memory fragmentation and improve memory utilization.
- Configurability : LwIP API allows users to configure according to specific application requirements. Users can selectively support TCP, UDP, ICMP and other protocols, and adjust various parameters to optimize resource usage. This flexibility makes LwIP applicable to various embedded system scenarios.
- Real-time performance : Due to its lightweight design and efficient internal mechanisms, LwIP is often able to provide excellent real-time performance in embedded systems. This is critical for applications that need to respond quickly to network events.
BSD Socket Compatibility Layer
Although the LwIP API has many advantages, the ease of use and popularity of the BSD socket API cannot be ignored. In order to make full use of existing code bases and tools, LwIP retains a BSD Socket compatibility layer. This allows developers to easily migrate applications written for BSD sockets to the LwIP environment while enjoying the performance advantages brought by LwIP.
3. LwIP API
When porting the LWIP protocol stack to the GD32H7 processor, the porting of the underlying driver is indeed mainly focused on two aspects: modifying the ethernetif.c file to adapt to the network hardware of GD32H7, and writing the network driver for GD32H7. The following is a detailed description of these two parts of work:
3.1 Modify ethernetif.c
the file
The ethernetif.c file is a sample file provided by the LWIP protocol stack, which is used to connect the network hardware driver and the LWIP network protocol stack. This file needs to be modified to adapt to the specific network hardware interface of GD32H7. The main work includes:
-
Interface definition : defines the interface functions used to communicate with the GD32H7 network hardware. These functions usually include initialization functions (such as low_level_init), sending functions (such as low_level_output), and receiving functions (such as low_level_input).
-
Initialization function : In the low_level_init function, configure the network hardware interface of GD32H7, such as setting the MAC address, initializing the DMA descriptor, configuring interrupts, etc.
-
Send function : In the low_level_output function, implement the function of sending the data frame from the LWIP buffer to the network hardware. This usually involves copying the data frame to the send buffer of the network hardware and starting the sending process.
-
Receive function : In the low_level_input function, read the data frame from the network hardware's receive buffer and pass it to the LWIP protocol stack. This function may be set as the handler for the network hardware receive interrupt.
3.2 Write the network driver for GD32H7
Writing the network driver for GD32H7 is the key to realize the underlying network functions. This driver needs to realize the initialization of the network interface, sending and receiving messages, etc. The specific work includes:
-
Hardware initialization : In the driver, you need to configure the network hardware interface of GD32H7, including setting the MAC address, configuring the clock, initializing DMA, etc.
-
Interrupt processing : Network hardware usually uses interrupts to notify the CPU that data has arrived or has been sent. In the driver, you need to write interrupt handlers to respond to these interrupts and perform corresponding operations (such as reading the receive buffer, clearing the send completion flag, etc.).
-
Data transmission and reception : The driver needs to implement the data transmission and reception functions. The transmission function usually involves copying the data frame to the network hardware's transmission buffer and starting the transmission process; the reception function involves reading the data frame from the network hardware's reception buffer and passing it to the upper layer protocol stack.
-
Error handling : During network communication, various errors may occur (such as sending failure, receiving buffer overflow, etc.). The driver needs to be able to detect these errors and take appropriate measures (such as retrying sending, discarding received data frames, etc.).
-
Performance optimization : To improve the performance of network communication, the driver may need to implement some optimization measures, such as using DMA for data transfer, using interrupt merging to reduce the number of interrupts, etc.