/*++ Copyright (c) Microsoft Corporation Module Name: FxIoQueueApi.cpp Abstract: This module implements the FxIoQueue object C interfaces Author: Revision History: --*/ #include "ioprivshared.hpp" #include "fxpkgio.hpp" #include "fxioqueue.hpp" extern "C" { // #include "FxIoQueueApi.tmh" } // // C Accessor methods // // These are the "public" API's used by driver writers for both // C and C++. In the C++ case, a "wrapper" class is placed around these // methods. This is to avoid exposing any internal details of our // driver frameworks implementation class, which would cause // binary coupling with the device driver. // // (thus causing drivers to rebuild for even small changes to this C++ object) // // // extern all functions // extern "C" { _Must_inspect_result_ __drv_maxIRQL(DISPATCH_LEVEL) NTSTATUS STDCALL WDFEXPORT(WdfIoQueueCreate)( __in PWDF_DRIVER_GLOBALS DriverGlobals, __in WDFDEVICE Device, __in PWDF_IO_QUEUE_CONFIG Config, __in_opt PWDF_OBJECT_ATTRIBUTES QueueAttributes, __out_opt WDFQUEUE* Queue ) /*++ Routine Description: Creates an IoQueue object and returns its handle to the caller. The newly created IoQueue object is associated with the IoPackage instance for the device. The IoQueue object is automatically dereferenced when its associated device is removed. This driver does not normally have to manually manage IoQueue object reference counts. An IoQueue object is created in the WdfIoQueuePause state, and is configured for WdfIoQueueDispatchSynchronous; Arguments: Device - Handle to the Device the I/O Package registered with at EvtDeviceFileCreate time. Config - WDF_IO_QUEUE_CONFIG structure pQueue - Pointer to location to store the returned IoQueue handle. Return Value: NTSTATUS --*/ { DDI_ENTRY(); PFX_DRIVER_GLOBALS pFxDriverGlobals; CfxDevice* pDevice; FxPkgIo* pPkgIo; FxIoQueue* pQueue; NTSTATUS status; // // Validate the I/O Package handle, and get the FxPkgIo* // FxObjectHandleGetPtrAndGlobals(GetFxDriverGlobals(DriverGlobals), Device, FX_TYPE_DEVICE, (PVOID*)&pDevice, &pFxDriverGlobals); pPkgIo = NULL; pQueue = NULL; FxPointerNotNull(pFxDriverGlobals, Config); status = FxValidateObjectAttributes(pFxDriverGlobals, QueueAttributes, (FX_VALIDATE_OPTION_EXECUTION_LEVEL_ALLOWED | FX_VALIDATE_OPTION_SYNCHRONIZATION_SCOPE_ALLOWED)); if (!NT_SUCCESS(status)) { return status; } // Validate Config structure if (Config->Size != sizeof(WDF_IO_QUEUE_CONFIG) && Config->Size != sizeof(WDF_IO_QUEUE_CONFIG_V1_9) && Config->Size != sizeof(WDF_IO_QUEUE_CONFIG_V1_7)) { status = STATUS_INFO_LENGTH_MISMATCH; DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO, "WDF_IO_QUEUE_CONFIG Size 0x%x, " "expected for v1.7 size 0x%x or v1.9 size 0x%x or " "current version size 0x%x, %!STATUS!", Config->Size, sizeof(WDF_IO_QUEUE_CONFIG_V1_7), sizeof(WDF_IO_QUEUE_CONFIG_V1_9), sizeof(WDF_IO_QUEUE_CONFIG), status); return status; } // // If the queue is not a default queue then the out parameter is not optional // if(!Config->DefaultQueue && Queue == NULL) { status = STATUS_INVALID_PARAMETER_4; DoTraceLevelMessage( pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO, "Parameter to receive WDFQUEUE handle is not optional " "for non default queue %!STATUS!", status); return status; } //pPkgIo = (FxPkgIo*)pDevice->m_PkgIo; pPkgIo = pDevice->m_PkgIo; // // If the queue is a default queue, then we restrict the creation to happen // a) before the device is started for pnp devices (FDO or PDO) // b) before the call to WdfControlDeviceFinishInitializing for controldevices. // This is done to prevent some unknown race conditions and reduce // the test matrix. // if(Config->DefaultQueue) { if(pDevice->IsLegacy()) { // // This is a controldevice. Make sure the create is called after the device // is initialized and ready to accept I/O. // if((pDevice->GetDeviceObjectFlags() & DO_DEVICE_INITIALIZING) == FALSE) { DoTraceLevelMessage( pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP, "Default queue can only be created before WdfControlDeviceFinishInitializing" "on the WDFDEVICE %p is called %!STATUS!", Device, STATUS_INVALID_DEVICE_STATE); return STATUS_INVALID_DEVICE_STATE; } } else { // // This is either FDO or PDO. Make sure it's not started yet. // if (pDevice->GetDevicePnpState() != WdfDevStatePnpInit) { status = STATUS_INVALID_DEVICE_STATE; DoTraceLevelMessage( pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP, "Default queue can only be created before the WDFDEVICE 0x%p " "is started, %!STATUS!", Device, status); return status; } } } // // Create the Queue for the I/O package // status = pPkgIo->CreateQueue(Config, QueueAttributes, GetFxDriverGlobals(DriverGlobals)->Driver, &pQueue); if (!NT_SUCCESS(status)) { DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO, "Queue Creation failed " "for WDFDEVICE 0x%p, %!STATUS!", Device, status); return status; } if(Config->DefaultQueue) { // // Make this a default queue. The default queue receives any // I/O requests that have not been otherwise forwarded to another // queue by WdfDeviceConfigureRequestDispatching . // status = pPkgIo->InitializeDefaultQueue( pDevice, pQueue ); if (!NT_SUCCESS(status)) { DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO, "Create failed " "for FxPkgIo 0x%p, WDFDEVICE 0x%p",pPkgIo, Device); // // Delete the queue *without* invoking driver defined callbacks // pQueue->DeleteFromFailedCreate(); return status; } } DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIO, "Created WDFQUEUE 0x%p", pQueue->GetObjectHandle()); if(Queue != NULL) { *Queue = (WDFQUEUE)pQueue->GetObjectHandle(); } return STATUS_SUCCESS; } __drv_maxIRQL(DISPATCH_LEVEL) WDF_IO_QUEUE_STATE STDCALL WDFEXPORT(WdfIoQueueGetState)( __in PWDF_DRIVER_GLOBALS DriverGlobals, __in WDFQUEUE Queue, __out_opt PULONG QueueCount, __out_opt PULONG DriverCount ) /*++ Routine Description: Return the Queues status Arguments: Queue - Handle to Queue object pQueueCount - Count of requests in the Queue not presented to the driver. pDriverCount - Count of requests the driver is operating on that are associated with this queue. Returns: WDF_IO_QUEUE_STATE --*/ { DDI_ENTRY(); FxIoQueue* pQueue; FxObjectHandleGetPtr(GetFxDriverGlobals(DriverGlobals), Queue, FX_TYPE_QUEUE, (PVOID*)&pQueue); return pQueue->GetState(QueueCount, DriverCount); } __drv_maxIRQL(DISPATCH_LEVEL) WDFDEVICE STDCALL WDFEXPORT(WdfIoQueueGetDevice)( __in PWDF_DRIVER_GLOBALS DriverGlobals, __in WDFQUEUE Queue ) /*++ Routine Description: Returns the device handle that the queue is associated with Arguments: Queue - Handle to queue object Return Value: WDFDEVICE handle --*/ { DDI_ENTRY(); FxIoQueue* pQueue; FxObjectHandleGetPtr(GetFxDriverGlobals(DriverGlobals), Queue, FX_TYPE_QUEUE, (PVOID*) &pQueue); return (WDFDEVICE) pQueue->GetDevice()->GetHandle(); } __drv_maxIRQL(DISPATCH_LEVEL) VOID STDCALL WDFEXPORT(WdfIoQueueStart)( __in PWDF_DRIVER_GLOBALS DriverGlobals, __in WDFQUEUE Queue ) /*++ Routine Description: Set the Queues state to start accepting and dispatching new requests. Arguments: Queue - Handle to Queue object A Queue may not go into a specific state right away, since it may have to wait for requests to be completed or cancelled. This is reflected in the status returned by WdfIoQueueGetState. See the Queue event API's for asynchronous notification of specific status states. Returns: NTSTATUS --*/ { DDI_ENTRY(); FxIoQueue* pQueue; FxObjectHandleGetPtr(GetFxDriverGlobals(DriverGlobals), Queue, FX_TYPE_QUEUE, (PVOID*)&pQueue); pQueue->QueueStart(); return; } __drv_maxIRQL(DISPATCH_LEVEL) VOID STDCALL WDFEXPORT(WdfIoQueueStop)( __in PWDF_DRIVER_GLOBALS DriverGlobals, __in WDFQUEUE Queue, __drv_when(Context != 0, __in) __drv_when(Context == 0, __in_opt) PFN_WDF_IO_QUEUE_STATE StopComplete, __drv_when(StopComplete != 0, __in) __drv_when(StopComplete == 0, __in_opt) WDFCONTEXT Context ) /*++ Routine Description: Set the Queue state to accept and queue (not dispatch) incoming new requests. Arguments: Queue - Handle to Queue object A Queue may not go into a specific state right away, since it may have to wait for requests to be completed or cancelled. This is reflected in the status returned by WdfIoQueueGetState. See the Queue event API's for asynchronous notification of specific status states. Returns: --*/ { DDI_ENTRY(); FxIoQueue* pQueue; NTSTATUS status; FxObjectHandleGetPtr(GetFxDriverGlobals(DriverGlobals), Queue, FX_TYPE_QUEUE, (PVOID*)&pQueue); status = pQueue->QueueIdle(FALSE, StopComplete, Context); if (!NT_SUCCESS(status)) { pQueue->FatalError(status); return; } return; } __drv_maxIRQL(PASSIVE_LEVEL) VOID STDCALL WDFEXPORT(WdfIoQueueStopSynchronously)( __in PWDF_DRIVER_GLOBALS DriverGlobals, __in WDFQUEUE Queue ) /*++ Routine Description: Set the Queue state to accept and queue (not dispatch) incoming new requests and wait for 1) all the dispatch callbacks to return and 2) all the driver-owned request to complete. Arguments: Queue - Handle to Queue object Returns: NTSTATUS --*/ { DDI_ENTRY(); PFX_DRIVER_GLOBALS pFxDriverGlobals; FxIoQueue* pQueue; NTSTATUS status; FxObjectHandleGetPtrAndGlobals(GetFxDriverGlobals(DriverGlobals), Queue, FX_TYPE_QUEUE, (PVOID*)&pQueue, &pFxDriverGlobals); status = FxVerifierCheckIrqlLevel(pFxDriverGlobals, PASSIVE_LEVEL); if (!NT_SUCCESS(status)) { return; } status = pQueue->QueueIdleSynchronously(FALSE); if (!NT_SUCCESS(status)) { pQueue->FatalError(status); return; } } __drv_maxIRQL(DISPATCH_LEVEL) VOID STDCALL WDFEXPORT(WdfIoQueueStopAndPurge)( __in PWDF_DRIVER_GLOBALS DriverGlobals, __in WDFQUEUE Queue, __drv_when(Context != 0, __in) __drv_when(Context == 0, __in_opt) PFN_WDF_IO_QUEUE_STATE StopAndPurgeComplete, __drv_when(StopAndPurgeComplete != 0, __in) __drv_when(StopAndPurgeComplete == 0, __in_opt) WDFCONTEXT Context ) /*++ Routine Description: This function does the following: - sets the queue state to accept and queues (not dispatch) incoming new requests. - cancels all current (at the time this function is called) queued requests. - invokes, if present, the cancel callback of cancellable driver requests. - Asynchronously if complete callback is specified, it waits until 1) all the dispatch callbacks to return and 2) all the driver-owned request to complete. Arguments: Queue - Handle to Queue object A Queue may not go into a specific state right away, since it may have to wait for requests to be completed or cancelled. This is reflected in the status returned by WdfIoQueueGetState. See the Queue event API's for asynchronous notification of specific status states. Returns: --*/ { DDI_ENTRY(); FxIoQueue* queue; NTSTATUS status; FxObjectHandleGetPtr(GetFxDriverGlobals(DriverGlobals), Queue, FX_TYPE_QUEUE, (PVOID*)&queue); status = queue->QueueIdle(TRUE, StopAndPurgeComplete, Context); if (!NT_SUCCESS(status)) { queue->FatalError(status); return; } return; } __drv_maxIRQL(PASSIVE_LEVEL) VOID STDCALL WDFEXPORT(WdfIoQueueStopAndPurgeSynchronously)( __in PWDF_DRIVER_GLOBALS DriverGlobals, __in WDFQUEUE Queue ) /*++ Routine Description: This function does the following: - sets the queue state to accept and queues (not dispatch) incoming new requests. - cancels all current (at the time this function is called) queued requests. - invokes, if present, the cancel callback of cancellable driver requests. - before returning it waits until 1) all the dispatch callbacks to return and 2) all the driver-owned request to complete. Arguments: Queue - Handle to Queue object Returns: NTSTATUS --*/ { DDI_ENTRY(); PFX_DRIVER_GLOBALS fxDriverGlobals; FxIoQueue* queue; NTSTATUS status; FxObjectHandleGetPtrAndGlobals(GetFxDriverGlobals(DriverGlobals), Queue, FX_TYPE_QUEUE, (PVOID*)&queue, &fxDriverGlobals); status = FxVerifierCheckIrqlLevel(fxDriverGlobals, PASSIVE_LEVEL); if (!NT_SUCCESS(status)) { return; } status = queue->QueueIdleSynchronously(TRUE); if (!NT_SUCCESS(status)) { queue->FatalError(status); return; } } _Must_inspect_result_ __drv_maxIRQL(DISPATCH_LEVEL) NTSTATUS STDCALL WDFEXPORT(WdfIoQueueRetrieveNextRequest)( __in PWDF_DRIVER_GLOBALS DriverGlobals, __in WDFQUEUE Queue, __out WDFREQUEST *OutRequest ) /*++ WdfIoQueueRetrieveNextRequest: Routine Description: Returns a request from the head of the queue. On successful return the driver owns the request, and must eventually call WdfRequestComplete. The driver does not need to release any extra reference counts, other than calling WdfRequestComplete. Arguments: Queue - Queue handle pOutRequest - Pointer to location to return new request handle Returns: STATUS_NO_MORE_ENTRIES - The queue is empty STATUS_SUCCESS - A request was returned in pOutRequest --*/ { DDI_ENTRY(); FxIoQueue* pQueue; FxRequest* pOutputRequest = NULL; NTSTATUS status; FxObjectHandleGetPtr(GetFxDriverGlobals(DriverGlobals), Queue, FX_TYPE_QUEUE, (PVOID*)&pQueue); FxPointerNotNull(pQueue->GetDriverGlobals(), OutRequest); status = pQueue->GetRequest(NULL, NULL, &pOutputRequest); if (NT_SUCCESS(status)) { *OutRequest = (WDFREQUEST)pOutputRequest->GetObjectHandle(); } else { *OutRequest = NULL; ASSERT(status != STATUS_NOT_FOUND); } return status; } _Must_inspect_result_ __drv_maxIRQL(DISPATCH_LEVEL) NTSTATUS STDCALL WDFEXPORT(WdfIoQueueRetrieveRequestByFileObject)( __in PWDF_DRIVER_GLOBALS DriverGlobals, __in WDFQUEUE Queue, __in WDFFILEOBJECT FileObject, __out WDFREQUEST *OutRequest ) /*++ WdfIoQueueRetrieveNextRequest: Routine Description: Returns a request from the queue that matches the fileobject. Requests are dequeued in a first in, first out manner. If there are no requests that match the selection criteria, STATUS_NO_MORE_ENTRIES is returned. On successful return the driver owns the request, and must eventually call WdfRequestComplete. The driver does not need to release any extra reference counts, other than calling WdfRequestComplete. Arguments: Queue - Queue handle FileObject - FileObject to match in the request pOutRequest - Pointer to location to return new request handle Returns: STATUS_NO_MORE_ENTRIES - The queue is empty, or no more requests match the selection criteria of TagRequest and FileObject specified above. STATUS_SUCCESS - A request context was returned in pOutRequest --*/ { DDI_ENTRY(); FxIoQueue* pQueue; FxRequest* pOutputRequest = NULL; FxFileObject* pFO = NULL; MdFileObject pWdmFO = NULL; NTSTATUS status; PFX_DRIVER_GLOBALS pFxDriverGlobals; FxObjectHandleGetPtrAndGlobals(GetFxDriverGlobals(DriverGlobals), Queue, FX_TYPE_QUEUE, (PVOID*)&pQueue, &pFxDriverGlobals); FxPointerNotNull(pFxDriverGlobals, OutRequest); FxObjectHandleGetPtr(pFxDriverGlobals, FileObject, FX_TYPE_FILEOBJECT, (PVOID*)&pFO); // Get the real WDM fileobject pWdmFO = pFO->GetWdmFileObject(); status = pQueue->GetRequest(pWdmFO, NULL, &pOutputRequest); if (NT_SUCCESS(status)) { // Copy out the handle to the new request *OutRequest = (WDFREQUEST)pOutputRequest->GetObjectHandle(); } else { *OutRequest = NULL; ASSERT(status != STATUS_NOT_FOUND); } return status; } _Must_inspect_result_ __drv_maxIRQL(DISPATCH_LEVEL) NTSTATUS STDCALL WDFEXPORT(WdfIoQueueRetrieveFoundRequest)( __in PWDF_DRIVER_GLOBALS DriverGlobals, __in WDFQUEUE Queue, __in WDFREQUEST TagRequest, __out WDFREQUEST * OutRequest ) /*++ WdfIoQueueRetrieveNextRequest: Routine Description: Returns a request from the queue. Requests are dequeued in a first in, first out manner. The queue is searched for specific peeked request, and if it is not found, STATUS_NOT_FOUND is returned. A TagRequest value is returned from WdfIoQueueFindRequest(). If there are no requests that match the selection criteria, STATUS_NO_MORE_ENTRIES is returned. On successful return the driver owns the request, and must eventually call WdfRequestComplete. The driver does not need to release any extra reference counts, other than calling WdfRequestComplete. Arguments: Queue - Queue handle TagRequest - Request to look for in queue pOutRequest - Pointer to location to return new request handle Returns: STATUS_NOT_FOUND - TagContext was specified, but not found in the queue. This could be because the request was cancelled, or is part of an active queue and the request was passed to the driver or forwarded to another queue. STATUS_NO_MORE_ENTRIES - The queue is empty, or no more requests match the selection criteria of TagRequest specified above. STATUS_SUCCESS - A request context was returned in pOutRequest --*/ { DDI_ENTRY(); FxIoQueue* pQueue; FxRequest* pTagRequest = NULL; FxRequest* pOutputRequest = NULL; NTSTATUS status; PFX_DRIVER_GLOBALS pFxDriverGlobals; FxObjectHandleGetPtrAndGlobals(GetFxDriverGlobals(DriverGlobals), Queue, FX_TYPE_QUEUE, (PVOID*)&pQueue, &pFxDriverGlobals); FxPointerNotNull(pFxDriverGlobals, OutRequest); FxObjectHandleGetPtr(pFxDriverGlobals, TagRequest, FX_TYPE_REQUEST, (PVOID*)&pTagRequest); status = pQueue->GetRequest(NULL, pTagRequest, &pOutputRequest); if (NT_SUCCESS(status)) { // Copy out the handle to the new request *OutRequest = (WDFREQUEST)pOutputRequest->GetObjectHandle(); } else { *OutRequest = NULL; } return status; } _Must_inspect_result_ __drv_maxIRQL(DISPATCH_LEVEL) NTSTATUS STDCALL WDFEXPORT(WdfIoQueueFindRequest)( __in PWDF_DRIVER_GLOBALS DriverGlobals, __in WDFQUEUE Queue, __in_opt WDFREQUEST TagRequest, __in_opt WDFFILEOBJECT FileObject, __inout_opt PWDF_REQUEST_PARAMETERS Parameters, __out WDFREQUEST * OutRequest ) /*++ WdfIoQueueFindRequest: Routine Description: PeekRequest allows a caller to enumerate through requests in a queue, optionally only returning requests that match a specified FileObject. The first call specifies TagContext == NULL, and the first request in the queue that matches the FileObject is returned. Subsequent requests specify the previous request value as the TagContext, and searching will continue at the request that follows. If the queue is empty, there are no requests after TagContext, or no requests match the FileObject, NULL is returned. If FileObject == NULL, this matches any FileObject in a request. If a WDF_REQUEST_PARAMETERS structure is supplied, the information from the request is returned to allow the driver to further examine the request to decide whether to service it. If a TagRequest is specified, and it is not found, the return status STATUS_NOT_FOUND means that the queue should be re-scanned. This is because the TagRequest was cancelled from the queue, or if the queue was active, delivered to the driver. There may still be un-examined requests on the queue that match the drivers search criteria, but the search marker has been lost. Re-scanning the queue starting with TagRequest == NULL and continuing until STATUS_NO_MORE_ENTRIES is returned will ensure all requests have been examined. Enumerating an active queue with this API could result in the driver frequently having to re-scan. If a successful return of a Request object handle occurs, the driver *must* call WdfObjectDereference when done with it, otherwise an object leak will result. Returned request objects are not owned by the driver, and may not be used by I/O or completed. The only valid operations are WdfRequestGetParameters, passing it as the TagRequest parameter to WdfIoQueueFindRequest, WdfIoQueueRetrieveFoundRequest, or calling WdfObjectDereference. All other actions are undefined. The request could be cancelled without a EvtIoCancel callback while the driver has the handle. The request then becomes invalid, and WdfObjectDereference must be called to release its resources. The caller should not use any buffer pointers in the returned parameters structure until it successfully receives ownership of the request by calling WdfIoQueueRetrieveFoundRequest with the request tag. This is because the I/O can be cancelled and completed at any time without the driver being notified until it has received ownership, thus invalidating the buffer pointers and releasing the memory, even if the request object itself it still valid due to the reference. The driver should hold the reference on the object until after a call to WdfIoQueueFindRequest using it as the TagRequest parameter, or WdfIoQueueRetrieveFoundtRequest. Otherwise, a race could result in which the object is cancelled, its memory re-used for a new request, and then an attempt to use it as a tag would result in a different state than intended. The driver verifier will catch this, but it is a rare race. Calling this API on an operating Queue can lead to confusing results. This is because the request could be presented to EvtIoDefault, causing it to be invalid as a TagRequest in calls to WdfIoQueueFindRequest, and WdfIoQueueRetrieveFoundRequest. The extra reference taken on the object by its return from this call must still be released by WdfObjectDereference. The intention of the API is to only hold onto the TagRequest long enough for the driver to make a decision whether to service the request, or to continue searching for requests. Arguments: Queue - Queue handle TagRequest - If !NULL, request to begin search at FileObject - If !NULL, FileObject to match in the request Parameters - If !NULL, pointer to buffer to return the requests parameters to aid in selection. pOutRequest - Pointer to location to return request handle Returns: STATUS_NOT_FOUND - TagContext was specified, but not found in the queue. This could be because the request was cancelled, or is part of an active queue and the request was passed to the driver or forwarded to another queue. STATUS_NO_MORE_ENTRIES - The queue is empty, or no more requests match the selection criteria of TagRequest and FileObject specified above. STATUS_SUCCESS - A request context was returned in pOutRequest --*/ { DDI_ENTRY(); FxIoQueue* pQueue; FxRequest* pTagRequest = NULL; FxRequest* pOutputRequest = NULL; FxFileObject* pFO = NULL; MdFileObject pWdmFO = NULL; NTSTATUS status; PFX_DRIVER_GLOBALS pFxDriverGlobals; FxObjectHandleGetPtrAndGlobals(GetFxDriverGlobals(DriverGlobals), Queue, FX_TYPE_QUEUE, (PVOID*)&pQueue, &pFxDriverGlobals); FxPointerNotNull(pFxDriverGlobals, OutRequest); // // Validate tag request handle if supplied // if (TagRequest != NULL) { FxObjectHandleGetPtr(pFxDriverGlobals, TagRequest, FX_TYPE_REQUEST, (PVOID*)&pTagRequest); } // // If present, validate the FileObject object handle, // and get its FxFileObject* // if (FileObject != NULL) { FxObjectHandleGetPtr(pFxDriverGlobals, FileObject, FX_TYPE_FILEOBJECT, (PVOID*)&pFO); // // Get the real WDM fileobject // pWdmFO = pFO->GetWdmFileObject(); } // // If a parameters buffer is supplied, validate its length // if ((Parameters != NULL) && (Parameters->Size < sizeof(WDF_REQUEST_PARAMETERS))) { status = STATUS_INVALID_PARAMETER_4; DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO, "Invalid WDF_REQUEST_PARAMETERS size %d %!STATUS!", Parameters->Size, status); return status; } status = pQueue->PeekRequest(pTagRequest, pWdmFO, Parameters, &pOutputRequest); if (NT_SUCCESS(status)) { // // Copy out the handle to the new request // *OutRequest = (WDFREQUEST)pOutputRequest->GetObjectHandle(); } else { *OutRequest = NULL; } return status; } __drv_maxIRQL(DISPATCH_LEVEL) VOID STDCALL WDFEXPORT(WdfIoQueueDrain)( __in PWDF_DRIVER_GLOBALS DriverGlobals, __in WDFQUEUE Queue, __drv_when(Context != 0, __in) __drv_when(Context == 0, __in_opt) PFN_WDF_IO_QUEUE_STATE DrainComplete, __drv_when(DrainComplete != 0, __in) __drv_when(DrainComplete == 0, __in_opt) WDFCONTEXT Context ) /*++ Set the Queue to reject new requests, with newly arriving requests being completed with STATUS_CANCELLED. If the optional DrainComplete callback is specified, the callback will be invoked with the supplied context when there are no requests owned by the driver and no request pending in the queue. Only one callback registration for WdfIoQueueIdle, WdfIoQueuePurge, or WdfIoQueueDrain may be outstanding at a time. --*/ { DDI_ENTRY(); FxIoQueue* pQueue; NTSTATUS status; FxObjectHandleGetPtr(GetFxDriverGlobals(DriverGlobals), Queue, FX_TYPE_QUEUE, (PVOID*)&pQueue); status = pQueue->QueueDrain(DrainComplete, Context); if (!NT_SUCCESS(status)) { pQueue->FatalError(status); return; } return; } __drv_maxIRQL(PASSIVE_LEVEL) VOID STDCALL WDFEXPORT(WdfIoQueueDrainSynchronously)( __in PWDF_DRIVER_GLOBALS DriverGlobals, __in WDFQUEUE Queue ) /*++ Set the Queue to fail new request with STATUS_CANCELLED, dispatches pending requests, and waits for the all the pending and inflight requests to complete before returning. Should be called at PASSIVE_LEVEL. --*/ { DDI_ENTRY(); PFX_DRIVER_GLOBALS pFxDriverGlobals; FxIoQueue* pQueue; NTSTATUS status; FxObjectHandleGetPtrAndGlobals(GetFxDriverGlobals(DriverGlobals), Queue, FX_TYPE_QUEUE, (PVOID*)&pQueue, &pFxDriverGlobals); status = FxVerifierCheckIrqlLevel(pFxDriverGlobals, PASSIVE_LEVEL); if (!NT_SUCCESS(status)) { return; } status = pQueue->QueueDrainSynchronously(); if (!NT_SUCCESS(status)) { pQueue->FatalError(status); return; } } __drv_maxIRQL(PASSIVE_LEVEL) VOID STDCALL WDFEXPORT(WdfIoQueuePurgeSynchronously)( __in PWDF_DRIVER_GLOBALS DriverGlobals, __in WDFQUEUE Queue ) /*++ Sets the queue state to fail incoming requests, cancels all the pending requests, cancels in-flights requests (if they are marked cancelable), and waits for all the requests to complete before returning. Should be called at PASSIVE_LEVEL. --*/ { DDI_ENTRY(); PFX_DRIVER_GLOBALS pFxDriverGlobals; FxIoQueue* pQueue; NTSTATUS status; FxObjectHandleGetPtrAndGlobals(GetFxDriverGlobals(DriverGlobals), Queue, FX_TYPE_QUEUE, (PVOID*)&pQueue, &pFxDriverGlobals); status = FxVerifierCheckIrqlLevel(pFxDriverGlobals, PASSIVE_LEVEL); if (!NT_SUCCESS(status)) { return; } status = pQueue->QueuePurgeSynchronously(); if (!NT_SUCCESS(status)) { pQueue->FatalError(status); return; } return; } __drv_maxIRQL(DISPATCH_LEVEL) VOID STDCALL WDFEXPORT(WdfIoQueuePurge)( __in PWDF_DRIVER_GLOBALS DriverGlobals, __in WDFQUEUE Queue, __drv_when(Context != 0, __in) __drv_when(Context == 0, __in_opt) PFN_WDF_IO_QUEUE_STATE PurgeComplete, __drv_when(PurgeComplete != 0, __in) __drv_when(PurgeComplete == 0, __in_opt) WDFCONTEXT Context ) /*++ Set the Queue to reject new requests, with newly arriving requests being completed with STATUS_CANCELLED. A Queue may not purge immediately, since the device driver could be operating on non-cancelable requests. If the optional PurgeComplete callback is specified, the callback will be invoked with the supplied context when the Queue is in the WDF_IO_QUEUE_PURGED state. (Reject new requests, no current requests in Queue or device driver) Only one callback registration for WdfIoQueueIdle, WdfIoQueuePurge, or WdfIoQueueDrain may be outstanding at a time. --*/ { DDI_ENTRY(); FxIoQueue* pQueue; NTSTATUS status; FxObjectHandleGetPtr(GetFxDriverGlobals(DriverGlobals), Queue, FX_TYPE_QUEUE, (PVOID*)&pQueue); status = pQueue->QueuePurge( TRUE, TRUE, PurgeComplete, Context ); if (!NT_SUCCESS(status)) { pQueue->FatalError(status); return; } return; } _Must_inspect_result_ __drv_maxIRQL(DISPATCH_LEVEL) NTSTATUS STDCALL WDFEXPORT(WdfIoQueueReadyNotify)( __in PWDF_DRIVER_GLOBALS DriverGlobals, __in WDFQUEUE Queue, __in_opt PFN_WDF_IO_QUEUE_STATE QueueReady, __in_opt WDFCONTEXT Context ) /*++ This API notifies the device driver when the Queue has one or more requests that can be processed by the device driver. This event registration continues until cancelled with by calling with NULL for QueueReady. One event is generated for each transition from not ready, to ready. An event is not generated for each new request, only request arrival on an empty Queue. --*/ { DDI_ENTRY(); FxIoQueue* pQueue; NTSTATUS status; FxObjectHandleGetPtr(GetFxDriverGlobals(DriverGlobals), Queue, FX_TYPE_QUEUE, (PVOID*)&pQueue); status = pQueue->ReadyNotify( QueueReady, Context ); return status; } _Must_inspect_result_ __drv_maxIRQL(PASSIVE_LEVEL) NTSTATUS STDCALL WDFEXPORT(WdfIoQueueAssignForwardProgressPolicy)( __in PWDF_DRIVER_GLOBALS DriverGlobals, __in WDFQUEUE Queue, __in PWDF_IO_QUEUE_FORWARD_PROGRESS_POLICY ForwardProgressPolicy ) /*++ This DDI is used by the driver to configure a queue to have forward progress. --*/ { DDI_ENTRY(); FxIoQueue* pQueue; NTSTATUS status; PFX_DRIVER_GLOBALS pFxDriverGlobals; FxObjectHandleGetPtrAndGlobals(GetFxDriverGlobals(DriverGlobals), Queue, FX_TYPE_QUEUE, (PVOID*)&pQueue, &pFxDriverGlobals); FxPointerNotNull(pFxDriverGlobals, ForwardProgressPolicy); status = FxVerifierCheckIrqlLevel(pFxDriverGlobals, PASSIVE_LEVEL); if (!NT_SUCCESS(status)) { return status; } if (pQueue->IsForwardProgressQueue()) { // // Queue is already configured for forward progress // status = STATUS_INVALID_PARAMETER; DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO, "Queue is already configured for forward progress %!STATUS!", status); FxVerifierDbgBreakPoint(pFxDriverGlobals); return status; } // // Validate Config structure // if (ForwardProgressPolicy->Size != sizeof(WDF_IO_QUEUE_FORWARD_PROGRESS_POLICY)) { status = STATUS_INFO_LENGTH_MISMATCH; DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO, "WDF_IO_QUEUE_FORWARD_PROGRESS_POLICY Size 0x%x, " "expected 0x%x, %!STATUS!", ForwardProgressPolicy->Size, sizeof(WDF_IO_QUEUE_FORWARD_PROGRESS_POLICY), status); return status; } // // Validate policy settings // switch (ForwardProgressPolicy->ForwardProgressReservedPolicy) { case WdfIoForwardProgressReservedPolicyUseExamine: if (ForwardProgressPolicy->ForwardProgressReservePolicySettings.Policy.ExaminePolicy.EvtIoWdmIrpForForwardProgress == NULL) { status = STATUS_INVALID_PARAMETER; DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO, "Examine callback can't be null for WdfIoForwardProgressReservedPolicyUseExamine " " %!STATUS!", status); return status; } break; default: break; } // // Validate number of forward progress requests // if (ForwardProgressPolicy->TotalForwardProgressRequests == 0) { status = STATUS_INVALID_PARAMETER; DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO, "Need to have more than 0 reserved Requests %!STATUS!", status); return status; } status = pQueue->AssignForwardProgressPolicy( ForwardProgressPolicy ); return status; } } // extern "C"