S3C2416裸機開發(fā)系列二二_Lwip的移植
Internet實現(xiàn)了全球范圍內計算機網絡的互連,不同主機之間必須遵循相同的網絡協(xié)議才能彼此通信。TCP/IP協(xié)議作為一種網絡互聯(lián)協(xié)議,在Internet中得到了最廣泛的支持以及應用。筆者此處就輕量級TCP/IP協(xié)議Lwip的移植作一個簡單的介紹。
1. TCP/IP協(xié)議概述TCP/IP協(xié)議是Internet上使用最廣泛的通信協(xié)議,其實際上是一個協(xié)議簇,其中TCP協(xié)議和IP協(xié)議是其中兩個最重要的協(xié)議。
TCP/IP協(xié)議采用4層層級結構:應用層、運輸層、網絡層、鏈路層。相應的層級除了能夠向上一層提供服務,還需要使用下一層所提供的服務。
1.1. 鏈路層數(shù)據(jù)鏈路層將網絡層交下來的IP數(shù)據(jù)報組裝成幀,在兩個相鄰結點間的鏈路上透明傳送以幀為單位的數(shù)據(jù),另一結點若收到無差錯的幀,則從收到的幀中提取出IP數(shù)據(jù)報上交上一層,否則丟棄此包。這一層級幀格式通常采用Ethernet V2,有效的MAC幀長度為64~1518字節(jié),通常由網絡適配器實現(xiàn)這一層級。
1.2. 網絡層網絡層把運輸層產生的報文段或用戶數(shù)據(jù)報封裝成分組或包進行傳送,由于網絡層使用無連接的網際協(xié)議IP,因此,分組也稱為IP數(shù)據(jù)報,并且是不可靠的。除了IP協(xié)議,網絡層還需配套使用以下幾個協(xié)議:地址解析協(xié)議ARP、逆地址解析協(xié)議RARP、因特網控制報文協(xié)議ICMP、因特網組管理協(xié)議IGMP。
1.3. 運輸層運輸層向上面的應用層提供通信服務,具有復用和分用的功能。復用就是多個應用層進程可同時使用運輸層的服務,分用則為運輸層可以把收到的數(shù)據(jù)分別交付上面應用層中相應的進程。這一層級有兩個不同的協(xié)議:傳輸控制協(xié)議TCP和用戶數(shù)據(jù)報協(xié)議UDP。TCP提供面向連接的服務,數(shù)據(jù)傳輸單位為報文段,能夠保證提供可靠的交付。其傳輸數(shù)據(jù)前必須先建立連接,結束傳輸后要釋放連接,不提供廣播或多播服務。UDP傳輸數(shù)據(jù)前不需要建立連接,數(shù)據(jù)傳輸單位為用戶數(shù)據(jù)報,不保證提供可靠的交付。運輸層接收到UDP報文后,不需要給出任何確認,可以實現(xiàn)廣播或多播服務。
1.4. 應用層應用層根據(jù)運輸層提供的服務,實現(xiàn)不同主機中多個應用進程之間的通信和協(xié)同工作,由實際的應用規(guī)劃應用進程在通信時所遵循的協(xié)議。在Internet中有很多標準的應用層協(xié)議如:域名系統(tǒng)DNS、文件傳輸協(xié)議FTP、超文本傳輸協(xié)議HTTP、遠程終端協(xié)議TELNET、動態(tài)主機配置協(xié)議DHCP、簡單網絡管理協(xié)議SNMP等等。
2. Lwip概述Lwip是瑞典計算機科學院(SICS)的Adam Dunkels開發(fā)的一個小型開源TCP/IP協(xié)議棧。作為輕量級TCP/IP協(xié)議,Lwip支持有操作系統(tǒng)以及無操作系統(tǒng)運行,其在保持TCP/IP協(xié)議主要功能的基礎上,減少對RAM、ROM的占用,非常適合于嵌入式系統(tǒng)使用。
3. Lwip移植對于一個可移植的開源協(xié)議棧,移植部分往往分成體系結構相關、操作系統(tǒng)相關、驅動接口相關這幾個部分。此處以Lwip-1.4.1版本為例說明移植的相關代碼文件。
3.1. 體系結構相關3.1.1. cc.h在cc.h中需實現(xiàn)cpu以及編譯器相關的定義,Lwip為了可移植,使用了自身的數(shù)據(jù)類型,需根據(jù)使用的cpu以及編譯器進行定義。網絡字節(jié)序使用大端格式,并且其數(shù)據(jù)結構需嚴格按照字節(jié)對齊,此處定義cpu的字節(jié)序以及編譯器字節(jié)對齊宏等。
#ifndef __ARCH_CC_H__
#define __ARCH_CC_H__
#include
#include
#define LWIP_PROVIDE_ERRNO
/* Define platform endianness (might already bedefined) */
#define BYTE_ORDER LITTLE_ENDIAN
/* Define generic types used in lwIP */
typedef unsignedchar u8_t;
typedef signedchar s8_t;
typedef unsignedshort u16_t;
typedef signedshort s16_t;
typedef unsignedlong u32_t;
typedef signedlong s32_t;
typedef u32_t mem_ptr_t;
typedef u32_t sys_prot_t;
/* Define (sn)printf formatters for these lwIP types*/
#define U16_F "hu"
#define S16_F "hd"
#define X16_F "hx"
#define U32_F "lu"
#define S32_F "ld"
#define X32_F "lx"
/* Compiler hints for packing structures */
#define PACK_STRUCT_BEGIN
#define PACK_STRUCT_STRUCT __attribute__((__packed__))
#define PACK_STRUCT_END
#define PACK_STRUCT_FIELD(x) x
/* Plaform specific diagnostic output */
#define LWIP_PLATFORM_DIAG(x) do { printf x; } while(0)
#define LWIP_PLATFORM_ASSERT(x) do {
printf("Assertion "%s" failed at line%d in %sn",
x, __LINE__, __FILE__); fflush(NULL); abort(); }while(0)
#endif /* __ARCH_CC_H__ */
3.1.2. perf.hperf.h用于實現(xiàn)系統(tǒng)統(tǒng)計和測量相關頭文件,在平時可以不用。
#ifndef __PERF_H__
#define __PERF_H__
#define PERF_START/* null definition */
#define PERF_STOP(x)/* null definition */
#endif /* __PERF_H__ */
3.2. 操作系統(tǒng)相關3.2.1. sys_arch.h/sys_arch.c在sys_arch.h/sys_arch.c中需實現(xiàn)操作系統(tǒng)相關的接口,主要是提供信號量、郵箱、臨界區(qū)保護的接口實現(xiàn),為了支持Lwip的完整功能,還需實現(xiàn)多線程接口,除此之外,Lwip還需使用系統(tǒng)時鐘來確定一些模塊(如ARP、TCP等)的超時處理。所有需在sys_arch.h/sys_arch.c中實現(xiàn)的接口函數(shù)均在sys.h中給出了原型,此處不使用操作系統(tǒng),只需實現(xiàn)Lwip的系統(tǒng)時鐘即可。
extern volatile uint32_t SystemTick;
uint32_t sys_now(void)
{
returnSystemTick;
}
3.3. 驅動接口相關3.3.1. ethernetif.cLwip最底層需要實現(xiàn)讀寫網卡,其中在源碼包中ethernetif.c,已經給出了底層驅動接口模板的實現(xiàn),因此只需要在模板中加入網卡驅動接口函數(shù)即可,主要是網卡初始化、網卡接收、網卡發(fā)送這三個函數(shù)。筆者使用的是DM9000A,驅動的實現(xiàn)在前面章節(jié)有詳細的介紹,此處不再細述。
/* Define those to better describe your networkinterface. */
#define IFNAME0 'e'
#define IFNAME1 'n'
struct ethernetif {
structeth_addr *ethaddr;
/* Addwhatever per-interface state that is needed here. */
};
struct netifDM9000_netif;
static void low_level_init(struct netif *netif)
{
/* set MAC hardware address length */
netif->hwaddr_len = ETHARP_HWADDR_LEN;
/* set MAC hardware address */
netif->hwaddr[0] = default_enetaddr[0];
netif->hwaddr[1] = default_enetaddr[1];
netif->hwaddr[2] = default_enetaddr[2];
netif->hwaddr[3] = default_enetaddr[3];
netif->hwaddr[4] = default_enetaddr[4];
netif->hwaddr[5] = default_enetaddr[5];
/* maximum transfer unit */
netif->mtu = 1500;
/* device capabilities */
/* don't set NETIF_FLAG_ETHARP if this device is notan ethernet one */
netif->flags = NETIF_FLAG_BROADCAST |NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;
/* Do whatever else is needed to initialize interface.*/
DM9000_Init();
}
static err_t low_level_output(struct netif *netif,struct pbuf *p)
{
struct pbuf*q;
uint16_tBuffer[1514/2];
uint8_t*pBuffer;
#if ETH_PAD_SIZE
pbuf_header(p,-ETH_PAD_SIZE); /* drop the padding word */
#endif
pBuffer =(uint8_t *)Buffer;
for(q = p; q!= NULL; q = q->next) {
memcpy(pBuffer, q->payload, q->len);
pBuffer +=q->len;
}
// signal thatpacket should be sent();
DM9000_SendPacket(Buffer, p->tot_len);
#if ETH_PAD_SIZE
pbuf_header(p,ETH_PAD_SIZE); /* reclaim the padding word */
#endif
LINK_STATS_INC(link.xmit);
return ERR_OK;
}
static struct pbuf * low_level_input(struct netif*netif)
{
struct pbuf*p, *q;
uint16_tBuffer[1514/2];
uint8_t*pBuffer;
int16_t len;
/* Obtain thesize of the packet and put it into the "len" variable. */
len =DM90000_ReceivePacket(Buffer);
if (len <=0) {
return NULL;
}
#if ETH_PAD_SIZE
len +=ETH_PAD_SIZE; /* allow room for Ethernet padding */
#endif
/* We allocatea pbuf chain of pbufs from the pool. */
p =pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
if (p != NULL){
#if ETH_PAD_SIZE
pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */
#endif
pBuffer =(uint8_t *)Buffer;
for(q = p; q!= NULL; q = q->next) {
memcpy(q->payload, pBuffer, q->len);
pBuffer+= q->len;
}
#if ETH_PAD_SIZE
pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */
#endif
LINK_STATS_INC(link.recv);
} else {
LINK_STATS_INC(link.memerr);
LINK_STATS_INC(link.drop);
}
return p;
}
void ethernetif_input(struct netif *netif)
{
struct eth_hdr*ethhdr;
struct pbuf*p;
/* movereceived packet into a new pbuf */
p =low_level_input(netif);
/* no packetcould be read, silently ignore this */
if (p == NULL)return;
/* points topacket payload, which starts with an Ethernet header */
ethhdr =p->payload;
switch(htons(ethhdr->type)) {
/* IP or ARPpacket? */
caseETHTYPE_IP:
caseETHTYPE_ARP:
#if PPPOE_SUPPORT
/* PPPoEpacket? */
caseETHTYPE_PPPOEDISC:
caseETHTYPE_PPPOE:
#endif /* PPPOE_SUPPORT */
if(netif->input(p, netif)!=ERR_OK) {
LWIP_DEBUGF(NETIF_DEBUG, ("e