S3C2416裸機開發(fā)系列二二_Lwip的移植
Internet實現了全球范圍內計算機網絡的互連,不同主機之間必須遵循相同的網絡協(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. 鏈路層數據鏈路層將網絡層交下來的IP數據報組裝成幀,在兩個相鄰結點間的鏈路上透明傳送以幀為單位的數據,另一結點若收到無差錯的幀,則從收到的幀中提取出IP數據報上交上一層,否則丟棄此包。這一層級幀格式通常采用Ethernet V2,有效的MAC幀長度為64~1518字節(jié),通常由網絡適配器實現這一層級。
1.2. 網絡層網絡層把運輸層產生的報文段或用戶數據報封裝成分組或包進行傳送,由于網絡層使用無連接的網際協(xié)議IP,因此,分組也稱為IP數據報,并且是不可靠的。除了IP協(xié)議,網絡層還需配套使用以下幾個協(xié)議:地址解析協(xié)議ARP、逆地址解析協(xié)議RARP、因特網控制報文協(xié)議ICMP、因特網組管理協(xié)議IGMP。
1.3. 運輸層運輸層向上面的應用層提供通信服務,具有復用和分用的功能。復用就是多個應用層進程可同時使用運輸層的服務,分用則為運輸層可以把收到的數據分別交付上面應用層中相應的進程。這一層級有兩個不同的協(xié)議:傳輸控制協(xié)議TCP和用戶數據報協(xié)議UDP。TCP提供面向連接的服務,數據傳輸單位為報文段,能夠保證提供可靠的交付。其傳輸數據前必須先建立連接,結束傳輸后要釋放連接,不提供廣播或多播服務。UDP傳輸數據前不需要建立連接,數據傳輸單位為用戶數據報,不保證提供可靠的交付。運輸層接收到UDP報文后,不需要給出任何確認,可以實現廣播或多播服務。
1.4. 應用層應用層根據運輸層提供的服務,實現不同主機中多個應用進程之間的通信和協(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中需實現cpu以及編譯器相關的定義,Lwip為了可移植,使用了自身的數據類型,需根據使用的cpu以及編譯器進行定義。網絡字節(jié)序使用大端格式,并且其數據結構需嚴格按照字節(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用于實現系統(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中需實現操作系統(tǒng)相關的接口,主要是提供信號量、郵箱、臨界區(qū)保護的接口實現,為了支持Lwip的完整功能,還需實現多線程接口,除此之外,Lwip還需使用系統(tǒng)時鐘來確定一些模塊(如ARP、TCP等)的超時處理。所有需在sys_arch.h/sys_arch.c中實現的接口函數均在sys.h中給出了原型,此處不使用操作系統(tǒng),只需實現Lwip的系統(tǒng)時鐘即可。
extern volatile uint32_t SystemTick;
uint32_t sys_now(void)
{
returnSystemTick;
}
3.3. 驅動接口相關3.3.1. ethernetif.cLwip最底層需要實現讀寫網卡,其中在源碼包中ethernetif.c,已經給出了底層驅動接口模板的實現,因此只需要在模板中加入網卡驅動接口函數即可,主要是網卡初始化、網卡接收、網卡發(fā)送這三個函數。筆者使用的是DM9000A,驅動的實現在前面章節(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