作為壹個新的項目,首先要列出開發的工作量,制定出合理的開發計劃,將整個項目細分為幾個小的項目,壹步壹步推進。下面介紹壹下工作內容。
這些內容中,首先要解決的是USB通信問題,由於硬件接口為USB總線接口,所以必須要打通USB通道。微軟windows系統中有專門的編程接口,我們只要將需要的功能封裝成URB(USB Request Block)請求包,通過調用USBD class driver接口,將URB包發到下壹層。最總由總線驅動與設備交互。
其次是開發NDIS miniport驅動框架。NDIS是微軟的網絡設備的開發庫,提供了許多和網絡設備相關的接口函數供調用。根據設計,本驅動為壹個miniport類型的驅動,所以需要開發標準的miniport接口供系統調用。和傳統的驅動壹樣,也是封裝壹系列的回調函數給系統,這些調用接口形式微軟的NDIS規範已經定義好了。
接下來就是對這些接口的完善。加入802.11協議內容,作為驅動的實體。由於intersil的芯片沒有集成802.11協議功能,所以我們需要在軟件中開發協議中的MAC層規定的內容。這部分也是我們驅動的主要內容。
802.11協議有三個主要狀態。根據協議規範只有在關聯的狀態下,才可以進行數據通信。認證,加密是安全相關的,理論上與協議沒有必然關系,802.11無線協議為了安全,將加密和認證也作為協議的壹部分,從而使得其具有等同有線網絡的安全級別。同時,802.11協議定義很多命令,如Probe request/response,Beacon,Authentication,Deauthentication,Association,Deassociation等,程序的主體是壹個狀態機,只要發送相應的命令就可以進行狀態切換,完成相應的功能。
協議中涉及到AP列表的搜索,認證過程,關聯過程,信道的切換等需要分別實現,分別是壹個更細節的狀態機。關於802.11協議詳細內容請參考相關的文檔。這裏不多說了。下圖1-2是802.11協議的狀態圖。
按照功能劃分:可分為管理、控制和數據3種不同類型幀。Class 1,Class 2&3 Frame的定義請參考802.11-1999。
1, Usb總線介紹
USB是Intel公司開發的通用串行總線架構,以簡單的設計,易用性,熱插拔特性受到了廣泛的歡迎,很多設備都開始支持USB規範。
壹個USB系統主要被定義為三個部分:USB的互連;USB的設備;USB的主機。
在任何USB系統中,只有壹個主機。USB和主機系統的接口稱作主機控制器,主機控制器可由硬件、固件和軟件綜合實現。根集線器是由主機系統整合的,用以提供更多的連接點。
圖2-1 總線的拓撲結構
圖2-1顯示了USB總線的拓撲結構。
標準USB規範有四根線,分別是電壓正負極,兩根數據線。外觀為扁平的方形接口。USB傳送信號和電源是通過壹種四線的電纜,圖2-2中的兩根線是用於發送信號。電纜中包括VBUS?、GND二條線,向設備提供電源 。VBUS?使用+5V電源。
USB總線屬壹種輪訊方式的總線,主機控制端口初始化所有的數據傳輸。在USB設備安裝後,主機通過設備控制通道激活該端口並以預設的地址值給USB設備。主機對每個設備指定唯壹的USB地址。
USB定義了壹些請求命令,所有的USB設備在設備的缺省控制通道(Default Control Pipe)處對主機的請求發出響應。這些請求是通過使用控制傳輸來達到的,請求及請求的參數通過Setup包發向設備,由主機負責設置Setup包內的每個域的值。每個Setup包有8個字節。見表2-1。
bmRequestType域
這個域表明此請求的特性。特別地,這個域表明了第二階段控制傳輸方向。如果wLength域被設作0的話,表明沒有數據傳送階段,那Direction位就會被忽略。
USB說明定義了壹系列所有設備必須支持的標準請求。這些請求被例舉在表8-3中。另外,壹個設備類可定義更多的請求。設備廠商也可定義設備支持的請求。
請求可被導引到設備,設備接口,或某壹個設備端結點(endpoint)上。這個請求域也指定了接收者。當指定的是接口或端結點(endpoint)時,wIndex域指出那個接口或端節點。
bRequest域
這個域標識特別的請求。bmRequestType域的Type啦可修改此域的含義。本說明僅定義Type 字位為0即標準設備請求時bRequest域值的含義。
wValue域
此域用來傳送當前請求的參數,隨請求不同而變。
wIndex域
wIndex域用來表明是哪壹個接口或端結點,圖2-3表明wIndex的格式(當標識端結點時)。Direction位在設為0時表示出結點,設為1時表示是入結點,Endpoint Number是結點號。圖2-4表明wIndex用於標識接口時的格式。
wLength域
這個域表明第二階段的數據傳輸長度。傳輸方向由bmRequstType域的Direction位指出。wLength域為0則表明無數據傳輸。在輸入請求下,設備返回的數據長度不應多於wLength,但可以少於。在輸出請求下,wLength指出主機發出的確切數據量。如果主機發送多於wLength的數據,設備做出的響應是無定義的。
表2-2描述了所有USB設備都定義的標準設備請求將它們列出。不管設備是否被分配了非缺省地址或設備當前是被配置了的,它們都應當對標準請求產生響應。具體請參考USB規範。
講到USB總線,就不得不講USB總線的傳輸方式。
壹個USB通道是設備上的壹個端點和主機上軟件之間的聯系。USB設備的設計者可以決定設備上每個端點的能力。壹旦為這個端點建立了壹個通道,這個通道的絕大多數傳送特征也就固定下來了,壹直到這個通道被取消為止。
USB定義了4種傳送類型:
·控制傳送:可靠的、非周期性的、由主機軟件發起的請求或者回應的傳送,通常用於命令事務和狀態事務。
·同步傳送:在主機與設備之間的周期性的、連續的通信,壹般用於傳送與時間相關的信息。這種類型保留了將時間概念包含於數據中的能力。但這並不意味著,傳送這樣數據的時間總是很重要的,即傳送並不壹定很緊急。
·中斷傳送:小規模數據的、低速的、固定延遲的傳送。
·批傳送:非周期性的,大包的可靠的傳送。典型地用於傳送那些可以利用任何帶寬的數據,而且這些數據當沒有可用帶寬時,可以容忍等待。
每壹個端點Endpoint都有自己的傳輸方式,通過配置描述符,主機就可以知道每壹個端點的傳輸方式,從而以該方式進行通信。
1, WDM框架介紹
WDM(windows driver model)是微軟對於驅動開發定義的壹整套規範。與原來的Vxd驅動相比,WDM更加封裝完好,並且可以對Windows各版本的做到二進制兼容,在壹個系統下完成了,可以很輕松的移植到其他windows系統下。本人有幸將windows xp下開發的驅動,移植到了windows 2000,window me和windows 98下。這是我很好的經驗。
根據Walter oney的觀點,設備驅動程序是壹個包含了許多操作系統可調用例程的容器,這些例程可以使硬件設備執行相應的動作。
WDM模型使用了層次結構,如圖3-1所示,左邊是壹個設備對象堆棧。設備對象是系統為幫助軟件管理硬件而創建的數據結構。壹個物理硬件可以有多個這樣的數據結構。出於堆棧最底層的設備對象稱為物理設備對象(physical device object),簡稱PDO。在設備對象堆棧的中間摸出有壹個對象稱為功能設備對象(function device object),簡稱FDO。在FDO的上面和下面還會有過濾器設備對象(filter device object),簡稱FiDO。
總線是個廣義的定義,包括PCI,SCSI卡,並行口,串行口,USB集線器(hub),等等。實際上它可以是任何能插入多個設備的硬件設備。總線驅動程序的壹個任務就是枚舉總線上的設備,並為每壹個設備創建壹個PDO。
我們的miniport其實也是壹個功能驅動程序。
1, 開發環境介紹
下面介紹壹下開發環境,windows驅動開發使用VC工具,也可以使用腳本直接編譯,關鍵是設置好環境變量,DDK庫的路徑設置好。編譯出的驅動有checked和free兩種,checked對應的debug版本,free對應release版本。
調試工具使用Softice,我使用的是2.7版本,這個工具非常強,可以看變量,內存,也可以看堆棧調用,很多棘手的問題手到擒來。
或者壹般也可以通過debugview工具調試,需要的程序的關鍵路徑上打印變量即可。當程序比較穩定了,就可以使用debugview觀察邏輯上的問題,這樣就可以比較快的定位,然後再使用softice進行細節調試。
2, WDM模型和USB底層的實現
微軟windows實現了usb總線的驅動,和壹部分USB類驅動,通過URB封裝好以後,把URB包發送給下壹層的驅動,最終由總線驅動與設備通信。
WDM模型提供了USB的編程架構。這個架構包含了多個概念,包括把設備夫做到計算機上的層次方法,電源管理的通用方案,硬件的多層次描述符的自識別標準。USB標準把定時幀(frame)分解成數據包(packet),從而實現設備和主機之間的數據傳輸。最後,在主機和設備上的端點之間,USB支持四種數據傳輸模式。壹種模式稱為等時傳輸(isochronous),它可以每個壹毫秒傳輸固定量無錯誤校驗的數據。其他模式為:控制傳輸,批量傳輸和中段傳輸,它們僅能傳輸少量(64字節或更少)帶有錯誤校驗的數據。
USB驅動高度依賴於總線驅動程序(USBD.sys),而不直接使用HAL函數與硬件通信。它僅靠創建URB(USB請求塊)並把URB提交到總線驅動程序就可以完成硬件操作。USBD.sys可以理解為接受URB的實體,向USBD的調用被轉化為帶有主功能代碼為IRP_MJ_INTERNAL_DEVICE_CONTROL的IRP。然後USBD再調度總線時間,發出URB中指定的操作。
壹般我們可以按照如下的方法建立並提交壹個URB給USBD驅動。
如當相應IRP_START_DEVICE消息時,首先需要讀取設備描述符,然後
URB urb;
USB_DEVICE_DESCRIPTOR deviceDesc;
UsbBuildGetDescriptorRequest
(
&urb,
(USHORT) sizeof (struct _URB_CONTROL_DESCRIPTOR_REQUEST),
USB_DEVICE_DESCRIPTOR_TYPE,
0,
0,
&deviceDesc,
NULL,
sizeof(USB_DEVICE_DESCRIPTOR),
NULL
);
UsbBuildGetDescriptorRequest其實是壹個宏,在USBDLIB.H中聲明,用於生成讀描述符請求子結構各個域。
創建完URB後,需要發壹個內部IO控制(IOCTL)請求到USBD驅動程序,USBD驅動程序位於驅動程序層次結構的低端,壹般需要等待設備回應。
NTSTATUS MPUSB_CallUSBD
(
IN PDEVICE_OBJECT DeviceObject,
IN PURB Urb
)
{
KEVENT event;
IO_STATUS_BLOCK ioStatus;
PDEVICE_EXTENSION pDevExt;
pDevExt = DeviceObject->DeviceExtension;
// issue a synchronous request to read the UTB
KeInitializeEvent(&event, NotificationEvent, FALSE);
PIRP irp = IoBuildDeviceIoControlRequest
(
IOCTL_INTERNAL_USB_SUBMIT_URB,
pDevExt -> StackDeviceObject,
NULL,
0,
NULL,
0,
TRUE, /* INTERNAL */
&event,
&ioStatus
);
// Call the class driver to perform the operation. If the returned status
// is PENDING, wait for the request to complete.
PIO_STACK_LOCATION nextStack = IoGetNextIrpStackLocation( irp );
nextStack->Parameters.Others.Argument1 = Urb;
NTSTATUS ntStatus = IoCallDriver( pDevExt -> StackDeviceObject, irp );
if (ntStatus == STATUS_PENDING)
{
ntStatus = KeWaitForSingleObject
(
&event,
Executive,
KernelMode,
FALSE,
NULL
);
}
// USBD maps the error code for us
ntStatus = ioStatus.Status;
return ntStatus;
}
這樣我們就可以通過USB總線與設備通信了。
當然這個過程比較復雜,需要讀取並配置設備描述符,配置描述符,接口描述符和端點描述符,這些描述符在USB協議規範中有講述,在相應的USB類規範中也有詳細的描述。
壹般配置描述符格式如下,
這個示例表明這個設備有兩個接口,對壹個接口有兩個端點,第二個接口也有兩個端點。每壹個接口可以是壹個獨立的功能,這個時候設備就是復符合設備,在配置描述符中就需要聲明這是個復合設備。而每壹個接口也需要分別聲明他的類型。
比如本人曾經有壹個項目中有三個接口,分別是modem,普通串口,USB mass storage。這三個接口都需要分別聲明他的類型,這些類型在USB的類規範中有詳細的定義。
壹般由於配置描述符的長度是不定的,所以我們需要先獲取這個長字符串的前面9個字節,即配置描述符(見規範)。格式如下,
typedef struct
{
byte length; // bLength: size in bytes
byte descriptor_type; // bDescriptorType
word total_length; // wTotalLength: in bytes
byte num_interfaces; // bNumInterfaces
byte configuration; // bConfigurationValue
byte config_index; // iConfiguration
byte attributes; // bmAttributes
byte max_power; // MaxPower: in 2mA units
} usbdc_configuration_descriptor_type;
這樣,在這個字符串的第三、四字節會指明整個字符串的長度,然後再讀取壹次該長度,就可以完整地得到整個配置字符串。
1, NDIS模型和帶有WDM底層的miniport驅動
NDIS(Network Device Interface Specification)是微軟的網絡設備的開發庫。但是由於本設備是壹個USB設備,需要通過WDM模型訪問。故而我們在設計時,考慮為壹個NDIS miniport上層接口,帶有壹個WDM訪問接口的驅動,很幸運,微軟支持這樣的設計。
根據設計,本驅動為壹個miniport類型的驅動,所以需要開發標準的miniport接口供系統調用。和傳統的驅動壹樣,也是封裝狀壹系列的調用函數給系統,這些調用接口形式微軟的NDIS規範已經定義好了。入口函數為DriverEntry,
入口函數的功能是系統調用驅動的第壹個函數,它負責初始化驅動,註冊回調函數(call back)。這些函數包括如下,通過壹個結構完成。
首先必須調用如下的函數,使得miniport驅動和NDIS相關聯,通過調用這個函數存儲miniport驅動的信息。返回的句柄NdisWrapperHandle,用於後面的回調函數註冊。NDIS也通過NdisWrapperHandle來區分不同的驅動。
NdisMInitializeWrapper(
&NdisWrapperHandle,
DriverObject,
RegistryPath,
NULL
);
註冊回調函數方法如下,註冊結構類型為NDIS_MINIPORT_CHARACTERISTICS。
// and the entry points for driver-supplied MiniportXxx
NdisZeroMemory(&MPChar, sizeof(MPChar));
//
// The NDIS version number, in addition to being included in
// NDIS_MINIPORT_CHARACTERISTICS, must also be specified when the
// miniport driver source code is compiled.
//
MPChar.MajorNdisVersion = MP_NDIS_MAJOR_VERSION;
MPChar.MinorNdisVersion = MP_NDIS_MINOR_VERSION;
MPChar.InitializeHandler = MPInitialize;
MPChar.HaltHandler = MPHalt;
MPChar.SetInformationHandler = MPSetInformation;
MPChar.QueryInformationHandler = MPQueryInformation;
MPChar.SendPacketsHandler = MiniportTxPackets;
MPChar.ReturnPacketHandler = MiniportReturnPacket;
MPChar.ResetHandler = MPReset;
MPChar.CheckForHangHandler = MPCheckForHang; //optional
#ifdef NDIS51_MINIPORT
// MPChar.CancelSendPacketsHandler = MPCancelSendPackets;
MPChar.PnPEventNotifyHandler = MPPnPEventNotify;
MPChar.AdapterShutdownHandler = MPShutdown;
#endif
NdisMRegisterMiniport(
NdisWrapperHandle,
&MPChar,
sizeof(NDIS_MINIPORT_CHARACTERISTICS));
當註冊好了後,系統會檢測NDIS版本號,然後調用初始化接口,MPChar.InitializeHandler = MPInitialize; 初始化驅動的其他內容,如變量的初始化,註冊表的讀寫,事件/互斥信號量的初始化等。
如果初始化完成,那麽驅動就可以正常工作了。壹般通過如下接口:
MPChar.SetInformationHandler = MPSetInformation;
MPChar.QueryInformationHandler = MPQueryInformation;
進行屬性的獲取和設置。通過如下接口:
MPChar.SendPacketsHandler = MiniportTxPackets;
MPChar.ReturnPacketHandler = MiniportReturnPacket;
進行數據的收發。
現在可以看到,NDIS和傳統的驅動沒有什麽區別,但是作為網絡的接口,它更加穩定,更加標準化,是開發更加簡單。
1, 802.11協議的實現
1996年,ETSI提出了HiperLAN的無線局域網協議,1998年日本出現HomeRFSWAP。最近幾年流行的Bluetooth(嚴格的說, Bluetooth不屬於無線局域網,而是無線PAN(Personal Area Net-work) ,應該算作無線局域網的壹個子集。1997年通過的IEEE 802.11是第壹種無線以太網標準,已經成為無線局域網的代名詞。1999年的修訂版是當前的標準主要參考。
802.11包括MAC(Media Access Control)層和物理層
3種不同物理層:–DSSS or Direct Sequence Spread Spectrum
–FHSS or Frequency Hopping Spread Spetrum
–紅線(IR or Infrared)
所以MAC層可以同時支持3種不同的物理層基本服務集(basic service set or BSS)
無線局域網的最小單元,由2個或多個移動臺Wireless station or STA)構成 壹個基本服務集(BSS)內的移動臺(STA)間可以直接通信; 壹個基本服務集(BSS)實際上是壹個自組織網絡(ad hoc),在802.11中稱成IBSS(independent BSS) STA到BSS的連接是動態的,自發的(non-preplanned)擴展服務集(Extended service set or ESS)
AP+BSS可以組成任意大的無線局域網,稱作擴展服務集網絡,它的所有組成部分合稱擴展服務集(ESS)
Infrastructure BSS:非IBSS都稱為Infrastructure BSS
802.11定義了9種服務類型:移動臺服務(Station Service or SS)有四種:
1.鑒別(Authentication)=who are you?
2.取消鑒別(Deauthentication)=I am about to leave, Stop communicating with me please
3.加密(privacy):數據怎能讓別人偷聽(Eavesdrop)
4.MSDU發送(MSDU delivery)
(MSDU是什麽?MAC service data unit; SDU是來自(OSI)上壹層的協議數據單元PDU,它定義了對下層協議的服務請求;而PDU協議數據單元(Protocol Data Unit)指對等層水平方向傳送的數據單元)
分布系統服務(Station Service or DSS)
要提供全部9種服務:
前4種:鑒別、取消鑒別、加密、MSDU發送 後5種:5.關聯(Association):它是壹個DSS(Distribution system service),STA如果希望將消息傳輸給其他BSS的STA,首先要與所在BSS的AP實現關聯。
6.取消關聯(Disassocation):關聯的反過程
7.分配(Distribution):將BSS1的STA1的數據(也可以說是消息)經過DS傳送到BSS2的STA2,即跨DS的數據傳送功能
8.集成(Integration):完成與有線網之間互通
9.再關聯(Reassociation):當STA從BSS1移動到BSS2時,要與BSS的AP實現再關聯,以支持跨BSS消息傳送
按照功能劃分:可分為管理、控制和數據3種不同類型幀
所有MAC幀由壹下3部分構成:
幀頭(MAC header) 可變長幀體(Frame body):與幀類型有關 校驗序列(frame check sequence or FCS):CRC-32SSID說白了就是IBSS或ESS的壹個32字節的壹個標識,壹個字節對應壹個字符,所有可以用32個以下的字符來表示某個公司或其他組織的無線網絡,類似於有線網絡的工作組名字,比如我們組就叫Eda(其它字節為0)
鑒別服務分為兩種:
開放系統(Open System)和***享密鑰(Shared Key)
開放系統:如果dot11AuthenticationType被置位為1,則為“開放系統鑒別”,要建立鑒別,***需要2幀就可完成任務:鑒別請求,鑒別應答
***享密鑰:***享密鑰鑒別需要4幀。首先requester發出請求幀,然後responder發送質問(challenge)文本,該文本是WEP PRNG生產的128字節隨機數,然後requester對收到的質問(challenge)文本進行WEP算法加密,responder將返回的文本解密得到相應質問文本,如果雙方(responder和requester)所用密鑰相同,則responder將返回的文本解密得到原始質問文本,鑒別成功
WEP加密已經被認為是不安全的算法。所以WiFi有提出了更高安全級別的算法,如WAP等。
MAC層功能:
1.點協調功能(PCF)
2.分布協調功能(DCF)
3.分段(Fragmentation)
4.去分段(defragmentation)
5.MSDU重新排序和丟棄
長度大於aFramentationThreshold的MSDU和
MMPDU(MAC管理協議數據單元)都要分段,
以提高傳輸成功率分段後的數據長度為某壹固
定偶數(不包括最後壹段)。
分段後的數據可以進行突發傳送。接收端
依靠Sequence和MoreFragments 域對數據進行去分段
分布控制方式(DCF):多個STA爭用無線信道,即所謂CSMA/CA方式(不同於CSMA/CD) 點控制方式(PCF): AP作為控制中心,控制所有STA對信道的使用。AP就是所謂Point coordinator, PCF是基於DCF的。主要用於實時業務,在802.11中是可選的。隨機避退時間
BackoffTime = Random()×aSlotTime
Random()--返回在[0, CW]之間均勻分布的整數
aSlotTime--物理層特性決定的CW的最小單位或稱CW的切片
CW--contention window。在[aCMin,aCMax] 之間取值。第壹次發送時值為aCMin,以
後每重傳壹次倍增,達到aCMax後維持aCMax。成功發送後復位到aCMin。
檢測到信道忙,則延遲(defer)
檢測到空閑且經過壹個IFS,再進行
避退(Backoff)。避退時間過後如果
信道空閑,就開始發送幀
信標幀(Beacons)