串口监视过滤驱动程序开发详解(1)
1 系统综述及架构分析
1.1 系统综述
串口监视主要由串口过滤驱动程序及串行数据监视显示应用程序两部分组成。
首先请允许我解释一下滤驱动程序:
WDM模型假定硬件设备可以有多个驱动程序,每个驱动程序都有自己管理设备的方法。WDM根据设备对象堆栈来完成驱动程序的分层。一个过滤器驱动程序,该驱动程序可位于功能驱动程序的上面或下面,它通过过滤流经它的IRP来修改设备的行为。
处于功能驱动程序之上的过滤器驱动程序称为上层过滤器;处于功能驱动程序之下的过滤器驱动程序(仍处于总线驱动程序之上)称为下层过滤器。虽然这两种驱动程序本身用于不同的目的,但创建这两种驱动程序的机制完全相同。实际上,创建过滤器驱动程序就象创建任何其它WDM驱动程序一样,都有DriverEntry例程、AddDevice例程、一组派遣函数,等等。
上层过滤器驱动程序的用途是帮助支持这样的设备,这种设备的大多数方面都象其所属类的普通设备,但有一些附加功能。你可以依靠一个通用的功能驱动程序来支持设备的普通行为。为了处理设备的附加功能,你可以写一个上层过滤器驱动程序来干预IRP流。举一个有趣的例子,假设存在一个烤面包机设备的标准类,并且已经有人为其写了一个标准驱动程序。再假设你的特殊烤面包机有一个高级的面包片弹出特征,它可以把烤好的面包片弹到两英尺高的空中。而控制这个AWE(Advanced Waffle Eject)特征的工作就是上层过滤器驱动程序的任务。
1.2 架构分析
串口过滤驱动程序:处于WINDOWS串口驱动程序之上,其分析截取流经它的IRP所携带的串行口收发数据,并通过与应用程序交互将所截数据显示给用户查看,同时将数据向下层驱动发送以运行正常的串行操作。从而达到分析相关数据而不影响串口正常工作方式的串口监视的目的。
具体到本系统中串口过滤驱动程序就建立了一个串口过滤设备对象,其处于串口功能设备对象之上分析流经串口的所有数据,并将其保存起来。
与此同时驱动程序还建立了一个数据交互设备对象,其主要用于完成与应用控制程序之间传递参数,读取数据等工作。
两个设备对象密切配合共同将流经串口的所有数据分析、传递给应用控制程序,以达到串口监视的功能。
串行数据监视显示应用程序:主要用于动态加载串口过滤驱动程序,发送串口号及交互事件,共享内存地址等必要参数,并开启监视线程接收串口过滤驱动程序的监视数据显示在用户终端上。程序使用VC6.0开发,运行独立稳定,交互性强。
在下面的章节中,我们将分别具体分析系统两部分的原理,实现及应用运行应用方法。
2 串口过滤驱动程序具体实现
2.1 开发方法及步骤
一旦最初的分析和设计完成,就要开始编写代码了。按照以下的步骤进行可以减少调试的时间
2.1.1. 确定驱动程序需要哪些内核模式对象。
2.1.2. 确定驱动程序需要哪些上下文环境或者状态信息和这些信息的存储位置。
2.1.3. 首先编写DriverEntry和Unload例程,最初不要增加即插即用支持,这样允许通过控制面板手动的测试驱动程序的装载和卸载。
2.1.4. 添加处理IRP_MJ_CREATE和IRP_MJ_CLOSE的操作和一些不需要进行设备的访问例程。然后可以使用一个简单的WIN32程序调用CreateFile和CloseHandle来测试。
2.1.5. 添加寻找和分配驱动程序的硬件的代码,还有在驱动程序被卸载后的重新分配硬件的代码。如果硬件支持即插即用,这一步测试硬件和驱动程序的自动加载能力。
2.1.6. 添加处理IRP_MJ_XXX函数的派遣例程,最初的例程应该没有使用物理设备,后来新的代码应该使用简单的WIN32程序进行测试,例如ReadFile和WriteFile调用,或者其它支持的函数。
2.1.7. 最后完成Start I/O例程,ISR和DPC例程。现在可以使用真实的数据和硬件进行测试。
遵循以上的开发步骤我们进行具体的开发。
2.2 ComSpy
.h头文件编写及函数、变量、结构、常量的定义
2.2.1. 头文件中首先定义了I/O控制代码,其主要用于DeviceIoControl API函数互驱动程序交互时的功能定义。
DeviceIoControl的Code参数是一个32位数值常量。
这些域的解释如下:
Device type(16位,CTL_CODE宏的第一个参数) 指出能实现这个控制操作的设备类型。
访问代码(“A”占2位,CTL_CODE的第四个参数) 指出使用设备句柄发出这个控制操作时,应用程序必须具有的访问权限。
Function code(12位,CTL_CODE的第二个参数) 指出该代码描述的是哪一个控制操作。Microsoft保留了该域的前半部分,从0到2047的值。因此我们只能使用从2048到4095之间的值。
缓冲方式(“M”占两位,CTL_CODE的第三个参数) 指出I/O管理器如何处理应用程序提供的输入输出缓冲区。
例如:在头文件中的
#define IO_OPEN_COM /
CTL_CODE(FILE_DEVICE_COMSPY, 0x080A, METHOD_BUFFERED, /
FILE_ANY_ACCESS)其主要用于向驱动程序传递将要监视的串口号。
#define IO_REFERENCE_EVENT /
CTL_CODE(FILE_DEVICE_COMSPY, 0x0803, METHOD_NEITHER, /
FILE_ANY_ACCESS)其主要用于向驱动程序传递交互事件对象,及时触发数据交互。
还有许多这样的功能代码共同完成了从应用程序到驱动程序的交互控制。详细内容见附录代码。
2.2.2 接下来在头文件中定义了一些结构,其它主要用于IRP传递时携带相关数据。
例如:在头文件中的
typedef struct _DEVICE_EXTENSION
{
PDEVICE_OBJECT pFilterDeviceObject; // 过滤设备对象(自身)
PDEVICE_OBJECT TargetDeviceObject; // 绑定的设备对象
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;其主要用于设备对象的DeviceExtension(PVOID),该结构可用于保存每个设备实例的信息。I/O管理器为该结构分配空间,但该结构的名字和内容完全由用户决定。程序中具体使用它来保存及获取过滤设备对象(自身)、绑定的设备对象(这里指的是串口驱动)。
typedef struct tagIO_REQ
{
ULONG SizeTotal;
ULONG SizeCopied;
CHAR type;
LIST_ENTRY entry;
PVOID pData;
}IO_REQ, *PIO_REQ;其主要用于将截取的数据保存至此结构队列链表中,以方便应用程序来读取。
其它结构内容不一一说明,详见附录代码。
2.2.3 最后在头文件中声明了一些驱动例程,其用于完成驱动程序的全部工作。
例如:在头文件中的
NTSTATUS DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
); DriverEntry是内核模式驱动程序主入口点, 一个驱动程序可以被多个类似的硬件使用,但驱动程序的某些全局初始化操作只能在第一次被装入时执行一次。而DriverEntry例程就是用于这个目的。后面的章节我还将重点分析实现此例程。
其它例程内容不一一说明,详见附录代码。
更多分享请关注:软信网-编程-http://www.iis365.net.cn