Windows NT 分层驱动程序

2010-08-31 13:51:25

 

Windows驱动开发技术详解 12章 分层驱动程序

 

我在这里推荐《Windows驱动开发技术详解》

这本书非常不错,适合初次接触Windows驱动的开发者,但是前提是要有一定的操作系统基础。

至于Windows的驱动开发环境建立下次再写。

 

Windows下的驱动一般分为NT式和WDM式。

NT驱动一般是分层结构,很多情况下在物理层上加上功能层以及过滤层。

而WDM驱动结构实际上也就是两层驱动,至于物理层一般由Windows系统提供。

 

第12章分层驱动程序提供了一个简单的分层驱动,下载地址:http://download.csdn.net/source/2660192

 

下面是驱动的结构说明:

1. 概述:

下层驱动DriverA,上层驱动DriverB,DriverB连接在DriverA上层。

DriverB转发Create,Close和Read的IRP给DriverA,为Read的IRP提供了完成例程。

DriverA在Read中设置了定时器DPC,模拟实际的读写操作延迟。

 

2. DriverA:

a.创建设备->初始化Timer->设置DPC例程

NTSTATUS CreateDevice (
  IN PDRIVER_OBJECT pDriverObject)
{
 NTSTATUS status;
 PDEVICE_OBJECT pDevObj;
 PDEVICE_EXTENSION pDevExt;
 
 //创建设备名称
 UNICODE_STRING devName;
 RtlInitUnicodeString(&devName,L"
//Device//MyDDKDeviceA");
 
 //创建设备
 status =
IoCreateDevice( pDriverObject,
      sizeof(DEVICE_EXTENSION),
      &(UNICODE_STRING)devName,
      FILE_DEVICE_UNKNOWN,
      0, TRUE,
      &pDevObj );
 if (!NT_SUCCESS(status))
  return status;

 pDevObj->Flags |= DO_BUFFERED_IO;
 pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;
 pDevExt->pDevice = pDevObj;
 pDevExt->ustrDeviceName = devName;

 KeInitializeTimer( &pDevExt->pollingTimer );

 KeInitializeDpc( &pDevExt->pollingDPC,
      OnTimerDpc,
      (PVOID) pDevObj );

 //创建符号链接
 UNICODE_STRING symLinkName;
 RtlInitUnicodeString(&symLinkName,L"
//??//HelloDDKA");
 pDevExt->ustrSymLinkName = symLinkName;
 status = IoCreateSymbolicLink( &symLinkName,&devName );
 if (!NT_SUCCESS(status))
 {
  IoDeleteDevice( pDevObj );
  return status;
 }
 return STATUS_SUCCESS;
}

 

b.Read中挂起IRP->启动Timer->DPC例程中完成IRP

NTSTATUS HelloDDKRead(IN PDEVICE_OBJECT pDevObj,
         IN PIRP pIrp)
{
 KdPrint(("DriverA:Enter A HelloDDKRead/n"));

 PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)
   pDevObj->DeviceExtension;

 //将IRP设置为挂起
 IoMarkIrpPending(pIrp);

 //将挂起的IRP记录下来
 pDevExt->currentPendingIRP = pIrp;

 //定义3秒后将IRP_MJ_READ的IRP完成
 ULONG ulMicroSecond = 3000000;

 //将32位整数转化成64位整数
 LARGE_INTEGER timeout = RtlConvertLongToLargeInteger(-10*ulMicroSecond);
 
 KeSetTimer(
  &pDevExt->pollingTimer,
  timeout,
  &pDevExt->pollingDPC );

 KdPrint(("DriverA:Leave A HelloDDKRead/n"));

 //返回pending状态
 return STATUS_PENDING;
}

 

VOID OnTimerDpc( IN PKDPC pDpc,
       IN PVOID pContext,
       IN PVOID SysArg1,
       IN PVOID SysArg2 )
{
 PDEVICE_OBJECT pDevObj = (PDEVICE_OBJECT)pContext;
 PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;

 PIRP currentPendingIRP = pdx->currentPendingIRP;

 KdPrint(("DriverA:complete the Driver A IRP_MJ_READ irp!/n"));

 //设置完成状态为STATUS_SUCCESS
  currentPendingIRP->IoStatus.Status = STATUS_SUCCESS;
  currentPendingIRP->IoStatus.Information = 0; // bytes xfered
  IoCompleteRequest( currentPendingIRP, IO_NO_INCREMENT );
}

 

3. DriverB:

a. 创建设备B->获取设备A->连接设备

extern "C" NTSTATUS DriverEntry (
   IN PDRIVER_OBJECT pDriverObject,
   IN PUNICODE_STRING pRegistryPath )
{
 NTSTATUS ntStatus;
 KdPrint(("DriverB:Enter B DriverEntry/n"));

 //注册其他驱动调用函数入口
 pDriverObject->DriverUnload = HelloDDKUnload;
 pDriverObject->MajorFunction[IRP_MJ_CREATE] = HelloDDKCreate;
 pDriverObject->MajorFunction[IRP_MJ_CLOSE] = HelloDDKClose;
 pDriverObject->MajorFunction[IRP_MJ_WRITE] = HelloDDKDispatchRoutine;
 pDriverObject->MajorFunction[IRP_MJ_READ] = HelloDDKRead;
 
 UNICODE_STRING DeviceName;
 RtlInitUnicodeString( &DeviceName, L"
//Device//MyDDKDeviceA" );

 PDEVICE_OBJECT DeviceObject = NULL;
 PFILE_OBJECT FileObject = NULL;
 //寻找DriverA创建的设备对象
 ntStatus = IoGetDeviceObjectPointer(&DeviceName,FILE_ALL_ACCESS,&FileObject,&DeviceObject);

 if (!NT_SUCCESS(ntStatus))
 {
  KdPrint(("DriverB:IoGetDeviceObjectPointer() 0x%x/n", ntStatus ));
  return ntStatus;
 }

 //创建自己的驱动设备对象
 ntStatus = CreateDevice(pDriverObject);

 if ( !NT_SUCCESS( ntStatus ) )
 {
  ObDereferenceObject( FileObject );
  DbgPrint( "IoCreateDevice() 0x%x!/n", ntStatus );
  return ntStatus;
 }

 PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) pDriverObject->DeviceObject->DeviceExtension;

 PDEVICE_OBJECT FilterDeviceObject = pdx->pDevice;

 //将自己的设备对象挂载在DriverA的设备对象上
 PDEVICE_OBJECT TargetDevice = IoAttachDeviceToDeviceStack( FilterDeviceObject,
            DeviceObject );
 //将底层设备对象记录下来
 pdx->TargetDevice = TargetDevice;
 
 if ( !TargetDevice )
 {
  ObDereferenceObject( FileObject );
  IoDeleteDevice( FilterDeviceObject );
  DbgPrint( "IoAttachDeviceToDeviceStack() 0x%x!/n", ntStatus );
  return STATUS_INSUFFICIENT_RESOURCES;
 }

 FilterDeviceObject->DeviceType = TargetDevice->DeviceType;
 FilterDeviceObject->Characteristics = TargetDevice->Characteristics;
 FilterDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
 FilterDeviceObject->Flags |= ( TargetDevice->Flags & ( DO_DIRECT_IO |
               DO_BUFFERED_IO ) );
 ObDereferenceObject( FileObject );

 KdPrint(("DriverB:B attached A successfully!/n"));
 
 KdPrint(("DriverB:Leave B DriverEntry/n"));
 return ntStatus;
}

 

b. 拷贝当前IRP至下层->设置完成例程->转发IRP至下层 ->完成例程中设置IRP状态

NTSTATUS
  MyIoCompletion(
    IN PDEVICE_OBJECT  DeviceObject,
    IN PIRP  Irp,
    IN PVOID  Context
    )
{
 //进入此函数标志底层驱动设备将IRP完成
 KdPrint(("Enter MyIoCompletion/n"));
    if (Irp->PendingReturned)
 {
  //传播pending位
        IoMarkIrpPending( Irp );
    }
 return STATUS_SUCCESS;//同STATUS_CONTINUE_COMPLETION
}

#pragma PAGEDCODE
NTSTATUS HelloDDKRead(IN PDEVICE_OBJECT pDevObj,
         IN PIRP pIrp)
{
 KdPrint(("DriverB:Enter B HelloDDKRead/n"));
 NTSTATUS ntStatus = STATUS_SUCCESS;
 //将自己完成IRP,改成由底层驱动负责

 PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;

 //将当前IRP堆栈拷贝底层堆栈
 IoCopyCurrentIrpStackLocationToNext(pIrp);

 //设置完成例程
 IoSetCompletionRoutine(pIrp,MyIoCompletion,NULL,TRUE,TRUE,TRUE);

 //调用底层驱动
    ntStatus = IoCallDriver(pdx->TargetDevice, pIrp);

 //当IoCallDriver后,并且完成例程返回的是STATUS_SUCCESS
 //IRP就不在属于派遣函数了,就不能对IRP进行操作了
 if (ntStatus == STATUS_PENDING)
 {
  KdPrint(("STATUS_PENDING/n"));
 }
 ntStatus = STATUS_PENDING;

 KdPrint(("DriverB:Leave B HelloDDKRead/n"));

 return ntStatus;
}


4. IRP转发:

转发IRP至下层驱动,调用IoCallDriver就可以了。

按照第12章中的说明,转发前,如果不需要用到本层IRP的,调用IoSkipCurrentIrpStackLocation。

如果需要用到本层IRP的,调用IoCopyCurrentIrpStackLocationToNext。

 

这里解释的不太清楚,所以还是看DDK的文档吧。

When your driver sends an IRP to the next-lower driver, your driver can call IoSkipCurrentIrpStackLocation if you do not intend to provide an IoCompletion routine (the address of which is stored in the driver's IO_STACK_LOCATION structure). If you call IoSkipCurrentIrpStackLocation before calling IoCallDriver, the next-lower driver receives the same IO_STACK_LOCATION that your driver received.

If you intend to provide an IoCompletion routine for the IRP, your driver should call IoCopyCurrentIrpStackLocationToNext instead of IoSkipCurrentIrpStackLocation.

 

 

所以说,需要用到完成例程的情况时,必须使用IoCopyCurrentIrpStackLocationToNext。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值