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
FxUsbPipeContinuousReader(__in FxUsbPipe * Pipe,__in UCHAR NumReaders)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
~FxUsbPipeContinuousReader()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
QueueWorkItemLocked(__in FxUsbPipeRepeatReader * Repeater)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
ResubmitRepeater(__in FxUsbPipeRepeatReader * Repeater,__out NTSTATUS * Status)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
_FxUsbPipeRequestComplete(__in WDFREQUEST Request,__in WDFIOTARGET Target,__in PWDF_REQUEST_COMPLETION_PARAMS Params,__in WDFCONTEXT Context)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
FxUsbPipeRequestWorkItemHandler(__in FxUsbPipeRepeatReader * FailedRepeater)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
_FxUsbPipeRequestWorkItemThunk(__in PVOID Context)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
FormatRepeater(__in FxUsbPipeRepeatReader * Repeater)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(¶ms, 0, STATUS_NOT_SUPPORTED);
708
709 pRequest->Reuse(¶ms);
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
CancelRepeaters(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
FxUsbPipeTransferContext(__in FX_URB_TYPE FxUrbType)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
~FxUsbPipeTransferContext(VOID)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
AllocateUrb(__in USBD_HANDLE USBDHandle)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
Dispose(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
ReleaseAndRestore(__in FxRequestBase * Request)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
CopyParameters(__in FxRequestBase * Request)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
SetUrbInfo(__in USBD_PIPE_HANDLE PipeHandle,__in ULONG TransferFlags)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
GetUsbdStatus(VOID)913 FxUsbPipeTransferContext::GetUsbdStatus(
914 VOID
915 )
916 {
917 return m_Urb->Hdr.Status;
918 }
919
FxUsbUrbContext(VOID)920 FxUsbUrbContext::FxUsbUrbContext(
921 VOID
922 ) :
923 FxUsbRequestContext(FX_RCT_USB_URB_REQUEST),
924 m_pUrb(NULL)
925 {
926 }
927
928 USBD_STATUS
GetUsbdStatus(VOID)929 FxUsbUrbContext::GetUsbdStatus(
930 VOID
931 )
932 {
933 return m_pUrb == NULL ? 0 : m_pUrb->UrbHeader.Status;
934 }
935
936 VOID
StoreAndReferenceMemory(__in FxRequestBuffer * Buffer)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
ReleaseAndRestore(__in FxRequestBase * Request)955 FxUsbUrbContext::ReleaseAndRestore(
956 __in FxRequestBase* Request
957 )
958 {
959 m_pUrb = NULL;
960 FxUsbRequestContext::ReleaseAndRestore(Request); // __super call
961 }
962
963
FxUsbPipeRequestContext(__in FX_URB_TYPE FxUrbType)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
~FxUsbPipeRequestContext(VOID)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
AllocateUrb(__in USBD_HANDLE USBDHandle)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
Dispose(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
SetInfo(__in WDF_USB_REQUEST_TYPE Type,__in USBD_PIPE_HANDLE PipeHandle,__in USHORT Function)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
GetUsbdStatus(VOID)1043 FxUsbPipeRequestContext::GetUsbdStatus(
1044 VOID
1045 )
1046 {
1047 return m_Urb->Hdr.Status;
1048 }
1049
FxUsbPipe(__in PFX_DRIVER_GLOBALS FxDriverGlobals,__in FxUsbDevice * UsbDevice)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
InitPipe(__in PUSBD_PIPE_INFORMATION PipeInfo,__in UCHAR InterfaceNumber,__in FxUsbInterface * UsbInterface)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
~FxUsbPipe()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
Dispose()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
GotoStartState(__in PLIST_ENTRY RequestListHead,__in BOOLEAN Lock)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
GotoStopState(__in WDF_IO_TARGET_SENT_IO_ACTION Action,__in PSINGLE_LIST_ENTRY SentRequestListHead,__out PBOOLEAN Wait,__in BOOLEAN LockSelf)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
GotoPurgeState(__in WDF_IO_TARGET_PURGE_IO_ACTION Action,__in PLIST_ENTRY PendedRequestListHead,__in PSINGLE_LIST_ENTRY SentRequestListHead,__out PBOOLEAN Wait,__in BOOLEAN LockSelf)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
GotoRemoveState(__in WDF_IO_TARGET_STATE NewState,__in PLIST_ENTRY PendedRequestListHead,__in PSINGLE_LIST_ENTRY SentRequestListHead,__in BOOLEAN LockSelf,__out PBOOLEAN Wait)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
WaitForSentIoToComplete(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
InitContinuousReader(__in PWDF_USB_CONTINUOUS_READER_CONFIG Config,__in size_t TotalBufferLength)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
_FormatTransfer(__in PFX_DRIVER_GLOBALS FxDriverGlobals,__in WDFUSBPIPE Pipe,__in WDFREQUEST Request,__in_opt WDFMEMORY TransferMemory,__in_opt PWDFMEMORY_OFFSET TransferOffsets,__in ULONG Flags)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
_SendTransfer(__in PFX_DRIVER_GLOBALS FxDriverGlobals,__in WDFUSBPIPE Pipe,__in_opt WDFREQUEST Request,__in_opt PWDF_REQUEST_SEND_OPTIONS RequestOptions,__in_opt PWDF_MEMORY_DESCRIPTOR MemoryDescriptor,__out_opt PULONG BytesTransferred,__in ULONG Flags)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
FormatAbortRequest(__in FxRequestBase * Request)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
FormatResetRequest(__in FxRequestBase * Request)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
Reset(VOID)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