USB裝置驅動開發之遠端存取USB裝置(二 USB裝置虛擬端)

來源:互聯網
上載者:User

標籤:

By Fanxiushu 2016-05-22 轉載或引用請註明原始作者
接上文,
在處理好USB資料擷取端的問題之後,接下來進入核心的部分,虛擬USB裝置端的開發工作。
上文簡單介紹過,需要開發虛擬匯流排驅動來類比USB裝置。
所謂虛擬匯流排驅動,就是安裝於System系統裝置下的一個驅動,由PnP管理器建立出一個虛擬匯流排PDO裝置,
我們的虛擬匯流排驅動Attach到這個PDO上,形成一個FDO功能裝置驅動,
然後在我們的驅動中,根據需要建立出若干個 Child PDO裝置,
這些 Child PDO裝置就是我們根據需要類比出來的虛擬設備。
我們的匯流排驅動每當建立出一個 Child PDO並且初始化之後,
調用 IoInvalidateDeviceRelations函數,通知PnP管理器我們的的Child PDO有變化。
於是PnP管理器接著發送 IRP_MN_QUERY_DEVICE_RELATIONS隨插即用訊息給我們的驅動,
等我們把新的所有Child PDO列表告訴給PnP管理器,它接著比較他內部維護的新舊的PDO列表,
知道哪些PDO被新添加,哪些已經被移除。
對於新添加的裝置,PnP管理器發送查詢裝置ID的訊息IRP_MN_QUERY_ID給我們建立的Child PDO,查詢裝置的各種ID,
然後PnP管理器根據裝置ID從註冊表尋找是否已經為這個Child PDO安裝了功能驅動,
如果已經安裝,則載入它,沒安裝則提示使用者安裝新的驅動。

這就是虛擬匯流排驅動的大致架構,原理上來說並不複雜,而且有微軟提供的 例子代碼,
可以閱讀它的例子代碼進一步加深對匯流排驅動原理的理解,或者可以查看我提供在CSDN上的原始碼來加深理解。

我們的匯流排驅動類比的是USB裝置介面,因此Child PDO必須具備USB介面的特性,
USB介面核心部分要處理的,其實就是上文簡單介紹過的USB介面的四種資料轉送方式:
一,控制傳輸,二中斷傳輸,三批量傳輸,四,同步傳輸。
中斷,批量,同步傳輸都比較好處理,而控制傳輸牽涉到的命令很多,因此需要處理多種命令。
windows平台把跟USB介面的裝置進行資料通訊統一使用URB資料包,每個包都指定一個Function功能號,
也就是URB的功能種類。Function的種類大概有20多個,其實依然是從USB介面的四種通訊方式派生出來的,
比如URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER 這個URB功能就是中斷傳輸和批量傳輸的合并。
URB_FUNCTION_ISOCH_TRANSFER就是同步傳輸,
而餘下來的20多個 URB_FUNCTION_XXX可以完全理解成控制傳輸的某個命令。
比如URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE就是控制傳輸中,從USB裝置擷取裝置描述符。

首先列舉中我們的驅動中需要處理的URB_FUNCTION_XXX命令:

(以下是中斷,批量,同步傳輸命令)

URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER(中斷或者批量傳輸)
URB_FUNCTION_ISOCH_TRANSFER(同步傳輸)

(以下全是控制傳輸命令)

通用的控制傳輸命令,當USB裝置傳輸的命令不在微軟定義的URB_FUNCTION時候,可以用它進行傳輸
URB_FUNCTION_CONTROL_TRANSFER

擷取裝置,介面,端點描述符
URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE
URB_FUNCTION_GET_DESCRIPTOR_FROM_INTERFACE
URB_FUNCTION_GET_DESCRIPTOR_FROM_ENDPOINT

選擇配置描述符, 介面可選描述符
URB_FUNCTION_SELECT_CONFIGURATION
URB_FUNCTION_SELECT_INTERFACE

擷取USB裝置的Class或Vendor資訊
URB_FUNCTION_CLASS_DEVICE
URB_FUNCTION_CLASS_INTERFACE
URB_FUNCTION_CLASS_ENDPOINT
URB_FUNCTION_CLASS_OTHER
URB_FUNCTION_VENDOR_DEVICE
URB_FUNCTION_VENDOR_INTERFACE
URB_FUNCTION_VENDOR_ENDPOINT
URB_FUNCTION_VENDOR_OTHER

重設或者中斷在某個端點的傳輸
URB_FUNCTION_RESET_PIPE
URB_FUNCTION_ABORT_PIPE

擷取裝置,介面,端點狀態
URB_FUNCTION_GET_STATUS_FROM_DEVICE
URB_FUNCTION_GET_STATUS_FROM_INTERFACE
URB_FUNCTION_GET_STATUS_FROM_ENDPOINT
URB_FUNCTION_GET_STATUS_FROM_OTHER

擷取當前配置,當前介面,當前framenumbber。當前的framehnumber用於同步傳輸
URB_FUNCTION_GET_CONFIGURATION
URB_FUNCTION_GET_INTERFACE
URB_FUNCTION_GET_CURRENT_FRAME_NUMBER

以下是設定或清除FEATURE,主要用於HUB,當然可能某些USB裝置會有用到
URB_FUNCTION_SET_FEATURE_TO_DEVICE
URB_FUNCTION_SET_FEATURE_TO_INTERFACE
URB_FUNCTION_SET_FEATURE_TO_ENDPOINT
URB_FUNCTION_SET_FEATURE_TO_OTHER
URB_FUNCTION_CLEAR_FEATURE_TO_DEVICE
URB_FUNCTION_CLEAR_FEATURE_TO_INTERFACE
URB_FUNCTION_CLEAR_FEATURE_TO_ENDPOINT
URB_FUNCTION_CLEAR_FEATURE_TO_OTHER

windows為何要搞出這麼多FUNCTION命令,估計是為了理解和處理USB控制命令的方便。
但是這些控制命令經過 Host Controller進入真正的USB裝置前,Host Controller依然要把它轉換成 8個位元組的SetupPacket控制命令。
這是硬體需求,而我們是虛擬設備,因此沒必要非得轉成 SetupPacket格式,只要網路通訊中適合我們的就可以。
我們的USB資料擷取端和虛擬USB端,都屬於windows平台,轉成Setuppacket再轉成FUNCTION,反而麻煩,
因此基本是根據URB_FUNCTION做些簡單轉換,這樣方便也快捷。
但是如果你的採集端和USB虛擬端分別屬於不同的平台,比如linux,windows,macos,等各種平台都有,那得使用一個統一的通訊方式。
到時USB通訊協議中規定的格式估計是更好的選擇。

知道哪些URB_FUNCTION命令需要處理,可能大家還是不大明白如何處理這些URB,如何完整的類比一個USB介面,
從而實現把遠方的資料擷取端的USB裝置搬到虛擬端來。
假設你已經熟悉了虛擬匯流排驅動的架構。
虛擬匯流排驅動應該與應用程式層程式有個通訊介面,應用程式使用IOCTL跟驅動通訊。
應用程式層程式通過網路連接到USB資料擷取端,擷取到某個需要被遠端存取的USB裝置的硬體ID,相容ID等初步資訊,
通過CreatePDO IOCTL傳遞給虛擬匯流排驅動,虛擬匯流排驅動根據硬體ID等各種參數建立child PDO裝置,
建立成功後調用IoInvalidateDeviceRelations通知PnP管理器,接下來就是PnP管理器該做的事。
當PnP管理器正確載入根據硬體ID對應的功能驅動之後,這個功能驅動就開始工作。這個功能驅動開始構造URB包,
並且發送URB包到我們在虛擬匯流排驅動中建立的Child PDO裝置上,
接下來,我們的匯流排驅動必須把這些URB資料正確的傳遞到遠端的USB資料擷取端,並且得到正確的響應。
至於如何處理這個主要和核心的過程,每個工程師可能有不同的處理辦法,我們是採用把URB資料傳遞到應用程式層,
然後在應用程式層通過socket通訊端傳遞給資料擷取端,得到採集端的回應資料包之後,再把它傳遞給驅動,
最後我們的虛擬匯流排驅動完成從功能驅動發下來的這個URB資料包。

我們在應用程式層建立一個訊號量,傳遞到驅動,匯流排驅動使用這個訊號量通知應用程式層程式有新的URB資料包到達。
比如上層的功能驅動有個 URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE 的URB資料包投遞給我們的匯流排驅動,
於是匯流排驅動的把這個URB包掛載到Child PDO的等待處理的隊列中,然後增加訊號量,通知應用程式層有URB資料包到達。

應用程式層程式有一個或者多個線程調用 WaitForSingleObject 函數等待這個訊號量,
當
WaitForSingleObject成功返回,說明有URB資料包,於是通過DeviceIoControl函數,投遞一個 BEGIN IOCTL到匯流排驅動,
我們的匯流排驅動從Child PDO的等待隊列取出一個URB資料包,分析處理這個URB資料包,
然後再把這個URB掛載到Child PDO的忙碌隊列中,同時產生一個seqno唯一標識這個URB包,完成這個BEGIN IOCTL。
應用程式層程式根據從BEGIN IOCTL擷取到的請求資料 ,發送到遠方的USB資料擷取端,等待對方的回應。
USB資料擷取端回應這個資料包之後,應用程式層程式調用 一個 END IOCTL
到匯流排驅動,
我們的虛擬匯流排驅動根據seqno從Child PDO的忙碌隊列尋找對應的URB包,把從 END IOCTL傳遞的資料,正確的填寫到URB資料包中,
最後完成這個URB包。

這個就是我的匯流排驅動對URB資料包的處理工程,這個跟前幾篇文章介紹的
“檔案過濾驅動實現目錄重新導向“(http://blog.csdn.net/fanxiushu/article/details/43845699)的處理架構是一致的。
如果不熟悉這個過程,可以去看看過濾驅動實現目錄重新導向的章節。

上邊介紹過的URB_FUNCTION_XXX非常之多,為了在 BEGIN IOCTL和END IOCTL簡化資料包,
都統一使用一個資料結構與驅動互動。
如下
struct ioctl_usbtx_header_t
{
    ULONGLONG        inter_handle;        //  是等待處理的檔案IRP 指標
    LONG             inter_seqno;         //  每個IRP的序號,由驅動產生,和inter_handle一起用來保證請求包的唯一性驗證 
    LONG             data_length;         //  資料的長度; 如果是讀裝置,則讀取的位元組數; 如果是寫資料到裝置,寫入前是需要寫入的位元組數,寫入成功後,實際寫入的位元組數,ISO 傳輸會包括 iso_packet_hdr_t結構大小
    LONG             result;              //  返回是否成功
    LONG             reserved1;           //  保留
    ////


    int        type;  // 1 擷取描述符, 2 vendor or class , 3 傳輸資料,  4 重設, 5 擷取狀態, 6 操作feature
    int        reserved[ 3 ]; //保留
    /////
    union{
        ///
        struct{
            int          type;     // 1 擷取或設定裝置描述符, 2 設定配置描述符, 3 擷取或設定介面描述符, 4 擷取或設定連接埠描述符
            int          subtype;  // (type=1,3,4) 1 擷取裝置描述符, 2 擷取配置描述符, 3 擷取字串;;;;; (type=2) 1設定config(index=-1 & value=-1 unconfigure), 2 設定 interface
            int          is_read;  // (type=1,3,4) is_read為TRUE擷取描述符,FALSE 設定描述符
            int          index;    // 序號
            int          value;    // 值, 擷取string時定義成language_id

        }descriptor;
        ////////
        struct{
            int          type;    //1 CLASS請求, 2 VENDOR請求
            int          subtype; //1 device; 2 interface ; 3 endpoint; 4 other
            int          is_read; //是從裝置讀,還是寫入裝置
            int          request;
            int          index;
            int          value;
        }vendor;
        ////
        struct {
            int           type; // 1 控制傳輸,  2 中斷或批量傳輸, 3 同步傳輸
            int           ep_address; //連接埠位置   如果 (ep_address &0x80) 則是讀,否則寫;  控制傳輸時候,如果為0表示使用預設連接埠
            int           is_read;    //是從裝置讀,還是寫入裝置
            union{
                int               number_packets; //同步傳輸時候,包個數,如果為0,則組合到一起傳輸,>0則在頭後面跟iso_packet_hdr_t結構,大小為 ISO_PACKET_HDR_SIZE + number_packets*sizeof(iso_packet_t)
                struct{
                    unsigned char setup_packet[8]; /////控制傳輸時候,發送的8個位元組的控制碼
                };
            };
            char          is_split; ///中斷批量傳輸,或同步傳輸是否拆分成多塊,
            char          reserved[3]; ///
        }transfer;
        ////////
        struct {
            int           type; /// 1 IOCTL_INTERNAL_USB_RESET_PORT重設裝置; 2 IOCTL_INTERNAL_USB_CYCLE_PORT 重設裝置; 3 重設連接埠URB_FUNCTION_RESET_PIPE; 4 中斷連接埠 URB_FUNCTION_ABORT_PIPE
            int           ep_address;
        }reset;
        /////
        struct {
            int           type; /// 1 device; 2 interface ; 3 endpoint; 4 other status; 5 擷取當前配置描述符;6 根據interface擷取當前介面的alterantesetting; 7 擷取current frame number
            int           index; ///
        }status;
        //////
        struct {
            int           type;     //// 1 SET請求, 2 CLEAR請求
            int           subtype;  ///  1 device; 2 interface ; 3 endpoint; 4 other
            int           index;    ///
            int           value;    ///
        }feature;
        ////////
    };
    ////////////

};

看起來似乎有點多,實際上BEGIN IOCTL和END IOCTL都使用
ioctl_usbtx_header_t 來傳遞各種URB資料,反而方便許多,
到了資料擷取端,也使用同樣的結構進行處理,因為都是windows平台,處理的各種轉換反而少了許多。

資料結構的定義或使用,請下載CSDN上提供的工程。

到此為止,一個基於虛擬匯流排驅動的實現USB裝置遠端存取的功能,基本算完成了,
但是有個不太完善的地方,這樣的虛擬USB裝置像個無主孤魂一樣存在於系統中,它既不附著在某個虛擬跟集線器上,
也沒有對應的虛擬USB控制器,因此在某些應用程式層程式看來,會把它當作不存在。
比如某些按照USB裝置棧的方式枚舉系統中存在的USB裝置 ,這樣的虛擬USB裝置是枚舉不出來的,因為他沒ROOTHUB,也沒USB控制器。
這個概念就跟以前介紹過的虛擬磁碟驅動很類似,使用 微軟的ScsiPort或StorePort模型的虛擬磁碟驅動,
會被當成真正的磁碟,
在磁碟管理器能找到我們的虛擬磁碟,而且可以像真正的磁碟那樣進行分區,格式化等各種基本的磁碟操作。
而在網上提供的一個類似 filedisk架構的虛擬磁碟驅動,也能提供磁碟訪問的功能,但是並不具備StorePort等架構的提供的磁碟驅動功能,
並不被系統視作一個磁碟系統。
 
我們現在實現的虛擬USB裝置也跟filedisk一樣,不會被系統視作一個真正的USB裝置。
但是它依然能欺騙大部分軟體,就跟filedisk一樣。

如何到達我們的虛擬USB盡善盡美呢? 需要實現虛擬ROOTHUB和虛擬USB控制器。

敬請關注下文關於RootHUB和USB控制器得開發過程。



USB裝置驅動開發之遠端存取USB裝置(二 USB裝置虛擬端)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.