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_PRINTF
printf("ethernet_add() failed, %s\r\n", onps_error(enErr));
#endif
return -1;
}
//* 啟動網卡,開始作業,在這里添加與目標網卡啟動相關的代碼
……
#if DHCP_REQ_ADDR_EN
//* 啟動一個dhcp客戶端,從dhcp服務器申請一個動態地址
if(dhcp_req_addr(l_pstNetifEth, &enErr))
{
#if SUPPORT_PRINTF
printf("dhcp request ip address successfully.\r\n");
#endif
}
else
{
#if SUPPORT_PRINTF
printf("dhcp request ip address failed, %s\r\n", onps_error(enErr));
#endif
}
#endif
return 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下:
#define THETHIIRECV_PRIO 21 //* ethernet網卡接收執行緒(任務)優先級
#define THETHIIRECV_STK_SIZE 384 * 4 //* 接收執行緒堆疊大小,這個堆疊要相對大一些,太小會報錯
#define THETHIIRECV_TIMESLICE 10 //* 單次任務調度執行緒能夠作業的最大時間片
static void start_thread_ethernet_ii_recv(void *pvParam)
{
rt_thread_t tid = rt_thread_create("EthRcv", thread_ethernet_ii_recv, pvParam, THETHIIRECV_STK_SIZE, THETHIIRECV_PRIO, THETHIIRECV_TIMESLICE);
if(tid != RT_NULL)
rt_thread_startup(tid);
}
其余os與之類似,我們啟動的這個以太網接收執行緒完成實際的以太網層的報文接收及處理作業,其輪詢等待網卡接收中斷函式發送的報文到達信號,收到信號則立即讀取并處理到達的報文,我們在后面講述網卡接收函式的移植細節時還會提到這個接收執行緒,
對于網卡發送函式,有一點需要注意的是——其原型必須符合協議堆疊的要求,因為我們在進行網卡注冊時還要向協議堆疊注冊發送函式的入口地址,前面在介紹ethernet_add()注冊函式時我們已經給出了發送函式的原型定義,也就是pfunEmacSend引數指向的函式原型,協議堆疊的目標系統是資源受限的單片機系統,為了最大限度節省記憶體,協議堆疊采用了寫時零復制(zero copy)技術,網卡發送函式需要結合協議堆疊的buf list機制撰寫實作代碼,其偽代碼實作如下:
int ethernet_send(SHORT sBufListHead, UCHAR *pubErr)
{
SHORT sNextNode = sBufListHead;
UCHAR *pubData;
USHORT usDataLen;
//* 呼叫buf_list_get_len()函式計算當前要發送的ethernet報文長度,其由協議堆疊提供
UINT unEthPacketLen = buf_list_get_len(sBufListHead);
//* 逐個取出buf list節點發送出去
__lblGetNextNode:
pubData = https://www.cnblogs.com/neo-T/archive/2022/11/07/(UCHAR *)buf_list_get_next_node(&sNextNode, &usDataLen); //* 獲取下一個節點,buf_list_get_next_node()函式由協議堆疊提供
if (NULL == pubData) //* 回傳空意味著已經到達鏈表尾部,沒有要發送的資料了,直接回傳就可以了
return (int)unEthPacketLen;
//* 啟動發送,將取出的資料發送出去,其中pubData指向要發送的資料,usDataLen為要發送的資料長度,這兩個值已經通過buf_list_get_next_node()函式得到
//* 在這里添加與具體目標網卡相關的資料發送代碼
……
//* 取下一個資料節點
goto __lblGetNextNode;
}
關于buf list,其實作機制其實很簡單,以udp通訊為例,用戶要發送資料到對端,會直接呼叫udp發送函式,將資料傳遞給udp層,udp層收到用戶資料后,為了節省記憶體,避免復制,協議堆疊直接將用戶資料掛接到buf list鏈表上成為鏈表的資料節點,接著,udp層會再申請一個節點把udp報文頭掛接到資料節點的前面,組成一個擁有兩個節點的完整udp報文鏈表——鏈表第一個節點掛載udp報文頭,第二個節點掛載用戶要發送的資料,至此,udp層的報文封裝作業完成,資料繼續向ip層傳遞,ip層會繼續申請一個節點把ip報文頭掛接到udp報文頭節點的前面,組成一個擁有三個節點的完整ip 報文鏈表,ip報文在ip層經過路由選擇后被送達資料鏈路層,也就是ethernet層,在這一層,協議堆疊再將ethernet ii報文頭掛接到ip報文頭節點的前面,至此,整個報文的封裝完成,協議堆疊此時會根據網卡注冊資訊呼叫對應網卡的ethernet_send()函式將報文發送出去,ethernet_send()函式的核心處理邏輯就是按照上述機制再依序取出鏈表節點攜帶的各層報文資料,然后順序發送出去,
網卡移植拼圖的最后一塊就是完成網卡接收函式,把網卡收到的資料推送給協議堆疊,其偽代碼實作如下:
//* 網卡接收函式,可以是接收中斷服務子函式,也可以是普通函式,普通函式必須確保能夠在資料到達的第一時間就能讀取并推送給協議堆疊
void ethernet_recv(void)
{
EN_ONPSERR enErr;
unsigned int unPacketLen;
unsigned char *pubRcvedPacket;
//* 在這里添加與具體網卡相關的代碼,等待接收報文到達,如果資料到達將報文長度賦值unPacketLen變數,將報文首地址賦值給pubRcvedPacket
……
……
//* 讀取到達報文并將其推送給協議堆疊進行處理,首先利用協議堆疊mmu模塊動態申請一塊記憶體用于保存到達的報文
unsigned char *pubPacket = (UCHAR *)buddy_alloc(sizeof(ST_SLINKEDLIST_NODE) + unPacketLen, &enErr);
//* 申請成功,根據協議堆疊要求,剛才申請的記憶體按照PST_SLINKEDLIST_NODE鏈表節點方式組織并保存剛剛收到的報文
PST_SLINKEDLIST_NODE pstNode = (PST_SLINKEDLIST_NODE)pubPacket;
pstNode->uniData.unVal = unPacketLen;
memcpy(pubPacket + sizeof(ST_SLINKEDLIST_NODE), (UCHAR *)pubRcvedPacket, unPacketLen);
//* 將上面組織好的報文節點放入接收鏈表,這個接收鏈表由協議堆疊管理,ethernet_put_packet()函式由協議堆疊提供
//* thread_ethernet_ii_recv()接收執行緒負責等待ethernet_put_packet()函式投遞的信號并讀取這個鏈表
//* 引數l_pstNetifEth為前面注冊網卡時由協議堆疊回傳的PST_NETIF指標值
ethernet_put_packet(l_pstNetifEth, pstNode);
}
其中buddy_alloc()函式在功能上與c語言的標準庫函式malloc()完全相同,都是動態分配一塊指定大小的記憶體給呼叫者使用,使用完畢后再由用戶通過buddy_free()函式釋放,這兩個函式由協議堆疊的記憶體管理(mmu)模塊提供,ethernet_put_packet()函式需要重點解釋一下,這個函式由協議堆疊提供,它完成的作業非常重要,它在網卡接收函式與協議堆疊以太網接收執行緒thread_ethernet_ii_recv()之間搭建了一個資料流通的“橋”,接收函式收到報文后按照協議堆疊要求,將報文封裝成ST_SLINKEDLIST_NODE型別的鏈表節點,然后傳遞給ethernet_put_packet()函式,該函式將立即把傳遞過來的節點掛載到由協議堆疊管理的以太網接收鏈表的尾部,然后投遞一個“有新報文到達”的信號量,前文提到的以太網接收執行緒thread_ethernet_ii_recv()會輪詢等待這個信號量,一旦信號到達,接收執行緒將立即讀取鏈表并取出報文交給協議堆疊處理,
至此,ethernet網卡相關的移植作業完成,
4.2 ppp撥號網卡
在Linux系統,2g/4g/5g模塊作為一個通訊終端,驅動層會把它當作一個tty設備來看待,Linux下ppp堆疊也是圍繞著操作一個標準的tty設備來實作底層通訊邏輯的,至于如何操作這個ppp撥號終端進行實際的資料收發tty層并不關心,所以,協議堆疊完全借鑒了這個成功的設計思想,在底層驅動與撥號終端之間增加了一個tty層,將具體的設備操作與上層的業務邏輯進行了剝離,ppp撥號網卡的的移植作業其實就是完成tty層到底層驅動的封裝作業,協議堆疊利用句柄來唯一的標識一個tty設備,在os_datatype.h檔案中定義了這個句柄型別:
#if SUPPORT_PPP
typedef INT HTTY; //* tty終端句柄
#define INVALID_HTTY -1 //* 無效的tty終端句柄
#endif
這個句柄型別非常重要,所有與tty操作相關的函式都要用到這個句柄型別,tty層要完成的驅動封裝作業涉及的函式原型定義依然是在os_adapter.h檔案中:
#if SUPPORT_PPP
//* 打開 tty 設備,回傳 tty 設備句柄,引數 pszTTYName 指定要打開的 tty 設備的名稱
OS_ADAPTER_EXT HTTY os_open_tty(const CHAR *pszTTYName);
//* 關閉 tty 設備,引數 hTTY 為要關閉的 tty 設備的句柄
OS_ADAPTER_EXT void os_close_tty(HTTY hTTY);
//* 向 hTTY 指定的 tty 設備發送資料,回傳實際發送的資料長度
//* hTTY:設備句柄
//* pubData:指標,指向要發送的資料的指標
//* nDataLen:要發送的資料長度
//* 回傳值為實際發送的位元組數
OS_ADAPTER_EXT INT os_tty_send(HTTY hTTY, UCHAR *pubData, INT nDataLen);
//* 從引數 hTTY 指定的 tty 設備等待接收資料,阻塞型
//* hTTY:設備句柄
//* pubRcvBuf:指標,指向資料接識訓沖區的指標,用于保存收到的資料
//* nRcvBufLen:接識訓沖區的長度
//* nWaitSecs:等待的時長,單位:秒,0 一直等待;直至收到資料或報錯,大于 0,等待指定秒數;小于 0,不支持
//* 回傳值為實際收到的資料長度,單位:位元組
OS_ADAPTER_EXT INT os_tty_recv(HTTY hTTY, UCHAR *pubRcvBuf, INT nRcvBufLen, INT nWaitSecs);
//* 復位 tty 設備,這個函式名稱體現了2g/4g/5g模塊作為tty設備的特殊性,其功能從本質上看就是一個 modem,modem 設備出現通訊
//* 故障時,最好的修復故障的方式就是直接復位,復位可以修復絕大部分的因軟體問題產生的故障
OS_ADAPTER_EXT void os_modem_reset(HTTY hTTY);
#endif
依據上述函式的原型定義及功能說明,我們在os_adapter.c檔案編碼實作它們,相關偽代碼實作如下:
#if SUPPORT_PPP
HTTY os_open_tty(const CHAR *pszTTYName)
{
//* 如果你的系統存在多個ppp撥號終端,那么pszTTYName引數用于區分打開哪個串口
//* 在這里添加串口打開代碼將連接撥號終端的串口打開
……
……
//* 如果目標系統只有一個撥號終端,那么這里回傳的tty句柄x值為0,如果目標系統存在多個模塊,這里需要你根據引數
//* pszTTYName指定的名稱來區分是哪個設備,并據此回傳不同的tty句柄,句柄值x應從0開始自增,步長為1
return x;
}
void os_close_tty(HTTY hTTY)
{
//* 在這里添加串口關閉代碼,關閉哪個串口的依據是tty設備句柄hTTY
……
}
INT os_tty_send(HTTY hTTY, UCHAR *pubData, INT nDataLen)
{
//* 在這里添加資料發送代碼,其實就是呼叫對應的串口驅動函式發送資料到撥號終端,如果存在多個tty設備,請依據引數hTTY來
//* 確定需要呼叫哪個串口驅動函式發送資料,回傳值為實際發送的位元組數
……
}
INT os_tty_recv(HTTY hTTY, UCHAR *pubRcvBuf, INT nRcvBufLen, INT nWaitSecs)
{
//* 同上,在這里添加資料讀取代碼,其實就是呼叫對應的串口驅動函式從撥號終端讀取資料,如果存在多個tty設備,請依據引數hTTY來
//* 確定需要呼叫哪個串口驅動函式讀取資料,回傳值為實際讀取到的位元組數
……
}
void os_modem_reset(HTTY hTTY)
{
//* 在這里添加撥號終端的復位代碼,如果你的目標板不支持軟體復位模塊,可以省略這一步
//* 復位模塊的目的是解決絕大部分的因軟體問題產生的故障
……
}
#endif
參照上述偽代碼,依據目標系統具體情況撰寫相應功能代碼即可,注意,上述代碼能夠正常作業的關鍵是目標系統的串口驅動必須能夠正常作業且健壯、可靠,因為tty層封裝的其實就是操作ppp撥號終端的串口驅動代碼,tty只是做了一層簡單封裝罷了,os_adapter.c檔案中關于ppp部分還有如下幾項定義需要根據你的實際目標環境進行配置:
#if SUPPORT_PPP
//* 連接ppp撥號終端的串口名稱,有幾個模塊,就指定幾個,其存盤的單元索引應等于os_open_tty()回傳的對應串口的tty句柄值
const CHAR *or_pszaTTY[PPP_NETLINK_NUM] = { …… /* 如"串口1", "串口2"等 */ };
//* 指定ppp撥號的apn、用戶和密碼,系統支持幾路ppp,就需要指定幾組撥號資訊
//* ST_DIAL_AUTH_INFO結構體保存這幾個資訊,該結構體的詳細內容參見協議堆疊原始碼ppp/ppp.h檔案
//* 這里設定的apn等撥號認證資訊會替代前面說過的APN_DEFAULT、AUTH_USER_DEFAULT、AUTH_PASSWORD_DEFAULT等預設設定
const ST_DIAL_AUTH_INFO or_staDialAuth[PPP_NETLINK_NUM] = {
{ "4gnet", "card", "any_char" }, //* 注意ppp賬戶和密碼盡量控制在20個位元組以內,太長需要需要修改chap.c
//* 中send_response()函式的szData陣列容量及pap.c中pap_send_auth_request()函式的
//* ubaPacket陣列的容量,確保其能夠封裝一個完整的回應報文
/* 系統存在幾路ppp鏈路,就在這里添加幾路撥號認證資訊 */
};
//* ppp鏈路協商的初始協商配置資訊,協商成功后這里保存最終的協商結果,ST_PPPNEGORESULT結構體的詳細說明參見下文
ST_PPPNEGORESULT o_staNegoResult[PPP_NETLINK_NUM] = {
{
{ 0, PPP_MRU, ACCM_INIT,{ PPP_CHAP, 0x05 /* CHAP協議,0-4未使用,0x05代表采用MD5演算法 */ }, TRUE, TRUE, FALSE, FALSE },
{ IP_ADDR_INIT, DNS_ADDR_INIT, DNS_ADDR_INIT, IP_ADDR_INIT, MASK_INIT }, 0
},
/* 系統存在幾路ppp鏈路,就在這里添加幾路的協商初始值,如果不確定,可以將上面預定義的初始值直接復制過來即可 */
};
#endif
上面給出的代碼做了幾件事情:
1)指定tty設備連接的串口名稱;
2)指定撥號認證資訊:apn、用戶和密碼;
3)指定ppp鏈路協商初始值;
總之,你的目標系統連接了幾個撥號終端,這幾件事情就要針對特定的終端分別做一遍,單獨指定,這里需要重點說明的是ppp鏈路協商配置資訊,這些資訊由ST_PPPNEGORESULT結構體保存(參見negotiation_storage.h檔案):
typedef struct _ST_PPPNEGORESULT_ {
struct {
UINT unMagicNum; //* 幻數(魔術字)
USHORT usMRU; //* 最大接收單元,預設值由PPP_MRU宏指定,一般為1500位元組
UINT unACCM; //* ACCM,異步控制字符映射,指定哪些字符需要轉義,如果不確定,建議采用ACCM_INIT宏指定的預設值
struct { //* 保存認證資訊的結構體
USHORT usType; //* 指定認證型別:chap或pap,預設chap認證
UCHAR ubaData[16]; //* 認證報文攜帶的資料,不同的協議攜帶的資料型別不同,一般情況下采用協議堆疊的預設值即可
} stAuth;
BOOL blIsProtoComp; //* 是否采用協議域壓縮(本地設定項,代表協議堆疊一側,協商結果不影響該欄位)
BOOL blIsAddrCtlComp; //* 是否采用地址及控制域壓縮(本地設定項,代表協議堆疊一側,協商結果不影響該欄位)
BOOL blIsNegoValOfProtoComp; //* 協議域是否壓縮的協商結果值(遠端設定項,代表對端是否支持該配置,協商結果影響該欄位)
BOOL blIsNegoValOfAddrCtlComp; //* 地址及控制域是否壓縮的協商結果值(遠端設定項,同上)
} stLCP;
//* 存盤ppp鏈路的初始及協商成功后的地址資訊
struct {
UINT unAddr; //* ip地址,初始值由協議堆疊提供的IP_ADDR_INIT宏指定,不要擅自修改
UINT unPrimaryDNS; //* 主dns服務器地址,初始值由協議堆疊提供的DNS_ADDR_INIT宏指定,不要擅自修改
UINT unSecondaryDNS; //* 次dns服務器地址,初始值由協議堆疊提供的DNS_ADDR_INIT宏指定,不要擅自修改
UINT unPointToPointAddr; //* 點對點地址,初始值由協議堆疊提供的IP_ADDR_INIT宏指定,不要擅自修改
UINT unSubnetMask; //* 子網掩碼
} stIPCP;
UCHAR ubIdentifier; //* 標識域,從0開始自增,唯一的標識一個ppp報文,用于確定應答報文
UINT unLastRcvedSecs; //* 最近一次收到對端報文時的秒數,其用于ppp鏈路故障探測,無需關心,協議堆疊底層使用
} ST_PPPNEGORESULT, *PST_PPPNEGORESULT;
基本上,要調整的地方幾乎沒有,我們直接采用預設值即可,
移植作業的最后一步就是把ppp網卡的主處理執行緒thread_ppp_handler()添加到os適配層的作業執行緒串列中,也就是前面講解os適配層移植作業時提到的lr_stcbaPStackThread陣列,這個陣列保存了協議堆疊內部作業執行緒串列,我們先前已經添加了one-shot定時器作業執行緒thread_one_shot_timer_count(),我們再把ppp主處理執行緒添加到這個陣列中即可,偽代碼實作如下:
//* 協議堆疊內部作業執行緒串列
const static STCB_PSTACKTHREAD lr_stcbaPStackThread[] = {
{ thread_one_shot_timer_count, NULL},
#if SUPPORT_PPP
//* 在此按照順序建立ppp作業執行緒,入口函式為thread_ppp_handler(),執行緒入口引數為os_open_tty()回傳的tty句柄值
//* 其直接強行進行資料型別轉換即可,即作為執行緒入口引數時直接以如下形式傳遞:
//* (void *)句柄值
//* 不要傳遞引數地址,即(void *)&句柄,這種方式是錯誤的
#endif
};
ppp主處理執行緒將在協議堆疊加載時由os適配層函式os_thread_onpstack_start()啟動,在這里只需把其添加到作業執行緒串列中即可,剩下的交由協議堆疊自動處理,在這里需要特別說明的是主處理執行緒的入口引數為tty句柄,其值應直接傳遞給執行緒,不能傳遞句柄地址(參見上面的偽代碼注釋),比如實際移植到目標系統時如果系統只存在一路ppp,os_open_tty()回傳的tty句柄值為0,那么添加到作業執行緒串列中的ppp主處理執行緒入口引數的值應為“(void *)0”,不用關心前面的“(void *)”,這段資料型別強制轉換代碼只是為了避免編譯器報錯,ppp鏈路建立成功后,協議堆疊會以“ppp+tty句柄”的方式命名該鏈路,命名時的tty句柄值就是通過這個啟動引數獲得的,所以這個值一定要配置正確,對于單路ppp,由于tty句柄值為0,所以ppp鏈路的名稱為“ppp0”,
至此,ppp網卡相關的移植作業完成,
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/528688.html
標籤:其他
