[轉帖]WinCE的網絡驅動實現原理

原來一直對NDIS不太清楚,DM9000AEP能用了後也沒再仔細研究這部分的工作原理。今天恰好看到這篇文章,分析了NDIS,MiniPort以及
網卡驅動的實現原理。
原文地址:http://www.cnblogs.com/sankye/articles/1651280.html

WinCE的網絡驅動實現原理

1.WinCE的網絡通信架構

WinCE的網絡通信架構如圖1所示,WinCE的網絡通信架構中一個重要的角色是網絡結構規範(NetworkDriver Interface Specification,NDIS),它支持多種網絡媒體,以及提供包括TCP/IP等多種網絡協議。

 

 

1 WINCE通信網絡的層

其中最上層的Wins0ck是提供給應用層的接口,一般開發網絡應用都會用Winsock接口來開發。NDIS位於協議驅動層下面,而位於硬件驅動Miniport Driver之上。協議驅動層通過調用NDIS封裝層的接口函數,實現與底層硬件驅動的交互。對於協議層來說,NDIS相當於一個Miniport Driver,而對於底層的硬件驅動來說,NDIS相當於上層的協議層,所以NDIS起到承上啓下的作用,也起到對底層硬件接口的規範作用

 

2.WinCE網絡驅動架構和實現原理

在WinCE中,網卡驅動的實現原理如圖2所示,在上層的協議驅動層看來,它調用NDIS接口函數訪問網絡設備,其具體實現過程(如圖2虛線框),是通過調用底層的Miniport Driver接口函數來實現。在WinCE系統中NDIS接口函數庫是Microsoft開發好的,所以開發winCE下的網卡驅動就是編寫一個Miniport Driver,它向上導出接口函數與NDIS接口實現對接,向下直接管理網卡硬件。

 

 

2 WINCE網絡驅動原理

 

3.網絡驅動的編程與實現

3.1網絡驅動接口的實現流程

在WinCE中,應用層通過調用NDIS接口(圖3實線框)實現與底層硬件的交互,而NDIS接口是微軟已經開發好的,被定義成一個數據結構體的形式。開發網卡驅動就是寫一個Miniport Driver,導出相應的Miniport接口函數(圖3的虛線框),這些接口函數會在系統註冊一個Miniport Driver的時候與NDIS封裝層的接口函數對接,這樣內核協議層通過調用NDIS的接口就可以訪問底層硬件。

3 網絡驅動接口實現流程

微軟定義的與Miniport Driver相關的NDIS標準接口總共有18個,針對不同的網絡設備其接口的實現也不盡相同,本流程圖中羅列出的是CS89OO網卡驅動所實現的接口函數,具體的接口實現可參照Platform Builder的幫助文檔。

3.2網絡驅動接口的具體實現

實際網絡驅動的編寫,就是理解wincE下網絡驅動程序的構架,然後針對實際的硬件編寫代碼,實現相應的中間層Miniport Driver接口函數。下面結合利用WinCE5.0內核在脈衝發生器嵌入式主板上移植編寫嵌入式CS8900網卡驅動程序的實例,介紹網卡驅動程序Miniport Driver接口的具體實現(由於本驅動的硬件設備是CS8900,所以在函數接口的取名上一律用CS8900代替Miniport Driver)。

3.2.1網絡驅動程序的入口函數

DriverEntry,該函數中首先調用NdisMinitializeWrapper函數來通知NDIS Library要註冊一個Miniport。然後初始化MINIPORT結構體,所有的Miniport的相關接口函數都會賦到 MINIPORT 結構中, 最後調用NdisMRegisterMiniport來註冊Miniport。通過此函數,實現了Miniport Driver接口與NDIS接口的對接。

3.2.2網絡設備的初始化接口

Miniportlnitialize, 該函數爲調用函數CS8900RegisterAdapter來完成網絡設備的初始化,而CS8900RegisterAdapter 又會調用CS8900Initialize,CS8900Initialize函數會相繼調用:findCS,查找網絡設備;resetCS,重啓網絡控制器,並設置工作模式爲16bit的I/0模式;InitIrq,開啓網絡控制器的中斷;initCS,設置臨時的物理地址,爲網絡控制器設置與嵌入式芯片之間中斷的硬件連接,以及總線讀寫的時序。

3.2.3網絡數據包的發送

WinCE網絡數據發送的流程:當上層協議驅動要發數據時,調用NdisSend請求NDIS發送數據包,NDIS將會調用緊接其下的中間層驅動的CS8900Send,該函數首先調用NdisQueryPacket,得到需要發送包的數據信息,並拷貝到一個緩衝區暫存,這樣做的目的是保證包數據不被丟失。然後調用CS8900RequestTransmit,向網絡控制器發送傳送數據的請求,最後調用函數CS8900CopyTxFrame完成數據包的發生。

3.2.4網絡數據的接收和中斷

網絡設備的接收數據包時通過中斷實現,當網絡接口接收到新數據包時,發送完成或者報錯誤信息及連接狀態都會出發中斷,通常中斷處理程序通過檢測硬件狀態寄存器判斷是哪種情況。

當網絡設備有數據到來的時候,將觸發中斷,相應的中斷處理程序接管中斷後,將調用Miniport Driver所註冊的中斷處理例程CS8900Isr,通過讀取CS8900的中斷寄存器判斷是否是接收到數據中斷,如果是就調用數據接收函數CS8900ReceiveEvent。Miniport Driver通常在這裏把網卡上的數據拷貝到Miniport Driver緩衝區隊列中去,出於效率的考慮,Miniport Driver這時可能不會立即通知上層處理新的數據,因爲很可能,馬上還有隨後的新的數據到來,當接收到的包的數量達到一定程度的時候,驅動程序的接收線程會調用函數NdisMIndicateReceivePacket指示新的NDIS新數據的到來。

3.2.5Miniport Driver其他接口

    CS8900Reset,復位硬件網卡;

CS8900Querylnformation,網卡信息查詢函數;

CS8900Setlnformation,設置網卡信息函數。

3.2.6 驅動下實現的CS8900A的幾個函數

1.讀寫ReadPacketPage和WritePacketPage。這兩個函數通常並不被EthDbg驅動程序的使用者直接調用。而是這個兩個函數向其他EthDbg驅動程序接口函數提供了最基本的讀寫CS8900A的PacketPage內部的控制與狀態寄存器和收發緩衝區的功能。

在源文件%_WINCEROOT%/PLATFORM/COMMON/SRC/COMMON/ETHDRV/CS8900A/cs8900a.c中定義了一個靜態全局指針變量g_pCS8900:

STATIC CS8900A_REGS *g_pCS8900;

其類型CS8900A_ERGSS是在同一源文件中定義的結構體:

Typedef struct{

Unsigned _int16 DATA0;

Unsigned _int16 DATA1;

Unsigned _int16 TXCMD;

Unsigned _int16 TXLENGTH;

Unsigned _int16 ISQ;

Unsigned _int16 PAGEIX;

Unsigned _int16 PAGE0;

Unsigned _int16 PAGE1;

}CS8900A_REGS;

顯然,靜態全局指針變量g_Pcs8900所指向的CS8900A_REGSS結構體數據專門用於映射CS8900A的8個I/O端口,g_Pcs8900指針的實際取值就應該是這8個I/O端口的基地址。ReadPacketPage和WritePacketPage兩個函數的實現對CS8900A的PacketPage讀寫就是通過這個g_Pcs8900指針進行的。

2.硬件初始化函數CS8900AInit。CS8900AInit函數的功能是執行對CS8900A以太網控制器芯片的硬件初始化,並且設置其工作模式至一個確定的狀態。CS8900AInit函數在%_WINCEROOT%/PLATFORM/DEVICEEMULATOR/SRC/DRIVERS/ETHERNET/cs8900a.c源文件中實現,其函數定義:

BOOL CS8900AInit(UINT *pAddress, UINT32 offset, UNIT16 MAC[3]);

*pAddress指針參數記錄以太網控制器的I/O端口基地址。Offset成員則是偏移地址,在當前的CS8900AInit函數中它沒有被使用。Mac數組記錄以太網端口的48位MAC地址。

如果把對全局變量g_pCS8900賦值看作是CS8900AInit函數執行的第一步,則它的第二個執行步驟就是檢測CS8900A以太網控制器芯片是否在目標硬件平臺上真實存在:

If (ReadPacketPage(EISA_NUMBER)!=CS8900A_EISA_NUMBER)

{

OALMSGS(OAL_ERROR,(LERROR:CS8900AInit:Failed detect chip/r/n));

Goto Exit;

}

檢測CS8900A芯片是否存在的依據是讀取PacketPage中便宜地址爲0的產品ID寄存器,這是個只讀的寄存器。如果讀取EISA_NUMBER寄存器返回的16爲數值是0X630E,則CS8900A芯片存在,否則不存在,CS8900AInit函數中止執行,並且向它的調用者返回FALSE表示執行失敗。

CS8900AInit函數執行的第三個步驟就是通過軟件操作出發CS8900A芯片復位:

WritePacketPage(SELT_CTL,SELF_CTL_RESET);

接下來,CS8900AInt函數接下來執行兩個步驟:

1)等待CS8900A芯片軟件復位後完成芯片初始化;

2)等待CS8900A芯片外置的用於存放芯片初始化配置信息的EEPROM存儲器可被訪問;

接下來CS8900AInit函數要爲受CS8900A芯片控制的以太網端口設置MAC地址,方法是寫INDIVIDUAL_ADDRESS寄存器:

WritePacketPage(INDIVIDUAL_ADDRESS + 0,mac[0]);

WritePacketPage(INDIVIDUAL_ADDRESS + 0,mac[1]);

WritePacketPage(INDIVIDUAL_ADDRESS + 0,mac[2]);

CS8900AInit剩下的代碼就是配置CS8900A以太網控制器的收發數據幀的模式,分爲以下四個步驟:

1)配置允許CS8900A以太網控制器芯片接收的以太網數據幀類型:

WritePacketPage(RX_CTL,RX_CTL_RX_OK|RX_CTL_INDIVIDUAL|RX_CTL_BRODCAST);

2)配置CS8900A以太網控制器芯片以中斷方式接收數據幀:

WritePacketPage(RX_CFG,RX_CFG_RX_OK_IE);

3)配置CS8900A以太網控制器芯片選擇使用第0號中斷引腳;

WritePacketPage(INTERRUPT_NUMBER,0);

4)配置CS8900A以太網控制器芯片使之允許接收發送數據幀:

WritePacketPage(LINE_CTL,LINE_CTL_RX_OK|LINE_CTL_TX_ON);

3.發送以太網數據幀。CS8900ASendFrame函數的功能是向以太網發出一個數據幀。其函數原型:

UINT16 CS8900ASendFrame(UINT8 *pData, UINT32 length);

pData參數是指向待發送數據在主機中存放位置的地址指針,length參數記錄是以字節爲單位的待發送數據的長度。儘管函數的返回值類型定義爲UINT16,但是它實際返回的是知識函數執行是否成功的BOOL值。

CS8900ASendFrame函數按照以下步驟完成數據的發送任務:

1)寫發送命令和數據長度:

OUTPORT16(&g_pCS8900->TXCMD,TX_CMD_START_ALL);

OUTPORT16(&g_pCS8900->TXLENGTH,length);

這裏的宏定義TX_CMD_START_ALL的實際數值(3<<6),它把發送命令端口TxCMD的第6位和第7位(TTxStart)置1,其作用是限定只有當整個數據幀都被寫入CS8900A時纔開始向外部網絡發送數據。

2)檢測CS8900A芯片已準備好接收來自主機的待發送數據:

Count = RETRY_COUNT;

While(count-->0){

If((ReadPacketPage(BUS_ST) & BUS_ST_TX_RDY)!=0) break;

}

If (count ==0) goto cleanup;

總線狀態寄存器BusST(PacketPage內偏移地址爲0138H)的第8位(Rdy4TxNOW)用於CS8900A芯片向主機通知已準備好接收數據,該位置1表示CS8900A已準備好接收來自主機的待發送數據。以上代碼採用的是輪詢而非中斷的方式查詢CS8900A芯片是否準備就緒,另一個更好的辦法是,將緩衝配置寄存器BufCFG的第8位(Rdy4TxiE)置1,當CS8900A芯片準備好接收待發送數據時將向主機發出中斷信號。

3)將待發送的數據依次寫入CS8900A的數據端口:

Length = (length + 1)>>1;

While (length-- >0) {

OUTPORT16(&G_Pcs8900->DATA0, *(UINT16*)pData);

pData += sizeof(UINT16);

}

4.接收以太網數據幀CS8900AGetFrame函數。數據幀接收必定要涉及中斷,還很有可能要使用DMA操作將接收到的數據從CS8900A芯片搬移到主機中。網絡數據如果通過了CS8900A芯片的地址過濾器的篩選(單播地址或廣播地址的數據幀),則CS8900A開始接收數據。如果一個數據幀被CS8900A全部接收完畢,具有有效的以太網數據幀長度並且CRC校驗無錯誤,則CS8900A向主機觸發RxOK中斷,則主機通過CS8900A的數據端口將數據幀讀出。

CS8900AGetFrame函數的原型定義如下:

UINT16 CS8900AGetFrame(UINT8 *pData, UINT16 *pLength);

pData參數是主機用來存放接收到的數據幀的內緩衝區起始地址,pLength參數所指向的內存單元記錄接收緩衝區的以字節爲單位的長度。CS8900AGetFrame函數的返回值是以字節爲單位的實際接收到的數據的長度。

CS8900AGetFrame函數首先讀CS8900A的ISQ端口,並且判斷是否有RxOK中斷存在:

Isq = INPORT16(&g_Pcs8900->ISQ);

If ((isq & ISQ_ID_MASK) == RX_EVENT_ID && (isq & RX_EVENT_RX_OK)!=0{

……

}

按照CS8900A的中斷機制運行原理,當有中斷事件發生時,除反映在事件寄存器的對應位外,還要把該事件寄存器關聯到ISQ端口,然後觸發中斷引腳。此時,ISQ端口的最低6位記錄所關聯的寄存器的內部偏移地址,其餘爲是關聯寄存器的數據內容。CS8900A芯片共有5個寄存器可以關聯到它的ISQ端口,除3個時間寄存器(接收事件寄存器RxEvent、發送事件寄存器TxEvent和緩衝區事件寄存器BufEvent)外,另外兩個是接收幀丟失計數器RxMISS和發送衝突計數器TxCOL。所以CS8900AGetFrame函數檢測到RxOK中斷必須滿足兩個條件:接收事件寄存器RxEvent(PacketPage內偏移地址爲0124H)被關聯到ISQ端口,並且其中的RxOK位(第8位)被置1.

如果這兩個條件均滿足,則主機可以從CS8900A的數據端口讀取數據了。讀取數據的操作可以按以下的順序進行:首先是本次數據接收的狀態和以字節爲單位的數據總長度:

//Get RxStatus and length

Status = INPORT16(&G_Pcs8900->DATA0);

length = INPORT16(&G_Pcs8900->DATA0);

然後是由length指定字節數總長度的幀數據。

如果主機所提供的接收數據緩衝區不夠用,則終止數據接收操作並且指令CS8900A丟棄接收到的數據:將接收配置寄存器RxCFG的第6位(Skip_1)置位。這會將當前存在於CS8900A芯片的接收數據緩衝區內的數據幀全部丟棄:

If (length> *pLength) {

// if packet doesnt fit in buffer, skip it

Data = ReadPacketPage(RX_CFG);

WritePacketPage(RX_CFG, data |RX_CFG_SKIP_1);

Length = 0;

} else{

}

5.啓用與禁用CS8900A的中斷功能的函數CS8900AEnableInts和CS8900ADisableInts。在CS8900A中,總線控制器BusCTL(PacketPage內偏移地址爲0116H)的第15位(EnableIRQ)是否被置位即表示CS8900A是否會根據相應的事件產生中斷。

6.CS8900A的配置地址過濾機制的函數。CS8900ACurrentPacketFilter和CS8900AMulticastList。

4 網絡驅動的編譯與加載

在WinCE下,所有的驅動程序都以用戶態的DLL文件形式存在。當編寫完驅動的模塊化接口之後,我們就要將這個模塊編譯成*.DLL的動態庫,然後在編譯系統的時候將該DLL動態庫加載到系統內核裏去,這樣操作系統就可以在運行時動態的加載需要的應用程序。

下面以移植CS89O0網卡驅動爲實例,介紹如何將網絡設備驅動模塊加載到WinCE 的內核,編譯器爲Platform-Builder5.O(簡稱PB5.O)。其編譯、加載過程主要分爲六步:

(1)在硬件平臺BSP的DRIVERS目錄下創建新目錄CS89OO。

(2)修改DRIVERS下的DIRS文件,DIR下定義了編譯器需要編譯的內容,所以需要在DIRS文件中將CS8900目錄添加上。

(3)將編寫好網絡驅動源程序代碼拷貝到CS89OO目錄下。

(4)編寫網絡設備驅動的sources文件,告訴編譯器和連接器如何編譯及連接本驅動程序。這樣的sources文件在每個WinCE的驅動下面都有一個,是爲了給PB編譯驅動的時候提供編譯“嚮導”的。其關鍵內容爲:TARGETTYPE定義編譯該驅動成爲哪種形式,有DLL和Lib兩種形式;TARGETLIBS定義編譯該程序的時候需要連接的其他庫;SOURCES確定需要編譯的文件。本驅動被編譯成的形式爲DLL,鏈接了ndis,ntcompat,coredll,ceddk 4個靜態庫。

(5)編寫CS8900的註冊表文件,可參考WinCE自帶的驅動源碼ne2000網卡驅動的註冊表文件編寫CS8900的註冊表文件。註冊表定義了網卡的基本參數信息提供給操作系統,其中:Parms項提供網卡驅動在系統中的邏輯中斷號,讀寫基地址,總線類型;TcpIp項提供了網卡IP

地址信息等,如果要修改IP地址就在本註冊表項中修改。特別注意網卡的中斷的設置,在WinCE中,與外設對應的中斷在0AL層被定義,所以這裏的中斷號必須與0AL層設置的一致,否則網絡驅動將無法工作。

(6)將驅動編譯進系統內核,修改系統平臺初始化文件platform.bib。

經過上述步驟之後,重新編譯內核,將內核下載到嵌入式主板上,就可以看到類似Windows操作系統的網絡連接一樣標誌,說明網卡驅動已經被加載到內核。

 

 

    本文在介紹嵌入式WinCE網絡驅動架構的基礎上,參照WinCE提供的網絡驅動模型,詳細介紹了嵌入式WinCE以太網驅動程序的設計原理,並已經成功的移植了驅動程序,在嵌入式WinCE下穩定運行。本網絡接口已經用於脈衝發生器的遠程控制,運行穩定。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章