1 //
2 //    Copyright (C) Microsoft.  All rights reserved.
3 //
4 #include "fxusbpch.hpp"
5 
6 extern "C" {
7 #include "FxUsbPipe.tmh"
8 }
9 
10 #include "Fxglobals.h"
11 //
12 //  NOTE: There are 3 different paths Requests could be sent to the lower driver
13 //  1) In case of reposting a successfully completed Request use the Dpc which calls SendIo.
14 //  2) For a failed completion use a workitem which works if the IoTarget is in Started state.
15 //  3) On moving to the start state the Repeater requests are inserted into the
16 //      Iotargets Pended Queue directly.
17 //
18 //  The function ResubmitRepeater calls SubmitLocked. If the action we get back
19 //  from SubmitLocked is "SubmitSend", (SubmitQueued is treated as failure because
20 //  of WDF_REQUEST_SEND_INTERNAL_OPTION_FAIL_ON_PEND flag) we are guaranteed to
21 //  call IoCallDriver in the workitem or  the DPC and hence the completion routine
22 //  being called.
23 //  This is very important because we increment the m_IoCount in SubmitLocked and
24 //  decrement in the completion routine.  So if there was a code path where
25 //  SubmitLocked was called and IoCallDriver(hence the completion routine) wasn't,
26 //  the IoTarget could stop responding in Dispose.
27 //
28 
29 
30 FxUsbPipeContinuousReader::FxUsbPipeContinuousReader(
31     __in FxUsbPipe* Pipe,
32     __in UCHAR NumReaders
33     ) :
34     m_NumReaders(NumReaders),
35     m_NumFailedReaders(0)
36 {
37     m_WorkItem = NULL;
38     m_WorkItemRerunContext = NULL;
39     m_WorkItemThread = NULL;
40     m_WorkItemFlags = 0;
41     m_WorkItemQueued = FALSE;
42     m_ReadersSubmitted = FALSE;
43 
44     m_Lookaside = NULL;
45     m_Pipe = Pipe;
46 
47     m_TargetDevice = m_Pipe->GetTargetDevice();
48 
49     RtlZeroMemory(&m_Readers[0], m_NumReaders * sizeof(FxUsbPipeRepeatReader));
50 }
51 
52 FxUsbPipeContinuousReader::~FxUsbPipeContinuousReader()
53 {
54     LONG i;
55 
56     FxUsbPipeRepeatReader * reader = &m_Readers[0];
57 
58     //
59     // It is impoortant to delete the requests before the lookaside because the
60     // requests may have outstanding references on memory objects allocated by
61     // the lookaside.  The lookaside will not be truly deleted until the oustanding
62     // memory object allocations are also freed.
63     //
64     for (i = 0; i < m_NumReaders; i++) {
65         if (reader[i].Request != NULL) {
66             DeleteMemory(reader[i].Request);
67 
68             reader[i].Request->DeleteObject();
69             reader[i].Request = NULL;
70         }
71 
72 #if (FX_CORE_MODE == FX_CORE_USER_MODE)
73         reader[i].ReadCompletedEvent.Uninitialize();
74         reader[i].m_ReadWorkItem.Free();
75 #endif
76     }
77 
78     if (m_Lookaside != NULL) {
79         m_Lookaside->DeleteObject();
80     }
81 
82     if (m_WorkItem != NULL) {
83         m_WorkItem->DeleteObject();
84         m_WorkItem = NULL;
85     }
86 }
87 
88 BOOLEAN
89 FxUsbPipeContinuousReader::QueueWorkItemLocked(
90     __in FxUsbPipeRepeatReader* Repeater
91     )
92 {
93     BOOLEAN queued;
94     PFX_DRIVER_GLOBALS fxDriverGlobals;
95 
96     queued = FALSE;
97     fxDriverGlobals = m_Pipe->GetDriverGlobals();
98 
99     if (m_Pipe->m_State == WdfIoTargetStarted && m_WorkItemQueued == FALSE) {
100         //
101         // No item queued, queue it up now.
102         //
103         DoTraceLevelMessage(
104             fxDriverGlobals, TRACE_LEVEL_INFORMATION, TRACINGIOTARGET,
105             "WDFUSBPIPE %p continuous reader queueing work item to recover "
106             "from failed allocation", m_Pipe->GetHandle());
107 
108         if (m_WorkItem->Enqueue(_FxUsbPipeRequestWorkItemThunk, Repeater)) {
109             m_WorkItemQueued = TRUE;
110             queued = TRUE;
111         }
112         else {
113             DoTraceLevelMessage(fxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO,
114                         "Could not Queue workitem");
115         }
116     }
117 
118     //
119     // We only want to queue the work item while the target is in the
120     // started state.  If it is not started, then we are no longer sending
121     // i/o and we should not queue the work item to try to restart.
122     //
123     if (FALSE == queued) {
124         DoTraceLevelMessage(
125             fxDriverGlobals, TRACE_LEVEL_INFORMATION, TRACINGIOTARGET,
126             "WDFUSBPIPE %p continuous reader not queueing work item,"
127             "WorkItemQueued = %d, target state %!WDF_IO_TARGET_STATE!",
128             m_Pipe->GetHandle(), m_WorkItemQueued, m_Pipe->m_State);
129     }
130 
131     return queued;
132 }
133 
134 ULONG
135 FxUsbPipeContinuousReader::ResubmitRepeater(
136     __in FxUsbPipeRepeatReader* Repeater,
137     __out NTSTATUS* Status
138     )
139 {
140     PFX_DRIVER_GLOBALS pFxDriverGlobals;
141     NTSTATUS status;
142     ULONG action;
143     KIRQL irql;
144 
145     action              = 0;
146     pFxDriverGlobals    = m_Pipe->GetDriverGlobals();
147     status              = STATUS_UNSUCCESSFUL;
148 
149     //
150     // Reformat and allocate any new needed buffers
151     //
152     status = FormatRepeater(Repeater);
153 
154     m_Pipe->Lock(&irql);
155 
156     //
157     // Do not re-submit repeaters if there is a queued/running work-item to
158     // reset pipe. Work-item will restart this repeater later.
159     // This check needs to be done after the FormatRepeater() call above to
160     // prevent a race condition where we are not detecting when the repeater
161     // is cancelled.
162     //
163     if (m_WorkItemQueued) {
164         //
165         // Return an error and no action flags to let the caller know that
166         // this request was not sent.
167         //
168         status = STATUS_CANCELLED;
169 
170         DoTraceLevelMessage(
171             pFxDriverGlobals, TRACE_LEVEL_INFORMATION, TRACINGIOTARGET,
172             "WDFUSBPIPE %p is being reset, continuous reader %p FxRequest %p"
173             " PIRP %p is deferred for later.",
174             m_Pipe->GetHandle(), Repeater, Repeater->Request,
175             Repeater->RequestIrp);
176     }
177     else if (NT_SUCCESS(status)) {
178         //
179         // Get ready to re-submit the repeater.
180         //
181         action = m_Pipe->SubmitLocked(
182             Repeater->Request,
183             NULL,
184             WDF_REQUEST_SEND_INTERNAL_OPTION_FAIL_ON_PEND
185             );
186 
187         if (action & SubmitSend) {
188             //
189             // Clear the event only if we are going to send the request
190             //
191             Repeater->ReadCompletedEvent.Clear();
192         }
193         else if (action & SubmitQueued) {
194             //
195             // Request got canceled asynchronously. The other thread is now
196             // responsible for calling its completion callback.
197             //
198             status = STATUS_CANCELLED;
199         }
200         else {
201             //
202             // Submit failed (which is expected when we are changing the target
203             // state or when the request is canceled).  It should always be an
204             // error.
205             //
206             status = Repeater->Request->GetFxIrp()->GetStatus();
207             ASSERT(!NT_SUCCESS(status));
208         }
209     }
210     else {
211         //
212         // Could not allocate a new buffer
213         //
214         Repeater->Request->GetFxIrp()->SetStatus(status);
215 
216         DoTraceLevelMessage(
217             pFxDriverGlobals, TRACE_LEVEL_INFORMATION, TRACINGIOTARGET,
218             "WDFUSBPIPE %p continuous reader, format failed, %!STATUS!, "
219             "repeater %p", m_Pipe->GetHandle(), status, Repeater);
220 
221         if (m_Pipe->m_State == WdfIoTargetStarted) {
222             m_NumFailedReaders++;
223             ASSERT(m_NumFailedReaders <= m_NumReaders);
224 
225             if (m_NumFailedReaders == m_NumReaders) {
226                 //
227                 // Queue a work item to clear problem.
228                 //
229                 QueueWorkItemLocked(Repeater);
230             }
231             else {
232                 DoTraceLevelMessage(
233                     pFxDriverGlobals, TRACE_LEVEL_INFORMATION, TRACINGIOTARGET,
234                     "WDFUSBPIPE %p continuous reader, buffer alloc failed, but "
235                     "there are %d readers left out of a max of %d",
236                     m_Pipe->GetHandle(), m_NumReaders - m_NumFailedReaders,
237                     m_NumReaders);
238 
239                 //
240                 // There are still other pending readers, just use those for
241                 // now.
242                 //
243                 DO_NOTHING();
244             }
245         }
246         else {
247             DoTraceLevelMessage(
248                 pFxDriverGlobals, TRACE_LEVEL_INFORMATION, TRACINGIOTARGET,
249                 "WDFUSBPIPE %p continuous reader, buffer alloc failed, but not "
250                 "in started state", m_Pipe->GetHandle());
251         }
252     }
253 
254     m_Pipe->Unlock(irql);
255 
256     *Status = status;
257 
258     return action;
259 }
260 
261 VOID
262 FxUsbPipeContinuousReader::_FxUsbPipeRequestComplete(
263     __in WDFREQUEST Request,
264     __in WDFIOTARGET Target,
265     __in PWDF_REQUEST_COMPLETION_PARAMS Params,
266     __in WDFCONTEXT Context
267     )
268 {
269     FxUsbPipeRepeatReader* pRepeater;
270     FxUsbPipeContinuousReader* pThis;
271     FxUsbPipe* pPipe;
272     NTSTATUS status;
273     ULONG action;
274     BOOLEAN readCompletedEventSet;
275 
276     UNREFERENCED_PARAMETER(Request);
277     UNREFERENCED_PARAMETER(Params);
278 
279     readCompletedEventSet = FALSE;
280     action = 0;
281     pRepeater = (FxUsbPipeRepeatReader*) Context;
282     pThis = (FxUsbPipeContinuousReader*) pRepeater->Parent;
283     pPipe = pThis->m_Pipe;
284 
285     status = pRepeater->Request->GetFxIrp()->GetStatus();
286 
287     if (NT_SUCCESS(status)) {
288         PWDF_USB_REQUEST_COMPLETION_PARAMS params;
289 
290         params  = pRepeater->Request->GetContext()->
291             m_CompletionParams.Parameters.Usb.Completion;
292 
293         pThis->m_ReadCompleteCallback((WDFUSBPIPE) Target,
294                                       params->Parameters.PipeRead.Buffer,
295                                       params->Parameters.PipeRead.Length,
296                                       pThis->m_ReadCompleteContext);
297 
298         //
299         // This will release the reference on the read memory and allocate a new
300         // one
301         //
302         action = pThis->ResubmitRepeater(pRepeater, &status);
303     }
304     else if (status != STATUS_CANCELLED) {
305         KIRQL irql;
306 
307         DoTraceLevelMessage(
308             pPipe->GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGIOTARGET,
309             "WDFUSBPIPE %p continuous reader FxRequest %p PIRP %p returned with "
310             "%!STATUS!", pPipe->GetHandle(), pRepeater->Request ,
311             pRepeater->RequestIrp, status);
312 
313 
314         pPipe->Lock(&irql);
315 
316         pRepeater->ReadCompletedEvent.Set();
317         readCompletedEventSet = TRUE;
318 
319         //
320         // Queue a work item to clear problem.
321         //
322         pThis->QueueWorkItemLocked(pRepeater);
323 
324         pPipe->Unlock(irql);
325 
326         ASSERT(!NT_SUCCESS(status));
327     }
328     else {
329         //
330         // I/O was cancelled, which means internally it was cancelled so don't
331         // do anything.
332         //
333         DoTraceLevelMessage(
334             pPipe->GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGIOTARGET,
335             "WDFUSBPIPE %p continuous reader %p FxRequest %p PIRP %p canceled",
336             pPipe->GetHandle(), pRepeater, pRepeater->Request , pRepeater->RequestIrp);
337 
338         DO_NOTHING();
339     }
340 
341     if (action & SubmitSend) {
342 
343         //
344         // We don't want to recurse on the same stack and overflow it.
345         // This is especially true if the device is pushing a lot of data and
346         // usb is completing everything within its dpc as soon as we send the
347         // read down.  Eventually on a chk build, we will be nailed for running
348         // in one DPC for too long.
349         //
350         // As a slower alternative, we could queue a work item and resubmit the
351         // read from there.
352         //
353 
354 #if (FX_CORE_MODE == FX_CORE_KERNEL_MODE)
355         BOOLEAN result;
356         result = KeInsertQueueDpc(&pRepeater->Dpc, NULL, NULL);
357 
358         //
359         // The DPC should never be currently queued when we try to queue it.
360         //
361         ASSERT(result != FALSE);
362 #else
363         pRepeater->m_ReadWorkItem.Enqueue((PMX_WORKITEM_ROUTINE)_ReadWorkItem, pRepeater);
364 #endif
365         UNREFERENCED_PARAMETER(status); //for fre build
366 
367     }
368     else if (action & SubmitQueued) {
369         //
370         // I/O got canceled asynchronously; the other thread is now
371         // responsible for re-invoking this completion routine.
372         //
373         ASSERT(STATUS_CANCELLED == status);
374 
375         DoTraceLevelMessage(
376             pPipe->GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGIOTARGET,
377             "WDFUSBPIPE %p continuous reader %p FxRequest %p PIRP %p got"
378             " asynchronously canceled",
379             pPipe->GetHandle(), pRepeater, pRepeater->Request ,
380             pRepeater->RequestIrp);
381 
382         DO_NOTHING();
383     }
384     else if (FALSE == readCompletedEventSet) {
385         ASSERT(!NT_SUCCESS(status));
386         //
387         // We are not sending the request and it is not queued so signal that
388         // it is done.
389         //
390         pRepeater->ReadCompletedEvent.Set();
391     }
392 }
393 
394 VOID
395 FxUsbPipeContinuousReader::FxUsbPipeRequestWorkItemHandler(
396     __in FxUsbPipeRepeatReader* FailedRepeater
397     )
398 {
399     FxUsbDevice* pDevice;
400     NTSTATUS status, failedStatus;
401     USBD_STATUS usbdStatus;
402     LONG i;
403     KIRQL irql;
404     BOOLEAN restart;
405     FxRequestContext* context;
406     PWDF_USB_REQUEST_COMPLETION_PARAMS usbCompletionParams;
407     PFX_DRIVER_GLOBALS pFxDriverGlobals;
408 
409     pFxDriverGlobals = m_Pipe->GetDriverGlobals();
410 
411     //
412     // Get failed info.
413     //
414     failedStatus = FailedRepeater->Request->GetStatus();
415 
416     //
417     // Context is allocated at config time and gets reused so context
418     // will never be NULL.
419     //
420     context = FailedRepeater->Request->GetContext();
421     usbCompletionParams = context->m_CompletionParams.Parameters.Usb.Completion;
422 
423     //
424     // In case FormatRepeater fails to allocate memory usbCompletionParams
425     // pointer is not set.
426     //
427     // usbCompletionParams are part of the context and
428     // not really allocated at the time of every Format but
429     // the pointer gets cleared by request->Reuse and gets set again by
430     // context->SetUsbType.
431     //
432     // In FormatRepeater, context->SetUsbType is skipped
433     // if a memory failure occurs before this step.
434     //
435     // Hence retrieve usbdStatus only when usbCompletionParams is set.
436     //
437     if (usbCompletionParams) {
438         usbdStatus = usbCompletionParams->UsbdStatus;
439     }
440     else {
441         //
442         // Set usbdStatus to success as we didn't receive a failure from
443         // USB stack.
444         //
445         // This path is reached during memory allocation failure. In such
446         // case failedStatus would already be set appropriately. (usbdStatus
447         // and failedStatus are passed to m_ReadersFailedCallback below.)
448         //
449         usbdStatus = STATUS_SUCCESS;
450     }
451 
452     //
453     // No read requests should be in progress when the framework calls the
454     // EvtUsbTargetPipeReadersFailed callback function. This is part of the
455     // contract so that the Driver doesn't need to bother with the
456     // Completion calllback while taking corrective action.
457     //
458     CancelRepeaters();
459     pDevice = m_Pipe->m_UsbDevice;
460 
461     if (m_ReadersFailedCallback != NULL) {
462         //
463         // Save the current thread object pointer. This value is
464         // used for not deadlocking when misbehaved drivers (< v1.9) call
465         // WdfIoTargetStop from EvtUsbTargetPipeReadersFailed callback
466         //
467         ASSERT(NULL == m_WorkItemThread);
468         m_WorkItemThread = Mx::MxGetCurrentThread();
469 
470         restart = m_ReadersFailedCallback(
471             (WDFUSBPIPE) m_Pipe->GetHandle(),
472             failedStatus,
473             usbdStatus
474             );
475 
476         m_WorkItemThread = NULL;
477     }
478     else {
479         //
480         // By default, we restart the readers
481         //
482         restart = TRUE;
483     }
484 
485     if (restart) {
486         status = pDevice->IsConnected();
487 
488         if (NT_SUCCESS(status)) {
489 
490             //
491             // for v1.9 or higher use the error recovery procedure prescribed
492             // by the USB team.
493             //
494             if (pFxDriverGlobals->IsVersionGreaterThanOrEqualTo(1,9)) {
495 
496                 if (pDevice->IsEnabled()) {
497                     //
498                     // Reset the pipe if port status is enabled
499                     //
500                     m_Pipe->Reset();
501                 }
502                 else {
503                     //
504                     // Reset the device if port status is disabled
505                     //
506                     status = pDevice->Reset();
507                 }
508             }
509             else {
510                 //
511                 // Reset the device if port status is disabled
512                 //
513                 status = pDevice->Reset();
514             }
515         }
516         else {
517             //
518             // if port status is disconnected we would get back
519             // a !NT_SUCCESS. This would mean that we would not
520             // send the readers again and treat it like a failed reader.
521             //
522             DO_NOTHING();
523         }
524 
525     }
526     else {
527         //
528         // By setting status to !NT_SUCCESS, we will not send the readers
529         // again and treat it like a failed reader.
530         //
531         status = STATUS_UNSUCCESSFUL;
532     }
533 
534     //
535     // Work item is no longer queued.  We set this before resubmitting the
536     // repeaters so that if they all complete and fail, we will requeue the
537     // work item.
538     //
539     m_Pipe->Lock(&irql);
540     m_WorkItemQueued = FALSE;
541     m_Pipe->Unlock(irql);
542 
543     if (NT_SUCCESS(status)) {
544         ULONG action;
545 
546         //
547         // Reset the count to zero.  This is safe since we stopped all the
548         // readers at the beginning of this function.
549         //
550         m_NumFailedReaders = 0;
551 
552         //
553         // restart the readers
554         //
555         for (i = 0; i < m_NumReaders; i++) {
556             FxUsbPipeRepeatReader* pRepeater;
557 
558             pRepeater = &m_Readers[i];
559 
560             action = ResubmitRepeater(pRepeater, &status);
561 
562             if (action & SubmitSend) {
563                 //
564                 // Ignore the return value because once we have sent the
565                 // request, we want all processing to be done in the
566                 // completion routine.
567                 //
568                 (void) pRepeater->Request->GetSubmitFxIrp()->CallDriver(m_Pipe->m_TargetDevice);
569             }
570         }
571     }
572 }
573 
574 VOID
575 FxUsbPipeContinuousReader::_FxUsbPipeRequestWorkItemThunk(
576     __in PVOID Context
577     )
578 /*
579     Only one work-item can be in-progress at any given time and
580     only one additional work-item can be queued at any given time.
581     This logic and m_WorkItemQueued makes this happen.
582 */
583 {
584     FxUsbPipeRepeatReader* pFailedRepeater;
585     FxUsbPipeContinuousReader* pThis;
586     FxUsbPipe* pPipe;
587     KIRQL irql;
588     BOOLEAN rerun, inprogress;
589 
590     pFailedRepeater = (FxUsbPipeRepeatReader*) Context;
591     pThis = (FxUsbPipeContinuousReader*) pFailedRepeater->Parent;
592     pPipe = pThis->m_Pipe;
593 
594     //
595     // Check if a work item is already in progress.
596     //
597     pPipe->Lock(&irql);
598     if (pThis->m_WorkItemFlags & FX_USB_WORKITEM_IN_PROGRESS) {
599         //
600         // Yes, just let the other thread re-run this logic.
601         //
602         inprogress = TRUE;
603 
604         ASSERT((pThis->m_WorkItemFlags & FX_USB_WORKITEM_RERUN) == 0);
605         pThis->m_WorkItemFlags |= FX_USB_WORKITEM_RERUN;
606 
607         ASSERT(NULL == pThis->m_WorkItemRerunContext);
608         pThis->m_WorkItemRerunContext = Context;
609     }
610     else {
611         //
612         // No, it not running.
613         //
614         inprogress = FALSE;
615 
616         pThis->m_WorkItemFlags |= FX_USB_WORKITEM_IN_PROGRESS;
617         ASSERT((pThis->m_WorkItemFlags & FX_USB_WORKITEM_RERUN) == 0);
618     }
619     pPipe->Unlock(irql);
620 
621     if (inprogress) {
622         return;
623     }
624 
625     //
626     // OK, this thread is responsible for running the work item logic.
627     //
628     do {
629         //
630         // Cleanup and restart the repeters.
631         //
632         pThis->FxUsbPipeRequestWorkItemHandler(pFailedRepeater);
633 
634         //
635         // Check if callback needs to be re-run.
636         //
637         pPipe->Lock(&irql);
638         if (pThis->m_WorkItemFlags & FX_USB_WORKITEM_RERUN) {
639             //
640             // Yes, a new work item was requested while it was already running.
641             //
642             rerun = TRUE;
643 
644             pThis->m_WorkItemFlags &= ~FX_USB_WORKITEM_RERUN;
645 
646             ASSERT(pThis->m_WorkItemRerunContext != NULL);
647             pFailedRepeater = (FxUsbPipeRepeatReader*)pThis->m_WorkItemRerunContext;
648             pThis->m_WorkItemRerunContext = NULL;
649 
650             ASSERT(pThis == (FxUsbPipeContinuousReader*)pFailedRepeater->Parent);
651         }
652         else {
653             //
654             // No, all done.
655             //
656             rerun = FALSE;
657 
658             ASSERT(pThis->m_WorkItemFlags & FX_USB_WORKITEM_IN_PROGRESS);
659             pThis->m_WorkItemFlags &= ~FX_USB_WORKITEM_IN_PROGRESS;
660 
661             ASSERT(NULL == pThis->m_WorkItemRerunContext);
662         }
663         pPipe->Unlock(irql);
664 
665     }
666     while (rerun);
667 }
668 
669 PVOID
670 FxUsbPipeContinuousReader::operator new(
671     __in size_t Size,
672     __in PFX_DRIVER_GLOBALS FxDriverGlobals,
673     __range(1, NUM_PENDING_READS_MAX) ULONG NumReaders
674     )
675 {
676     ASSERT(NumReaders >= 1);
677 
678     return FxPoolAllocate(
679         FxDriverGlobals,
680         NonPagedPool,
681         Size + (NumReaders-1) * sizeof(FxUsbPipeRepeatReader)
682         );
683 }
684 
685 _Must_inspect_result_
686 NTSTATUS
687 FxUsbPipeContinuousReader::FormatRepeater(
688     __in FxUsbPipeRepeatReader* Repeater
689     )
690 {
691     WDF_REQUEST_REUSE_PARAMS params;
692     FxRequestBuffer buf;
693     FxUsbPipeTransferContext* pContext;
694     FxMemoryObject* pMemory;
695     FxRequest* pRequest;
696     NTSTATUS status;
697 
698     pRequest = Repeater->Request;
699     //
700     // The repeater owns the request memory.  If there is a memory on the
701     // context, delete it now.  the memory will still be referencable since
702     // it will still have a reference against it until FormatTransferRequest is
703     // called or the request is freed and the context releases its references
704     //
705     DeleteMemory(pRequest);
706 
707     WDF_REQUEST_REUSE_PARAMS_INIT(&params, 0, STATUS_NOT_SUPPORTED);
708 
709     pRequest->Reuse(&params);
710 
711     //
712     // pMemory will be deleted when either
713     // a) The request completes
714     // or
715     // b) The continuous reader is destroyed and we delete the lookaside.  since
716     //    the lookaside is the parent object for pMemory, pMemory will be disposed
717     //    of when the parent is Disposed
718     //
719     status = m_Lookaside->Allocate(&pMemory);
720     if (!NT_SUCCESS(status)) {
721         FxRequestContext* pContext;
722 
723         pContext = pRequest->GetContext();
724         if (pContext != NULL ) {
725            pContext->m_RequestMemory = NULL;
726         }
727 
728         return STATUS_INSUFFICIENT_RESOURCES;
729     }
730 
731     RtlZeroMemory(pMemory->GetBuffer(), pMemory->GetBufferSize());
732 
733     buf.SetMemory(pMemory, &m_Offsets);
734 
735     status = m_Pipe->FormatTransferRequest(
736         pRequest,
737         &buf,
738         USBD_TRANSFER_DIRECTION_IN | USBD_SHORT_TRANSFER_OK
739         );
740 
741     if (!NT_SUCCESS(status)) {
742         //
743         // In the case of failure, the context in the request will delete the
744         // memory.  If there is no context, delete the memory here.
745         //
746         if (pRequest->GetContext() == NULL) {
747             //
748             // Use DeleteFromFailedCreate because the driver never saw the
749             // buffer, so they shouldn't be told about it going away.
750             //
751             pMemory->DeleteFromFailedCreate();
752         }
753 
754         return status;
755     }
756 
757     pContext = (FxUsbPipeTransferContext*) pRequest->GetContext();
758     pContext->SetUsbType(WdfUsbRequestTypePipeRead);
759     pContext->m_UsbParameters.Parameters.PipeRead.Buffer = (WDFMEMORY)
760         pMemory->GetObjectHandle();
761 
762     pRequest->SetCompletionRoutine(_FxUsbPipeRequestComplete, Repeater);
763     return status;
764 }
765 
766 
767 VOID
768 FxUsbPipeContinuousReader::CancelRepeaters(
769     VOID
770     )
771 {
772     LONG i;
773 
774     Mx::MxEnterCriticalRegion();
775 
776     for (i = 0; i < m_NumReaders; i++) {
777         m_Readers[i].Request->Cancel();
778         m_Pipe->GetDriverGlobals()->WaitForSignal(
779                 m_Readers[i].ReadCompletedEvent.GetSelfPointer(),
780                 "waiting for continuous reader to finish, WDFUSBPIPE",
781                 m_Pipe->GetHandle(),
782                 m_Pipe->GetDriverGlobals()->FxVerifierDbgWaitForSignalTimeoutInSec,
783                 WaitSignalBreakUnderVerifier);
784 
785     }
786 
787     Mx::MxLeaveCriticalRegion();
788     //
789     // Checking for IO Count <= 1 is not a good idea here because there could be always other IO
790     // besides that from the continous reader going on the Read Pipe.
791     //
792 }
793 
794 FxUsbPipeTransferContext::FxUsbPipeTransferContext(
795     __in FX_URB_TYPE FxUrbType
796     ) :
797     FxUsbRequestContext(FX_RCT_USB_PIPE_XFER)
798 {
799     m_UnlockPages = FALSE;
800     m_PartialMdl = NULL;
801     m_USBDHandle = NULL;
802 
803     if (FxUrbType == FxUrbTypeLegacy) {
804         m_Urb = &m_UrbLegacy;
805     }
806     else {
807         m_Urb = NULL;
808     }
809 
810 }
811 
812 FxUsbPipeTransferContext::~FxUsbPipeTransferContext(
813     VOID
814     )
815 {
816     if (m_Urb && (m_Urb != &m_UrbLegacy)) {
817         USBD_UrbFree(m_USBDHandle, (PURB)m_Urb);
818     }
819     m_Urb = NULL;
820     m_USBDHandle = NULL;
821 }
822 
823 __checkReturn
824 NTSTATUS
825 FxUsbPipeTransferContext::AllocateUrb(
826     __in USBD_HANDLE USBDHandle
827     )
828 {
829     NTSTATUS status;
830 
831     if (m_Urb) {
832         status = STATUS_INVALID_DEVICE_STATE;
833         goto Done;
834     }
835 
836     status = USBD_UrbAllocate(USBDHandle, (PURB*)&m_Urb);
837 
838     if (!NT_SUCCESS(status)) {
839         goto Done;
840     }
841 
842     m_USBDHandle = USBDHandle;
843 
844 Done:
845     return status;
846 }
847 
848 VOID
849 FxUsbPipeTransferContext::Dispose(
850     VOID
851     )
852 {
853     if (m_Urb && (m_Urb != &m_UrbLegacy)){
854         USBD_UrbFree(m_USBDHandle, (PURB) m_Urb);
855         m_Urb = NULL;
856         m_USBDHandle = NULL;
857     }
858 }
859 
860 VOID
861 FxUsbPipeTransferContext::ReleaseAndRestore(
862     __in FxRequestBase* Request
863     )
864 {
865 #if (FX_CORE_MODE == FX_CORE_KERNEL_MODE)
866     //
867     // Check now because Init will NULL out the field
868     //
869     if (m_PartialMdl != NULL) {
870         if (m_UnlockPages) {
871             MmUnlockPages(m_PartialMdl);
872             m_UnlockPages = FALSE;
873         }
874 
875         FxMdlFree(Request->GetDriverGlobals(), m_PartialMdl);
876         m_PartialMdl = NULL;
877     }
878 #endif
879     FxUsbRequestContext::ReleaseAndRestore(Request); // __super call
880 }
881 
882 VOID
883 FxUsbPipeTransferContext::CopyParameters(
884     __in FxRequestBase* Request
885     )
886 {
887     m_CompletionParams.IoStatus.Information = GetUrbTransferLength();
888 
889     //
890     // If both are at the same offset, we don't have to compare type for
891     // Read or Write
892     //
893     WDFCASSERT(FIELD_OFFSET(WDF_USB_REQUEST_COMPLETION_PARAMS,
894                             Parameters.PipeRead.Length) ==
895                FIELD_OFFSET(WDF_USB_REQUEST_COMPLETION_PARAMS,
896                             Parameters.PipeWrite.Length));
897 
898     m_UsbParameters.Parameters.PipeRead.Length = GetUrbTransferLength();
899     FxUsbRequestContext::CopyParameters(Request); // __super call
900 }
901 
902 VOID
903 FxUsbPipeTransferContext::SetUrbInfo(
904     __in USBD_PIPE_HANDLE PipeHandle,
905     __in ULONG TransferFlags
906     )
907 {
908     m_Urb->TransferFlags = TransferFlags;
909     m_Urb->PipeHandle = PipeHandle;
910 }
911 
912 USBD_STATUS
913 FxUsbPipeTransferContext::GetUsbdStatus(
914     VOID
915     )
916 {
917     return m_Urb->Hdr.Status;
918 }
919 
920 FxUsbUrbContext::FxUsbUrbContext(
921     VOID
922     ) :
923     FxUsbRequestContext(FX_RCT_USB_URB_REQUEST),
924     m_pUrb(NULL)
925 {
926 }
927 
928 USBD_STATUS
929 FxUsbUrbContext::GetUsbdStatus(
930     VOID
931     )
932 {
933     return m_pUrb == NULL ? 0 : m_pUrb->UrbHeader.Status;
934 }
935 
936 VOID
937 FxUsbUrbContext::StoreAndReferenceMemory(
938     __in FxRequestBuffer* Buffer
939     )
940 {
941     ULONG dummy;
942 
943     FxUsbRequestContext::StoreAndReferenceMemory(Buffer);
944 
945     //
946     // make sure it is framework managed memory or raw PVOID
947     //
948     ASSERT(Buffer->DataType == FxRequestBufferMemory ||
949            Buffer->DataType == FxRequestBufferBuffer);
950 
951     Buffer->AssignValues((PVOID*) &m_pUrb, NULL, &dummy);
952 }
953 
954 VOID
955 FxUsbUrbContext::ReleaseAndRestore(
956     __in FxRequestBase* Request
957     )
958 {
959     m_pUrb = NULL;
960     FxUsbRequestContext::ReleaseAndRestore(Request); // __super call
961 }
962 
963 
964 FxUsbPipeRequestContext::FxUsbPipeRequestContext(
965     __in FX_URB_TYPE FxUrbType
966     ) :
967     FxUsbRequestContext(FX_RCT_USB_PIPE_REQUEST)
968 {
969     m_USBDHandle = NULL;
970 
971     if (FxUrbType == FxUrbTypeLegacy) {
972         m_Urb = &m_UrbLegacy;
973     }
974     else {
975         m_Urb = NULL;
976     }
977 }
978 
979 FxUsbPipeRequestContext::~FxUsbPipeRequestContext(
980     VOID
981     )
982 {
983     if (m_Urb && (m_Urb != &m_UrbLegacy)) {
984         USBD_UrbFree(m_USBDHandle, (PURB)m_Urb);
985     }
986     m_Urb = NULL;
987     m_USBDHandle = NULL;
988 }
989 
990 __checkReturn
991 NTSTATUS
992 FxUsbPipeRequestContext::AllocateUrb(
993     __in USBD_HANDLE USBDHandle
994     )
995 {
996     NTSTATUS status;
997 
998     if (m_Urb) {
999         status = STATUS_INVALID_DEVICE_STATE;
1000         goto Done;
1001     }
1002 
1003     status = USBD_UrbAllocate(USBDHandle, (PURB*)&m_Urb);
1004 
1005     if (!NT_SUCCESS(status)) {
1006         goto Done;
1007     }
1008 
1009     m_USBDHandle = USBDHandle;
1010 
1011 Done:
1012     return status;
1013 }
1014 
1015 VOID
1016 FxUsbPipeRequestContext::Dispose(
1017     VOID
1018     )
1019 {
1020     if (m_Urb && (m_Urb != &m_UrbLegacy)){
1021         USBD_UrbFree(m_USBDHandle, (PURB) m_Urb);
1022         m_Urb = NULL;
1023         m_USBDHandle = NULL;
1024     }
1025 }
1026 
1027 VOID
1028 FxUsbPipeRequestContext::SetInfo(
1029     __in WDF_USB_REQUEST_TYPE Type,
1030     __in USBD_PIPE_HANDLE PipeHandle,
1031     __in USHORT Function
1032     )
1033 {
1034     RtlZeroMemory(m_Urb, sizeof(*m_Urb));
1035     m_Urb->Hdr.Length = sizeof(*m_Urb);
1036     m_Urb->Hdr.Function = Function;
1037     m_Urb->PipeHandle = PipeHandle;
1038 
1039     SetUsbType(Type);
1040 }
1041 
1042 USBD_STATUS
1043 FxUsbPipeRequestContext::GetUsbdStatus(
1044     VOID
1045     )
1046 {
1047     return m_Urb->Hdr.Status;
1048 }
1049 
1050 FxUsbPipe::FxUsbPipe(
1051     __in PFX_DRIVER_GLOBALS FxDriverGlobals,
1052     __in FxUsbDevice* UsbDevice
1053     ) :
1054     FxIoTarget(FxDriverGlobals, sizeof(FxUsbPipe), FX_TYPE_IO_TARGET_USB_PIPE),
1055     m_UsbDevice(UsbDevice)
1056 {
1057     InitializeListHead(&m_ListEntry);
1058     RtlZeroMemory(&m_PipeInformation, sizeof(m_PipeInformation));
1059 #if (FX_CORE_MODE == FX_CORE_USER_MODE)
1060     RtlZeroMemory(&m_PipeInformationUm, sizeof(m_PipeInformationUm));
1061 #endif
1062     m_InterfaceNumber = 0;
1063     m_Reader = NULL;
1064     m_UsbInterface = NULL;
1065     m_CheckPacketSize = TRUE;
1066     m_USBDHandle = UsbDevice->m_USBDHandle;
1067     m_UrbType = UsbDevice->m_UrbType;
1068 
1069     MarkNoDeleteDDI(ObjectDoNotLock);
1070 }
1071 
1072 VOID
1073 FxUsbPipe::InitPipe(
1074     __in PUSBD_PIPE_INFORMATION PipeInfo,
1075     __in UCHAR InterfaceNumber,
1076     __in FxUsbInterface* UsbInterface
1077     )
1078 {
1079     RtlCopyMemory(&m_PipeInformation, PipeInfo, sizeof(m_PipeInformation));
1080     m_InterfaceNumber = InterfaceNumber;
1081 
1082     if (m_UsbInterface != NULL) {
1083         m_UsbInterface->RELEASE(this);
1084         m_UsbInterface = NULL;
1085     }
1086 
1087     m_UsbInterface = UsbInterface;
1088     m_UsbInterface->ADDREF(this);
1089 }
1090 
1091 FxUsbPipe::~FxUsbPipe()
1092 {
1093     if (m_UsbInterface != NULL) {
1094         m_UsbInterface->RemoveDeletedPipe(this);
1095         m_UsbInterface->RELEASE(this);
1096     }
1097 
1098     ASSERT(IsListEmpty(&m_ListEntry));
1099 }
1100 
1101 BOOLEAN
1102 FxUsbPipe::Dispose()
1103 {
1104     BOOLEAN callCleanup;
1105 
1106     //
1107     // Call base class: callbacks, terminates I/Os, etc.
1108     //
1109     callCleanup = FxIoTarget::Dispose(); // __super call
1110 
1111     //
1112     // Don't need the reader anymore. The reader is deleted after calling the
1113     // parent class Dispose() to preserve the existing deletion order (it was
1114     // deleted in the Pipe's dtor() before this change).
1115     //
1116     if (m_Reader != NULL)
1117     {
1118         delete m_Reader;
1119 
1120         //
1121         // By doing this assignment we prevent misbehaved drivers
1122         // from crashing the system when they call WdfIoTargetStop from their
1123         // usb pipe's destroy callback.
1124         //
1125         m_Reader = NULL;
1126     }
1127 
1128     return callCleanup;
1129 }
1130 
1131 _Must_inspect_result_
1132 NTSTATUS
1133 FxUsbPipe::GotoStartState(
1134     __in PLIST_ENTRY    RequestListHead,
1135     __in BOOLEAN        Lock
1136     )
1137 {
1138     NTSTATUS status;
1139     LONG i;
1140 
1141     if (m_Reader != NULL) {
1142         if (m_Reader->m_ReadersSubmitted == FALSE) {
1143             ASSERT(IsListEmpty(&m_SentIoListHead));
1144 
1145             for (i = 0; i < m_Reader->m_NumReaders; i++) {
1146                 FxRequest* pRequest;
1147 
1148                 pRequest = m_Reader->m_Readers[i].Request;
1149 
1150                 UNREFERENCED_PARAMETER(pRequest); //for fre build
1151                 ASSERT(IsListEmpty(&pRequest->m_ListEntry));
1152                 ASSERT(pRequest->m_DrainSingleEntry.Next == NULL);
1153             }
1154         }
1155     }
1156 
1157     status = FxIoTarget::GotoStartState(RequestListHead, Lock);
1158 
1159     if (m_Reader == NULL || !NT_SUCCESS(status)) {
1160         return status;
1161     }
1162 
1163     //
1164     // Add the repeater requests to the list head so that they are sent by the
1165     // caller of this function when this function returns IFF they have not yet
1166     // been queued.  (They can be queued on a start -> start transition.)
1167     //
1168     if (m_Reader->m_ReadersSubmitted == FALSE) {
1169         for (i = 0; i < m_Reader->m_NumReaders; i++) {
1170             //
1171             // This will clear ReadCompletedEvent as well
1172             //
1173             status = m_Reader->FormatRepeater(&m_Reader->m_Readers[i]);
1174 
1175             if (!NT_SUCCESS(status)) {
1176                 return status;
1177             }
1178         }
1179 
1180         //
1181         // Reset the number of failed readers in case we had failure in a
1182         // previously started state.
1183         //
1184         m_Reader->m_NumFailedReaders = 0;
1185 
1186         for (i = 0; i < m_Reader->m_NumReaders; i++) {
1187             FxRequest* pRequest;
1188 
1189             pRequest = m_Reader->m_Readers[i].Request;
1190             pRequest->SetTarget(this);
1191             pRequest->ADDREF(this);
1192 
1193             //
1194             // NOTE: This is an elusive backdoor to send the Request down
1195             // since it is inserted directly into the IoTargets pended list.
1196             // The IoTarget is not started so we add the request to the
1197             // pended list so that it is processed when the IoTarget starts.
1198             //
1199             m_Reader->m_Pipe->IncrementIoCount();
1200             InsertTailList(RequestListHead, &pRequest->m_ListEntry);
1201 
1202             //
1203             // Clear the event only when we know it will be submitted to the
1204             // target.  It will be set when  the request is submitted to the
1205             // target and the submit fails or if it is cancelled.
1206             //
1207             m_Reader->m_Readers[i].ReadCompletedEvent.Clear();
1208         }
1209 
1210         m_Reader->m_ReadersSubmitted = TRUE;
1211     }
1212 
1213     return status;
1214 }
1215 
1216 VOID
1217 FxUsbPipe::GotoStopState(
1218     __in WDF_IO_TARGET_SENT_IO_ACTION   Action,
1219     __in PSINGLE_LIST_ENTRY             SentRequestListHead,
1220     __out PBOOLEAN                      Wait,
1221     __in BOOLEAN                        LockSelf
1222     )
1223 {
1224     KIRQL irql;
1225     PFX_DRIVER_GLOBALS pFxDriverGlobals;
1226 
1227     irql = PASSIVE_LEVEL;
1228     pFxDriverGlobals = GetDriverGlobals();
1229 
1230     if (LockSelf) {
1231         Lock(&irql);
1232     }
1233 
1234     if (m_Reader != NULL) {
1235         //
1236         // If we are a continuous reader, always cancel the sent io so that we
1237         // can resubmit it later on a restart.
1238         //
1239         DoTraceLevelMessage(
1240             pFxDriverGlobals, TRACE_LEVEL_INFORMATION, TRACINGIOTARGET,
1241             "WDFUSBPIPE %p converting stop action %!WDF_IO_TARGET_SENT_IO_ACTION!"
1242             " to %!WDF_IO_TARGET_SENT_IO_ACTION!", GetHandle(), Action,
1243             WdfIoTargetCancelSentIo);
1244 
1245         Action = WdfIoTargetCancelSentIo;
1246     }
1247 
1248     FxIoTarget::GotoStopState(Action, SentRequestListHead, Wait, FALSE); // __super call
1249 
1250     if (m_Reader != NULL) {
1251         //
1252         // The continuous reader requests are no longer enqueued.  Remember that
1253         // state, so when we restart, we resend them.
1254         //
1255         m_Reader->m_ReadersSubmitted = FALSE;
1256 
1257         //
1258         // Log a message when misbehaved drivers call WdfIoTargetStop
1259         // from EvtUsbTargetPipeReadersFailed callback.
1260         //
1261         if (m_Reader->m_WorkItemThread == Mx::MxGetCurrentThread()) {
1262             DoTraceLevelMessage(
1263                 pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIOTARGET,
1264                 "WDFUSBPIPE %p is stopped from EvtUsbTargetPipeReadersFailed"
1265                 " callback", GetHandle());
1266 
1267             if (pFxDriverGlobals->IsVerificationEnabled(1, 9, OkForDownLevel)) {
1268                 FxVerifierDbgBreakPoint(pFxDriverGlobals);
1269             }
1270         }
1271 
1272         //
1273         // Do not deadlock when misbehaved drivers (< v1.9) call
1274         // WdfIoTargetStop from EvtUsbTargetPipeReadersFailed callback.
1275         //
1276         if (m_Reader->m_WorkItemThread != Mx::MxGetCurrentThread() ||
1277             pFxDriverGlobals->IsVersionGreaterThanOrEqualTo(1,9)) {
1278             //
1279             // Make sure work item is done. It is possible for the upper class
1280             // to return wait = false if the list of sent requests is empty. We
1281             // still want to wait anyway for making sure work item is not about
1282             // to run or it is running.
1283             //
1284             *Wait = TRUE;
1285         }
1286     }
1287 
1288     if (LockSelf) {
1289         Unlock(irql);
1290     }
1291 }
1292 
1293 VOID
1294 FxUsbPipe::GotoPurgeState(
1295     __in WDF_IO_TARGET_PURGE_IO_ACTION  Action,
1296     __in PLIST_ENTRY                    PendedRequestListHead,
1297     __in PSINGLE_LIST_ENTRY             SentRequestListHead,
1298     __out PBOOLEAN                      Wait,
1299     __in BOOLEAN                        LockSelf
1300     )
1301 {
1302     KIRQL irql;
1303     PFX_DRIVER_GLOBALS pFxDriverGlobals;
1304 
1305     irql = PASSIVE_LEVEL;
1306     pFxDriverGlobals = GetDriverGlobals();
1307 
1308     if (LockSelf) {
1309         Lock(&irql);
1310     }
1311 
1312     if (m_Reader != NULL) {
1313         //
1314         // If we are a continuous reader, always wait for the sent io, so that we
1315         // can resubmit it later on a restart.
1316         //
1317         DoTraceLevelMessage(
1318             pFxDriverGlobals, TRACE_LEVEL_INFORMATION, TRACINGIOTARGET,
1319             "WDFUSBPIPE %p converting purge action %!WDF_IO_TARGET_PURGE_IO_ACTION!"
1320             " to %!WDF_IO_TARGET_PURGE_IO_ACTION!", GetHandle(), Action,
1321             WdfIoTargetPurgeIoAndWait);
1322 
1323         Action = WdfIoTargetPurgeIoAndWait;
1324     }
1325 
1326     FxIoTarget::GotoPurgeState(Action, // __super call
1327                             PendedRequestListHead,
1328                             SentRequestListHead,
1329                             Wait,
1330                             FALSE);
1331 
1332     if (m_Reader != NULL) {
1333         //
1334         // The continuous reader requests are no longer enqueued.  Remember that
1335         // state, so when we restart, we resend them.
1336         //
1337         m_Reader->m_ReadersSubmitted = FALSE;
1338 
1339         //
1340         // Log a message when misbehaved drivers call WdfIoTargetPurge
1341         // from EvtUsbTargetPipeReadersFailed callback.
1342         //
1343         if (m_Reader->m_WorkItemThread == Mx::MxGetCurrentThread()) {
1344             DoTraceLevelMessage(
1345                 pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIOTARGET,
1346                 "WDFUSBPIPE %p is purged from EvtUsbTargetPipeReadersFailed"
1347                 " callback", GetHandle());
1348 
1349             FxVerifierDbgBreakPoint(pFxDriverGlobals);
1350         }
1351 
1352         //
1353         // Make sure work item is done. It is possible for the upper class
1354         // to return wait = false if the list of sent requests is empty. We
1355         // still want to wait anyway for making sure work item is not about
1356         // to run or it is running.
1357         //
1358         *Wait = TRUE;
1359     }
1360 
1361     if (LockSelf) {
1362         Unlock(irql);
1363     }
1364 }
1365 
1366 VOID
1367 FxUsbPipe::GotoRemoveState(
1368     __in WDF_IO_TARGET_STATE    NewState,
1369     __in PLIST_ENTRY            PendedRequestListHead,
1370     __in PSINGLE_LIST_ENTRY     SentRequestListHead,
1371     __in BOOLEAN                LockSelf,
1372     __out PBOOLEAN              Wait
1373     )
1374 {
1375     KIRQL irql;
1376 
1377     irql = PASSIVE_LEVEL;
1378 
1379     if (LockSelf) {
1380         Lock(&irql);
1381     }
1382 
1383     if (m_Reader != NULL && m_Reader->m_ReadersSubmitted &&
1384         WdfIoTargetStarted == m_State) {
1385         //
1386         // Driver forgot to stop the pipe on D0Exit.
1387         //
1388         DoTraceLevelMessage(
1389             GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
1390             "WDFUSBPIPE %p was not stopped in EvtDeviceD0Exit callback",
1391             GetHandle());
1392 
1393         if (GetDriverGlobals()->IsVerificationEnabled(1,9,OkForDownLevel)) {
1394             FxVerifierDbgBreakPoint(GetDriverGlobals());
1395         }
1396     }
1397 
1398     FxIoTarget::GotoRemoveState(NewState, // __super call
1399                              PendedRequestListHead,
1400                              SentRequestListHead,
1401                              FALSE,
1402                              Wait);
1403     if (m_Reader != NULL) {
1404         //
1405         // Make sure work item is done. It is possible for the upper class to
1406         // return wait = false if the list of sent requests is empty. We still
1407         // want to wait anyway for making sure work item is not about to run or
1408         // it is running.
1409         //
1410         *Wait = TRUE;
1411     }
1412 
1413     if (LockSelf) {
1414         Unlock(irql);
1415     }
1416 }
1417 
1418 VOID
1419 FxUsbPipe::WaitForSentIoToComplete(
1420     VOID
1421     )
1422 {
1423     if (m_Reader != NULL) {
1424         DoTraceLevelMessage(
1425             GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGIOTARGET,
1426             "WDFUSBPIPE %p, waiting for continuous reader work item to complete",
1427             GetHandle());
1428 
1429         //
1430         // First, wait for the work item to complete if it is running.
1431         //
1432         // NOTE: We don't wait for the DPC  to complete because
1433         //  they are flushed in FxUsbDevice::Dispose
1434         //
1435         m_Reader->m_WorkItem->WaitForExit();
1436 
1437         DoTraceLevelMessage(
1438             GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGIOTARGET,
1439             "WDFUSBPIPE %p, cancelling for continuous reader (max of %d)",
1440             GetHandle(), m_Reader->m_NumReaders);
1441 
1442         //
1443         // Now that the work item is not running, make sure all the readers are
1444         // truly canceled and *NOT* in the pended queue.  In between the call to
1445         // GotoStopState and here, the work item could have run and retried to
1446         // send the I/O.
1447         //
1448         m_Reader->CancelRepeaters();
1449     }
1450 
1451     DoTraceLevelMessage(
1452         GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGIOTARGET,
1453         "WDFUSBPIPE %p, waiting for all i/o to complete", GetHandle());
1454 
1455     //
1456     // Finally, let the parent class wait for all I/O to complete
1457     //
1458     FxIoTarget::WaitForSentIoToComplete(); // __super call
1459 }
1460 
1461 _Must_inspect_result_
1462 NTSTATUS
1463 FxUsbPipe::InitContinuousReader(
1464     __in PWDF_USB_CONTINUOUS_READER_CONFIG Config,
1465     __in size_t TotalBufferLength
1466     )
1467 {
1468     FxUsbPipeContinuousReader* pReader;
1469     NTSTATUS status;
1470     UCHAR numReaders;
1471 
1472     pReader = NULL;
1473 
1474     if (m_Reader != NULL) {
1475         status = STATUS_INVALID_DEVICE_STATE;
1476 
1477         DoTraceLevelMessage(
1478             GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
1479             "Continuous reader already initialized on WDFUSBPIPE %p %!STATUS!",
1480             GetHandle(), status);
1481 
1482         return status;
1483     }
1484 
1485     numReaders = Config->NumPendingReads;
1486 
1487     if (numReaders == 0) {
1488         numReaders = NUM_PENDING_READS_DEFAULT;
1489     }
1490     else if (numReaders > NUM_PENDING_READS_MAX) {
1491         numReaders = NUM_PENDING_READS_MAX;
1492     }
1493 
1494     pReader = new(GetDriverGlobals(), numReaders)
1495         FxUsbPipeContinuousReader(this, numReaders);
1496 
1497     if (pReader == NULL) {
1498         return STATUS_INSUFFICIENT_RESOURCES;
1499     }
1500 
1501     //
1502     // Allocate all of the structurs and objects required
1503     //
1504     status = pReader->Config(Config, TotalBufferLength);
1505 
1506     if (!NT_SUCCESS(status)) {
1507         delete pReader;
1508         return status;
1509     }
1510 
1511     pReader->m_ReadCompleteCallback = Config->EvtUsbTargetPipeReadComplete;
1512     pReader->m_ReadCompleteContext = Config->EvtUsbTargetPipeReadCompleteContext;
1513 
1514     pReader->m_ReadersFailedCallback = Config->EvtUsbTargetPipeReadersFailed;
1515 
1516     if (InterlockedCompareExchangePointer((PVOID*) &m_Reader,
1517                                           pReader,
1518                                           NULL) == NULL) {
1519         //
1520         // We set the field, do nothing.
1521         //
1522         DO_NOTHING();
1523     }
1524     else {
1525         //
1526         // Some other thread came in and set the field, free our allocation.
1527         //
1528         delete pReader;
1529     }
1530 
1531     return STATUS_SUCCESS;
1532 }
1533 
1534 _Must_inspect_result_
1535 NTSTATUS
1536 FxUsbPipe::_FormatTransfer(
1537     __in  PFX_DRIVER_GLOBALS FxDriverGlobals,
1538     __in WDFUSBPIPE Pipe,
1539     __in WDFREQUEST Request,
1540     __in_opt WDFMEMORY TransferMemory,
1541     __in_opt PWDFMEMORY_OFFSET TransferOffsets,
1542     __in ULONG Flags
1543     )
1544 {
1545     FxRequestBuffer buf;
1546     IFxMemory* pMemory;
1547     FxUsbPipe* pUsbPipe;
1548     FxRequest* pRequest;
1549     NTSTATUS status;
1550 
1551     FxObjectHandleGetPtrAndGlobals(FxDriverGlobals,
1552                                    Pipe,
1553                                    FX_TYPE_IO_TARGET_USB_PIPE,
1554                                    (PVOID*) &pUsbPipe,
1555                                    &FxDriverGlobals);
1556 
1557     FxObjectHandleGetPtr(FxDriverGlobals,
1558                          Request,
1559                          FX_TYPE_REQUEST,
1560                          (PVOID*) &pRequest);
1561 
1562     //
1563     // We allow zero length transfers (which are indicated by TransferMemory == NULL)
1564     //
1565     if (TransferMemory != NULL) {
1566         FxObjectHandleGetPtr(FxDriverGlobals,
1567                              TransferMemory,
1568                              IFX_TYPE_MEMORY,
1569                              (PVOID*) &pMemory);
1570 
1571         status = pMemory->ValidateMemoryOffsets(TransferOffsets);
1572         if (!NT_SUCCESS(status)) {
1573             goto Done;
1574         }
1575 
1576         buf.SetMemory(pMemory, TransferOffsets);
1577     }
1578     else {
1579         pMemory = NULL;
1580     }
1581 
1582     status = pUsbPipe->FormatTransferRequest(pRequest,  &buf, Flags);
1583 
1584     if (NT_SUCCESS(status)) {
1585         FxUsbPipeTransferContext* pContext;
1586 
1587         pContext = (FxUsbPipeTransferContext*) pRequest->GetContext();
1588 
1589         //
1590         // By assuming the fields are at the same offset, we can use simpler
1591         // logic (w/out comparisons for type) to set them.
1592         //
1593         WDFCASSERT(
1594             FIELD_OFFSET(WDF_USB_REQUEST_COMPLETION_PARAMS, Parameters.PipeWrite.Buffer) ==
1595             FIELD_OFFSET(WDF_USB_REQUEST_COMPLETION_PARAMS, Parameters.PipeRead.Buffer)
1596             );
1597 
1598         WDFCASSERT(
1599             FIELD_OFFSET(WDF_USB_REQUEST_COMPLETION_PARAMS, Parameters.PipeWrite.Offset) ==
1600             FIELD_OFFSET(WDF_USB_REQUEST_COMPLETION_PARAMS, Parameters.PipeRead.Offset)
1601             );
1602 
1603         pContext->m_UsbParameters.Parameters.PipeWrite.Buffer = TransferMemory;
1604         pContext->m_UsbParameters.Parameters.PipeWrite.Length = buf.GetBufferLength();
1605 
1606         pContext->m_UsbParameters.Parameters.PipeWrite.Offset =
1607             (TransferOffsets != NULL) ? TransferOffsets->BufferOffset
1608                                       : 0;
1609         pContext->SetUsbType(
1610             (Flags & USBD_TRANSFER_DIRECTION_IN) ? WdfUsbRequestTypePipeRead
1611                                                  : WdfUsbRequestTypePipeWrite
1612             );
1613     }
1614 
1615 Done:
1616     DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIOTARGET,
1617                         "WDFUSBPIPE %p, WDFREQUEST %p, WDFMEMORY %p, %!STATUS!",
1618                         Pipe, Request, TransferMemory, status);
1619 
1620     return status;
1621 }
1622 
1623 _Must_inspect_result_
1624 NTSTATUS
1625 FxUsbPipe::_SendTransfer(
1626     __in PFX_DRIVER_GLOBALS FxDriverGlobals,
1627     __in WDFUSBPIPE Pipe,
1628     __in_opt WDFREQUEST Request,
1629     __in_opt PWDF_REQUEST_SEND_OPTIONS RequestOptions,
1630     __in_opt PWDF_MEMORY_DESCRIPTOR MemoryDescriptor,
1631     __out_opt PULONG BytesTransferred,
1632     __in ULONG Flags
1633     )
1634 {
1635     FxRequestBuffer buf;
1636     FxUsbPipe* pUsbPipe;
1637     NTSTATUS status;
1638 
1639     FxObjectHandleGetPtrAndGlobals(FxDriverGlobals,
1640                                    Pipe,
1641                                    FX_TYPE_IO_TARGET_USB_PIPE,
1642                                    (PVOID*) &pUsbPipe,
1643                                    &FxDriverGlobals);
1644 
1645     FxUsbPipeTransferContext context(FxUrbTypeLegacy);
1646 
1647     FxSyncRequest request(FxDriverGlobals, &context, Request);
1648 
1649     //
1650     // FxSyncRequest always succeesds for KM but can fail for UM.
1651     //
1652     status = request.Initialize();
1653     if (!NT_SUCCESS(status)) {
1654         DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIOTARGET,
1655                             "Failed to initialize FxSyncRequest");
1656         return status;
1657     }
1658 
1659     if (BytesTransferred != NULL) {
1660         *BytesTransferred = 0;
1661     }
1662 
1663     status = FxVerifierCheckIrqlLevel(FxDriverGlobals, PASSIVE_LEVEL);
1664     if (!NT_SUCCESS(status)) {
1665         return status;
1666     }
1667 
1668     status = FxValidateRequestOptions(FxDriverGlobals, RequestOptions);
1669     if (!NT_SUCCESS(status)) {
1670         return status;
1671     }
1672 
1673     //
1674     // We allow zero length writes (which are indicated by MemoryDescriptor == NULL)
1675     //
1676     if (MemoryDescriptor != NULL) {
1677         status = buf.ValidateMemoryDescriptor(FxDriverGlobals, MemoryDescriptor);
1678         if (!NT_SUCCESS(status)) {
1679             return status;
1680         }
1681     }
1682 
1683     status = pUsbPipe->FormatTransferRequest(request.m_TrueRequest, &buf, Flags);
1684 
1685     if (NT_SUCCESS(status)) {
1686         DoTraceLevelMessage(
1687             FxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIOTARGET,
1688             "WDFUSBPIPE %p, WDFREQUEST %p being submitted",
1689             Pipe, request.m_TrueRequest->GetTraceObjectHandle());
1690 
1691         status = pUsbPipe->SubmitSync(request.m_TrueRequest, RequestOptions);
1692 
1693         //
1694         // Even on error we want to set this value.  USBD should be clearing
1695         // it if the transfer fails.
1696         //
1697         if (BytesTransferred != NULL) {
1698             *BytesTransferred = context.GetUrbTransferLength();
1699         }
1700     }
1701 
1702     DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIOTARGET,
1703                         "WDFUSBPIPE %p, %!STATUS!", Pipe, status);
1704 
1705     return status;
1706 }
1707 
1708 _Must_inspect_result_
1709 NTSTATUS
1710 FxUsbPipe::FormatAbortRequest(
1711     __in FxRequestBase* Request
1712     )
1713 {
1714     FxUsbPipeRequestContext* pContext;
1715     NTSTATUS status;
1716     FX_URB_TYPE urbType;
1717 
1718     status = Request->ValidateTarget(this);
1719     if (!NT_SUCCESS(status)) {
1720         DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
1721                             "Pipe %p, Request %p, setting target failed, "
1722                             "status %!STATUS!", this, Request, status);
1723 
1724         return status;
1725     }
1726 
1727     if (Request->HasContextType(FX_RCT_USB_PIPE_REQUEST)) {
1728         pContext = (FxUsbPipeRequestContext*) Request->GetContext();
1729     }
1730     else {
1731 
1732         urbType = m_UsbDevice->GetFxUrbTypeForRequest(Request);
1733         pContext = new(GetDriverGlobals()) FxUsbPipeRequestContext(urbType);
1734         if (pContext == NULL) {
1735             return STATUS_INSUFFICIENT_RESOURCES;
1736         }
1737 
1738 #if (FX_CORE_MODE == FX_CORE_KERNEL_MODE)
1739         if (urbType == FxUrbTypeUsbdAllocated) {
1740             status = pContext->AllocateUrb(m_USBDHandle);
1741             if (!NT_SUCCESS(status)) {
1742                 delete pContext;
1743                 return status;
1744             }
1745             //
1746             // Since the AllocateUrb routine calls USBD_xxxUrbAllocate APIs to allocate an Urb, it is
1747             // important to release those resorces before the devnode is removed. Those
1748             // resoruces are removed at the time Request is disposed.
1749             //
1750             Request->EnableContextDisposeNotification();
1751         }
1752 #endif
1753 
1754         Request->SetContext(pContext);
1755     }
1756 
1757 #if (FX_CORE_MODE == FX_CORE_KERNEL_MODE)
1758     pContext->SetInfo(WdfUsbRequestTypePipeAbort,
1759                       m_PipeInformation.PipeHandle,
1760                       URB_FUNCTION_ABORT_PIPE);
1761 
1762     if (pContext->m_Urb == &pContext->m_UrbLegacy) {
1763         urbType = FxUrbTypeLegacy;
1764     }
1765     else {
1766         urbType = FxUrbTypeUsbdAllocated;
1767     }
1768 
1769     FxFormatUsbRequest(Request, (PURB)pContext->m_Urb, urbType, m_USBDHandle);
1770 #elif (FX_CORE_MODE == FX_CORE_USER_MODE)
1771     pContext->SetInfo(WdfUsbRequestTypePipeAbort,
1772                       m_UsbInterface->m_WinUsbHandle,
1773                       m_PipeInformationUm.PipeId,
1774                       UMURB_FUNCTION_ABORT_PIPE);
1775     FxUsbUmFormatRequest(Request, &pContext->m_UmUrb.UmUrbPipeRequest.Hdr, m_UsbDevice->m_pHostTargetFile);
1776 #endif
1777 
1778     return STATUS_SUCCESS;
1779 }
1780 
1781 _Must_inspect_result_
1782 NTSTATUS
1783 FxUsbPipe::FormatResetRequest(
1784     __in FxRequestBase* Request
1785     )
1786 {
1787     FxUsbPipeRequestContext* pContext;
1788     NTSTATUS status;
1789     FX_URB_TYPE urbType;
1790 
1791     status = Request->ValidateTarget(this);
1792     if (!NT_SUCCESS(status)) {
1793         DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
1794                             "Pipe %p, Request %p, setting target failed, "
1795                             "status %!STATUS!", this, Request, status);
1796 
1797         return status;
1798     }
1799 
1800     if (Request->HasContextType(FX_RCT_USB_PIPE_REQUEST)) {
1801         pContext = (FxUsbPipeRequestContext*) Request->GetContext();
1802     }
1803     else {
1804         urbType = m_UsbDevice->GetFxUrbTypeForRequest(Request);
1805         pContext = new(GetDriverGlobals()) FxUsbPipeRequestContext(urbType);
1806         if (pContext == NULL) {
1807             return STATUS_INSUFFICIENT_RESOURCES;
1808         }
1809 
1810 #if (FX_CORE_MODE == FX_CORE_KERNEL_MODE)
1811         if (urbType == FxUrbTypeUsbdAllocated) {
1812             status = pContext->AllocateUrb(m_USBDHandle);
1813             if (!NT_SUCCESS(status)) {
1814                 delete pContext;
1815                 return status;
1816             }
1817             //
1818             // Since the AllocateUrb routine calls USBD_xxxUrbAllocate APIs to allocate an Urb, it is
1819             // important to release those resorces before the devnode is removed. Those
1820             // resoruces are removed at the time Request is disposed.
1821             //
1822             Request->EnableContextDisposeNotification();
1823         }
1824 #endif
1825 
1826         Request->SetContext(pContext);
1827     }
1828 
1829 #if (FX_CORE_MODE == FX_CORE_KERNEL_MODE)
1830     //
1831     // URB_FUNCTION_RESET_PIPE and URB_FUNCTION_SYNC_RESET_PIPE_AND_CLEAR_STALL
1832     // are the same value
1833     //
1834     pContext->SetInfo(WdfUsbRequestTypePipeReset,
1835                       m_PipeInformation.PipeHandle,
1836                       URB_FUNCTION_RESET_PIPE);
1837 
1838     if (pContext->m_Urb == &pContext->m_UrbLegacy) {
1839         urbType = FxUrbTypeLegacy;
1840     }
1841     else {
1842         urbType = FxUrbTypeUsbdAllocated;
1843     }
1844 
1845     FxFormatUsbRequest(Request, (PURB)pContext->m_Urb, urbType, m_USBDHandle);
1846 #elif (FX_CORE_MODE == FX_CORE_USER_MODE)
1847     pContext->SetInfo(WdfUsbRequestTypePipeReset,
1848                       m_UsbInterface->m_WinUsbHandle,
1849                       m_PipeInformationUm.PipeId,
1850                       UMURB_FUNCTION_SYNC_RESET_PIPE_AND_CLEAR_STALL);
1851     FxUsbUmFormatRequest(Request, &pContext->m_UmUrb.UmUrbPipeRequest.Hdr, m_UsbDevice->m_pHostTargetFile);
1852 #endif
1853 
1854     return STATUS_SUCCESS;
1855 }
1856 
1857 NTSTATUS
1858 FxUsbPipe::Reset(
1859     VOID
1860     )
1861 {
1862     FxUsbPipeRequestContext context(FxUrbTypeLegacy);
1863 
1864     FxSyncRequest request(GetDriverGlobals(), &context);
1865     NTSTATUS status;
1866 
1867     //
1868     // FxSyncRequest always succeesds for KM but can fail for UM.
1869     //
1870     status = request.Initialize();
1871     if (!NT_SUCCESS(status)) {
1872         DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
1873                             "Failed to initialize FxSyncRequest");
1874         return status;
1875     }
1876 
1877     status = FormatResetRequest(request.m_TrueRequest);
1878     if (NT_SUCCESS(status)) {
1879         if (m_Reader != NULL) {
1880             //
1881             // This assumes that no other I/O besides reader I/O is going on.
1882             //
1883             m_Reader->CancelRepeaters();
1884         }
1885         else {
1886              CancelSentIo();
1887         }
1888         status = SubmitSyncRequestIgnoreTargetState(request.m_TrueRequest, NULL);
1889     }
1890     return status;
1891 }
1892 
1893