摘要:為了在Windows下實(shí)現(xiàn)USB網(wǎng)卡驅(qū)動(dòng)程序,本文根據(jù)NDIS_WDM驅(qū)動(dòng)模型,分析了NDIS_WDM模型的內(nèi)在運(yùn)行機(jī)制,提出了一個(gè)USB網(wǎng)卡驅(qū)動(dòng)的設(shè)計(jì)方案,并重點(diǎn)討論了NDIS和USB設(shè)備棧的交互過程,圍繞這個(gè)過程,使用Windows DDK 2003開發(fā)包及VC開發(fā)工具,寫出了USB網(wǎng)卡驅(qū)動(dòng)程序的初始化和發(fā)送接收過程的關(guān)鍵代碼。實(shí)現(xiàn)了對(duì)CY3681評(píng)估板的發(fā)送接收操作。
關(guān)鍵詞:WDM;NDIS;WDM-NDIS;usb類驅(qū)動(dòng);網(wǎng)卡驅(qū)動(dòng)
1 引言
USB(Universal Serial Bus)即通用串行總線,正在成為各種新型設(shè)備的標(biāo)準(zhǔn)總線,USB的傳輸速度已由USB1.0的12Mbps提高到了USB2.0的480Mbps,同時(shí)USB設(shè)備具有即插即用的特點(diǎn),所以它越來越受到業(yè)界的重視。
基于PCI總線的網(wǎng)卡一般符合NDIS體系結(jié)構(gòu),在臺(tái)式機(jī)上應(yīng)用廣泛。隨著計(jì)算機(jī)的微型化發(fā)展,要求設(shè)備具即插即用的特點(diǎn),這種傳統(tǒng)PCI的網(wǎng)卡已不能適應(yīng)新的需要,因此開發(fā)一種基于USB的網(wǎng)卡便有重大的意義。這種網(wǎng)卡驅(qū)動(dòng)程序遵循NDIS_WDMW體系結(jié)構(gòu)。它結(jié)合了NDIS(Network Driver Interface Specification)和WDM(Win32 Driver Model)兩種體系結(jié)構(gòu)的特點(diǎn)。本文將探討NDIS_WDM驅(qū)動(dòng)程序體系結(jié)構(gòu)特點(diǎn),及USB網(wǎng)卡驅(qū)動(dòng)實(shí)現(xiàn)的有關(guān)問題。
2 基于WDM-NDIS體系的USB網(wǎng)卡的設(shè)計(jì)原理
2.1 系統(tǒng)結(jié)構(gòu)
NDIS體系結(jié)構(gòu)獨(dú)立于硬件,用戶可以將不同的協(xié)議和不同的網(wǎng)卡綁定進(jìn)而完成不同的功能。使網(wǎng)絡(luò)協(xié)議更具有通用性[3]。
NDIS支持三種類型的驅(qū)動(dòng)程序,從下往上向看,他們分別是微端口驅(qū)動(dòng)程序、中間層驅(qū)動(dòng)程序和協(xié)議驅(qū)動(dòng)程序。傳統(tǒng)的PCI網(wǎng)卡驅(qū)動(dòng)程序位于NDIS的第三層,屬于微端口驅(qū)動(dòng)程序(Miniport Driver) ,它有兩個(gè)基本功能:
管理一個(gè)網(wǎng)絡(luò)接口卡NIC(Network Interface Card),包括通過NIC發(fā)送和接收數(shù)據(jù)。與高層驅(qū)動(dòng)程序相接[1]。
但是基于USB的網(wǎng)卡驅(qū)動(dòng)程序和傳統(tǒng)PCI網(wǎng)卡驅(qū)動(dòng)最大的差別是NDIS下層的基礎(chǔ)不同。PCI網(wǎng)卡驅(qū)動(dòng)的下層是硬件抽象層HAL,USB網(wǎng)卡驅(qū)動(dòng)的下層是USB設(shè)備棧。如圖1所示:
基于USB的網(wǎng)卡驅(qū)動(dòng)向上要和NDIS體系結(jié)構(gòu)交互,本身屬于NDIS的一部分,也要完成微端口驅(qū)動(dòng)程序的收發(fā)數(shù)據(jù)功能。但是它不通過HAL而通過符合WDM模型的USB類驅(qū)動(dòng)程序的接口完成,為此,基于USB網(wǎng)卡驅(qū)動(dòng)必須自己構(gòu)造USB請(qǐng)求包URB(Usb Requet Block)請(qǐng)求下層USB設(shè)備棧完成數(shù)據(jù)收發(fā)功能。這種驅(qū)動(dòng)程序和傳統(tǒng)的NDIS的微端口驅(qū)動(dòng)程序有異有同,和WDM下的USB棧中的客戶驅(qū)動(dòng)程序也有異同。
2.2 收發(fā)數(shù)據(jù)過程
每當(dāng)NDIS上層協(xié)議或中間驅(qū)動(dòng)請(qǐng)求發(fā)送數(shù)據(jù)時(shí),NDIS_WDM微端口驅(qū)動(dòng)程序的收發(fā)函數(shù)將被調(diào)用,因?yàn)樗聦邮荱SB設(shè)備棧,所以不能直接調(diào)用NDIS庫里的中斷或DMA操作硬件。而是重新在內(nèi)核的不分頁內(nèi)存中重建一個(gè)請(qǐng)求包URB,把上層的請(qǐng)求包轉(zhuǎn)化為URB, 下傳給USB設(shè)備棧,然后把包標(biāo)志為pending,等待下層USB設(shè)備完成相關(guān)的操作后返回,此時(shí),NDIS_WDM驅(qū)動(dòng)程序收到下層的結(jié)果后再把原來標(biāo)志為Pending 的包重新處理,回收在內(nèi)存中分配的URB空間[3]。返回上層程序。如圖2所示:
3 基于WDM-NDIS體系的USB網(wǎng)卡設(shè)計(jì)的實(shí)現(xiàn)
3.1 網(wǎng)卡初始化過程
NDIS_WDM驅(qū)動(dòng)程序完成對(duì)驅(qū)動(dòng)程序?qū)ο蟮淖?cè)后,系統(tǒng)探測(cè)到硬件設(shè)備后,將對(duì)網(wǎng)卡進(jìn)行初始化。通常初始化主要完成網(wǎng)卡的注冊(cè)及發(fā)送和接收資源的分配,NDIS_WDM還要探索USB設(shè)備及配置USB設(shè)備。
3.1.1 注冊(cè)自定義網(wǎng)卡對(duì)象
由于NDIS_WDM Miniport Driver要將上層的協(xié)議驅(qū)動(dòng)程序或是中間驅(qū)動(dòng)程序的請(qǐng)求包轉(zhuǎn)交給下層的USB設(shè)備棧完成,因此,在自定義的網(wǎng)卡對(duì)象中除了有通常網(wǎng)卡對(duì)象中的接收鏈表,發(fā)送鏈表,網(wǎng)卡地址信息,和狀態(tài)統(tǒng)計(jì)信息外,還必須有如下關(guān)鍵結(jié)構(gòu):
PIRP StatusIndicationIrpNDIS_WDM Miniport Driver在不分頁內(nèi)存中分配的URB請(qǐng)求包,表示當(dāng)前驅(qū)動(dòng)程序正要處理或正在處理的請(qǐng)求。
DEVICE_OBJECT TargetDeviceObject表示在整個(gè)設(shè)備棧體系中NDIS_WDM Miniport 設(shè)備對(duì)象的下層第一個(gè)設(shè)備對(duì)象。當(dāng)NDIS_WDM 驅(qū)動(dòng)要要完成收發(fā)數(shù)據(jù)時(shí),構(gòu)造一個(gè)StatusIndicationIrp下傳給它。
除了這兩個(gè)結(jié)構(gòu)外,網(wǎng)卡對(duì)象還包括其他信息,具體如下:
typedef struct _MP_ADAPTER
{
網(wǎng)址名稱及地址信息
發(fā)送隊(duì)資源及操作信息
接收隊(duì)資源及操作信息
PIRP StatusIndicationIrp
DEVICE_OBJECT TargetDeviceObject
網(wǎng)卡狀態(tài)統(tǒng)計(jì)信息
}
通過StatusIndicationIrp TargetDeviceObject 這兩個(gè)數(shù)據(jù)結(jié)構(gòu)完成了NDIS和USB設(shè)備棧的連接。網(wǎng)卡可以通過StatusIndicationIrp保存當(dāng)前處理的IRP(IO Request Packet),并且能跟蹤IRP的去向。
3.1.2 自定義請(qǐng)求轉(zhuǎn)換函數(shù)
正如圖2所示,上層的驅(qū)動(dòng)程序請(qǐng)求不能直接下傳給下層USB設(shè)備棧,必須要把上層的NDIS請(qǐng)求轉(zhuǎn)化為IRP請(qǐng)求,下去才能接收并處理。NDIS請(qǐng)求中包括如下信息:請(qǐng)求的操作類型,輸入緩沖區(qū),輸出緩沖區(qū),NDIS_WDM驅(qū)動(dòng)程序必須把這些信息轉(zhuǎn)化為IRP請(qǐng)求包發(fā)送到下層的USB設(shè)備棧中,并報(bào)告下下層設(shè)備的執(zhí)行結(jié)果。據(jù)此自定義一個(gè)轉(zhuǎn)換函數(shù)。
NICMakeSynchronousIoctl
(
INPDEVICE_OBJECT TopOfDeviceStack,INPFILE_OBJECT FileObject,IN ULONG IoctlControlCode,INOUT PVOIDInputBuffer, INULONG InputBufferLength, IN OUT PVOID OutputBuffer,INULONG OutputBufferLength,OUT PULONG BytesReadOrWritten
)
其中,TopOfDeviceStack指明下層的第一個(gè)設(shè)備棧,BytesReadOrWritten 返回下層的執(zhí)行結(jié)果。
該函數(shù)的實(shí)現(xiàn)過程的關(guān)鍵例程如下:
調(diào)用IoAllocateIrp在內(nèi)核的不分頁內(nèi)存之中分配一個(gè)空白的IRP請(qǐng)求包。
利用NICMakeSynchronousIoctl的參數(shù)對(duì)剛創(chuàng)建的IRP初始化
設(shè)置完成例程IoSetCompletionRoutine,這個(gè)完成例程在下層設(shè)備完成IRP后捕捉它,以完成報(bào)告執(zhí)行結(jié)果及回收IRP的工作。
把初始化后的IRP下傳到下層設(shè)備棧 IoCallDriver(TopOfDeviceStack, irp)
等待下層設(shè)備棧完成,調(diào)用KeWaitForSingleObject函數(shù),與下層設(shè)備棧同步。
這個(gè)自定義函數(shù)很重要,上層的每個(gè)一個(gè)請(qǐng)求最終都會(huì)調(diào)用它來完成。
3.1.3探索下層的設(shè)備棧接口
NDIS_WDM微端口驅(qū)動(dòng)程序必須要知道其下層的類驅(qū)動(dòng)程序接口(USBI),才能確定IRP的去向,因此在初始化函數(shù)之中,必須找到下層的USB設(shè)備棧接口(USBDI)。為此調(diào)用一個(gè)關(guān)鍵的函數(shù):
NdisMGetDeviceProperty(
MiniportAdapterHandle,Adapter->Pdo,Adapter->Fdo,
Adapter->NextDeviceObject, NULL,NULL
);
第三個(gè)參數(shù)Adapter->NextDeviceObject返回的就是下層的USB設(shè)備對(duì)象,用Adapter->TargetDeviceObject指向這個(gè)對(duì)象。作為以后IRP的去向。
3.1.4配置USB設(shè)備
USB設(shè)備對(duì)象有多個(gè)配置,每一個(gè)配置有多個(gè)接口,每一個(gè)接口又有多個(gè)端點(diǎn),每一個(gè)端點(diǎn)又有控制傳輸、批量傳輸、中斷傳輸、等時(shí)傳輸方式[4]。 因此在上層的NDIS_WDM Miniport 微端口驅(qū)動(dòng)程序和下層的USB設(shè)備交互前,也就是初始化函數(shù)中,必須要對(duì)USB設(shè)備進(jìn)行適當(dāng)?shù)呐渲谩?/p>
其關(guān)鍵過程如下:
1、 獲得設(shè)備并設(shè)置設(shè)備描述符
ReadandSelectDescriptors(
IN PDEVICE_OBJECT DeviceObject)
2、讀取第一個(gè)配置描述符
ConfigureDevice(
IN PDEVICE_OBJECT DeviceObject)
3、設(shè)置配置描述符
SelectInterfaces(
IN PDEVICE_OBJECT DeviceObject,
IN PUSB_CONFIGURATION_DESCRIPTOR
onfigurationDescriptor)
這三個(gè)函數(shù)的實(shí)現(xiàn)過程大體相同,都是構(gòu)造URB,但使用不同的構(gòu)造函數(shù)構(gòu)建的USR。UsbBuildGetDescriptorRequest創(chuàng)造一個(gè)獲得配置描述符的USR,USBD_CreateConfigurationRequestEx創(chuàng)建一個(gè)配置設(shè)備描述符的USR,將這些不同的URB通過自定義函數(shù)NICMakeSynchronousIoctl下傳到設(shè)備棧。以完成對(duì)USB設(shè)備的配置。
3.2接收/發(fā)送函數(shù)
在ndis網(wǎng)卡驅(qū)動(dòng)程序中,發(fā)送數(shù)據(jù)包過程是驅(qū)動(dòng)程序通過調(diào)用發(fā)送函數(shù)完成,而接收過程是通過中斷方式完成,兩者差別很大,但在NDIS_WDM驅(qū)動(dòng)程序的下層是USB設(shè)備棧,不能使用中斷,發(fā)送和接收過程都是通過向下層的USB設(shè)備棧發(fā)送URP來完成[5]。兩都的處理過程原理相同。只是發(fā)送和接收過程中URP的位域設(shè)置不同。
其關(guān)鍵過程如下:
UsbBuildInterruptOrBulkTransferRequest(
urb,sizeof(struct(_URB_BULK_OR_INTERRUPT_T RANSFER),
pipeInformation->PipeHandle, NULL, mdl,, stageLength,
urbFlags, NULL
)
該函數(shù)作用是在非分頁內(nèi)存中構(gòu)造一個(gè)_URB_BULK_OR_INTERRUPT_TANSFER類型的URB。
填充URP的各個(gè)域。
IoSetCompletionRoutine為URP設(shè)置完成例程,待URP處理完成以后繼續(xù)處理這個(gè)請(qǐng)求。
IoCallDriver(TargetDeviceObject, URB)把構(gòu)建的帶有收發(fā)請(qǐng)求的URB通過USBDI下傳給USB設(shè)備棧。并等待這個(gè)USR請(qǐng)求的完成。
4 實(shí)驗(yàn)結(jié)果
由于網(wǎng)卡由硬件和軟件組成,本工程采用符USB2.0標(biāo)準(zhǔn)的CY3681評(píng)估板作為是硬件測(cè)試工具。使用winpcap作為網(wǎng)絡(luò)軟件測(cè)試工具。
pcap_open在數(shù)據(jù)鏈路層上打開USB網(wǎng)絡(luò)接口,pcap_sendpacket向打開的網(wǎng)口發(fā)送數(shù)據(jù)包,以完成對(duì)CY3681評(píng)估板進(jìn)行發(fā)送操作。發(fā)送速度可達(dá)35.8M。
結(jié)束語
本文采用與傳統(tǒng)PCI網(wǎng)卡驅(qū)動(dòng)設(shè)計(jì)相對(duì)比的方法,介紹了NDIS_WDM的結(jié)構(gòu)特點(diǎn),并提出一個(gè)USB網(wǎng)卡驅(qū)動(dòng)程序運(yùn)行機(jī)制和它的設(shè)計(jì)方案,寫出了實(shí)現(xiàn)這個(gè)網(wǎng)卡的關(guān)鍵代碼。但是它不是一個(gè)完整的網(wǎng)卡設(shè)計(jì)方案,沒有涉及到硬件部分,還希望讀者能夠完善它。
參考文獻(xiàn)
[1] 陳向群、馬洪兵,Windows 內(nèi)核實(shí)驗(yàn)教程[M],第一版 北京:機(jī)械工業(yè)出版社。2002
[2] Windows IFS Kit and DDK 3790 Document[M], Microsoft Corporatino,20003
[3] Chris Cant .Windows WDM設(shè)備驅(qū)動(dòng)開發(fā)指南[M]. 北京:機(jī)械工業(yè)出版社2001
[4] Art Baker1W indow s 2000 設(shè)備驅(qū)動(dòng)程序設(shè)計(jì)指南[M ]. 第2 版. 北京: 機(jī)械工業(yè)出版社, 2001
[5] Walter Oney. Programming the Windows DriverModel [M ] . Microsoft Press, 19991