LwIP 之一 源码目录文件详解及移植说明
?? lwIP 是 TCP/IP 協(xié)議套件的一個(gè)小型獨(dú)立實(shí)現(xiàn)。lwIP TCP/IP 實(shí)現(xiàn)的重點(diǎn)是減少 RAM 使用同時(shí)仍然有一個(gè)完整的 TCP。 這使得 lwIP 適合使用在具有數(shù) 10 千字節(jié)的可用 RAM 和空間大約 40 千字節(jié)的代碼 ROM 的嵌入式系統(tǒng)中。
?? lwIP 最開始由 Adam Dunkels 在瑞典計(jì)算機(jī)科學(xué)研究所(SICS)的計(jì)算機(jī)與網(wǎng)絡(luò)體系結(jié)構(gòu)(CNA)實(shí)驗(yàn)室時(shí)開發(fā),現(xiàn)在由全球開發(fā)人員網(wǎng)絡(luò)開發(fā)和維護(hù)。
版本變更
2.1.x
?? LWIP 從 2.0.3 版本,直接跳到了 2.1.0 版本,又是一個(gè)大的版本更新。增加了一些功能!同時(shí)源碼的目錄結(jié)構(gòu)也有了一定的變化(增加了一些文件)! 按照 LWIP 的發(fā)布策略,以后 2.1.x 都是從 2.1.0 版本開始的 BUG 修復(fù)版本,最新的 BUG 修復(fù)版本是 2.1.2。具體變化參見源碼目錄下的 CHANGELOG 文件。下圖顯示了 2.0.3 版 和 2.1.2 版的文件對(duì)比差異:
??關(guān)于這個(gè)版本,官網(wǎng)有個(gè)這么一句話 “ Oh, and this will be the last release with a separate contrib repository. I’ll be merging the files from contrib into the main repository soon after the release. In the git world, having two separate repositories just doesn’t work nice. ” 。大意就是,這是帶有獨(dú)立 contrib 庫的最后一版本,后續(xù)會(huì)將 contrib 合并到 LWIP 主版本庫中。
??其次,如果詳細(xì)去看 LWIP 的說明文檔,會(huì)發(fā)現(xiàn)文檔中有些描述還是之前的,并沒有跟隨版本變更而改變!例如在doc目錄下,原來的系統(tǒng)結(jié)構(gòu)模板文件名為sys_arch.txt,在此版本沒有了這個(gè)文件,但是說明文檔中,仍然有這部分的介紹!
2.0.x
?? LWIP 從 1.4.1 版本,直接跳到了 2.0.x 版本,是一個(gè)大的版本更新。源碼結(jié)構(gòu)變化比較大! 按照 LWIP 的發(fā)布策略,以后 2.0.x 都是從 2.0.0 版本開始的 BUG 修復(fù)版本,最終的 BUG 修復(fù)版本是 2.0.3。2.0.3 版本修復(fù)自 2.0.2 以來的一些錯(cuò)誤,沒有任何功能的變化。具體如下:
- 2017-09-11: Simon Goldschmidt
- tcp_in.c: fix bug #51937 (leaking tcp_pcbs on passive close with unacked data)
- 2017-08-02: Abroz Bizjak/Simon Goldschmidt
- multiple fixes in IPv4 reassembly (leading to corrupted datagrams received)
- 2017-03-30: Simon Goldschmidt
- dhcp.c: return ERR_VAL instead of asserting on offset-out-of-pbuf
- 2017-03-23: Dirk Ziegelmeier
- dhcp.h: fix bug #50618 (dhcp_remove_struct() macro does not work)
1.x.x
最終版本為 1.4.1。
源碼目錄文件
??要使用 LWIP 的源碼由兩部分組成,分別為 LWIP 和 contrib 。這兩個(gè)是由兩個(gè)獨(dú)立的版本庫,并且由不同的人來負(fù)責(zé)的!我們?cè)趯?shí)際使用 LWIP 時(shí),這兩部分都是需要使用的!
其中,contrib 中是一些和平臺(tái)移植相關(guān)的代碼,LWIP 則是 TCP/IP 協(xié)議棧的核心源碼!
??下面我就以 2.0.3 版為例!從 1.4.1 到 2.0.3(從 2.0.0 開始),LwIP 的源碼有了一定的變化,甚至于源碼的文件結(jié)構(gòu)也不一樣,內(nèi)部的一些實(shí)現(xiàn)源文件也被更新和替換了。其源碼目錄結(jié)構(gòu)如下所示(對(duì)于簡單的文件以注釋的形式給出,核心源碼下文會(huì)詳細(xì)說明):
??在LwIP的源碼包中,一共包含三個(gè)目錄:doc、src、test。分別對(duì)應(yīng):源碼的文檔、源碼、測試代碼。最新版的源碼與早期源碼在某些目錄文件是有區(qū)別的。以下主要說明 src ,即:源碼部分。
api目錄
??LwIP 提供了兩種類型的 API : Callback-style APIs 和 Sequential-style APIs 。其中,Callback-style APIs 即為LwIP最底層的接口,被稱為Raw API或者Native API;而Sequential-style APIs主要是對(duì)底層接口進(jìn)行了封裝,主要包含:Netconn API、NETIF API和Socket API。在實(shí)際使用中,使用者可以任選一種API來使用。
??api目錄下主要包含對(duì)底層API(raw API)封裝后的高級(jí)API的代碼。 如果直接使用底層的的Raw API,則不需要該目錄下的文件。而封裝后的高級(jí)別的這兩種API實(shí)現(xiàn)的原理都是通過引進(jìn)郵箱和信號(hào)量等通信與同步機(jī)制,來實(shí)現(xiàn)對(duì)內(nèi)核中***Raw API(native API)***函數(shù)的封裝和調(diào)用。要使用這兩種類型的API,需要底層操作系統(tǒng)的支持。
- Raw API:(有時(shí)稱為native API)是一個(gè)設(shè)計(jì)用于在沒有操作系統(tǒng)時(shí),實(shí)現(xiàn)零拷貝發(fā)送和接收的事件驅(qū)動(dòng)的API。 這個(gè)API也被核心堆棧用于各種協(xié)議之間的交互。 這是在沒有操作系統(tǒng)的情況下,運(yùn)行l(wèi)wIP時(shí)唯一可用的API。
??因?yàn)镃allback/Raw API是協(xié)議棧提供的三種編程接口中最復(fù)雜的一種, 它通過直接與協(xié)議棧內(nèi) 核函數(shù)交互以實(shí)現(xiàn)編程,所以整個(gè)過程比較復(fù)雜。源碼的doc目錄下有一個(gè)專門的文檔:rawapi.txt說明了具體如何使用Raw API。 - Netconn API: 為普通的、順序的程序提供了使用lwIP棧的方法。 線程安全,僅從非TCPIP線程調(diào)用。 基于網(wǎng)絡(luò)緩沖區(qū)(包含數(shù)據(jù)包緩沖區(qū)(PBUF))的TX / RX處理,以避免復(fù)制數(shù)據(jù)。這與BSD Socket API非常相似。 執(zhí)行模型基于 打開-讀取-寫入-關(guān)閉 范例。 由于TCP / IP堆棧本質(zhì)上是事件,所以TCP / IP代碼和應(yīng)用程序必須駐留在不同的執(zhí)行上下文(線程)中。
- Socket API: 它是建立在Netconn API之上的。其主要是由于BSD Socket API是網(wǎng)絡(luò)通信的一個(gè)實(shí)現(xiàn)。線程安全,僅從非TCPIP線程調(diào)用。BSD Socket API已經(jīng)是網(wǎng)絡(luò)套接字的事實(shí)上的抽象標(biāo)準(zhǔn)。目前,所有主流操作系統(tǒng)均實(shí)現(xiàn)了BSD Socket API。出于此,LwIP也提供了一套BSD Socket API。但是,標(biāo)準(zhǔn) socket 庫中的部分函數(shù)仍無法直接通過封裝 Netconn API 來實(shí)現(xiàn),因此 LwIP 中提供的socket 函數(shù)并不完整,用戶最好不要使用它進(jìn)行實(shí)際應(yīng)用程序開發(fā)。對(duì)應(yīng)文件為posix/sys/socket.h。
以下為每個(gè)文件的具體說明:
- api_lib.c: 包含 對(duì)外提供的 sequential API 函數(shù)的實(shí)現(xiàn)。函數(shù)名均以netconn_開頭。主要分為三組API:同時(shí)可用于TCP和UDP的API、只能用于TCP的API、只能用于UDP的API。
- api_msg.c: 包含sequential API內(nèi)部自己調(diào)用的函數(shù)的實(shí)現(xiàn)。主要包含API消息的封裝和處理函數(shù)
- err.c: 錯(cuò)誤管理模塊
- netbuf.c: 包含了上層數(shù)據(jù)包管理函數(shù)的實(shí)現(xiàn)。應(yīng)用程序描述待發(fā)送數(shù)據(jù)和已接收數(shù)據(jù)的基本結(jié)構(gòu)。該結(jié)構(gòu)只是對(duì)內(nèi)核 pbuf 的簡單封裝,避免了數(shù)據(jù)的拷貝。緩沖區(qū)不能在多個(gè)線程之間共享。
- netdb.c: 包含與主機(jī)名字轉(zhuǎn)換相關(guān)的函數(shù),主要在 socket 中被使用到
- netifapi.c: 包含了上層網(wǎng)絡(luò)接口管理函數(shù)的實(shí)現(xiàn)
- sockets.c: 包含了 Socket API 函數(shù)的實(shí)現(xiàn)
- tcpip.c: 包含了上層 API 與協(xié)議棧內(nèi)核交互的函數(shù),它是整個(gè)上層 API 功能得以實(shí)現(xiàn)的一個(gè)樞紐,其實(shí)現(xiàn)的功能可以簡單理解為:從 API 函數(shù)處接收消息,然后將消息遞交給內(nèi)核函數(shù),內(nèi)核函數(shù)根據(jù)消息做出相應(yīng)的處理。
apps目錄
??使用lwIP低級(jí)raw API編寫的高層應(yīng)用程序。
core目錄
??TCP/IP 協(xié)議棧的核心部分。主要包含協(xié)議實(shí)現(xiàn)、內(nèi)存和緩沖區(qū)管理以及底層raw API的實(shí)現(xiàn)。它包含了IP、ICMP、IGMP、TCP、UDP 等核 心協(xié)議以及建立在它們基礎(chǔ)上的 DNS、DHCP、SNMP 等上層應(yīng)用協(xié)議。內(nèi)核源代碼可以單獨(dú)運(yùn)行,且不需要操作系統(tǒng)的支持。即:直接使用 raw API 編程。
-
ipv4目錄: 包含了IPv4 標(biāo)準(zhǔn)中與IP層數(shù)據(jù)包處理相關(guān)的所有代碼
-
autoip.c: 這是lwIP TCP / IP協(xié)議棧的AutoIP實(shí)現(xiàn)。 它旨在符合RFC 3927。
-
dhcp.c: 實(shí)現(xiàn)了DHCP 客戶端的所有代碼,DHCP 稱為動(dòng)態(tài)主機(jī)配置協(xié)議,DHCP 可以使計(jì)算機(jī)使用者不必為主機(jī)的IP 地址的分配問題而煩惱。DHCP 也是一個(gè)上層應(yīng) 用程序, 通常DHCP客戶端通過使用UDP提供的功能來實(shí)現(xiàn)與DHCP服務(wù)器的通信, 從DHCP 服務(wù)器處獲得一個(gè)有效的IP 地址
-
etharp.c: 包含了ARP 協(xié)議實(shí)現(xiàn)的相關(guān)函數(shù),ARP 協(xié)議是以太網(wǎng)通信中的重要部分, 主要用來實(shí)現(xiàn)主機(jī)以太網(wǎng)物理地址到IP 地址的映射。這點(diǎn)是非常必要的,以太網(wǎng)中底層數(shù)據(jù)包的發(fā)送是基于網(wǎng)卡物理地址的,而不是主機(jī)的IP地址。 通過ARP協(xié)議, 主機(jī)可以發(fā)送請(qǐng)求, 得到鄰居節(jié)點(diǎn)的IP 地址與物理地址等信息,為以太網(wǎng)數(shù)據(jù)包交互提供保證。
-
icmp.c: 包含了ICMP 協(xié)議實(shí)現(xiàn)的相關(guān)函數(shù),ICMP 協(xié)議為IP 數(shù)據(jù)包傳遞過程中的差錯(cuò) 報(bào)告、差錯(cuò)糾正以及目的地址可達(dá)性測試提供了支持,常見的Ping 命令就屬于ICMP 應(yīng)用的 一種
-
igmp.c: 包含了網(wǎng)絡(luò)組管理協(xié)議IGMP 的實(shí)現(xiàn),IGMP 為網(wǎng)絡(luò)中的多播數(shù)據(jù)傳輸提供了 支持,主機(jī)加入某個(gè)多播組后,可以接收到該組的UDP 多播數(shù)據(jù)
-
ip4_addr.c: 實(shí)現(xiàn)了幾個(gè)比較簡單 的IP 地址處理函數(shù),如判斷一個(gè)IP 地址是否為廣播地址的函數(shù),以及32 位IP 地址與點(diǎn)分十 進(jìn)制地址間的轉(zhuǎn)換函數(shù)等
-
ip4_frag.c: 提供了IP 層數(shù)據(jù)包分片與重組相關(guān)的函數(shù)的實(shí)現(xiàn)。
-
Ip4.c: 包含了IPv4 協(xié)議實(shí)現(xiàn)的相關(guān)函數(shù),如數(shù)據(jù)包的接收、遞交、發(fā)送等
-
ipv6目錄: 包含了IPv6 標(biāo)準(zhǔn)中和IP層數(shù)據(jù)包處理相關(guān)的所有代碼
-
mld6.c: IPv6的多播偵聽器發(fā)現(xiàn)。旨在符合RFC 2710。
-
nd6.c: IPv6的鄰居發(fā)現(xiàn)和無狀態(tài)地址自動(dòng)配置。 旨在符合RFC 4861(鄰居發(fā)現(xiàn))和RFC 4862(地址自動(dòng)配置)。
-
Iinet6.c: 目前沒有啥實(shí)際內(nèi)容。
-
其他文件與IPv4目錄下的功能相同,不過是在IPv6版上的實(shí)現(xiàn)
-
def.c: 文件包含了IP 層使用到的一些功能函數(shù)的定義,如IP 地址的轉(zhuǎn)換、網(wǎng)絡(luò)字節(jié)序與 主機(jī)字節(jié)序轉(zhuǎn)換等
-
dns.c: 實(shí)現(xiàn)了DNS 客戶端的所有代碼,DNS 稱為域名系統(tǒng),通常用戶要訪問某個(gè)外 部的主機(jī)時(shí),可能只知道該主機(jī)的名字,而不知道該主機(jī)的IP 地址。常理也是這樣,用戶可 以簡單地記得某個(gè)主機(jī)的名字,如www.baidu.com,卻很難記得這個(gè)主機(jī)的IP 地址。通過使 用DNS,可以解決這個(gè)問題,通過訪問DNS 服務(wù)器,我們可以得到與主機(jī)名對(duì)應(yīng)的IP 地址, 這為用戶的使用帶來了方便。值得指出的是,DNS 也是一個(gè)上層應(yīng)用程序,它通常基于UDP 來傳輸數(shù)據(jù)。
-
inet_chksum.c: 這些是校驗(yàn)和算法的一些參考實(shí)現(xiàn),其目標(biāo)是簡單,正確和完全便攜。 Checksumming是您希望為您的平臺(tái)優(yōu)化的第一件事。 如果您創(chuàng)建自己的版本,請(qǐng)將其鏈接到cc.h中
-
init.c: 主要包含了一個(gè)與LwIP 協(xié)議棧初始化密切相關(guān)的函數(shù),以及一些 協(xié)議棧配置信息的檢查與輸出
-
ip.c: IPv4和IPv6共用的代碼。
-
mem.c: 包含了協(xié)議棧內(nèi)存堆管理函數(shù)的實(shí)現(xiàn)。
(1)LwIP 支持的動(dòng)態(tài)內(nèi)存管理機(jī)制主要有三種:C庫自帶內(nèi)存分配策略(malloc/free/realloc)、動(dòng)態(tài)內(nèi)存堆(Heap)分配策略、動(dòng)態(tài)內(nèi)存池(pool)分配策略。
(2)此文件即LwIP實(shí)現(xiàn)的動(dòng)態(tài)內(nèi)存堆(Heap)分配策略。
(3)默認(rèn)不使用C庫自帶內(nèi)存分配策略。 在lwipopts.h使用宏值MEM_LIBC_MALLOC = 1選擇使用C庫自帶內(nèi)存分配策略 -
memp.c: 包含了協(xié)議棧內(nèi)存池管理函數(shù)的實(shí)現(xiàn)。
(1)LwIP 支持的動(dòng)態(tài)內(nèi)存管理機(jī)制主要有三種:C庫自帶內(nèi)存分配策略(malloc/free/realloc)、動(dòng)態(tài)內(nèi)存堆(HEAP)分配策略、動(dòng)態(tài)內(nèi)存池(POOL)分配策略。
(2)此文件即LwIP實(shí)現(xiàn)的動(dòng)態(tài)內(nèi)存池(POOL)分配策略
(3)默認(rèn)不使用動(dòng)態(tài)內(nèi)存堆(HEAP)分配策略。 在lwipopts.h使用宏值MEMP_MEM_MALLOC = 1選擇使用動(dòng)態(tài)內(nèi)存堆(HEAP)分配策略 -
netif.c: 包含了協(xié)議棧網(wǎng)絡(luò)接口管理的相關(guān)函數(shù),協(xié)議棧支持多個(gè)網(wǎng)絡(luò)接口,例如以太網(wǎng)接口、SLIP接口等,協(xié)議棧內(nèi)部對(duì)每個(gè)接口用一個(gè)對(duì)應(yīng)的netif數(shù)據(jù)結(jié)構(gòu)進(jìn)行描述,并通過使用netif.c中的函數(shù)進(jìn)行統(tǒng)一管理。同時(shí),netif.c還包含了環(huán)回接口管理和數(shù)據(jù)處理的相關(guān)函數(shù),使用環(huán)回接口可以實(shí)現(xiàn)同一主機(jī)上兩個(gè)應(yīng)用程序之間的數(shù)據(jù)交換。
-
pbuf.c: 包含了協(xié)議棧內(nèi)核使用的數(shù)據(jù)包管理函數(shù),數(shù)據(jù)包pbuf管理的實(shí)現(xiàn)是整個(gè)協(xié)議棧中很有特色的地方,采用特殊的數(shù)據(jù)包pbuf 結(jié)構(gòu),可以避免數(shù)據(jù)在各個(gè)層次之間遞交時(shí)的拷貝,這既提高了數(shù)據(jù)遞交效率,也節(jié)省了內(nèi)存空間。
-
raw.c: 為應(yīng)用層提供了一種直接和IP 數(shù)據(jù)包交互的方式,這類似于Socket 編程中原始套接字的概念,它同TCP、UDP 處于同一等級(jí),享受IP 層提供的服務(wù)。使用原始套接字, 可以直接讀取IP 層接收到的數(shù)據(jù)包,例如ICMP 包、UDP 包等,也可以自行構(gòu)造ping包等, 為用戶的程序編寫提供了很大的方便。這是LwIP最底層的API部分。使用這一級(jí)別的api進(jìn)行編程需要對(duì)TCP/IP相當(dāng)了解。
-
stats.c: 包含了協(xié)議棧內(nèi)部數(shù)據(jù)統(tǒng)計(jì)與顯示的相關(guān)函數(shù),如內(nèi)存使用狀況、郵箱、信號(hào)量等信息。
-
sys.c: 實(shí)現(xiàn)了一個(gè)簡單的函數(shù)void sys_msleep(u32_t ms);,它借助操作系統(tǒng)模擬層的信號(hào)量機(jī)制完成睡眠一定時(shí)間的功能,該函數(shù)主要在PPP 中使用。前面曾提到過,若需要使用協(xié)議棧的Sequential API和Socket API,則必須使用底層操作系統(tǒng)提供的郵箱與信號(hào)量機(jī)制,這時(shí)內(nèi)核要求移植者提供一個(gè)稱為sys_arch.c的操作系統(tǒng)模擬層文件,該文件主要完成對(duì)操作系統(tǒng)中郵 箱與信號(hào)量函數(shù)的封裝。需要注意,只有在提供文件sys_arch.c 的基礎(chǔ)上,文件`sys.c 才有效,換句話說,在無操作系統(tǒng)環(huán)境下運(yùn)行LwIP 時(shí),sys.c文件都不會(huì)被編譯。
-
tcp_in.c: 包含了TCP 協(xié)議中數(shù)據(jù)接收、處理相關(guān)的函數(shù),最重要的TCP 狀態(tài)機(jī)函數(shù)也在這個(gè)文件中
-
tcp_out.c: 包含了TCP 中數(shù)據(jù)發(fā)送相關(guān)的函數(shù),例如數(shù)據(jù)包發(fā)送函數(shù)、超時(shí)重傳函數(shù)等。
-
tcp: 包含了對(duì)TCP 控制塊操作的函數(shù),也包括了TCP 定時(shí)處理函數(shù)
-
timeouts.c: 統(tǒng)一完成了對(duì)內(nèi)核各個(gè)協(xié)議定時(shí)事件處理函數(shù)的封裝,同時(shí)對(duì)各個(gè)注冊(cè)的定時(shí)事件進(jìn)行處理。在有無操作系統(tǒng)模擬層的環(huán)境下,timeouts.c 采用不同的方法來實(shí)現(xiàn)定時(shí):在無操作系統(tǒng)模擬層時(shí),timeouts.c使用系統(tǒng)時(shí)鐘函數(shù)sys_now(不同的平臺(tái)的都需要移植該函數(shù))來獲得當(dāng)前系統(tǒng)時(shí)間,從而可以判斷出各個(gè)事件是否超時(shí);在有操作系統(tǒng)模擬層時(shí),timers.c 實(shí)現(xiàn)了對(duì)操作系統(tǒng)模擬層郵箱等待函數(shù)的再次封裝,得到一個(gè)具有協(xié)議棧特色的郵箱操作函數(shù)。 所謂特色,就是在郵箱等待函數(shù)中加入一種機(jī)制,在郵箱上等待消息的同時(shí),可以同時(shí)實(shí)現(xiàn)協(xié)議棧中各個(gè)定時(shí)事件的正確處理。
在2.0.0之前的版本中,該文件名為timer.c。新的timeouts.c對(duì)原來的內(nèi)容進(jìn)行了一定的封裝,簡化。
-
udp.c: 包含了實(shí)現(xiàn)UDP 協(xié)議的相關(guān)函數(shù),包括UDP 控制塊管理、UDP 數(shù)據(jù)包發(fā)送函數(shù)、UDP 數(shù)據(jù)包接收函數(shù)等
include目錄
LwIP使用的各種頭文件。與各源碼目錄相對(duì)應(yīng)。
- netif文件夾: 主要是針對(duì)netif目錄中源文件的各種頭文件。
- posix文件夾: 主要是針對(duì)POSIX標(biāo)準(zhǔn),進(jìn)行的封裝,里面的文件非常簡單,基本都是對(duì)外部的引用。
- lwip文件夾: LwIP的各種頭文件,其中需要注意的有opt.h 文件,它包含了所有LwIP內(nèi)核參數(shù)的默認(rèn)配置值;還有init.h 文件,它包含了與當(dāng)前 LwIP 源代碼信息相關(guān)的宏定義,如協(xié)議版本號(hào)、是否為官方版等。
- apps文件夾: 主要是針對(duì)源碼中 app目錄的各頭文件。
- priv文件夾: LwIP內(nèi)部使用的頭文件,禁止外部使用
- prot文件夾: 這個(gè)文件夾中主要是針對(duì)TCP/IP規(guī)約的各種定義。與netif文件夾中同名文件區(qū)別在于netif文件夾中的文件均為對(duì)外提供的API,本目錄才是對(duì)規(guī)約層各種結(jié)構(gòu)的定義。
netif目錄
??該目錄下主要包含通用網(wǎng)絡(luò)接口設(shè)備驅(qū)動(dòng)程序。實(shí)現(xiàn)最底層的相關(guān)協(xié)議,該部分的多數(shù)源碼基本已經(jīng)到了最底層和硬件直接打交道了。
- ppp文件夾: 包含了PPP 協(xié)議實(shí)現(xiàn)的源代碼。PPP 協(xié)議即點(diǎn)對(duì)點(diǎn)協(xié)議,它提供了 一種在點(diǎn)對(duì)點(diǎn)線路上傳輸多協(xié)議數(shù)據(jù)包的標(biāo)準(zhǔn)格式,PPP 協(xié)議為鏈路的建立、控制與認(rèn)證提供 了標(biāo)準(zhǔn)。起初PPP 主要用來替代SLIP 這種簡單的串行鏈路數(shù)據(jù)傳輸協(xié)議,但是由于其完整的認(rèn)證機(jī)制,后來在以太網(wǎng)上也引入了PPP 機(jī)制,即PPPoE,它已成為近年來小區(qū)寬帶撥號(hào)上網(wǎng)的主要方式。使用PPPoE,為用戶的上網(wǎng)計(jì)費(fèi)、配置、接入等提供了方便。LwIP 提供了對(duì)PPPoE 的支持,在ppp文件夾下的PPPoE.c文件中有相關(guān)的函數(shù)實(shí)現(xiàn)。
- ethernet.c: 以太網(wǎng)接口的共享代碼。目前就兩個(gè)函數(shù):用來從網(wǎng)卡接收以太網(wǎng)數(shù)據(jù)包的函數(shù)和在網(wǎng)卡上發(fā)送以太網(wǎng)數(shù)據(jù)包的函數(shù)。
- ethernetif.c : 包含了與以太網(wǎng)網(wǎng)卡密切相關(guān)的初始化、發(fā)送、接收等函數(shù)的實(shí)現(xiàn)。注意:這個(gè)文件夾中的函數(shù)并不能使用, 它們都是一個(gè)框架性的結(jié)構(gòu), 移植者需要根據(jù)自己使用的網(wǎng) 卡特性來完成這些函數(shù)。
- slipif.c: 和 ethernetif.c 很相似,SLIP即串行鏈路IP,它提供了一種在串行鏈路上傳送IP 數(shù)據(jù)包的函數(shù)定義。SLIP 協(xié)議比較簡單,它只是定義了一系列的字符,以實(shí)現(xiàn)對(duì)鏈路上的IP數(shù)據(jù)包封裝和發(fā)送,除此之外,它不提供任何尋址、錯(cuò)誤檢測、包類型識(shí)別機(jī)制,因此相關(guān)驅(qū)動(dòng)程序的實(shí)現(xiàn)也比較簡單。它需要一個(gè)sio(串行I / O)模塊才能工作。移植者需要根據(jù)自己使用的串行線路特性(如串口)來實(shí)現(xiàn)以下這些函數(shù)(通常在另一個(gè)文件中,不要直接修改該文件):
- u32_t sio_read(sio_fd_t fd, u8_t *data, u32_t len);
- u32_t sio_tryread(sio_fd_t fd, u8_t *data, u32_t len);
- void sio_send(u8_t c, sio_fd_t fd);
- sio_fd_t sio_open(u8_t devnum);
- lowpan6.c: 6LoWPAN協(xié)議的實(shí)現(xiàn)文件。6LoWPAN是一種基于IPv6的低速無線個(gè)域網(wǎng)標(biāo)準(zhǔn),即IPv6 over IEEE 802.15.4。
移植說明
??LWIP 的手動(dòng)移植可以說非常麻煩,需要我們自己實(shí)現(xiàn)好多文件。最主要的是,需要實(shí)現(xiàn)的文件還沒有個(gè)統(tǒng)一的說明,必須要東拼西湊! LWIP 的移植由兩部分組成,分別為 LWIP 協(xié)議棧和 contrib 應(yīng)用實(shí)例 。目前,這兩部分是由兩個(gè)獨(dú)立的版本庫控制,并且由不同的人來負(fù)責(zé)的(好消息是從2.1.x之后,這倆要合并了)!我們?cè)谝浦彩褂?LWIP 時(shí),需要從官網(wǎng)分別下載這兩部分的源代碼!
其中,contrib 中是一些和平臺(tái)移植相關(guān)的代碼以及一些使用 LWIP 實(shí)現(xiàn)的應(yīng)用,在移植時(shí)非常有用;LWIP 則是 TCP/IP 協(xié)議棧的核心源碼!綜上所述,LwIP 的移植主要分為以下這些部分:
- 運(yùn)行環(huán)境的搭建: 有其需要注意的是,針對(duì)嵌入式的移植時(shí),無論哪種模式,都需要首先需要處理硬件資源,例如在 STM32Fx 系列芯片移植時(shí),需要提供文件:stm32f4x7_eth_bsp.c/h,已處理使用的硬件資源。當(dāng)然,如果是在 Windows 上做測試使用,則沒有這一步。
- 源代碼文件的整理: 主要就是東拼西湊LWIP需要的文件(LWIP需要用戶實(shí)現(xiàn)很多文件)
- LwIP 的基本配置: 根據(jù)自己的需要來配置功能
- 與 CPU 或編譯器相關(guān)源文件: LWIP 需要用戶的文件之一
- 與操作系統(tǒng)的接口源文件(僅在系統(tǒng)模式下使用): LWIP 需要用戶的文件之一
- 與底層網(wǎng)卡驅(qū)動(dòng)的接口源文件。: LWIP 需要用戶的文件之一
- 初始化 LwIP,然后正常使用各功能即可。:
源碼文件整理
??源代碼文件的整理需要注意目錄結(jié)構(gòu),因?yàn)?LWIP 源碼的頭文件的包含路徑是包含路徑的。否則我們就必須更改LWIP 的源代碼!整理完成基本就是以下這個(gè)結(jié)構(gòu):
LWIP │ lwipopts.h /* 這四個(gè)文件屬于用戶文件,每個(gè)用戶可能是不同的,通常放在用戶自己的目錄*/ │ perf.c │ sys_arch.c | ethernetif.c ├─arch /* 這個(gè)需要我們自己建立目錄,存放以下頭文件!注意,根據(jù)平臺(tái),以下文件并不是全部都需要 */ │ bpstruct.h │ cc.h │ cpu.h │ epstruct.h │ perf.h │ sys_arch.h └─lwip /* 這個(gè)目錄下就是 LWIP 源碼目錄下的 src 目錄中的所有文件!根據(jù)功能需要,里面的部分文件其實(shí)是可以不需要的 */├─api├─apps├─core├─include└─netif??lwIP 可以用于兩種基本模式:主循環(huán)模式(通過配置 NO_SYS == 1,在目標(biāo)系統(tǒng)上運(yùn)行沒有 OS/RTOS)或OS 模式(TCPIP 線程,目標(biāo)系統(tǒng)上有一個(gè)操作系統(tǒng))。在兩種模式下的移植時(shí)區(qū)別還是很大的!
- Mainloop Mode: 即裸機(jī)下的死循環(huán)模式。在主循環(huán)模式下,只能使用回調(diào)式 API(Raw API)。需要確保確保在 lwIP 中一次只有一個(gè)執(zhí)行環(huán)境。
- OS Mode: 此時(shí),使用者需要提供一個(gè)名為sys_arch.c/h 的文件。因?yàn)樵谙到y(tǒng)模式下,LwIP 需要系統(tǒng)的信號(hào)量、郵箱隊(duì)列等。LWIP 提供了這兩個(gè)文件的模板,用戶只需要在模板文件中進(jìn)行實(shí)現(xiàn)各預(yù)定義的接口即可。
第一步:LwIP的基本配置
LwIP 的配置主要通過文件 lwipopts.h 實(shí)現(xiàn),移植時(shí)需要根據(jù)自己的需要,進(jìn)行相關(guān)的配置,然后在自己的項(xiàng)目中添加該文件。一個(gè)在 STM32F4 芯片移植是的配置文件如下:
/********************************************************************************* @file lwipopts.h* @author MCD Application Team* @version V1.1.0* @date 31-July-2013* @brief lwIP Options Configuration.* This file is based on Utilities\lwip_v1.4.1\src\include\lwip\opt.h * and contains the lwIP configuration for the STM32F4x7 demonstration.******************************************************************************* @attention** <h2><center>© COPYRIGHT 2013 STMicroelectronics</center></h2>** Licensed under MCD-ST Liberty SW License Agreement V2, (the "License");* You may not use this file except in compliance with the License.* You may obtain a copy of the License at:** http://www.st.com/software_license_agreement_liberty_v2** Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.********************************************************************************/#ifndef __LWIPOPTS_H__ #define __LWIPOPTS_H__/*** SYS_LIGHTWEIGHT_PROT==1: if you want inter-task protection for certain* critical regions during buffer allocation, deallocation and memory* allocation and deallocation.*/ #define SYS_LIGHTWEIGHT_PROT 0#define ETHARP_TRUST_IP_MAC 0 #define IP_REASSEMBLY 0 #define IP_FRAG 0 #define ARP_QUEUEING 0/*** NO_SYS==1: Provides VERY minimal functionality. Otherwise,* use lwIP facilities.*/ #define NO_SYS 0/* ---------- Memory options ---------- */ /* MEM_ALIGNMENT: should be set to the alignment of the CPU for whichlwIP is compiled. 4 byte alignment -> define MEM_ALIGNMENT to 4, 2byte alignment -> define MEM_ALIGNMENT to 2. */ #define MEM_ALIGNMENT 4/* MEM_SIZE: the size of the heap memory. If the application will send a lot of data that needs to be copied, this should be set high. */ #define MEM_SIZE (5*1024)/* MEMP_NUM_PBUF: the number of memp struct pbufs. If the applicationsends a lot of data out of ROM (or other static memory), thisshould be set high. */ #define MEMP_NUM_PBUF 100 /* MEMP_NUM_UDP_PCB: the number of UDP protocol control blocks. Oneper active UDP "connection". */ #define MEMP_NUM_UDP_PCB 6 /* MEMP_NUM_TCP_PCB: the number of simulatenously active TCPconnections. */ #define MEMP_NUM_TCP_PCB 10 /* MEMP_NUM_TCP_PCB_LISTEN: the number of listening TCPconnections. */ #define MEMP_NUM_TCP_PCB_LISTEN 5 /* MEMP_NUM_TCP_SEG: the number of simultaneously queued TCPsegments. */ #define MEMP_NUM_TCP_SEG 20 /* MEMP_NUM_SYS_TIMEOUT: the number of simulateously activetimeouts. */ #define MEMP_NUM_SYS_TIMEOUT 10/* ---------- Pbuf options ---------- */ /* PBUF_POOL_SIZE: the number of buffers in the pbuf pool. */ #define PBUF_POOL_SIZE 20/* PBUF_POOL_BUFSIZE: the size of each pbuf in the pbuf pool. */ #define PBUF_POOL_BUFSIZE 500/* ---------- TCP options ---------- */ #define LWIP_TCP 1 #define TCP_TTL 255/* Controls if TCP should queue segments that arrive out oforder. Define to 0 if your device is low on memory. */ #define TCP_QUEUE_OOSEQ 0/* TCP Maximum segment size. */ #define TCP_MSS (1500 - 40) /* TCP_MSS = (Ethernet MTU - IP header size - TCP header size) *//* TCP sender buffer space (bytes). */ #define TCP_SND_BUF (5*TCP_MSS)/* TCP_SND_QUEUELEN: TCP sender buffer space (pbufs). This must be at leastas much as (2 * TCP_SND_BUF/TCP_MSS) for things to work. */#define TCP_SND_QUEUELEN (4* TCP_SND_BUF/TCP_MSS)/* TCP receive window. */ #define TCP_WND (2*TCP_MSS)/* ---------- ICMP options ---------- */ #define LWIP_ICMP 1/* ---------- DHCP options ---------- */ /* Define LWIP_DHCP to 1 if you want DHCP configuration ofinterfaces. DHCP is not implemented in lwIP 0.5.1, however, soturning this on does currently not work. */ #define LWIP_DHCP 1/* ---------- UDP options ---------- */ #define LWIP_UDP 1 #define UDP_TTL 255/* ---------- Statistics options ---------- */ #define LWIP_STATS 0 #define LWIP_PROVIDE_ERRNO 1/* ---------- link callback options ---------- */ /* LWIP_NETIF_LINK_CALLBACK==1: Support a callback function from an interface* whenever the link changes (i.e., link down)*/ #define LWIP_NETIF_LINK_CALLBACK 1/*------------------------------------------------ Checksum options ------------------------------------------------ *//* The STM32F4x7 allows computing and verifying the IP, UDP, TCP and ICMP checksums by hardware:- To use this feature let the following define uncommented.- To disable it and process by CPU comment the the checksum. */ /* 注意:校驗(yàn)和可以選擇 由硬件來處理。如果硬件支持該功能,則加載其配置文件(其中需要定義 CHECKSUM_BY_HARDWARE ) */ #include "stm32f4x7_eth_conf.h" #ifdef CHECKSUM_BY_HARDWARE /* 如果芯片的配置中開啟了硬件處理校驗(yàn)和,則對(duì)LwIP進(jìn)行配置 *//* CHECKSUM_GEN_IP==0: Generate checksums by hardware for outgoing IP packets.*/#define CHECKSUM_GEN_IP 0/* CHECKSUM_GEN_UDP==0: Generate checksums by hardware for outgoing UDP packets.*/#define CHECKSUM_GEN_UDP 0/* CHECKSUM_GEN_TCP==0: Generate checksums by hardware for outgoing TCP packets.*/#define CHECKSUM_GEN_TCP 0 /* CHECKSUM_CHECK_IP==0: Check checksums by hardware for incoming IP packets.*/#define CHECKSUM_CHECK_IP 0/* CHECKSUM_CHECK_UDP==0: Check checksums by hardware for incoming UDP packets.*/#define CHECKSUM_CHECK_UDP 0/* CHECKSUM_CHECK_TCP==0: Check checksums by hardware for incoming TCP packets.*/#define CHECKSUM_CHECK_TCP 0/* CHECKSUM_CHECK_ICMP==0: Check checksums by hardware for incoming ICMP packets.*/#define CHECKSUM_GEN_ICMP 0 #else/* CHECKSUM_GEN_IP==1: Generate checksums in software for outgoing IP packets.*/#define CHECKSUM_GEN_IP 1/* CHECKSUM_GEN_UDP==1: Generate checksums in software for outgoing UDP packets.*/#define CHECKSUM_GEN_UDP 1/* CHECKSUM_GEN_TCP==1: Generate checksums in software for outgoing TCP packets.*/#define CHECKSUM_GEN_TCP 1/* CHECKSUM_CHECK_IP==1: Check checksums in software for incoming IP packets.*/#define CHECKSUM_CHECK_IP 1/* CHECKSUM_CHECK_UDP==1: Check checksums in software for incoming UDP packets.*/#define CHECKSUM_CHECK_UDP 1/* CHECKSUM_CHECK_TCP==1: Check checksums in software for incoming TCP packets.*/#define CHECKSUM_CHECK_TCP 1/* CHECKSUM_CHECK_ICMP==1: Check checksums by hardware for incoming ICMP packets.*/#define CHECKSUM_GEN_ICMP 1 #endif/*-------------------------------------------------------- Sequential layer options -------------------------------------------------------- */ /*** LWIP_NETCONN==1: Enable Netconn API (require to use api_lib.c)*/ #define LWIP_NETCONN 1/*---------------------------------------------- Socket options ---------------------------------------------- */ /*** LWIP_SOCKET==1: Enable Socket API (require to use sockets.c)*/ #define LWIP_SOCKET 0/*--------------------------------------------- DEBUG options --------------------------------------------- */ #define LWIP_DEBUG 0/*------------------------------------------- OS options ------------------------------------------- */#define TCPIP_THREAD_NAME "TCP/IP" #define TCPIP_THREAD_STACKSIZE 1000 #define TCPIP_MBOX_SIZE 5 #define DEFAULT_UDP_RECVMBOX_SIZE 2000 #define DEFAULT_TCP_RECVMBOX_SIZE 2000 #define DEFAULT_ACCEPTMBOX_SIZE 2000 #define DEFAULT_THREAD_STACKSIZE 500 #define TCPIP_THREAD_PRIO (configMAX_PRIORITIES - 2) #define LWIP_COMPAT_MUTEX 1#define LWIP_COMPAT_MUTEX_ALLOWED 1#endif /* __LWIPOPTS_H__ *//************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/第二步:與 CPU 或編譯器相關(guān)接口
??與 CPU 或編譯器相關(guān)的源文件,主要包括數(shù)據(jù)長度,字的高低位順序,編譯器對(duì)c語言中struct結(jié)構(gòu)字節(jié)對(duì)齊問題(c語言中struct結(jié)構(gòu)是四字節(jié)對(duì)對(duì)齊的,但lwip中是根據(jù)struct結(jié)構(gòu)中不同數(shù)據(jù)的長度來讀取數(shù)據(jù)的)。主要有以下文件組成(僅僅是個(gè)例子,實(shí)際不一定有全部文件):
??上圖例子中,處理了針對(duì)目前主流的編譯器(VC、ARMCC、IAR等)的各種宏定義,比較全面,文件也比較多。自己在移植時(shí)文件個(gè)數(shù)/名字可能不同。但是最基本的cc.h、perf.h、sys_arch.h是 LwIP 規(guī)定的文件名,必須一致。
第三步:與操作系統(tǒng)的接口源文件
??LwIP為了適應(yīng)不同的操作系統(tǒng),在代碼中沒有使用和某一個(gè)操作系統(tǒng)相關(guān)的系統(tǒng)調(diào)用和數(shù)據(jù)結(jié)構(gòu)。而是在LwIP和操作系統(tǒng)之間增加了一個(gè)操作系統(tǒng)封裝層。操作系統(tǒng)封裝層為操作系統(tǒng)服務(wù)(定時(shí),進(jìn)程同步,消息傳遞)提供了一個(gè)統(tǒng)一的接口。在Lwip中進(jìn)程同步使用semaphone和消息傳遞采用"mbox"。
??因此,使用者必須根據(jù)自己使用的系統(tǒng),完成sys_arch.h 和sys_arch.c這兩個(gè)文件。關(guān)于這個(gè)文件,在LwIP源碼的doc/sys_arch.txt有詳細(xì)的說明。當(dāng)然,如果在裸機(jī)下使用,則不需要該步驟。
注意:
無論是否有系統(tǒng),其中有個(gè)函數(shù)sys_now都必須被提供,具體可參見 LwIP 之 sys_arch
第四步:與底層網(wǎng)卡驅(qū)動(dòng)的接口源文件
??該部分主要是針對(duì)文件ethernetif.c和slipif.c。移植時(shí),需要根據(jù)自己使用的網(wǎng)卡或者串行鏈路修改/實(shí)現(xiàn)內(nèi)部的各函數(shù)。
ethernetif.c
??LwIP的網(wǎng)絡(luò)驅(qū)動(dòng)有一定的模型,/src/netif/ethernetif.c文件即為驅(qū)動(dòng)的模版,用戶為自己的網(wǎng)絡(luò)設(shè)備實(shí)現(xiàn)驅(qū)動(dòng)時(shí)應(yīng)參照此模塊。在 LwIP中可以有多個(gè)網(wǎng)絡(luò)接口,每個(gè)網(wǎng)絡(luò)接口都對(duì)應(yīng)了一個(gè)struct netif。這個(gè)netif包含了相應(yīng)網(wǎng)絡(luò)接口的屬性、收發(fā)函數(shù)。
slipif.c
??和ethernetif.c很相似,SLIP即串行鏈路IP,它提供了一種在串行鏈路上傳送IP 數(shù)據(jù)包的函數(shù)定義。SLIP 協(xié)議比較簡單,它只是定義了一系列的字符,以實(shí)現(xiàn)對(duì)鏈路上的IP數(shù)據(jù)包封裝和發(fā)送,除此之外,它不提供任何尋址、錯(cuò)誤檢測、包類型識(shí)別機(jī)制,因此相關(guān)驅(qū)動(dòng)程序的實(shí)現(xiàn)也比較簡單。它需要一個(gè)sio(串行I / O)模塊才能工作。 移植者需要根據(jù)自己使用的串行線路特性(如串口)來實(shí)現(xiàn)以下這些函數(shù)(通常在另一個(gè)文件中,不要直接修改該文件):
- u32_t sio_read(sio_fd_t fd, u8_t *data, u32_t len);
- u32_t sio_tryread(sio_fd_t fd, u8_t *data, u32_t len);
- void sio_send(u8_t c, sio_fd_t fd);
- sio_fd_t sio_open(u8_t devnum);
通常,在移植時(shí)不會(huì)使用該文件。
第五步:初始化LwIP
??在處理完以上需要移植的個(gè)文件后,接下來就可以在自己的項(xiàng)目中建立初始化函數(shù)來對(duì)LwIP進(jìn)行初始化了,以下為使用系統(tǒng)時(shí)的一個(gè)初始化例子。
void LwIP_Init(void) {ip_addr_t ipaddr;ip_addr_t netmask;ip_addr_t gw; #ifdef USE_DHCPuint8_t iptab[4] = {0};uint8_t iptxt[20]; #endif/* 創(chuàng)建tcp_ip線程,初始化LwIP的各部分 */tcpip_init(NULL, NULL);/* 設(shè)置IP地址等信息 */ #ifdef USE_DHCPipaddr.addr = 0;netmask.addr = 0;gw.addr = 0; #elseIP4_ADDR(&ipaddr, IP_ADDR0, IP_ADDR1, IP_ADDR2, IP_ADDR3);IP4_ADDR(&netmask, NETMASK_ADDR0, NETMASK_ADDR1, NETMASK_ADDR2, NETMASK_ADDR3);IP4_ADDR(&gw, GW_ADDR0, GW_ADDR1, GW_ADDR2, GW_ADDR3); #endif/* 以下就是添加網(wǎng)絡(luò)接口了 *//* - netif_add(struct netif *netif, struct ip_addr *ipaddr,struct ip_addr *netmask, struct ip_addr *gw,void *state, err_t (* init)(struct netif *netif),err_t (* input)(struct pbuf *p, struct netif *netif))Adds your network interface to the netif_list. Allocate a structnetif and pass a pointer to this structure as the first argument.Give pointers to cleared ip_addr structures when using DHCP,or fill them with sane numbers otherwise. The state pointer may be NULL.The init function pointer must point to a initialization function foryour ethernet netif interface. The following code illustrates it's use.*/netif_add(&xnetif, &ipaddr, &netmask, &gw, NULL, ðernetif_init, &tcpip_input);/* Registers the default network interface.*/netif_set_default(&xnetif);if (EthStatus == (ETH_INIT_FLAG | ETH_LINK_FLAG)){/* Set Ethernet link flag */xnetif.flags |= NETIF_FLAG_LINK_UP;/* When the netif is fully configured this function must be called.*/netif_set_up(&xnetif);#ifdef USE_DHCPDHCP_state = DHCP_START;#endif /* USE_DHCP */}else{/* When the netif link is down this function must be called.*/netif_set_down(&xnetif);#ifdef USE_DHCPDHCP_state = DHCP_LINK_DOWN;#endif /* USE_DHCP */}/* Set the link callback function, this function is called on change of link status*/netif_set_link_callback(&xnetif, ETH_link_callback); }??首先看以下tcpip_init函數(shù),該函數(shù)負(fù)責(zé)建立tcp_ip線程,并且初始化LwIP的各部分功能,具體看以下圖片。
??設(shè)置IP地址這塊沒啥可說的,接下來就是添加網(wǎng)絡(luò)接口,具體參見 LwIP 之 ethernetif.c。
總結(jié)
以上是生活随笔為你收集整理的LwIP 之一 源码目录文件详解及移植说明的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: STM32 之五 Core Couple
- 下一篇: Visual Studio 2017中的