开篇:润墨网以专业的文秘视角,为您筛选了一篇基于WINDOWSDDK的USB键盘驱动开发范文,如需获取更多写作素材,在线客服老师一对一协助。欢迎您的阅读与分享!
【摘要】usb接口具有方便快速等优点,已经发展成为一种比较普遍的计算机与外设的接口。基于微软windows系统DDK,本文介绍了一种非标准USB键盘的windows设备驱动程序的开发过程与方法。
【关键词】设备驱动;驱动开发包;非标准键盘
1.引言
USB总线的成功关键是使用户感到了使用USB设备的方便。即插即用(PnP)概念的使用使某些硬件的安装过程得到了简化。USB规范中指出,适合迁移到USB1.1上的硬件限定于那些低速到中速的外设,包括键盘,鼠标等。即这些设备的数据传输速率低于12Mb/sec,并且能通过单一的PC接口被系统软件识别。现在标准的usb键盘设备只需要遵循一些hid设备的协议就可以被windows操作系统自动识别无需设备制造商开发驱动程序,但有些键盘带有特殊功能,所以需要设备驱动程序。
2.Usb软件系统简介
USB设备对于USB系统来说是一个端点的集合,端点被分成组,一组端点实现一个接口,如图1所示。设备端点和主机软件之间利用管道进行数据交互。设备驱动程序就是通过这些接口和管道与设备进行通信的。
USB数据传输就是指发生在主机软件和USB设备上特定端点(endpoint)之间的数据交互,一个设备可以具有若干管道(pipe)。一般情况下,一个管道中数据传输与其他管道中的数据传输是相互独立的。这种发生在管道中的数据流动共有4种基本类型:
(1)控制传输,一般发生在设备枚举阶段
(2)块传输,一般用于usb disk
(3)中断传输,一般用于键盘鼠标类设备
(4)流传输,一般用于语音视频流设备
USB设备驱动程序都必须使用这些管道和接口来管理设备,而不是直接通过内存或I/O端口来存取来管理。
3.Windows驱动程序的工作原理
3.1 Windows驱动程序基本架构
驱动程序与应用程序的最大差别在于驱动程序的架构。应用程序从头到尾都在main或者winmain函数的控制下执行,驱动程序不存在这样的函数,而只是一个由I/O管理组件根据需要调用的子例程的集合,I/O管理组件根据不同的情况调用驱动程序中不同的例程。构成内核模式驱动程序的主要例程包括:初始化和清除例程,I/O系统服务调度例程,数据传输例程和资源回调例程,下面是一个基本的驱动例程架构:
extern”C”
NTSTATUS
DriverEntry(IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING RegistryPath)
{
DriverObject->DriverUnload=Driver Unload;
DriverObject->DriverExtension->AddDevice=AddDevice;
DriverObject->DriverStartIo=StartIo;
DriverObject->MajorFunction[IRP_MJ_PNP]=DispatchPnp;
DriverObject->MajorFunction[IRP_MJ_POWER]=DispatchPower;
DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL]=DispatchWmi;
...
servkey.Buffer=(PWSTR)ExAllocatePool(PagedPool,RegistryPath->Length+sizeof(WCHAR));
if(!servkey.Buffer)
return STATUS_INSUFFICIENT_RESOURCES;
servkey.Maximum Length=Registry Path->Length+sizeof(WCHAR);
RtlCopyUnicodeString(&servkey,RegistryPath);
return STATUS_SUCCESS;
}
(1)前三条语句为驱动程序的其它入口点设置了函数指针。在这里,我用了能表达其功能的名字命名了这些函数:DriverUnload、AddDevice、StartIo。
(2)每个WDM驱动程序必须能处理PNP、POWER、SYSTEM_CONTROL这三种请求;应该在这里为这些请求指定派遣函数。在早期的Windows 2000 DDK中,IRP_MJ_SYSTEM_CONTROL曾被称作IRP_MJ_WMI,所以我把系统控制派遣函数命名为DispatchWmi。
(3)在省略号处,你可以插入设置其它MajorFunction指针的代码。
(4)如果驱动程序需要访问设备的服务键,可以在这里备份RegistryPath串。例如,如果驱动程序要作为WMI生产者,则需要备份这个串。这里我假设已经在某处声明了一个类型为UNICODE_STRING的全局变量servkey。
(5)返回STATUS_SUCCESS指出函数成功。如果函数失败,应该返回NTSTATUS.H中的一个错误代码,或者返回用户定义的错误代码。STATUS_SUCCESS的值为0。
3.2 Windows系统的I/O过程
为了实现操作系统的兼容性,可移植性,Windows操作系统不允许用户的应用程序对硬件设备进行操作,而由操作系统的I/O管理组件管理几乎所有的I/O操作请求,并最终由驱动程序完成所有的操作。具体过程如下:
(1)对I/O的每种用户其请求,I/O管理程序从分页系统内存中分配一个I/O请求包(IRP),基于用户请求的文件句柄和I/O函数,I/O管理程序将IRP传递给对应的驱动程序调度例程;
(2)调度例程检查请求的参数,如合法,把IRP包传递给驱动程序的Start I/O例程;
(3)Start I/O例程使用IRP的内容开始一个设备I/O操作;
(4)当操作完成,驱动程序的DpcForIsr例程将最后状态代码保存在IRP中,并将它返回给I/O管理程序;
(5)I/O管理程序使用IRP中的信息完成请求,并将最后状态返回给用户程序。
4.Usb键盘驱动
Usb系统驱动程序采用WDM结构,具体结构如图2所示。其中设备驱动程序位于整个结构的最顶层,它不直接操作硬件,而是通过一个接口传递请求。
4.1 USB设备驱动程序的初始化和枚举过程
当设备驱动程序被系统加载时,首先进入DriverEntry()入口例程,在该例程中主要设置程序要处理的各个MajorFunction IRP和AddDevice的处理函数入口点。当USB堆栈检测到设备后,PNP调用DriverEntry例程中设置的AddDevice处理函数进行资源的分配和FDO的创建。下面介绍usb键盘驱动这段代码:
NTSTATUS UsbKbdAddDevice(IN PDRIVER_OBJECT DriverObject,
IN PDEVICE_OBJECT pdo)
{
DebugPrint("AddDevice");
NTSTATUS status;
PDEVICE_OBJECT fdo;
status=IoCreateDevice(DriverObject,sizeof(USBKBD_DEVICE_EXTENSION),NULL,FILE_DEVICE_UNKNOWN,0,FALSE,&fdo);
if(!NT_SUCCESS(status))
return status;
PUSBKBD_DEVICE_EXTENSION dx=(PUSBKBD_DEVICE_EXTENSION)fdo->Device Extension;
dx->fdo=fdo;
dx->pdo=pdo;
dx->UsageCount=1;
KeInitializeEvent(&dx->StoppingEvent,NotificationEvent,FALSE);
dx->OpenHandleCount=0;
dx->GotResources=false;
dx->Paused=false;
dx->IODisabled=true;
dx->Stopping=false;
dx->UsbConfigurationHandle=NULL;
dx->UsbPipeHandle=NULL;
dx->UsbTimeout=10;
DebugPrint("FDO is %x",fdo);
//Register and enable our device interface
status=IoRegisterDeviceInterface
(pdo,&USBKBD_GUID,NULL,&dx->ifSymLinkName);
if(!NT_SUCCESS(status))
{
IoDeleteDevice(fdo);
return status;
}
dx->NextStackDevice=IoAttachDeviceToDeviceStack(fdo,pdo);
fdo->Flags|=DO_BUFFERED_IO|DO_POWER_PAGABLE;
fdo->Flags &=~DO_DEVICE_INITIAL IZING;
return STATUS_SUCCESS;
}
当USB堆栈分配完资源后,将发送一个IRP_MN_START_DEVICE,此时DriverEntry例程中指定的IRP_MJ_PNP处理函数将被激活。它将完成下面的工作:获取设备描述符;获取配置描述符;从配置描述符中选择一个接口,并用该接口配置设备。这就是USB设备的枚举过程,如枚举成功,将配置信息放在设备扩展中,这时的驱动程序已经完全就绪,等待应用程序发出与设备进行通信的请求。
4.2 USB键盘设备的数据交互过程
当应用程序与设备进行数据传输时,应用程序首先必须调用CreateFile打开设备,然后利用返回句柄对设备进行读写操作,用户在调用CreateFile时使用的文件名一般是驱动程序里建立一个符合链接(SymLinkName)。对于驱动程序,当应用程序调用CreateFile时,相应于IRP_MJ_CREATE的处理函数被激活,并完成下面工作:获取当前IRP的堆栈指针,通过堆栈指针获得要打开的文件名,然后通过文件名找到内部管道ID,如对当前接口此管道ID合法,则将该管道打开,并返回给应用程序一个句柄。
管道打开后,APP将会调用ReadFile或者WriteFile从设备读取数据或向设备写数据。此时驱动程序中对应于IRP_MJ_READ或者IRP_MJ_WRITE的处理函数被激活。这些处理函数将读写请求转换成相应的USB请求包(URP)传递给下层驱动程序,并等待I/O处理的最后完成。
本项目USB设备数据传输采用中断方式,IRP_MJ_READ对应函数UsbKbdRead,IRP_MJ_WRITE对应函数UsbKbdWrite,IRP_MJ_CREATE对应函数UsbKbdCreate,对于UsbKbdCreate函数里配置设备接口时,由于标准键盘的中断传输就是单方向只有一个管道,本文的非标准键盘是自定义的也需要对键盘进行写操作,所以需要配置一个IN和OUT管道,所以配置函数UsbSelectConfiguration里需要配置设备传递来中断端点的管道信息。
5.驱动程序的编译调试
为了能在VC下利用DDK编译驱动程序,创建文本文件build.bat,并粘贴下面代码
@echo on
set DDKPATH=D:\WINDDK @ddk安装目录
set PRJDRV=F:
set PRJPATH=F:\UsbKbd @项目程序目录
rem call BuildDriver.bat %DDKPATH% %PRJDRV% %PRJPATH% %1 %2
if %1==""goto usage1
if %2==""goto usage2
if not exist %DDKPATH%\bin\setenv.bat goto usage
call %DDKPATH%\bin\setenv.bat %DDKPATH% %1 %2
@echo on
%PRJDRV%
cd %PRJPATH%
build %1 %2
goto exit
:usage1
echo usage can't know DDK path!
goto exit
:usage2
echo usage can't know os type!
:exit
建立一个新的makefile工程,并在build command line那里设定build.bat free wxp,就是进行free版本的编译并利用wxp的DDK,本项目调试主要通过DebugPrint打印信息。
6.结束语
本文分析了驱动程序的工作原理,给出了编写驱动程序的主要过程,给出编写驱动程序的总体架构,并在主要细节上作了解释,为开发人员提供一定的参考。
参考文献
[1]Art Baker.Windows2000设备驱动程序设计指南[M].北京:机械工业出版社,2001.
[2]杨龙,刘岩.USB设备驱动程序的设计与开发[J].北京:装备指挥技术学院学报,2003-01-Vol14.
[3]刘蓬,张培仁.用DDK开发WDM驱动程序[J].合肥:计算机应用,2003(12):vol23.
[4]Walter Oney.Programming the Microsoft Windows Driver Model[M].1999.