uIP无操作系统(裸机)移植
?本文轉自music_fong博客:http://blog.csdn.net/music_fong/article/details/7191773
以前自己寫了一個TCP/IP的協議棧,但是需要的48K的RAM。后來隨著產品功能的增加,RAM資源就越來越不夠用了。在一個偶然的機會下,發現了一個開源的協議棧--uIP。于是在網上找了一些資料。
原來現在比較流行的開源的小型協議棧有兩個--lwIP和uIP。
lwIP主頁:http://savannah.nongnu.org/projects/lwip/
? ? ? ?uIP主頁:http://www.sics.se/~adam/old-uip/
? uIP文檔:http://www.sics.se/~adam/download/?f=uip-1.0-refman.pdf
其中lwIP實現的功能比較多,所以其會較復雜;uIP主要實現了TCP和UDP以及ARP的等簡單功能。并且兩者都可用于裸機或有操作系統。由于我只需要簡單的功能,所以選擇了簡單的uIP。
下面概述一下uIP及裸機的移植方法,不足之處,請大家多多指正。
uIP概述
uIP的結構不是層次式的,其整個處理過程單獨為一個進程,即一個函數,下面就大概分析這個函數。
一、主循環控制
在主循環中,需要處理兩個事情。
1. ? ?檢測是否有數據到來,當有數據到來時調用uip_input()函數。
? ? 2. 檢測網絡定時器是否超時,如果超時,則調用uip_periodic()函數。
二、體系結構相關功能定義
? ? 1. 檢驗計算,可以手動修改的兩個函數是uip_ipchksum()和uip_tcpchksum()。
? ? 2. uip不使用32-bit的變量,所以要手動實現uip_add32()這個函數。
(這里移植的時候可以不重寫這些函數,按照其默認的算法也可以)
三、內存管理
? ? 1. 整個協議棧使用一個全局的buffer(變量uip_buf)裝載收到的數據包,所以有數據到來時,必須立刻處理該數據,或者將數據從buffer中考出來;只有buffer中的數據處理了,才會接受下一包數據。
? ? 2. 當程序正在處理數據包時,這時如果有數據到來,則需要網卡的驅動程序去把數據按順序緩存
有一些網卡可以緩存好幾包的數據(即網卡或驅動程序把數據包緩存起來),否則丟掉數據或
降低性能。(但這一般只會在多個TCP連接時才會發生)
? ? 3. 發送的數據的頭部會使用全局buffer,同時uIP沒有重傳緩沖,即沒有把已發送的數據排隊,
? ? ? ?所以當需要重傳數據時,需要應用程序再生成重傳數據。
? ? 4. 總共需要的內存可以按應用程序的發在來配置。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 移植
一、下載源代碼。
從http://www.sics.se/~adam/old-uip/download.html下載源代碼。解壓后,uip的核心代碼都在uip文件夾中,所以我們只要把該目錄放到自己的工程里面就行了。同時去掉
? ? ? ?clock.h timer.c(因為我不使用這種定時方法,我只是在定時器里自加來做超時)
? ? ? ?psock.c psock.h(因為我是裸機,所以不需用到這個功能)
? ? ? ?uip-split.c?uip-split.h(因為我的程序中不需要分包,所以也沒有這個功能)
二、配置
配置文件是uipopt.h和uip-conf.h文件,其中uip-conf.h在解壓后的unix文件夾下,并且include在uipopt.h文件中,具體配置參考其文檔。
我修改了以下地方
//#define UIP_CONF_BYTE_ORDER ? ? ?UIP_LITTLE_ENDIAN // 注釋這行,如果該配置,則默認配置為LITTLE_ENDIAN(小端系統)
#define UIP_CONF_BUFFER_SIZE ? ? 1500 //定義了uip_buf的大小
? ? ? ?#include "client.h" ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//include了我的客戶端程序的頭文件
? ? ? ?
在uipopt.h中
#define UIP_APPCALL?HandleClientApp //“HandleClientApp”是我的客戶端處理函數。因為其收到數據或定時器到期,都會回調該函數。
三、結構和變量的定義
在我的"client.h"文件中定義了
struct client_state {
uint8 state;
};
typedef struct client_state uip_tcp_appstate_t; // 這里定義了uip_tcp_appstate_t 結構,這個結構在uip_conn結構里引用,其作用是記錄應用程序的狀態。
四、初始化
uip_ipaddr_t ipaddr;
struct uip_eth_addr temp_eth_addr;
uip_init();
uip_arp_init();
// 設置主機的ip地址
uip_ipaddr(ipaddr, 192, 168, 1,228);
? ? uip_sethostaddr(ipaddr);
// 設置子網掩碼
? ? uip_ipaddr(ipaddr, 255, 255, 255,0);
? ? uip_setnetmask(ipaddr);
? ??
? ? // 設置網關
? ? uip_ipaddr(ipaddr, 192, 168, 1,1);
? ? uip_setdraddr(ipaddr);
? ??
? ? // 設置mac
? ? temp_eth_addr.addr[0] = SourceMAC[0];
? ? temp_eth_addr.addr[1] = SourceMAC[1];
? ? temp_eth_addr.addr[2] = SourceMAC[2];
? ? temp_eth_addr.addr[3] = SourceMAC[3];
? ? temp_eth_addr.addr[4] = SourceMAC[4];
? ? temp_eth_addr.addr[5] = SourceMAC[5];
? ? uip_setethaddr(temp_eth_addr);
五、與硬件相關部分(即把數據從網卡讀出來或把數據發到網卡)
uint8 temp;
? ? uint8 *inbuf;
//#define BUF ((struct uip_eth_hdr *)&uip_buf[0])
struct uip_eth_hdr *pfram;
? ? //檢測是否有數據到達
? ? QueryRTL8019(); // 如果有數據到來,就置位EVENT_WORD
? ??
? ? // 以太網接收數據 ??
? ? if(EVENT_WORD == VALID_FLAG) {
? ??pfram = (struct uip_eth_hdr *)&uip_buf[0];
? ? ? ? EVENT_WORD = 0;?
? ? ? ? uip_len = RTLReceivePacket(); ? ? ? ? ? ? ? ? ? ? ? ? ? // 這個函數的作用是吧網卡收到的數據拷貝到uip_buf中,并返回數據長度
if(uip_len > 0) {
if(pfram->type == HTONS(UIP_ETHTYPE_IP)) {
uip_arp_ipin();
uip_input();
if(uip_len > 0) {
uip_arp_out();
Eth_Send(); //這個函數發送網絡數據
}
} else if(pfram->type == HTONS(UIP_ETHTYPE_ARP)) {
uip_arp_arpin();
if(uip_len > 0) {
Eth_Send();
}
}
}
}
if(ethernet_timer > 5) { ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//ethernet_timer在100ms定時器中自加ethernet_timer++
ethernet_timer = 0;
for(temp = 0; temp < UIP_CONNS; temp++) {
uip_periodic(temp);
? ? ? ? ? ? ? ? //
? ? ? ? ? ? ? ? // If the above function invocation resulted in data that
? ? ? ? ? ? ? ? // should be sent out on the network, the global variable
? ? ? ? ? ? ? ? // uip_len is set to a value > 0.
? ? ? ? ? ? ? ? //
if(uip_len > 0) {
uip_arp_out();
Eth_Send();
}
}
#if UIP_UDP
for(temp = 0; temp < UIP_UDP_CONNS; temp++) {
uip_udp_periodic(temp);
? ? ? ? ? ? ? ? //
? ? ? ? ? ? ? ? // If the above function invocation resulted in data that
? ? ? ? ? ? ? ? // should be sent out on the network, the global variable
? ? ? ? ? ? ? ? // uip_len is set to a value > 0.
? ? ? ? ? ? ? ? //
if(uip_len > 0) {
uip_arp_out();
Eth_Send();
}
}
#endif
}
//
// Process ARP Timer here.
//
if(arp_timer > 100) ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //arp_timer也在100ms定時器中自加
{
arp_timer = 0;
uip_arp_timer();
}
? ? ? ?// 發送函數
? ? ?void Eth_Send(void)
{
memcpy(EthernetTxData, uip_buf, UIP_LLH_LEN); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // 把uip_buf中的幀的頭部靠到EthernetTxData中
? ??
? ? ? ? // 拷貝uip_buf中的剩下部分
if(uip_len <= UIP_LLH_LEN + UIP_TCPIP_HLEN) {
memcpy(&EthernetTxData[UIP_LLH_LEN], &uip_buf[UIP_LLH_LEN], uip_len - UIP_LLH_LEN);
} else {
memcpy(&EthernetTxData[UIP_LLH_LEN], &uip_buf[UIP_LLH_LEN], UIP_TCPIP_HLEN);
memcpy(&EthernetTxData[UIP_LLH_LEN+UIP_TCPIP_HLEN], uip_appdata, uip_len - UIP_TCPIP_HLEN - UIP_LLH_LEN);
}
? ? ? ? // 把數據發到網卡
RTLSendPacket(EthernetTxData, uip_len);
uip_len = 0;
}
六、于應用程序相關部分
void HandleClientApp(void) // 配置中的函數
{
if (uip_connected()) {
//uip_send("Start\n",6);
ClientLinkStatus = CLIENT_WORK;
return;
}
if (ClientLinkStatus == CLIENT_CLOSING) {
uip_close();
ClientLinkStatus = CLIENT_UNLINK;
return;
}
if (uip_aborted() || uip_closed()) {
ClientLinkStatus = CLIENT_UNLINK;
uip_abort();
return;
}
if (uip_timedout()) {
uip_abort();
ClientLinkStatus = CLIENT_UNLINK;
return;
}
if(uip_acked()) {
ClientAcked();
}
if(uip_newdata()) {
ClientNewData(); // 這個是收到數據的處理函數
}
if(uip_rexmit() ||
uip_newdata() ||
uip_acked() ? ||
uip_poll()) {
ClientSendData(); // 這個函數的作用是把緩存中的數據靠到uip_buf中
}?
}
//我在應用程序中會調用uint8 ClientSend(uint8 *pdata, uint16 len)函數,把要發的數據緩沖在ClientTxBuf 這個循環緩存中,然后在這里實際拷貝到uip_buf中發送
void ClientSendData(void)
{
uint16 len;
uint16 left;
uint8 ?*psend;
? ? ? ?// 緩存中有數據
if (ClientTxSize>0) {
len = ClientTxSize > uip_mss() ? uip_mss():ClientTxSize;
if ((pTxBuf+len) > (ClientTxBuf+2048)) {
memcpy(ClientTxTempBuf, pTxBuf, 2048 - (pTxBuf-ClientTxBuf));
left = 2048-(pTxBuf-ClientTxBuf);
memcpy(ClientTxTempBuf+left, ClientTxBuf, len-left);
uip_send(ClientTxTempBuf, len);
} else {
uip_send(pTxBuf, len);
}
}
}
七、總結
整個移植主要處理三方面的事情,一是跟應用程序的接口,即應用程序怎么知道收到數據(在HandleClientApp里面檢測uip_newdata()是否收到信數據),以及怎么發數據;二是跟硬件的接口,即怎么知道網口收到了數據和怎么發數據(我是通過循環檢測處理的);三是定時問題,因為整個協議棧的定時都是通過uip_periodic(uint8 conn_id)處理的。
總結
以上是生活随笔為你收集整理的uIP无操作系统(裸机)移植的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 目标信息收集
- 下一篇: Android人脸识别的初步学习,移动端