LwIP 之 原始碼目錄檔案詳解及移植說明
原始碼目錄檔案
目前,網路上多數文章所使用的LwIP版本為1.4.1。最新版本為2.0.3。從1.4.1到2.0.3(貌似從2.0.0開始),LwIP的原始碼有了一定的變化,甚至於原始碼的檔案結構也不一樣,內部的一些實現原始檔也被更新和替換了。其原始碼目錄結構如下所示(對於簡單的檔案以註釋的形式給出,核心原始碼下文會詳細說明):
LWIP-2.0.3
│ CHANGELOG // 版本更新記錄,從中可以看到LwIP不同版本的變化
│ COPYING
│ FILES // 其中說明了其所在目錄下的各目錄或檔案的用途。在不同的目錄下會有不同的該檔案
│ README
│ UPGRADING // 版本升級後可能出現不相容,該文件記錄了從老版本升級需要修改的地方。對於升級自己使用的LwIP版本時很有用處。
├─doc
│ │ contrib.txt // LwIP作為開源軟體,如果想要為其做貢獻,則需要遵循一定的準則,例如:提交程式碼的風格、報告Bug等。該文件給出了詳細的貢獻準則。
│ │ doxygen_docs.zip // 用doxygen生成的LwIP的配套文件
│ │ FILES // 其中說明了該目錄下的每個檔案的用途
│ │ mdns.txt // MDNS的說明文件
│ │ mqtt_client.txt
│ │ NO_SYS_SampleCode.c
│ │ ppp.txt // lwIP的PPP介面文件
│ │ rawapi.txt // 告訴讀者怎樣使用協議棧的Raw/Callback API進行程式設計
│ │ savannah.txt // 說明了如何獲取當前的開發原始碼
│ │ sys_arch.txt // 在有作業系統的移植的時候會被使用到,包含了移植說明,規定了移植者需要實現的函式、巨集定義等,後面有詳細說明。
│ └─doxygen // doxygen指令碼,主要用來維護LwIP的配套文件。對於使用LwIP來說用不到
│ │ generate.bat
│ │ generate.sh
│ │ lwip.Doxyfile
│ │ main_page.h
│ └─output
│ index.html
├─src /* 原始碼檔案部分下面獨立詳細說明 */
│ │ Filelists.mk
│ │ FILES // 主要記錄了該目錄下每個檔案、目錄的用途
│ ├─api
│ │ api_lib.c
│ │ api_msg.c
│ │ err.c
│ │ netbuf.c
│ │ netdb.c
│ │ netifapi.c
│ │ sockets.c
│ │ tcpip.c
│ ├─apps
│ │ ├─httpd
│ │ │ │ fs.c
│ │ │ │ fsdata.c
│ │ │ │ fsdata.h
│ │ │ │ httpd.c
│ │ │ │ httpd_structs.h
│ │ │ ├─fs
│ │ │ │ │ 404.html
│ │ │ │ │ index.html
│ │ │ │ └─img
│ │ │ │ sics.gif
│ │ │ └─makefsdata
│ │ │ makefsdata
│ │ │ makefsdata.c
│ │ │ readme.txt
│ │ ├─lwiperf
│ │ │ lwiperf.c
│ │ ├─mdns
│ │ │ mdns.c
│ │ ├─mqtt
│ │ │ mqtt.c
│ │ ├─netbiosns
│ │ │ netbiosns.c
│ │ ├─snmp
│ │ │ snmpv3.c
│ │ │ snmpv3_dummy.c
│ │ │ snmpv3_mbedtls.c
│ │ │ snmpv3_priv.h
│ │ │ snmp_asn1.c
│ │ │ snmp_asn1.h
│ │ │ snmp_core.c
│ │ │ snmp_core_priv.h
│ │ │ snmp_mib2.c
│ │ │ snmp_mib2_icmp.c
│ │ │ snmp_mib2_interfaces.c
│ │ │ snmp_mib2_ip.c
│ │ │ snmp_mib2_snmp.c
│ │ │ snmp_mib2_system.c
│ │ │ snmp_mib2_tcp.c
│ │ │ snmp_mib2_udp.c
│ │ │ snmp_msg.c
│ │ │ snmp_msg.h
│ │ │ snmp_netconn.c
│ │ │ snmp_pbuf_stream.c
│ │ │ snmp_pbuf_stream.h
│ │ │ snmp_raw.c
│ │ │ snmp_scalar.c
│ │ │ snmp_table.c
│ │ │ snmp_threadsync.c
│ │ │ snmp_traps.c
│ │ ├─sntp
│ │ │ sntp.c
│ │ └─tftp
│ │ tftp_server.c
│ ├─core
│ │ │ def.c
│ │ │ dns.c
│ │ │ inet_chksum.c
│ │ │ init.c
│ │ │ ip.c
│ │ │ mem.c
│ │ │ memp.c
│ │ │ netif.c
│ │ │ pbuf.c
│ │ │ raw.c
│ │ │ stats.c
│ │ │ sys.c
│ │ │ tcp.c
│ │ │ tcp_in.c
│ │ │ tcp_out.c
│ │ │ timeouts.c
│ │ │ udp.c
│ │ ├─ipv4
│ │ │ autoip.c
│ │ │ dhcp.c
│ │ │ etharp.c
│ │ │ icmp.c
│ │ │ igmp.c
│ │ │ ip4.c
│ │ │ ip4_addr.c
│ │ │ ip4_frag.c
│ │ └─ipv6
│ │ dhcp6.c
│ │ ethip6.c
│ │ icmp6.c
│ │ inet6.c
│ │ ip6.c
│ │ ip6_addr.c
│ │ ip6_frag.c
│ │ mld6.c
│ │ nd6.c
│ ├─include
│ │ ├─lwip
│ │ │ │ api.h
│ │ │ │ arch.h
│ │ │ │ autoip.h
│ │ │ │ debug.h
│ │ │ │ def.h
│ │ │ │ dhcp.h
│ │ │ │ dhcp6.h
│ │ │ │ dns.h
│ │ │ │ err.h
│ │ │ │ errno.h
│ │ │ │ etharp.h
│ │ │ │ ethip6.h
│ │ │ │ icmp.h
│ │ │ │ icmp6.h
│ │ │ │ igmp.h
│ │ │ │ inet.h
│ │ │ │ inet_chksum.h
│ │ │ │ init.h
│ │ │ │ ip.h
│ │ │ │ ip4.h
│ │ │ │ ip4_addr.h
│ │ │ │ ip4_frag.h
│ │ │ │ ip6.h
│ │ │ │ ip6_addr.h
│ │ │ │ ip6_frag.h
│ │ │ │ ip_addr.h
│ │ │ │ mem.h
│ │ │ │ memp.h
│ │ │ │ mld6.h
│ │ │ │ nd6.h
│ │ │ │ netbuf.h
│ │ │ │ netdb.h
│ │ │ │ netif.h
│ │ │ │ netifapi.h
│ │ │ │ opt.h
│ │ │ │ pbuf.h
│ │ │ │ raw.h
│ │ │ │ sio.h
│ │ │ │ snmp.h
│ │ │ │ sockets.h
│ │ │ │ stats.h
│ │ │ │ sys.h
│ │ │ │ tcp.h
│ │ │ │ tcpip.h
│ │ │ │ timeouts.h
│ │ │ │ udp.h
│ │ │ ├─apps
│ │ │ │ FILES
│ │ │ │ fs.h
│ │ │ │ httpd.h
│ │ │ │ httpd_opts.h
│ │ │ │ lwiperf.h
│ │ │ │ mdns.h
│ │ │ │ mdns_opts.h
│ │ │ │ mdns_priv.h
│ │ │ │ mqtt.h
│ │ │ │ mqtt_opts.h
│ │ │ │ netbiosns.h
│ │ │ │ netbiosns_opts.h
│ │ │ │ snmp.h
│ │ │ │ snmpv3.h
│ │ │ │ snmp_core.h
│ │ │ │ snmp_mib2.h
│ │ │ │ snmp_opts.h
│ │ │ │ snmp_scalar.h
│ │ │ │ snmp_table.h
│ │ │ │ snmp_threadsync.h
│ │ │ │ sntp.h
│ │ │ │ sntp_opts.h
│ │ │ │ tftp_opts.h
│ │ │ │ tftp_server.h
│ │ │ ├─priv
│ │ │ │ api_msg.h
│ │ │ │ memp_priv.h
│ │ │ │ memp_std.h
│ │ │ │ nd6_priv.h
│ │ │ │ tcpip_priv.h
│ │ │ │ tcp_priv.h
│ │ │ └─prot
│ │ │ autoip.h
│ │ │ dhcp.h
│ │ │ dns.h
│ │ │ etharp.h
│ │ │ ethernet.h
│ │ │ icmp.h
│ │ │ icmp6.h
│ │ │ igmp.h
│ │ │ ip.h
│ │ │ ip4.h
│ │ │ ip6.h
│ │ │ mld6.h
│ │ │ nd6.h
│ │ │ tcp.h
│ │ │ udp.h
│ │ ├─netif
│ │ │ │ etharp.h
│ │ │ │ ethernet.h
│ │ │ │ lowpan6.h
│ │ │ │ lowpan6_opts.h
│ │ │ │ slipif.h
│ │ │ └─ppp
│ │ │ │ ccp.h
│ │ │ │ chap-md5.h
│ │ │ │ chap-new.h
│ │ │ │ chap_ms.h
│ │ │ │ eap.h
│ │ │ │ ecp.h
│ │ │ │ eui64.h
│ │ │ │ fsm.h
│ │ │ │ ipcp.h
│ │ │ │ ipv6cp.h
│ │ │ │ lcp.h
│ │ │ │ magic.h
│ │ │ │ mppe.h
│ │ │ │ ppp.h
│ │ │ │ pppapi.h
│ │ │ │ pppcrypt.h
│ │ │ │ pppdebug.h
│ │ │ │ pppoe.h
│ │ │ │ pppol2tp.h
│ │ │ │ pppos.h
│ │ │ │ ppp_impl.h
│ │ │ │ ppp_opts.h
│ │ │ │ upap.h
│ │ │ │ vj.h
│ │ │ └─polarssl
│ │ │ arc4.h
│ │ │ des.h
│ │ │ md4.h
│ │ │ md5.h
│ │ │ sha1.h
│ │ └─posix
│ │ │ errno.h
│ │ │ netdb.h
│ │ └─sys
│ │ socket.h
│ └─netif
│ │ ethernet.c
│ │ ethernetif.c
│ │ FILES
│ │ lowpan6.c
│ │ slipif.c
│ └─ppp
│ │ auth.c
│ │ ccp.c
│ │ chap-md5.c
│ │ chap-new.c
│ │ chap_ms.c
│ │ demand.c
│ │ eap.c
│ │ ecp.c
│ │ eui64.c
│ │ fsm.c
│ │ ipcp.c
│ │ ipv6cp.c
│ │ lcp.c
│ │ magic.c
│ │ mppe.c
│ │ multilink.c
│ │ ppp.c
│ │ pppapi.c
│ │ pppcrypt.c
│ │ PPPD_FOLLOWUP
│ │ pppoe.c
│ │ pppol2tp.c
│ │ pppos.c
│ │ upap.c
│ │ utils.c
│ │ vj.c
│ └─polarssl
│ arc4.c
│ des.c
│ md4.c
│ md5.c
│ README
│ sha1.c
└─test // 一些協議棧核心測試程式.在實際使用時一般用不到!可直接刪除。
├─fuzz
│ │ config.h
│ │ fuzz.c
│ │ lwipopts.h
│ │ Makefile
│ │ output_to_pcap.sh
│ │ README
│ └─inputs
│ ├─arp
│ │ arp_req.bin
│ ├─icmp
│ │ icmp_ping.bin
│ ├─ipv6
│ │ neighbor_solicitation.bin
│ │ router_adv.bin
│ ├─tcp
│ │ tcp_syn.bin
│ └─udp
│ udp_port_5000.bin
└─unit
│ lwipopts.h
│ lwip_check.h
│ lwip_unittests.c
├─core
│ test_mem.c
│ test_mem.h
│ test_pbuf.c
│ test_pbuf.h
├─dhcp
│ test_dhcp.c
│ test_dhcp.h
├─etharp
│ test_etharp.c
│ test_etharp.h
├─ip4
│ test_ip4.c
│ test_ip4.h
├─mdns
│ test_mdns.c
│ test_mdns.h
├─tcp
│ tcp_helper.c
│ tcp_helper.h
│ test_tcp.c
│ test_tcp.h
│ test_tcp_oos.c
│ test_tcp_oos.h
└─udp
test_udp.c
test_udp.h
在LwIP的原始碼包中,一共包含三個目錄:doc
、src
、test
。分別對應:原始碼的文件、原始碼、測試程式碼。最新版的原始碼與早期原始碼在某些目錄檔案是有區別的。以下主要說明src
即:原始碼部分。
api目錄
LwIP提供了兩種型別的API: Callback-style APIs和Sequential-style APIs 。其中,Callback-style APIs即為LwIP最底層的介面,被稱為Raw API或者Native API;而Sequential-style APIs主要是對底層介面進行了封裝,主要包含:Netconn API、NETIF API和Socket API。在實際使用中,使用者可以任選一種API來使用。
api目錄下主要包含對底層API(raw API)封裝後的高階API的程式碼。 如果直接使用底層的的Raw API,則不需要該目錄下的檔案。而封裝後的高級別的這兩種API實現的原理都是通過引進郵箱和訊號量等通訊與同步機制,來實現對核心中***Raw API(native API)***函式的封裝和呼叫。要使用這兩種型別的API,需要底層作業系統的支援。
- Raw API:(有時稱為native API)是一個設計用於在沒有作業系統時,實現零拷貝傳送和接收的事件驅動的API。 這個API也被核心堆疊用於各種協議之間的互動。 這是在沒有作業系統的情況下,執行lwIP時唯一可用的API。
因為Callback/Raw API是協議棧提供的三種程式設計介面中最複雜的一種, 它通過直接與協議棧內 核函式互動以實現程式設計,所以整個過程比較複雜。原始碼的doc目錄
下有一個專門的文件:rawapi.txt說明了具體如何使用Raw API。 - **Netconn API:**為普通的、順序的程式提供了使用lwIP棧的方法。 執行緒安全,僅從非TCPIP執行緒呼叫。 基於網路緩衝區(包含資料包緩衝區(PBUF))的TX / RX處理,以避免複製資料。這與BSD Socket API非常相似。 執行模型基於 開啟-讀取-寫入-關閉 範例。 由於TCP / IP堆疊本質上是事件,所以TCP / IP程式碼和應用程式必須駐留在不同的執行上下文(執行緒)中。
- **Socket API:**它是建立在Netconn API之上的。其主要是由於BSD Socket API是網路通訊的一個實現。執行緒安全,僅從非TCPIP執行緒呼叫。BSD Socket API已經是網路套接字的事實上的抽象標準。目前,所有主流作業系統均實現了BSD Socket API。出於此,LwIP也提供了一套BSD Socket API。但是,標準 socket 庫中的部分函式仍無法直接通過封裝 Netconn API 來實現,因此 LwIP 中提供的socket 函式並不完整,使用者最好不要使用它進行實際應用程式開發。對應檔案為
posix/sys/socket.h
。
以下為每個檔案的具體說明:
- api_lib.c: 包含 對外提供的 sequential API 函式的實現。函式名均以
netconn_
開頭。主要分為三組API:同時可用於TCP和UDP的API、只能用於TCP的API、只能用於UDP的API。 - api_msg.c: 包含sequential API內部自己呼叫的函式的實現。主要包含API訊息的封裝和處理函式
- err.c: 錯誤管理模組
- netbuf.c: 包含了上層資料包管理函式的實現。應用程式描述待發送資料和已接收資料的基本結構。該結構只是對核心 pbuf 的簡單封裝,避免了資料的拷貝。緩衝區不能在多個執行緒之間共享。
- netdb.c: 包含與主機名字轉換相關的函式,主要在 socket 中被使用到
- netifapi.c: 包含了上層網路介面管理函式的實現
- sockets.c: 包含了 Socket API 函式的實現
- tcpip.c: 包含了上層 API 與協議棧核心互動的函式,它是整個上層 API 功能得以實現的一個樞紐,其實現的功能可以簡單理解為:從 API 函式處接收訊息,然後將訊息遞交給核心函式,核心函式根據訊息做出相應的處理。
apps目錄
使用lwIP低階raw API編寫的高層應用程式。
core目錄
TCP / IP協議棧的核心部分。主要包含協議實現、記憶體和緩衝區管理以及底層raw API的實現。它包含了IP、ICMP、IGMP、TCP、UDP 等核 心協議以及建立在它們基礎上的DNS、DHCP、SNMP 等上層應用協議。核心原始碼可以單獨執行,且不需要作業系統的支援。即:直接使用raw API程式設計。
- ipv4目錄: 包含了IPv4 標準中與IP層資料包處理相關的所有程式碼
- autoip.c: 這是lwIP TCP / IP協議棧的AutoIP實現。 它旨在符合RFC 3927。
- **dhcp.c:**實現了DHCP 客戶端的所有程式碼,DHCP 稱為動態主機配置協議,DHCP 可以使計算機使用者不必為主機的IP 地址的分配問題而煩惱。DHCP 也是一個上層應 用程式, 通常DHCP客戶端通過使用UDP提供的功能來實現與DHCP伺服器的通訊, 從DHCP 伺服器處獲得一個有效的IP 地址
- etharp.c: 包含了ARP 協議實現的相關函式,ARP 協議是乙太網通訊中的重要部分, 主要用來實現主機乙太網實體地址到IP 地址的對映。這點是非常必要的,乙太網中底層資料包的傳送是基於網絡卡實體地址的,而不是主機的IP地址。 通過ARP協議, 主機可以傳送請求, 得到鄰居節點的IP 地址與實體地址等資訊,為乙太網資料包互動提供保證。
- icmp.c: 包含了ICMP 協議實現的相關函式,ICMP 協議為IP 資料包傳遞過程中的差錯 報告、差錯糾正以及目的地址可達性測試提供了支援,常見的Ping 命令就屬於ICMP 應用的 一種
- **igmp.c:**包含了網路組管理協議IGMP 的實現,IGMP 為網路中的多播資料傳輸提供了 支援,主機加入某個多播組後,可以接收到該組的UDP 多播資料
- ip4_addr.c: 實現了幾個比較簡單 的IP 地址處理函式,如判斷一個IP 地址是否為廣播地址的函式,以及32 位IP 地址與點分十 進位制地址間的轉換函式等
- ip4_frag.c: 提供了IP 層資料包分片與重組相關的函式的實現。
- Ip4.c: 包含了IPv4 協議實現的相關函式,如資料包的接收、遞交、傳送等
- **ipv6目錄:**包含了IPv6 標準中和IP層資料包處理相關的所有程式碼
- mld6.c: IPv6的多播偵聽器發現。旨在符合RFC 2710。
- nd6.c: IPv6的鄰居發現和無狀態地址自動配置。 旨在符合RFC 4861(鄰居發現)和RFC 4862(地址自動配置)。
- Iinet6.c: 目前沒有啥實際內容。
- 其他檔案與IPv4目錄下的功能相同,不過是在IPv6版上的實現
- **def.c:**檔案包含了IP 層使用到的一些功能函式的定義,如IP 地址的轉換、網路位元組序與 主機位元組序轉換等
- **dns.c:**實現了DNS 客戶端的所有程式碼,DNS 稱為域名系統,通常使用者要訪問某個外 部的主機時,可能只知道該主機的名字,而不知道該主機的IP 地址。常理也是這樣,使用者可 以簡單地記得某個主機的名字,如www.baidu.com,卻很難記得這個主機的IP 地址。通過使 用DNS,可以解決這個問題,通過訪問DNS 伺服器,我們可以得到與主機名對應的IP 地址, 這為使用者的使用帶來了方便。值得指出的是,DNS 也是一個上層應用程式,它通常基於UDP 來傳輸資料。
- inet_chksum.c: 這些是校驗和演算法的一些參考實現,其目標是簡單,正確和完全便攜。 Checksumming是您希望為您的平臺優化的第一件事。 如果您建立自己的版本,請將其連結到cc.h中
- init.c: 主要包含了一個與LwIP 協議棧初始化密切相關的函式,以及一些 協議棧配置資訊的檢查與輸出
- ip.c: IPv4和IPv6共用的程式碼。
- **mem.c:**包含了協議棧記憶體堆管理函式的實現。
(1)LwIP 支援的動態記憶體管理機制主要有三種:C庫自帶記憶體分配策略(malloc/free/realloc)、動態記憶體堆(Heap)分配策略、動態記憶體池(pool)分配策略。
(2)此檔案即LwIP實現的動態記憶體堆(Heap)分配策略。
(3)預設不使用C庫自帶記憶體分配策略。 在lwipopts.h
使用巨集值MEM_LIBC_MALLOC = 1
選擇使用C庫自帶記憶體分配策略
- **memp.c:**包含了協議棧記憶體池管理函式的實現。
(1)LwIP 支援的動態記憶體管理機制主要有三種:C庫自帶記憶體分配策略(malloc/free/realloc)、動態記憶體堆(HEAP)分配策略、動態記憶體池(POOL)分配策略。
(2)此檔案即LwIP實現的動態記憶體池(POOL)分配策略
(3)預設不使用動態記憶體堆(HEAP)分配策略。 在lwipopts.h
使用巨集值MEMP_MEM_MALLOC = 1
選擇使用動態記憶體堆(HEAP)分配策略
- **netif.c:**包含了協議棧網路介面管理的相關函式,協議棧支援多個網路介面,例如乙太網介面、SLIP介面等,協議棧內部對每個介面用一個對應的netif資料結構進行描述,並通過使用
netif.c
中的函式進行統一管理。同時,netif.c
還包含了環回介面管理和資料處理的相關函式,使用環回介面可以實現同一主機上兩個應用程式之間的資料交換。 - pbuf.c: 包含了協議棧核心使用的資料包管理函式,資料包pbuf管理的實現是整個協議棧中很有特色的地方,採用特殊的資料包pbuf 結構,可以避免資料在各個層次之間遞交時的拷貝,這既提高了資料遞交效率,也節省了記憶體空間。
- raw.c: 為應用層提供了一種直接和IP 資料包互動的方式,這類似於Socket 程式設計中原始套接字的概念,它同TCP、UDP 處於同一等級,享受IP 層提供的服務。使用原始套接字, 可以直接讀取IP 層接收到的資料包,例如ICMP 包、UDP 包等,也可以自行構造ping包等, 為使用者的程式編寫提供了很大的方便。這是LwIP最底層的API部分。使用這一級別的api進行程式設計需要對TCP/IP相當瞭解。
- stats.c: 包含了協議棧內部資料統計與顯示的相關函式,如記憶體使用狀況、郵箱、訊號量等資訊。
- sys.c: 實現了一個簡單的函式
void sys_msleep(u32_t ms);
,它藉助作業系統模擬層的訊號量機制完成睡眠一定時間的功能,該函式主要在PPP 中使用。前面曾提到過,若需要使用協議棧的Sequential API和Socket API,則必須使用底層作業系統提供的郵箱與訊號量機制,這時核心要求移植者提供一個稱為sys_arch.c
的作業系統模擬層檔案,該檔案主要完成對作業系統中郵 箱與訊號量函式的封裝。需要注意,只有在提供檔案sys_arch.c
的基礎上,檔案`sys.c 才有效,換句話說,在無作業系統環境下執行LwIP 時,sys.c檔案都不會被編譯。 - tcp_in.c: 包含了TCP 協議中資料接收、處理相關的函式,最重要的TCP 狀態機函式也在這個檔案中
- tcp_out.c: 包含了TCP 中資料傳送相關的函式,例如資料包傳送函式、超時重傳函式等。
- tcp: 包含了對TCP 控制塊操作的函式,也包括了TCP 定時處理函式
- timeouts.c: 統一完成了對核心各個協議定時事件處理函式的封裝,同時對各個註冊的定時事件進行處理。在有無作業系統模擬層的環境下,
timeouts.c
採用不同的方法來實現定時:在無作業系統模擬層時,timeouts.c
使用系統時鐘函式sys_now
(不同的平臺的都需要移植該函式)來獲得當前系統時間,從而可以判斷出各個事件是否超時;在有作業系統模擬層時,timers.c 實現了對作業系統模擬層郵箱等待函式的再次封裝,得到一個具有協議棧特色的郵箱操作函式。 所謂特色,就是在郵箱等待函式中加入一種機制,在郵箱上等待訊息的同時,可以同時實現協議棧中各個定時事件的正確處理。
在2.0.0之前的版本中,該檔名為
timer.c
。新的timeouts.c
對原來的內容進行了一定的封裝,簡化。
- udp.c: 包含了實現UDP 協議的相關函式,包括UDP 控制塊管理、UDP 資料包傳送函式、UDP 資料包接收函式等
include目錄
LwIP使用的各種標頭檔案。與各原始碼目錄相對應。
- netif資料夾: 主要是針對netif目錄中原始檔的各種標頭檔案。
- posix資料夾: 主要是針對POSIX標準,進行的封裝,裡面的檔案非常簡單,基本都是對外部的引用。
- lwip資料夾: LwIP的各種標頭檔案,其中需要注意的有
opt.h
檔案,它包含了所有LwIP核心引數的預設配置值;還有init.h
檔案,它包含了與當前 LwIP 原始碼資訊相關的巨集定義,如協議版本號、是否為官方版等。 - apps資料夾: 主要是針對原始碼中 app目錄的各標頭檔案。
- priv資料夾: LwIP內部使用的標頭檔案,禁止外部使用
- prot資料夾: 這個資料夾中主要是針對TCP/IP規約的各種定義。與netif資料夾中同名檔案區別在於netif資料夾中的檔案均為對外提供的API,本目錄才是對規約層各種結構的定義。
netif目錄
該目錄下主要包含通用網路介面裝置驅動程式。實現最底層的相關協議,該部分的多數原始碼基本已經到了最底層和硬體直接打交道了。
- **ppp資料夾:**包含了PPP 協議實現的原始碼。PPP 協議即點對點協議,它提供了 一種在點對點線路上傳輸多協議資料包的標準格式,PPP 協議為鏈路的建立、控制與認證提供 了標準。起初PPP 主要用來替代SLIP 這種簡單的序列鏈路資料傳輸協議,但是由於其完整的認證機制,後來在乙太網上也引入了PPP 機制,即PPPoE,它已成為近年來小區寬頻撥號上網的主要方式。使用PPPoE,為使用者的上網計費、配置、接入等提供了方便。LwIP 提供了對PPPoE 的支援,在ppp資料夾下的PPPoE.c檔案中有相關的函式實現。
- **ethernet.c:**乙太網介面的共享程式碼。目前就兩個函式:用來從網絡卡接收乙太網資料包的函式和在網絡卡上傳送乙太網資料包的函式。
- **ethernetif.c :**包含了與乙太網網絡卡密切相關的初始化、傳送、接收等函式的實現。注意:這個資料夾中的函式並不能使用, 它們都是一個框架性的結構, 移植者需要根據自己使用的網 卡特性來完成這些函式。
- **slipif.c:**和ethernetif.c很相似,SLIP即序列鏈路IP,它提供了一種在序列鏈路上傳送IP 資料包的函式定義。SLIP 協議比較簡單,它只是定義了一系列的字元,以實現對鏈路上的IP資料包封裝和傳送,除此之外,它不提供任何定址、錯誤檢測、包型別識別機制,因此相關驅動程式的實現也比較簡單。它需要一個sio(序列I / O)模組才能工作。移植者需要根據自己使用的序列線路特性(如串列埠)來實現以下這些函式(通常在另一個檔案中,不要直接修改該檔案):
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協議的實現檔案。6LoWPAN是一種基於IPv6的低速無線個域網標準,即IPv6 over IEEE 802.15.4。
移植說明
lwIP可以用於兩種基本模式:主迴圈模式(通過配置NO_SYS == 1,在目標系統上執行沒有OS / RTOS)或OS模式(TCPIP執行緒,目標系統上有一個作業系統)。在兩種模式下的移植是有區別還是很大的!
- Mainloop Mode: 即裸機下的死迴圈模式。在主迴圈模式下,只能使用回撥式API(Raw API)。需要確保確保在lwIP中一次只有一個執行環境。
- OS Mode: 此時,使用者需要提供一個名為
sys_arch.c/h
的檔案。因為,在系統模式下,LwIP需要系統的訊號量、郵箱佇列等。必須在此檔案中進行實現。
綜合以上兩種模式,LwIP的移植主要分為四部分:
- LwIP的基本配置
- 與CPU或編譯器相關原始檔
- 與作業系統的介面原始檔(僅在系統模式下使用)
- 與底層網絡卡驅動的介面原始檔。
- 初始化LwIP。
針對嵌入式的移植時,無論哪種模式,都需要首先需要處理硬體資源,例如在STM32Fx系列晶片移植時,需要提供檔案:
stm32f4x7_eth_bsp.c/h
,已處理使用的硬體資源。當然,如果是在Windows上做測試使用,則沒有這一步。
第一步:LwIP的基本配置
LwIP的配置主要通過檔案lwipopts.h
實現,移植時需要根據自己的需要,進行相關的配置,然後在自己的專案中新增該檔案。一個在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 which
lwIP is compiled. 4 byte alignment -> define MEM_ALIGNMENT to 4, 2
byte 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 application
sends a lot of data out of ROM (or other static memory), this
should be set high. */
#define MEMP_NUM_PBUF 100
/* MEMP_NUM_UDP_PCB: the number of UDP protocol control blocks. One
per active UDP "connection". */
#define MEMP_NUM_UDP_PCB 6
/* MEMP_NUM_TCP_PCB: the number of simulatenously active TCP
connections. */
#define MEMP_NUM_TCP_PCB 10
/* MEMP_NUM_TCP_PCB_LISTEN: the number of listening TCP
connections. */
#define MEMP_NUM_TCP_PCB_LISTEN 5
/* MEMP_NUM_TCP_SEG: the number of simultaneously queued TCP
segments. */
#define MEMP_NUM_TCP_SEG 20
/* MEMP_NUM_SYS_TIMEOUT: the number of simulateously active
timeouts. */
#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 of
order. 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 least
as 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 of
interfaces. DHCP is not implemented in lwIP 0.5.1, however, so
turning 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.
*/
/*
注意:校驗和可以選擇 由硬體來處理。如果硬體支援該功能,則載入其配置檔案(其中需要定義 CHECKSUM_BY_HARDWARE )
*/
#include "stm32f4x7_eth_conf.h"
#ifdef CHECKSUM_BY_HARDWARE /* 如果晶片的配置中開啟了硬體處理校驗和,則對LwIP進行配置 */
/* 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或編譯器相關介面
與CPU或編譯器相關的原始檔,主要包括資料長度,字的高低位順序,編譯器對c語言中struct結構位元組對齊問題(c語言中struct結構是四位元組對對齊的,但lwip中是根據struct結構中不同資料的長度來讀取資料的)。主要有以下檔案組成(僅僅是個例子,實際不一定有全部檔案):
上圖例子中,處理了針對目前主流的編譯器(VC、ARMCC、IAR等)的各種巨集定義,比較全面,檔案也比較多。自己在移植時檔案個數/名字可能不同。但是最基本的cc.h、perf.h、sys_arch.h
是LwIP規定的檔名,必須一致。
第三步:與作業系統的介面原始檔
LwIP為了適應不同的作業系統,在程式碼中沒有使用和某一個作業系統相關的系統呼叫和資料結構。而是在LwIP和作業系統之間增加了一個作業系統封裝層。作業系統封裝層為作業系統服務(定時,程序同步,訊息傳遞)提供了一個統一的介面。在Lwip中程序同步使用semaphone和訊息傳遞採用"mbox"。
因此,使用者必須根據自己使用的系統,完成sys_arch.h 和sys_arch.c
這兩個檔案。關於這個檔案,在LwIP原始碼的doc/sys_arch.txt
有詳細的說明。當然,如果在裸機下使用,則不需要該步驟。
注意:
無論是否有系統,其中有個函式sys_now
都必須被提供,具體可參見 LwIP 之 sys_arch
第四步:與底層網絡卡驅動的介面原始檔
該部分主要是針對檔案ethernetif.c
和slipif.c
。移植時,需要根據自己使用的網絡卡或者序列鏈路修改/實現內部的各函式。
ethernetif.c
LwIP的網路驅動有一定的模型,/src/netif/ethernetif.c
檔案即為驅動的模版,使用者為自己的網路裝置實現驅動時應參照此模組。在 LwIP中可以有多個網路介面,每個網路介面都對應了一個struct netif。這個netif包含了相應網路介面的屬性、收發函式。
slipif.c
和ethernetif.c很相似,SLIP即序列鏈路IP,它提供了一種在序列鏈路上傳送IP 資料包的函式定義。SLIP 協議比較簡單,它只是定義了一系列的字元,以實現對鏈路上的IP資料包封裝和傳送,除此之外,它不提供任何定址、錯誤檢測、包型別識別機制,因此相關驅動程式的實現也比較簡單。它需要一個sio(序列I / O)模組才能工作。 移植者需要根據自己使用的序列線路特性(如串列埠)來實現以下這些函式(通常在另一個檔案中,不要直接修改該檔案):
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);
通常,在移植時不會使用該檔案。
第五步:初始化LwIP
在處理完以上需要移植的個檔案後,接下來就可以在自己的專案中建立初始化函式來對LwIP進行初始化了,以下為使用系統時的一個初始化例子。
void LwIP_Init(void)
{
ip_addr_t ipaddr;
ip_addr_t netmask;
ip_addr_t gw;
#ifdef USE_DHCP
uint8_t iptab[4] = {0};
uint8_t iptxt[20];
#endif
/* 建立tcp_ip執行緒,初始化LwIP的各部分 */
tcpip_init(NULL, NULL);
/* 設定IP地址等資訊 */
#ifdef USE_DHCP
ipaddr.addr = 0;
netmask.addr = 0;
gw.addr = 0;
#else
IP4_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
/* 以下就是新增網路介面了 */
/* - 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 struct
netif 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 for
your 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_DHCP
DHCP_state = DHCP_START;
#endif /* USE_DHCP */
}
else
{
/* When the netif link is down this function must be called.*/
netif_set_down(&xnetif);
#ifdef USE_DHCP
DHCP_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
函式,該函式負責建立tcp_ip執行緒,並且初始化LwIP的各部分功能,具體看以下圖片。
設定IP地址這塊沒啥可說的,接下來就是新增網路介面,具體參見LwIP 之 ethernetif.c。