1 /*++
2 
3 Copyright (c) Microsoft Corporation
4 
5 Module Name:
6 
7     FxUsbDevice.cpp
8 
9 Abstract:
10 
11 Author:
12 
13 Environment:
14 
15     kernel mode only
16 
17 Revision History:
18 
19 --*/
20 
21 extern "C" {
22 #include <initguid.h>
23 }
24 
25 #include "fxusbpch.hpp"
26 
27 
28 extern "C" {
29 #include "FxUsbDevice.tmh"
30 }
31 
32 #if (FX_CORE_MODE == FX_CORE_KERNEL_MODE)
33 #define UCHAR_MAX (0xff)
34 #endif
35 
FxUsbDeviceControlContext(__in FX_URB_TYPE FxUrbType)36 FxUsbDeviceControlContext::FxUsbDeviceControlContext(
37     __in FX_URB_TYPE FxUrbType
38     ) :
39     FxUsbRequestContext(FX_RCT_USB_CONTROL_REQUEST)
40 {
41     m_PartialMdl = NULL;
42     m_UnlockPages = FALSE;
43     m_USBDHandle = NULL;
44 
45     if (FxUrbType == FxUrbTypeLegacy) {
46         m_Urb = &m_UrbLegacy;
47     }
48     else {
49         m_Urb = NULL;
50     }
51 }
52 
~FxUsbDeviceControlContext(VOID)53 FxUsbDeviceControlContext::~FxUsbDeviceControlContext(
54     VOID
55     )
56 {
57     if (m_Urb && (m_Urb != &m_UrbLegacy)) {
58         USBD_UrbFree(m_USBDHandle, (PURB)m_Urb);
59     }
60     m_Urb = NULL;
61     m_USBDHandle = NULL;
62 }
63 
64 __checkReturn
65 NTSTATUS
AllocateUrb(__in USBD_HANDLE USBDHandle)66 FxUsbDeviceControlContext::AllocateUrb(
67     __in USBD_HANDLE USBDHandle
68     )
69 {
70     NTSTATUS status;
71 
72     ASSERT(USBDHandle != NULL);
73     ASSERT(m_Urb == NULL);
74 
75     status = USBD_UrbAllocate(USBDHandle, (PURB*)&m_Urb);
76 
77     if (!NT_SUCCESS(status)) {
78         goto Done;
79     }
80 
81     m_USBDHandle = USBDHandle;
82 
83 Done:
84     return status;
85 }
86 
87 VOID
Dispose(VOID)88 FxUsbDeviceControlContext::Dispose(
89     VOID
90     )
91 {
92     if (m_Urb && (m_Urb != &m_UrbLegacy)){
93         USBD_UrbFree(m_USBDHandle, (PURB) m_Urb);
94         m_Urb = NULL;
95         m_USBDHandle = NULL;
96     }
97 }
98 
99 VOID
CopyParameters(__in FxRequestBase * Request)100 FxUsbDeviceControlContext::CopyParameters(
101     __in FxRequestBase* Request
102     )
103 {
104 #if (FX_CORE_MODE == FX_CORE_KERNEL_MODE)
105     m_CompletionParams.IoStatus.Information = m_Urb->TransferBufferLength;
106     m_UsbParameters.Parameters.DeviceControlTransfer.Length = m_Urb->TransferBufferLength;
107 #elif (FX_CORE_MODE == FX_CORE_USER_MODE)
108     m_CompletionParams.IoStatus.Information = m_UmUrb.UmUrbControlTransfer.TransferBufferLength;
109     m_UsbParameters.Parameters.DeviceControlTransfer.Length = m_UmUrb.UmUrbControlTransfer.TransferBufferLength;
110 #endif
111     FxUsbRequestContext::CopyParameters(Request); // __super call
112 }
113 
114 VOID
ReleaseAndRestore(__in FxRequestBase * Request)115 FxUsbDeviceControlContext::ReleaseAndRestore(
116     __in FxRequestBase* Request
117     )
118 {
119     //
120     // Check now because Init will NULL out the field
121     //
122     if (m_PartialMdl != NULL) {
123         if (m_UnlockPages) {
124             Mx::MxUnlockPages(m_PartialMdl);
125             m_UnlockPages = FALSE;
126         }
127 
128 #if (FX_CORE_MODE == FX_CORE_KERNEL_MODE)
129         FxMdlFree(Request->GetDriverGlobals(), m_PartialMdl);
130 #endif
131         m_PartialMdl = NULL;
132     }
133 
134     FxUsbRequestContext::ReleaseAndRestore(Request); // __super call
135 }
136 
137 USBD_STATUS
GetUsbdStatus(VOID)138 FxUsbDeviceControlContext::GetUsbdStatus(
139     VOID
140     )
141 {
142     return m_Urb->Hdr.Status;
143 }
144 
FxUsbDeviceStringContext(__in FX_URB_TYPE FxUrbType)145 FxUsbDeviceStringContext::FxUsbDeviceStringContext(
146     __in FX_URB_TYPE FxUrbType
147     ) :
148     FxUsbRequestContext(FX_RCT_USB_STRING_REQUEST)
149 {
150     m_USBDHandle = NULL;
151     m_StringDescriptor = NULL;
152     m_StringDescriptorLength = 0;
153     RtlZeroMemory(&m_UrbLegacy, sizeof(m_UrbLegacy));
154 
155     if (FxUrbType == FxUrbTypeLegacy) {
156         m_Urb = &m_UrbLegacy;
157         m_Urb->Hdr.Function =  URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE;
158         m_Urb->Hdr.Length = sizeof(*m_Urb);
159         m_Urb->DescriptorType = USB_STRING_DESCRIPTOR_TYPE;
160     }
161     else {
162         m_Urb = NULL;
163     }
164 }
165 
~FxUsbDeviceStringContext(VOID)166 FxUsbDeviceStringContext::~FxUsbDeviceStringContext(
167     VOID
168     )
169 {
170     if (m_StringDescriptor != NULL) {
171         FxPoolFree(m_StringDescriptor);
172         m_StringDescriptor = NULL;
173     }
174 
175     if (m_Urb && (m_Urb != &m_UrbLegacy)){
176         USBD_UrbFree(m_USBDHandle, (PURB) m_Urb);
177     }
178     m_Urb = NULL;
179     m_USBDHandle = NULL;
180 }
181 
182 __checkReturn
183 NTSTATUS
AllocateUrb(__in USBD_HANDLE USBDHandle)184 FxUsbDeviceStringContext::AllocateUrb(
185     __in USBD_HANDLE USBDHandle
186     )
187 {
188     NTSTATUS status;
189 
190     ASSERT(USBDHandle != NULL);
191     ASSERT(m_Urb == NULL);
192 
193     status = USBD_UrbAllocate(USBDHandle, (PURB*)&m_Urb);
194 
195     if (!NT_SUCCESS(status)) {
196         goto Done;
197     }
198 
199     m_USBDHandle = USBDHandle;
200 
201     m_Urb->Hdr.Function =  URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE;
202     m_Urb->Hdr.Length = sizeof(*m_Urb);
203     m_Urb->DescriptorType = USB_STRING_DESCRIPTOR_TYPE;
204 
205 Done:
206     return status;
207 }
208 
209 VOID
Dispose(VOID)210 FxUsbDeviceStringContext::Dispose(
211     VOID
212     )
213 {
214     if (m_Urb && (m_Urb != &m_UrbLegacy)){
215         USBD_UrbFree(m_USBDHandle, (PURB) m_Urb);
216         m_Urb = NULL;
217         m_USBDHandle = NULL;
218     }
219 
220 }
221 
222 VOID
CopyParameters(__in FxRequestBase * Request)223 FxUsbDeviceStringContext::CopyParameters(
224     __in FxRequestBase* Request
225     )
226 {
227     //
228     // Make sure we got an even number of bytes and that we got a header
229     //
230     if ((m_StringDescriptor->bLength & 0x1) ||
231         m_StringDescriptor->bLength < sizeof(USB_COMMON_DESCRIPTOR)) {
232         m_CompletionParams.IoStatus.Status = STATUS_DEVICE_DATA_ERROR;
233     }
234     else if (NT_SUCCESS(Request->GetSubmitFxIrp()->GetStatus())) {
235         //
236         // No matter what, indicate the required size to the caller
237         //
238         m_UsbParameters.Parameters.DeviceString.RequiredSize =
239             m_StringDescriptor->bLength - sizeof(USB_COMMON_DESCRIPTOR);
240 
241         if (m_UsbParameters.Parameters.DeviceString.RequiredSize >
242                                             m_RequestMemory->GetBufferSize()) {
243             //
244             // Too much string to fit into the buffer supplied by the client.
245             // Morph the status into a warning.  Copy as much as we can.
246             //
247             m_CompletionParams.IoStatus.Status = STATUS_BUFFER_OVERFLOW;
248             RtlCopyMemory(m_RequestMemory->GetBuffer(),
249                           &m_StringDescriptor->bString[0],
250                           m_RequestMemory->GetBufferSize());
251         }
252         else {
253             //
254             // Everything fits, copy it over
255             //
256             m_CompletionParams.IoStatus.Information =
257                 m_UsbParameters.Parameters.DeviceString.RequiredSize;
258 
259             RtlCopyMemory(m_RequestMemory->GetBuffer(),
260                           &m_StringDescriptor->bString[0],
261                           m_UsbParameters.Parameters.DeviceString.RequiredSize);
262         }
263     }
264 
265     FxUsbRequestContext::CopyParameters(Request); // __super call
266 }
267 
268 VOID
SetUrbInfo(__in UCHAR StringIndex,__in USHORT LangID)269 FxUsbDeviceStringContext::SetUrbInfo(
270     __in UCHAR StringIndex,
271     __in USHORT LangID
272     )
273 {
274     SetUsbType(WdfUsbRequestTypeDeviceString);
275 
276     ASSERT(m_StringDescriptor != NULL && m_StringDescriptorLength != 0);
277 
278 #if (FX_CORE_MODE == FX_CORE_KERNEL_MODE)
279     m_Urb->TransferBuffer = m_StringDescriptor;
280     m_Urb->TransferBufferLength = m_StringDescriptorLength;
281 
282     m_Urb->Index = StringIndex;
283     m_Urb->LanguageId = LangID;
284 #elif (FX_CORE_MODE == FX_CORE_USER_MODE)
285     m_UmUrb.UmUrbDescriptorRequest.Buffer = m_StringDescriptor;
286     m_UmUrb.UmUrbDescriptorRequest.BufferLength = m_StringDescriptorLength;
287 
288     m_UmUrb.UmUrbDescriptorRequest.Index = StringIndex;
289     m_UmUrb.UmUrbDescriptorRequest.LanguageID = LangID;
290 #endif
291 }
292 
293 USBD_STATUS
GetUsbdStatus(VOID)294 FxUsbDeviceStringContext::GetUsbdStatus(
295     VOID
296     )
297 {
298     return m_Urb->Hdr.Status;
299 }
300 
301 _Must_inspect_result_
302 NTSTATUS
AllocateDescriptor(__in PFX_DRIVER_GLOBALS FxDriverGlobals,__in size_t BufferSize)303 FxUsbDeviceStringContext::AllocateDescriptor(
304     __in PFX_DRIVER_GLOBALS FxDriverGlobals,
305     __in size_t BufferSize
306     )
307 {
308     PUSB_STRING_DESCRIPTOR pDescriptor;
309     size_t length;
310     NTSTATUS status;
311 
312     if (BufferSize <= m_StringDescriptorLength) {
313         return STATUS_SUCCESS;
314     }
315 
316     length = sizeof(USB_STRING_DESCRIPTOR) - sizeof(pDescriptor->bString[0]) +
317              BufferSize;
318 
319     pDescriptor = (PUSB_STRING_DESCRIPTOR) FxPoolAllocate(
320         FxDriverGlobals,
321         NonPagedPool,
322         length);
323 
324     if (pDescriptor == NULL) {
325         return STATUS_INSUFFICIENT_RESOURCES;
326     }
327 
328     if (m_StringDescriptor != NULL) {
329         FxPoolFree(m_StringDescriptor);
330     }
331 
332     RtlZeroMemory(pDescriptor, length);
333 
334     m_StringDescriptor = pDescriptor;
335 
336     status = RtlSizeTToULong(length, &m_StringDescriptorLength);
337 
338     return status;
339 }
340 
FxUsbUrb(__in PFX_DRIVER_GLOBALS FxDriverGlobals,__in USBD_HANDLE USBDHandle,__in_bcount (BufferSize)PVOID Buffer,__in size_t BufferSize)341 FxUsbUrb::FxUsbUrb(
342     __in PFX_DRIVER_GLOBALS FxDriverGlobals,
343     __in USBD_HANDLE USBDHandle,
344     __in_bcount(BufferSize) PVOID Buffer,
345     __in size_t BufferSize
346     ) :
347     FxMemoryBufferPreallocated(FxDriverGlobals, sizeof(*this), Buffer, BufferSize),
348     m_USBDHandle(USBDHandle)
349 {
350     MarkDisposeOverride();
351 }
352 
~FxUsbUrb()353 FxUsbUrb::~FxUsbUrb()
354 {
355 }
356 
357 BOOLEAN
Dispose(VOID)358 FxUsbUrb::Dispose(
359     VOID
360     )
361 {
362     ASSERT(m_USBDHandle != NULL);
363     ASSERT(m_pBuffer != NULL);
364     USBD_UrbFree(m_USBDHandle, (PURB)m_pBuffer);
365     m_pBuffer = NULL;
366     m_USBDHandle = NULL;
367 
368     return FxMemoryBufferPreallocated::Dispose(); // __super call
369 }
370 
FxUsbDevice(__in PFX_DRIVER_GLOBALS FxDriverGlobals)371 FxUsbDevice::FxUsbDevice(
372     __in PFX_DRIVER_GLOBALS FxDriverGlobals
373     ) :
374     FxIoTarget(FxDriverGlobals, sizeof(FxUsbDevice), FX_TYPE_IO_TARGET_USB_DEVICE)
375 {
376     RtlZeroMemory(&m_DeviceDescriptor, sizeof(m_DeviceDescriptor));
377     RtlZeroMemory(&m_UsbdVersionInformation, sizeof(m_UsbdVersionInformation));
378 
379     m_OnUSBD = FALSE;
380     m_Interfaces = NULL;;
381     m_NumInterfaces = 0;
382 
383     m_Traits = 0;
384     m_HcdPortCapabilities = 0;
385     m_ControlPipe = NULL;
386     m_QueryBusTime = NULL;
387     m_BusInterfaceContext = NULL;
388     m_BusInterfaceDereference = NULL;
389     m_ConfigHandle = NULL;
390     m_ConfigDescriptor = NULL;
391 
392     m_MismatchedInterfacesInConfigDescriptor = FALSE;
393 
394     m_USBDHandle = NULL;
395     m_UrbType = FxUrbTypeLegacy;
396 
397 #if (FX_CORE_MODE == FX_CORE_USER_MODE)
398     m_pHostTargetFile = NULL;
399     m_WinUsbHandle = NULL;
400 #endif
401 
402     MarkDisposeOverride(ObjectDoNotLock);
403 }
404 
405 BOOLEAN
Dispose(VOID)406 FxUsbDevice::Dispose(
407     VOID
408     )
409 {
410 #if ((FX_CORE_MODE)==(FX_CORE_KERNEL_MODE))
411     KeFlushQueuedDpcs();
412 #endif
413 
414     if (m_USBDHandle) {
415         USBD_CloseHandle(m_USBDHandle);
416         m_USBDHandle = NULL;
417     }
418 
419 #if (FX_CORE_MODE == FX_CORE_USER_MODE)
420     IWudfDevice* device = NULL;
421     IWudfDeviceStack* devstack = NULL;
422 
423     device = m_DeviceBase->GetDeviceObject();
424     devstack = device->GetDeviceStackInterface();
425 
426     if (m_pHostTargetFile) {
427         devstack->CloseFile(m_pHostTargetFile);
428         SAFE_RELEASE(m_pHostTargetFile);
429     }
430 #endif
431 
432     return FxIoTarget::Dispose(); // __super call
433 }
434 
~FxUsbDevice()435 FxUsbDevice::~FxUsbDevice()
436 {
437     UCHAR i;
438 
439     if (m_BusInterfaceDereference != NULL) {
440         m_BusInterfaceDereference(m_BusInterfaceContext);
441         m_BusInterfaceDereference = NULL;
442     }
443 
444     if (m_ConfigDescriptor != NULL) {
445         FxPoolFree(m_ConfigDescriptor);
446         m_ConfigDescriptor = NULL;
447     }
448 
449     for (i = 0; i < m_NumInterfaces; i++) {
450         ASSERT(m_Interfaces[i] == NULL);
451     }
452 
453     if (m_Interfaces != NULL){
454         FxPoolFree(m_Interfaces);
455         m_Interfaces = NULL;
456     }
457 
458     m_NumInterfaces = 0;
459 }
460 
461 VOID
RemoveDeletedInterface(__in FxUsbInterface * Interface)462 FxUsbDevice::RemoveDeletedInterface(
463     __in FxUsbInterface* Interface
464     )
465 {
466     UCHAR i;
467 
468     if (m_Interfaces == NULL) {
469         return;
470     }
471 
472     for (i = 0; i < m_NumInterfaces; i++) {
473         if (m_Interfaces[i] == Interface) {
474             m_Interfaces[i] = NULL;
475             return;
476         }
477     }
478 }
479 
480 VOID
GetInformation(__out PWDF_USB_DEVICE_INFORMATION Information)481 FxUsbDevice::GetInformation(
482     __out PWDF_USB_DEVICE_INFORMATION Information
483     )
484 {
485     Information->Traits = m_Traits;
486     Information->HcdPortCapabilities = m_HcdPortCapabilities;
487 
488     RtlCopyMemory(&Information->UsbdVersionInformation,
489                   &m_UsbdVersionInformation,
490                   sizeof(m_UsbdVersionInformation));
491 }
492 
493 ULONG
GetDefaultMaxTransferSize(VOID)494 FxUsbDevice::GetDefaultMaxTransferSize(
495     VOID
496     )
497 /*++
498 
499 Routine Description:
500     Determines the default max transfer size based on the usb host controller
501     and OS we are running on.  What it boils down to is that on XP and later
502     the usb core ignores the default max transfer size, but it does use the
503     value on Windows 2000.  To make life fun, there is only one definition of
504     USBD_DEFAULT_MAXIMUM_TRANSFER_SIZE whose value changes depending on header
505     versioning.  Since we are versioned for the latest OS, we do not pick up the
506     Win2k value by using the #define, rather we have to use the value that we
507     *would* have picked up if we were header versioned for Win2k.
508 
509     NOTE:  we could be on win2k with a usbport serviced stack.  in this case,
510            usbport doesn't care about max transfer sizes
511 
512 Arguments:
513     None
514 
515 Return Value:
516     usb core and OS appropriate default max transfer size.
517 
518   --*/
519 {
520     //
521     // On a usbport serviced stack (which can be running on Win2k) or on a
522     // usbd stack on XP and later.  In any case, always use the current max
523     // transfer size definition.
524     //
525     return USBD_DEFAULT_MAXIMUM_TRANSFER_SIZE;
526 }
527 
528 _Must_inspect_result_
529 NTSTATUS
Start(VOID)530 FxUsbDevice::Start(
531     VOID
532     )
533 {
534     NTSTATUS status;
535 
536     status = FxIoTarget::Start();
537 
538     if (NT_SUCCESS(status)) {
539         FxRequestBase* pRequest;
540         LIST_ENTRY head, *ple;
541         ULONG i, iInterface;
542         FxUsbInterface *pUsbInterface;
543         KIRQL irql;
544 
545         InitializeListHead(&head);
546 
547         Lock(&irql);
548 
549         //
550         // Iterate over all of the interfaces.  For each pipe on each interface,
551         // grab all pended i/o for later submission.
552         //
553         for (iInterface = 0; iInterface < m_NumInterfaces; iInterface++){
554 
555             pUsbInterface = m_Interfaces[iInterface];
556 
557             for (i = 0; i < pUsbInterface->m_NumberOfConfiguredPipes; i++) {
558                 pUsbInterface->m_ConfiguredPipes[i]->GotoStartState(&head);
559             }
560         }
561         Unlock(irql);
562 
563         //
564         // Since we are going to reference the FxUsbPipe's outside of the
565         // lock and the interface can go away in the meantime, add a reference
566         // (multiple times perhaps) to each FxUsbPipe to make sure it sticks
567         // around.
568         //
569         for (ple = head.Flink; ple != &head; ple = ple->Flink) {
570             pRequest = FxRequestBase::_FromListEntry(ple);
571             pRequest->GetTarget()->ADDREF(this);
572         }
573 
574         //
575         // Drain the list of pended requests.
576         //
577         while (!IsListEmpty(&head)) {
578             FxIoTarget* pTarget;
579 
580             ple = RemoveHeadList(&head);
581 
582             pRequest = FxRequestBase::_FromListEntry(ple);
583 
584             pTarget = pRequest->GetTarget();
585 
586             pTarget->SubmitPendedRequest(pRequest);
587 
588             //
589             // Release the reference taken above when accumulating pended i/o.
590             //
591             pTarget->RELEASE(this);
592         }
593     }
594 
595     return status;
596 }
597 
598 #define STOP_TAG (PVOID) 'pots'
599 
600 VOID
Stop(__in WDF_IO_TARGET_SENT_IO_ACTION Action)601 FxUsbDevice::Stop(
602     __in WDF_IO_TARGET_SENT_IO_ACTION Action
603     )
604 {
605     FxUsbInterface *pUsbInterface;
606     SINGLE_LIST_ENTRY head;
607     ULONG iPipe, iInterface;
608     KIRQL irql;
609 
610     head.Next = NULL;
611 
612     //
613     // Stop all of our own I/O first
614     //
615     FxIoTarget::Stop(Action);
616 
617     //
618     // if we are just canceling i/o, then we just acquire the spin lock b/c
619     // we can be called at dispatch level for this action code.
620     //
621     if (Action != WdfIoTargetLeaveSentIoPending) {
622         ASSERT(Mx::MxGetCurrentIrql() == PASSIVE_LEVEL);
623 
624         AcquireInterfaceIterationLock();
625     }
626 
627     //
628     // Since we don't have to synchronize on the I/O already sent, just set
629     // each pipe's state to stop.
630     //
631     Lock(&irql);
632 
633     for (iInterface = 0; iInterface < m_NumInterfaces; iInterface++) {
634         pUsbInterface = m_Interfaces[iInterface];
635 
636         if (pUsbInterface->m_ConfiguredPipes != NULL) {
637             for (iPipe = 0;
638                  iPipe < pUsbInterface->m_NumberOfConfiguredPipes;
639                  iPipe++) {
640 
641                 if (pUsbInterface->m_ConfiguredPipes[iPipe] != NULL) {
642                     BOOLEAN wait;
643 
644                     wait = FALSE;
645 
646                     pUsbInterface->m_ConfiguredPipes[iPipe]->GotoStopState(
647                         Action,
648                         &head,
649                         &wait,
650                         TRUE
651                         );
652                 }
653             }
654         }
655     }
656     Unlock(irql);
657 
658     //
659     // If we are leaving sent IO pending, the io target will set the IO
660     // completion event during stop even if there is still outstanding IO.
661     //
662 
663     if (head.Next != NULL) {
664         _CancelSentRequests(&head);
665     }
666 
667     for (iInterface = 0; iInterface < m_NumInterfaces;  iInterface++) {
668         pUsbInterface = m_Interfaces[iInterface];
669 
670         if (pUsbInterface->m_ConfiguredPipes != NULL) {
671             //
672             // Iterate over the pipes and clean each one up
673             //
674             for (iPipe = 0;
675                  iPipe < pUsbInterface->m_NumberOfConfiguredPipes;
676                  iPipe++) {
677                 //
678                 // Same reason as above
679                 //
680                 if (pUsbInterface->m_ConfiguredPipes[iPipe] != NULL) {
681                     pUsbInterface->m_ConfiguredPipes[iPipe]->
682                         WaitForSentIoToComplete();
683                 }
684             }
685         }
686     }
687 
688     if (Action != WdfIoTargetLeaveSentIoPending) {
689         ReleaseInterfaceIterationLock();
690     }
691 }
692 
693 VOID
Purge(__in WDF_IO_TARGET_PURGE_IO_ACTION Action)694 FxUsbDevice::Purge(
695     __in WDF_IO_TARGET_PURGE_IO_ACTION Action
696     )
697 {
698     FxUsbInterface      *pUsbInterface;
699     SINGLE_LIST_ENTRY   sentHead;
700     ULONG               iPipe, iInterface;
701     KIRQL               irql;
702 
703     sentHead.Next = NULL;
704 
705     //
706     // Purge all of our own I/O first
707     //
708     FxIoTarget::Purge(Action);
709 
710     //
711     // if we are just canceling i/o, then we just acquire the spin lock b/c
712     // we can be called at dispatch level for this action code.
713     //
714     if (Action != WdfIoTargetPurgeIo) {
715         ASSERT(Mx::MxGetCurrentIrql() == PASSIVE_LEVEL);
716 
717         AcquireInterfaceIterationLock();
718     }
719 
720     //
721     // Since we don't have to synchronize on the I/O already sent, just set
722     // each pipe's state to purged.
723     //
724     Lock(&irql);
725 
726     for (iInterface = 0; iInterface < m_NumInterfaces; iInterface++) {
727         pUsbInterface = m_Interfaces[iInterface];
728 
729         if (pUsbInterface->m_ConfiguredPipes != NULL) {
730             for (iPipe = 0;
731                  iPipe < pUsbInterface->m_NumberOfConfiguredPipes;
732                  iPipe++) {
733 
734                 if (pUsbInterface->m_ConfiguredPipes[iPipe] != NULL) {
735                     BOOLEAN wait;
736                     LIST_ENTRY pendedHead;
737 
738                     wait = FALSE;
739                     InitializeListHead(&pendedHead);
740 
741                     pUsbInterface->m_ConfiguredPipes[iPipe]->GotoPurgeState(
742                         Action,
743                         &pendedHead,
744                         &sentHead,
745                         &wait,
746                         TRUE
747                         );
748 
749                     //
750                     // Complete any requests pulled off from this pipe.
751                     //
752                     pUsbInterface->m_ConfiguredPipes[iPipe]->
753                         CompletePendedRequestList(&pendedHead);
754                 }
755             }
756         }
757     }
758     Unlock(irql);
759 
760     //
761     // Cancel all sent requests.
762     //
763     _CancelSentRequests(&sentHead);
764 
765     for (iInterface = 0; iInterface < m_NumInterfaces;  iInterface++) {
766         pUsbInterface = m_Interfaces[iInterface];
767 
768         if (pUsbInterface->m_ConfiguredPipes != NULL) {
769             //
770             // Iterate over the pipes and clean each one up
771             //
772             for (iPipe = 0;
773                  iPipe < pUsbInterface->m_NumberOfConfiguredPipes;
774                  iPipe++) {
775                 //
776                 // Same reason as above
777                 //
778                 if (pUsbInterface->m_ConfiguredPipes[iPipe] != NULL) {
779                     pUsbInterface->m_ConfiguredPipes[iPipe]->
780                         WaitForSentIoToComplete();
781                 }
782             }
783         }
784     }
785 
786     if (Action != WdfIoTargetPurgeIo) {
787         ReleaseInterfaceIterationLock();
788     }
789 }
790 
791 VOID
_CleanupPipesRequests(__in PLIST_ENTRY PendHead,__in PSINGLE_LIST_ENTRY SentHead)792 FxUsbDevice::_CleanupPipesRequests(
793     __in PLIST_ENTRY PendHead,
794     __in PSINGLE_LIST_ENTRY SentHead
795     )
796 {
797     while (!IsListEmpty(PendHead)) {
798         PLIST_ENTRY ple;
799         FxRequestBase* pRequest;
800 
801         ple = RemoveHeadList(PendHead);
802 
803         InitializeListHead(ple);
804 
805         pRequest = FxRequestBase::_FromListEntry(ple);
806         pRequest->GetTarget()->CompletePendedRequest(pRequest);
807     }
808 
809     _CancelSentRequests(SentHead);
810 }
811 
812 VOID
PipesGotoRemoveState(__in BOOLEAN ForceRemovePipes)813 FxUsbDevice::PipesGotoRemoveState(
814     __in BOOLEAN ForceRemovePipes
815     )
816 {
817     SINGLE_LIST_ENTRY sentHead;
818     LIST_ENTRY pendHead, interfaceHead;
819     FxUsbInterface* pUsbInterface;
820     ULONG iPipe, intfIndex;
821     KIRQL irql;
822 
823     sentHead.Next = NULL;
824     InitializeListHead(&pendHead);
825     InitializeListHead(&interfaceHead);
826 
827     AcquireInterfaceIterationLock();
828 
829     Lock(&irql);
830     for (intfIndex = 0; intfIndex < m_NumInterfaces;  intfIndex++  ) {
831         pUsbInterface = m_Interfaces[intfIndex];
832 
833         if (pUsbInterface->m_ConfiguredPipes != NULL) {
834             for (iPipe = 0;
835                  iPipe < pUsbInterface->m_NumberOfConfiguredPipes;
836                  iPipe++) {
837                 BOOLEAN wait;
838 
839                 wait = FALSE;
840 
841                 //
842                 // Pipe can be NULL if the interface is half initialized
843                 //
844                 if (pUsbInterface->m_ConfiguredPipes[iPipe] != NULL) {
845                     pUsbInterface->m_ConfiguredPipes[iPipe]->GotoRemoveState(
846                         WdfIoTargetDeleted,
847                         &pendHead,
848                         &sentHead,
849                         TRUE,
850                         &wait);
851 
852                 }
853             }
854         }
855     }
856     Unlock(irql);
857 
858     //
859     // We cleanup requests no matter what the new state is because we complete all
860     // pended requests in the surprise removed case.
861     //
862     _CleanupPipesRequests(&pendHead, &sentHead);
863 
864     //
865     // Only destroy child pipe objects when the parent is going away or the
866     // caller indicates that this is the desired action.
867     //
868     if (m_State == WdfIoTargetDeleted || ForceRemovePipes) {
869         for (intfIndex = 0; intfIndex < m_NumInterfaces;  intfIndex++) {
870             pUsbInterface = m_Interfaces[intfIndex];
871 
872             if (pUsbInterface->m_ConfiguredPipes != NULL) {
873                 //
874                 // Iterate over the pipes and clean each one up
875                 //
876                 for (iPipe = 0;
877                      iPipe < pUsbInterface->m_NumberOfConfiguredPipes;
878                      iPipe++) {
879                     //
880                     // Same reason as above
881                     //
882                     if (pUsbInterface->m_ConfiguredPipes[iPipe] != NULL) {
883                         pUsbInterface->m_ConfiguredPipes[iPipe]->
884                             WaitForSentIoToComplete();
885                     }
886                 }
887             }
888 
889             pUsbInterface->CleanUpAndDelete(FALSE);
890         }
891     }
892 
893     ReleaseInterfaceIterationLock();
894 }
895 
896 _Must_inspect_result_
897 NTSTATUS
CreateInterfaces(VOID)898 FxUsbDevice::CreateInterfaces(
899     VOID
900     )
901 {
902     PFX_DRIVER_GLOBALS pFxDriverGlobals;
903     UCHAR descCountBitMap[UCHAR_MAX / sizeof(UCHAR)];
904     PUSB_INTERFACE_DESCRIPTOR  pInterfaceDescriptor;
905     UCHAR iInterface, numFound;
906     NTSTATUS status;
907     ULONG size;
908     ULONG totalLength;
909 
910     pFxDriverGlobals = GetDriverGlobals();
911     status = STATUS_SUCCESS;
912     totalLength = m_ConfigDescriptor->wTotalLength;
913 
914     //
915     // Make sure each PCOMMON_DESCRIPTOR_HEADER within the entire config descriptor is well formed.
916     // If successful, we can walk the config descriptor using common headers without any more top
917     // level error checking. Task specific checking of the specialized header types must still occur.
918     //
919     status = FxUsbValidateConfigDescriptorHeaders(
920         pFxDriverGlobals,
921         m_ConfigDescriptor,
922         m_ConfigDescriptor->wTotalLength
923         );
924 
925     if (!NT_SUCCESS(status)) {
926         DoTraceLevelMessage(
927             pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIOTARGET,
928             "Validation of the config descriptor failed due to a bad common descriptor header, %!STATUS!",
929             status);
930         return status;
931     }
932 
933     //
934     // Validate all interface descriptors in config descriptor are at least
935     // sizeof(USB_INTERFACE_DESCRIPTOR).
936     //
937 
938 
939 
940 
941 
942     status =  FxUsbValidateDescriptorType(
943         pFxDriverGlobals,
944         m_ConfigDescriptor,
945         m_ConfigDescriptor,
946         WDF_PTR_ADD_OFFSET(m_ConfigDescriptor, m_ConfigDescriptor->wTotalLength),
947         USB_INTERFACE_DESCRIPTOR_TYPE,
948         sizeof(USB_INTERFACE_DESCRIPTOR),
949         FxUsbValidateDescriptorOpAtLeast,
950         0
951         );
952 
953     if (!NT_SUCCESS(status)) {
954         DoTraceLevelMessage(
955             pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIOTARGET,
956             "Validation of interface descriptors in config descriptor failed, %!STATUS!",
957             status);
958 
959         return status;
960     }
961 
962     if (m_ConfigDescriptor->bNumInterfaces == 0) {
963         //
964         // Use an array of one in the zero case
965         //
966         size = sizeof(FxUsbInterface*);
967     }
968     else {
969         size = sizeof(FxUsbInterface*) * m_ConfigDescriptor->bNumInterfaces;
970     }
971 
972     //
973     // Allocate an array large enough to hold pointers to interfaces
974     //
975     m_Interfaces = (FxUsbInterface**)
976         FxPoolAllocate(pFxDriverGlobals, NonPagedPool, size);
977 
978     if (m_Interfaces == NULL) {
979         status = STATUS_INSUFFICIENT_RESOURCES;
980 
981         DoTraceLevelMessage(
982             pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIOTARGET,
983             "Could not allocate memory for %d interfaces, %!STATUS!",
984             m_ConfigDescriptor->bNumInterfaces, status);
985 
986         goto Done;
987     }
988 
989     RtlZeroMemory(m_Interfaces, size);
990     m_NumInterfaces = m_ConfigDescriptor->bNumInterfaces;
991 
992     //
993     // Iterate over the desciptors again, this time allocating an FxUsbInterface
994     // for each one and capturing the interface information.
995     //
996     RtlZeroMemory(descCountBitMap, sizeof(descCountBitMap));
997     iInterface = 0;
998     numFound = 0;
999 
1000     pInterfaceDescriptor = (PUSB_INTERFACE_DESCRIPTOR) FxUsbFindDescriptorType(
1001         m_ConfigDescriptor,
1002         m_ConfigDescriptor->wTotalLength,
1003         m_ConfigDescriptor,
1004         USB_INTERFACE_DESCRIPTOR_TYPE
1005         );
1006 
1007     while (pInterfaceDescriptor != NULL &&
1008            iInterface < m_ConfigDescriptor->bNumInterfaces) {
1009 
1010         //
1011         // This function will retun false if the bit wasn't already set
1012         //
1013         if (FxBitArraySet(descCountBitMap,
1014                           pInterfaceDescriptor->bInterfaceNumber) == FALSE) {
1015             FxUsbInterface* pInterface;
1016 
1017             pInterface = new (GetDriverGlobals(), WDF_NO_OBJECT_ATTRIBUTES)
1018                 FxUsbInterface(pFxDriverGlobals,
1019                                this,
1020                                pInterfaceDescriptor);
1021 
1022             if (pInterface == NULL) {
1023                 status = STATUS_INSUFFICIENT_RESOURCES;
1024                 DoTraceLevelMessage(
1025                     GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
1026                     "Could not allocate memory for interface object #%d, %!STATUS!",
1027                     iInterface, status);
1028                 goto Done;
1029             }
1030 
1031             status = pInterface->Commit(WDF_NO_OBJECT_ATTRIBUTES, NULL, this);
1032 
1033             //
1034             // This should never fail
1035             //
1036             ASSERT(NT_SUCCESS(status));
1037 
1038             if (!NT_SUCCESS(status)) {
1039                 goto Done;
1040             }
1041 
1042             status = pInterface->CreateSettings();
1043             if (!NT_SUCCESS(status)) {
1044                 goto Done;
1045             }
1046 
1047 #if (FX_CORE_MODE == FX_CORE_USER_MODE)
1048             status = pInterface->SetWinUsbHandle(iInterface);
1049             if (!NT_SUCCESS(status)) {
1050                 goto Done;
1051             }
1052 
1053             status = pInterface->MakeAndConfigurePipes(WDF_NO_OBJECT_ATTRIBUTES,
1054                                                        pInterfaceDescriptor->bNumEndpoints);
1055             if (!NT_SUCCESS(status)) {
1056                 goto Done;
1057             }
1058 #endif
1059 
1060             m_Interfaces[iInterface] = pInterface;
1061 
1062             iInterface++;
1063         }
1064 
1065         pInterfaceDescriptor = (PUSB_INTERFACE_DESCRIPTOR) FxUsbFindDescriptorType(
1066             m_ConfigDescriptor,
1067             totalLength,
1068             WDF_PTR_ADD_OFFSET(pInterfaceDescriptor,
1069                                pInterfaceDescriptor->bLength),
1070             USB_INTERFACE_DESCRIPTOR_TYPE
1071             );
1072     }
1073 
1074     //
1075     // We cannot check for the error case of
1076     //
1077     // pInterfaceDescriptor != NULL &&
1078     //  iInterface == m_ConfigDescriptor->bNumInterfaces
1079     //
1080     // Because if there are multiple alternative settings for the last interface
1081     // in the config descriptor, we will have hit the limit of interfaces
1082     // (correctly), but have found another pInterfaceDescriptor (the next alt
1083     // setting).
1084     //
1085 
1086     //
1087     // We already logged and found the case where iInterface >= m_NumInterfaces.
1088     // Check for the case where we found too few interfaces and when we found
1089     // no interfaces even though the config descriptor says otherwise.
1090     //
1091     //
1092     if (iInterface == 0 && m_NumInterfaces > 0) {
1093         status = STATUS_INVALID_DEVICE_REQUEST;
1094 
1095         DoTraceLevelMessage(
1096             GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
1097             "Config descriptor indicated there were %d interfaces, but did not "
1098             "find any interface descriptors in config descriptor %p, %!STATUS!",
1099             m_NumInterfaces, m_ConfigDescriptor, status);
1100     }
1101     else if (pInterfaceDescriptor != NULL && m_NumInterfaces == 0) {
1102         DoTraceLevelMessage(
1103             GetDriverGlobals(), TRACE_LEVEL_WARNING, TRACINGIOTARGET,
1104             "Config descriptor indicated there were 0 interfaces, but an interface "
1105             "descriptor was found");
1106 
1107         m_MismatchedInterfacesInConfigDescriptor = TRUE;
1108     }
1109     else if (iInterface < m_NumInterfaces) {
1110         DoTraceLevelMessage(
1111             GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
1112             "Config descriptor indicated there were %d interfaces, only found "
1113             "%d interfaces", m_NumInterfaces, iInterface);
1114 
1115         //
1116         // Instead of considering this an error, just use the number found.
1117         // This will not have an adverse affect elsewhere and since the framework
1118         // is probably more strict then previous USB code, this would have not
1119         // been found earlier by a WDM driver.
1120         //
1121         m_NumInterfaces = iInterface;
1122     }
1123 
1124 Done:
1125     return status;
1126 }
1127 
1128 
1129 _Must_inspect_result_
1130 NTSTATUS
GetPortStatus(__out PULONG PortStatus)1131 FxUsbDevice::GetPortStatus(
1132     __out PULONG PortStatus
1133     )
1134 {
1135     NTSTATUS status;
1136 
1137     FxInternalIoctlOthersContext context;
1138     FxRequestBuffer args[FX_REQUEST_NUM_OTHER_PARAMS];
1139     FxSyncRequest syncRequest(GetDriverGlobals(), &context);
1140 
1141     //
1142     // FxSyncRequest always succeesds for KM.
1143     //
1144     status = syncRequest.Initialize();
1145     if (!NT_SUCCESS(status)) {
1146         DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
1147                             "Failed to initialize FxSyncRequest");
1148         return status;
1149     }
1150 
1151     *PortStatus = 0;
1152     args[0].SetBuffer(PortStatus, 0);
1153     args[1].SetBuffer(NULL, 0);
1154     args[2].SetBuffer(NULL, 0);
1155 
1156     status = FormatInternalIoctlOthersRequest(syncRequest.m_TrueRequest,
1157                                               IOCTL_INTERNAL_USB_GET_PORT_STATUS,
1158                                               args);
1159 
1160     if (NT_SUCCESS(status)) {
1161         WDF_REQUEST_SEND_OPTIONS options;
1162 
1163         WDF_REQUEST_SEND_OPTIONS_INIT(
1164             &options, WDF_REQUEST_SEND_OPTION_IGNORE_TARGET_STATE);
1165 
1166         status = SubmitSync(syncRequest.m_TrueRequest, &options);
1167     }
1168 
1169     return status;
1170 }
1171 
1172 
1173 
1174 BOOLEAN
IsEnabled(VOID)1175 FxUsbDevice::IsEnabled(
1176     VOID
1177     )
1178 {
1179     NTSTATUS status;
1180     ULONG portStatus;
1181     BOOLEAN enabled;
1182 
1183     enabled = TRUE;
1184     status = GetPortStatus(&portStatus);
1185 
1186     //
1187     // Inability to get STATUS_SUCCESS from GetPortStatus is more likely a resource
1188     // issue rather than a device issue so return FALSE from this function only if
1189     // we were able to read the PortStatus and the port was disabled.
1190     // What you don't want is to continuosly reset the device (by returning FALSE)
1191     // instead of resetting pipe (by returning TRUE) under low memory conditions.
1192     //
1193     if (NT_SUCCESS(status) && (portStatus & USBD_PORT_ENABLED) == 0) {
1194         enabled = FALSE;
1195     }
1196 
1197     return enabled;
1198 }
1199 
1200 _Must_inspect_result_
1201 NTSTATUS
IsConnected(VOID)1202 FxUsbDevice::IsConnected(
1203     VOID
1204     )
1205 {
1206 #if (FX_CORE_MODE == FX_CORE_USER_MODE)
1207 
1208 
1209 
1210 
1211     return STATUS_UNSUCCESSFUL;
1212 #elif (FX_CORE_MODE == FX_CORE_KERNEL_MODE)
1213     NTSTATUS status;
1214     ULONG portStatus;
1215 
1216     status = GetPortStatus(&portStatus);
1217     if (NT_SUCCESS(status) && (portStatus & USBD_PORT_CONNECTED) == 0) {
1218         status = STATUS_DEVICE_DOES_NOT_EXIST;
1219     }
1220 
1221     return status;
1222 #endif
1223 }
1224 
1225 _Must_inspect_result_
1226 NTSTATUS
CyclePort(VOID)1227 FxUsbDevice::CyclePort(
1228     VOID
1229     )
1230 {
1231     FxIoContext context;
1232     FxSyncRequest request(GetDriverGlobals(), &context);
1233     NTSTATUS status;
1234 
1235     //
1236     // FxSyncRequest always succeesds for KM.
1237     //
1238     status = request.Initialize();
1239     if (!NT_SUCCESS(status)) {
1240         DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
1241                             "Failed to initialize FxSyncRequest");
1242         return status;
1243     }
1244 
1245     status = FormatCycleRequest(request.m_TrueRequest);
1246 
1247     if (NT_SUCCESS(status)) {
1248         CancelSentIo();
1249         status = SubmitSyncRequestIgnoreTargetState(request.m_TrueRequest, NULL);
1250         //
1251         // NOTE: CyclePort causes the device to be removed and re-enumerated so
1252         // don't do anymore operations after this point.
1253         //
1254     }
1255 
1256     return status;
1257 }
1258 
1259 _Must_inspect_result_
1260 NTSTATUS
FormatCycleRequest(__in FxRequestBase * Request)1261 FxUsbDevice::FormatCycleRequest(
1262     __in FxRequestBase* Request
1263     )
1264 {
1265     FxRequestBuffer emptyBuffer;
1266 
1267     return FormatIoctlRequest(Request,
1268                               IOCTL_INTERNAL_USB_CYCLE_PORT,
1269                               TRUE,
1270                               &emptyBuffer,
1271                               &emptyBuffer);
1272 }
1273 
1274 _Must_inspect_result_
1275 NTSTATUS
GetConfigDescriptor(__out PVOID ConfigDescriptor,__inout PUSHORT ConfigDescriptorLength)1276 FxUsbDevice::GetConfigDescriptor(
1277     __out PVOID ConfigDescriptor,
1278     __inout PUSHORT ConfigDescriptorLength
1279     )
1280 {
1281     NTSTATUS status;
1282     USHORT copyLength;
1283 
1284     if (ConfigDescriptor == NULL) {
1285         //
1286         // Caller wants length to allocate
1287         //
1288         *ConfigDescriptorLength = m_ConfigDescriptor->wTotalLength;
1289         return STATUS_BUFFER_TOO_SMALL;
1290     }
1291 
1292     if (*ConfigDescriptorLength < m_ConfigDescriptor->wTotalLength) {
1293         //
1294         // Valid ConfigDescriptor passed in, but its too small.  Copy as many
1295         // bytes as we can.
1296         //
1297         copyLength = *ConfigDescriptorLength;
1298         status = STATUS_BUFFER_TOO_SMALL;
1299     }
1300     else {
1301         copyLength = m_ConfigDescriptor->wTotalLength;
1302         status = STATUS_SUCCESS;
1303     }
1304 
1305     //
1306     // Always indicate to the caller the number of required bytes or the
1307     // number of bytes we copied.
1308     //
1309     RtlCopyMemory(ConfigDescriptor, m_ConfigDescriptor, copyLength);
1310     *ConfigDescriptorLength = m_ConfigDescriptor->wTotalLength;
1311 
1312     return status;
1313 }
1314 
1315 _Must_inspect_result_
1316 NTSTATUS
SelectConfigDescriptor(__in PWDF_OBJECT_ATTRIBUTES PipesAttributes,__in PWDF_USB_DEVICE_SELECT_CONFIG_PARAMS Params)1317 FxUsbDevice::SelectConfigDescriptor(
1318     __in PWDF_OBJECT_ATTRIBUTES PipesAttributes,
1319     __in PWDF_USB_DEVICE_SELECT_CONFIG_PARAMS Params
1320     )
1321 {
1322     PUSBD_INTERFACE_LIST_ENTRY pInterfaces;
1323     PURB urb;
1324     NTSTATUS status;
1325     ULONG i, size;
1326     PUSB_CONFIGURATION_DESCRIPTOR configurationDescriptor;
1327     PUSB_INTERFACE_DESCRIPTOR* interfaceDescriptors;
1328     ULONG numInterfaces;
1329     PFX_DRIVER_GLOBALS  pFxDriverGlobals;
1330 
1331     pFxDriverGlobals = GetDriverGlobals();
1332 
1333     configurationDescriptor = Params->Types.Descriptor.ConfigurationDescriptor;
1334     interfaceDescriptors = Params->Types.Descriptor.InterfaceDescriptors;
1335     numInterfaces = Params->Types.Descriptor.NumInterfaceDescriptors;
1336 
1337     for (i = 0; i < numInterfaces; i++) {
1338         if (interfaceDescriptors[i] == NULL) {
1339             return STATUS_INVALID_PARAMETER;
1340         }
1341     }
1342 
1343     //
1344     // eventually size = sizeof(USBD_INTERFACE_LIST_ENTRY) * (numInterfaces + 1)
1345     //
1346     status = RtlULongAdd(numInterfaces, 1, &size);
1347     if (!NT_SUCCESS(status)) {
1348         return status;
1349     }
1350 
1351     status = RtlULongMult(size, sizeof(USBD_INTERFACE_LIST_ENTRY), &size);
1352     if (!NT_SUCCESS(status)) {
1353         return status;
1354     }
1355 
1356     pInterfaces = (PUSBD_INTERFACE_LIST_ENTRY) FxPoolAllocate(
1357         pFxDriverGlobals, NonPagedPool, size);
1358 
1359     if (pInterfaces == NULL) {
1360         status = STATUS_INSUFFICIENT_RESOURCES;
1361 
1362         DoTraceLevelMessage(
1363             GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
1364             "Could not allocate array of USBD_INTERFACE_LIST_ENTRY, %!STATUS!",
1365             status);
1366 
1367         return status;
1368     }
1369 
1370     RtlZeroMemory(pInterfaces, size);
1371 
1372     for (i = 0; i < numInterfaces; i++) {
1373         pInterfaces[i].InterfaceDescriptor = interfaceDescriptors[i];
1374     }
1375 
1376     if (configurationDescriptor == NULL) {
1377         configurationDescriptor = m_ConfigDescriptor;
1378     }
1379 
1380     //
1381     // NOTE:
1382     //
1383     // Creating a config request using the caller's config descriptor does not
1384     // currently work if the provided config descriptor is not the same as the
1385     // descriptor reported by the device.   It does not work because we try to
1386     // validate the interface number in the URB against an existing
1387     // FxUsbInterface (which is based on the config descriptor described by the
1388     // device, not the provided one), and if that validation fails, we return
1389     // !NT_SUCCESS from SelectConfig().
1390     //
1391     urb = FxUsbCreateConfigRequest(GetDriverGlobals(),
1392                                    configurationDescriptor,
1393                                    pInterfaces,
1394                                    GetDefaultMaxTransferSize());
1395     if (urb == NULL) {
1396         status = STATUS_INSUFFICIENT_RESOURCES;
1397     }
1398     else {
1399         status = SelectConfig(PipesAttributes, urb, FxUrbTypeLegacy, NULL);
1400         FxPoolFree(urb);
1401         urb = NULL;
1402     }
1403 
1404     FxPoolFree(pInterfaces);
1405     pInterfaces = NULL;
1406 
1407     return status;
1408 }
1409 
1410 struct FxInterfacePipeInformation {
1411     //
1412     // Array of pipes
1413     //
1414     FxUsbPipe** Pipes;
1415 
1416     //
1417     // Number of entries in Pipes
1418     //
1419     ULONG NumPipes;
1420 };
1421 
1422 _Must_inspect_result_
1423 NTSTATUS
SelectConfig(__in PWDF_OBJECT_ATTRIBUTES PipesAttributes,__in PURB Urb,__in FX_URB_TYPE FxUrbType,__out_opt PUCHAR NumConfiguredInterfaces)1424 FxUsbDevice::SelectConfig(
1425     __in PWDF_OBJECT_ATTRIBUTES PipesAttributes,
1426     __in PURB Urb,
1427     __in FX_URB_TYPE FxUrbType,
1428     __out_opt PUCHAR NumConfiguredInterfaces
1429     )
1430 /*++
1431 
1432 Routine Description:
1433     Selects the configuration as described by the parameter Urb.  If there is a
1434     previous active configuration, the WDFUSBPIPEs for it are stopped and
1435     destroyed before the new configuration is selected
1436 
1437 Arguments:
1438     PipesAttributes - object attributes to apply to each created WDFUSBPIPE
1439 
1440     Urb -  the URB describing the configuration to select
1441 
1442 Return Value:
1443     NTSTATUS
1444 
1445   --*/
1446 {
1447     WDF_REQUEST_SEND_OPTIONS options;
1448     PUCHAR pCur, pEnd;
1449     PUSBD_INTERFACE_INFORMATION pIface;
1450     FxUsbPipe* pPipe;
1451     PURB pSelectUrb;
1452     NTSTATUS status;
1453     ULONG iPipe;
1454     USHORT maxNumPipes, size;
1455     UCHAR numPipes;
1456     FxInterfacePipeInformation* pPipeInfo;
1457     PFX_DRIVER_GLOBALS pFxDriverGlobals;
1458     FxUsbInterface * pUsbInterface ;
1459     UCHAR intfIndex;
1460     FxIrp* irp;
1461 
1462     pFxDriverGlobals = GetDriverGlobals();
1463     FxSyncRequest request(GetDriverGlobals(), NULL);
1464 
1465     pIface = NULL;
1466     maxNumPipes = 0;
1467     size = 0;
1468     pPipeInfo = NULL;
1469     pSelectUrb = NULL;
1470 
1471     //
1472     // Callers to this function have guaranteed that there are interfaces
1473     // reported on this device.
1474     //
1475     ASSERT(m_NumInterfaces != 0);
1476 
1477     if (NumConfiguredInterfaces != NULL) {
1478         *NumConfiguredInterfaces = 0;
1479     }
1480 
1481     //
1482     // FxSyncRequest always succeesds for KM but can fail for UM.
1483     //
1484     status = request.Initialize();
1485     if (!NT_SUCCESS(status)) {
1486         DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
1487                             "Failed to initialize FxSyncRequest");
1488         goto Done;
1489     }
1490 
1491     //
1492     // Allocate a PIRP for the select config and possible select interface(s)
1493     //
1494     status = request.m_TrueRequest->ValidateTarget(this);
1495     if (!NT_SUCCESS(status)) {
1496         goto Done;
1497     }
1498 
1499     //
1500     // Allocate a pool for storing the FxUsbPipe ** before we
1501     // assign them to the Interfaces just in case all doesn't go well.
1502     //
1503     if (m_NumInterfaces == 0) {
1504         //
1505         // Use one in the zero case to make the logic simpler
1506         //
1507         size = sizeof(FxInterfacePipeInformation);
1508     }
1509     else {
1510         size = m_NumInterfaces * sizeof(FxInterfacePipeInformation);
1511     }
1512 
1513     pPipeInfo = (FxInterfacePipeInformation*) FxPoolAllocate(
1514         pFxDriverGlobals, NonPagedPool, size
1515         );
1516 
1517     if (pPipeInfo == NULL) {
1518         status = STATUS_INSUFFICIENT_RESOURCES;
1519         DoTraceLevelMessage(
1520             GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
1521             "Could not internal allocate tracking info for selecting a config on "
1522             "WDFUSBDEVICE 0x%p, %!STATUS!", GetHandle(), status);
1523         goto Done;
1524     }
1525 
1526     RtlZeroMemory(pPipeInfo, size);
1527 
1528     //
1529     // The following code and the one in select setting have a lot in common
1530     // but can't leverage on that as that sends the select interface URB down
1531     // Our goal is allocate the resources -- the pipes and the select setting URB
1532     // before sending down the select config URB so that the tear down is simpler.
1533     //
1534 
1535     //
1536     // Each FxUsbInterface will need a FxUsbPipe* for each pipe contained in
1537     // the interface.
1538     //
1539     pCur = (PUCHAR) &Urb->UrbSelectConfiguration.Interface;
1540     pEnd = ((PUCHAR) Urb) + Urb->UrbSelectConfiguration.Hdr.Length;
1541     intfIndex = 0;
1542 
1543     for ( ; pCur < pEnd; pCur += pIface->Length, intfIndex++) {
1544         FxUsbPipe** ppPipes;
1545 
1546         pIface = (PUSBD_INTERFACE_INFORMATION) pCur;
1547         if (pIface->NumberOfPipes > UCHAR_MAX) {
1548             status = STATUS_INVALID_DEVICE_REQUEST;
1549             DoTraceLevelMessage(
1550                 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
1551                 "WDFUSBDEVICE supports a maximum of %d pipes per interface, "
1552                 "USBD_INTERFACE_INFORMATION %p specified %d, %!STATUS!",
1553                 UCHAR_MAX, pIface, pIface->NumberOfPipes, status);
1554             goto Done;
1555         }
1556 
1557         pUsbInterface = GetInterfaceFromNumber(pIface->InterfaceNumber);
1558         if (pUsbInterface == NULL) {
1559             status = STATUS_INVALID_DEVICE_REQUEST;
1560             DoTraceLevelMessage(
1561                 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
1562                 "Could not find an instance of an interface descriptor with "
1563                 "InterfaceNumber %d, %!STATUS!",
1564                 pIface->InterfaceNumber, status);
1565             goto Done;
1566         }
1567 
1568         numPipes = (UCHAR) pIface->NumberOfPipes;
1569 
1570         //
1571         // Track the maximum number of pipes we have seen in an interface in
1572         // case we need to allocate a select interface URB.
1573         //
1574         if (numPipes > maxNumPipes) {
1575             maxNumPipes = (USHORT) numPipes;
1576         }
1577 
1578         if (numPipes > 0) {
1579             size = numPipes * sizeof(FxUsbPipe *);
1580         }
1581         else {
1582             //
1583             // It is valid to have an interface with zero pipes in it.  In that
1584             // case, we just allocate one entry so that we have a valid array
1585             // and keep the remaining code simple.
1586             //
1587             size = sizeof(FxUsbPipe*);
1588         }
1589 
1590         ppPipes = (FxUsbPipe**) FxPoolAllocate(
1591             pFxDriverGlobals,
1592             NonPagedPool,
1593             size
1594             );
1595 
1596         if (ppPipes == NULL) {
1597             status = STATUS_INSUFFICIENT_RESOURCES;
1598             DoTraceLevelMessage(
1599                 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
1600                 "Could not allocate memory for Pipes "
1601                 "InterfaceNumber %d, %!STATUS!",
1602                 pIface->InterfaceNumber, status);
1603             goto Done;
1604         }
1605 
1606         RtlZeroMemory(ppPipes, size);
1607 
1608         //
1609         // We store the pointer to the newly allocated arary in a temporary b/c
1610         // we can still fail and we don't want a half baked interface.
1611         //
1612 
1613         pPipeInfo[intfIndex].Pipes = ppPipes;
1614         pPipeInfo[intfIndex].NumPipes = numPipes;
1615 
1616         for (iPipe = 0; iPipe < numPipes; iPipe++) {
1617             ppPipes[iPipe] = new (GetDriverGlobals(), PipesAttributes)
1618                 FxUsbPipe(GetDriverGlobals(),this);
1619 
1620             if (ppPipes[iPipe] == NULL) {
1621                 status = STATUS_INSUFFICIENT_RESOURCES;
1622                 DoTraceLevelMessage(
1623                     GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
1624                     "Could not allocate a pipe object, %!STATUS!", status);
1625                 goto Done;
1626             }
1627 
1628             pPipe = ppPipes[iPipe];
1629             status = pPipe->Init(m_Device);
1630             if (!NT_SUCCESS(status)) {
1631                 DoTraceLevelMessage(
1632                     GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
1633                     "Could not Init the pipe object, %!STATUS!", status);
1634                 goto Done;
1635             }
1636 
1637             status = pPipe->Commit(PipesAttributes, NULL, pUsbInterface);
1638             if (!NT_SUCCESS(status)) {
1639                 DoTraceLevelMessage(
1640                     GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
1641                     "Could not commit the pipe object, %!STATUS!", status);
1642                 goto Done;
1643             }
1644         }
1645 
1646 
1647 
1648 
1649 
1650 
1651 
1652 
1653 
1654         if (pUsbInterface->IsInterfaceConfigured()) {
1655             CleanupInterfacePipesAndDelete(pUsbInterface);
1656         }
1657     }
1658 
1659     //
1660     // If we are selecting more then one interface and at least one of them
1661     // has pipes in them, allocate a select interface URB that will be used
1662     // after the select config to select each interface.  The select interface
1663     // URB will be big enough to select the largest interface in the group.
1664     //
1665     // The select interface URB is allocated before the select config so that
1666     // we know once the select config has completed successfully, we can be
1667     // guaranteed that we can select the interfaces we want and not have to
1668     // handle undoing the select config upon URB allocation failure.
1669     //
1670     if (m_NumInterfaces > 1 && maxNumPipes > 0) {
1671         size = GET_SELECT_INTERFACE_REQUEST_SIZE(maxNumPipes);
1672 
1673         pSelectUrb = (PURB) FxPoolAllocate(GetDriverGlobals(),
1674                                            NonPagedPool,
1675                                            size
1676                                            );
1677 
1678         if (pSelectUrb == NULL) {
1679             status = STATUS_INSUFFICIENT_RESOURCES;
1680             DoTraceLevelMessage(
1681                 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
1682                 "Could not allocate a select interface URB, %!STATUS!", status);
1683             goto Done;
1684         }
1685 
1686         RtlZeroMemory(pSelectUrb, size);
1687     }
1688 
1689     //
1690     // Send the select config to the usb core
1691     //
1692     WDF_REQUEST_SEND_OPTIONS_INIT(&options,
1693                                   WDF_REQUEST_SEND_OPTION_IGNORE_TARGET_STATE);
1694     WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT(&options, WDF_REL_TIMEOUT_IN_SEC(2));
1695 
1696     FxFormatUsbRequest(request.m_TrueRequest, Urb, FxUrbType, m_USBDHandle);
1697     status = SubmitSync(request.m_TrueRequest, &options);
1698 
1699     if (NT_SUCCESS(status)) {
1700         //
1701         // Save the config handle and store off all the pipes
1702         //
1703         m_ConfigHandle = Urb->UrbSelectConfiguration.ConfigurationHandle;
1704 
1705         //
1706         // Initialize the first interface
1707         //
1708         pIface = &Urb->UrbSelectConfiguration.Interface;
1709 
1710         pUsbInterface = GetInterfaceFromNumber(pIface->InterfaceNumber);
1711         pUsbInterface->SetNumConfiguredPipes((UCHAR)pIface->NumberOfPipes);
1712 
1713         //
1714         // The interface now owns the array of pipes
1715         //
1716         pUsbInterface->SetConfiguredPipes(pPipeInfo[0].Pipes);
1717         pPipeInfo[0].Pipes = NULL;
1718         pPipeInfo[0].NumPipes = 0;
1719 
1720         pUsbInterface->SetInfo(pIface);
1721 
1722         //
1723         // Since this could be the only interface, set the index now, so if we
1724         // return the number of configured interfaces, it will be valid for the
1725         // single interface case.
1726         //
1727         intfIndex = 1;
1728 
1729         //
1730         // If we have a more then one interface, we must select each of the addtional
1731         // interfaces after the select config because usbccgp (the generic parent
1732         // driver) didn't fill out all of the pipe info for IAD (interface
1733         // association descriptor) devices.  The first interface is filled out
1734         // property, it is 2->N that are left blank.
1735         //
1736         // pSelectUrb can be equal to NULL and have more then one interface if
1737         // all interfaces have no pipes associated with them.  In that case, we
1738         // still want to iterate over the remaining pipes so that we can
1739         // initialize our structures properly.
1740         //
1741         if (m_NumInterfaces > 1) {
1742             //
1743             // Loop over the interfaces again.
1744             //
1745             pCur = (PUCHAR) &Urb->UrbSelectConfiguration.Interface;
1746             pIface = (PUSBD_INTERFACE_INFORMATION) pCur;
1747             pEnd = ((PUCHAR) Urb) + Urb->UrbSelectConfiguration.Hdr.Length;
1748 
1749             //
1750             // Start at the 2nd one since the first is already selected
1751             //
1752             pCur += pIface->Length;
1753 
1754             for ( ; pCur < pEnd; pCur += pIface->Length, intfIndex++) {
1755 #pragma prefast(suppress: __WARNING_UNUSED_POINTER_ASSIGNMENT, "pIface is used in the for loop in many places. It looks like a false positive")
1756                 pIface = (PUSBD_INTERFACE_INFORMATION) pCur;
1757                 ASSERT(pIface->NumberOfPipes <= maxNumPipes);
1758 
1759                 pUsbInterface = GetInterfaceFromNumber(pIface->InterfaceNumber);
1760                 ASSERT(pUsbInterface != NULL);
1761 
1762                 //
1763                 // Valid to have an interface with no pipes.  If there are no
1764                 // pipes, GET_SELECT_INTERFACE_REQUEST_SIZE will compute value
1765                 // too small of a size to pass validation.
1766                 //
1767                 if (pIface->NumberOfPipes > 0) {
1768 
1769                     pUsbInterface->FormatSelectSettingUrb(
1770                         pSelectUrb,
1771                         (USHORT) pIface->NumberOfPipes,
1772                         pIface->AlternateSetting
1773                         );
1774                     irp = request.m_TrueRequest->GetSubmitFxIrp();
1775                     irp->Reuse(STATUS_SUCCESS);
1776 
1777                     request.m_TrueRequest->ClearFieldsForReuse();
1778                     FxFormatUsbRequest(request.m_TrueRequest,
1779                                        pSelectUrb,
1780                                        FxUrbTypeLegacy,
1781                                        NULL);
1782 
1783                     status = SubmitSync(request.m_TrueRequest, &options);
1784                     if (!NT_SUCCESS(status)) {
1785                         DoTraceLevelMessage(
1786                             GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
1787                             "USB core failed Select Interface URB, %!STATUS!",
1788                             status);
1789                         goto Done;
1790                     }
1791 
1792                     //
1793                     // Copy the info back into the original select config URB
1794                     //
1795 
1796                     RtlCopyMemory(pIface,
1797                                   &pSelectUrb->UrbSelectInterface.Interface,
1798                                   pSelectUrb->UrbSelectInterface.Interface.Length);
1799                 }
1800 
1801                 //
1802                 // Update the pointer to point to the new pipes with the pointer
1803                 // to the pipes stored earlier.
1804                 //
1805                 ASSERT(pPipeInfo[intfIndex].NumPipes == pIface->NumberOfPipes);
1806                 pUsbInterface->SetNumConfiguredPipes((UCHAR)pIface->NumberOfPipes);
1807 
1808                 //
1809                 // The interface now owns the array of pipes
1810                 //
1811                 pUsbInterface->SetConfiguredPipes(pPipeInfo[intfIndex].Pipes);
1812                 pPipeInfo[intfIndex].Pipes = NULL;
1813                 pPipeInfo[intfIndex].NumPipes = 0;
1814 
1815                 //
1816                 // SetInfo only after a successful select config so that we can
1817                 // copy the USBD_PIPE_INFORMATION .
1818                 //
1819                 pUsbInterface->SetInfo(pIface);
1820             }
1821         }
1822 
1823         if (NumConfiguredInterfaces != NULL) {
1824             *NumConfiguredInterfaces = intfIndex;
1825         }
1826     }
1827     else {
1828         DoTraceLevelMessage(
1829             GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
1830             "USB core failed Select Configuration, %!STATUS!", status);
1831     }
1832 
1833 Done:
1834     if (pSelectUrb != NULL) {
1835         FxPoolFree(pSelectUrb);
1836         pSelectUrb = NULL;
1837     }
1838 
1839     if (pPipeInfo != NULL) {
1840         //
1841         // Free all arrays that may have been allocated
1842         //
1843         for (intfIndex = 0; intfIndex < m_NumInterfaces; intfIndex++) {
1844             //
1845             // We can have NumPipes == 0 and still have an allocated array, so
1846             // use the array != NULL as the check.
1847             //
1848             if (pPipeInfo[intfIndex].Pipes != NULL) {
1849                 //
1850                 // Delete any pipes that might have been created
1851                 //
1852                 for (iPipe = 0;  iPipe < pPipeInfo[intfIndex].NumPipes; iPipe++) {
1853                     if (pPipeInfo[intfIndex].Pipes[iPipe] != NULL) {
1854                         pPipeInfo[intfIndex].Pipes[iPipe]->DeleteFromFailedCreate();
1855                         pPipeInfo[intfIndex].Pipes[iPipe] = NULL;
1856                     }
1857                 }
1858 
1859                 //
1860                 // Now free the array itself and clear out the count
1861                 //
1862                 FxPoolFree(pPipeInfo[intfIndex].Pipes);
1863                 pPipeInfo[intfIndex].Pipes = NULL;
1864                 pPipeInfo[intfIndex].NumPipes = 0;
1865             }
1866         }
1867 
1868         FxPoolFree(pPipeInfo);
1869         pPipeInfo = NULL;
1870     }
1871 
1872     return status;
1873 }
1874 
1875 _Must_inspect_result_
1876 NTSTATUS
Deconfig(VOID)1877 FxUsbDevice::Deconfig(
1878     VOID
1879     )
1880 {
1881     WDF_REQUEST_SEND_OPTIONS options;
1882     _URB_SELECT_CONFIGURATION urb;
1883 
1884     FxSyncRequest request(GetDriverGlobals(), NULL);
1885     NTSTATUS status;
1886 
1887     //
1888     // FxSyncRequest always succeesds for KM but can fail for UM.
1889     //
1890     status = request.Initialize();
1891     if (!NT_SUCCESS(status)) {
1892         DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
1893                             "Failed to initialize FxSyncRequest");
1894         return status;
1895     }
1896 
1897     status = request.m_TrueRequest->ValidateTarget(this);
1898     if (!NT_SUCCESS(status)) {
1899         return status;
1900     }
1901 
1902     //
1903     // This will remove and free all interfaces and associated pipes
1904     //
1905     PipesGotoRemoveState(TRUE);
1906 
1907     RtlZeroMemory(&urb, sizeof(urb));
1908 
1909 #pragma prefast(suppress: __WARNING_BUFFER_OVERFLOW, "this annotation change in usb.h is communicated to usb team");
1910     UsbBuildSelectConfigurationRequest((PURB) &urb, sizeof(urb), NULL);
1911 #pragma prefast(suppress: __WARNING_BUFFER_OVERFLOW, "this annotation change in usb.h is communicated to usb team");
1912     FxFormatUsbRequest(request.m_TrueRequest, (PURB) &urb, FxUrbTypeLegacy, NULL);
1913 
1914     WDF_REQUEST_SEND_OPTIONS_INIT(
1915         &options, WDF_REQUEST_SEND_OPTION_IGNORE_TARGET_STATE);
1916 
1917     status = SubmitSync(request.m_TrueRequest, &options);
1918 
1919     return status;
1920 }
1921 
1922 _Must_inspect_result_
1923 NTSTATUS
SelectConfigInterfaces(__in PWDF_OBJECT_ATTRIBUTES PipesAttributes,__in PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor,__in_ecount (NumInterfaces)PUSB_INTERFACE_DESCRIPTOR * InterfaceDescriptors,__in ULONG NumInterfaces)1924 FxUsbDevice::SelectConfigInterfaces(
1925     __in PWDF_OBJECT_ATTRIBUTES PipesAttributes,
1926     __in PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor,
1927     __in_ecount(NumInterfaces) PUSB_INTERFACE_DESCRIPTOR* InterfaceDescriptors,
1928     __in ULONG NumInterfaces
1929     )
1930 {
1931     PUSBD_INTERFACE_LIST_ENTRY pInterfaces;
1932     PURB urb;
1933     NTSTATUS status;
1934     ULONG i, size;
1935     PFX_DRIVER_GLOBALS  pFxDriverGlobals;
1936 
1937     pFxDriverGlobals = GetDriverGlobals();
1938 
1939     //
1940     // eventually size = sizeof(USBD_INTERFACE_LIST_ENTRY) * (NumInterfaces + 1)
1941     //
1942     status = RtlULongAdd(NumInterfaces, 1, &size);
1943     if (!NT_SUCCESS(status)) {
1944         return status;
1945     }
1946 
1947     status = RtlULongMult(size, sizeof(USBD_INTERFACE_LIST_ENTRY), &size);
1948     if (!NT_SUCCESS(status)) {
1949         return status;
1950     }
1951 
1952     for (i = 0; i < NumInterfaces; i++) {
1953         if (InterfaceDescriptors[i] == NULL) {
1954             return STATUS_INVALID_PARAMETER;
1955         }
1956     }
1957 
1958     pInterfaces = (PUSBD_INTERFACE_LIST_ENTRY) FxPoolAllocate(
1959         pFxDriverGlobals,
1960         NonPagedPool,
1961         size
1962         );
1963 
1964     if (pInterfaces == NULL) {
1965         status = STATUS_INSUFFICIENT_RESOURCES;
1966 
1967         DoTraceLevelMessage(
1968             GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
1969             "Could not allocate array of USBD_INTERFACE_LIST_ENTRY, %!STATUS!",
1970             status);
1971 
1972         return status;
1973     }
1974 
1975     RtlZeroMemory(pInterfaces, size);
1976 
1977     for (i = 0; i < NumInterfaces; i++) {
1978         pInterfaces[i].InterfaceDescriptor = InterfaceDescriptors[i];
1979     }
1980 
1981     if (ConfigurationDescriptor == NULL) {
1982         ConfigurationDescriptor = m_ConfigDescriptor;
1983     }
1984 
1985     urb = FxUsbCreateConfigRequest(GetDriverGlobals(),
1986                                    ConfigurationDescriptor,
1987                                    pInterfaces,
1988                                    GetDefaultMaxTransferSize());
1989     if (urb == NULL) {
1990         status = STATUS_INSUFFICIENT_RESOURCES;
1991     }
1992     else {
1993         status = SelectConfig(PipesAttributes, urb, FxUrbTypeLegacy, NULL);
1994         FxPoolFree(urb);
1995         urb = NULL;
1996     }
1997 
1998     FxPoolFree(pInterfaces);
1999     pInterfaces = NULL;
2000 
2001     return status;
2002 }
2003 
2004 FxUsbInterface *
GetInterfaceFromIndex(__in UCHAR InterfaceIndex)2005 FxUsbDevice::GetInterfaceFromIndex(
2006     __in UCHAR InterfaceIndex
2007     )
2008 {
2009     if (InterfaceIndex < m_NumInterfaces){
2010         return m_Interfaces[InterfaceIndex];
2011     }
2012 
2013     return NULL;
2014 }
2015 
2016 _Must_inspect_result_
2017 NTSTATUS
GetInterfaceNumberFromInterface(__in WDFUSBINTERFACE UsbInterface,__out PUCHAR InterfaceNumber)2018 FxUsbDevice::GetInterfaceNumberFromInterface(
2019     __in WDFUSBINTERFACE UsbInterface,
2020     __out PUCHAR InterfaceNumber
2021     )
2022 {
2023      FxUsbInterface * pUsbInterface;
2024 
2025      FxObjectHandleGetPtr(GetDriverGlobals(),
2026                           UsbInterface,
2027                           FX_TYPE_USB_INTERFACE ,
2028                           (PVOID*) &pUsbInterface);
2029 
2030      *InterfaceNumber = pUsbInterface->GetInterfaceNumber();
2031 
2032      return STATUS_SUCCESS;
2033 }
2034 
2035 FxUsbInterface *
GetInterfaceFromNumber(__in UCHAR InterfaceNumber)2036 FxUsbDevice::GetInterfaceFromNumber(
2037     __in UCHAR InterfaceNumber
2038     )
2039 {
2040     ULONG i;
2041 
2042     for (i = 0; i < m_NumInterfaces; i++) {
2043         if (m_Interfaces[i]->GetInterfaceNumber() == InterfaceNumber) {
2044             return m_Interfaces[i];
2045         }
2046     }
2047 
2048     return NULL;
2049 }
2050 
2051 VOID
CleanupInterfacePipesAndDelete(__in FxUsbInterface * UsbInterface)2052 FxUsbDevice::CleanupInterfacePipesAndDelete(
2053     __in FxUsbInterface * UsbInterface
2054     )
2055 {
2056     SINGLE_LIST_ENTRY sentHead;
2057     LIST_ENTRY pendHead;
2058     ULONG iPipe;
2059     FxUsbPipe *pPipe;
2060     KIRQL irql;
2061 
2062     sentHead.Next = NULL;
2063     InitializeListHead(&pendHead);
2064 
2065     AcquireInterfaceIterationLock();
2066 
2067     Lock(&irql);
2068     for (iPipe = 0; iPipe < UsbInterface->m_NumberOfConfiguredPipes; iPipe++) {
2069         BOOLEAN wait;
2070 
2071         wait = FALSE;
2072         pPipe = UsbInterface->m_ConfiguredPipes[iPipe];
2073         pPipe->GotoRemoveState(
2074             WdfIoTargetDeleted,
2075             &pendHead,
2076             &sentHead,
2077             TRUE,
2078             &wait);
2079     }
2080     Unlock(irql);
2081 
2082     _CleanupPipesRequests(&pendHead, &sentHead);
2083 
2084     for (iPipe = 0; iPipe < UsbInterface->m_NumberOfConfiguredPipes; iPipe++) {
2085         pPipe = UsbInterface->m_ConfiguredPipes[iPipe];
2086         pPipe->WaitForSentIoToComplete();
2087     }
2088 
2089     UsbInterface->CleanUpAndDelete(FALSE);
2090 
2091     ReleaseInterfaceIterationLock();
2092 }
2093 
2094 VOID
CancelSentIo(VOID)2095 FxUsbDevice::CancelSentIo(
2096     VOID
2097     )
2098 {
2099     FxUsbInterface *pUsbInterface;
2100     ULONG iInterface, iPipe;
2101 
2102     for (iInterface = 0; iInterface < m_NumInterfaces; iInterface++) {
2103         pUsbInterface = m_Interfaces[iInterface];
2104 
2105         if (pUsbInterface->m_ConfiguredPipes != NULL) {
2106             for (iPipe = 0;
2107                  iPipe < pUsbInterface->m_NumberOfConfiguredPipes;
2108                  iPipe++) {
2109 
2110                 if (pUsbInterface->m_ConfiguredPipes[iPipe] != NULL) {
2111                     pUsbInterface->m_ConfiguredPipes[iPipe]->CancelSentIo();
2112                 }
2113 
2114             }
2115         }
2116     }
2117     FxIoTarget::CancelSentIo(); // __super call
2118 }
2119 
2120 __checkReturn
2121 NTSTATUS
CreateUrb(__in_opt PWDF_OBJECT_ATTRIBUTES Attributes,__out WDFMEMORY * UrbMemory,__deref_opt_out_bcount (sizeof (URB))PURB * Urb)2122 FxUsbDevice::CreateUrb(
2123     __in_opt
2124     PWDF_OBJECT_ATTRIBUTES Attributes,
2125     __out
2126     WDFMEMORY* UrbMemory,
2127     __deref_opt_out_bcount(sizeof(URB))
2128     PURB* Urb
2129     )
2130 {
2131     NTSTATUS status;
2132     PURB urbLocal = NULL;
2133     PFX_DRIVER_GLOBALS pFxDriverGlobals;
2134     FxUsbUrb * pUrb = NULL;
2135     WDFMEMORY hMemory;
2136     FxObject* pParent;
2137 
2138     //
2139     // Get the parent's globals if it is present, else use the ones for FxUsbDevice
2140     //
2141     pFxDriverGlobals = GetDriverGlobals();
2142     status = FxValidateObjectAttributesForParentHandle(pFxDriverGlobals, Attributes);
2143 
2144     if (NT_SUCCESS(status)) {
2145 
2146         FxObjectHandleGetPtrAndGlobals(pFxDriverGlobals,
2147                                        Attributes->ParentObject,
2148                                        FX_TYPE_OBJECT,
2149                                        (PVOID*)&pParent,
2150                                        &pFxDriverGlobals);
2151 
2152         if (FALSE == IsObjectDisposedOnRemove(pParent)) {
2153             DoTraceLevelMessage(
2154                 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
2155                 "Urb must be parented to FxDevice or an IoAllocated Request");
2156             status = STATUS_INVALID_PARAMETER;
2157             goto Done;
2158         }
2159 
2160     }
2161     else if (status == STATUS_WDF_PARENT_NOT_SPECIFIED) {
2162 
2163         pFxDriverGlobals = GetDriverGlobals();
2164         pParent = this;
2165         status = STATUS_SUCCESS;
2166 
2167     }
2168     else {
2169 
2170         goto Done;
2171     }
2172 
2173     status = FxValidateObjectAttributes(pFxDriverGlobals, Attributes);
2174     if (!NT_SUCCESS(status)) {
2175         goto Done;
2176     }
2177 
2178     FxPointerNotNull(pFxDriverGlobals, UrbMemory);
2179 
2180     *UrbMemory = NULL;
2181 
2182     status = USBD_UrbAllocate(m_USBDHandle, &urbLocal);
2183 
2184     if (!NT_SUCCESS(status)) {
2185 
2186         urbLocal = NULL;
2187         DoTraceLevelMessage(
2188             GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
2189             "USBDEVICE Must have been created with Client Contract Version Info, %!STATUS!",
2190             status);
2191 
2192         goto Done;
2193 
2194     }
2195 
2196     pUrb = new(pFxDriverGlobals, Attributes)
2197             FxUsbUrb(pFxDriverGlobals, m_USBDHandle, urbLocal, sizeof(URB));
2198 
2199     if (pUrb == NULL) {
2200         status = STATUS_INSUFFICIENT_RESOURCES;
2201         goto Done;
2202     }
2203 
2204     urbLocal = NULL;
2205 
2206     status = pUrb->Commit(Attributes, (WDFOBJECT*)&hMemory, pParent);
2207 
2208     if (!NT_SUCCESS(status)) {
2209         goto Done;
2210     }
2211 
2212     *UrbMemory = hMemory;
2213 
2214     if (Urb) {
2215         *Urb = (PURB) pUrb->GetBuffer();
2216     }
2217 
2218 Done:
2219 
2220     if (!NT_SUCCESS(status)) {
2221 
2222         if (pUrb) {
2223             pUrb->DeleteFromFailedCreate();
2224         }
2225 
2226         if (urbLocal) {
2227             USBD_UrbFree(m_USBDHandle, urbLocal);
2228         }
2229 
2230     }
2231 
2232     return status;
2233 }
2234 
2235 __checkReturn
2236 NTSTATUS
CreateIsochUrb(__in_opt PWDF_OBJECT_ATTRIBUTES Attributes,__in ULONG NumberOfIsochPackets,__out WDFMEMORY * UrbMemory,__deref_opt_out_bcount (GET_ISOCH_URB_SIZE (NumberOfIsochPackets))PURB * Urb)2237 FxUsbDevice::CreateIsochUrb(
2238     __in_opt
2239     PWDF_OBJECT_ATTRIBUTES Attributes,
2240     __in
2241     ULONG NumberOfIsochPackets,
2242     __out
2243     WDFMEMORY* UrbMemory,
2244     __deref_opt_out_bcount(GET_ISOCH_URB_SIZE(NumberOfIsochPackets))
2245     PURB* Urb
2246     )
2247 {
2248     NTSTATUS status;
2249     PURB urbLocal = NULL;
2250     PFX_DRIVER_GLOBALS pFxDriverGlobals;
2251     FxUsbUrb * pUrb = NULL;
2252     WDFMEMORY hMemory;
2253     ULONG size;
2254     FxObject* pParent;
2255 
2256     //
2257     // Get the parent's globals if it is present, else use the ones for FxUsbDevice
2258     //
2259     pFxDriverGlobals = GetDriverGlobals();
2260     status = FxValidateObjectAttributesForParentHandle(pFxDriverGlobals, Attributes);
2261 
2262     if (NT_SUCCESS(status)) {
2263 
2264         FxObjectHandleGetPtrAndGlobals(pFxDriverGlobals,
2265                                        Attributes->ParentObject,
2266                                        FX_TYPE_OBJECT,
2267                                        (PVOID*)&pParent,
2268                                        &pFxDriverGlobals);
2269 
2270         if (FALSE == IsObjectDisposedOnRemove(pParent)) {
2271             DoTraceLevelMessage(
2272                 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
2273                 "Urb must be parented to FxDevice or IoAllocated Request");
2274             status = STATUS_INVALID_PARAMETER;
2275             goto Done;
2276         }
2277 
2278     }
2279     else if (status == STATUS_WDF_PARENT_NOT_SPECIFIED) {
2280 
2281         pFxDriverGlobals = GetDriverGlobals();
2282         pParent = this;
2283         status = STATUS_SUCCESS;
2284 
2285     }
2286     else {
2287 
2288         goto Done;
2289     }
2290 
2291     status = FxValidateObjectAttributes(pFxDriverGlobals, Attributes);
2292     if (!NT_SUCCESS(status)) {
2293         goto Done;
2294     }
2295 
2296     FxPointerNotNull(pFxDriverGlobals, UrbMemory);
2297 
2298     *UrbMemory = NULL;
2299 
2300     status = USBD_IsochUrbAllocate(m_USBDHandle, NumberOfIsochPackets, &urbLocal);
2301 
2302     if (!NT_SUCCESS(status)) {
2303 
2304         urbLocal = NULL;
2305         DoTraceLevelMessage(
2306             GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
2307             "USBDEVICE Must have been created with Client Contract Version Info, %!STATUS!",
2308             status);
2309 
2310         goto Done;
2311 
2312     }
2313 
2314     size = GET_ISO_URB_SIZE(NumberOfIsochPackets);
2315 
2316     pUrb = new(pFxDriverGlobals, Attributes)
2317             FxUsbUrb(pFxDriverGlobals, m_USBDHandle, urbLocal, size);
2318 
2319     if (pUrb == NULL) {
2320         status = STATUS_INSUFFICIENT_RESOURCES;
2321         goto Done;
2322     }
2323 
2324     urbLocal = NULL;
2325 
2326     status = pUrb->Commit(Attributes, (WDFOBJECT*)&hMemory, pParent);
2327 
2328     if (!NT_SUCCESS(status)) {
2329         goto Done;
2330     }
2331 
2332     *UrbMemory = hMemory;
2333 
2334     if (Urb) {
2335         *Urb = (PURB) pUrb->GetBuffer();
2336     }
2337 
2338 Done:
2339 
2340     if (!NT_SUCCESS(status)) {
2341 
2342         if (pUrb) {
2343             pUrb->DeleteFromFailedCreate();
2344         }
2345 
2346         if (urbLocal) {
2347             USBD_UrbFree(m_USBDHandle, urbLocal);
2348         }
2349 
2350     }
2351 
2352     return status;
2353 }
2354 
2355 BOOLEAN
IsObjectDisposedOnRemove(__in FxObject * Object)2356 FxUsbDevice::IsObjectDisposedOnRemove(
2357     __in FxObject* Object
2358     )
2359 /*++
2360 
2361 Routine Description:
2362     This routine determines if the Object being passed is guranteed to be disposed on a
2363     Pnp remove operation.
2364 
2365     The object will be disposed on Pnp remove operation if it is a somewhere in the child
2366     tree of the FxDevice assocaited with this FxUsbDevice object, or in the child tree of an
2367     Io Allocated Request.
2368 
2369 Arguments:
2370 
2371     Object - The Object being checked
2372 
2373 Return Value:
2374 
2375     TRUE if the Object is guranteed to be disposed on a pnp remove operation
2376     FALSE otherwise.
2377 
2378   --*/
2379 {
2380     FxObject * obj;
2381     FxObject * parent;
2382     BOOLEAN isObjectDisposedOnRemove = FALSE;
2383 
2384     obj = Object;
2385 
2386     //
2387     // By adding a reference now, we simulate what GetParentObjectReferenced
2388     // does later, thus allowing simple logic on when/how to release the
2389     // reference on exit.
2390     //
2391     obj->ADDREF(Object);
2392 
2393     while (obj != NULL) {
2394 
2395         if (obj == (FxObject*) m_Device) {
2396 
2397             isObjectDisposedOnRemove = TRUE;
2398             break;
2399         }
2400 
2401         if (FxObjectCheckType(obj, FX_TYPE_REQUEST)) {
2402 
2403             FxRequestBase* request = (FxRequestBase*) obj;
2404             if (request->IsAllocatedFromIo()) {
2405 
2406                 isObjectDisposedOnRemove = TRUE;
2407                 break;
2408             }
2409         }
2410 
2411         parent = obj->GetParentObjectReferenced(Object);
2412 
2413         //
2414         // Release the reference previously taken by the top of the function
2415         // or GetParentObjectReferenced in a previous pass in the loop.
2416         //
2417         obj->RELEASE(Object);
2418         obj = parent;
2419     }
2420 
2421     if (obj != NULL) {
2422 
2423         //
2424         // Release the reference previously taken by the top of the function
2425         // or GetParentObjectReferenced in a last pass in the loop.
2426         //
2427         obj->RELEASE(Object);
2428     }
2429 
2430     return isObjectDisposedOnRemove;
2431 }
2432 
2433 FX_URB_TYPE
GetFxUrbTypeForRequest(__in FxRequestBase * Request)2434 FxUsbDevice::GetFxUrbTypeForRequest(
2435     __in FxRequestBase* Request
2436     )
2437 /*++
2438 
2439 Routine Description:
2440     This routine essentially determines whether this routine can use a urb allocated by
2441     the USBD_xxxUrbAllocate APIs
2442 
2443     The USBD_xxxUrbAllocate APIs are only used for those requests that could be disposed at the
2444     time the client driver devnode is being removed.
2445 
2446     If we cannot make that gurantee about that request, FxUrbTypeLegacy is returned and the
2447     USBD_xxxUrbAllocate api's must not be used to allocate an Urb.
2448 
2449     Else FxUrbTypeUsbdAllocated is returned.
2450 
2451 Arguments:
2452 
2453     Request - FxRequest
2454 
2455 Return Value:
2456 
2457     FxUrbTypeUsbdAllocated, or FxUrbTypeLegacy
2458 
2459   --*/
2460 {
2461     if (m_UrbType == FxUrbTypeLegacy) {
2462         return FxUrbTypeLegacy;
2463     }
2464 
2465     if (Request->IsAllocatedFromIo()) {
2466         return FxUrbTypeUsbdAllocated;
2467     }
2468 
2469     if (IsObjectDisposedOnRemove((FxObject*) Request)) {
2470         return FxUrbTypeUsbdAllocated;
2471     }
2472 
2473     return FxUrbTypeLegacy;
2474 }
2475 
2476