??很多寫(xiě)Windows Device Driver的開(kāi)發(fā)人員基本上都是使用Windows DDK進(jìn)行開(kāi)發(fā)的。但是,現(xiàn)在也有不少人都開(kāi)始借助一些輔助工具。筆者去年開(kāi)始接觸到riverStudio,發(fā)現(xiàn)它真的是一個(gè)不錯(cuò)的開(kāi)發(fā)工具,不僅寫(xiě)代碼的時(shí)候思路清晰,而且和DDK的結(jié)合很好。
????當(dāng)然,也有很多人覺(jué)得用DriverStudio不夠正宗,或者說(shuō)不能很好的理解Windows Device Driver的架構(gòu)。我感覺(jué)這就有點(diǎn)像MFC和SDK的關(guān)系,關(guān)于這個(gè)問(wèn)題在很多地方都有爭(zhēng)論,比如在萬(wàn)千新聞組上,就討論了將近2個(gè)月。每個(gè)人都有自己的最?lèi)?ài),都有自己的習(xí)慣,只要你能把事情做好,我想用什么方法應(yīng)該都是一樣的。如果你已經(jīng)習(xí)慣了用DDK開(kāi)發(fā),那完全還可以繼續(xù)用下去;如果你覺(jué)得DriverStudio不錯(cuò),那嘗試用一個(gè)可以給你按照OOP概念來(lái)編程的工具有什么不好呢?
在驅(qū)動(dòng)開(kāi)發(fā)網(wǎng)上,經(jīng)常看到有人詢(xún)問(wèn)一些關(guān)于DriverStudio的使用的問(wèn)題。我正好很有幸用它作了幾個(gè)驅(qū)動(dòng)程序,包括VXD, KMD和WDM,稍微有點(diǎn)心得,因此想寫(xiě)下來(lái)給大家作一個(gè)小小的參考。如果其中有錯(cuò)誤,歡迎大家給我指出,謝謝。
下面我就介紹一下用DriverStudio開(kāi)發(fā)一個(gè)USB驅(qū)動(dòng)程序的過(guò)程。這個(gè)USB設(shè)備有3個(gè)雙向端點(diǎn),每個(gè)端點(diǎn)的配置如下:
EP 類(lèi)型 地址 buffer(Bytes)
0 IN/OUT Control 0x80/0x00 16/16
1 IN/OUT Bulk 0x81/0x01 16/16
2 IN/OUT Bulk 0x82/0x02 64/64
我們的驅(qū)動(dòng)程序需要實(shí)現(xiàn)的功能就是控制設(shè)備上的LED燈的亮和滅,以及通過(guò)Endpoint 2對(duì)設(shè)備進(jìn)行讀寫(xiě)。
由于DriveStudio由幾個(gè)部分組成,我們寫(xiě)這個(gè)驅(qū)動(dòng)程序只要用到DriverWorks,因此下面我們就簡(jiǎn)稱(chēng)它為DW。在這里,我們假定讀者已經(jīng)正確的安裝了DW,并且已經(jīng)編譯好了各個(gè)庫(kù)文件。
1. 首先,我們通過(guò)快捷方式“Setup DDK and Start MSVC“來(lái)啟動(dòng)VC IDE。這個(gè)快捷方式所指向的程序,會(huì)進(jìn)行一些必要的設(shè)置,然后再啟動(dòng)VC IDE,這樣我們的程序就可以使用DDK和DW的頭文件和庫(kù)了。
2. 從VC IDE的菜單"DriverStudio"中選擇"DriverWizard", 在如圖1所示的對(duì)話(huà)框中, 寫(xiě)上項(xiàng)目名稱(chēng). 在這里, 我們將這個(gè)項(xiàng)目稱(chēng)為: TEST, 所在的目錄為D:\TEST. 然后點(diǎn)按鈕"Next >".

圖1
3. 在接下來(lái)的這個(gè)對(duì)話(huà)框中(如圖2), 我們需要選擇驅(qū)動(dòng)程序的類(lèi)型. 由于USB設(shè)備驅(qū)動(dòng)程序是WDM類(lèi)型的, 所以我們選擇第二項(xiàng)并且點(diǎn)按鈕"Next >".
 圖2
4. 在第3個(gè)對(duì)話(huà)框中(如圖3), 選擇我們的驅(qū)動(dòng)程序所操作的總線(xiàn)類(lèi)型. 這里, 我們選擇USB. 在USB Vendor ID和USB Product ID中填入U(xiǎn)SB設(shè)備的VID和PID. 假定我們的USB設(shè)備的VID和PID分別是16進(jìn)制的0471和1801. 然后點(diǎn)按鈕"Next >". 關(guān)于VID和PID的規(guī)定請(qǐng)參考USB-IF的規(guī)范.

圖3
5. 在接下來(lái)的對(duì)話(huà)框中(如圖4), 我們需要加入Endpoint 1和Endpoint 2的定義. 由于在USB中規(guī)定Endpoint 0是必須存在的, 所以我們不需要對(duì)Endpoint 0進(jìn)行定義. 點(diǎn)"Add..."按鈕, 彈出一個(gè)如圖5所示的對(duì)話(huà)框. 我們將它修改成如圖6所示. 其中, 按照USB的規(guī)定, 對(duì)于端點(diǎn), 它的地址是1; 按照前面說(shuō)明的設(shè)備的特點(diǎn), Endpoint 1的最大的包大小為16字節(jié), 因此在"Max Transer Size"中填入16; Endpoint Name可以通過(guò)"Suggest Name"得到. 按照這些原則, 繼續(xù)設(shè)置其他的配置, 以使對(duì)話(huà)框4變成如圖7所示. 接下來(lái), 繼續(xù)按"Next >"按鈕.
 圖4
 圖5

圖6
 圖7
6. 在如圖8所示的對(duì)話(huà)框中, 可以填入我們需要的Driver Class的名字和文件名. 一般我們不需要更改. 繼續(xù)按"Next >"按鈕.
 圖8
7. 在如圖9所示的對(duì)話(huà)框中, 因?yàn)椴恍枰o其他的驅(qū)動(dòng)程序提供接口, 也不需要提供Flush功能, 所以不需要任何修改, 直接按"Next >"按鈕.

圖9
8. 在如圖10所示的對(duì)話(huà)框中, 我們選擇給端點(diǎn)2產(chǎn)生BULK Read的代碼, 并且按"Next >"按鈕. DW會(huì)給我們產(chǎn)生一套對(duì)端點(diǎn)2進(jìn)行讀的代碼, 不用修改, 就可以直接使用.
 圖10
9. 在如圖11所示的對(duì)話(huà)框中, 我們選擇給端點(diǎn)2產(chǎn)生BULK Write的代碼, 并且按"Next "按鈕. 這樣, DW也會(huì)給我們產(chǎn)生一套對(duì)端點(diǎn)2進(jìn)行寫(xiě)的代碼, 不用修改, 就可以直接使用.
 圖11
10. 對(duì)于如圖12的對(duì)話(huà)框, 我們直接按"Next >"按鈕. 這里是設(shè)置是否要將I/O請(qǐng)求排隊(duì), 在這里, 我們不需要排隊(duì).
 圖12
11. 在如圖13所示的對(duì)話(huà)框中, 我們不需要?jiǎng)?chuàng)建任何注冊(cè)表項(xiàng), 所以直接按"Next >"按鈕.

圖13
12. 如圖14所示的對(duì)話(huà)框, 是讓我們?cè)O(shè)置一些驅(qū)動(dòng)程序的屬性, 比如接口, 緩沖區(qū)之類(lèi)的. 一般的都可以使用缺省設(shè)置. 繼續(xù)按"Next >"按鈕.

圖14
13. 在如圖15所示的對(duì)話(huà)框中, 是讓我們給驅(qū)動(dòng)程序增加一些IOCTL接口. 我們只增加一個(gè)如圖16所示的IOCTL來(lái)控制USB設(shè)備的LED燈. 然后按"Next >"按鈕.
 圖15

圖16
14. 在最后一個(gè)如圖17所示的對(duì)話(huà)框中, 可以設(shè)置一些驅(qū)動(dòng)程序的屬性, 產(chǎn)生一個(gè)console測(cè)試程序. 按下"Finish"按鈕, 就結(jié)束了Wizard.
 圖17
這樣, 我們就創(chuàng)建好了一個(gè)基本的驅(qū)動(dòng)程序, 下面來(lái)看看還要做哪些工作才可以和我們的設(shè)備以及上層的應(yīng)用程序通訊.
把函數(shù)NTSTATUS TESTDevice::TEST_IOCTL_LED_Handler(KIrp I)改成如下面的樣子:
NTSTATUS TESTDevice::TEST_IOCTL_LED_Handler(KIrp I)
{
NTSTATUS status = STATUS_INVALID_PARAMETER;
t << "Entering TESTDevice::TEST_IOCTL_LED_Handler, " << I << EOL;
__try
{
// TODO: Verify that the input parameters are correct
// If not, return STATUS_INVALID_PARAMETER
if(I.IoctlOutputBufferSize() || !I.IoctlBuffer() ||
(I.IoctlInputBufferSize() != sizeof(UCHAR)))
__leave;
// TODO: Handle the the ZBUARD_IOCTL_LED_ON request, or
// defer the processing of the IRP (i.e. by queuing) and set
// status to STATUS_PENDING.
PURB pUrb = m_Lower.BuildVendorRequest(
NULL, // transfer buffer
0, // transfer buffer size
0, // request reserved bits
(UCHAR)(*(PUCHAR)I.IoctlBuffer()), // request. 1 = LED_ON, 0 = LED_OFF
0 // Value
);
// transmit
status = m_Lower.SubmitUrb(pUrb, NULL, NULL, 5000L);
}
__finally
{
// TODO: Assuming that the request was handled here. Set I.Information
// to indicate how much data to copy back to the user.
I.Information() = 0;
I.Status() = status;
}
return status;
}
這個(gè)函數(shù)是控制LED燈的,它是通過(guò)USB Vendor Request來(lái)向設(shè)備傳送的。其中,request=1的時(shí)候表示讓LED亮,request=0的時(shí)候讓LED滅。它是通過(guò)DeviceIoControl由上層應(yīng)用程序傳下來(lái)。
再看看讀寫(xiě)部分,經(jīng)過(guò)檢查NTSTATUS TESTDevice::Read(KIrp I)和NTSTATUS TESTDevice::Write(KIrp I)可以發(fā)現(xiàn),DW已經(jīng)給我們寫(xiě)好了讀寫(xiě)的代碼,我們可以直接使用了。這些代碼就是在上面的第8和第9步中產(chǎn)生的代碼。
最后,修改編譯一下DriverStudio產(chǎn)生的測(cè)試程序Test_TEST程序,我們就可以通過(guò)命令行來(lái)測(cè)試我們的驅(qū)動(dòng)程序了。對(duì)于LED的控制,我們可以直觀的在設(shè)備上看到,但對(duì)于讀寫(xiě)的操作就需要和firmware程序配合,這已經(jīng)超出了本文的范圍,不在這里討論了。
通過(guò)上面的講解,我們可以看到有了DriverStudio,就可以快速的產(chǎn)生一個(gè)驅(qū)動(dòng)程序,然后在里面作一些小的改動(dòng)就可以使用了。即使是寫(xiě)一個(gè)比較復(fù)雜的USB驅(qū)動(dòng)程序,我們也可以不用管一些系統(tǒng)的IRP處理,只要專(zhuān)注于我們自己的特定應(yīng)用就可以了。而且它把一個(gè)驅(qū)動(dòng)程序概括成幾個(gè)類(lèi)的概念,并且DW還附帶有一些很有用的STL類(lèi),在VC IDE里面有了一個(gè)很清晰直觀的表示。這樣,對(duì)一些從上層應(yīng)用轉(zhuǎn)向驅(qū)動(dòng)程序的開(kāi)發(fā)人員,或者一些對(duì)C++/OOP很熟悉但不太了解系統(tǒng)內(nèi)核的開(kāi)發(fā)人員,都比較容易上手。即使對(duì)于推崇直接用DDK編程的人來(lái)說(shuō),通過(guò)閱讀DriverStudio附帶的源代碼,也可以對(duì)驅(qū)動(dòng)程序的開(kāi)發(fā)有一個(gè)更加深入的了解。 |