3 onps棧移植說明——添加網卡

4. 添加網卡移植的最后一步就是編寫網卡驅動然后將網卡添加到協議棧 。網卡驅動其本質上完成的是數據鏈路層的工作,在整個通訊鏈路上處于通訊樞紐位置,通訊報文的發送和接收均由其實際完成 。針對網卡部分的移植工作共三步:
1)編寫網卡驅動;
2)注冊網卡到協議棧;
3)對接網卡數據收發接口;
協議棧目前支持兩種網卡類型:ethernet和ppp 。兩種網卡的移植工作雖然步驟一樣,但具體移植細節還是有很大區別的,需要分開單獨進行 。
4.1 ethernet網卡從移植的角度看,ethernet網卡驅動要提供三個接口函數并完成與協議棧的對接:
1)網卡初始化函數,完成網卡初始及啟動工作,并將其添加到協議棧;
2)網卡發送函數,發送上層協議傳遞的通訊報文到對端;
3)網卡接收函數,接收到達的通訊報文并傳遞給上層協議;
對于網卡初始化函數,其要做的工作用一句話總結就是:參照網卡數據手冊對其進行配置,然后將其注冊到協議棧:
#define DHCP_REQ_ADDR_EN 1 //* dhcp請求ip地址使能宏static PST_NETIF l_pstNetifEth = NULL; //* 協議棧返回的netif結構int ethernet_init(void){/* 進行初始配置 , 比如引腳配置、使能時鐘、相關工作參數配置等工作 *///* 在這里添加能夠完成上述工作的相關代碼,請參照目標網卡的技術手冊編寫…………/* 到這里網卡配置工作完成,但還未啟動 *///* 添加網卡到協議棧 , 一定要注意啟動以太網卡之前一定要先將其添加到協議棧EN_ONPSERR enErr;ST_IPV4 stIPv4;#if !DHCP_REQ_ADDR_EN//* 分配一個靜態地址,請根據自己的具體網絡情形設置地址stIPv4.unAddr = inet_addr_small("192.168.0.4");stIPv4.unSubnetMask = inet_addr_small("255.255.255.0");stIPv4.unGateway = inet_addr_small("192.168.0.1");stIPv4.unPrimaryDNS = inet_addr_small("1.2.4.8");stIPv4.unSecondaryDNS = inet_addr_small("8.8.8.8");stIPv4.unBroadcast = inet_addr_small("192.168.0.255");#else//* 地址清零,為dhcp客戶端申請動態地址做好準備memset(&stIPv4, 0, sizeof(stIPv4));#endif//* 注冊網卡,也就是將網卡添加到協議棧l_pstNetifEth = ethernet_add(……);if(!l_pstNetifEth){#if SUPPORT_PRINTFprintf("ethernet_add() failed, %s\r\n", onps_error(enErr));#endifreturn -1;}//* 啟動網卡,開始工作,在這里添加與目標網卡啟動相關的代碼……#if DHCP_REQ_ADDR_EN//* 啟動一個dhcp客戶端,從dhcp服務器申請一個動態地址if(dhcp_req_addr(l_pstNetifEth, &enErr)){#if SUPPORT_PRINTFprintf("dhcp request ip address successfully.\r\n");#endif}else{#if SUPPORT_PRINTFprintf("dhcp request ip address failed, %s\r\n", onps_error(enErr));#endif}#endifreturn 0;}上面給出的樣例代碼中 , 省略的部分是與目標系統相關的網卡初始配置代碼,其余則是與協議棧有關的網卡注冊代碼 。這部分代碼主要是完成了兩塊工作:一,注冊網卡到協議棧;二,指定或申請一個靜態/動態地址 。注冊網卡的工作是由協議棧提供的ethernet_add()函數完成的,其詳細說明如下:
//* 注冊ethernet網卡到協議棧,只有如此協議棧才能正常使用該網卡進行數據通訊 。//*pszIfName:網卡名稱//*ubaMacAddr:網卡mac地址//*pstIPv4:指向ST_IPV4結構體的指針(include/netif/netif.h),這個結構體保存用戶指定的ip地址、網關、dns、子網掩碼等配置信息//*//*pfunEmacSend:函數指針,指向發送函數,函數原型為INT(* PFUN_EMAC_SEND)(SHORT sBufListHead, UCHAR *pubErr),這個指針指向的其實//*就是網卡發送函數//*//* pfunStartTHEmacRecv:函數指針,協議棧使用該函數啟動網卡接收線程,該線程為協議棧內部工作線程,用戶移植時只需提供啟動該線程的接口函數即可//*//*ppstNetif:二維指針,協議棧成功注冊網卡后ethernet_add()函數會返回一個PST_NETIF指針給調用者,這個參數指向這個指針,其最終會被//*協議棧通過pvParam參數傳遞給pfunStartTHEmacRecv指向的函數//*//*penErr:如果注冊失敗,ethernet_add()函數會返回一個錯誤碼,這個參數用于接收這個錯誤碼//*//* 注冊成功,返回一個PST_NETIF類型的指針,后續的報文收發均用到這個指針;注冊失敗則返回NULL 。具體錯誤信息參見penErr參數攜帶的錯誤碼 。PST_NETIF ethernet_add(const CHAR *pszIfName, const UCHAR ubaMacAddr[ETH_MAC_ADDR_LEN], PST_IPV4 pstIPv4, PFUN_EMAC_SEND pfunEmacSend,void (*pfunStartTHEmacRecv)(void *pvParam), PST_NETIF *ppstNetif, EN_ONPSERR *penErr);ethernet_add()函數提供的參數看起來較為復雜,但其實就完成了一件事情:告訴協議棧這個新增加的網卡的相關身份信息及功能接口,包括名稱、地址、數據讀寫接口等 。這個函數有兩個地方需要特別說明:一個是樣例代碼中該函數的返回值保存在了一個靜態存儲時期的變量l_pstNetifEth中;另一個是入口參數pfunStartTHEmacRecv 。前一個用于接收注冊成功后返回的PST_NETIF指針;后一個則是需要提供一個線程啟動函數,啟動協議棧內部的以太網接收線程thread_ethernet_ii_recv(),該線程在協議棧源碼ethernet.c文件中實現 。PST_NETIF指針非常重要,它是網卡能夠正常工作的關鍵 。報文收發均用到這個指針 。它的生命周期應該與協議棧的生命周期相同,因此這個指針變量在上面的樣例代碼中被定義成一個靜態存儲時期的變量,并確保網卡的接收、發送函數均能訪問 。pfunStartTHEmacRecv參數指向的函數要實現的功能與前面我們編寫的os適配層函數os_thread_onpstack_start()相同,其就是調用os提供的線程啟動函數啟動thread_ethernet_ii_recv()線程 。比如rt-thread下:

推薦閱讀