linux usb usbip驅動詳解(二)

      終於來到usbip驅動代碼分析了!

      我們在做產品時,通常是先討論方案、制定協議、編碼和測試。

      usbip的方案是行得通的,它是從URB對象獲取信息,然後從tcp發送出去的,URB是linux usb子系統裏面用於抽象usb通信而精心設計的對象,只要server和client兩邊在恰當的時機分別隔斷各自系統的usb通信流程,然後巧妙地交換數據,各自系統都察覺不到,就像黑客利用鉤子函數做rookit。如果server和client都是linux系統,就很容易理解了,畢竟部門內的兄弟好說話,跨部門溝通就沒那麼好了。譬如是linux和windows的話,那麼linux的urb就需要轉換爲windows那邊能辨認的的“urb”了,它們的結構體成員肯定不一樣,畢竟操作系統都不一樣,不過夠信息組包就好了。

      usbip有一套協議,用於解決上述問題。

      協議文檔在kernel源碼根目錄下的drivers/usb/usbip/usbip_protocol.txt,比較新的版本在Documentation/usb/usbip_protocol.txt

      在描述usbip原理前,我們先回顧下usb主機控制器和usb驅動(接口驅動)的關係。可以通過閱讀usb-skeleton.c代碼來學習USB設備驅動是怎麼寫的。簡單來說,當主機控制器驅動裏的hub.c檢測到root hub的port有插入設備(譬如檢測到D-/D+管腳被hub拉低),主機控制器hcd就會發出“獲取設備描述符”的請求進行枚舉設備,獲取到PID/.VID,接口描述符、端口描述符等信息後,報給上層驅動(usb core),usb core會根據PID/VID來match設備驅動,此時假設匹配到usb-skeleton驅動,那麼usb-skeleton驅動的probe()被回調,這時就可以進行usb通信了,如U盤讀寫,鍵鼠操作等,這些通信是通過填充urb結構體,然後交給usb core模塊導出的usb_submit_urb函數,它會回調從主機控制器驅動hcd的struct hc_driver註冊到的urb_enqueue回調函數和urb_dequeue回調函數。這兩個函數是比較底層的,直接控制usb控制器的寄存器,開啓dma傳輸,最後編碼成usb硬件層的“非歸零反轉碼”,用差分電信號傳出去。其中usb core模塊是linux的大牛們封裝好的很多通用的api供我們使用,減少了我們開發usb驅動的難度,因爲大部分事情已經交給usb core做了,很多時候就是在框架內註冊回調方法。

       對於usbip,假設在client端(vhci-hcd)安裝有U盤驅動,vhci-hcd的一個重要作用是虛擬出一個usb主機控制器(hcd),linux很多虛擬設備,譬如虛擬網卡(driver/net/dummy.c)等,根據上面的描述,我們可以知道usb主機控制器的作用,就是枚舉信息,以及獲取U盤驅動組裝的usb通信數據轉發給底層硬件,最後給到U盤,跟U盤通信。所以虛擬hcd也具有這樣的功能,我們在上一篇文章中知道,client端是使用“usbip attach -r 192.168.100.191 -b 2-1.1”把usb設備attach到本地的,其實這個工具的attach操作就是類似於usb的“熱插拔”,踢一下vhci-hcd虛擬出來的主機控制器的root hub(任何主機控制器都有一個根hub),這時hub.c就以爲有真實的usb設備插入,按照usb hub的協議發出請求,譬如詢問root hub究竟是哪個port口插入了設備等,下面是一些hub需要處理的請求,具體功能參考《usb2.0規範》的CH11章節:

ClearHubFeature    
ClearPortFeature
GetHubDescriptor
GetHubStatus        
GetPortStatus        
SetHubFeature        
SetPortFeature   

       只要我們模擬出root hub端口號以及端口狀態值給hub.c,就能矇騙它,讓它以爲真的有硬件插入,此時hub.c就會發出枚舉usb設備的“請求設備描述符”給root hub,最後給到urb_enqueue,vhci-hcd就是實現一個vhci_urb_enqueue,並註冊到struct hc_driver對象的.urb_enqueue成員函數裏,vhci_urb_enqueue的功能不是像真實主機控制器驅動那樣,操控寄存器,操控DMA,而是通過socket發送出去,給server端的真實主機控制器那邊接收,由於server端(usbip-host)真的存在有usb主機控制器,所以把從client(vhci-hcd)的socket發出來的usb數據接收到,重新組裝好urb,通過usb core模塊的usb_submit_urb接口傳給真實的主機控制器裏,就能跟接在host端pc的U盤通信了,既然鏈路已經通了,client端的U盤驅動的其他操作(寫入U盤數據或者讀取U盤裏的文件等)就能按照上面的鏈路走了,能完全操控host端的真實的U盤了!

      文字很囉嗦,不知道有沒有講明白,但講解usbip協議前必須要給大家一個初步的原理介紹。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章