1 引言
隨著微機(jī)技術(shù)水平的日益提高,傳統(tǒng)的計算接口已經(jīng)不能滿足當(dāng)前計算機(jī)高速發(fā)展的需求,計算機(jī)業(yè)界迫切需要新的通用型、高速總線接口。通用外設(shè)接口標(biāo)準(zhǔn)USB應(yīng)運而生。USB,全稱為通用串行總線(Universal Serial Bus),它是Compaq、IBM等PC大廠商聯(lián)合開發(fā)的一種新型的、基于令牌的、高速的串行總線標(biāo)準(zhǔn)。開發(fā)者要設(shè)計USB設(shè)備接口,就必須首先了解USB協(xié)議,在此基礎(chǔ)上有針對性的開發(fā)USB設(shè)備驅(qū)動程序。
2 USB簡介
在眾多的PC機(jī)總線中,USB以其突出的優(yōu)點獨樹一幟:① 使用方便。支持熱拔插,不涉及中斷請求(IRQ)沖突等問題,能真正做到“即插即用”。②傳輸速率高。目前的USB 2.0協(xié)議速度高達(dá)480Mbps 。③易于擴(kuò)展。通過使用Hub擴(kuò)展可連接多達(dá)127個外設(shè)。④使用靈活。USB共有4種傳輸模式:控制(control)、同步(Synchronization)、中斷(interrupt)、批量(bulk),以適應(yīng)不同設(shè)備的需要。⑤獨立供電。正由于上述優(yōu)點,開發(fā)USB接口的設(shè)備已成為一種發(fā)展趨勢。
一個完整的USB系統(tǒng)包括主機(jī)系統(tǒng)和USB設(shè)備。所有的傳輸事務(wù)都是由主機(jī)發(fā)起的。一個主機(jī)系統(tǒng)又可以分為以下幾個層次結(jié)構(gòu),如圖1所示:
圖1 USB 互連通信模型
USB總線接口包括USB主控制器和根集線器,其中USB主控制器負(fù)責(zé)處理主機(jī)與設(shè)備之間電氣和協(xié)議層的互連,根集線器提供USB設(shè)備連接點。USB系統(tǒng)使用USB主控制器來管理主機(jī)和USB設(shè)備之間的數(shù)據(jù)傳輸,另外它也負(fù)責(zé)管理USB資源,如帶寬等。應(yīng)用
軟件不能直接訪問USB設(shè)備硬件,而通過USB系統(tǒng)和USB總線接口與USB設(shè)備進(jìn)行交互。
USB設(shè)備包含一些向主機(jī)軟件提供一系列USB設(shè)備的特征和能力的信息的設(shè)備描述符,用來配置設(shè)備和定位USB設(shè)備驅(qū)動程序。這些信息確保了主機(jī)以正確的方式訪問設(shè)備。通常,一個設(shè)備有一個或多個配置(Configuration)來控制其行為。配置是接口(Interface)的集合,接口指出軟件應(yīng)該如何訪問硬件。接口又是端點(endpoint)的集合,每一個與USB交換數(shù)據(jù)的硬件就為端點,它是作為通信管道的一個終點。圖1顯示了一個多層次結(jié)構(gòu)的通信模型,它表明了端點和管道所扮演的角色。
3 WDM驅(qū)動程序和USB驅(qū)動程序的分層結(jié)構(gòu)
設(shè)備驅(qū)動程序?qū)嶋H上是指一系列控制硬件設(shè)備的函數(shù),是操作系統(tǒng)中控制和連接硬件的關(guān)鍵模塊。它提供連接到計算機(jī)的硬件設(shè)備的軟件接口。
3.1 WDM 驅(qū)動程序介紹
WDM(Win32 Driver Model)是Microsoft公司力推的一種符合Windows2k/XP下的內(nèi)核模式驅(qū)動程序的分層體系結(jié)構(gòu)的驅(qū)動程序模式。它源于 Windows NT的分層32位設(shè)備驅(qū)動程序模型,它支持更多的特性,如即插即用( PnP ,Plug and Play )、電源管理( PM ,Power Management )、Windows管理診斷( WMI ,Windows Management Instrumentation )和 NT 事件。它為Windows操作系統(tǒng)的設(shè)備驅(qū)動程序提供了統(tǒng)一的框架,在Windows平臺上,WDM將成為主流的驅(qū)動模式。
WDM引入了功能設(shè)備對象FDO(Function Device Object)與物理設(shè)備對象PDO(Physical Device Object)兩個新類來描述硬件,一個PDO對應(yīng)一個真實的硬件。一個硬件只允許有一個PDO,卻可以擁有多個FDO,在驅(qū)動程序中直接操作的不是硬件而是相應(yīng)的PDO和FDO。
WDM是通過一個128位的全局唯一標(biāo)識符(GUID)實現(xiàn)驅(qū)動程序的識別。應(yīng)用程序與WDM驅(qū)動程序通信時,應(yīng)用程序?qū)⒚坑脩粽埱笮纬蒊/O請求包(IRP)發(fā)送到驅(qū)動程序。驅(qū)動程序識別出IRP請求后指揮硬件執(zhí)行相應(yīng)操作。
3.2開發(fā)WDM驅(qū)動程序的方法
目前開發(fā)WDM驅(qū)動程序的方法有三種:
①使用 Microsoft 的 Windows2000 DDK工具開發(fā)。②使用 KRFTech 公司的 WinDriver 。③使用 NuMega 公司的 DriverStudio 。
3.3 WDM型的USB驅(qū)動程序結(jié)構(gòu)
對于USB設(shè)備來說,其WDM驅(qū)動程序分為USB底層(總線)驅(qū)動程序和USB功能(設(shè)備)驅(qū)動程序。USB驅(qū)動程序符合Windows 2000下的內(nèi)核模式驅(qū)動程序的分層體系結(jié)構(gòu),如圖2所示:
圖2 WDM型的USB驅(qū)動程序體系結(jié)構(gòu)
USB底層驅(qū)動程序由操作系統(tǒng)提供,負(fù)責(zé)與實際的硬件打交道,實現(xiàn)煩瑣的底層通信。USB功能驅(qū)動程序由設(shè)備開發(fā)者編寫,不對實際的硬件進(jìn)行操作,而是通過向USB底層驅(qū)動程序發(fā)送包含URB(USB Request Block,請求塊)的IRP,來實現(xiàn)對USB設(shè)備信息的發(fā)送和接收。采用這種分層驅(qū)動程序的設(shè)計方法有兩個優(yōu)點:(1)多個USB設(shè)備可以通過USB底層驅(qū)動程序來協(xié)調(diào)它們的工作。(2)編寫分層驅(qū)動程序較之編寫單一驅(qū)動程序相對簡單,且可以節(jié)省內(nèi)存和資源,不易出錯。
USB驅(qū)動程序工作簡述如下:當(dāng)應(yīng)用程序想對USB設(shè)備進(jìn)行I/O操作,它需調(diào)用Windows API函數(shù),I/O管理器將此請求構(gòu)造成一個合適的I/O請求包(IRP)并把它傳遞給USB功能驅(qū)動程序。USB功能驅(qū)動程序接收到這個IRP后,根據(jù)IPR中包含的具體操作代碼構(gòu)造相應(yīng)USB請求塊(URB),并把此URB放到一個新的IRP中,然后把它傳遞給USB底層驅(qū)動程序。USB底層驅(qū)動程序根據(jù)IRP中所含的URB執(zhí)行相應(yīng)的操作,并把操作的結(jié)果返回給USB功能驅(qū)動程序。USB功能驅(qū)動程序接收到此返回的IRP后,將操作結(jié)果通過IRP返還給I/O管理器,最后I/O管理器將此IRP操作結(jié)果傳回給應(yīng)用程序,至此應(yīng)用程序?qū)υO(shè)備的一次I/O操作完成。
4 用Driver Studio工具包開發(fā)WDM型的USB設(shè)備驅(qū)動程序
前文所提及的WDM驅(qū)動程序開發(fā)方法,筆者都曾嘗試過。個人認(rèn)為用DriverStudio開發(fā)工具包來開發(fā)USB驅(qū)動程序行之有效。其中的Driver Wizard是創(chuàng)建WDM驅(qū)動程序框架的一個很好的工具,后文將介紹用它來創(chuàng)建USB設(shè)備驅(qū)動程序的基本框架。
4.1搭建開發(fā)平臺
由于利用 DriverStudio 開發(fā)WDM驅(qū)動程序在搭建開發(fā)平臺的過程中對軟件的安裝順序要求頗高,在開發(fā)過程中我也曾因為安裝順序的顛倒而失敗。在實踐中總結(jié)了以下的安裝步驟,有必要在此作以介紹。
①在已裝了Windows 2000 操作系統(tǒng)的機(jī)子上安裝 Microsoft Visual C++6.0。 ②安裝 Win2000 DDK 。③安裝 NuMega DriverStudio 2.0 ( or 2.6 ) 驅(qū)動程序開發(fā)工具包。它包含DriverWorks( 用于開發(fā)內(nèi)核模式WDM驅(qū)動程序 )、SoftICE( 用于調(diào)試WDM驅(qū)動程序 )等開發(fā)工具。④由于DriverWorks 所用的類庫是對 DDK 函數(shù)的封裝,必須在 VC中編譯,創(chuàng)建自己的庫文件。⑤設(shè)置 DDK 路徑。
4.2 利用DriverStudio 的DriverWorks生成USB設(shè)備驅(qū)動程序框架
驅(qū)動程序開發(fā)平臺搭建成功后,我們可利用驅(qū)動程序生成向?qū)river Wizard,根據(jù)硬件設(shè)置較為容易的生成USB設(shè)備驅(qū)動程序的大體框架。本人的設(shè)置如下:①選擇WDM的驅(qū)動程序類型和Windows 2000運行平臺。②選擇USB總線類型,系統(tǒng)選擇的USB芯片是Philip公司的ISP1581,填寫它的VID(供應(yīng)商ID)和PID(設(shè)備ID),這些信息由芯片的供應(yīng)商提供。③增加端點1和端點2,它們分別具有IN和OUT屬性。④根據(jù)需要選擇對設(shè)備的操作有:Read、Write、Device Control和CleanUp。⑤選擇給端點2產(chǎn)生BULK Read和Write的代碼, 向?qū)詣赢a(chǎn)生一套對端點2進(jìn)行讀、寫的代碼。⑥設(shè)置驅(qū)動程序的屬性,采用WDM接口;在選取讀寫方式時應(yīng)遵循一條原則:需要快速傳送大量數(shù)據(jù)時,用 Direct I/O ,反之用 Buffer I/O ,這里選擇BufferI/O;由于無特殊的電源需求,故選用系統(tǒng)默認(rèn)的Manage Power For This Device。⑧增加IOCTL接口,在其生成的代碼框架中加入自己的操作,以實現(xiàn)一個完整的USB設(shè)備驅(qū)動程序。最后就生成了一個WDM型的USB設(shè)備驅(qū)動程序框架和一個測試該驅(qū)動程序的測試程序大體框架。然后在其中添加需要的功能代碼。
4.3 USB設(shè)備驅(qū)動程序中的關(guān)鍵例程代碼實現(xiàn)
下面以我們的驅(qū)動程序為例,介紹USB驅(qū)動程序開發(fā)中的幾個關(guān)鍵例程的實現(xiàn)。本驅(qū)動程序的主要功能是控制USB設(shè)備上LED燈通斷并且對設(shè)備進(jìn)行讀寫。
4.3.1初始化例程 DriverEntry()
設(shè)備驅(qū)動程序與應(yīng)用程序不同,它沒有main()或WinMain()函數(shù),而是有一個名為DriverEntry()的入口函數(shù),它通常完成一些初始化工作。當(dāng)設(shè)備驅(qū)動程序被加載時,操作系統(tǒng)調(diào)用這個入口。在使用DriverWizard 創(chuàng)建的驅(qū)動程序基本框架中,DriverEntry()函數(shù)已經(jīng)寫好了,無需添寫代碼。在該例程中,驅(qū)動程序要向操作系統(tǒng)登記并注冊一些消息處理器,通過RegistryPath來找到位于注冊表中的驅(qū)動程序參數(shù),當(dāng)驅(qū)動程序正確安裝后,在注冊表KEY_LOCAL_MACHINE \ SYSTEM \ CurrentControlSet \ Service 下可以找到MyUSB 項。而用DDK編寫該入口函數(shù)還需初始化Dispatch(分派)例程入口。
4.3.2創(chuàng)建設(shè)備例程 AddDevice()
大多數(shù)的PDO 都是在 PnP 管理器調(diào)用該程序入口點時被創(chuàng)建的。插入新設(shè)備后,系統(tǒng)啟動時,總線枚舉器會發(fā)現(xiàn)總線上的所有設(shè)備,會自動尋找并安裝設(shè)備的驅(qū)動程序,并由驅(qū)動程序中的處理 PnP 功能模塊自動處理 AddDevice() 例程及其他PnP消息。此例程使用IoCreateDevice() 函數(shù)創(chuàng)建設(shè)備對象,再使用 IoRegisterDeviceInterface() 函數(shù)將設(shè)備組成為一個特定的設(shè)備接口,然后使用IoAttachDeviceToDeviceStack() 函數(shù)關(guān)聯(lián)設(shè)備棧。
NTSTATUS MyUSBDevice::AddDevice( PDEVICE_OBJECT Pdo )
{ // 產(chǎn)生一個DDK中KDevice類新的設(shè)備對象
MyUSBDevice *pDevice = new (
static cast( KUnitizedName(L“MyUSBDevice”,m_Unit) ),// 設(shè)備名
FILE_DEVICE_UNKNOWN, // 設(shè)備類型
NULL, // 指針鏈接名
0, // 設(shè)備特征標(biāo)志位
DO_BUFFERED_IO| DO_POWER_PAGABLE); // I/O傳輸方式
MyUSBDevice(Pdo, m_Unit);
if ( pDevice == NULL )
{
return STATUS_INSUFFICIENT_RESOURCES;
}
NTSTATUS status = devices -> ConstructorStatus();
if ( !NT_SUCCESS(status) ) // 不成功,返回錯誤狀態(tài)并刪除指針
{
delete pDevice;
}
else // 如果成功,向系統(tǒng)報考設(shè)備的電源狀態(tài)變化為PowerDeviceD0
{
m_Unit++;
pDevice -> ReportNewDevicePowerState( PowerDeviceD0 );
}
return status;
}
4.3.3 LED控制處理例程 MyUSB_IOCTL_LED_Handler()
該例程是實現(xiàn)本驅(qū)動程序功能的關(guān)鍵例程,它是用來控制設(shè)備上的LED燈通斷,主要利用USB Vendor Request來向設(shè)備傳送。其中,request=1的時候表示讓LED亮,request=0的時候讓LED滅。它是通過DeviceControl由上層應(yīng)用程序傳下來。實現(xiàn)代碼如下:
NTSTATUS MyUSBDevice::MyUSB_IOCTL_LED_Handler(KIrp I)
{
NTSTATUS status = STATUS_INVALID_PARAMETER;
//檢查輸入?yún)?shù)是否正確,如果不正確,返回STATUS_INVALID_PARAMETER
if(I.IoctlOutputBufferSize() || !I.IoctlBuffer() ||(I.IoctlInputBufferSize() != sizeof(UCHAR)))
return status;
//處理MyUSB_IOCTL_LED_ON請求
PURB pUrb = m_Lower.BuildVendorRequest(
NULL, // 傳輸緩沖區(qū)
0, // 傳輸緩沖區(qū)大小
0, // 請求保留位
(UCHAR)(*(PUCHAR)I.IoctlBuffer()), // 請求1=LED_ON ,0=LED_OFF
0 ); // 值
//向下傳送URB
status = m_Lower.SubmitUrb(pUrb, NULL, NULL, 5000L);
//若請求在此處理,設(shè)置I.Information指示多少數(shù)據(jù)拷貝回用戶
I.Information()=0;
I.Status()=status;
return status;
}
4.3.4訪問硬件例程 DeviceControl()
上層應(yīng)用軟件程序就是通過此例程來將IRP傳到下層。
NTSTATUS MyUSBDevice::DeviceControl(KIrp I)
{
NTSTATUS status;
switch (I.IoctlCode())
{
case MyUSB_IOCTL_LED:
status = MyUSB_IOCTL_LED_Handler(I);
break;
default: // 未被聲明的I/O 控制請求
status = STATUS_INVALID_PARAMETER;
break;
}
}
限于篇幅,這里僅介紹本驅(qū)動程序中的部分例程實現(xiàn)代碼。編寫完驅(qū)動程序后,首先在Visual C++ 中編譯通過,然后連接硬件,用DriverStudio 工具包中的SoftICE調(diào)試器調(diào)試該驅(qū)動程序,并且修改編譯DriverStudio產(chǎn)生的該驅(qū)動程序的測試程序,就通過命令行來測試我們的驅(qū)動程序。最后對于LED的控制,我們可以直觀的在設(shè)備上看到。
5 結(jié)束語
USB技術(shù)的不斷發(fā)展和完善,已經(jīng)使其逐漸成為先進(jìn)總線接口技術(shù)的標(biāo)志和方向,如今USB OTG標(biāo)準(zhǔn)已經(jīng)發(fā)布,那么USB的應(yīng)用領(lǐng)域也將越發(fā)的廣泛。開發(fā)一些特定功能的USB接口并設(shè)計其設(shè)備驅(qū)動程序也將成為應(yīng)用USB技術(shù)的關(guān)鍵。通過對USB的學(xué)習(xí)和Windows 2000下的WDM驅(qū)動程序的研究,本文已經(jīng)給出了編寫WDM型USB設(shè)備驅(qū)動程序的一般方法,讀者可以在實際應(yīng)用中逐步提高對USB和驅(qū)動程序的認(rèn)識,取得事半功倍的效果。