什么时候需要取消?
如果驱动调用者(caller)向驱动程序发送Request,需要从硬件读取数据,而读取完成不是一个同步操作时,那么该Request必须是可以被取消的。比如数据传输完成以中断方式通知时。
取消流程
- Request Handlers调用WdfRequestMarkCancelableor WdfRequestMarkCancelableEx注册一个取消例程,该例程将在IRQL: <=DISPATCH_LEVEL被Framework调用。另外在Device Context里保存Request
- 当caller取消该Request时,就会调用取消例程,在取消历程内部调用WdfRequestComplete,一般以STATUS_CANCELLED方式完成Request 。另外把Device Context里的Request设置为NULL
- 当取消例程得到调用时,IRQL: <=DISPATCH_LEVEL。也就是说ISR和DPC可以打断取消例程。
- 如果取消例程完成了Request,并且DeviceContext->InProcessRequest=NULL都已执行,DPC只要判断DeviceContext->InProcessRequest是否为NULL,如果是NULL就不用完成Request。
- 如果取消例程完成了Request,并且DeviceContext->InProcessRequest=NULL没有执行,DPC将得到DeviceContext->InProcessRequest,而该Request已经被取消,DPC调用WdfRequestUnmarkCancelable将返回STATUS_CANCELLED,此时不要完成Request
- 如果取消例程没有完成Request,DPC调用WdfRequestUnmarkCancelable将返回STATUS_CANCELLED,表示该Request将要被取消,此时也不要完成Request.
- DPC是不能被取消例程打断的,所以取消例程只要完成就可以了
- 取消例程必须把InProcessRequest设置为NULL,否则DPC中的WdfRequestUnmarkCancelable会发生BugCheck
DMA取消
如果驱动程序中包含DMA操作,那么取消工作要复杂一些
- 如果DMA基于CommBuffer,DPC从WdfRequestUnmarkCancelable得到STATUS_CANCELLED状态就不用拷贝数据到虚拟Buffer了
- 如果DMA基于Packet,必须硬件支持DMA取消(比如写寄存器,或者复位),因为DMA控制器无法知道目的地址是否还是有效的。取消DMA在取消例程中做就行了,这也就是为什么Framework提供这么一个取消例程的原因。如果不是这个原因,取消例程完全可以做到Framework里。