青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

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

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

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

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

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

    c:\dosusb>dosusb

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

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

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

3、USB 1.1協(xié)議中的一些內(nèi)容

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

    實際上,所謂描述符就是一個數(shù)據(jù)結(jié)構(gòu),不管是什么描述符,其前兩個字節(jié)的含義都是一樣的,第一個字節(jié)是描述符的長度,第二個字節(jié)是描述符的類型。

  • 設(shè)備描述符(Device Descriptor):

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

  • 配置描述符(Configuration Descriptor)

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

    bmAttributes :b7:備用,b6:自供電,b5:遠(yuǎn)程喚醒,b4--b0:備用

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

  • 接口描述符(Interface Descriptor):

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

    bInterfaceClass:USB協(xié)議根據(jù)功能將不同的接口劃分成不同的類,如下:

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

  •  端點描述符(Endpoint Descriptor):

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

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

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

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

  • USB命令請求(USB DEVICE REQUEST)

    為了更好地協(xié)調(diào)USB主機與設(shè)備之間的數(shù)據(jù)通信,USB規(guī)范定義了一套命令請求,用于完成主機對總線上所有USB設(shè)備的統(tǒng)一控制,USB命令請求由統(tǒng)一的格式,其結(jié)構(gòu)如下:

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

    }
 setup_request;

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

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

    bRequest : 標(biāo)準(zhǔn)命令的編碼如下,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,所以應(yīng)該填6。

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

    低字節(jié)表示表示描述符的索引值。所以,當(dāng)讀取設(shè)備描述符時,該字段值為0x100,當(dāng)讀取配置描述符是,應(yīng)為0x03yy,其中yy為描述符的索引值。

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

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

4、讀取設(shè)備描述符的范例程序

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

&&
① 連接兩個命令,當(dāng)&&前的命令成功時,才執(zhí)行&&后的命令。

||
① 連接兩個命令,當(dāng)||前的命令失敗時,才執(zhí)行||后的命令。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

\>
① 在findstr的一般表達(dá)式中表示字的結(jié)束處。

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

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

' '
① 在for/f中表示將它們包含的內(nèi)容當(dāng)作命令行執(zhí)行并分析其輸出。
② 在for/f "usebackq"中表示將它們包含的字符串當(dāng)作字符串分析。

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

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

` `
① 在for/f中表示它們所包含的內(nèi)容當(dāng)作命令行執(zhí)行并分析它的輸出。

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

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




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

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

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

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

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

5.%在for循環(huán)中,循環(huán)變量引用格式:%%變量名.
如: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,顯示結(jié)果為:
1+2+3+…+100=505

6.>>想用批處理實現(xiàn)向s.txt中多次分別導(dǎo)入文本例如:“aaaa","bbbb","cccc"
實現(xiàn)s.txt內(nèi)效果如:
aaaabbbbcccc
可以執(zhí)行
>>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.用批處理刪除文本每行的前幾個字符
現(xiàn)在有a.txt 內(nèi)容如下
\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
現(xiàn)在想把 ) 以及前面的內(nèi)容刪除 只剩下
123856

Hi!ED
traps
Diele
open

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

9.一文本:D:\w\tongji.txt 共2000行,現(xiàn)在需要刪除前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="均可刪除空白行,即只要""內(nèi)有內(nèi)容,即可刪除空白行 (自注:如果缺少delims=項,則默認(rèn)分隔符為空格)

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

我只是想導(dǎo)出的內(nèi)容只有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,里面的內(nèi)容為
---------- C:\1.TXT
IP Address. . . . . . . . . . . . : 192.168.0.5
IP Address. . . . . . . . . . . . : 218.15.245.210

我只是想導(dǎo)出的內(nèi)容只有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.用批處理命令刪除文本文件的整行內(nèi)容
用批處理命令bat解決
例如文本文件a.txt的內(nèi)容如下:
001,李明,語文,90分
002,李明,數(shù)學(xué),70分
003,李明,離散數(shù)學(xué),63分
004,李明,英語,60分
005,陳紅,語文,80分
006,陳紅,數(shù)學(xué),60分
007,陳紅,離散數(shù)學(xué),78分
008,陳紅,英語,65分
求:如果某行有“數(shù)學(xué)”或者“英語”這個詞,則刪除該行的內(nèi)容。
要求通過批處理得出如下結(jié)果:
001,李明,語文,90分
003,李明,離散數(shù)學(xué),63分
005,陳紅,語文,80分
007,陳紅,離散數(shù)學(xué),78分

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

12.不換行顯示文本內(nèi)容
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
   換行顯示文本內(nèi)容
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 閱讀(755) | 評論 (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 閱讀(347) | 評論 (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 閱讀(7582) | 評論 (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 閱讀(515) | 評論 (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)循環(huán)打開這10個usb端口,如果端口沒有這么多,調(diào)用
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上的節(jié)點
        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)根據(jù)節(jié)點的連接狀態(tài),獲取節(jié)點信息,得到序列號。
    if (connectionInfo->ConnectionStatus == DeviceConnected)
            {
                //取出序列號索引
                UCHAR nSerialno = connectionInfo->DeviceDescriptor.iSerialNumber;
                CHAR OutBuff[20] = {0};
                GetStringDescriptor(hHubDevice,connectionInfo->ConnectionIndex,nSerialno,OutBuff);

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

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

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

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

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

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

    c:\dosusb>dosusb

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

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

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

3、 USB 1.1協(xié)議中的一些內(nèi)容

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

    實際上,所謂描述符就是一個數(shù)據(jù)結(jié)構(gòu),不管是什么描述符,其前兩個字節(jié)的含義都是一樣的,第一個字節(jié)是描述符的長度,第二個字節(jié)是描述符的類型。

  • 設(shè)備描述符(Device Descriptor):

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

  • 配置描述符 (Configuration Descriptor)

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

    bmAttributes :b7:備用,b6:自供電,b5:遠(yuǎn)程喚醒,b4--b0:備用

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

  • 接口描述符(Interface Descriptor):

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

    bInterfaceClass:USB協(xié)議根據(jù)功能將不同的接口劃分成不同的類,如下:

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

  •  端點描述符(Endpoint Descriptor):

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

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

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

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

  • USB命令請求(USB DEVICE REQUEST)

    為了更好地協(xié)調(diào)USB主機與設(shè)備之間的數(shù)據(jù)通信,USB規(guī)范定義了一套命令請求,用于完成主機對總線上所有USB設(shè)備的統(tǒng)一控制,USB命令請求由統(tǒng)一的格式,其結(jié)構(gòu)如下:

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

    }
 setup_request;

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

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

    bRequest : 標(biāo)準(zhǔn)命令的編碼如下,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,所以應(yīng)該填6。

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

    低字節(jié)表示表示描述符的索引值。所以,當(dāng)讀取設(shè)備描述符時,該字段值為0x100,當(dāng)讀取配置描述符是,應(yīng)為0x03yy,其中yy為描述符的索引值。

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

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

4、讀取設(shè)備描述符的范例程序

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

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

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

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

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

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

 

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

  1、USB協(xié)議本身很復(fù)雜,但方便在提供了統(tǒng)一的接口方式,使得驅(qū)動程序在使用設(shè)備的時候,工作簡化到了類似操作串行接口。

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

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

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

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

二、描述符介紹
   標(biāo)準(zhǔn)的USB設(shè)備有5種USB描述符:設(shè)備描述符,配置描述符,字符串描述符,接口描述符,端點描述符。下面詳解:

1、設(shè)備描述符:一個設(shè)備只有一個設(shè)備描述符

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 : 設(shè)備描述符類型.固定為0x01.
bcdUSB : USB 規(guī)范發(fā)布號.表示了本設(shè)備能適用于那種協(xié)議,如2.0=0200,1.1=0110等.

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

idProduct : 產(chǎn)品ID(由廠商分配).由供應(yīng)商ID和產(chǎn)品ID,就可以讓操作系統(tǒng)加載不同的驅(qū)動程序.
bcdDevice : 設(shè)備出產(chǎn)編碼.由廠家自行設(shè)置.

iManufacturer : 廠商描述符字符串索引.索引到對應(yīng)的字符串描述符. 為0則表示沒有.
iProduct : :產(chǎn)品描述符字符串索引.同上.
iSerialNumber : 設(shè)備序列號字符串索引.同上.
bNumConfigurations : 可能的配置數(shù).指配置字符串的個數(shù)
2、配置描述符:配置描述符定義了設(shè)備的配置信息,一個設(shè)備可以有多個配置描述符
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 : 返回整個數(shù)據(jù)的長度.指此配置返回的配置描述符,接口描述符以及端點描述符的全部大小.
bNumInterfaces : 配置所支持的接口數(shù).指該配置配備的接口數(shù)量,也表示該配置下接口描述符數(shù)量.
bConfigurationValue : 作為Set Configuration的一個參數(shù)選擇配置值.
iConfiguration : 用于描述該配置字符串描述符的索引.
bmAttributes : 供電模式選擇.Bit4-0保留,D7:總線供電,D6:自供電,D5:遠(yuǎn)程喚醒.
MaxPower : 總線供電的USB設(shè)備的最大消耗電流.以2mA為單位.
3、接口描述符:接口描述符說明了接口所提供的配置,一個配置所擁有的接口數(shù)量通過配置描述符的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 : 用于為上一個字段選擇可供替換的位置.即備用的接口描述符標(biāo)號.
bNumEndpoint : 使用的端點數(shù)目.端點0除外.
bInterfaceClass : 類型代碼(由USB分配).
bInterfaceSunClass : 子類型代碼(由USB分配).
bInterfaceProtocol : 協(xié)議代碼(由USB分配).
iInterface : 字符串描述符的索引
4、端點描述符:USB設(shè)備中的每個端點都有自己的端點描述符,由接口描述符中的bNumEndpoint決定其數(shù)量
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設(shè)備的端點地址.Bit7,方向,對于控制端點可以忽略,1/0:IN/OUT.Bit6-4,保留.BIt3-0:端點號.
bmAttributes : 端點屬性.Bit7-2,保留.BIt1-0:00控制,01同步,02批量,03中斷.
wMaxPacketSize : 本端點接收或發(fā)送的最大信息包大小.
bInterval : 輪訓(xùn)數(shù)據(jù)傳送端點的時間間隔.對于批量傳送和控制傳送的端點忽略.對于同步傳送的端點,必須為1,對于中斷傳送的端點,范圍為1-255.
5、字符串描述符:其中字符串描述符是可選的.如果不支持字符串描述符,其設(shè)備,配置,接口描述符內(nèi)的所有字符串描述符索引都必須為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博客,轉(zhuǎn)載請標(biāo)明出處:http://blog.csdn.net/alien75/archive/2009/09/30/4622319.aspx

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

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

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

 

說明:

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

-          在此討論的是 HID 自定義設(shè)備,對于標(biāo)準(zhǔn)設(shè)備,譬如 USB 鼠標(biāo)和鍵盤,由于操作系統(tǒng)對其獨占,許多操作未必能正確執(zhí)行。

 

1 .   所使用的典型 Windows API

CreateFile

ReadFile

WriteFile

以下函數(shù)是 DDK 的內(nèi)容:

HidD_SetFeature

HidD_GetFeature

HidD_SetOutputReport

HidD_GetInputReport

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

 

2 .   幾個常見錯誤

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

       6 :          句柄無效

       23 :        數(shù)據(jù)錯誤(循環(huán)冗余碼檢查)

       87 :        參數(shù)錯誤

       1784 :     用戶提供的 buffer 無效

       后文將會詳細(xì)說明這些錯誤情況。

 

3.         主機端設(shè)備枚舉程序流程

 

 

4.         函數(shù)使用說明

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

               GENERIC_READ | GENERIC_WRITE,                    // 訪問方式

               FILE_SHARE_READ | FILE_SHARE_WRITE,         // 共享模式

               NULL,

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

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

               NULL);

 

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

 

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

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 。

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

 

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

              recvBuffer,                          // 用于接收數(shù)據(jù)的 buffer

              IN_REPORT_LEN,              // 要讀取數(shù)據(jù)的長度

              &recvBytes,                         // 實際收到的數(shù)據(jù)的字節(jié)數(shù)

              &ol);                                  // 異步模式

 

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

 

1 、 ReadFile 的調(diào)用不會引起設(shè)備的任何反應(yīng),即 HID 設(shè)備與主機之間的中斷 IN 傳輸不與 ReadFile 打交道。實際上主機會在最大間隔時間(由設(shè)備的端點描述符來指定)內(nèi)輪詢設(shè)備,發(fā)出中斷 IN 傳輸?shù)恼埱蟆?#8220;讀取”即意味著從某個 buffer 里面取回數(shù)據(jù),實際上這個 buffer 就是 HID 設(shè)備驅(qū)動中的 buffer 。這個 buffer 的大小可以通過 HidD_SetNumInputBuffers 來改變。在 XP 上缺省值是 32 (個報告)。

 

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

 

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

 

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

 

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

                     reportBuf,                           // 存有待發(fā)送數(shù)據(jù)的 buffer

                     OUT_REPORT_LEN,           // 待發(fā)送數(shù)據(jù)的長度

                     &sendBytes,                        // 實際收到的數(shù)據(jù)的字節(jié)數(shù)

                     &ol);                                  // 異步模式

 

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

 

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

 

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

 

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

 

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

 

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

                     reportBuf,                                   // 存有待發(fā)送數(shù)據(jù)的 buffer

                     FEATURE_REPORT_LEN);        //buffer 的長度

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

                     reportBuf,                                   // 存有待發(fā)送數(shù)據(jù)的 buffer

                     OUT_REPORT_LEN);                //buffer 的長度

 

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

 

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

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

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

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

據(jù)有關(guān)資料反映(非官方文檔),只要是驅(qū)動程序?qū)φ埱鬅o反應(yīng),都會產(chǎn)生此錯誤。

 

5.         常見錯誤匯總

- HID ReadFile

  - Error Code 6 (handle is invalid)

    傳入的句柄無效

  - Error Code 87 ( 參數(shù)錯誤 )

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

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

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

 

- HID WriteFile

  - Error Code 6 (handle is invalid)

    傳入的句柄無效

  - Error Code 87 (參數(shù)錯誤)

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

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

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

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

 

- HidD_SetFeature

- HidD_SetOutputReport

  - Error Code 1 (incorrect function)

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

  - Error Code 6 (handle is invalid)

    傳入的句柄無效

  - Error Code 23 (數(shù)據(jù)錯誤(循環(huán)冗余碼檢查))

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

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

    - 據(jù)相關(guān)資料反映(非官方文檔),只要是驅(qū)動程序不接受此請求(對請求無反應(yīng)),都會產(chǎn)生此錯誤

 

6.         報告描述符及數(shù)據(jù)通信程序示例

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

 

_ReportDescriptor:                              // 報告描述符

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

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

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

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

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

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

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

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

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

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

 

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

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

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

 

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

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

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

 

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

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

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

    .dw   0xc0                    // 結(jié)合結(jié)束

_ReportDescriptor_End:

 

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

 

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);                                // 打開設(shè)備,不使用重疊(異步)方式 ;   
    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))         // 寫入數(shù)據(jù)到設(shè)備   
         return;   
    ReadFile(hDev, recvDatatBuf, 8, &bytes, NULL);            // 讀取設(shè)備發(fā)給主機的數(shù)據(jù)   
}   
    
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博客,轉(zhuǎn)載請標(biāo)明出處:http://blog.csdn.net/kevinyujm/archive/2009/06/12/4264506.aspx

posted @ 2010-11-18 09:12 wrh 閱讀(6445) | 評論 (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
聲明消息響應(yīng)函數(shù):
afx_msg BOOL OnDeviceChange(UINT nEventType, DWORD dwData);
定義函數(shù)內(nèi)容:
BOOL XXXXXXX::OnDeviceChange(UINT nEventType,DWORD dwData)
{
DEV_BROADCAST_DEVICEINTERFACE* dbd = (DEV_BROADCAST_DEVICEINTERFACE*) dwData;
//這里進(jìn)行信息匹配,比如guid等
//針對各個事件進(jìn)行處理.
switch (nEventType)
{
case DBT_DEVICEREMOVECOMPLETE:
...
break;
case DBT_DEVICEARRIVAL:
...
break;
...
...
...
default:
break;
}
return TRUE;
}
2.注冊設(shè)備
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;
只有注冊了該設(shè)備,OnDeviceChange才能獲得詳細(xì)的信息,否則收到的參數(shù)都是0007.
手動添加吧
我也沒找到 
posted @ 2010-11-18 08:52 wrh 閱讀(3325) | 評論 (0)編輯 收藏
僅列出標(biāo)題
共25頁: First 3 4 5 6 7 8 9 10 11 Last 

導(dǎo)航

<2025年11月>
2627282930311
2345678
9101112131415
16171819202122
23242526272829
30123456

統(tǒng)計

常用鏈接

留言簿(19)

隨筆檔案

文章檔案

收藏夾

搜索

最新評論

閱讀排行榜

評論排行榜

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            欧美色中文字幕| 欧美一级艳片视频免费观看| 欧美成人一区二区三区| 亚洲精品欧美| 久久久久久久久久码影片| 国产精品久久久一本精品| 亚洲国产专区校园欧美| 久久久久久久性| 亚洲专区一区二区三区| 欧美午夜精品久久久久免费视 | 欧美一区二区三区视频在线| 欧美日韩在线播放一区二区| 亚洲精品日韩久久| 欧美成人一区二区三区片免费| 欧美在线视频全部完| 欧美体内she精视频在线观看| 亚洲国产综合91精品麻豆| 麻豆成人在线播放| 久久精品国产精品亚洲精品| 欧美国产日韩亚洲一区| 久久综合九色欧美综合狠狠| 久久久精品国产免大香伊 | 欧美激情中文字幕一区二区| 香蕉久久夜色精品| 红桃av永久久久| 久久9热精品视频| 亚洲午夜小视频| 久久久久久久网| 欧美一区二区三区在线播放| 国产欧美精品一区aⅴ影院| 香蕉精品999视频一区二区| 99ri日韩精品视频| 欧美日韩国产另类不卡| 午夜精品福利在线| 欧美在线播放| 亚洲日本va午夜在线电影| 亚洲精品国产品国语在线app| 欧美伦理91| 欧美在线观看视频一区二区三区| 欧美在线视频导航| 另类酷文…触手系列精品集v1小说| 亚洲国产欧美在线| 亚洲国产精品免费| 欧美日韩国产免费观看| 亚洲免费影视| 久久精品国产精品亚洲综合| 日韩亚洲欧美在线观看| 在线视频精品一区| 国产精品视频免费观看www| 老司机午夜精品视频| 欧美高清视频免费观看| 欧美一区在线看| 欧美aa国产视频| 亚洲视屏在线播放| 久久精品最新地址| 中国女人久久久| 欧美亚洲一级| 一本综合精品| 欧美制服丝袜第一页| 日韩亚洲一区在线播放| 午夜宅男欧美| 一本色道久久88精品综合| 午夜久久久久| 一区二区久久久久| 久久九九精品| 中文久久精品| 久久亚洲影音av资源网| 亚洲天堂偷拍| 欧美激情一区二区在线 | 欧美理论在线播放| 欧美一区二区三区四区视频| 欧美精品在线一区| 另类亚洲自拍| 国产精品亚洲综合色区韩国| 亚洲第一成人在线| 亚洲国产成人精品女人久久久| 欧美专区亚洲专区| 99伊人成综合| 亚洲三级毛片| 蜜桃久久精品乱码一区二区| 欧美一区二区三区男人的天堂 | 一区二区免费在线观看| 久久久综合精品| 另类亚洲自拍| 伊人男人综合视频网| 欧美综合国产精品久久丁香| 亚洲一区在线直播| 欧美日本一区二区视频在线观看| 欧美14一18处毛片| 精品动漫3d一区二区三区免费| 亚洲欧美一区二区原创| 亚洲欧美电影在线观看| 国产精品夫妻自拍| 中文在线一区| 亚洲性xxxx| 欧美亚一区二区| 亚洲一区二区影院| 午夜一级在线看亚洲| 欧美亚洲成人网| 亚洲私人影院在线观看| 亚洲欧美电影在线观看| 国产伦精品一区二区三区四区免费| 日韩视频在线免费| 亚洲专区在线视频| 国产九区一区在线| 午夜亚洲福利| 狂野欧美激情性xxxx欧美| 韩国女主播一区| 久久蜜桃香蕉精品一区二区三区| 噜噜噜噜噜久久久久久91| 在线观看视频亚洲| 欧美国产高清| 亚洲天堂成人在线视频| 欧美影院在线播放| 韩国一区二区三区在线观看| 久久只精品国产| 日韩视频免费看| 久久精品国产视频| 在线日韩精品视频| 欧美日韩精品是欧美日韩精品| 一本综合久久| 久久色在线观看| 亚洲麻豆国产自偷在线| 欧美午夜无遮挡| 欧美一区二区视频在线| 亚洲电影免费在线 | 国产欧美一区视频| 欧美一进一出视频| 亚洲国产另类久久精品| 亚洲在线网站| 1024成人| 国产精品成人观看视频免费| 久久精品国产亚洲a| 亚洲高清精品中出| 欧美一级理论性理论a| 在线观看视频一区二区| 欧美午夜精品久久久| 久久国产视频网| 日韩亚洲欧美高清| 久久在线91| 亚洲一区二区三区影院| 在线观看免费视频综合| 欧美午夜精品电影| 蜜桃av噜噜一区| 欧美亚洲日本网站| 一本大道久久a久久精品综合| 亚洲影院高清在线| 欧美激情91| 久久不射网站| 亚洲网址在线| 亚洲欧洲一区| 韩日精品在线| 国产精品亚洲片夜色在线| 欧美二区乱c少妇| 欧美综合激情网| 亚洲一区二区三区视频| 亚洲黄色视屏| 蜜臀av在线播放一区二区三区| 亚洲免费在线观看| 一区二区三区日韩欧美| 伊人久久大香线蕉av超碰演员| 欧美性片在线观看| 欧美精品一区二区三区久久久竹菊| 久久gogo国模啪啪人体图| 中文日韩电影网站| 99精品国产福利在线观看免费| 91久久久久久久久久久久久| 久久久久久一区二区三区| 亚洲欧美日韩成人高清在线一区| 99视频日韩| 日韩午夜在线视频| 亚洲人体大胆视频| 亚洲国产精品毛片| 136国产福利精品导航网址应用| 国产一区二区| 国产视频不卡| 国产一区二区三区四区五区美女 | 国产精品久久久久毛片软件| 欧美日韩免费观看一区二区三区 | 亚洲欧美日韩中文在线制服| 一区二区三区欧美视频| 一区二区三区视频在线观看| 99精品久久免费看蜜臀剧情介绍| 亚洲国产精品精华液2区45| 亚洲高清自拍| 亚洲欧洲在线免费| 亚洲国产裸拍裸体视频在线观看乱了中文 | 亚洲国产日本| 亚洲国产专区校园欧美| 亚洲国产美女久久久久| 亚洲经典三级| 一区二区三区国产盗摄| 亚洲深夜影院| 欧美在线看片| 久久综合久久综合久久综合| 蜜臀av一级做a爰片久久| 欧美激情va永久在线播放| 欧美视频在线一区| 国产日本欧美一区二区三区在线| 在线观看国产成人av片|