串口的过滤驱动例子

这里我们主要以串口1过滤驱动为例,例程:comtest
1、 先建好makefile文件和sources文件,因为用到RtlStringCchPrintfW函数,所以必须包含TARGETLIBS= $(DDK_LIB_PATH)/ntstrsafe.lib这句话,然后源文件(comtest.c)中加入
#define NTSTRSAFE_LIB
#include

2、程序部分我们先看DriverEntry函数,也就是整个驱动的入口程序
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver,PUNICODE_STRING reg_path)
{
ULONG i;
for (i = 0;i < IRP_MJ_MAXIMUM_FUNCTION;i ++)
{//遍历每个功能号
//本地函数 Dispatch 处理IRP请求
pDriver->MajorFunction = Dispatch;
}
pDriver->MajorFunction[IRP_MJ_POWER] = PowerDispatch;
pDriver->DriverUnload = DriverUnload;
//本地函数 AttachAllComs
AttachCom(pDriver);
return STATUS_SUCCESS;
}
pDriver->MajorFunction = Dispatch;这句是关联IRP函数,IRP_MJ_MAXIMUM_FUNCTION就是IRP请求的所有类别数,这里就是将所有类别的IRP请求关联到函数Dispatch,后面还有一句:
pDriver ->MajorFunction[IRP_MJ_POWER] = PowerDispatch;这句的意思也就是说,你也可以将个别的功能请求关联到其它函数,同时你从这几条语句也可以看出判断IRP主功能号的方法有两种,第一是关联特定函数,第二就是Dispatch函数中的判断方法。
pDriver ->DriverUnload = DriverUnload;这句是在动态卸载的时候调用的,里面一般放些内存和分配的设备的释放。
AttachCom(pDriver);这个函数是用来创建和加载过滤设备的(串口1)。

3、绑定要过滤的设备
void AttachCom(PDRIVER_OBJECT pDriver)
{
ULONG i;
PDEVICE_OBJECT com_dev_obj;
NTSTATUS status;
//本地函数 MyOpenCom 这里得到设备对象
status = MyOpenCom(&com_dev_obj);
if (com_dev_obj == NULL)
return;
//本地函数 AttachDevice
//这里创建空设备,并加入设备栈
//next_object 为 IoAttachDeviceToDeviceStack 的返回值
status = AttachDevice(pDriver,com_dev_obj,&filter_object,&next_object);
if (status != STATUS_SUCCESS)
{
//不做任何操作
}
}
status = MyOpenCom(&com_dev_obj);这句是获取上一级的串口设备对象,上一章已经说过,一个设备只有一个驱动对象,但可以有多个设备对象,上一章中的图,一个驱动程序就相当于一个设备对象,到此为止,相信大家对这些概念有点熟悉了。
status = AttachDevice(pDriver,com_dev_obj,&filter_object,&next_object);这句就是创建一个空设备,然后附加到上一级的串口设备对象上(变量com_dev_obj)。
后面还会讲到解除绑定和删除设备,就在DriverUnload函数中。
做个小结(中心思想):一般的驱动程序都会自已创建一个空的设备对象,然后附加到指定的设备上,大多数情况下新附加的程序会放在顶层。当然,在此之前,要做好处理IRP请求的准备工作(设定IRP处理函数,本例中的Dispatch函数)

4、AttachCom又包括两个函数 AttachDevice和MyOpenCom
打开下层串口设备对象
NTSTATUS MyOpenCom(PDEVICE_OBJECT * ppDeviceObject)
{
UNICODE_STRING uni_name;
PFILE_OBJECT FileObject = NULL;
NTSTATUS status;
* ppDeviceObject = NULL;//先初始化为NULL
//这里的"//Device//Serial1",指串口1,你可以通过设备查看器(WinObj)看到,你的硬件中带有几个串口
RtlInitUnicodeString(&uni_name,L"//Device//Serial1");
//根据名字得到文件对象和设备对象
status = IoGetDeviceObjectPointer(&uni_name,FILE_ALL_ACCESS,&FileObject,ppDeviceObject);
if (status == STATUS_SUCCESS)
ObDereferenceObject(FileObject);//解除文件对象的引用
return status;
}
RtlInitUnicodeString(&uni_name,L"//Device//Serial1");//前面有过说明,将宽字符串转换为UNICODE_STRING
status = IoGetDeviceObjectPointer(&uni_name,FILE_ALL_ACCESS,&FileObject,ppDeviceObject);//用于得到串口设备(当前顶层设备对象),调用后*ppDeviceObject就是得到的串口设备对象
ObDereferenceObject(FileObject);//当调用成功后,要注意解除文件对象引用,以节省内存空间,本章不讲文件对象,所以用不着

5、AttachDevice创建和加载设备
NTSTATUS AttachDevice(PDRIVER_OBJECT pDriver,PDEVICE_OBJECT pOldObject,PDEVICE_OBJECT *ppFilterObject,PDEVICE_OBJECT *ppAddObject)
{
NTSTATUS status;
PDEVICE_OBJECT AddDevice = NULL;
//创建空设备
status = IoCreateDevice(pDriver,0,NULL,pOldObject->DeviceType,0,FALSE,ppFilterObject);
if (status != STATUS_SUCCESS)
return status;
//初始化空设备的标志位
if (pOldObject->Flags & DO_BUFFERED_IO)
(*ppFilterObject)->Flags |= DO_BUFFERED_IO;
if (pOldObject->Flags & DO_DIRECT_IO)
(*ppFilterObject)->Flags |= DO_DIRECT_IO;
if (pOldObject->Characteristics & FILE_DEVICE_SECURE_OPEN)
(*ppFilterObject)->Characteristics |= FILE_DEVICE_SECURE_OPEN;
(*ppFilterObject)->Flags |= DO_POWER_PAGABLE;
//将pOldObject绑定到*ppFilterObject设备上
AddDevice = IoAttachDeviceToDeviceStack(*ppFilterObject,pOldObject);
if (AddDevice == NULL)
{//绑定失败 则删除刚创建的设备
IoDeleteDevice(*ppFilterObject);
*ppFilterObject = NULL;
status = STATUS_UNSUCCESSFUL;
return status;
}
*ppAddObject = AddDevice;
//清除初始化标记
(*ppFilterObject)->Flags = (*ppFilterObject)->Flags & ~DO_DEVICE_INITIALIZING;
return STATUS_SUCCESS;
}
这个函数做了这么几件事:
① 创建空设备对象(IoCreateDevice)
② 复制下层设备对象(加载前是顶层)的标志,对本设备对象进行一系列初始化
③ 加载设备对象到下层设备中(IoAttachDeviceToDeviceStack)
④ 加载失败,则删除刚创建的设备(IoDeleteDevice)
注意:IoCreateDevice参数2是扩展结构的大小(字节数),具体参考WDK帮助,后面也会讲到

6、Dispatch函数(处理所有IRP的函数),在DriverEntry中调用pDriver->MajorFunction = Dispatch;
NTSTATUS Dispatch(PDEVICE_OBJECT pDevice,PIRP irp)
{
PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(irp);
NTSTATUS status;
ULONG i,j;
if (irpsp->MajorFunction == IRP_MJ_POWER)
{
PoStartNextPowerIrp(irp);
IoSkipCurrentIrpStackLocation(irp);//跳过当前栈空间,不对当前栈进行处理
return PoCallDriver(next_object,irp);//将电源IRP传递到指定的低级驱动中,从VISTA开始使用IoCallDriver
}
if (irpsp->MajorFunction == IRP_MJ_WRITE)
{//如果是写操作
ULONG len = irpsp->Parameters.Write.Length;//len 是写入的长度
PUCHAR buf = NULL;
if (irp->MdlAddress != NULL)//如果是直接方式(传输方式)
buf = (PUCHAR)MmGetSystemAddressForMdlSafe(irp->MdlAddress,NormalPagePriority);
else//如果是皆不是方式
buf = (PUCHAR)irp->UserBuffer;
if (buf == NULL)//缓冲方式
buf = (PUCHAR) irp->AssociatedIrp.SystemBuffer;
for (j = 0;j < len;++j)
{
DbgPrint("comcap: Send Data: %2x/r/n",buf[j]);//将写入的内容以16进制打印
}
}
IoSkipCurrentIrpStackLocation(irp);//跳过当前栈空间
return IoCallDriver(next_object,irp);//将除电源以外的IRP传递到指定的驱动中
}

① 获得当前IRP栈指针irpsp = IoGetCurrentIrpStackLocation(irp);
② 电源IRP处理(本程序中可以去除这部分,因为电源管理IRP被指定到PowerDispatch函数中)
PoStartNextPowerIrp(Irp);
IoSkipCurrentIrpStackLocation(irp);//跳过当前栈空间,不对当前栈进行处理
return PoCallDriver(next_object,irp);//将电源IRP传递到指定的低级驱动中,从VISTA开始使用IoCallDriver

///
#include /
//这个宏定义为了使用静态的ntstrsafe库
//#define NTSTRSAFE_LIB
//#include /<ntstrsafe.h/>//包含这个库为了使用RtlStringCchPrintfW,sources文件中也必须包含

//这里的时间以100NS为1单位的,一般情况下使用负数,原因可以参考WDK帮助,这个在《寒江独钓》中没有提到
#define DELAY_ONE_MICROSECOND -10
#define DELAY_ONE_SECOND -10000000

static PDEVICE_OBJECT filter_object = NULL;
static PDEVICE_OBJECT next_object = NULL;

NTSTATUS MyOpenCom(PDEVICE_OBJECT * ppDeviceObject);
void DriverUnload(PDRIVER_OBJECT pDriver);

NTSTATUS AttachDevice(PDRIVER_OBJECT pDriver,PDEVICE_OBJECT pOldObject,PDEVICE_OBJECT *ppFilterObject,PDEVICE_OBJECT *ppAddObject)
{
NTSTATUS status;
PDEVICE_OBJECT AddDevice = NULL;

//创建空设备
status = IoCreateDevice(pDriver,0,NULL,pOldObject->DeviceType,0,FALSE,ppFilterObject);
if (status != STATUS_SUCCESS)
return status;

//初始化空设备的标志位
if (pOldObject->Flags & DO_BUFFERED_IO)
(*ppFilterObject)->Flags |= DO_BUFFERED_IO;
if (pOldObject->Flags & DO_DIRECT_IO)
(*ppFilterObject)->Flags |= DO_DIRECT_IO;
if (pOldObject->Characteristics & FILE_DEVICE_SECURE_OPEN)
(*ppFilterObject)->Characteristics |= FILE_DEVICE_SECURE_OPEN;
(*ppFilterObject)->Flags |= DO_POWER_PAGABLE;

//将pOldObject绑定到*ppFilterObject设备上
AddDevice = IoAttachDeviceToDeviceStack(*ppFilterObject,pOldObject);
if (AddDevice == NULL)
{//绑定失败 则删除刚创建的设备
IoDeleteDevice(*ppFilterObject);
*ppFilterObject = NULL;
status = STATUS_UNSUCCESSFUL;
return status;
}
*ppAddObject = AddDevice;

//清除初始化标记
(*ppFilterObject)->Flags = (*ppFilterObject)->Flags & ~DO_DEVICE_INITIALIZING;
return STATUS_SUCCESS;
}

NTSTATUS MyOpenCom(PDEVICE_OBJECT * ppDeviceObject)
{
UNICODE_STRING uni_name;
// static WCHAR name[32] = {0};
PFILE_OBJECT FileObject = NULL;
NTSTATUS status;

* ppDeviceObject = NULL;//先初始化为NULL
// memset(name,0,sizeof(WCHAR) * 32);
//这里的"//Device//Serial%d",你可以通过设备查看器(WinObj)看到,你的硬件中带有几个串口
// RtlStringCchPrintfW(name,32,L"//Device//Serial%d",com_id);//要包含 ntstrsafe.h 才可以用 因为在《寒江独钓》中要绑定32个设备,而本例只绑定串口1
RtlInitUnicodeString(&uni_name,L"//Device//Serial1");

//根据名字得到文件对象和设备对象
status = IoGetDeviceObjectPointer(&uni_name,FILE_ALL_ACCESS,&FileObject,ppDeviceObject);

if (status == STATUS_SUCCESS)
ObDereferenceObject(FileObject);//解除文件对象的引用

return status;
}

void AttachCom(PDRIVER_OBJECT pDriver)
{
ULONG i;
PDEVICE_OBJECT com_dev_obj;
NTSTATUS status;

//本地函数 MyOpenCom 这里得到设备对象
status = MyOpenCom(&com_dev_obj);
if (com_dev_obj == NULL)
return;
//本地函数 AttachDevice
//这里创建空设备,并加入设备栈
//next_object 为 IoAttachDeviceToDeviceStack 的返回值
status = AttachDevice(pDriver,com_dev_obj,&filter_object,&next_object);
if (status != STATUS_SUCCESS)
{
//不做任何操作
}
}

void DriverUnload(PDRIVER_OBJECT pDriver)
{
ULONG i;
LARGE_INTEGER interval;

if (next_object != NULL)
IoDetachDevice(next_object);//解除绑定

interval.QuadPart = 5 * DELAY_ONE_SECOND;
KeDelayExecutionThread(KernelMode,FALSE,&interval);//延迟5秒

if (filter_object != NULL)
IoDeleteDevice(filter_object);//删除设备
}

NTSTATUS Dispatch(PDEVICE_OBJECT pDevice,PIRP irp)
{
PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(irp);
NTSTATUS status;
ULONG i,j;

if (irpsp->MajorFunction == IRP_MJ_POWER)
{
PoStartNextPowerIrp(irp);
IoSkipCurrentIrpStackLocation(irp);//跳过当前栈空间,不对当前栈进行处理
return PoCallDriver(next_object,irp);//将电源IRP传递到指定的低级驱动中,从VISTA开始使用IoCallDriver
}

if (irpsp->MajorFunction == IRP_MJ_WRITE)
{//如果是写操作
ULONG len = irpsp->Parameters.Write.Length;//len 是写入的长度
PUCHAR buf = NULL;
if (irp->MdlAddress != NULL)//如果是直接方式(传输方式)
buf = (PUCHAR)MmGetSystemAddressForMdlSafe(irp->MdlAddress,NormalPagePriority);
else//如果是皆不是方式
buf = (PUCHAR)irp->UserBuffer;
if (buf == NULL)//缓冲方式
buf = (PUCHAR) irp->AssociatedIrp.SystemBuffer;

for (j = 0;j < len;++j)
{
DbgPrint("comcap: Send Data: %2x/r/n",buf[j]);//将写入的内容以16进制打印
}
}

IoSkipCurrentIrpStackLocation(irp);//跳过当前栈空间
return IoCallDriver(next_object,irp);//将除电源以外的IRP传递到指定的驱动中
}

NTSTATUS PowerDispatch(PDEVICE_OBJECT pDevice,PIRP irp)
{
ULONG i;

#if (NTDDI_VERSION < NTDDI_VISTA)
PoStartNextPowerIrp(irp);
IoSkipCurrentIrpStackLocation(irp);//跳过当前栈空间,不对当前栈进行处理
return PoCallDriver(next_object,irp);//将电源IRP传递到指定的低级驱动中,从VISTA开始使用IoCallDriver
#else
IoSkipCurrentIrpStackLocation(irp);
return IoCallDriver(next_object,irp);
#endif
}

NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver,PUNICODE_STRING reg_path)
{
ULONG i;

for (i = 0;i < IRP_MJ_MAXIMUM_FUNCTION;i ++)
{//遍历每个功能号
//本地函数 Dispatch 处理IRP请求
pDriver->MajorFunction = Dispatch;
}
pDriver->MajorFunction[IRP_MJ_POWER] = PowerDispatch;

pDriver->DriverUnload = DriverUnload;
//本地函数 AttachAllComs
AttachCom(pDriver);

return STATUS_SUCCESS;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值