一、單符號
~① 在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
一、基本概念
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
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