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數據結構表示。

  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。

  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表示。

  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數據

結構表示:

  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命令,這個命令主要是設置串口驅動的某些事件發生時,需要嚮應用程序通知,這些事件包括以下幾種事件:

  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  
  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設置的事件中的一項發生時,阻塞狀態改爲完成,並通知應用程序是哪種事件發生了。

  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.         }  

 

  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。

  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. }  


 

  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傳送到應用程序。同時,還需要做一些同步處理。

  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. }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章