创建queue:
1.UMDF,调用IWDFDevice::CreateIoQueue
2.KMDF,调用WdfIoQueueCreate
驱动可以创建一至多个queue,对于每个queue的配置:
1.接收quests的types
2.power management选项
3.queue的dispatch方法(几个quest可以同时active)
4.queue是否接收0长度buffer
在queue中的请求,owner是queue,分派到驱动里时,就是in-flight request,owner是driver。驱动能通过函数把请求从一个queue转移到另一个queue。
queue配置和请求类型
驱动可以有多个queue和不同的配置,read/write/CTL/内部使用
不是所有的请求都能使用queue,一些请求需要即刻通过callback分派。
分派机制表如下:
I/O request type | UMDF delivery mechanism | KMDF delivery mechanism |
---|---|---|
Read | Queue | Queue |
Write | Queue | Queue |
Device I/O control | Queue | Queue |
Internal device I/O control | Queue | Queue |
Create | Queue | Queue or callback |
Close | Callback | Callback |
Cleanup | Callback | Callback |
为queue指定Request类型
UMDF===
KMDF:
WdfDeviceConfigureRequestDispatching,类型有:
WdfRequestTypeCreate
WdfRequestTypeRead
WdfRequestTypeWrite
WdfRequestTypeDeviceControl
WdfRequestTypeDeviceControlInternal
驱动必须在queue configuration structure中注册I/O事件回调函数
Queue回调函数:
Associated event | UMDF callback interface | KMDF callback function |
---|---|---|
Read request | IQueueCallbackRead | EvtIoRead |
Write request | IQueueCallbackWrite | EvtIoWrite |
Device I/O control request | IQueueCallbackDeviceIoControl | EvtIoDeviceIoControl |
Internal device I/O control request | Not applicable | EvtIoInternalDeviceIoControl |
Create request | IQueueCallbackCreate | EvtIoDefault |
I/O request for which no other callback is implemented | IQueueCallbackDefaultIoHandler | EvtIoDefault |
Power-managed queue stop notification | IQueueCallbackIoStop | EvtIoStop |
Power-managed queue resume notification | IQueueCallbackIoResume | EvtIoResume |
Queue state change notification | IQueueCallbackStateChange | EvtIoQueueState |
Queued request cancellation notification | Not applicable | EvtIoCanceledOnQueue |
种类:Power change,Queue state change, I/O request cancellation
默认queue
把所有其他的quest放入默认queue中。驱动可以没有default queue
Queue和电源管理
Power-managed queue
========
Non-Power-Managed Queue
若请求到达且设备在sleep时,WDF不会启动设备
只有Power-managed queue才会影响idle timer。驱动能调用WdfDeviceStopIdle影响idle状态
Driver可以实现I/O stop回调函数EvtIoStop,只在设备移除时调用
queue的分派类型:
顺序:直到完成请求或请求被转到另一个queue时,才push出下一个请求。
并行:queue尽快push I/O 请求
手动:由driver调用获取方法
driver不必像WDM driver一样把请求标记为pending
分派类型只是说明同一时间活动的请求数,而并发性由同步范围确定:并行queue的请求也可能是不并行的(不过驱动里会有很多in-flight请求)
不用标记请求为pending和callback不用返回status的原因:
在callback时,framework总是标记IRP为pending,并返回STATUS_PENDING
Queue控制
驱动可以调用方法停止、开始、消耗和清空,确定queue状态:
UMDF使用IWDFIoQueue方法
KMDF使用WdfIoQueueXxx方法
对于消耗和清空方法,WDF提供了同步和非同步的方法。
Operation | UMDF method in IWDFIoQueue interface | KMDF method |
---|---|---|
Stops adding requests to the queue. Optionally notifies the driver or returns control to the driver after all pending I/O requests have been completed. | Drain DrainSynchronously | WdfIoQueueDrain WdfIoQueueDrainSynchronously |
Returns the state of the I/O queue. | GetState | WdfIoQueueGetState |
Stops adding requests to the queue. Cancels all requests that are already in the queue and all in-flight requests that are in a cancelable state. Notifies the driver or returns control to the driver only after all pending I/O requests have been completed. | Purge PurgeSynchronously | WdfIoQueuePurge WdfIoQueuePurgeSynchronously |
Resumes delivery of requests from the queue. | Start | WdfIoQueueStart |
Stops delivery of requests from the queue but continues to add new requests to the queue. | Stop StopSynchronously | WdfIoQueueStop WdfIoQueueStopSynchronously |
驱动可以把控制方法和self-managed I/O回调结合使用来手动控制non-power-managed queue的状态。
UMDF例子
================
KMDF例子
KMDF创建和配置一个I/O queue步骤:
1.定义WDF_IO_QUEUE_CONFIG structure
2.使用WDF_IO_QUEUE_CONFIG_INIT或WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE初始化configuration
3. 在WDF_IO_QUEUE_CONFIG structure设置事件回调函数,设置queue分派类型。
4.在CONFIG中设置是否电源管理和是否允许0长度请求
5.通过WdfIoQueueCreate创建queue,(WDFDEVICIE指针,config,attr,接受queue的指针)
6.WdfDeviceConfigureRequestDispatching来声明queue接受的I/O请求类型
Osrusbfx2例子:Device.c的OsrFxDeviceAdd方法
1.IOCTL:并行默认queue
2.read和write两个queue
代码例子:KMDF默认queue,KMDF非默认queue的创建方法
从Manual queue中获取请求
UMDF:IWDFIoQueue
KMDF:WdfIoQueueXxx
方法列表:
Operation | UMDF method | KMDF method |
---|---|---|
Retrieve the next request from the queue. | IWDFIoQueue:: RetrieveNextRequest | WdfIoQueueRetrieveNextRequest |
Retrieve the next request for a particular file object. | IWDFIoQueue::RetrieveNextRequestByFileObject | WdfIoQueueRetrieveRequestByFileObject |
Search the queue for a particular request and then retrieve that request. | None | WdfIoQueueFindRequest followed by WdfIoQueueRetrieveFoundRequest |
默认,queue使用FIFO机制
驱动可以在请求到达queue是获得通知:
UMDF:==
KMDF:调用WdfIoQueueReadyNotify,并传递EvtIoQueueState方法的指针,framework会在请求到达时调用EvtIoQueueState方法
[KMDF]
WdfIoQueueFindRequest:检查传入的request是否在queue中
WdfIoQueueRetrieveFoundRequest:把找到的request dequeue
代码例子:Pcidrv sample,Pcidrv\sys\hw\nic_req.c:在manual device I/O CTL
queue中获得特定function code的请求。
NTSTATUS NICGetIoctlRequest( IN WDFQUEUE Queue, IN ULONG FunctionCode, OUT WDFREQUEST* Request ) { NTSTATUS status = STATUS_UNSUCCESSFUL; WDF_REQUEST_PARAMETERS params; WDFREQUEST tagRequest; WDFREQUEST prevTagRequest; WDF_REQUEST_PARAMETERS_INIT(¶ms); *Request = NULL; prevTagRequest = tagRequest = NULL; do { WDF_REQUEST_PARAMETERS_INIT(¶ms); status = WdfIoQueueFindRequest(Queue, prevTagRequest, NULL, ¶ms, &tagRequest); // WdfIoQueueFindRequest takes an extra reference on tagRequest to prevent // the memory from being freed. However, tagRequest is still in the queue // and can be cancelled or removed by another thread and completed. if(prevTagRequest) { WdfObjectDereference(prevTagRequest); } if(status == STATUS_NO_MORE_ENTRIES) { status = STATUS_UNSUCCESSFUL; break; } if(status == STATUS_NOT_FOUND) { // The prevTagRequest disappeared from the queue. Either it was // cancelled or dispatched to the driver. Other requests might match // our criteria so restart the search. prevTagRequest = tagRequest = NULL; continue; } if( !NT_SUCCESS(status)) { status = STATUS_UNSUCCESSFUL; break; } if(FunctionCode == params.Parameters.DeviceIoControl.IoControlCode){ status = WdfIoQueueRetrieveFoundRequest(Queue, tagRequest, Request); WdfObjectDereference(tagRequest); if(status == STATUS_NOT_FOUND) { // The prevTagRequest disappeared from the queue. // Restart the search. prevTagRequest = tagRequest = NULL; continue; } if( !NT_SUCCESS(status)) { status = STATUS_UNSUCCESSFUL; break; } // Found a request. Drop the extra reference before returning. ASSERT(*Request == tagRequest); status = STATUS_SUCCESS; break; } else { // This is not the request we need. Drop the reference // on the tagRequest after looking for the next request. prevTagRequest = tagRequest; continue; } } while (TRUE); return status; }表8-7