PDF全文下載地址:http://download.csdn.net/source/2320280
http://bbs.driverdevelop.com/read.php?tid-120461.html
《USB 軟件結(jié)構(gòu) 》
軟件結(jié)構(gòu)比硬件來的復(fù)雜很多。因為它包含了許多從表面上看不到的層次。比如總線驅(qū)動、功能驅(qū)動、過濾驅(qū)動等。套用社會學(xué)的話,這體現(xiàn)了功能應(yīng)用中的分工和統(tǒng)籌。下面我們逐層來看它們。
總線驅(qū)動
總線驅(qū)動位于驅(qū)動棧的最低層,處理復(fù)雜的任務(wù),必須資源分配,子設(shè)備管理。作為下層驅(qū)動,負(fù)責(zé)處理上層驅(qū)動發(fā)下來的請求。 USB 設(shè)備中的總線驅(qū)動主要有兩類:控制器驅(qū)動、 HUB 驅(qū)動;另外還有一個端口驅(qū)動。
1 ) 控制器驅(qū)動: Ushohci.sys 、 Usbuhci.sys 、 Usbehci.sys 。
首先解釋一下 HCI ,它是主機控制接口( Host Control Interface )的縮寫。前后一共有三種 HCI 協(xié)議出現(xiàn): USB 1.1 時代,有 OHCI (開發(fā) HCI )協(xié)議和 UHCI (通用 HCI )協(xié)議; USB2.0 時代,有 EHCI (擴展 HCI )協(xié)議。三個協(xié)議分別對應(yīng)了上面的三個驅(qū)動程序。因為 USB 是向后兼容的,所以 UsbEHC.sys 中也包含了 UsbOhci 和 UsbUhci 的功能。請看下圖。
圖 1 總線設(shè)備
上圖中設(shè)備 1 、 3 是控制器設(shè),從名稱上就可以區(qū)別它們的不同: Universal Host Controller 和 Enhanced Host Controller 。所以設(shè)備 1 的驅(qū)動程序是 USBUhci.sys ,設(shè)備 3 的驅(qū)動程序是 USBEhci.sys 。
這說明同一臺主機特別是筆記本電腦中, 1.1 和 2.0 的控制器并存,像筆記本電腦中的鍵盤通過內(nèi)置 USB 接口與系統(tǒng)連接,其數(shù)據(jù)吞吐量小,只需要 1.1 的控制器就能滿足;而暴露在外供用戶使用的接口則需要 2.0 。
2) Hub 驅(qū)動: UsbHub.sys 。 Hub 驅(qū)動是所有 USB 設(shè)備的父驅(qū)動。下圖描繪了這一景況:
圖2
上圖中看到, Hub 驅(qū)動的子設(shè)備要不是獨立設(shè)備,如左側(cè)圖;要么是一個含有多個子設(shè)備的父設(shè)備,如右側(cè)圖。 Hub 驅(qū)動只為直系子設(shè)備創(chuàng)建唯一的物理設(shè)備對象。上圖中, Hub 驅(qū)動為設(shè)備 1 和設(shè)備 2 創(chuàng)建物理設(shè)備對象,但并不為設(shè)備 2 的子設(shè)備創(chuàng)建物理設(shè)備對象。
3 ) Port 驅(qū)動: UsbPort.sys 。這是個框架驅(qū)動,比較復(fù)雜,很少人會用到。而上面的 Ushohci.sys 、 Usbuhci.sys 、 Usbehci.sys 其實都是他的微端口驅(qū)動。對于這么偏門的框架,不提也罷。
系統(tǒng)類驅(qū)動
所以出現(xiàn)類驅(qū)動,體現(xiàn)了 USB 總線在應(yīng)用上的繁榮景象。只有用得多了,才有被歸類的可能。就像人類社會中有幾百個國家,幾千個民族,正是體現(xiàn)了人類這個團體的多樣性與繁榮。只有在“多”的基礎(chǔ)上,分類才是有必要的;少數(shù)人的小群體,再怎么獨立特行,也都不足以被分類,甚至定義為“ XX 民族”。
USB 設(shè)備包含很多的通用的功能類,比如: USB 集線器設(shè)備, USB HID 設(shè)備, USB 音頻設(shè)備, USB MIDI 設(shè)備, USB 存儲設(shè)備。為了讓開發(fā)工作變得更加簡單, Windows 操作系統(tǒng)為他們提供了系統(tǒng)驅(qū)動程序。
大部分時候,系統(tǒng)類驅(qū)動就是功能驅(qū)動。下表是系統(tǒng)提供的 USB 類驅(qū)動。
USB 類名稱
類代碼
驅(qū)動名稱
系統(tǒng)支持
藍牙設(shè)備
0xE0
Bthusb.sys
Vista 、 XP
USB 芯片智能卡接口設(shè)備
(CCID)
0x0B
Usbccid.sys
2K 、 XP 、 2K3 、 Vista 、 2K8
Hub 設(shè)備
0x09
Usbhub.sys
2K 、 XP 、 2K3 、 Vista 、 2K8
HID 設(shè)備
0x03
Hidusb.sys
2K 、 XP 、 2K3 、 Vista 、 2K8
USB 大容量存儲設(shè)備
0x08
Usbstor.sys
2K 、 XP 、 2K3 、 Vista 、 2K8
打印設(shè)備
0x07
Usbprint.sys
2K 、 XP 、 2K3 、 Vista 、 2K8
掃描設(shè)備
0x06
WpdUsb.sys Usbscan.sys
2K 、 XP 、 2K3 、 Vista 、 2K8
媒體傳輸設(shè)備 (MTP)
0x06
WpdUsb.sys
XP 、 2K3 、 Vista 、 2K8
音頻設(shè)備
0x01
Usbaudio.sys
2K 、 XP 、 2K3 、 Vista 、 2K8
Modem 設(shè)備 (CDC)
0x02
Usbser.sys
2K 、 XP 、 2K3 、 Vista 、 2K8
視頻設(shè)備 (UVC)
0x0E
Usbvideo.sys
XP 、 Vista
表 1 系統(tǒng)提供的 USB 類驅(qū)動
功能驅(qū)動
不是所有的 USB 設(shè)備都有類驅(qū)動,但功能驅(qū)動卻是它們唯一的身份證。沒有功能驅(qū)動,設(shè)備就不足以在系統(tǒng)中存在。它的作用是為設(shè)備創(chuàng)造一個獨一無二的內(nèi)核設(shè)備對象( DEVICE_OBJCET ),并因此而在需要的時候,系統(tǒng)能夠通過此內(nèi)核設(shè)備對象找到它。
如果要讓用戶層也能夠知道并使用 USB 設(shè)備,功能驅(qū)動更加不可少。它為設(shè)備在用戶程序可見的名字空間中,為它起一個別名,這個別名可以是一個符號鏈接,也可以是一個由 GUID 定義的設(shè)備 Interface 。通過對這個別名進行操作,也就是對設(shè)備本身進行操作。
OK ,上面的話說得太滿了,有例外的!唯一的例外是以 RAW 模式驅(qū)動的設(shè)備。這種設(shè)備直接由總線驅(qū)動來驅(qū)動其工作,不需要功能驅(qū)動。這種例子真的不多見,也許只有很很底層的控制器設(shè)備、 Hub 設(shè)備之類,才會這樣做。對于 RAW 模式驅(qū)動的設(shè)備,當(dāng)收到 IRP_MN_QUERY_CAPABILITIES 查詢請求的時候,在返回的 DEVICE_CAPABILITIES 結(jié)構(gòu)體中,必須將 RawDeviceOK 位設(shè)置為 TRUE 。
建議讀者在需要的時候使用 WinOBJ.exe 工具查看系統(tǒng)空間中的設(shè)備與別名。
父驅(qū)動與混合設(shè)備
通過一定的設(shè)置, USB 設(shè)備中的每個接口可以擁有不同的 Class 和 Protocol 定義,從而實現(xiàn):一個設(shè)備,多個功能。這種設(shè)備被稱為混合設(shè)備。混合設(shè)備的前提是擁有多個接口,對單接口設(shè)備談 “ 混合 ” 是沒有意義的。
滿足了如下兩個條件的多接口 USB 設(shè)備,被系統(tǒng)認(rèn)為是混合設(shè)備:
1. 設(shè)備描述符中,設(shè)備類的值為 0 : (bDeviceClass , bDeviceSubClass, bDeviceProtocol ) = (0, 0, 0);
2. 只有唯一的配置描述符,即設(shè)備描述符中: (bNumConfigurations ) = (1)
從 WinXP SP2 以后,還支持另外一種混合設(shè)備的判別方式,稱作: Interface Association Descriptor ( IAD )。其實 IAD 描述符是用來組織 “ 接口組( Interfaces Group ) ” 的。配置描述符中可以有多個 IAD 存在,如果將某兩個接口將組成接口組,那么首先這兩個接口必須是緊挨著的,其次,必須有一個 IAD 描述符位于這兩個接口描述符的前面,也必須是緊挨著的, IAD 描述符中的 bFirstInterface 用來描述接口組中的第一個接口 ID , bInterfaceCount 用來描述接口組中包含多少個接口。這樣接口集合 : [bFirstInterface, bFirstInterface+bInterfaceCount) 為一個接口組。
當(dāng)然,能夠用上 IAD 的設(shè)備,一定是有多接口存在了,否則就是多此一舉了。 IAD 描述符的識別和實現(xiàn)是通用父驅(qū)動完成的,所以有 IAD 支持的設(shè)備,都被認(rèn)作混合設(shè)備。而識別設(shè)備是否有 IAD 支持,是通過設(shè)備描述符中的如下值判斷的:
(bDeviceClass, bDeviceSubClass, bDeviceProtocol) = (0xEF, 0x02, 0x01)
IAD 普及率不是很廣,一個原因就是 XP sp2 以后的操作系統(tǒng)版本才對它支持,這樣如果把設(shè)備插入到 Win 2000 甚至 XP SP1 上,都不能被正確識別。這樣,廠商可能必須為不同的系統(tǒng)寫兩套驅(qū)動程序。
我們下面來說說當(dāng)一個多接口混合設(shè)備插入電腦后,系統(tǒng)是如何識別它,并為它加載驅(qū)動的。這里大家要注意到一點,就是系統(tǒng)是如何把一個設(shè)備,通過多接口,識別為多個物理設(shè)備的。
一開始,系統(tǒng) PNP 管理器安裝常規(guī),讀取并分析 USB 設(shè)備描述符,然后為它分配如下設(shè)備 ID :
USB\VID_vvvv&PID_pppp
USB\VID_vvvv&PID_pppp&REV_rrrr
(vvvv, pppp, rrrr: 4 位 16 進制數(shù),分別代表了廠商 ID ,產(chǎn)品 ID , USB 版本。對應(yīng)于設(shè)備描述符中的這些值: idVendor/ idProduct/ bcdDevice)
和兼容 ID :
USB\COMPOSITE
PNP 管理器首先按照常規(guī),根據(jù)上述的設(shè)備 ID 為設(shè)備尋找合適的驅(qū)動程序:根據(jù)設(shè)備 ID 到注冊表的設(shè)備安裝信息庫(對應(yīng)于 Enum 和 Class 兩個鍵)中進行搜索,如果找到了安裝記錄,就根據(jù)記錄中的信息加載驅(qū)動程序。
問題是如果找不到合法的記錄怎么辦?這時候就用的著兼容 ID 了,兼容 ID “ USB\COMPOSITE ”是在系統(tǒng)中有注冊記錄的,并且就對應(yīng)著通用父設(shè)備驅(qū)動( USBCCGP.sys) 。于是,系統(tǒng)為混合設(shè)備加載通用父設(shè)備驅(qū)動。讀者可到目錄 Windows\Inf 下查看 usb.inf 文件,此文件中包含了通用父設(shè)備驅(qū)動的安裝信息。
通用父設(shè)備驅(qū)動通過分析配置描述符,完成兩個動作:首先為每個 USB 接口分配一個的設(shè)備 ID 和兼容 ID ;然后為每個 USB 接口創(chuàng)建一個物理設(shè)備對象( Physical Device Object )。設(shè)備 ID 形式如下:
USB\VID_vvvv&PID_pppp&MI_mm
USB\VID_vvvv&PID_pppp&REV_rrrr&MI_mm
(mm: 兩個 16 位數(shù)字表示的接口號 )
根據(jù)接口描述符中的 clsss 類型,分配兼容 ID ,其形式如下:
USB\CLASS_cc
USB\CLASS_cc&SUBCLASS_ss
USB\CLASS_cc&SUBCLASS_ss&PROT_pp
(cc/ ss / pp: 兩位 16 進制數(shù)。分別對應(yīng)于接口描述符中的: bInterfaceClass/ bInterfaceSubClass/ bInterfaceProtocol)
通用父設(shè)備驅(qū)動為每個接口創(chuàng)建了物理設(shè)備對象后,將接口的設(shè)備 ID 、兼容 ID 信息提交給 PNP 管理器, PNP 管理器就有責(zé)任為這些“虛假的”物理設(shè)備安裝驅(qū)動。仍然重復(fù)上面的過程:根據(jù)每個接口的設(shè)備 ID 和兼容 ID 在注冊表的設(shè)備安裝信息庫中搜索,試圖找到相關(guān)的安裝記錄,如果找到了,就為接口加載相應(yīng)的驅(qū)動程序。如果找不到,系統(tǒng)就會彈出“發(fā)現(xiàn)新設(shè)備”的對話框,啟動驅(qū)動安裝向?qū)А4藭r用戶需為這些接口手動安裝驅(qū)動。
所以,對于多接口的混合設(shè)備( composite device ),其設(shè)備驅(qū)動的安裝分為兩個過程,先嘗試安裝混合驅(qū)動,如果找不到,就默認(rèn)安裝通用父設(shè)備驅(qū)動;接下來通用父設(shè)備驅(qū)動為每個接口分配設(shè)備 ID ,并要求系統(tǒng)為每個接口啟動 PNP 過程。
最后,那么來說,在 USB 混合設(shè)備中,一定是 X 個接口對應(yīng)于 X 個功能設(shè)備(有一個物理設(shè)備對象)嗎?一般情況下是這樣的,但也有例外,這就是接口組:在符合一定條件的情況下,多個接口中的某些接口可以被組合為一個接口組,一個接口組代表一個功能,父設(shè)備驅(qū)動只為之創(chuàng)建一個物理設(shè)備對象。
接口組的詳情,大家看后面的章節(jié)內(nèi)容。
過濾驅(qū)動
過濾驅(qū)動無處不在。在很多時候,它被稱作 Hook ,是一種 Hack 手段;很多時候它又是必不可少的,這種技術(shù)甚至被操作系統(tǒng)自己使用。沒有哪一個殺防毒軟件不使用過濾驅(qū)動,我們在使用網(wǎng)上銀行的時候,會用到一些安全控件,基本上都借助了過濾驅(qū)動的技術(shù)。
過濾驅(qū)動可以位于任何一層驅(qū)動的上面,或下面。過濾的對象也包括已經(jīng)存在于系統(tǒng)中的其他的過濾驅(qū)動。當(dāng)它位于某層驅(qū)動( D 驅(qū)動)上面的時候,所有目標(biāo)發(fā)往 D 驅(qū)動的請求,都首先被它截取;當(dāng)它位于某層驅(qū)動下面的時候,所有和 D 驅(qū)動相關(guān)的從更底層驅(qū)動反饋回來的的“完成消息”都預(yù)先被過濾驅(qū)動截取。這正是它威力強大的原因所在。對于被過濾的驅(qū)動來說,過濾驅(qū)動簡直就是它的先知了。
但使用過濾驅(qū)動,要很慎重。很容易把系統(tǒng)搞得很不穩(wěn)定。讀者在寫過濾驅(qū)動的時候,要明白這樣一件事:你想過濾誰,得先了解誰;好像你追求一個人,要先認(rèn)識這個人。否則死機藍屏都會與你不期而遇。
USB 驅(qū)動棧、設(shè)備棧
請大家不要把這里的“棧”理解成“程序堆棧”的那個棧,朋友們要回到這個字最簡單的本意來理解它,而不是想象成一個數(shù)據(jù)結(jié)構(gòu)。就看成草垛柴堆一樣。
驅(qū)動棧、設(shè)備棧本質(zhì)上是并行概念。驅(qū)動之間的聯(lián)系是通過設(shè)備對象進行的,所以驅(qū)動之間是間接聯(lián)系的,驅(qū)動棧多少也就只是概念上的,他用來表示一個設(shè)備能夠在系統(tǒng)中識別、運行,從上到下中共需要哪些驅(qū)動程序支持。
設(shè)備棧則是由據(jù)可查的。系統(tǒng)中每個 DevNode 就表現(xiàn)一個設(shè)備棧。可以這樣理解,多個設(shè)備棧,串聯(lián)成了驅(qū)動棧。
使用 WinDBG 的 !devnode 命令,可以列舉系統(tǒng)中的設(shè)備樹。下圖截取了 CY001 相關(guān)片段。
圖 3 CY001 DevNode 片段
上圖中紅色框標(biāo)出的三個 DevNode ,正好對應(yīng)于三個內(nèi)核驅(qū)動,建立了 CY001 的驅(qū)動棧。從上到下分別是控制器驅(qū)動 (usbEHCI) ,集線器驅(qū)動 (usbHUB) ,和功能驅(qū)動 (CY001) 。下圖以 CY001 為例,更加清晰詳細(xì)地描繪了驅(qū)動棧、設(shè)備棧的面貌。
圖 4 CY001 的驅(qū)動棧和設(shè)備棧
上圖是單接口 CY001 設(shè)備的驅(qū)動棧、設(shè)備棧全圖。也適用于所有其他的單接口 USB 設(shè)備。如果是多接口混合設(shè)備,就要多一個通用父驅(qū)動,稍微復(fù)雜一點。
最上面是可能存在的過濾驅(qū)動,因為只是“可能存在”,所以都用虛框表示。其實過濾驅(qū)動可以存在于設(shè)備棧的任何一個位置,而不僅僅是最上層。筆者不可能盡皆畫全,以上層過濾為例,能說明問題也就可以了。
過濾驅(qū)動生成的過濾設(shè)備對象,掛載到 CY001 驅(qū)動生成的功能設(shè)備對象上;這樣所有發(fā)送給 CY001 功能設(shè)備對象的請求,過濾設(shè)備對象總是先得到。根集線器生成了 CY001 驅(qū)動的物理設(shè)備對象。三個設(shè)備連在一起,就是 CY001 驅(qū)動程序的設(shè)備棧。
但還沒有完,根集線器驅(qū)動并不是最底層驅(qū)動,他必須得到控制器驅(qū)動的支持,于是加上可能存在的過濾驅(qū)動,由此形成中間的那條設(shè)備棧。
仍舊未結(jié)束,控制器驅(qū)動也不是直接和系統(tǒng)交互的,而是通過更底層的系統(tǒng)總線如 PCI 、 ACPI 進行的。這樣,右側(cè)的第三條設(shè)備棧也形成了。
由此也可以看出設(shè)備棧、驅(qū)動棧之間的關(guān)系了。驅(qū)動棧是形而上的,設(shè)備棧是形而下的。
本文來自CSDN博客,轉(zhuǎn)載請標(biāo)明出處:http://blog.csdn.net/changpei/archive/2010/05/06/5562542.aspx