• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            在前面的文章中,我們已經給出了USB協議的鏈接地址,從這篇文章起,我們會涉及到許多USB 1.1的內容,我們的指導思想是先從熟悉USB 1.1協議入手,先使用現成的HCD和USBD,直接面對客戶端驅動編程,盡快看到成果,使讀者對USB的開發充滿信心,進而去研究USBD和HCD的編程方法。請讀者自行閱讀協議,文章中有關協議的詳細情況,由于會涉及非常多的文字,恕不能過多解釋。
            1、USB系統主機端的軟件結構
                一般來說,教科書或者協議上都會把USB主機端的軟件說成有三層,第一層叫主機控制器驅動程序HCD(Host Controller Driver),第二層叫USB驅動程序USBD(USB Driver),第三層叫客戶端驅動程序(Client Driver);實際上,我們實際看到的東西,往往HCD和USBD是由一個程序完成的,比如windows就提供了HCD和USBD,如果你自己開發了一個USB設備,只需要在HCD和USBD上面開發一個客戶端驅動程序即可;linux也是同樣,linux內核已經提供了HCD和USBD;所以在windows和linux下我們基本上沒有開發HCD和USBD的必要,而且linux還提供源代碼;但DOS就不一樣了,DOS本身對USB沒有任何支持,所以要想在DOS下徹底玩轉USB,需要研究HCD、USBD和客戶端驅動程序。
            2、DOSUSB介紹
                很顯然,HCD和USBD更加底層一些,需要理解的東西也更多一些;如果我們能夠繞過HCD和USBD,直接從客戶端驅動程序入手,將會容易許多。幸運的是我們可以找到一個免費的DOS下的USB驅動程序,叫DOSUSB,該驅動程序實現了大部分的HCD和USBD的功能,使我們進行USB編程的好幫手。
                DOSUSB目前還沒有實現EHIC的驅動,也就是說還不支持USB2.0,這也是我們從USB 1.1開始的原因之一,另一方面,由于USB2.0是兼容USB1.1的,所以,即便你在USB2.0的設備下,仍然可以使用USB1.1的驅動程序,只不過不能實現480MB/秒的傳送速度而已。
                下面我們介紹一下DOSUSB。DOSUSB的官方網站如下:
                http://www.usbdos.net

                可以從其官方網站上下載DOGUSB的最新版本,當前版本是1.1.1。或者在下面在下面網址下載這個版本的DOSUSB。

                http://blog.hengch.com/software/dosusb/dosusb.zip

                DOSUSB可以在非商業領域免費使用,如果肯花費費用,可以購買到源代碼,從其官方網站的論壇上看到,在2006年9月作者開出的源代碼的價格是1000歐元。

                DOSUSB的安裝十分簡便,只需要解壓縮到某一個目錄下即可,比如放在c:\dosusb目錄下,請自行閱讀DOSUSB自帶的文檔,使用也非常簡單,在DOS提示符下鍵入dosusb執行即可。

                c:\dosusb>dosusb

                缺省情況下,DOSUSB使用int 65h作為其驅動的調用軟中斷,如果和你的系統有沖突,在運行dosusb時可以加參數/I,請自行閱讀DOSUSB的文檔。

                DOSUSB通過一個叫做URB(USB Request Block)的數據結構與客戶端驅動程序進行通訊,這一點和linux非常相似,估計作者參考了linux下的源代碼,在DOSUSB文檔里給出了這個結構的定義,如下:
                struct {
                  BYTE  transaction_type;   // 設置事務(控制傳輸)(2Dh),輸入事務(69h)輸出事務(E1h)
                  BYTE  chain_end_flag;     // 備用
                  BYTE  dev_add;            // 設備地址
                  BYTE  end_point;          // 端點號
                  BYTE  error_code;         // 錯誤嗎
                  BYTE  status;             // 設備狀態
                  WORD  transaction_flags;  // 備用
                  WORD  buffer_off;         // 接收/發送緩沖區偏移地址
                  WORD  buffer_seg;         // 接收/發送緩沖區段地址
                  WORD  buffer_length;      // 接收/發送緩沖區長度
                  WORD  actual_length;      // 接收/發送時每個包的最大長度
                  WORD  setup_buffer_off;   // setup_request結構的偏移地址
                  WORD  setup_buffer_seg;   // setup_request結構的段地址
                  WORD  start_frame;        // 備用
                  WORD  nr_of_packets;      // >0時會啟動實時傳輸
                  WORD  int_interval;       // 備用
                  WORD  error_count;        // 重試的次數
                  WORD  timeout;            // 備用
                  WORD  next_urb_off;       // 備用
                  WORD  next_urb_seg;       // 備用
                } urb                       // 32字節
                之所以列出這個結構,是因為我們將使用這個結構與USBD進行交互。關于結構中字段的定義,在DOSUSB的文檔中有詳細的說明。除備用字段不需要填以外,error_code和status由DOSUSB返回,故填0即可,后面還會介紹更詳細的填寫方法。

                在DOSUSB的發行包中,有一個sample目錄,里面有很多例子,但大多是使用power basic寫的,不過仍然有很好的參考價值。

            3、USB 1.1協議中的一些內容

                USB協議為USB設備定義了一套描述設備功能和屬性的固有結構的描述符,包括標準描述符(設備描述符、培植描述符、接口描述符、端點描述符和字符串描述符),還有費標準描述符,如類描述符等。按照協議,設備描述符下可以有若干個配置描述符,每個配置描述符可以有若干個接口描述符,每個接口描述符下又可以有若干個端點描述符,字符串描述符主要用于描述一些文字信息,比如廠家名稱,產品名稱等。這篇文章的目的就是要讀出這些描述符。

                實際上,所謂描述符就是一個數據結構,不管是什么描述符,其前兩個字節的含義都是一樣的,第一個字節是描述符的長度,第二個字節是描述符的類型。

            • 設備描述符(Device Descriptor):

                struct {
                  BYTE    bLength;            // 描述符的長度,以字節為單位
                  BYTE    bDescriptorType;    // 設備描述符類型,0x01
                  WORD    bcdUSB;             // 設備支持的USB協議版本,BCD碼
                  BYTE    bDeviceClass;       // 設備類代碼(由USB-IF分配)
                  BYTE    bDeviceSubClass;    // 子類代碼
                  BYTE    bDeviceProtocol;    // 協議碼
                  BYTE    bMaxPacketSize0;    // 端點0的最大包長度(僅為8,16,32,64)
                  WORD    idVendor;           // 廠商ID(由USB-IF分配)
                  WORD    idProduct;          // 產品ID(由制造商定義)
                  WORD    bcdDevice;          // 設備發行號(BCD碼)
                  BYTE    iManufacture;       // 描述廠商信息的字符串描述符的索引值
                  BYTE    iProduct;           // 描述產品信息的字符串描述符的索引值
                  BYTE    iSerialNumber;      // 描述設備序列號信息的字符串描述符的索引值
                  BYTE    bNumConfigurations; // 可能的配置描述符的數目
                } device_descriptor

            • 配置描述符(Configuration Descriptor)

                struct {
                  BYTE    bLength;             // 描述符的長度,以字節為單位
                  BYTE    bDescriptorType;     // 配置描述符類型,0x02
                  WORD    wTotalLength;        // 配置信息的總長
                  BYTE    bNumInterfaces;      // 該配置所支持的接口數目
                  BYTE    bConfigurationValue; // 被SetCongiguration()請求用做參數來選定該配置
                  BYTE    bConfiguration;      // 描述該配置的字符串描述符的索引值
                  BYTE    bmAttributes;        // 配置特性
                  BYTE    MaxPower;            // 該配置下的總線電源耗費量,以2mA為一個單位 
                }configuration_descriptor;

                bmAttributes :b7:備用,b6:自供電,b5:遠程喚醒,b4--b0:備用

                另外,在讀取配置描述符時可以把該配置下的所有描述符全部讀出,這些描述符的總長度就是wTotalLength字段的值,讀出所有描述符后,在一個一個地拆分。

            • 接口描述符(Interface Descriptor):

                struct {
                  BYTE    bLength;            // 描述符的長度,以字節為單位
                  BYTE    bDescriptorType;    // 接口描述符類型,0x04
                  BYTE    bInterfaceNumber;   // 接口號,從0開始
                  BYTE    bAlternateSetting;  // 可選設置的索引值.
                  BYTE    bNumEndpoints;      // 此接口的端點數量。 
                  BYTE    bInterfaceClass;    // 接口類編碼(由USB-IF分配)
                  BYTE    bInterfaceSubClass; // 接口子類編碼(由USB-IF分配)
                  BYTE    bInterfaceProtocol; // 協議碼(由USB-IF分配)
                  BYTE    iInterface;         // 描述該接口的字符串描述符的索引值
                }interface_descriptor;

                bInterfaceClass:USB協議根據功能將不同的接口劃分成不同的類,如下:

                1:音頻類,2:CDC控制類,3:人機接口類(HID),5:物理類,6:圖像類,7:打印機類,8:大數據存儲類,9:集線器類,10:CDC數據類,11:智能卡類,13:安全類,220:診斷設備類,224:無線控制類,254:特定應用類,255廠商定義的設備。

            •  端點描述符(Endpoint Descriptor):

                struct {
                  BYTE    bLength;            // 描述符的長度,以字節為單位
                  BYTE    bDescriptorType;    // 端點描述符類型,0x05
                  BYTE    bEndpointAddress;   // 端點地址
                  BYTE    bmAttributes;       // 在bconfigurationValue所指的配置下的端點特性.
                  WORD    wMaxPacketSize;     // 接收/發送的最大數據報長度. 
                  BYTE    bInterval;          // 周期數據傳輸端點的時間間隙.
                }endpoint_descriptor;

                bmAttributes:bit 1:0--傳送類型,00=控制傳輸,01=實時傳輸,10=批量傳輸,11=中斷傳輸;所有其他位均保留。

            • 字符串描述符(String Descriptor):

                struct {
                  BYTE    bLength;            // 描述符的長度,以字節為單位
                  BYTE    bDescriptorType;    // 字符串描述符類型,0x03
                  char    bString[];          // UNICODE編碼的字符串
                }string_descriptor;

            • USB命令請求(USB DEVICE REQUEST)

                為了更好地協調USB主機與設備之間的數據通信,USB規范定義了一套命令請求,用于完成主機對總線上所有USB設備的統一控制,USB命令請求由統一的格式,其結構如下:

                struct {
                  BYTE  bmRequestType;  // 請求類型
                  BYTE  bRequest;       // 命令請求的編碼
                  WORD  wValue;         // 命令不同,含義不同
                  WORD  wIndex;         // 命令不同,含義不同 
                  WORD  wLength;        // 如果有數據階段,此字段為數據字節數

                }
             setup_request;

                后面我們向設備發出指令就全靠這個結構了。作為我們本文要用到的讀取USB設備描述符的命令請求,該結構各字段的填法如下。

                bmRequestType : b7--數據傳輸方向,0=主機到設備,1=設備到主機;b6:5--命令的類型,0=標準命令,1=類命令,2=廠商提供的命令,3=保留;b4:0--接收對象,0=設備,1=接口,2=端點,3=其他。我們發出的得到描述符的命令,數據傳輸方向為設備到主機,標準命令,接收對象為設備,所以該字段填0x80。

                bRequest : 標準命令的編碼如下,GET_STATUS=0;CLEAR_FEATURE=1;SET_FEATURE=3;SET_ADDRESS=5;GET_DESCRIPTOR=6;SET_DESCRIPTOR=7;GET_CONFIGURATION=8;SET_CONFIGURATION=9;GET_INTERFACE=10;SET_INTERFACE=11;SYNCH_FRAME=12。我們的命令是GET_DESCRIPTOR,所以應該填6。

                wValue : 高字節表示描述符的類型,0x01=設備描述符,0x02=配置描述符,0x03=字符串描述符,0x04=接口描述符,0x05=端點描述符,0x29=集線器類描述符,0x21=人機接口類描述符,0xFF=廠商定義的描述符。

                低字節表示表示描述符的索引值。所以,當讀取設備描述符時,該字段值為0x100,當讀取配置描述符是,應為0x03yy,其中yy為描述符的索引值。

                wIndex : 當讀取字符串描述符時,填0x0409,表示希望獲得英文字符串,其他情況下填0。

                wLength : 數據長度,一般應該填寫,描述符的第一個字節,bLength。由于我們在讀描述符時,并不知道其實際長度,通常的做法是先讀取8個字節,然后根據第一個字節bLength的值在重新讀取一次完整的描述符;注意,當讀取配置描述符的錢8個字節后,應該使用wTotalLength字段的值作為長度讀取與該配置有關的所有描述符。

            4、讀取設備描述符的范例程序

                按照我們文章的習慣,幾乎每篇文章都有一個范例程序,本文也不例外,本文的范例程序請在下面地址下載:

                http://blog.hengch.com/source/usbview.zip

                程序用C++寫成,在DJGPP下編譯通過,所以是32位保護模式下的代碼,要注意的是,DOSUSB是實模式下的驅動,所以在申請內存塊時要申請1M以內實模式可以讀取的內存,否則,在使用int 65h調用DOSUSB時一定會出現問題。

                有4個頭文件,public.h中定義了一些方便使用的數據類型,比如BYTE為char,WORD為short int等等,可以不必太關注;x86int.h中定義了調用DOS中斷所需的函數和數據結構,直到怎么使用就可以了;dosmem.h中定義了一個DOS_MEM類,主要是為了在保護模式下申請和使用DOS內存塊更為方便,也是知道其中的方法,能夠明白程序中的意義就可以了;usb.h定義了與USB協議有關的所有常數,這些常數與前面介紹的各種數據結構一一對應,由于我們是在保護模式下使用DOS內存,所以把一個內存塊映射到一個數據結構上有一些麻煩,讀取各個字段主要靠在usb.h中定義的這些常數。

                主要程序在usbview.cc中,主要思路如下:

            • USB設備的地址從1--127,所以我們從1--127做一個循環,逐一讀取USB設備描述符,直到出現“非法地址”為止。
            • 每一個USB設備的設備描述符只有一個,所以我們從讀取某個地址下的設備描述符開始。
            • 開始我們并不知道設備描述符的長度,即便我們知道其長度為18個字符,但我們仍然不知道端點0允許的包長度(設備描述符中bMaxPacketSize0字段),但我們知道包長度的最小值是8,所以我們先讀取8個字節的設備描述符。
            • 在我們得到8個字符的設備描述符后,我們就可以得到該描述符的長度和端點0的包長度,在后面發出的所有命令中,始終要把這個值填在URB結構的actual_length字段中。
            • buffer用于存放USBD返回的描述符,在使用前建議初始化一下,全部清0。
            • 要向設備發出命令請求(Request),需要先填setup_request結構,前面講過,bmRequestType=0x80,bRequest=6,這兩個字段的填法始終不變;我們現在讀取設備描述符,所以wValue=0x100,wIndex=0,wLength在首次調用時填8,第二次調用時填返回的bLength字段(應該是18)。
            • 準備好buffer和setup_request后,我們要填URB一邊與DOSUSB交互;讀取描述符是一個控制傳輸,所以transaction=0x2D(后面一直是0x2D);dev_add填上面提到的循環變量;end_point=0,因為我們總對端點0(見USB協議);buffer_off和buffer_seg分別填buffer的便宜地址和段地址;setup_buffer_off和setup_buffer_seg分別填前面setup_request結構的偏移地址和段地址;buffer_length同setup_request結構中的wLength,也可以把值設在wLength和buferr的最大長度之間,如果buffer的最大長度小于wLength,我們只能填buffer的最大長度,但這種情況下我們將得不到完整的描述符;actual_length在第一次調用時填8,以后一直填返回的bMaxPacketsize0字段;其他字段均為0。
            • 讓DS:DX指向剛剛填完的URB結構,調用軟中斷65h,DOSUSB將為你處理下面的事情。
            • 如果正常,error_code應該返回0,如果非0,含義如下:
              1--非法的設備地址;2--內部錯誤;3--非法的transation_type字段;4--非法的buffer長度。
            • 如果正常,status字段應該為0,該字段是是USB控制器返回的狀態字節,不同的控制器(OHCI或UHCI)會返回不同的值。
            • 當我們得到設備描述符后,如果設備描述符中的iManufacturer字段不為0,我們可以根據這個所引值得到相應的字符串描述符,從而顯示出廠家信息,要注意的是字符串描述符是UNICODE編碼,對于ASCII而言,它是一個ASSCII碼跟一個ASCII 0組成;同理我們可以得到產品信息和序列號信息。
            • 當我們得到了設備的設備描述符后,我們就可以知道這個設備上有多少個配置(設備描述符中的bNumConfigurations),進而通過一個循環得到所有的配置描述符及其配置下的所有描述符。
            • 讀取配置描述符的方法與讀取設備描述符大同小異,也是先讀取8個字節,然后根據返回的內容再讀取所有的描述符內容,要注意的是,實際上,我們不能單獨得到接口描述符和端點描述符,唯一的辦法是把一個配置下的描述符全部讀出來,所以setup_request結構中的wLength字段一定要與配置描述符中返回的wTotalLength值一致才行。
            • 剩下的事情就是如何顯示我們得到的描述符,我想這對每一位讀者而言都不是什么困難的事。
            posted @ 2010-11-24 14:06 wrh 閱讀(2912) | 評論 (0)編輯 收藏
            一、單符號
            ~

            ① 在for中表示使用增強的變量擴展。
            ② 在%var:~n,m%中表示使用擴展環境變量指定位置的字符串。
            ③ 在set/a中表示一元運算符,將操作數按位取反。

            !
            ① 在set /a中一元運算符,表示邏輯非。比如set /a a=!0,這時a就表示邏輯1。

            @
            ① 隱藏命令行本身的回顯,常用于批處理中。

            $
            ① 在findstr命令里面表示一行的結束。
            ② 在prompt命令里面,表示將其后的字符轉義(符號化或者效果化)。

            %
            ① 在set /a中的二元運算符,表示算術取余。
            ② 命令行環境下,在for命令in前,后面接一個字符(可以是字母、數字或者一些特定字符),表示指定一個循環或者遍歷指標變量。
            ③ 批處理中,后接一個數字表示引用本批處理當前執行時的指定的參數。
            ④ 其它情況下,%將會被脫去(批處理)或保留(命令行)

            ^
            ① 取消特定字符的轉義作用,比如& | > < ! "等,但不包括%。比如要在屏幕顯示一些特殊的字符,比如> >> | ^ &等符號時,就可以在其前面加一個^符號來顯示這個^后面的字符了,^^就是顯示一個^,^|就是顯示一個|字符了;
            ② 在set/a中的二元運算符,表示按位異或。
            ③ 在findstr/r的[]中表示不匹配指定的字符集。

            &
            ① 命令連接字符。比如我要在一行文本上同時執行兩個命令,就可以用&命令連接這兩個命令。
            ② 在set/a中是按位與。

            *
            ① 代表任意個任意字符,就是我們通常所說的"通配符";比如想在c盤的根目錄查找c盤根目錄里所有的文本文件(.txt),那么就可以輸入命令"dir c:\*.txt"。
            ② 在set /a中的二元運算符,表示算術乘法。
            ③ 在findstr/r中表示將前一個字符多次匹配。

            -
            ① 范圍表示符,比如日期的查找,for命令里的tokens操作中就可以用到這個字符。
            ② 在findstr/r中連接兩個字符表示匹配范圍。
            ③ -跟在某些命令的/后表示取反向的開關。
            ④ 在set /a中:
            1.表示一個負數。
            2.表示算術減運算。

            +
            ① 主要是在copy命令里面會用到它,表示將很多個文件合并為一個文件,就要用到這個+字符了。
            ② 在set/a中的二元運算符,表示算術加法。

            :
            ① 標簽定位符,表示其后的字符串為以標簽,可以作為goto命令的作用對象。比如在批處理文件里面定義了一個":begin"標簽,用"goto begin"命令就可以轉到":begin"標簽后面來執行批處理命令了。
            ② 在%var:string1=string2%中分隔變量名和被替換字串關系。

            |
            ① 管道符,就是將上一個命令的輸出,作為下一個命令的輸入."dir /a/b |more"就可以逐屏的顯示dir命令所輸出的信息。
            ② 在set/a中的二元運算符,表示按位或。
            ③ 在幫助文檔中表示其前后兩個開關、選項或參數是二選一的。

            /
            ① 表示其后的字符(串)是命令的功能開關(選項)。比如"dir /s/b/a-d"表示"dir"命令指定的不同的參數。
            ② 在set/a中表示除法。

            >
            ① 命令重定向符,將其前面的命令的輸出結果重新定向到其后面的設備中去,后面的設備中的內容被覆蓋。比如可以用"dir > lxmxn.txt"將"dir"命令的結果輸出到"lxmxn.txt"這個文本文件中去。
            ② 在findstr/r中表示匹配單詞的右邊界,需要配合轉義字符\使用。

            <
            ① 將其后面的文件的內容作為其前面命令的輸入。
            ② 在findstr/r中表示匹配單詞的左邊界,需要配合轉義字符\使用。

            =
            ① 賦值符號,用于變量的賦值。比如"set a=windows"的意思意思是將"windows"這個字符串賦給變量"a"。
            ② 在set/a中表示算術運算,比如"set /a x=5-6*5"。

            \
            ① 這個"\"符號在有的情況下,代表的是當前路徑的根目錄.比如當前目錄在c:\windows\system32下,那么你"dir \"的話,就相當與"dir c:\"。
            ② 在findstr/r中表示正則轉義字符。

            ,
            ① 在set /a中表示連續表達式的分割符。
            ② 在某些命令中分割元素。

            .
            ① 在路徑的\后緊跟或者單獨出現時:
            一個.表示當前目錄。
            兩個.表示上一級目錄。
            ② 在路徑中的文件名中出現時:
            最后的一個.表示主文件名與擴展文件名的分隔。

            ?
            ① 在findstr/r中表示在此位置匹配一個任意字符。
            ② 在路徑中表示在此位置通配任意一個字符。
            ③ 緊跟在/后表示獲取命令的幫助文檔。

            二、多符號(符號不能分隔)

            &&
            ① 連接兩個命令,當&&前的命令成功時,才執行&&后的命令。

            ||
            ① 連接兩個命令,當||前的命令失敗時,才執行||后的命令。

            >&
            ① 將一個句柄的輸出寫入到另一個句柄的輸入中。

            <&
            ① 從一個句柄讀取輸入并將其寫入到另一個句柄輸出中。

            %%
            ① 兩個連續的%表示在預處理中脫為一個%。
            ② 批處理中,在for語句的in子句之前,連續兩個%緊跟一個字符(可以是字母、數字和一些特定字符),表示指定一個循

            環或者遍歷指標變量。
            ③ 批處理中,在for語句中,使用與in之前指定的指標變量相同的串,表示引用這個指標變量。

            >>
            ① 命令重定向符,將其前面的命令的輸出結果追加到其后面的設備中去。
            ② 在set /a中的二元運算符,表示邏輯右移。

            ==
            ① 在if命令中判斷==兩邊的元素是否相同。

            <<
            ① 在set /a中的二元運算符,表示邏輯左移。

            +=
            ① 在set /a中的二元運算符。例如set /a a+=b表示將a加上b的結果賦值給a。

            -=
            ① 在set /a中的二元運算符。例如set /a a-=b表示將a減去b的結果賦值給a。

            *=
            ① 在set /a中的二元運算符。例如set /a a*=b表示將a乘以b的結果賦值給a。

            /=
            ① 在set /a中的二元運算符。例如set /a a/=b表示將a加上b的結果賦值給a。

            %=
            ① 在set /a中的二元運算符。例如set /a a%=b表示將a除以b的余數賦值給a。
            【注:命令行可以直接用 set /a a%=b ,在批處理里面可以用 set /a a%%=b 。】

            ^=
            ① 在set /a中的二元運算符。例如set /a a"^="b表示將a與b按位異的結果賦值給a。
            【注:這里 "^=" 加引號是為了防止^被轉義,下同。】

            &=
            ① 在set /a中的二元運算符。例如set /a a"&="b表示將a與b按位與的結果賦值給a。

            |=
            ① 在set /a中的二元運算符。例如set /a a"|="b表示將a與b按位或的結果賦值給a。

            <<=
            ① 在set /a中的二元運算符。例如set /a a"<<="b表示將a按位左移b位的結果賦值給a。

            >>=
            ① 在set /a中的二元運算符。例如set /a a">>="b表示將a按位右移b位的結果賦值給a。

            \<
            ① 在findstr的一般表達式中表示字的開始處。

            \>
            ① 在findstr的一般表達式中表示字的結束處。

            三、雙符號對(兩個符號之間須指定字符串)

            ! !
            ① 當啟用變量延遲時,使用!!將變量名擴起來表示對變量值的引用。

            ' '
            ① 在for/f中表示將它們包含的內容當作命令行執行并分析其輸出。
            ② 在for/f "usebackq"中表示將它們包含的字符串當作字符串分析。

            ( )
            ① 命令包含或者是具有優先權的界定符,比如for命令要用到這個(),我們還可以在if,echo等命令中見到它的身影。
            ② 在set /a中表示表達式分組。

            " "
            ① 界定符,在表示帶有空格的路徑時常要用""來將路徑括起來,在一些命令里面也需要" "符號。
            ② 在for/f中將表示它們包含的內容當作字符串分析。
            ③ 在for/f "usebackq"表示它們包含的內容當作文件路徑并分析其文件的內容。
            ④ 在其它情況下表示其中的內容是一個完整的字符串,其中的>、>>、<、&、|、空格等不再轉義。

            ` `
            ① 在for/f中表示它們所包含的內容當作命令行執行并分析它的輸出。

            % %
            ① 使用兩個單獨的%包含一個字符串表示引用以此串為名的環境變量。比如一個%time%可以擴展到當前的系統時間。

            [ ]
            ① 在幫助文檔表示其中的開關、選項或參數是可選的。
            ② 在findstr /r中表示按其中指定的字符集匹配。




            1.^取消特殊符號的作用
            例子:echo ^> >1.txt  將“>”輸出到1.txt中

            2.","某些時候可以當空格使用
            例子:echo,  dir,c:\

            3.";"當命令相同時,可以將不同目標用;來隔離
            例子:dir c:\;d:\

            4.= 賦值符號,變量賦值。如"set a=windows"是將"windows"這個字符串賦給變量"a"。
            在set/a中表示算術運算,比如"set /a x=5-6*5"。
            命令:SET /P Choice=選擇:
            顯示:選擇:   提示輸入“abc”
            命令:echo %choice%
            顯示:abc

            @echo off
            set txt1=%time:~0,2%
            ::當前小時
            set txt2=%time:~3,2%
            ::當前分鐘
            set txt3=%time:~6,2%
            ::當前秒
            set time=%txt1%%txt2%%txt3%
            echo 當前時間:%txt1%:%txt2%:%txt3%

            5.%在for循環中,循環變量引用格式:%%變量名.
            如:SUM.bat
            @echo off
            ::求1+2+3+…
            set sum=0
            for /l %%i in (1,1,%1) do set /a sum+=%%i
            echo 1+2+3+…+100=%sum%
            說明:在命令行下輸入SUM 100,顯示結果為:
            1+2+3+…+100=505

            6.>>想用批處理實現向s.txt中多次分別導入文本例如:“aaaa","bbbb","cccc"
            實現s.txt內效果如:
            aaaabbbbcccc
            可以執行
            >>s.txt set/p="aaaa" <nul   同set/p="aaaa" >>s.txt <nul
            >>s.txt set/p="bbbb" <nul
            >>s.txt set/p="cccc" <nul

            7.一個刪除行的批處理
            有一A.TXT文件,在其中找到HZF時,刪除含有HZF字符串的行(不分大小寫).
            findstr /ivc:"HZF" a.txt >b.txt
            @dir |findstr /n .*   給DIR文件打上行號
            find /v "HZF" a.txt>b.txt

            8.用批處理刪除文本每行的前幾個字符
            現在有a.txt 內容如下
            \t (00:00:02) 123856

            \t (00:00:03) Hi!lg
            \i (00:00:03) traps
            \w (00:00:03) Diele
            \i (00:00:07) open
            現在想把 ) 以及前面的內容刪除 只剩下
            123856

            Hi!ED
            traps
            Diele
            open

            for /f "tokens=3 delims= " %%a in (a.txt) do echo %%a>>b.txt     以空格為分隔符取第3個段內容,如果為"tokens=2 delims= "則會選取包括括號以內的內容tokens只能是數字 delims是多個字符,如()

            9.一文本:D:\w\tongji.txt 共2000行,現在需要刪除前1500行,保留最后的500行。
            for /f "SKIP=1500 tokens=*" %%i in (D:\w\tongji.txt ) do (echo %%i>>hh.txt)
            刪除空白行
            for /f "eol==" %%a in (aa.txt) do echo %%a>>b.txt 刪除首字符為=的所有行及空白行
            for /f "delims=" %%i in (aa.txt) do echo %%i>>b.txt 刪除空白行
            實際上"eol="和"delims="均可刪除空白行,即只要""內有內容,即可刪除空白行 (自注:如果缺少delims=項,則默認分隔符為空格)

            10.我IP是隨機的,我用以下命令:
            ipconfig /all > c:\1.txt
            find "IP Address" c:\1.txt >> c:\ip.txt
            自注(可用ipconfig /all|find "IP Address")
            那么就會得出ip.txt,里面的內容為
            ---------- C:\1.TXT
            IP Address. . . . . . . . . . . . : 192.168.0.5
            IP Address. . . . . . . . . . . . : 218.15.245.210

            我只是想導出的內容只有IP,也就是說ip.txt里面只有192.168.0.5和218.15.245.210

            @echo off
            for /f "tokens=2 delims=:" %%a in ('ipconfig /all^|find "IP Address"') do (echo IP地址為:%%a)
            pause 我IP是隨機的,我用以下命令:
            ipconfig /all > c:\1.txt
            find "IP Address" c:\1.txt >> c:\ip.txt
            自注(可用ipconfig /all|find "IP Address")
            那么就會得出ip.txt,里面的內容為
            ---------- C:\1.TXT
            IP Address. . . . . . . . . . . . : 192.168.0.5
            IP Address. . . . . . . . . . . . : 218.15.245.210

            我只是想導出的內容只有IP,也就是說ip.txt里面只有192.168.0.5和218.15.245.210

            @echo off
            for /f "tokens=2 delims=:" %%a in ('ipconfig /all^|find "IP Address"') do (echo IP地址為:%%a)
            pause

             

            11.用批處理命令刪除文本文件的整行內容
            用批處理命令bat解決
            例如文本文件a.txt的內容如下:
            001,李明,語文,90分
            002,李明,數學,70分
            003,李明,離散數學,63分
            004,李明,英語,60分
            005,陳紅,語文,80分
            006,陳紅,數學,60分
            007,陳紅,離散數學,78分
            008,陳紅,英語,65分
            求:如果某行有“數學”或者“英語”這個詞,則刪除該行的內容。
            要求通過批處理得出如下結果:
            001,李明,語文,90分
            003,李明,離散數學,63分
            005,陳紅,語文,80分
            007,陳紅,離散數學,78分

            @echo off
            findstr /i /v ",數學 英語" "aa.txt">>jg.txt
            findstr /iv /c:",數學" /c:"英語" "aa.txt">>jg.txt (此命令與上行等效)
            findstr /iv /c:",數學 英語" "aa.txt">>jg.txt     (此命令失效,因為沒有“,數學 英語”這樣的內容)

            12.不換行顯示文本內容
            for /f %%c in (aa.txt) do set /p=%%c>>c.txt<nul
            for /f %%a in (aa.txt) do set /p d+=%%a<nul>>c.txt
               換行顯示文本內容
            for /f %%c in (aa.txt) do echo %%C>>c.txt

            13.變量賦值
            set hzf=abcd
            echo %hzf%        顯示為abcd

            posted @ 2010-11-22 16:22 wrh 閱讀(717) | 評論 (0)編輯 收藏
            http://www.nirsoft.net/utils/usb_devices_view.html
            USB類代碼
            http://www.usb.org/developers/defined_class/#BaseClassE0h
            posted @ 2010-11-21 15:40 wrh 閱讀(336) | 評論 (0)編輯 收藏
                 摘要: # # List of USB ID's # # Maintained by Stephen J. Gowdy <linux.usb.ids@gmail.com> # If you have any new entries, please submit them via # http://www.linux-usb.org/usb-ids.html # or send e...  閱讀全文
            posted @ 2010-11-21 15:37 wrh 閱讀(7553) | 評論 (0)編輯 收藏

            #
            # List of USB ID's
            $Id: usb.ids,v 1.378 2009/03/08 12:48:06

            #
            # Maintained by Stephen J. Gowdy <sgowdy+usb.ids@gmail.com>
            # If you have any new entries, send them to the maintainer.
            # Send entries as patches (diff -u old new).
            # The latest version can be obtained from
            #  http://www.linux-usb.org/usb.ids
            #
            # $Id: usb.ids,v 1.378 2009/03/08 12:48:06 gowdy Exp $
            #

            # Vendors, devices and interfaces. Please keep sorted.

            # Syntax:
            # vendor vendor_name
            # device device_name     <-- single tab
            #   interface interface_name   <-- two tabs

            Download 1

            Download 2

            posted @ 2010-11-21 15:35 wrh 閱讀(494) | 評論 (0)編輯 收藏
            代碼:見光盤GetSerialNo

            #include "windows.h"
            #include "PlkUsbIo.h"
            #include <malloc.h>

            #define NUM_HCS_TO_CHECK 10

            /******************************************************************/
            bool EnumUsbDevice();
            PCHAR GetDriverKeyName(HANDLE Hub, ULONG ConnectionIndex);
            PCHAR GetHCDDriverKeyName(HANDLE HCD);
            PCHAR GetRootHubName(HANDLE HostController);
            PCHAR WideStrToMultiStr(PWCHAR WideStr);
            bool GetStringDescriptor (
                    HANDLE hHubDevice,
                    ULONG   ConnectionIndex,
                    UCHAR   DescriptorIndex    ,
                    CHAR * outBuff);
               
            /******************************************************************/

            int main(int argc, char* argv[])
            {
                EnumUsbDevice();
                return 0;
            }

            bool EnumUsbDevice()
            {
                char        HCName[16];
                int         HCNum;
                HANDLE      hHCDev;
                PCHAR       rootHubName;

                ULONG       index;
                BOOL        success;

                PUSB_NODE_CONNECTION_INFORMATION    connectionInfo;
                HANDLE hHubDevice;

                for (HCNum = 0; HCNum < NUM_HCS_TO_CHECK; HCNum++)
                {
                    wsprintf(HCName, "\\\\.\\HCD%d", HCNum);
                    hHCDev = CreateFile(HCName,
                        GENERIC_WRITE,
                        FILE_SHARE_WRITE,
                        NULL,
                        OPEN_EXISTING,
                        0,
                        NULL);
                    if (hHCDev == INVALID_HANDLE_VALUE)   
                        break;
                   
                    PCHAR driverKeyName, deviceDesc;
                    driverKeyName = GetHCDDriverKeyName(hHCDev);
                    if(driverKeyName == NULL)
                        goto end;   
                   
               
                    ULONG nBytes;
                    rootHubName =(char*) GetRootHubName(hHCDev);
                    if(rootHubName==NULL)
                        goto end;
               
                    PUSB_NODE_INFORMATION HubInfo;
                    HubInfo = (PUSB_NODE_INFORMATION)malloc(sizeof(USB_NODE_INFORMATION));
                    PCHAR deviceName;
                    deviceName = (PCHAR)malloc(strlen(rootHubName) + sizeof("\\\\.\\"));
                    if (rootHubName != NULL)
                    {
                        strcpy(deviceName, "\\\\.\\");
                        strcpy(deviceName + sizeof("\\\\.\\") - 1, rootHubName);
                        hHubDevice = CreateFile(deviceName,
                            GENERIC_WRITE,
                            FILE_SHARE_WRITE,
                            NULL,
                            OPEN_EXISTING,
                            0,
                            NULL);
                        free(deviceName);
                        if (hHubDevice == INVALID_HANDLE_VALUE)
                            goto end;
                       
                        success = DeviceIoControl(hHubDevice,
                            IOCTL_USB_GET_NODE_INFORMATION,
                            HubInfo,
                            sizeof(USB_NODE_INFORMATION),
                            HubInfo,
                            sizeof(USB_NODE_INFORMATION),
                            &nBytes,
                            NULL);
                        if (!success)
                            goto end;
                       

                    }

                    int port;
                    port=HubInfo->u.HubInformation.HubDescriptor.bNumberOfPorts;
                    for (index=1; index <= port; index++)
                    {
                        ULONG nBytes;
                        nBytes = sizeof(USB_NODE_CONNECTION_INFORMATION) +
                            sizeof(USB_PIPE_INFO) * 30;
                        connectionInfo = (PUSB_NODE_CONNECTION_INFORMATION)malloc(nBytes);
                        if (connectionInfo == NULL)
                            goto end;
                       
                        connectionInfo->ConnectionIndex = index;
                        success = DeviceIoControl(hHubDevice,
                            IOCTL_USB_GET_NODE_CONNECTION_INFORMATION,
                            connectionInfo,
                            nBytes,
                            connectionInfo,
                            nBytes,
                            &nBytes,
                            NULL);
                        if (!success)
                        {
                            free(connectionInfo);
                            goto end;
                        }
                   

                        deviceDesc = NULL;
                        if (connectionInfo->ConnectionStatus != NoDeviceConnected)
                        {
                            driverKeyName = GetDriverKeyName(hHubDevice,
                                index);
                            if (driverKeyName)
                            {
                                free(driverKeyName);
                            }
                        }

                        if (connectionInfo->ConnectionStatus == DeviceConnected)
                        {
                            //取出序列號索引
                            UCHAR nSerialno = connectionInfo->DeviceDescriptor.iSerialNumber;
                            CHAR OutBuff[20] = {0};
                            GetStringDescriptor(hHubDevice,connectionInfo->ConnectionIndex,nSerialno,OutBuff);

                            //判斷序列號是否有效
                            if(
            序列號是否有效)
                            {
                                  CloseHandle(hHubDevice);
                                  CloseHandle(hHCDev);
                                 return true;
                            }
                        }

                    }
            end:;
                }

                CloseHandle(hHubDevice);
                CloseHandle(hHCDev);
                return false;
            }

            PCHAR GetDriverKeyName(HANDLE Hub, ULONG ConnectionIndex)
            {
                BOOL                                success;
                ULONG                               nBytes;
                USB_NODE_CONNECTION_DRIVERKEY_NAME driverKeyName;
                PUSB_NODE_CONNECTION_DRIVERKEY_NAME driverKeyNameW;
                PCHAR                               driverKeyNameA;

                driverKeyNameW = NULL;
                driverKeyNameA = NULL;

                driverKeyName.ConnectionIndex = ConnectionIndex;

                success = DeviceIoControl(Hub,
                    IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME,
                    &driverKeyName,
                    sizeof(driverKeyName),
                    &driverKeyName,
                    sizeof(driverKeyName),
                    &nBytes,
                    NULL);

                if (!success)
                {
                    goto GetDriverKeyNameError;
                }

                nBytes = driverKeyName.ActualLength;

                if (nBytes <= sizeof(driverKeyName))
                {
                    goto GetDriverKeyNameError;
                }

                driverKeyNameW = (PUSB_NODE_CONNECTION_DRIVERKEY_NAME)malloc(nBytes);

                if (driverKeyNameW == NULL)
                {
                    goto GetDriverKeyNameError;
                }

                driverKeyNameW->ConnectionIndex = ConnectionIndex;

                success = DeviceIoControl(Hub,
                    IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME,
                    driverKeyNameW,
                    nBytes,
                    driverKeyNameW,
                    nBytes,
                    &nBytes,
                    NULL);

                if (!success)
                {
                    goto GetDriverKeyNameError;
                }
                driverKeyNameA = WideStrToMultiStr(driverKeyNameW->DriverKeyName);
                free(driverKeyNameW);

                return driverKeyNameA;


            GetDriverKeyNameError:
                if (driverKeyNameW != NULL)
                {
                    free(driverKeyNameW);
                    driverKeyNameW = NULL;
                }

                return NULL;
            }
            PCHAR GetRootHubName(HANDLE HostController)
            {
                BOOL                success;
                ULONG               nBytes;
                USB_ROOT_HUB_NAME   rootHubName;
                PUSB_ROOT_HUB_NAME rootHubNameW;
                PCHAR               rootHubNameA;

                rootHubNameW = NULL;
                rootHubNameA = NULL;

                success = DeviceIoControl(HostController,
                    IOCTL_USB_GET_ROOT_HUB_NAME,
                    0,
                    0,
                    &rootHubName,
                    sizeof(rootHubName),
                    &nBytes,
                    NULL);

                if (!success)
                {
                    goto GetRootHubNameError;
                }

                nBytes = rootHubName.ActualLength;

                rootHubNameW =(PUSB_ROOT_HUB_NAME) malloc(nBytes);

                if (rootHubNameW == NULL)
                {

                    goto GetRootHubNameError;
                }

                success = DeviceIoControl(HostController,
                    IOCTL_USB_GET_ROOT_HUB_NAME,
                    NULL,
                    0,
                    rootHubNameW,
                    nBytes,
                    &nBytes,
                    NULL);

                if (!success)
                {
                    goto GetRootHubNameError;
                }

                rootHubNameA = WideStrToMultiStr(rootHubNameW->RootHubName);
                free(rootHubNameW);

                return rootHubNameA;


            GetRootHubNameError:
                if (rootHubNameW != NULL)
                {
                    free(rootHubNameW);
                    rootHubNameW = NULL;
                }

                return NULL;
            }
            PCHAR GetHCDDriverKeyName(HANDLE HCD)
            {
                BOOL                    success;
                ULONG                   nBytes;
                USB_HCD_DRIVERKEY_NAME driverKeyName;
                PUSB_HCD_DRIVERKEY_NAME driverKeyNameW;
                PCHAR                   driverKeyNameA;

                driverKeyNameW = NULL;
                driverKeyNameA = NULL;

                success = DeviceIoControl(HCD,
                    IOCTL_GET_HCD_DRIVERKEY_NAME,
                    &driverKeyName,
                    sizeof(driverKeyName),
                    &driverKeyName,
                    sizeof(driverKeyName),
                    &nBytes,
                    NULL);

                if (!success)
                {
                    goto GetHCDDriverKeyNameError;
                }

                nBytes = driverKeyName.ActualLength;

                if (nBytes <= sizeof(driverKeyName))
                {
                    goto GetHCDDriverKeyNameError;
                }

                driverKeyNameW =(PUSB_HCD_DRIVERKEY_NAME) malloc(nBytes);

                if (driverKeyNameW == NULL)
                {
                    goto GetHCDDriverKeyNameError;
                }

                success = DeviceIoControl(HCD,
                    IOCTL_GET_HCD_DRIVERKEY_NAME,
                    driverKeyNameW,
                    nBytes,
                    driverKeyNameW,
                    nBytes,
                    &nBytes,
                    NULL);

                if (!success)
                {
                    goto GetHCDDriverKeyNameError;
                }

                driverKeyNameA = WideStrToMultiStr(driverKeyNameW->DriverKeyName);
                free(driverKeyNameW);

                return driverKeyNameA;


            GetHCDDriverKeyNameError:
                if (driverKeyNameW != NULL)
                {
                    free(driverKeyNameW);
                    driverKeyNameW = NULL;
                }

                return NULL;
            }

            PCHAR WideStrToMultiStr(PWCHAR WideStr)
            {
                ULONG nBytes;
                PCHAR MultiStr;
                nBytes = WideCharToMultiByte(
                    CP_ACP,
                    0,
                    WideStr,
                    -1,
                    NULL,
                    0,
                    NULL,
                    NULL);

                if (nBytes == 0)
                {
                    return NULL;
                }
                MultiStr =(PCHAR) malloc(nBytes);

                if (MultiStr == NULL)
                {
                    return NULL;
                }
                nBytes = WideCharToMultiByte(
                    CP_ACP,
                    0,
                    WideStr,
                    -1,
                    MultiStr,
                    nBytes,
                    NULL,
                    NULL);

                if (nBytes == 0)
                {
                    free(MultiStr);
                    return NULL;
                }

                return MultiStr;
            }


            bool    GetStringDescriptor (
                    HANDLE hHubDevice,
                    ULONG   ConnectionIndex,
                    UCHAR   DescriptorIndex    ,
                    CHAR * outBuff
                    )
                {
                    BOOL    success;
                    ULONG   nBytes;
                    ULONG   nBytesReturned;

                    UCHAR   stringDescReqBuf[sizeof(USB_DESCRIPTOR_REQUEST) + MAXIMUM_USB_STRING_LENGTH];

                    PUSB_DESCRIPTOR_REQUEST stringDescReq;
                    PUSB_STRING_DESCRIPTOR stringDesc;

                    nBytes = sizeof(stringDescReqBuf);

                    stringDescReq = (PUSB_DESCRIPTOR_REQUEST)stringDescReqBuf;
                    stringDesc = (PUSB_STRING_DESCRIPTOR)(stringDescReq+1);

               
                    ::ZeroMemory(stringDescReq,nBytes);
                    stringDescReq->ConnectionIndex = ConnectionIndex;
                    stringDescReq->SetupPacket.wValue = (USB_STRING_DESCRIPTOR_TYPE << 8) | DescriptorIndex;
                    stringDescReq->SetupPacket.wIndex = GetSystemDefaultLangID();
                    stringDescReq->SetupPacket.wLength = (USHORT)(nBytes - sizeof(USB_DESCRIPTOR_REQUEST));

                    success = DeviceIoControl(hHubDevice,IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION,
                                            stringDescReq,nBytes,
                                            stringDescReq,nBytes,
                                            &nBytesReturned,NULL);

                    if (!success || nBytesReturned < 2)
                        return false;

                    if (stringDesc->bDescriptorType != USB_STRING_DESCRIPTOR_TYPE)
                        return false;

                    if (stringDesc->bLength != nBytesReturned - sizeof(USB_DESCRIPTOR_REQUEST))
                        return false;

                    if (stringDesc->bLength % 2 != 0)
                        return false;

                    WCHAR * wChar = new WCHAR[stringDesc->bLength + 1];
                    memcpy(wChar,stringDesc->bString,stringDesc->bLength);
                    char *szTemp = WideStrToMultiStr(wChar);
                    lstrcpy(outBuff, szTemp);
                   
                    if(szTemp)
                    delete []szTemp;
                   
                    if(wChar)
                    delete []wChar;
                    return true;
                }

            分析:

            1)首先假定有10個usb接口
                #define NUM_HCS_TO_CHECK 10

            2)循環打開這10個usb端口,如果端口沒有這么多,調用
            CreateFile,就會返回 INVALID_HANDLE_VALUE。
                for (HCNum = 0; HCNum < NUM_HCS_TO_CHECK; HCNum++)
                {
                    wsprintf(HCName, "\\\\.\\HCD%d", HCNum);
                    hHCDev = CreateFile(HCName,
                        GENERIC_WRITE,
                        FILE_SHARE_WRITE,
                        NULL,
                        OPEN_EXISTING,
                        0,
                        NULL);
                    if (hHCDev == INVALID_HANDLE_VALUE)   
                        break;

            3)獲取root hub
                   ULONG nBytes;
                    rootHubName =(char*) GetRootHubName(hHCDev);
                    if(rootHubName==NULL)
                        goto end;

            4) 遍歷連接在root hub上的節點
                    int port;
                    port=HubInfo->u.HubInformation.HubDescriptor.bNumberOfPorts;
                    for (index=1; index <= port; index++)
                    {
                        ULONG nBytes;
                        nBytes = sizeof(USB_NODE_CONNECTION_INFORMATION) +
                            sizeof(USB_PIPE_INFO) * 30;
                        connectionInfo = (PUSB_NODE_CONNECTION_INFORMATION)malloc(nBytes);
                        if (connectionInfo == NULL)
                            goto end;
                       
                        connectionInfo->ConnectionIndex = index;
                        success = DeviceIoControl(hHubDevice,
                            IOCTL_USB_GET_NODE_CONNECTION_INFORMATION,
                            connectionInfo,
                            nBytes,
                            connectionInfo,
                            nBytes,
                            &nBytes,
                            NULL);
                        if (!success)
                        {
                            free(connectionInfo);
                            goto end;
                        }

            5)根據節點的連接狀態,獲取節點信息,得到序列號。
                if (connectionInfo->ConnectionStatus == DeviceConnected)
                        {
                            //取出序列號索引
                            UCHAR nSerialno = connectionInfo->DeviceDescriptor.iSerialNumber;
                            CHAR OutBuff[20] = {0};
                            GetStringDescriptor(hHubDevice,connectionInfo->ConnectionIndex,nSerialno,OutBuff);

            6)得到序列號的方法在理論部分已經詳細說明了,對應的函數是
            GetStringDescriptor,這里不再重復
            posted @ 2010-11-21 15:24 wrh 閱讀(1621) | 評論 (0)編輯 收藏

            在前面的文章中,我們已經給出了USB協議的鏈接地址,從這篇文章起,我們會涉及到許多USB 1.1的內容,我們的指導思想是先從熟悉USB 1.1協議入手,先使用現成的HCD和USBD,直接面對客戶端驅動編程,盡快看到成果,使讀者對USB的開發充滿信心,進而去研究USBD和HCD的編程方法。請讀者自行閱讀協議,文章中有關協議的詳細情況,由于會涉及非常多的文字,恕不能過多解釋。
            1、USB系統主機端的軟件結構
                一般來說,教科書或者協議上都會把USB主機端的軟件說成有三層,第一層叫主機控制器驅動程序HCD(Host Controller Driver),第二層叫USB驅動程序USBD(USB Driver),第三層叫客戶端驅動程序(Client Driver);實際上,我們實際看到的東西,往往HCD和USBD是由一個程序完成的,比如windows就提供了HCD和USBD,如果你自己開發了一個USB設備,只需要在HCD和USBD上面開發一個客戶端驅動程序即可;linux也是同樣,linux內核已經提供了HCD和USBD;所以在 windows和linux下我們基本上沒有開發HCD和USBD的必要,而且linux還提供源代碼;但DOS就不一樣了,DOS本身對USB沒有任何支持,所以要想在DOS下徹底玩轉USB,需要研究HCD、USBD和客戶端驅動程序。
            2、DOSUSB介紹
                很顯然,HCD和USBD更加底層一些,需要理解的東西也更多一些;如果我們能夠繞過HCD和USBD,直接從客戶端驅動程序入手,將會容易許多。幸運的是我們可以找到一個免費的DOS下的USB驅動程序,叫DOSUSB,該驅動程序實現了大部分的HCD和USBD的功能,使我們進行USB編程的好幫手。
                DOSUSB目前還沒有實現EHIC的驅動,也就是說還不支持USB2.0,這也是我們從USB 1.1開始的原因之一,另一方面,由于USB2.0是兼容USB1.1的,所以,即便你在USB2.0的設備下,仍然可以使用USB1.1的驅動程序,只不過不能實現480MB/秒的傳送速度而已。
                下面我們介紹一下DOSUSB。DOSUSB的官方網站如下:
                http://www.usbdos.net

                可以從其官方網站上下載DOGUSB的最新版本,當前版本是1.1.1。或者在下面在下面網址下載這個版本的DOSUSB。

                http://blog.hengch.com/software/dosusb/dosusb.zip

                DOSUSB可以在非商業領域免費使用,如果肯花費費用,可以購買到源代碼,從其官方網站的論壇上看到,在2006年9月作者開出的源代碼的價格是 1000歐元。

                DOSUSB的安裝十分簡便,只需要解壓縮到某一個目錄下即可,比如放在c:\dosusb目錄下,請自行閱讀DOSUSB自帶的文檔,使用也非常簡單,在DOS提示符下鍵入dosusb執行即可。

                c:\dosusb>dosusb

                缺省情況下,DOSUSB使用int 65h作為其驅動的調用軟中斷,如果和你的系統有沖突,在運行dosusb時可以加參數/I,請自行閱讀DOSUSB的文檔。

                DOSUSB通過一個叫做URB(USB Request Block)的數據結構與客戶端驅動程序進行通訊,這一點和linux非常相似,估計作者參考了linux下的源代碼,在DOSUSB文檔里給出了這個結構的定義,如下:
                struct {
                  BYTE  transaction_type;   // 設置事務(控制傳輸)(2Dh),輸入事務(69h)輸出事務(E1h)
                  BYTE  chain_end_flag;     // 備用
                  BYTE  dev_add;            // 設備地址
                  BYTE  end_point;          // 端點號
                  BYTE  error_code;         // 錯誤嗎
                  BYTE  status;             // 設備狀態
                  WORD  transaction_flags;  // 備用
                  WORD  buffer_off;         // 接收/發送緩沖區偏移地址
                  WORD  buffer_seg;         // 接收/發送緩沖區段地址
                  WORD  buffer_length;      // 接收/發送緩沖區長度
                  WORD  actual_length;      // 接收/發送時每個包的最大長度
                  WORD  setup_buffer_off;   // setup_request結構的偏移地址
                  WORD  setup_buffer_seg;   // setup_request結構的段地址
                  WORD  start_frame;        // 備用
                  WORD  nr_of_packets;      // >0時會啟動實時傳輸
                  WORD  int_interval;       // 備用
                  WORD  error_count;        // 重試的次數
                  WORD  timeout;            // 備用
                  WORD  next_urb_off;       // 備用
                  WORD  next_urb_seg;       // 備用
                } urb                       // 32字節
                之所以列出這個結構,是因為我們將使用這個結構與USBD進行交互。關于結構中字段的定義,在DOSUSB的文檔中有詳細的說明。除備用字段不需要填以外,error_code和status由DOSUSB返回,故填0即可,后面還會介紹更詳細的填寫方法。

                在DOSUSB的發行包中,有一個sample目錄,里面有很多例子,但大多是使用power basic寫的,不過仍然有很好的參考價值。

            3、 USB 1.1協議中的一些內容

                USB協議為USB設備定義了一套描述設備功能和屬性的固有結構的描述符,包括標準描述符(設備描述符、培植描述符、接口描述符、端點描述符和字符串描述符),還有費標準描述符,如類描述符等。按照協議,設備描述符下可以有若干個配置描述符,每個配置描述符可以有若干個接口描述符,每個接口描述符下又可以有若干個端點描述符,字符串描述符主要用于描述一些文字信息,比如廠家名稱,產品名稱等。這篇文章的目的就是要讀出這些描述符。

                實際上,所謂描述符就是一個數據結構,不管是什么描述符,其前兩個字節的含義都是一樣的,第一個字節是描述符的長度,第二個字節是描述符的類型。

            • 設備描述符(Device Descriptor):

                struct {
                  BYTE    bLength;            // 描述符的長度,以字節為單位
                  BYTE    bDescriptorType;    // 設備描述符類型,0x01
                  WORD    bcdUSB;             // 設備支持的USB協議版本,BCD碼
                  BYTE    bDeviceClass;       // 設備類代碼(由USB-IF分配)
                  BYTE    bDeviceSubClass;    // 子類代碼
                  BYTE    bDeviceProtocol;    // 協議碼
                  BYTE    bMaxPacketSize0;    // 端點0的最大包長度(僅為8,16,32,64)
                  WORD    idVendor;           // 廠商ID(由USB-IF分配)
                  WORD    idProduct;          // 產品ID(由制造商定義)
                  WORD    bcdDevice;          // 設備發行號(BCD碼)
                  BYTE    iManufacture;       // 描述廠商信息的字符串描述符的索引值
                  BYTE    iProduct;           // 描述產品信息的字符串描述符的索引值
                  BYTE    iSerialNumber;      // 描述設備序列號信息的字符串描述符的索引值
                  BYTE    bNumConfigurations; // 可能的配置描述符的數目
                } device_descriptor

            • 配置描述符 (Configuration Descriptor)

                struct {
                  BYTE    bLength;             // 描述符的長度,以字節為單位
                  BYTE    bDescriptorType;     // 配置描述符類型,0x02
                  WORD    wTotalLength;        // 配置信息的總長
                  BYTE    bNumInterfaces;      // 該配置所支持的接口數目
                  BYTE    bConfigurationValue; // 被SetCongiguration()請求用做參數來選定該配置
                  BYTE    bConfiguration;      // 描述該配置的字符串描述符的索引值
                  BYTE    bmAttributes;        // 配置特性
                  BYTE    MaxPower;            // 該配置下的總線電源耗費量,以2mA為一個單位 
                }configuration_descriptor;

                bmAttributes :b7:備用,b6:自供電,b5:遠程喚醒,b4--b0:備用

                另外,在讀取配置描述符時可以把該配置下的所有描述符全部讀出,這些描述符的總長度就是wTotalLength字段的值,讀出所有描述符后,在一個一個地拆分。

            • 接口描述符(Interface Descriptor):

                struct {
                  BYTE    bLength;            // 描述符的長度,以字節為單位
                  BYTE    bDescriptorType;    // 接口描述符類型,0x04
                  BYTE    bInterfaceNumber;   // 接口號,從0開始
                  BYTE    bAlternateSetting;  // 可選設置的索引值.
                  BYTE    bNumEndpoints;      // 此接口的端點數量。 
                  BYTE    bInterfaceClass;    // 接口類編碼(由USB-IF分配)
                  BYTE    bInterfaceSubClass; // 接口子類編碼(由USB-IF分配)
                  BYTE    bInterfaceProtocol; // 協議碼(由USB-IF分配)
                  BYTE    iInterface;         // 描述該接口的字符串描述符的索引值
                }interface_descriptor;

                bInterfaceClass:USB協議根據功能將不同的接口劃分成不同的類,如下:

                1:音頻類,2:CDC控制類,3:人機接口類(HID),5:物理類,6:圖像類,7:打印機類,8:大數據存儲類,9:集線器類,10:CDC數據類,11:智能卡類,13:安全類,220:診斷設備類,224:無線控制類,254:特定應用類,255廠商定義的設備。

            •  端點描述符(Endpoint Descriptor):

                struct {
                  BYTE    bLength;            // 描述符的長度,以字節為單位
                  BYTE    bDescriptorType;    // 端點描述符類型,0x05
                  BYTE    bEndpointAddress;   // 端點地址
                  BYTE    bmAttributes;       // 在bconfigurationValue所指的配置下的端點特性.
                  WORD    wMaxPacketSize;     // 接收/發送的最大數據報長度. 
                  BYTE    bInterval;          // 周期數據傳輸端點的時間間隙.
                }endpoint_descriptor;

                bmAttributes:bit 1:0--傳送類型,00=控制傳輸,01=實時傳輸,10=批量傳輸,11=中斷傳輸;所有其他位均保留。

            • 字符串描述符(String Descriptor):

                struct {
                  BYTE    bLength;            // 描述符的長度,以字節為單位
                  BYTE    bDescriptorType;    // 字符串描述符類型,0x03
                  char    bString[];          // UNICODE編碼的字符串
                }string_descriptor;

            • USB命令請求(USB DEVICE REQUEST)

                為了更好地協調USB主機與設備之間的數據通信,USB規范定義了一套命令請求,用于完成主機對總線上所有USB設備的統一控制,USB命令請求由統一的格式,其結構如下:

                struct {
                  BYTE  bmRequestType;  // 請求類型
                  BYTE  bRequest;       // 命令請求的編碼
                  WORD  wValue;         // 命令不同,含義不同
                  WORD  wIndex;         // 命令不同,含義不同 
                  WORD  wLength;        // 如果有數據階段,此字段為數據字節數

                }
             setup_request;

                后面我們向設備發出指令就全靠這個結構了。作為我們本文要用到的讀取USB設備描述符的命令請求,該結構各字段的填法如下。

                bmRequestType : b7--數據傳輸方向,0=主機到設備,1=設備到主機;b6:5--命令的類型,0=標準命令,1=類命令,2=廠商提供的命令,3=保留;b4:0--接收對象,0=設備,1=接口,2=端點,3=其他。我們發出的得到描述符的命令,數據傳輸方向為設備到主機,標準命令,接收對象為設備,所以該字段填0x80。

                bRequest : 標準命令的編碼如下,GET_STATUS=0;CLEAR_FEATURE=1;SET_FEATURE=3; SET_ADDRESS=5;GET_DESCRIPTOR=6;SET_DESCRIPTOR=7;GET_CONFIGURATION=8; SET_CONFIGURATION=9;GET_INTERFACE=10;SET_INTERFACE=11;SYNCH_FRAME=12。我們的命令是GET_DESCRIPTOR,所以應該填6。

                wValue : 高字節表示描述符的類型,0x01=設備描述符,0x02=配置描述符,0x03=字符串描述符,0x04=接口描述符,0x05=端點描述符,0x29=集線器類描述符,0x21=人機接口類描述符,0xFF=廠商定義的描述符。

                低字節表示表示描述符的索引值。所以,當讀取設備描述符時,該字段值為0x100,當讀取配置描述符是,應為0x03yy,其中yy為描述符的索引值。

                wIndex : 當讀取字符串描述符時,填0x0409,表示希望獲得英文字符串,其他情況下填0。

                wLength : 數據長度,一般應該填寫,描述符的第一個字節,bLength。由于我們在讀描述符時,并不知道其實際長度,通常的做法是先讀取8個字節,然后根據第一個字節bLength的值在重新讀取一次完整的描述符;注意,當讀取配置描述符的錢8個字節后,應該使用wTotalLength字段的值作為長度讀取與該配置有關的所有描述符。

            4、讀取設備描述符的范例程序

                按照我們文章的習慣,幾乎每篇文章都有一個范例程序,本文也不例外,本文的范例程序請在下面地址下載:

                http://blog.hengch.com/source/usbview.zip

                程序用C++寫成,在DJGPP下編譯通過,所以是32位保護模式下的代碼,要注意的是,DOSUSB是實模式下的驅動,所以在申請內存塊時要申請1M以內實模式可以讀取的內存,否則,在使用int 65h調用DOSUSB時一定會出現問題。

                有4個頭文件,public.h中定義了一些方便使用的數據類型,比如BYTE為char,WORD為short int等等,可以不必太關注;x86int.h中定義了調用DOS中斷所需的函數和數據結構,直到怎么使用就可以了;dosmem.h中定義了一個 DOS_MEM類,主要是為了在保護模式下申請和使用DOS內存塊更為方便,也是知道其中的方法,能夠明白程序中的意義就可以了;usb.h定義了與 USB協議有關的所有常數,這些常數與前面介紹的各種數據結構一一對應,由于我們是在保護模式下使用DOS內存,所以把一個內存塊映射到一個數據結構上有一些麻煩,讀取各個字段主要靠在usb.h中定義的這些常數。

                主要程序在usbview.cc中,主要思路如下:

            • USB 設備的地址從1--127,所以我們從1--127做一個循環,逐一讀取USB設備描述符,直到出現“非法地址”為止。
            • 每一個USB設備的設備描述符只有一個,所以我們從讀取某個地址下的設備描述符開始。
            • 開始我們并不知道設備描述符的長度,即便我們知道其長度為18個字符,但我們仍然不知道端點0允許的包長度(設備描述符中bMaxPacketSize0字段),但我們知道包長度的最小值是8,所以我們先讀取8個字節的設備描述符。
            • 在我們得到8個字符的設備描述符后,我們就可以得到該描述符的長度和端點0的包長度,在后面發出的所有命令中,始終要把這個值填在URB結構的actual_length字段中。
            • buffer用于存放USBD返回的描述符,在使用前建議初始化一下,全部清0。
            • 要向設備發出命令請求(Request),需要先填setup_request結構,前面講過,bmRequestType=0x80,bRequest=6,這兩個字段的填法始終不變;我們現在讀取設備描述符,所以 wValue=0x100,wIndex=0,wLength在首次調用時填8,第二次調用時填返回的bLength字段(應該是18)。
            • 準備好buffer和setup_request后,我們要填URB一邊與DOSUSB交互;讀取描述符是一個控制傳輸,所以transaction=0x2D(后面一直是0x2D);dev_add填上面提到的循環變量;end_point=0,因為我們總對端點0(見USB協議);buffer_off和buffer_seg分別填buffer的便宜地址和段地址;setup_buffer_off和setup_buffer_seg分別填前面setup_request結構的偏移地址和段地址;buffer_length同setup_request結構中的wLength,也可以把值設在wLength和buferr的最大長度之間,如果 buffer的最大長度小于wLength,我們只能填buffer的最大長度,但這種情況下我們將得不到完整的描述符;actual_length在第一次調用時填8,以后一直填返回的bMaxPacketsize0字段;其他字段均為0。
            • 讓DS:DX指向剛剛填完的URB結構,調用軟中斷65h,DOSUSB將為你處理下面的事情。
            • 如果正常,error_code應該返回0,如果非0,含義如下:
              1--非法的設備地址;2--內部錯誤;3--非法的transation_type字段;4--非法的buffer長度。
            • 如果正常,status字段應該為0,該字段是是USB控制器返回的狀態字節,不同的控制器(OHCI或 UHCI)會返回不同的值。
            • 當我們得到設備描述符后,如果設備描述符中的iManufacturer字段不為0,我們可以根據這個所引值得到相應的字符串描述符,從而顯示出廠家信息,要注意的是字符串描述符是 UNICODE編碼,對于ASCII而言,它是一個ASSCII碼跟一個ASCII 0組成;同理我們可以得到產品信息和序列號信息。
            • 當我們得到了設備的設備描述符后,我們就可以知道這個設備上有多少個配置(設備描述符中的 bNumConfigurations),進而通過一個循環得到所有的配置描述符及其配置下的所有描述符。
            • 讀取配置描述符的方法與讀取設備描述符大同小異,也是先讀取8個字節,然后根據返回的內容再讀取所有的描述符內容,要注意的是,實際上,我們不能單獨得到接口描述符和端點描述符,唯一的辦法是把一個配置下的描述符全部讀出來,所以setup_request結構中的wLength字段一定要與配置描述符中返回的wTotalLength值一致才行。
            • 剩下的事情就是如何顯示我們得到的描述符,我想這對每一位讀者而言都不是什么困難的事。

             

             原文地址 http://hengch.blog.163.com/blog/static/107800672008423111324286/
            posted @ 2010-11-21 13:16 wrh 閱讀(1044) | 評論 (0)編輯 收藏
            一、基本概念  

              1、USB協議本身很復雜,但方便在提供了統一的接口方式,使得驅動程序在使用設備的時候,工作簡化到了類似操作串行接口。

              2、USB設備可以看作提供了多個串口的設備,依據USB的規范,我們將每個串口稱作端點(Endpoint),要和這個端點通信,我們就要打開到這個端點的連接,這個連接就是管道(Pipe)。

              3、打開端點之后,就可以像串口一樣進行數據傳輸了。USB有4種不同類型的傳輸方式:控制傳輸(Control Transfer),批量傳輸(Bulk Transfer),中斷傳輸(Interrupt Transfer)和實時傳輸(IsochTransfer)。

              4、由于一個設備可能要適應多種情況,端點的設置會有多套,以備使用。端點設置稱為接口(Interface)。USB設備展現給我們能夠找到的東西就是這些Interface,我們選擇要用的Interface,就可以找到Endpoint,再打開Endpoint,就可以傳輸數據了。所以,在驅動程序開始的時候,需要記錄下這些Interface。

              5、例如:OV511+的端點0是控制端點,用來設置參數以及起停設備;端點1是實時傳輸端點,用來傳輸視頻。端點1有8套不同的設置,主要區別就在于一次傳輸的數據幀的大小,所以在USBDeviceAttach的時候,要記錄這些設置到驅動程序中,后面才能夠選用。

            二、描述符介紹
               標準的USB設備有5種USB描述符:設備描述符,配置描述符,字符串描述符,接口描述符,端點描述符。下面詳解:

            1、設備描述符:一個設備只有一個設備描述符

            typedef struct _USB_DEVICE_DESCRIPTOR_
            {
                BYTE        bLength,
                BYTE        bDescriptorType,
                WORD      bcdUSB,
                BYTE        bDeviceClass,
                BTYE        bDeviceSubClass,
                BYTE        bDeviceProtol,
                BYTE        bMaxPacketSize0,
                WORD      idVenderI,
                WORD      idProduct,
                WORD      bcdDevice,
                BYTE        iManufacturer,
                BYTE        iProduct,
                BYTE        iSerialNumber,
                BYTE        iNumConfiguations
            }USB_DEVICE_DESCRIPTOR;

            bLength : 描述符大小.固定為0x12.
            bDescriptorType : 設備描述符類型.固定為0x01.
            bcdUSB : USB 規范發布號.表示了本設備能適用于那種協議,如2.0=0200,1.1=0110等.

            bDeviceClass : 類型代碼(由USB指定)。當它的值是0時,表示所有接口在配置描述符里,并且所有接口是獨立的。當它的值是1到FEH時,表示不同的接口關聯的。當它的值是FFH時,它是廠商自己定義的.
            bDeviceSubClass : 子類型代碼(由USB分配).如果bDeviceClass值是0,一定要設置為0.其它情況就跟據USB-IF組織定義的編碼.
            bDeviceProtocol : 協議代碼(由USB分配).如果使用USB-IF組織定義的協議,就需要設置這里的值,否則直接設置為0。如果廠商自己定義的可以設置為FFH.
            bMaxPacketSize0 : 端點0最大分組大小(只有8,16,32,64有效).
            idVendor : 供應商ID(由USB分配).

            idProduct : 產品ID(由廠商分配).由供應商ID和產品ID,就可以讓操作系統加載不同的驅動程序.
            bcdDevice : 設備出產編碼.由廠家自行設置.

            iManufacturer : 廠商描述符字符串索引.索引到對應的字符串描述符. 為0則表示沒有.
            iProduct : :產品描述符字符串索引.同上.
            iSerialNumber : 設備序列號字符串索引.同上.
            bNumConfigurations : 可能的配置數.指配置字符串的個數
            2、配置描述符:配置描述符定義了設備的配置信息,一個設備可以有多個配置描述符
            typedef struct _USB_CONFIGURATION_DESCRIPTOR_
            {
                BYTE      bLength,
                BYTE      bDescriptorType,
                WORD    wTotalLength,
                BYTE      bNumInterfaces,
                BYTE      bConfigurationValue,
                BYTE      iConfiguration,
                BYTE      bmAttributes,
                BYTE      MaxPower
            }USB_CONFIGURATION_DESCRIPTOR;

            bLength : 描述符大小.固定為0x09.
            bDescriptorType : 配置描述符類型.固定為0x02.
            wTotalLength : 返回整個數據的長度.指此配置返回的配置描述符,接口描述符以及端點描述符的全部大小.
            bNumInterfaces : 配置所支持的接口數.指該配置配備的接口數量,也表示該配置下接口描述符數量.
            bConfigurationValue : 作為Set Configuration的一個參數選擇配置值.
            iConfiguration : 用于描述該配置字符串描述符的索引.
            bmAttributes : 供電模式選擇.Bit4-0保留,D7:總線供電,D6:自供電,D5:遠程喚醒.
            MaxPower : 總線供電的USB設備的最大消耗電流.以2mA為單位.
            3、接口描述符:接口描述符說明了接口所提供的配置,一個配置所擁有的接口數量通過配置描述符的bNumInterfaces決定
            typedef struct _USB_INTERFACE_DESCRIPTOR_
            {
                BYTE      bLength,
                BYTE      bDescriptorType,
                BYTE      bInterfaceNumber,
                BYTE      bAlternateSetting,
                BYTE      bNumEndpoint,
                BYTE      bInterfaceClass,
                BYTE      bInterfaceSubClass,
                BYTE      bInterfaceProtocol,
                BYTE      iInterface
            }USB_INTERFACE_DESCRIPTOR;

            bLength : 描述符大小.固定為0x09.
            bDescriptorType : 接口描述符類型.固定為0x04.
            bInterfaceNumber: 該接口的編號.
            bAlternateSetting : 用于為上一個字段選擇可供替換的位置.即備用的接口描述符標號.
            bNumEndpoint : 使用的端點數目.端點0除外.
            bInterfaceClass : 類型代碼(由USB分配).
            bInterfaceSunClass : 子類型代碼(由USB分配).
            bInterfaceProtocol : 協議代碼(由USB分配).
            iInterface : 字符串描述符的索引
            4、端點描述符:USB設備中的每個端點都有自己的端點描述符,由接口描述符中的bNumEndpoint決定其數量
            typedef struct _USB_ENDPOINT_DESCRIPTOR_
            {
                BYTE        bLength,
                BYTE        bDescriptorType,
                BYTE        bEndpointAddress,
                BYTE        bmAttributes,
                WORD      wMaxPacketSize,
                BYTE        bInterval
            }USB_ENDPOINT_DESCRIPTOR;

            bLength : 描述符大小.固定為0x07.
            bDescriptorType : 接口描述符類型.固定為0x05.
            bEndpointType : USB設備的端點地址.Bit7,方向,對于控制端點可以忽略,1/0:IN/OUT.Bit6-4,保留.BIt3-0:端點號.
            bmAttributes : 端點屬性.Bit7-2,保留.BIt1-0:00控制,01同步,02批量,03中斷.
            wMaxPacketSize : 本端點接收或發送的最大信息包大小.
            bInterval : 輪訓數據傳送端點的時間間隔.對于批量傳送和控制傳送的端點忽略.對于同步傳送的端點,必須為1,對于中斷傳送的端點,范圍為1-255.
            5、字符串描述符:其中字符串描述符是可選的.如果不支持字符串描述符,其設備,配置,接口描述符內的所有字符串描述符索引都必須為0
            typedef struct _USB_STRING_DESCRIPTION_
            {
                BYTE      bLength,
                BYTE      bDescriptionType,
                BYTE      bString[1];
            }USB_STRING_DESCRIPTION;

            bLength : 描述符大小.由整個字符串的長度加上bLength和bDescriptorType的長度決定.
            bDescriptorType : 接口描述符類型.固定為0x03.
            bString[1] : Unicode編碼字符串.

            本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/alien75/archive/2009/09/30/4622319.aspx

            posted @ 2010-11-18 11:30 wrh 閱讀(2051) | 評論 (0)編輯 收藏

              Windows主機端與自定義USB HID設備通信詳解 收藏

            Windows主機端與自定義USB HID設備通信詳解

             

            說明:

            -          以下結論都是基于 Windows XP 系統所得出的,不保證在其他系統的適用性。

            -          在此討論的是 HID 自定義設備,對于標準設備,譬如 USB 鼠標和鍵盤,由于操作系統對其獨占,許多操作未必能正確執行。

             

            1 .   所使用的典型 Windows API

            CreateFile

            ReadFile

            WriteFile

            以下函數是 DDK 的內容:

            HidD_SetFeature

            HidD_GetFeature

            HidD_SetOutputReport

            HidD_GetInputReport

            其中, CreateFile 用于打開設備; ReadFile 、 HidD_GetFeature 、 HidD_GetInputReport 用于設備到主機方向的數據通信; WriteFile 、 HidD_SetFeature 、 HidD_SetOutputReport 用于主機到設備方向的數據通信。鑒于實際應用,后文主要討論 CreateFile , WriteFile , ReadFile , HidD_SetFeature 四個函數,明白了這四個函數,其它的可以類推之。

             

            2 .   幾個常見錯誤

                   當使用以上 API 時,如果操作失敗,調用 GetLastError() 會得到以下常見錯誤:

                   6 :          句柄無效

                   23 :        數據錯誤(循環冗余碼檢查)

                   87 :        參數錯誤

                   1784 :     用戶提供的 buffer 無效

                   后文將會詳細說明這些錯誤情況。

             

            3.         主機端設備枚舉程序流程

             

             

            4.         函數使用說明

            CreateFile(devDetail->DevicePath,                                         // 設備路徑

                           GENERIC_READ | GENERIC_WRITE,                    // 訪問方式

                           FILE_SHARE_READ | FILE_SHARE_WRITE,         // 共享模式

                           NULL,

                           OPEN_EXISTING,                                           // 文件不存在時,返回失敗

                           FILE_FLAG_OVERLAPPED,                                 // 以重疊(異步)模式打開

                           NULL);

             

            在這里, CreateFile 用于打開 HID 設備,其中設備路徑通過函數 SetupDiGetInterfaceDeviceDetail 取得。 CreateFile 有以下幾點需要注意:

             

            -     訪問方式: 如果是系統獨占設備,例如鼠標、鍵盤等等,應將此參數設置為 0 ,否則后續函數操作將失敗(譬如 HidD_GetAttributes );也就是說,不能對獨占設備進行除了查詢以外的任何操作,所以能夠使用的函數也是很有限的,下文的一些函數并不一定適合這些設備。在此順便列出 MSDN 上關于此參數的說明:

            If this parameter is zero, the application can query file and device attributes without accessing the device. This is useful if an application wants to determine the size of a floppy disk drive and the formats it supports without requiring a floppy in the drive. It can also be used to test for the file's or directory's existence without opening it for read or write access 。

            -          重疊(異步)模式:此參數并不會在此處表現出明顯的意義,它主要是對后續的 WriteFile , ReadFile 有影響。如果這里設置為重疊(異步)模式,那么在使用 WriteFile , ReadFile 時也應該使用重疊(異步)模式,反之亦然。這首先要求 WriteFile , ReadFile 的最后一個參數不能為空( NULL )。否則,便會返回 87 (參數錯誤)錯誤號。當然, 87 號錯誤并不代表就是此參數不正確,更多的信息將在具體講述這兩個函數時指出。此參數為 0 時,代表同步模式,即 WriteFile , ReadFile 操作會在數據處理完成之后才返回,否則阻塞在函數內部。

             

            ReadFile(hDev,                                 // 設備句柄,即 CreateFile 的返回值

                          recvBuffer,                          // 用于接收數據的 buffer

                          IN_REPORT_LEN,              // 要讀取數據的長度

                          &recvBytes,                         // 實際收到的數據的字節數

                          &ol);                                  // 異步模式

             

            在這里, ReadFile 用于讀取 HID 設備通過中斷 IN 傳輸發來的輸入報告 。有以下幾點要注意:

             

            1 、 ReadFile 的調用不會引起設備的任何反應,即 HID 設備與主機之間的中斷 IN 傳輸不與 ReadFile 打交道。實際上主機會在最大間隔時間(由設備的端點描述符來指定)內輪詢設備,發出中斷 IN 傳輸的請求。“讀取”即意味著從某個 buffer 里面取回數據,實際上這個 buffer 就是 HID 設備驅動中的 buffer 。這個 buffer 的大小可以通過 HidD_SetNumInputBuffers 來改變。在 XP 上缺省值是 32 (個報告)。

             

            2 、讀取的數據對象是輸入報告,也即通過中斷輸入管道傳入的數據。所以,如果設備不支持中斷 IN 傳輸,那么是無法使用此函數來得到預期結果的。實際上這種情況不可能在 HID 中出現,因為協議指明了至少要有一個中斷 IN 端點。

             

            3 、 IN_REPORT_LEN 代表要讀取的數據的長度(實際的數據正文 + 一個 byte 的報告 ID ),這里是一個常數,主要是因為設備固件的信息我是完全知道的,當然知道要讀取多少數據(也就是報告的長度);不過也可以通過另外的函數( HidD_GetPreparsedData )來事先取得報告的長度,這里不做詳細討論。因為很難想象在不了解固件信息的情況下來做自定義設備的 HID 通信,在實際應用中一般來說就是固件與 PC 程序匹配著來開發。此參數如果設置過大,不會有實質性的錯誤,在 recvBytes 參數中會輸出實際讀到的長度;如果設置過小,即小于報告的長度,會返回 1784 號錯誤(用戶提供的 buffer 無效)。

             

            4 、關于異步模式。前面已經提過,此參數的設置必須與 CreateFile 時的設置相對應,否則會返回 87 號錯誤(參數錯誤)。如果不需要異步模式,此參數需置為 NULL 。在這種情況下, ReadFile 會一直等待直到數據讀取成功,所以會阻塞住程序的當前過程。

             

                   WriteFile(hDev,                                 // 設備句柄,即 CreateFile 的返回值

                                 reportBuf,                           // 存有待發送數據的 buffer

                                 OUT_REPORT_LEN,           // 待發送數據的長度

                                 &sendBytes,                        // 實際收到的數據的字節數

                                 &ol);                                  // 異步模式

             

                   在這里, WriteFile 用于傳輸一個輸出報告 給 HID 設備。有以下幾點要注意:

             

            1、  與 ReadFile 不同, WriteFile 函數被調用后,雖然也是經過驅動程序,但是最終會反映到設備中。也就是說,調用 WriteFile 后,設備會接收到輸出報告的請求。如果設備使用了中斷 OUT 傳輸,則 WriteFile 會通過中斷 OUT 管道來進行傳輸;否則會使用 SetReport 請求通過控制管道來傳輸。

             

            2、  OUT_REPORT_LEN 代表要寫入的數據長度(實際的數據正文 + 一個 byte 的報告 ID )。如果大于實際報告的長度,則使用實際報告長度;如果小于實際報告長度,會返回 1784 號錯誤(用戶提供的 buffer 無效)。

             

            3、  reportBuf [0] 必須存有待發送報告的 ID ,并且此報告 ID 指示的必須是輸出報告,否則會返回 87 號錯誤(參數錯誤)。這種情況可能容易被程序員忽略,結果不知錯誤號所反映的是什么,網上也經常有類似疑問的帖子。順便指出,輸入報告、輸入報告、特征報告這些報告類型,是反映在 HID 設備的報告描述符中。后文將做舉例討論。

             

            4、  關于異步模式。前面已經提過,此參數的設置必須與 CreateFile 時的設置相對應,否則會返回 87 號錯誤(參數錯誤)。如果不需要異步模式,此參數需置為 NULL 。在這種情況下, WriteFile 會一直等待直到數據讀取成功,所以會阻塞住程序的當前過程。

             

            HidD_SetFeature(hDev,                                    // 設備句柄,即 CreateFile 的返回值

                                 reportBuf,                                   // 存有待發送數據的 buffer

                                 FEATURE_REPORT_LEN);        //buffer 的長度

            HidD_SetOutputReport(hDev,                            // 設備句柄,即 CreateFile 的返回值

                                 reportBuf,                                   // 存有待發送數據的 buffer

                                 OUT_REPORT_LEN);                //buffer 的長度

             

            HidD_SetFeature 發送一個特征報告 給設備, HidD_ SetOutputReport 發送一個輸出報告 給設備。注意以下幾點:

             

            1、  跟 WriteFile 類似,必須在 reportBuf [0] 中指明要發送的報告的 ID ,并且和各自適合的類型相對應。也就是說, HidD_SetFeature 只能發送特征報告,因此報告 ID 必須是特征報告的 ID ; HidD_SetOutputReport 只能發送輸出報告,因此報告 ID 只能是輸出報告的 ID 。

            2、  這兩個函數最常返回的錯誤代碼是 23 (數據錯誤)。包括但不僅限于以下情況:

            - 報告 ID 與固件描述的不符。

            - 傳入的 buffer 長度少于固件描述的報告的長度。

            據有關資料反映(非官方文檔),只要是驅動程序對請求無反應,都會產生此錯誤。

             

            5.         常見錯誤匯總

            - HID ReadFile

              - Error Code 6 (handle is invalid)

                傳入的句柄無效

              - Error Code 87 ( 參數錯誤 )

                很可能是 createfile 時聲明了異步方式,但是讀取時按同步讀取。

              - Error Code 1784 ( 用戶提供的 buffer 無效 ):

                傳參時傳入的“讀取 buffer 長度”與實際的報告長度不符。

             

            - HID WriteFile

              - Error Code 6 (handle is invalid)

                傳入的句柄無效

              - Error Code 87 (參數錯誤)

                - CreateFile 時聲明的同步 / 異步方式與實際調用 WriteFile 時傳入的不同。

                - 報告 ID 與固件中定義的不一致( buffer 的首字節是報告 ID )

              - Error Code 1784 ( 用戶提供的 buffer 無效 )

                傳參時傳入的“寫入 buffer 長度”與實際的報告長度不符。

             

            - HidD_SetFeature

            - HidD_SetOutputReport

              - Error Code 1 (incorrect function)

                不支持此函數,很可能是設備的報告描述符中未定義這樣的報告類型(輸入、輸出、特征)

              - Error Code 6 (handle is invalid)

                傳入的句柄無效

              - Error Code 23 (數據錯誤(循環冗余碼檢查))

                - 報告 ID 與固件中定義的不相符( buffer 的首字節是報告 ID )

                - 傳入的 buffer 長度少于固件定義的報告長度(報告正文 +1byte, 1byte 為報告 ID )

                - 據相關資料反映(非官方文檔),只要是驅動程序不接受此請求(對請求無反應),都會產生此錯誤

             

            6.         報告描述符及數據通信程序示例

            報告描述符(由于是匯編代碼,所以不必留意其語法,僅需注意表中的每個數據都占 1 個字節):

             

            _ReportDescriptor:                              // 報告描述符

                   .dw 0x06,  0x00, 0xff               // 用法頁

                .dw 0x09,  0x01                     // 用法 ( 供應商用法 1)

                .dw 0xa1,  0x01                      // 集合開始

                .dw 0x85,  0x01                         // 報告 ID(1)

                .dw 0x09,  0x01                  // 用法 ( 供應商用法 1)  

                .dw 0x15,  0x00                 // 邏輯最小值 (0)

                .dw 0x26,  0xff, 0x0                     // 邏輯最大值 (255)

                .dw 0x75,  0x08               // 報告大小 (8)

                .dw 0x95,  0x07                        // 報告計數 (7)

                .dw 0x81,  0x06                // 輸入 (數據,變量,相對值)

             

                .dw 0x09,  0x01                     // 用法 ( 供應商用法 1)  

                .dw 0x85,  0x03                         // 報告 ID ( 3 )

                .dw 0xb1,   0x06                         // 特征 (數據,變量,相對值)

             

                   .dw 0x09,  0x01                    // 用法 ( 供應商用法 1)

                .dw 0x85,  0x02                         // 報告 ID ( 2 )

                .dw 0xb1,  0x06                         // 特征 (數據,變量,相對值)

             

                 .dw 0x09,  0x01                     // 用法 ( 供應商用法 1)  

                .dw 0x85,  0x04                         // 報告 ID ( 4 )

                .dw 0x91,   0x06                         // 輸出 (數據,變量,相對值)

                .dw   0xc0                    // 結合結束

            _ReportDescriptor_End:

             

            這個報告描述符,定義了 4 個不同的報告:輸入報告 1 ,特征報告 2 ,特征報告 3 ,輸出報告 4 (數字代表其報告 ID )。為了簡化,每個報告都是 7 個字節(加上報告 ID 就是 8 個字節)。下面用一個簡單的示例來描述 PC 端與 USB HID 設備進行通信的一般方法。

             

            view plaincopy to clipboardprint?
            #define     USB_VID       0xFC0   
            #define     USB_PID       0x420   
            HANDLE OpenMyHIDDevice(int overlapped);   
            void HIDSampleFunc()   
            {   
                HANDLE       hDev;   
                BYTE         recvDataBuf[8];   
                BYTE         reportBuf[8];   
                DWORD        bytes;   
                hDev = OpenMyHIDDevice(0);                                // 打開設備,不使用重疊(異步)方式 ;   
                if (hDev == INVALID_HANDLE_VALUE)   
                    return;   
                reportBuf[0] = 4;                                         // 輸出報告的報告 ID 是 4   
                memset(reportBuf, 0, 8);   
                reportBuf[1] = 1;   
                if (!WriteFile(hDev, reportBuf, 8, &bytes, NULL))         // 寫入數據到設備   
                     return;   
                ReadFile(hDev, recvDatatBuf, 8, &bytes, NULL);            // 讀取設備發給主機的數據   
            }   
                
            HANDLE OpenMyHIDDevice(int overlapped)   
            {   
                HANDLE hidHandle;   
                GUID hidGuid;   
                HidD_GetHidGuid(&hidGuid);   
                HDEVINFO hDevInfo = SetupDiGetClassDevs(&hidGuid,   
                                                        NULL,   
                                                        NULL,   
                                                        (DIGCF_PRESENT | DIGCF_DEVICEINTERFACE));   
                if (hDevInfo == INVALID_HANDLE_VALUE)   
                {   
                    return INVALID_HANDLE_VALUE;   
                }   
                SP_DEVICE_INTERFACE_DATA devInfoData;   
                devInfoData.cbSize = sizeof (SP_DEVICE_INTERFACE_DATA);   
                int deviceNo = 0;   
                SetLastError(NO_ERROR);   
                while (GetLastError() != ERROR_NO_MORE_ITEMS)   
                {   
                    if (SetupDiEnumInterfaceDevice (hDevInfo,   
                                                    0,   
                                                   &hidGuid,   
                                                   deviceNo,   
                                                   &devInfoData))   
                    {   
                        ULONG  requiredLength = 0;   
                        SetupDiGetInterfaceDeviceDetail(hDevInfo,   
                                                        &devInfoData,   
                                                        NULL,   
                                                        0,   
                                                        &requiredLength,   
                                                         NULL);  
                        PSP_INTERFACE_DEVICE_DETAIL_DATA devDetail = (SP_INTERFACE_DEVICE_DETAIL_DATA*) malloc (requiredLength);   
                        devDetail->cbSize = sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA);   
                        if(!SetupDiGetInterfaceDeviceDetail(hDevInfo,   
                                                             &devInfoData,   
                                                             devDetail,   
                                                             requiredLength,   
                                                             NULL,   
                                                             NULL))   
                        {   
                            free(devDetail);   
                            SetupDiDestroyDeviceInfoList(hDevInfo);   
                            return INVALID_HANDLE_VALUE;   
                        }   
                        if (overlapped)   
                        {   
                            hidHandle = CreateFile(devDetail->DevicePath,   
                                                   GENERIC_READ | GENERIC_WRITE,   
                                                   FILE_SHARE_READ | FILE_SHARE_WRITE,   
                                                   NULL,   
                                                   OPEN_EXISTING,           
                                                   FILE_FLAG_OVERLAPPED,   
                                                   NULL);   
                        }   
                        else   
                        {   
                            hidHandle = CreateFile(devDetail->DevicePath,   
                                                   GENERIC_READ | GENERIC_WRITE,   
                                                   FILE_SHARE_READ | FILE_SHARE_WRITE,   
                                                   NULL,   
                                                   OPEN_EXISTING,           
                                                   0,   
                                                   NULL);   
                        }   
                        free(devDetail);   
                        if (hidHandle==INVALID_HANDLE_VALUE)   
                        {   
                            SetupDiDestroyDeviceInfoList(hDevInfo);   
                            free(devDetail);   
                            return INVALID_HANDLE_VALUE;   
                        }   
                        _HIDD_ATTRIBUTES hidAttributes;   
                        if(!HidD_GetAttributes(hidHandle, &hidAttributes))   
                        {   
                            CloseHandle(hidHandle);   
                            SetupDiDestroyDeviceInfoList(hDevInfo);   
                            return INVALID_HANDLE_VALUE;   
                        }   
                        if (USB_VID == hidAttributes.VendorID   
                            && USB_PID  == hidAttributes.ProductID)   
                        {   
                            break;   
                        }   
                        else   
                        {   
                            CloseHandle(hidHandle);   
                            ++deviceNo;   
                        }   
                    }   
                }   
                SetupDiDestroyDeviceInfoList(hDevInfo);   
                return hidHandle;   
            }


            本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/kevinyujm/archive/2009/06/12/4264506.aspx

            posted @ 2010-11-18 09:12 wrh 閱讀(6376) | 評論 (0)編輯 收藏
            1.添加消息
            消息映射
            BEGIN_MESSAGE_MAP(CAFT_1394Dlg, CDialog)
            //{{AFX_MSG_MAP(CAFT_1394Dlg)
            ON_WM_PAINT()
            ON_WM_QUERYDRAGICON()
            ...
            ON_WM_DEVICECHANGE()
            //}}AFX_MSG_MAP
            聲明消息響應函數:
            afx_msg BOOL OnDeviceChange(UINT nEventType, DWORD dwData);
            定義函數內容:
            BOOL XXXXXXX::OnDeviceChange(UINT nEventType,DWORD dwData)
            {
            DEV_BROADCAST_DEVICEINTERFACE* dbd = (DEV_BROADCAST_DEVICEINTERFACE*) dwData;
            //這里進行信息匹配,比如guid等
            //針對各個事件進行處理.
            switch (nEventType)
            {
            case DBT_DEVICEREMOVECOMPLETE:
            ...
            break;
            case DBT_DEVICEARRIVAL:
            ...
            break;
            ...
            ...
            ...
            default:
            break;
            }
            return TRUE;
            }
            2.注冊設備
            if (Handle == NULL)
            return FALSE;
            DEV_BROADCAST_DEVICEINTERFACE DevInt;
            memset(&DevInt,0,sizeof(DEV_BROADCAST_DEVICEINTERFACE));
            DevInt.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
            DevInt.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
            DevInt.dbcc_classguid = GetCurrentUSBGUID();//m_usb->GetDriverGUID();
            if (!RegisterDeviceNotification(m_hWnd, &DevInt,DEVICE_NOTIFY_WINDOW_HANDLE) )
            return FALSE;
            只有注冊了該設備,OnDeviceChange才能獲得詳細的信息,否則收到的參數都是0007.
            手動添加吧
            我也沒找到 
            posted @ 2010-11-18 08:52 wrh 閱讀(3307) | 評論 (0)編輯 收藏
            僅列出標題
            共25頁: First 3 4 5 6 7 8 9 10 11 Last 

            導航

            <2011年3月>
            272812345
            6789101112
            13141516171819
            20212223242526
            272829303112
            3456789

            統計

            常用鏈接

            留言簿(19)

            隨筆檔案

            文章檔案

            收藏夾

            搜索

            最新評論

            閱讀排行榜

            評論排行榜

            无码精品久久一区二区三区 | 欧美牲交A欧牲交aⅴ久久| 久久精品国产一区二区三区不卡| 久久伊人五月天论坛| 久久免费看黄a级毛片| 久久国产精品成人免费 | 日日狠狠久久偷偷色综合0 | 欧美日韩久久中文字幕| 国产91久久精品一区二区| 日本精品久久久久中文字幕8 | 日韩AV无码久久一区二区| 人人狠狠综合久久亚洲88| 亚洲精品无码专区久久久| 国产亚洲精品美女久久久| 2021国产精品久久精品| 一本一道久久精品综合| 无夜精品久久久久久| 嫩草影院久久国产精品| 久久夜色精品国产欧美乱| 国产精品午夜久久| 久久福利青草精品资源站| 久久综合给久久狠狠97色| 99久久夜色精品国产网站| 久久精品国产亚洲av瑜伽| 国产精品久久久久久久| 久久发布国产伦子伦精品| 久久综合九色综合网站| 国产成人综合久久精品红| 亚洲午夜无码AV毛片久久| 久久国产精品免费一区| 久久精品国产99国产精品澳门| 欧美va久久久噜噜噜久久| 亚洲美日韩Av中文字幕无码久久久妻妇 | 伊人色综合久久天天人手人婷| 综合久久给合久久狠狠狠97色| 久久精品国产色蜜蜜麻豆| 99久久免费国产精精品| 精品综合久久久久久97超人 | 久久丫精品国产亚洲av| 久久精品毛片免费观看| 狠狠综合久久综合88亚洲|