1 /*++
2 
3 Copyright (c) Microsoft Corporation
4 
5 Module Name:
6 
7     FxIoQueueApi.cpp
8 
9 Abstract:
10 
11     This module implements the FxIoQueue object C interfaces
12 
13 Author:
14 
15 
16 
17 
18 Revision History:
19 
20 
21 --*/
22 
23 #include "ioprivshared.hpp"
24 #include "fxpkgio.hpp"
25 #include "fxioqueue.hpp"
26 
27 extern "C" {
28 // #include "FxIoQueueApi.tmh"
29 }
30 
31 //
32 // C Accessor methods
33 //
34 // These are the "public" API's used by driver writers for both
35 // C and C++. In the C++ case, a "wrapper" class is placed around these
36 // methods. This is to avoid exposing any internal details of our
37 // driver frameworks implementation class, which would cause
38 // binary coupling with the device driver.
39 //
40 // (thus causing drivers to rebuild for even small changes to this C++ object)
41 //
42 
43 //
44 // extern all functions
45 //
46 extern "C" {
47 
48 
49 _Must_inspect_result_
50 __drv_maxIRQL(DISPATCH_LEVEL)
51 NTSTATUS
52 STDCALL
53 WDFEXPORT(WdfIoQueueCreate)(
54     __in
55     PWDF_DRIVER_GLOBALS DriverGlobals,
56     __in
57     WDFDEVICE Device,
58     __in
59     PWDF_IO_QUEUE_CONFIG Config,
60     __in_opt
61     PWDF_OBJECT_ATTRIBUTES QueueAttributes,
62     __out_opt
63     WDFQUEUE* Queue
64     )
65 
66 /*++
67 
68 Routine Description:
69 
70     Creates an IoQueue object and returns its handle to the caller.
71 
72     The newly created IoQueue object is associated with the IoPackage
73     instance for the device.
74 
75     The IoQueue object is automatically dereferenced when its
76     associated device is removed. This driver does not normally have
77     to manually manage IoQueue object reference counts.
78 
79     An IoQueue object is created in the WdfIoQueuePause state, and is
80     configured for WdfIoQueueDispatchSynchronous;
81 
82 Arguments:
83 
84     Device      - Handle to the Device the I/O Package registered with
85                    at EvtDeviceFileCreate time.
86 
87     Config     - WDF_IO_QUEUE_CONFIG structure
88 
89     pQueue      - Pointer to location to store the returned IoQueue handle.
90 
91 Return Value:
92 
93     NTSTATUS
94 
95 --*/
96 
97 {
98     DDI_ENTRY();
99 
100     PFX_DRIVER_GLOBALS pFxDriverGlobals;
101     CfxDevice*  pDevice;
102     FxPkgIo*   pPkgIo;
103     FxIoQueue* pQueue;
104     NTSTATUS status;
105 
106     //
107     // Validate the I/O Package handle, and get the FxPkgIo*
108     //
109     FxObjectHandleGetPtrAndGlobals(GetFxDriverGlobals(DriverGlobals),
110                                    Device,
111                                    FX_TYPE_DEVICE,
112                                    (PVOID*)&pDevice,
113                                    &pFxDriverGlobals);
114 
115     pPkgIo = NULL;
116     pQueue = NULL;
117 
118     FxPointerNotNull(pFxDriverGlobals, Config);
119 
120     status = FxValidateObjectAttributes(pFxDriverGlobals,
121                                         QueueAttributes,
122                                         (FX_VALIDATE_OPTION_EXECUTION_LEVEL_ALLOWED |
123                                          FX_VALIDATE_OPTION_SYNCHRONIZATION_SCOPE_ALLOWED));
124     if (!NT_SUCCESS(status)) {
125         return status;
126     }
127 
128     // Validate Config structure
129     if (Config->Size != sizeof(WDF_IO_QUEUE_CONFIG) &&
130         Config->Size != sizeof(WDF_IO_QUEUE_CONFIG_V1_9) &&
131         Config->Size != sizeof(WDF_IO_QUEUE_CONFIG_V1_7)) {
132         status = STATUS_INFO_LENGTH_MISMATCH;
133 
134         DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO,
135                             "WDF_IO_QUEUE_CONFIG Size 0x%x, "
136                             "expected for v1.7 size 0x%x or v1.9 size 0x%x or "
137                             "current version size 0x%x, %!STATUS!",
138                             Config->Size, sizeof(WDF_IO_QUEUE_CONFIG_V1_7),
139                             sizeof(WDF_IO_QUEUE_CONFIG_V1_9),
140                             sizeof(WDF_IO_QUEUE_CONFIG), status);
141         return status;
142     }
143 
144     //
145     // If the queue is not a default queue then the out parameter is not optional
146     //
147     if(!Config->DefaultQueue && Queue == NULL) {
148         status = STATUS_INVALID_PARAMETER_4;
149 
150         DoTraceLevelMessage(
151             pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO,
152             "Parameter to receive WDFQUEUE handle is not optional "
153             "for non default queue %!STATUS!", status);
154 
155         return status;
156     }
157 
158     //pPkgIo = (FxPkgIo*)pDevice->m_PkgIo;
159     pPkgIo = pDevice->m_PkgIo;
160 
161     //
162     // If the queue is a default queue, then we restrict the creation to happen
163     // a) before the device is started for pnp devices (FDO or PDO)
164     // b) before the call to WdfControlDeviceFinishInitializing for controldevices.
165     // This is done to prevent some unknown race conditions and reduce
166     // the test matrix.
167     //
168     if(Config->DefaultQueue) {
169 
170         if(pDevice->IsLegacy()) {
171             //
172             // This is a controldevice. Make sure the create is called after the device
173             // is initialized and ready to accept I/O.
174             //
175         if((pDevice->GetDeviceObjectFlags() & DO_DEVICE_INITIALIZING) == FALSE) {
176                 DoTraceLevelMessage(
177                     pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
178                     "Default queue can only be created before WdfControlDeviceFinishInitializing"
179                     "on the WDFDEVICE %p is called %!STATUS!",
180                     Device,
181                     STATUS_INVALID_DEVICE_STATE);
182                 return STATUS_INVALID_DEVICE_STATE;
183             }
184 
185         } else {
186             //
187             // This is either FDO or PDO. Make sure it's not started yet.
188             //
189         if (pDevice->GetDevicePnpState() != WdfDevStatePnpInit) {
190                 status = STATUS_INVALID_DEVICE_STATE;
191                 DoTraceLevelMessage(
192                     pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
193                     "Default queue can only be created before the WDFDEVICE 0x%p "
194                     "is started, %!STATUS!", Device, status);
195                 return status;
196             }
197         }
198     }
199 
200     //
201     // Create the Queue for the I/O package
202     //
203     status = pPkgIo->CreateQueue(Config,
204                                  QueueAttributes,
205                                  GetFxDriverGlobals(DriverGlobals)->Driver,
206                                  &pQueue);
207     if (!NT_SUCCESS(status)) {
208        DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO,
209                            "Queue Creation failed "
210                            "for WDFDEVICE 0x%p, %!STATUS!", Device, status);
211        return status;
212     }
213 
214     if(Config->DefaultQueue) {
215 
216         //
217         // Make this a default queue.   The default queue receives any
218         // I/O requests that have not been otherwise forwarded to another
219         // queue by WdfDeviceConfigureRequestDispatching .
220         //
221 
222         status = pPkgIo->InitializeDefaultQueue(
223                           pDevice,
224                           pQueue
225                           );
226 
227         if (!NT_SUCCESS(status)) {
228            DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO,
229                                "Create failed "
230                                "for FxPkgIo 0x%p, WDFDEVICE 0x%p",pPkgIo, Device);
231            //
232            // Delete the queue *without* invoking driver defined callbacks
233            //
234            pQueue->DeleteFromFailedCreate();
235 
236            return status;
237        }
238     }
239 
240     DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIO,
241                        "Created WDFQUEUE 0x%p", pQueue->GetObjectHandle());
242 
243     if(Queue != NULL) {
244         *Queue = (WDFQUEUE)pQueue->GetObjectHandle();
245     }
246 
247     return STATUS_SUCCESS;
248 }
249 
250 
251 __drv_maxIRQL(DISPATCH_LEVEL)
252 WDF_IO_QUEUE_STATE
253 STDCALL
254 WDFEXPORT(WdfIoQueueGetState)(
255     __in
256     PWDF_DRIVER_GLOBALS DriverGlobals,
257     __in
258     WDFQUEUE Queue,
259     __out_opt
260     PULONG QueueCount,
261     __out_opt
262     PULONG DriverCount
263     )
264 
265 /*++
266 
267 Routine Description:
268 
269     Return the Queues status
270 
271 Arguments:
272 
273     Queue        - Handle to Queue object
274 
275     pQueueCount  - Count of requests in the Queue not presented
276                   to the driver.
277 
278     pDriverCount - Count of requests the driver is operating
279                    on that are associated with this queue.
280 
281 Returns:
282 
283     WDF_IO_QUEUE_STATE
284 
285 --*/
286 
287 {
288     DDI_ENTRY();
289 
290     FxIoQueue* pQueue;
291 
292     FxObjectHandleGetPtr(GetFxDriverGlobals(DriverGlobals),
293                           Queue,
294                           FX_TYPE_QUEUE,
295                           (PVOID*)&pQueue);
296 
297     return pQueue->GetState(QueueCount, DriverCount);
298 }
299 
300 __drv_maxIRQL(DISPATCH_LEVEL)
301 WDFDEVICE
302 STDCALL
303 WDFEXPORT(WdfIoQueueGetDevice)(
304     __in
305     PWDF_DRIVER_GLOBALS DriverGlobals,
306     __in
307     WDFQUEUE Queue
308     )
309 
310 /*++
311 
312 Routine Description:
313     Returns the device handle that the queue is associated with
314 
315 Arguments:
316     Queue - Handle to queue object
317 
318 Return Value:
319     WDFDEVICE handle
320 
321 --*/
322 
323 {
324     DDI_ENTRY();
325 
326     FxIoQueue* pQueue;
327 
328     FxObjectHandleGetPtr(GetFxDriverGlobals(DriverGlobals),
329                           Queue,
330                           FX_TYPE_QUEUE,
331                           (PVOID*) &pQueue);
332 
333     return (WDFDEVICE) pQueue->GetDevice()->GetHandle();
334 }
335 
336 
337 __drv_maxIRQL(DISPATCH_LEVEL)
338 VOID
339 STDCALL
340 WDFEXPORT(WdfIoQueueStart)(
341     __in
342     PWDF_DRIVER_GLOBALS DriverGlobals,
343     __in
344     WDFQUEUE Queue
345     )
346 
347 /*++
348 
349 Routine Description:
350 
351     Set the Queues state to start accepting and dispatching new requests.
352 
353 Arguments:
354 
355     Queue - Handle to Queue object
356 
357     A Queue may not go into a specific state right away, since it may have to
358     wait for requests to be completed or cancelled. This is reflected
359     in the status returned by WdfIoQueueGetState.
360 
361     See the Queue event API's for asynchronous notification of
362     specific status states.
363 
364 Returns:
365 
366     NTSTATUS
367 --*/
368 
369 {
370     DDI_ENTRY();
371 
372     FxIoQueue* pQueue;
373 
374     FxObjectHandleGetPtr(GetFxDriverGlobals(DriverGlobals),
375                           Queue,
376                           FX_TYPE_QUEUE,
377                           (PVOID*)&pQueue);
378 
379     pQueue->QueueStart();
380 
381     return;
382 }
383 
384 __drv_maxIRQL(DISPATCH_LEVEL)
385 VOID
386 STDCALL
387 WDFEXPORT(WdfIoQueueStop)(
388     __in
389     PWDF_DRIVER_GLOBALS DriverGlobals,
390     __in
391     WDFQUEUE Queue,
392     __drv_when(Context != 0, __in)
393     __drv_when(Context == 0, __in_opt)
394     PFN_WDF_IO_QUEUE_STATE StopComplete,
395     __drv_when(StopComplete != 0, __in)
396     __drv_when(StopComplete == 0, __in_opt)
397     WDFCONTEXT Context
398     )
399 
400 /*++
401 
402 Routine Description:
403 
404     Set the Queue state to accept and queue (not dispatch) incoming new requests.
405 
406 Arguments:
407 
408     Queue - Handle to Queue object
409 
410     A Queue may not go into a specific state right away, since it may have to
411     wait for requests to be completed or cancelled. This is reflected
412     in the status returned by WdfIoQueueGetState.
413 
414     See the Queue event API's for asynchronous notification of
415     specific status states.
416 
417 Returns:
418 
419 --*/
420 
421 {
422     DDI_ENTRY();
423 
424     FxIoQueue* pQueue;
425     NTSTATUS status;
426 
427     FxObjectHandleGetPtr(GetFxDriverGlobals(DriverGlobals),
428                          Queue,
429                          FX_TYPE_QUEUE,
430                          (PVOID*)&pQueue);
431 
432     status =  pQueue->QueueIdle(FALSE, StopComplete, Context);
433     if (!NT_SUCCESS(status)) {
434         pQueue->FatalError(status);
435         return;
436     }
437 
438     return;
439 }
440 
441 __drv_maxIRQL(PASSIVE_LEVEL)
442 VOID
443 STDCALL
444 WDFEXPORT(WdfIoQueueStopSynchronously)(
445     __in
446     PWDF_DRIVER_GLOBALS DriverGlobals,
447     __in
448     WDFQUEUE Queue
449     )
450 
451 /*++
452 
453 Routine Description:
454 
455     Set the Queue state to accept and queue (not dispatch) incoming new requests and wait
456     for 1) all the dispatch callbacks to return and 2) all the driver-owned request to complete.
457 
458 Arguments:
459 
460     Queue - Handle to Queue object
461 
462 Returns:
463 
464     NTSTATUS
465 --*/
466 
467 {
468     DDI_ENTRY();
469 
470     PFX_DRIVER_GLOBALS pFxDriverGlobals;
471     FxIoQueue* pQueue;
472     NTSTATUS status;
473 
474     FxObjectHandleGetPtrAndGlobals(GetFxDriverGlobals(DriverGlobals),
475                                    Queue,
476                                    FX_TYPE_QUEUE,
477                                    (PVOID*)&pQueue,
478                                    &pFxDriverGlobals);
479 
480     status = FxVerifierCheckIrqlLevel(pFxDriverGlobals, PASSIVE_LEVEL);
481     if (!NT_SUCCESS(status)) {
482         return;
483     }
484 
485     status = pQueue->QueueIdleSynchronously(FALSE);
486 
487     if (!NT_SUCCESS(status)) {
488         pQueue->FatalError(status);
489         return;
490     }
491 }
492 
493 __drv_maxIRQL(DISPATCH_LEVEL)
494 VOID
495 STDCALL
496 WDFEXPORT(WdfIoQueueStopAndPurge)(
497     __in
498     PWDF_DRIVER_GLOBALS DriverGlobals,
499     __in
500     WDFQUEUE Queue,
501     __drv_when(Context != 0, __in)
502     __drv_when(Context == 0, __in_opt)
503     PFN_WDF_IO_QUEUE_STATE StopAndPurgeComplete,
504     __drv_when(StopAndPurgeComplete != 0, __in)
505     __drv_when(StopAndPurgeComplete == 0, __in_opt)
506     WDFCONTEXT Context
507     )
508 
509 /*++
510 
511 Routine Description:
512 
513     This function does the following:
514     - sets the queue state to accept and queues (not dispatch) incoming new requests.
515     - cancels all current (at the time this function is called) queued requests.
516     - invokes, if present, the cancel callback of cancellable driver requests.
517     - Asynchronously if complete callback is specified, it waits until
518         1) all the dispatch callbacks to return and
519         2) all the driver-owned request to complete.
520 
521 Arguments:
522 
523     Queue - Handle to Queue object
524 
525     A Queue may not go into a specific state right away, since it may have to
526     wait for requests to be completed or cancelled. This is reflected
527     in the status returned by WdfIoQueueGetState.
528 
529     See the Queue event API's for asynchronous notification of
530     specific status states.
531 
532 Returns:
533 
534 --*/
535 
536 {
537     DDI_ENTRY();
538 
539     FxIoQueue*  queue;
540     NTSTATUS    status;
541 
542     FxObjectHandleGetPtr(GetFxDriverGlobals(DriverGlobals),
543                          Queue,
544                          FX_TYPE_QUEUE,
545                          (PVOID*)&queue);
546 
547     status =  queue->QueueIdle(TRUE, StopAndPurgeComplete, Context);
548     if (!NT_SUCCESS(status)) {
549         queue->FatalError(status);
550         return;
551     }
552 
553     return;
554 }
555 
556 __drv_maxIRQL(PASSIVE_LEVEL)
557 VOID
558 STDCALL
559 WDFEXPORT(WdfIoQueueStopAndPurgeSynchronously)(
560     __in
561     PWDF_DRIVER_GLOBALS DriverGlobals,
562     __in
563     WDFQUEUE Queue
564     )
565 
566 /*++
567 
568 Routine Description:
569 
570     This function does the following:
571     - sets the queue state to accept and queues (not dispatch) incoming new requests.
572     - cancels all current (at the time this function is called) queued requests.
573     - invokes, if present, the cancel callback of cancellable driver requests.
574     - before returning it waits until
575         1) all the dispatch callbacks to return and
576         2) all the driver-owned request to complete.
577 
578 Arguments:
579 
580     Queue - Handle to Queue object
581 
582 Returns:
583 
584     NTSTATUS
585 --*/
586 
587 {
588     DDI_ENTRY();
589 
590     PFX_DRIVER_GLOBALS  fxDriverGlobals;
591     FxIoQueue*          queue;
592     NTSTATUS            status;
593 
594     FxObjectHandleGetPtrAndGlobals(GetFxDriverGlobals(DriverGlobals),
595                                    Queue,
596                                    FX_TYPE_QUEUE,
597                                    (PVOID*)&queue,
598                                    &fxDriverGlobals);
599 
600     status = FxVerifierCheckIrqlLevel(fxDriverGlobals, PASSIVE_LEVEL);
601     if (!NT_SUCCESS(status)) {
602         return;
603     }
604 
605     status = queue->QueueIdleSynchronously(TRUE);
606 
607     if (!NT_SUCCESS(status)) {
608         queue->FatalError(status);
609         return;
610     }
611 }
612 
613 _Must_inspect_result_
614 __drv_maxIRQL(DISPATCH_LEVEL)
615 NTSTATUS
616 STDCALL
617 WDFEXPORT(WdfIoQueueRetrieveNextRequest)(
618     __in
619     PWDF_DRIVER_GLOBALS DriverGlobals,
620     __in
621     WDFQUEUE Queue,
622     __out
623     WDFREQUEST *OutRequest
624     )
625 
626 /*++
627 
628 WdfIoQueueRetrieveNextRequest:
629 
630 Routine Description:
631 
632     Returns a request from the head of the queue.
633 
634     On successful return the driver owns the request, and must
635     eventually call WdfRequestComplete.
636 
637     The driver does not need to release any extra reference counts,
638     other than calling WdfRequestComplete.
639 
640 
641 Arguments:
642 
643     Queue - Queue handle
644 
645     pOutRequest - Pointer to location to return new request handle
646 
647 Returns:
648 
649 
650     STATUS_NO_MORE_ENTRIES -     The queue is empty
651 
652     STATUS_SUCCESS        -     A request was returned in pOutRequest
653 
654 --*/
655 
656 {
657     DDI_ENTRY();
658 
659     FxIoQueue* pQueue;
660     FxRequest* pOutputRequest = NULL;
661     NTSTATUS status;
662 
663     FxObjectHandleGetPtr(GetFxDriverGlobals(DriverGlobals),
664                           Queue,
665                           FX_TYPE_QUEUE,
666                           (PVOID*)&pQueue);
667 
668     FxPointerNotNull(pQueue->GetDriverGlobals(), OutRequest);
669 
670     status = pQueue->GetRequest(NULL, NULL, &pOutputRequest);
671 
672     if (NT_SUCCESS(status)) {
673         *OutRequest = (WDFREQUEST)pOutputRequest->GetObjectHandle();
674     } else {
675         *OutRequest = NULL;
676         ASSERT(status != STATUS_NOT_FOUND);
677     }
678 
679     return status;
680 }
681 
682 _Must_inspect_result_
683 __drv_maxIRQL(DISPATCH_LEVEL)
684 NTSTATUS
685 STDCALL
686 WDFEXPORT(WdfIoQueueRetrieveRequestByFileObject)(
687     __in
688     PWDF_DRIVER_GLOBALS DriverGlobals,
689     __in
690     WDFQUEUE Queue,
691     __in
692     WDFFILEOBJECT FileObject,
693     __out
694     WDFREQUEST *OutRequest
695     )
696 
697 /*++
698 
699 WdfIoQueueRetrieveNextRequest:
700 
701 Routine Description:
702 
703     Returns a request from the queue that matches the fileobject.
704 
705     Requests are dequeued in a first in, first out manner.
706 
707     If there are no requests that match the selection criteria,
708     STATUS_NO_MORE_ENTRIES is returned.
709 
710     On successful return the driver owns the request, and must
711     eventually call WdfRequestComplete.
712 
713     The driver does not need to release any extra reference counts,
714     other than calling WdfRequestComplete.
715 
716 Arguments:
717 
718     Queue - Queue handle
719 
720     FileObject - FileObject to match in the request
721 
722     pOutRequest - Pointer to location to return new request handle
723 
724 Returns:
725 
726 
727     STATUS_NO_MORE_ENTRIES -     The queue is empty, or no more requests
728                                 match the selection criteria of TagRequest
729                                 and FileObject specified above.
730 
731     STATUS_SUCCESS        -     A request context was returned in
732                                 pOutRequest
733 
734 --*/
735 
736 {
737     DDI_ENTRY();
738 
739     FxIoQueue* pQueue;
740     FxRequest* pOutputRequest = NULL;
741     FxFileObject* pFO = NULL;
742     MdFileObject pWdmFO = NULL;
743     NTSTATUS status;
744     PFX_DRIVER_GLOBALS pFxDriverGlobals;
745 
746     FxObjectHandleGetPtrAndGlobals(GetFxDriverGlobals(DriverGlobals),
747                                    Queue,
748                                    FX_TYPE_QUEUE,
749                                    (PVOID*)&pQueue,
750                                    &pFxDriverGlobals);
751 
752     FxPointerNotNull(pFxDriverGlobals, OutRequest);
753 
754     FxObjectHandleGetPtr(pFxDriverGlobals,
755                          FileObject,
756                          FX_TYPE_FILEOBJECT,
757                          (PVOID*)&pFO);
758 
759     // Get the real WDM fileobject
760     pWdmFO = pFO->GetWdmFileObject();
761 
762     status = pQueue->GetRequest(pWdmFO, NULL, &pOutputRequest);
763 
764     if (NT_SUCCESS(status)) {
765 
766         // Copy out the handle to the new request
767         *OutRequest = (WDFREQUEST)pOutputRequest->GetObjectHandle();
768     } else {
769         *OutRequest = NULL;
770         ASSERT(status != STATUS_NOT_FOUND);
771     }
772 
773     return status;
774 }
775 
776 _Must_inspect_result_
777 __drv_maxIRQL(DISPATCH_LEVEL)
778 NTSTATUS
779 STDCALL
780 WDFEXPORT(WdfIoQueueRetrieveFoundRequest)(
781     __in
782     PWDF_DRIVER_GLOBALS DriverGlobals,
783     __in
784     WDFQUEUE Queue,
785     __in
786     WDFREQUEST TagRequest,
787     __out
788     WDFREQUEST * OutRequest
789     )
790 
791 /*++
792 
793 WdfIoQueueRetrieveNextRequest:
794 
795 Routine Description:
796 
797     Returns a request from the queue.
798 
799     Requests are dequeued in a first in, first out manner.
800 
801     The queue is searched for specific peeked
802     request, and if it is not found, STATUS_NOT_FOUND is
803     returned. A TagRequest value is returned
804     from WdfIoQueueFindRequest().
805 
806     If there are no requests that match the selection criteria,
807     STATUS_NO_MORE_ENTRIES is returned.
808 
809     On successful return the driver owns the request, and must
810     eventually call WdfRequestComplete.
811 
812     The driver does not need to release any extra reference counts,
813     other than calling WdfRequestComplete.
814 
815 Arguments:
816 
817     Queue - Queue handle
818 
819     TagRequest - Request to look for in queue
820 
821     pOutRequest - Pointer to location to return new request handle
822 
823 Returns:
824 
825     STATUS_NOT_FOUND - TagContext was specified, but not
826                                 found in the queue. This could be
827                                 because the request was cancelled,
828                                 or is part of an active queue and
829                                 the request was passed to the driver
830                                 or forwarded to another queue.
831 
832     STATUS_NO_MORE_ENTRIES -     The queue is empty, or no more requests
833                                 match the selection criteria of TagRequest
834                                 specified above.
835 
836     STATUS_SUCCESS        -     A request context was returned in
837                                 pOutRequest
838 
839 --*/
840 
841 {
842     DDI_ENTRY();
843 
844     FxIoQueue* pQueue;
845     FxRequest* pTagRequest = NULL;
846     FxRequest* pOutputRequest = NULL;
847     NTSTATUS status;
848     PFX_DRIVER_GLOBALS pFxDriverGlobals;
849 
850     FxObjectHandleGetPtrAndGlobals(GetFxDriverGlobals(DriverGlobals),
851                                    Queue,
852                                    FX_TYPE_QUEUE,
853                                    (PVOID*)&pQueue,
854                                    &pFxDriverGlobals);
855 
856     FxPointerNotNull(pFxDriverGlobals, OutRequest);
857 
858     FxObjectHandleGetPtr(pFxDriverGlobals,
859                          TagRequest,
860                          FX_TYPE_REQUEST,
861                          (PVOID*)&pTagRequest);
862 
863     status = pQueue->GetRequest(NULL, pTagRequest, &pOutputRequest);
864 
865     if (NT_SUCCESS(status)) {
866         // Copy out the handle to the new request
867         *OutRequest = (WDFREQUEST)pOutputRequest->GetObjectHandle();
868     } else {
869         *OutRequest = NULL;
870     }
871 
872     return status;
873 }
874 
875 _Must_inspect_result_
876 __drv_maxIRQL(DISPATCH_LEVEL)
877 NTSTATUS
878 STDCALL
879 WDFEXPORT(WdfIoQueueFindRequest)(
880     __in
881     PWDF_DRIVER_GLOBALS DriverGlobals,
882     __in
883     WDFQUEUE Queue,
884     __in_opt
885     WDFREQUEST TagRequest,
886     __in_opt
887     WDFFILEOBJECT FileObject,
888     __inout_opt
889     PWDF_REQUEST_PARAMETERS Parameters,
890     __out
891     WDFREQUEST * OutRequest
892     )
893 
894 /*++
895 
896 WdfIoQueueFindRequest:
897 
898 Routine Description:
899 
900     PeekRequest allows a caller to enumerate through requests in
901     a queue, optionally only returning requests that match a specified
902     FileObject.
903 
904     The first call specifies TagContext == NULL, and the first request
905     in the queue that matches the FileObject is returned.
906 
907     Subsequent requests specify the previous request value as the
908     TagContext, and searching will continue at the request that follows.
909 
910     If the queue is empty, there are no requests after TagContext, or no
911     requests match the FileObject, NULL is returned.
912 
913     If FileObject == NULL, this matches any FileObject in a request.
914 
915     If a WDF_REQUEST_PARAMETERS structure is supplied, the information
916     from the request is returned to allow the driver to further examine
917     the request to decide whether to service it.
918 
919     If a TagRequest is specified, and it is not found, the return
920     status STATUS_NOT_FOUND means that the queue should
921     be re-scanned. This is because the TagRequest was cancelled from
922     the queue, or if the queue was active, delivered to the driver.
923     There may still be un-examined requests on the queue that match
924     the drivers search criteria, but the search marker has been lost.
925 
926     Re-scanning the queue starting with TagRequest == NULL and
927     continuing until STATUS_NO_MORE_ENTRIES is returned will ensure
928     all requests have been examined.
929 
930     Enumerating an active queue with this API could result in the
931     driver frequently having to re-scan.
932 
933     If a successful return of a Request object handle occurs, the driver
934     *must* call WdfObjectDereference when done with it, otherwise an
935     object leak will result.
936 
937     Returned request objects are not owned by the driver, and may not be
938     used by I/O or completed. The only valid operations are
939     WdfRequestGetParameters, passing it as the TagRequest parameter to
940     WdfIoQueueFindRequest, WdfIoQueueRetrieveFoundRequest, or calling
941     WdfObjectDereference. All other actions are undefined.
942 
943     The request could be cancelled without a EvtIoCancel callback while the
944     driver has the handle. The request then becomes invalid, and
945     WdfObjectDereference must be called to release its resources.
946 
947     The caller should not use any buffer pointers in the returned parameters
948     structure until it successfully receives ownership of the request by
949     calling WdfIoQueueRetrieveFoundRequest with the request tag. This is because
950     the I/O can be cancelled and completed at any time without the driver
951     being notified until it has received ownership, thus invalidating
952     the buffer pointers and releasing the memory, even if the request
953     object itself it still valid due to the reference.
954 
955     The driver should hold the reference on the object until after
956     a call to WdfIoQueueFindRequest using it as the TagRequest
957     parameter, or WdfIoQueueRetrieveFoundtRequest. Otherwise, a race could
958     result in which the object is cancelled, its memory re-used for
959     a new request, and then an attempt to use it as a tag would result
960     in a different state than intended. The driver verifier will
961     catch this, but it is a rare race.
962 
963     Calling this API on an operating Queue can lead to confusing results. This
964     is because the request could be presented to EvtIoDefault, causing it to be
965     invalid as a TagRequest in calls to WdfIoQueueFindRequest, and
966     WdfIoQueueRetrieveFoundRequest. The extra reference taken on the object by its return
967     from this call must still be released by WdfObjectDereference.
968 
969     The intention of the API is to only hold onto the TagRequest long enough
970     for the driver to make a decision whether to service the request, or
971     to continue searching for requests.
972 
973 Arguments:
974 
975     Queue       - Queue handle
976 
977     TagRequest  - If !NULL, request to begin search at
978 
979     FileObject  - If !NULL, FileObject to match in the request
980 
981     Parameters  - If !NULL, pointer to buffer to return the requests
982                   parameters to aid in selection.
983 
984     pOutRequest - Pointer to location to return request handle
985 
986 Returns:
987 
988     STATUS_NOT_FOUND - TagContext was specified, but not
989                                 found in the queue. This could be
990                                 because the request was cancelled,
991                                 or is part of an active queue and
992                                 the request was passed to the driver
993                                 or forwarded to another queue.
994 
995     STATUS_NO_MORE_ENTRIES -     The queue is empty, or no more requests
996                                 match the selection criteria of TagRequest
997                                 and FileObject specified above.
998 
999     STATUS_SUCCESS        -     A request context was returned in
1000                                 pOutRequest
1001 
1002 --*/
1003 
1004 {
1005     DDI_ENTRY();
1006 
1007     FxIoQueue* pQueue;
1008     FxRequest* pTagRequest = NULL;
1009     FxRequest* pOutputRequest = NULL;
1010     FxFileObject* pFO = NULL;
1011     MdFileObject pWdmFO = NULL;
1012     NTSTATUS status;
1013     PFX_DRIVER_GLOBALS pFxDriverGlobals;
1014 
1015     FxObjectHandleGetPtrAndGlobals(GetFxDriverGlobals(DriverGlobals),
1016                                    Queue,
1017                                    FX_TYPE_QUEUE,
1018                                    (PVOID*)&pQueue,
1019                                    &pFxDriverGlobals);
1020 
1021     FxPointerNotNull(pFxDriverGlobals, OutRequest);
1022 
1023     //
1024     // Validate tag request handle if supplied
1025     //
1026     if (TagRequest != NULL) {
1027         FxObjectHandleGetPtr(pFxDriverGlobals,
1028                              TagRequest,
1029                              FX_TYPE_REQUEST,
1030                              (PVOID*)&pTagRequest);
1031     }
1032 
1033     //
1034     // If present, validate the FileObject object handle,
1035     // and get its FxFileObject*
1036     //
1037     if (FileObject != NULL) {
1038         FxObjectHandleGetPtr(pFxDriverGlobals,
1039                              FileObject,
1040                              FX_TYPE_FILEOBJECT,
1041                              (PVOID*)&pFO);
1042         //
1043         // Get the real WDM fileobject
1044         //
1045         pWdmFO = pFO->GetWdmFileObject();
1046     }
1047 
1048     //
1049     // If a parameters buffer is supplied, validate its length
1050     //
1051     if ((Parameters != NULL) && (Parameters->Size < sizeof(WDF_REQUEST_PARAMETERS))) {
1052         status = STATUS_INVALID_PARAMETER_4;
1053         DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO,
1054                             "Invalid WDF_REQUEST_PARAMETERS size %d %!STATUS!",
1055                             Parameters->Size, status);
1056         return status;
1057     }
1058 
1059 
1060     status = pQueue->PeekRequest(pTagRequest,
1061                                  pWdmFO,
1062                                  Parameters,
1063                                  &pOutputRequest);
1064 
1065     if (NT_SUCCESS(status)) {
1066         //
1067         // Copy out the handle to the new request
1068         //
1069         *OutRequest = (WDFREQUEST)pOutputRequest->GetObjectHandle();
1070     } else {
1071         *OutRequest = NULL;
1072     }
1073 
1074     return status;
1075 }
1076 
1077 __drv_maxIRQL(DISPATCH_LEVEL)
1078 VOID
1079 STDCALL
1080 WDFEXPORT(WdfIoQueueDrain)(
1081     __in
1082     PWDF_DRIVER_GLOBALS DriverGlobals,
1083     __in
1084     WDFQUEUE Queue,
1085     __drv_when(Context != 0, __in)
1086     __drv_when(Context == 0, __in_opt)
1087     PFN_WDF_IO_QUEUE_STATE DrainComplete,
1088     __drv_when(DrainComplete != 0, __in)
1089     __drv_when(DrainComplete == 0, __in_opt)
1090     WDFCONTEXT Context
1091     )
1092 
1093 /*++
1094 
1095      Set the Queue to reject new requests, with newly arriving
1096      requests being completed with STATUS_CANCELLED.
1097 
1098      If the optional DrainComplete callback is specified, the
1099      callback will be invoked with the supplied context when
1100      there are no requests owned by the driver and no request pending in the
1101      queue.
1102 
1103      Only one callback registration for WdfIoQueueIdle, WdfIoQueuePurge,
1104      or WdfIoQueueDrain may be outstanding at a time.
1105 
1106 --*/
1107 
1108 {
1109     DDI_ENTRY();
1110 
1111     FxIoQueue* pQueue;
1112     NTSTATUS status;
1113 
1114     FxObjectHandleGetPtr(GetFxDriverGlobals(DriverGlobals),
1115                          Queue,
1116                          FX_TYPE_QUEUE,
1117                          (PVOID*)&pQueue);
1118 
1119     status = pQueue->QueueDrain(DrainComplete,  Context);
1120 
1121     if (!NT_SUCCESS(status)) {
1122         pQueue->FatalError(status);
1123         return;
1124     }
1125 
1126     return;
1127 }
1128 
1129 __drv_maxIRQL(PASSIVE_LEVEL)
1130 VOID
1131 STDCALL
1132 WDFEXPORT(WdfIoQueueDrainSynchronously)(
1133     __in
1134     PWDF_DRIVER_GLOBALS DriverGlobals,
1135     __in
1136     WDFQUEUE Queue
1137     )
1138 
1139 /*++
1140 
1141     Set the Queue to fail new request with STATUS_CANCELLED,
1142     dispatches pending requests, and waits for the all the pending and
1143     inflight requests to complete before returning.
1144 
1145     Should be called at PASSIVE_LEVEL.
1146 
1147 --*/
1148 
1149 {
1150     DDI_ENTRY();
1151 
1152     PFX_DRIVER_GLOBALS pFxDriverGlobals;
1153     FxIoQueue* pQueue;
1154     NTSTATUS status;
1155 
1156     FxObjectHandleGetPtrAndGlobals(GetFxDriverGlobals(DriverGlobals),
1157                                    Queue,
1158                                    FX_TYPE_QUEUE,
1159                                    (PVOID*)&pQueue,
1160                                    &pFxDriverGlobals);
1161 
1162     status = FxVerifierCheckIrqlLevel(pFxDriverGlobals, PASSIVE_LEVEL);
1163     if (!NT_SUCCESS(status)) {
1164         return;
1165     }
1166 
1167     status = pQueue->QueueDrainSynchronously();
1168 
1169     if (!NT_SUCCESS(status)) {
1170         pQueue->FatalError(status);
1171         return;
1172     }
1173 }
1174 
1175 __drv_maxIRQL(PASSIVE_LEVEL)
1176 VOID
1177 STDCALL
1178 WDFEXPORT(WdfIoQueuePurgeSynchronously)(
1179    __in
1180    PWDF_DRIVER_GLOBALS DriverGlobals,
1181    __in
1182    WDFQUEUE Queue
1183    )
1184 
1185 /*++
1186 
1187     Sets the queue state to fail incoming requests,
1188     cancels all the pending requests, cancels in-flights requests
1189     (if they are marked cancelable), and waits for all the requests
1190     to complete before returning.
1191 
1192     Should be called at PASSIVE_LEVEL.
1193 
1194 --*/
1195 
1196 {
1197     DDI_ENTRY();
1198 
1199     PFX_DRIVER_GLOBALS pFxDriverGlobals;
1200     FxIoQueue* pQueue;
1201     NTSTATUS status;
1202 
1203     FxObjectHandleGetPtrAndGlobals(GetFxDriverGlobals(DriverGlobals),
1204                                    Queue,
1205                                    FX_TYPE_QUEUE,
1206                                    (PVOID*)&pQueue,
1207                                    &pFxDriverGlobals);
1208 
1209     status = FxVerifierCheckIrqlLevel(pFxDriverGlobals, PASSIVE_LEVEL);
1210     if (!NT_SUCCESS(status)) {
1211         return;
1212     }
1213 
1214     status = pQueue->QueuePurgeSynchronously();
1215 
1216     if (!NT_SUCCESS(status)) {
1217         pQueue->FatalError(status);
1218         return;
1219     }
1220 
1221     return;
1222 }
1223 
1224 __drv_maxIRQL(DISPATCH_LEVEL)
1225 VOID
1226 STDCALL
1227 WDFEXPORT(WdfIoQueuePurge)(
1228     __in
1229     PWDF_DRIVER_GLOBALS DriverGlobals,
1230     __in
1231     WDFQUEUE Queue,
1232     __drv_when(Context != 0, __in)
1233     __drv_when(Context == 0, __in_opt)
1234     PFN_WDF_IO_QUEUE_STATE PurgeComplete,
1235     __drv_when(PurgeComplete != 0, __in)
1236     __drv_when(PurgeComplete == 0, __in_opt)
1237     WDFCONTEXT Context
1238     )
1239 
1240 /*++
1241 
1242  Set the Queue to reject new requests, with newly arriving
1243  requests being completed with STATUS_CANCELLED.
1244 
1245  A Queue may not purge immediately, since the device driver
1246  could be operating on non-cancelable requests.
1247 
1248 
1249  If the optional PurgeComplete callback is specified, the
1250  callback will be invoked with the supplied context when
1251  the Queue is in the WDF_IO_QUEUE_PURGED state.
1252  (Reject new requests, no current requests in Queue or device driver)
1253 
1254  Only one callback registration for WdfIoQueueIdle, WdfIoQueuePurge,
1255  or WdfIoQueueDrain may be outstanding at a time.
1256 
1257 --*/
1258 
1259 {
1260     DDI_ENTRY();
1261 
1262     FxIoQueue* pQueue;
1263     NTSTATUS status;
1264 
1265     FxObjectHandleGetPtr(GetFxDriverGlobals(DriverGlobals),
1266                          Queue,
1267                          FX_TYPE_QUEUE,
1268                          (PVOID*)&pQueue);
1269 
1270     status = pQueue->QueuePurge(
1271         TRUE,
1272         TRUE,
1273         PurgeComplete,
1274         Context
1275         );
1276 
1277     if (!NT_SUCCESS(status)) {
1278         pQueue->FatalError(status);
1279         return;
1280     }
1281 
1282     return;
1283 }
1284 
1285 
1286 _Must_inspect_result_
1287 __drv_maxIRQL(DISPATCH_LEVEL)
1288 NTSTATUS
1289 STDCALL
1290 WDFEXPORT(WdfIoQueueReadyNotify)(
1291     __in
1292     PWDF_DRIVER_GLOBALS DriverGlobals,
1293     __in
1294     WDFQUEUE Queue,
1295      __in_opt
1296     PFN_WDF_IO_QUEUE_STATE QueueReady,
1297      __in_opt
1298     WDFCONTEXT Context
1299     )
1300 
1301 /*++
1302 
1303  This API notifies the device driver when the Queue
1304  has one or more requests that can be processed by
1305  the device driver.
1306 
1307  This event registration continues until cancelled with
1308  by calling with NULL for QueueReady.
1309 
1310  One event is generated for each transition from
1311  not ready, to ready. An event is not generated for each
1312  new request, only request arrival on an empty Queue.
1313 
1314 --*/
1315 
1316 {
1317     DDI_ENTRY();
1318 
1319     FxIoQueue* pQueue;
1320     NTSTATUS status;
1321 
1322     FxObjectHandleGetPtr(GetFxDriverGlobals(DriverGlobals),
1323                          Queue,
1324                          FX_TYPE_QUEUE,
1325                          (PVOID*)&pQueue);
1326 
1327     status = pQueue->ReadyNotify(
1328         QueueReady,
1329         Context
1330         );
1331 
1332     return status;
1333 }
1334 
1335 _Must_inspect_result_
1336 __drv_maxIRQL(PASSIVE_LEVEL)
1337 NTSTATUS
1338 STDCALL
1339 WDFEXPORT(WdfIoQueueAssignForwardProgressPolicy)(
1340     __in
1341     PWDF_DRIVER_GLOBALS DriverGlobals,
1342     __in
1343     WDFQUEUE   Queue,
1344     __in
1345     PWDF_IO_QUEUE_FORWARD_PROGRESS_POLICY ForwardProgressPolicy
1346    )
1347 
1348 /*++
1349 
1350  This DDI  is used by the driver to configure a queue to have forward
1351   progress.
1352 --*/
1353 {
1354     DDI_ENTRY();
1355 
1356     FxIoQueue* pQueue;
1357     NTSTATUS status;
1358     PFX_DRIVER_GLOBALS pFxDriverGlobals;
1359 
1360     FxObjectHandleGetPtrAndGlobals(GetFxDriverGlobals(DriverGlobals),
1361                                    Queue,
1362                                    FX_TYPE_QUEUE,
1363                                    (PVOID*)&pQueue,
1364                                    &pFxDriverGlobals);
1365 
1366     FxPointerNotNull(pFxDriverGlobals, ForwardProgressPolicy);
1367     status = FxVerifierCheckIrqlLevel(pFxDriverGlobals, PASSIVE_LEVEL);
1368     if (!NT_SUCCESS(status)) {
1369         return status;
1370     }
1371 
1372     if (pQueue->IsForwardProgressQueue()) {
1373         //
1374         // Queue is already configured for forward progress
1375         //
1376         status =  STATUS_INVALID_PARAMETER;
1377         DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO,
1378                             "Queue is already configured for forward progress %!STATUS!",
1379                              status);
1380         FxVerifierDbgBreakPoint(pFxDriverGlobals);
1381         return status;
1382     }
1383 
1384     //
1385     // Validate Config structure
1386     //
1387     if (ForwardProgressPolicy->Size != sizeof(WDF_IO_QUEUE_FORWARD_PROGRESS_POLICY)) {
1388         status = STATUS_INFO_LENGTH_MISMATCH;
1389         DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO,
1390                             "WDF_IO_QUEUE_FORWARD_PROGRESS_POLICY Size 0x%x, "
1391                             "expected 0x%x, %!STATUS!",
1392                             ForwardProgressPolicy->Size, sizeof(WDF_IO_QUEUE_FORWARD_PROGRESS_POLICY), status);
1393         return status;
1394     }
1395 
1396     //
1397     // Validate policy settings
1398     //
1399     switch (ForwardProgressPolicy->ForwardProgressReservedPolicy) {
1400         case WdfIoForwardProgressReservedPolicyUseExamine:
1401             if (ForwardProgressPolicy->ForwardProgressReservePolicySettings.Policy.ExaminePolicy.EvtIoWdmIrpForForwardProgress == NULL) {
1402                 status = STATUS_INVALID_PARAMETER;
1403                 DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO,
1404                                     "Examine callback can't be null for WdfIoForwardProgressReservedPolicyUseExamine "
1405                                     " %!STATUS!",
1406                                      status);
1407 
1408                 return status;
1409             }
1410             break;
1411 
1412         default:
1413             break;
1414 
1415     }
1416 
1417     //
1418     // Validate number of forward progress requests
1419     //
1420     if (ForwardProgressPolicy->TotalForwardProgressRequests == 0) {
1421         status = STATUS_INVALID_PARAMETER;
1422         DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO,
1423                             "Need to have more than  0 reserved Requests %!STATUS!",
1424                             status);
1425 
1426         return status;
1427     }
1428 
1429     status = pQueue->AssignForwardProgressPolicy(
1430         ForwardProgressPolicy
1431         );
1432 
1433     return status;
1434 }
1435 
1436 
1437 
1438 } // extern "C"
1439