Windows驱动开发——虚拟串口设备

文章转自:http://blog.csdn.net/chenyujing1234/article/details/7896364

1、DDK串口开发框架

DDK对串口驱动提供了专门接口。只要编写的驱动满足这些接口,并按照串口标准的命名方法,不管是真实的串口设备,还是虚拟设备,Windows操作系统都会认为

这个设备是一个标准的串口设备。用标准的串口调试工具都可以与这个设备进行通信

1、1 串口驱动的入口函数

本章的实例程序是在HelloWDM驱动的基础上修改而来,入口函数依然是DriverEntry,在DriverEntry函数中指定各种IRP的派遣函数,以及AddDevice 例程、卸载例程等。

/************************************************************************
* 函数名称:DriverEntry
* 功能描述:初始化驱动程序,定位和申请硬件资源,创建内核对象
* 参数列表:
      pDriverObject:从I/O管理器中传进来的驱动对象
      pRegistryPath:驱动程序在注册表的中的路径
* 返回 值:返回初始化驱动状态
*************************************************************************/
#pragma INITCODE 
extern "C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject,
								IN PUNICODE_STRING pRegistryPath)
{
	KdPrint(("Enter DriverEntry\n"));

	pDriverObject->DriverExtension->AddDevice = HelloWDMAddDevice;
	pDriverObject->MajorFunction[IRP_MJ_PNP] = HelloWDMPnp;
	pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = HelloWDMDispatchControlp;
	pDriverObject->MajorFunction[IRP_MJ_CREATE] = HelloWDMCreate;
	pDriverObject->MajorFunction[IRP_MJ_CLOSE] = HelloWDMClose;
	pDriverObject->MajorFunction[IRP_MJ_READ] = HelloWDMRead;
	pDriverObject->MajorFunction[IRP_MJ_WRITE] = HelloWDMWrite;
	pDriverObject->DriverUnload = HelloWDMUnload;

	KdPrint(("Leave DriverEntry\n"));
	return STATUS_SUCCESS;
}

其中在AddDevice例程中,需要创建设备对象,这些都是和以前的HelloWDM驱动程序类似。在创建完设备对象后,需要将设备对象指定一个符号链接,该符号链接必须是

COM开头,并接一下数字,如本例就采用了COM7。因为COM1和COM2在有些计算机中有时会被占用,因此,当该设备对象在指定符号链接时,应该避免采用这些名称。

/************************************************************************
* 函数名称:HelloWDMAddDevice
* 功能描述:添加新设备
* 参数列表:
      DriverObject:从I/O管理器中传进来的驱动对象
      PhysicalDeviceObject:从I/O管理器中传进来的物理设备对象
* 返回 值:返回添加新设备状态
*************************************************************************/
#pragma PAGEDCODE
NTSTATUS HelloWDMAddDevice(IN PDRIVER_OBJECT DriverObject,
                           IN PDEVICE_OBJECT PhysicalDeviceObject)
{ 
	PAGED_CODE();
	KdPrint(("Enter HelloWDMAddDevice\n"));

	NTSTATUS status;
	PDEVICE_OBJECT fdo;
	UNICODE_STRING devName;
	RtlInitUnicodeString(&devName,L"\\Device\\MyWDMDevice");
	status = IoCreateDevice(
		DriverObject,
		sizeof(DEVICE_EXTENSION),
		&(UNICODE_STRING)devName,
		FILE_DEVICE_UNKNOWN,
		0,
		FALSE,
		&fdo);
	if( !NT_SUCCESS(status))
		return status;
	PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)fdo->DeviceExtension;
	pdx->fdo = fdo;
	pdx->NextStackDevice = IoAttachDeviceToDeviceStack(fdo, PhysicalDeviceObject);
	UNICODE_STRING symLinkName;
	RtlInitUnicodeString(&symLinkName,L"\\DosDevices\\COM7");

	pdx->ustrDeviceName = devName;
	pdx->ustrSymLinkName = symLinkName;
	status = IoCreateSymbolicLink(&(UNICODE_STRING)symLinkName,&(UNICODE_STRING)devName);

	if( !NT_SUCCESS(status))
	{
		IoDeleteSymbolicLink(&pdx->ustrSymLinkName);
		status = IoCreateSymbolicLink(&symLinkName,&devName);
		if( !NT_SUCCESS(status))
		{
			return status;
		}
	}
	// 设置为缓冲区设备
	fdo->Flags |= DO_BUFFERED_IO | DO_POWER_PAGABLE;
	fdo->Flags &= ~DO_DEVICE_INITIALIZING;

	KdPrint(("Leave HelloWDMAddDevice\n"));
	return STATUS_SUCCESS;
}

在创建完符号链接后,还不能保证应用程序能找出这个虚拟的串口设备,还需要进一步修改注册表。具体位置是HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM,可以在这里加入新项目。本例的项目名是MyWDMDevice,类型为REG_SZ,内容是COM7。

在上述步骤后,即在AddDevice例程中创建COM7的符号链接,并且在注册表进行相应设置,系统会认为有这个串口驱动,用任何一个串口调试软件,都可以枚举到

该串口。

1、2  应用程序与串口驱动的通信

其实对于一个真实的串口驱动,或者这个介绍的虚拟串口驱动,都需要遵循一组接口。这组接口由微软事先定义好了,只要符合这组接口,

windows就会认为这是一个串口设备。这里所指的接口就是应用程序发的IO控制码和读写命令,因此对于串口驱动只要对这些IRP的派遣函数编写适当,就能实现一个串口驱动。

首先用IRPTrace看一下,需要对哪些IRP进行处理,笔者加载本章已经介绍的虚拟串口驱动,并用IRPTrace 拦截其IRP处理信息,在打开串口工具后,会发现IRPTrace立刻跟踪到若干个IO控制码。

下面依次解释这些IO控制码,理解这些IO控制码,并处理好这些控制码,是编写串口驱动的核心。关于这些IO控制码在ntddser.h文件中,都有相应的定义,并且还有

相应的数据结构定义。

(1)IOCTL_SERIAL_SET_QUEUE_SIZE

这个控制码是应用程序向驱动请求设置串口驱动内部的缓冲区大小,它是向驱动传递SEARIAL_QUEUE_SIZE 数据结构来进行设置的,对于虚拟串口驱动来说,这是不需要

关心的。用IRPTrace可以看出,串口调试工具会向驱动发送的请求是0x400大小的缓冲区大小。

(2)IOCTL_SERIAL_GET_BAUD_RATE

串口调试工具会接着向驱动发送IOCTL_SERIAL_GET_BAUD_RATE命令,这主要是询问驱动这个设备的波特率。驱动应该回应应用程序SEARIAL_BAUD_RATE数据结构,来通知波特率的数值。

(3)IOCTL_SERIAL_GET_LINE_CONTROL

串口调试工具接着向驱动发送IOTCL_SERIAL_GET_LINE_CONTROL命令,这主要是为了返回串口的行控制信息,行控制信息用SERIAL_LINE_CONTROL数据结构表示。

[cpp]  view plain copy
  1. typedef struct _SERIAL_LINE_CONTROL {  
  2.     UCHAR StopBits;  
  3.     UCHAR Parity;  
  4.     UCHAR WordLength;  
  5.     } SERIAL_LINE_CONTROL,*PSERIAL_LINE_CONTROL;  

其中StopBits是停止位,可以是STOP_BIT_1、STOP_BITS_1_5、STOP_BITS_2等取值。

Parity代表校验位,可以是NO_PARITY、ODD——PARITY、EVEN_PARITY、MARK——PARITY、SPACE_PARITY。WorkLength是数据位,可以是5、6、7、8。

[cpp]  view plain copy
  1. case IOCTL_SERIAL_GET_LINE_CONTROL:   
  2. {  
  3.     *((PSERIAL_LINE_CONTROL)(Irp->AssociatedIrp.SystemBuffer)) = pdx->Lc;  
  4.   
  5.           Irp->IoStatus.Information = sizeof(SERIAL_LINE_CONTROL);  
  6.     break;  
  7. }  

(4)IOCTL_SERIAL_GET_CHARS

串口调试工具会接着向驱动发送IOCTL_SERIAL_GET_CHARS命令,这个命令是应用程序向驱动请求特殊字符,用来与控制信号握手,用数据结构SERIAL_CHARS表示。

[cpp]  view plain copy
  1. typedef struct _SERIAL_CHARS {  
  2.     UCHAR EofChar;  
  3.     UCHAR ErrorChar;  
  4.     UCHAR BreakChar;  
  5.     UCHAR EventChar;  
  6.     UCHAR XonChar;  
  7.     UCHAR XoffChar;  
  8.     } SERIAL_CHARS,*PSERIAL_CHARS;  


其中EofChar代表是否是传送结束、ErrorChar代码是否传送中有错误、BreadChar代码是否传送有停止等。

(5)IOCTL_SERIAL_GET_HANDFLOW

串口调试工具会接着向驱动发送IOCTL_SRIAL_GET_HANDFLOW命令,这个命令是负责向驱动程序获得串口驱动的握手信号,握手信号用SERIAL_HANDFLOW数据

结构表示:

[cpp]  view plain copy
  1. typedef struct _SERIAL_HANDFLOW {  
  2.     ULONG ControlHandShake;  
  3.     ULONG FlowReplace;  
  4.     LONG XonLimit;  
  5.     LONG XoffLimit;  
  6.     } SERIAL_HANDFLOW,*PSERIAL_HANDFLOW;  


(6)IOCTL_SERIAL_SET_WAIT_MASK

串口工具会接着向驱动发送IOCTL_SERIAL_SET_WAIT_MASK命令,这个命令主要是设置串口驱动的某些事件发生时,需要向应用程序通知,这些事件包括以下几种事件:

[cpp]  view plain copy
  1. #define SERIAL_EV_RXCHAR           0x0001  // Any Character received  
  2. #define SERIAL_EV_RXFLAG           0x0002  // Received certain character  
  3. #define SERIAL_EV_TXEMPTY          0x0004  // Transmitt Queue Empty  
  4. #define SERIAL_EV_CTS              0x0008  // CTS changed state  
  5. #define SERIAL_EV_DSR              0x0010  // DSR changed state  
  6. #define SERIAL_EV_RLSD             0x0020  // RLSD changed state  
  7. #define SERIAL_EV_BREAK            0x0040  // BREAK received  
  8. #define SERIAL_EV_ERR              0x0080  // Line status error occurred  
  9. #define SERIAL_EV_RING             0x0100  // Ring signal detected  
  10. #define SERIAL_EV_PERR             0x0200  // Printer error occured  
  11. #define SERIAL_EV_RX80FULL         0x0400  // Receive buffer is 80 percent full  
  12. #define SERIAL_EV_EVENT1           0x0800  // Provider specific event 1  
  13. #define SERIAL_EV_EVENT2           0x1000  // Provider specific event 2  
[cpp]  view plain copy
  1. case IOCTL_SERIAL_SET_WAIT_MASK:  
  2.         {  
  3.             PIRP            pOldWaitIrp;  
  4.             PDRIVER_CANCEL  pOldCancelRoutine;  
  5.   
  6.             pdx->EventMask = *(PULONG)Irp->AssociatedIrp.SystemBuffer;  
  7.   
  8.             KeAcquireSpinLock(&pdx->IoctlSpinLock, &OldIrql);  
  9.   
  10.             pOldWaitIrp = pdx->pWaitIrp;  
  11.             if (pOldWaitIrp != NULL)  
  12.             {  
  13.                 pOldCancelRoutine = IoSetCancelRoutine(pOldWaitIrp, NULL);  
  14.   
  15.                 //对以前没有进行完成例程的等待irp,进行完成  
  16.                 if (pOldCancelRoutine != NULL)  
  17.                 {  
  18.                     pOldWaitIrp->IoStatus.Information = sizeof(ULONG);  
  19.                     *(PULONG)pOldWaitIrp->AssociatedIrp.SystemBuffer = 0;  
  20.   
  21.                     pOldWaitIrp->IoStatus.Status = STATUS_SUCCESS;  
  22.   
  23.                     pdx->pWaitIrp = NULL;  
  24.                 }  
  25.                 else  
  26.                 {  
  27.                     pOldWaitIrp = NULL;  
  28.                 }  
  29.             }  
  30.   
  31.             KeReleaseSpinLock(&pdx->IoctlSpinLock, OldIrql);  
  32.   
  33.             if (pOldWaitIrp != NULL)  
  34.             {  
  35.                 IoCompleteRequest(pOldWaitIrp, IO_NO_INCREMENT);  
  36.             }  
  37.   
  38.             break;  
  39.         }  

当设置新的阻塞事件时,哪果之前有等待的IRP,那么先进行完成

(7)IOCTL_SERIAL_WAIT_ON_MASK

这个IO控制码是最重要的一个,当串口调试工具通过前面几个IO控制码初始华好后,就会发送这个请求。

在驱动程序中,应该阻塞在那里,即返回PENDING状态,且通过IoSetCancelRoutine设置取消例程,而不是完成这个IRP。

当IOCTL_SERIAL_SET_WAIT_MASK设置的事件中的一项发生时,阻塞状态改为完成,并通知应用程序是哪种事件发生了。

[cpp]  view plain copy
  1. case IOCTL_SERIAL_WAIT_ON_MASK:  
  2.         {  
  3.             PDRIVER_CANCEL  pOldCancelRoutine;  
  4.   
  5.             KeAcquireSpinLock(&pdx->IoctlSpinLock, &OldIrql);  
  6.   
  7.             //等待irp一定被清除,且eventMask一定不为0  
  8.             if ((pdx->pWaitIrp != NULL) || (pdx->EventMask == 0))  
  9.                 ntStatus = STATUS_INVALID_PARAMETER;  
  10.             else if ((pdx->EventMask & pdx->HistoryEvents) != 0)  
  11.             {  
  12.                 // Some events happened  
  13.                 Irp->IoStatus.Information = sizeof(ULONG);  
  14.                 *(PULONG)Irp->AssociatedIrp.SystemBuffer = pdx->EventMask & pdx->HistoryEvents;  
  15.                 pdx->HistoryEvents = 0;  
  16.                 ntStatus = STATUS_SUCCESS;  
  17.             }else  
  18.             {  
  19.                 pdx->pWaitIrp = Irp;  
  20.   
  21.                 ntStatus = STATUS_PENDING;  
  22.   
  23.                 IoSetCancelRoutine(Irp, DriverCancelWaitIrp);  
  24.   
  25.                 if (Irp->Cancel)  
  26.                 {  
  27.                     pOldCancelRoutine = IoSetCancelRoutine(Irp, NULL);  
  28.   
  29.                     if (pOldCancelRoutine != NULL)  
  30.                     {  
  31.                         ntStatus = STATUS_CANCELLED;  
  32.   
  33.                         pdx->pWaitIrp = NULL;  
  34.                     }  
  35.                     else  
  36.                     {  
  37.                         IoMarkIrpPending(Irp);  
  38.                     }  
  39.                 }  
  40.                 else  
  41.                 {  
  42.                     IoMarkIrpPending(Irp);  
  43.                 }  
  44.             }  
  45.   
  46.             KeReleaseSpinLock(&pdx->IoctlSpinLock, OldIrql);  
  47.             break;  
  48.   
  49.         }  

 

[cpp]  view plain copy
  1. VOID DriverCancelWaitIrp(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)  
  2. {  
  3.     KdPrint(("DriverCancelWaitIrp\n"));  
  4.     PDEVICE_EXTENSION pExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;  
  5.     KIRQL                   OldIrql;  
  6.   
  7.     IoReleaseCancelSpinLock(Irp->CancelIrql);  
  8.   
  9.     KeAcquireSpinLock(&pExtension->IoctlSpinLock, &OldIrql);  
  10.   
  11.     pExtension->pWaitIrp = NULL;          
  12.   
  13.     KeReleaseSpinLock(&pExtension->IoctlSpinLock, OldIrql);  
  14.   
  15.     Irp->IoStatus.Status = STATUS_CANCELLED;  
  16.     IoCompleteRequest(Irp, IO_NO_INCREMENT);  
  17. }  


关于删除例程的知识可以参考<<驱动程序的取消IRP >>

1、3  写的实现

串口驱动除了需要完成处理IO控制码外,还需要对读写IRP进行处理。一般情况下,作为应用程序的串口调试工具会开启多个线程,其中主线程负责与串口驱动初始化的IO控制码通信。

另外一个很重要的线程就是发送IOCTL_SERIAL_WAIT_ON_MASK请求,对于没有数据传输的情况下,这个IO控制码请求会PENDING在那里,即阻塞。当有传送的请求时,相应的事件被触发,刚才因为IOCTL_SERAIL_WAIT_ON_MASK的IRP被阻塞的线程得以继续运行,

如果应用程序得知该事件是被写入了一个字符,会去发出一个读请求 ,对于驱动则是读的IRP。如果循环过程,从而实现了一个虚拟摄像头回写的例子。

在对于写IRP的派遣函数中,主要是将写的数据存储在设备扩展中,以便以后读的时候将这些内容返回到应用程序。

另外一个很重要的内容,就是阻塞的IO控制苏醒过来。在本例中调用DriverCheckEvent函数,该函数将阻塞的IRP完成,使应用程序的线程得以继续进行。并且这个线程还知道了SERIAL_EV_RXCHAR和SERIAL_EV_RX80FULL事件的到来,从而发起一个读请求,传送到驱动中就是读IRP。

[cpp]  view plain copy
  1. NTSTATUS HelloWDMWrite(IN PDEVICE_OBJECT fdo,  
  2.                         IN PIRP Irp)  
  3. {  
  4.     KdPrint(("HelloWDMWrite\n"));  
  5.       
  6.     NTSTATUS ntStatus = STATUS_SUCCESS;// Assume success  
  7.   
  8.     PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)fdo->DeviceExtension;  
  9.     // 获得当前IO堆栈  
  10.     PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation( Irp );  
  11.     // 获取当前IO堆栈的操作字节数  
  12.     ULONG DataLen = irpSp->Parameters.Write.Length;  
  13.     // 从IRP的缓冲区中得到数据  
  14.     PUCHAR pData = (PUCHAR)Irp->AssociatedIrp.SystemBuffer;  
  15.     KIRQL OldIrql;  
  16.     PIRP            pOldReadIrp = NULL;  
  17.     PDRIVER_CANCEL  pOldCancelRoutine;  
  18.     // 设置IRP的操作字节数  
  19.     Irp->IoStatus.Information = 0;  
  20.     ntStatus = STATUS_SUCCESS;  
  21.   
  22.     if (DataLen == 0)  
  23.     {  
  24.         ntStatus = STATUS_SUCCESS;  
  25.     }else if (DataLen>COMBUFLEN)  
  26.     {  
  27.         ntStatus = STATUS_INVALID_PARAMETER;  
  28.     }  
  29.     else  
  30.     {  
  31.         KdPrint(("Write\n"));  
  32.         // 获取自旋锁  
  33.         KeAcquireSpinLock(&pdx->WriteSpinLock, &OldIrql);  
  34.         // 复制内存块  
  35.         RtlCopyMemory(pdx->Buffer,pData,DataLen);  
  36.   
  37.         pdx->uReadWrite = DataLen;  
  38.   
  39.         if (pdx->pReadIrp != NULL) // drop it out  
  40.         {  
  41.             // 记录IRP  
  42.             pOldReadIrp = pdx->pReadIrp;  
  43.             // 设置取消函数  
  44.             pOldCancelRoutine = IoSetCancelRoutine(pOldReadIrp, NULL);  
  45.   
  46.             if (pOldCancelRoutine != NULL)  
  47.             {  
  48.                 pOldReadIrp->IoStatus.Information = 0;  
  49.   
  50.                 pOldReadIrp->IoStatus.Status = STATUS_SUCCESS;  
  51.   
  52.                 pdx->pReadIrp = NULL;  
  53.             }  
  54.             else  
  55.             {  
  56.                 pOldReadIrp = NULL;  
  57.             }  
  58.   
  59.         }  
  60.         // 检查事件  
  61.         DriverCheckEvent(pdx, SERIAL_EV_RXCHAR | SERIAL_EV_RX80FULL);  
  62.   
  63. //      DriverCheckEvent(pdx, SERIAL_EV_TXEMPTY);  
  64.         // 释放自旋锁  
  65.         KeReleaseSpinLock(&pdx->WriteSpinLock, OldIrql);  
  66.   
  67.         if (pOldReadIrp != NULL)  
  68.             IoCompleteRequest(pOldReadIrp, IO_NO_INCREMENT);  
  69.     }  
  70.   
  71.     Irp->IoStatus.Status = ntStatus;  
  72.     Irp->IoStatus.Information = DataLen;  
  73.     IoCompleteRequest( Irp, IO_NO_INCREMENT );  
  74.   
  75.     return ntStatus;  
  76. }  


 

[cpp]  view plain copy
  1. VOID DriverCheckEvent(IN PDEVICE_EXTENSION pExtension, IN ULONG events)  
  2. {  
  3.     KdPrint(("DriverCheckEvent\n"));  
  4.     PIRP            pOldWaitIrp = NULL;  
  5.     PDRIVER_CANCEL  pOldCancelRoutine;  
  6.     KIRQL           OldIrql;  
  7.   
  8.     KeAcquireSpinLock(&pExtension->IoctlSpinLock, &OldIrql);  
  9.   
  10.     pExtension->HistoryEvents |= events;  
  11.   
  12.     events &= pExtension->EventMask;  
  13.   
  14.     //相当于设置触发事件  
  15.     if ((pExtension->pWaitIrp != NULL) && (events != 0))  
  16.     {  
  17.         pOldWaitIrp = pExtension->pWaitIrp;  
  18.   
  19.         pOldCancelRoutine = IoSetCancelRoutine(pOldWaitIrp, NULL);  
  20.   
  21.         //是否已经被cancel掉?  
  22.         if (pOldCancelRoutine != NULL)  
  23.         {  
  24.             // Nein, also Request beenden  
  25.             pOldWaitIrp->IoStatus.Information = sizeof(ULONG);  
  26.             *(PULONG)pOldWaitIrp->AssociatedIrp.SystemBuffer = events;  
  27.   
  28.             pOldWaitIrp->IoStatus.Status = STATUS_SUCCESS;  
  29.   
  30.             pExtension->pWaitIrp      = NULL;  
  31.             pExtension->HistoryEvents = 0;  
  32.         }  
  33.         else  
  34.         {  
  35.             //如果cancel掉,就不用IoCompleteRequest了  
  36.             pOldWaitIrp = NULL;  
  37.         }  
  38.     }  
  39.   
  40.     KeReleaseSpinLock(&pExtension->IoctlSpinLock, OldIrql);  
  41.   
  42.     if (pOldWaitIrp != NULL)  
  43.     {  
  44.         KdPrint(("complete the wait irp\n"));  
  45.         IoCompleteRequest(pOldWaitIrp, IO_NO_INCREMENT);  
  46.     }  
  47. }  


1、4  读的实现

对于虚拟串口的读工作,就变得相对简单。因为写IRP会负责通知让阻塞的线程继续运行,并且通知是何种事件的来临。串口调试软件得知SERAIL_EV_RXCHAR这个事件

,因此发起了读事件。在驱动中,就是进入读IRP的派遣函数。

在该派遣函数中,负责将存储在设备扩展中的数据通过IRP传送到应用程序。同时,还需要做一些同步处理。

[cpp]  view plain copy
  1. NTSTATUS HelloWDMRead(IN PDEVICE_OBJECT fdo,  
  2.                         IN PIRP Irp)  
  3. {  
  4.     KdPrint(("HelloWDMRead\n"));  
  5.   
  6.     NTSTATUS ntStatus = STATUS_SUCCESS;// Assume success  
  7.   
  8.     PDEVICE_EXTENSION pExtension = (PDEVICE_EXTENSION)fdo->DeviceExtension;  
  9.   
  10.     PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation( Irp );  
  11.   
  12.     ULONG BufLen = irpSp->Parameters.Read.Length;  
  13.     PCHAR pBuf = (PCHAR)Irp->AssociatedIrp.SystemBuffer;  
  14.   
  15.     KIRQL OldIrql;  
  16.   
  17.     PDRIVER_CANCEL pOldCancelRoutine;  
  18.   
  19.     Irp->IoStatus.Information = 0;  
  20.       
  21.     DbgPrint("DeviceObject:%08X Read\n",fdo);  
  22.   
  23.     if (BufLen == 0)  
  24.     {  
  25.         ntStatus = STATUS_SUCCESS;  
  26.     }  
  27.     else  
  28.     {  
  29.         KeAcquireSpinLock(&pExtension->WriteSpinLock, &OldIrql);  
  30.         // 内存复制  
  31.         RtlCopyMemory(pBuf,pExtension->Buffer,BufLen);  
  32.   
  33.         Irp->IoStatus.Information = BufLen;  
  34.   
  35.         if (BufLen==0 && pExtension->pReadIrp==NULL) // nothing, store  
  36.         {  
  37.             // 保存IRP  
  38.             pExtension->pReadIrp = Irp;  
  39.             Irp->IoStatus.Status = ntStatus = STATUS_PENDING;  
  40.             // 设置取消函数  
  41.             IoSetCancelRoutine(Irp, DriverCancelCurrentReadIrp);  
  42.   
  43.             // 重新设置取消函数  
  44.             if (Irp->Cancel)  
  45.             {  
  46.                 pOldCancelRoutine = IoSetCancelRoutine(Irp, NULL);  
  47.   
  48.                 if (pOldCancelRoutine != NULL)  
  49.                 {  
  50.                     // Nein, also IRP hier abbrechen  
  51.                     Irp->IoStatus.Status = ntStatus = STATUS_CANCELLED;  
  52.   
  53.                     pExtension->pReadIrp = NULL;  
  54.                 }  
  55.                 else  
  56.                 {  
  57.                     // 标记IRP挂起   Ja, Cancel-Routine wird Request beenden  
  58.                     IoMarkIrpPending(Irp);  
  59.                 }  
  60.             }  
  61.             else  
  62.             {  
  63.                     IoMarkIrpPending(Irp);  
  64.             }  
  65.         }  
  66.   
  67.         KeReleaseSpinLock(&pExtension->WriteSpinLock, OldIrql);  
  68.       
  69.     }  
  70.   
  71.     Irp->IoStatus.Status = ntStatus;  
  72.     if (ntStatus != STATUS_PENDING)  
  73.         IoCompleteRequest( Irp, IO_NO_INCREMENT );  
  74.   
  75.     return ntStatus;  
  76. }
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值