由于 USB 只是一個設(shè)備接入到系統(tǒng)中的方式,對于大部分用戶來說,只要設(shè)備接入系統(tǒng)后能夠正常工作就可以了,不需要知道這個設(shè)備是連接在系統(tǒng)的哪一個端口上。但是如果一個系統(tǒng)中同時有多個具有相同的VID和PID的設(shè)備接入,要對其中的每一臺設(shè)備進行單獨的測試,就有必要研究這個設(shè)備的接入方式了。
一般的研究方法,應該是順藤摸瓜,或者相反。我在查找了相關(guān)的介紹后,很少能夠看到完整的例子,特別是對于U盤這樣的設(shè)備,很少有人直接將最終系統(tǒng)中顯示的文件卷與USB端口的連接關(guān)系做成樹狀的關(guān)系圖顯示出來。昨天看了那個設(shè)備的卸載過程示例后,覺得應該可以實現(xiàn)的。因為可以從一個文件卷查找到起對應的連接關(guān)系。即從 U 盤的文件卷找到其連接的HUB,然后從HUB的端口出發(fā),找到其所有的連接點(在設(shè)備管理器的資源中,顯示為總線關(guān)系)。后續(xù)應該可以繼續(xù)逆向?qū)ふ,一直找到主控制器。這樣就可以查清楚連接的脈絡(luò)。
看了微軟的 USB View 示例以及網(wǎng)友寫的 USB Port 示例,可以看出,查找USB設(shè)備,不一定非要從 USB 的設(shè)備接口類開始檢查,只要采用嘗試的方式,用 CreateFile 嘗試打開系統(tǒng)中名字為 HCD0, HCD1類似的名稱,就可以直接知道是不是有USB主控制器存在。此后通過 DeviceIoControl 函數(shù),給主控制器發(fā)送 IOCTL_USB_GET_ROOT_HUB_NAME 命令,獲取 Root Hub 的名字。此后再次使用 CreateFile 的方式,打開 RootHub,用DeviceIoControl 給RootHub發(fā)送 IOCTL_USB_GET_NODE_INFORMATION 命令,取得該 Hub上端口信息。在端口信息的 HubDescriptors 中,包含了 NumOfPorts,即該 Hub 上的端口數(shù)量。同時,這個端口信息中包含了指向端口連接關(guān)系的指針數(shù)組。通過循環(huán)檢查的方式,使用 DeviceIoControl 給端口發(fā)送 IOCTL_USB_GET_NODE_CONNECTION_INFORMATION 命令,獲取該端口的連接關(guān)系信息表,其中的一個bConnected 即標識了是否有設(shè)備連接在該端口上。并且有一個 ConnectedIsHub的標志,反映該端口連接是下一級 Hub還是一般的設(shè)備。通過 GetDriverKeyName 函數(shù),從 HubDevice 中獲得所連接的設(shè)備的驅(qū)動信息,包括了設(shè)備類Guid,設(shè)備的 PID,VID,連接速度等描述內(nèi)容。采用循環(huán)的方式可以逐一查找所有已連接的設(shè)備。
如果要進一步查找其中的Hub設(shè)備所連接的信息,必須采用遞歸方法逐步深入到每一個Hub的下層,才可以獲得完整的設(shè)備連接信息。
知道了設(shè)備名稱,并無法獲取對應的映射文件卷的關(guān)系。此時還需要回頭從文件系統(tǒng)著手,用循環(huán)的方法,檢查系統(tǒng)中已經(jīng)支持的文件卷多對應的設(shè)備,通過比對的方式與連接關(guān)系做對應。特別是對于一個連接口映射為多個文件卷的情形,可能有一部分是支持在文件系統(tǒng)中,而另一部分不一定顯示(如未分配驅(qū)動器符號或作為一個現(xiàn)有文件卷的連接等),則比較難于顯示了。
|