網(wǎng)卡移植拼圖的最后一塊就是完成網(wǎng)卡接收函數(shù),把網(wǎng)卡收到的數(shù)據(jù)推送給協(xié)議棧 。其偽代碼實現(xiàn)如下:
//* 網(wǎng)卡接收函數(shù),可以是接收中斷服務子函數(shù),也可以是普通函數(shù),普通函數(shù)必須確保能夠在數(shù)據(jù)到達的第一時間就能讀取并推送給協(xié)議棧void ethernet_recv(void){EN_ONPSERR enErr;unsigned int unPacketLen;unsigned char *pubRcvedPacket;//* 在這里添加與具體網(wǎng)卡相關的代碼,等待接收報文到達,如果數(shù)據(jù)到達將報文長度賦值unPacketLen變量,將報文首地址賦值給pubRcvedPacket…………//* 讀取到達報文并將其推送給協(xié)議棧進行處理,首先利用協(xié)議棧mmu模塊動態(tài)申請一塊內存用于保存到達的報文unsigned char *pubPacket = (UCHAR *)buddy_alloc(sizeof(ST_SLINKEDLIST_NODE) + unPacketLen, &enErr);//* 申請成功,根據(jù)協(xié)議棧要求,剛才申請的內存按照PST_SLINKEDLIST_NODE鏈表節(jié)點方式組織并保存剛剛收到的報文PST_SLINKEDLIST_NODE pstNode = (PST_SLINKEDLIST_NODE)pubPacket;pstNode->uniData.unVal = unPacketLen;memcpy(pubPacket + sizeof(ST_SLINKEDLIST_NODE), (UCHAR *)pubRcvedPacket, unPacketLen);//* 將上面組織好的報文節(jié)點放入接收鏈表,這個接收鏈表由協(xié)議棧管理,ethernet_put_packet()函數(shù)由協(xié)議棧提供//* thread_ethernet_ii_recv()接收線程負責等待ethernet_put_packet()函數(shù)投遞的信號并讀取這個鏈表//* 參數(shù)l_pstNetifEth為前面注冊網(wǎng)卡時由協(xié)議棧返回的PST_NETIF指針值ethernet_put_packet(l_pstNetifEth, pstNode);}其中buddy_alloc()函數(shù)在功能上與c語言的標準庫函數(shù)malloc()完全相同,都是動態(tài)分配一塊指定大小的內存給調用者使用,使用完畢后再由用戶通過buddy_free()函數(shù)釋放 。這兩個函數(shù)由協(xié)議棧的內存管理(mmu)模塊提供 。ethernet_put_packet()函數(shù)需要重點解釋一下 。這個函數(shù)由協(xié)議棧提供 。它完成的工作非常重要,它在網(wǎng)卡接收函數(shù)與協(xié)議棧以太網(wǎng)接收線程thread_ethernet_ii_recv()之間搭建了一個數(shù)據(jù)流通的“橋” 。接收函數(shù)收到報文后按照協(xié)議棧要求,將報文封裝成ST_SLINKEDLIST_NODE類型的鏈表節(jié)點,然后傳遞給ethernet_put_packet()函數(shù) 。該函數(shù)將立即把傳遞過來的節(jié)點掛載到由協(xié)議棧管理的以太網(wǎng)接收鏈表的尾部,然后投遞一個“有新報文到達”的信號量 。前文提到的以太網(wǎng)接收線程thread_ethernet_ii_recv()會輪詢等待這個信號量 。一旦信號到達,接收線程將立即讀取鏈表并取出報文交給協(xié)議棧處理 。
至此,ethernet網(wǎng)卡相關的移植工作完成 。
4.2 ppp撥號網(wǎng)卡在Linux系統(tǒng),2g/4g/5g模塊作為一個通訊終端,驅動層會把它當作一個tty設備來看待 。Linux下ppp棧也是圍繞著操作一個標準的tty設備來實現(xiàn)底層通訊邏輯的 。至于如何操作這個ppp撥號終端進行實際的數(shù)據(jù)收發(fā)tty層并不關心 。所以,協(xié)議棧完全借鑒了這個成功的設計思想,在底層驅動與撥號終端之間增加了一個tty層,將具體的設備操作與上層的業(yè)務邏輯進行了剝離 。ppp撥號網(wǎng)卡的的移植工作其實就是完成tty層到底層驅動的封裝工作 。協(xié)議棧利用句柄來唯一的標識一個tty設備 。在os_datatype.h文件中定義了這個句柄類型:
#if SUPPORT_PPPtypedef INT HTTY;//* tty終端句柄#define INVALID_HTTY -1 //* 無效的tty終端句柄#endif這個句柄類型非常重要,所有與tty操作相關的函數(shù)都要用到這個句柄類型 。tty層要完成的驅動封裝工作涉及的函數(shù)原型定義依然是在os_adapter.h文件中:
#if SUPPORT_PPP//* 打開 tty 設備,返回 tty 設備句柄,參數(shù) pszTTYName 指定要打開的 tty 設備的名稱OS_ADAPTER_EXT HTTY os_open_tty(const CHAR *pszTTYName);//* 關閉 tty 設備,參數(shù) hTTY 為要關閉的 tty 設備的句柄OS_ADAPTER_EXT void os_close_tty(HTTY hTTY);//* 向 hTTY 指定的 tty 設備發(fā)送數(shù)據(jù),返回實際發(fā)送的數(shù)據(jù)長度//*hTTY:設備句柄//*pubData:指針,指向要發(fā)送的數(shù)據(jù)的指針//* nDataLen:要發(fā)送的數(shù)據(jù)長度//* 返回值為實際發(fā)送的字節(jié)數(shù)OS_ADAPTER_EXT INT os_tty_send(HTTY hTTY, UCHAR *pubData, INT nDataLen);//* 從參數(shù) hTTY 指定的 tty 設備等待接收數(shù)據(jù),阻塞型//*hTTY:設備句柄//*pubRcvBuf:指針,指向數(shù)據(jù)接收緩沖區(qū)的指針,用于保存收到的數(shù)據(jù)//* nRcvBufLen:接收緩沖區(qū)的長度//*nWaitSecs:等待的時長,單位:秒 。0 一直等待;直至收到數(shù)據(jù)或報錯,大于 0,等待指定秒數(shù);小于 0,不支持//* 返回值為實際收到的數(shù)據(jù)長度,單位:字節(jié)OS_ADAPTER_EXT INT os_tty_recv(HTTY hTTY, UCHAR *pubRcvBuf, INT nRcvBufLen, INT nWaitSecs); //* 復位 tty 設備,這個函數(shù)名稱體現(xiàn)了2g/4g/5g模塊作為tty設備的特殊性,其功能從本質上看就是一個 modem,modem 設備出現(xiàn)通訊//* 故障時,最好的修復故障的方式就是直接復位,復位可以修復絕大部分的因軟件問題產(chǎn)生的故障OS_ADAPTER_EXT void os_modem_reset(HTTY hTTY);#endif
經(jīng)驗總結擴展閱讀
- 2 onps棧移植說明——編譯器及os適配層移植
- 1 onps棧移植說明——onps棧的配置及裁剪
- 親愛的客棧楊紫喬欣是哪期?
- <一>從指令角度了解函數(shù)堆棧調用過程
- stm32h750移植lvgl
- 3 Python全棧工程師之從網(wǎng)頁搭建入門到Flask全棧項目實戰(zhàn) - 入門Flask微框架
- 都卷Java,你看看你得學多少技術棧才能工作!
- flutter系列之:flutter中可以建索引的棧布局IndexedStack
- 官棧花膠多久吃一次 官棧即食花膠要加熱嗎
- 官棧即食花膠是不是真的 官棧即食花膠有激素嗎
