1 /*++
2 
3 Copyright (c) Microsoft Corporation
4 
5 Module Name:
6 
7     FxUsbDeviceUm.cpp
8 
9 Abstract:
10 
11 Author:
12 
13 Environment:
14 
15     User mode only
16 
17 Revision History:
18 
19 --*/
20 extern "C" {
21 #include <initguid.h>
22 
23 }
24 #include "fxusbpch.hpp"
25 extern "C" {
26 #include "FxUsbDeviceUm.tmh"
27 }
28 
29 _Must_inspect_result_
30 NTSTATUS
31 FxUsbDevice::SendSyncRequest(
32     __in FxSyncRequest* Request,
33     __in ULONGLONG Time
34     )
35 {
36     NTSTATUS status;
37     WDF_REQUEST_SEND_OPTIONS options;
38 
39     WDF_REQUEST_SEND_OPTIONS_INIT(&options, 0);
40     WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT(&options, WDF_REL_TIMEOUT_IN_SEC(Time));
41 
42     status = SubmitSync(Request->m_TrueRequest, &options);
43     if (!NT_SUCCESS(status)) {
44         DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
45                             "FxUsbDevice SubmitSync failed");
46         goto Done;
47     }
48 
49 Done:
50     return status;
51 }
52 
53 _Must_inspect_result_
54 NTSTATUS
55 FxUsbDevice::SendSyncUmUrb(
56     __inout PUMURB Urb,
57     __in ULONGLONG Time,
58     __in_opt WDFREQUEST Request,
59     __in_opt PWDF_REQUEST_SEND_OPTIONS Options
60     )
61 {
62     NTSTATUS status;
63     WDF_REQUEST_SEND_OPTIONS options;
64     FxSyncRequest request(GetDriverGlobals(), NULL, Request);
65 
66     status = request.Initialize();
67     if (!NT_SUCCESS(status)) {
68         DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
69                             "Failed to initialize FxSyncRequest");
70         return status;
71     }
72 
73     if (NULL == Options) {
74         Options = &options;
75     }
76 
77     WDF_REQUEST_SEND_OPTIONS_INIT(Options, 0);
78     WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT(Options, WDF_REL_TIMEOUT_IN_SEC(Time));
79 
80     status = request.m_TrueRequest->ValidateTarget(this);
81     if (NT_SUCCESS(status)) {
82         FxUsbUmFormatRequest(request.m_TrueRequest, &Urb->UmUrbHeader, m_pHostTargetFile);
83         status = SubmitSync(request.m_TrueRequest, Options);
84         if (!NT_SUCCESS(status)) {
85             DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
86                                 "FxUsbDevice SubmitSync failed");
87             return status;
88         }
89     }
90 
91     return status;
92 }
93 
94 _Must_inspect_result_
95 NTSTATUS
96 FxUsbDevice::InitDevice(
97     __in ULONG USBDClientContractVersionForWdfClient
98     )
99 {
100     HRESULT hr = S_OK;
101     NTSTATUS status = STATUS_SUCCESS;
102 
103     IWudfDevice* device = NULL;
104     IWudfDeviceStack2* devstack2 = NULL;
105 
106     UMURB urb;
107     USB_CONFIGURATION_DESCRIPTOR config;
108     USHORT wTotalLength = 0;
109 
110     FxRequestBuffer buf;
111     FxUsbDeviceControlContext context(FxUrbTypeLegacy);
112 
113     WDF_USB_CONTROL_SETUP_PACKET setupPacket;
114     USHORT deviceStatus = 0;
115     UCHAR deviceSpeed = 0;
116 
117     FxSyncRequest request(GetDriverGlobals(), NULL);
118     FxSyncRequest request2(GetDriverGlobals(), &context);
119 
120 
121 
122 
123     UNREFERENCED_PARAMETER(USBDClientContractVersionForWdfClient);
124 
125     status = request.Initialize();
126     if (!NT_SUCCESS(status)) {
127         DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
128                             "Failed to initialize FxSyncRequest");
129         goto Done;
130     }
131 
132     status = request2.Initialize();
133     if (!NT_SUCCESS(status)) {
134         DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
135                             "Failed to initialize second FxSyncRequest");
136         goto Done;
137     }
138 
139     status = request.m_TrueRequest->ValidateTarget(this);
140     if (!NT_SUCCESS(status)) {
141         DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
142                             "Failed to validate FxSyncRequest target");
143         goto Done;
144     }
145 
146     status = request2.m_TrueRequest->ValidateTarget(this);
147     if (!NT_SUCCESS(status)) {
148         DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
149                             "Failed to validate second FxSyncRequest target");
150         goto Done;
151     }
152 
153     RtlZeroMemory(&urb, sizeof(urb));
154 
155     device = m_DeviceBase->GetDeviceObject();
156     devstack2 = m_Device->GetDeviceStack2();
157 
158     if (m_pHostTargetFile == NULL) {
159 
160         //
161         // Opens a handle on the reflector for USB side-band communication
162         // based on the currently selected dispatcher type.
163         //
164         hr = devstack2->OpenUSBCommunicationChannel(device,
165                                                     device->GetAttachedDevice(),
166                                                     &m_pHostTargetFile);
167 
168         if (SUCCEEDED(hr)) {
169             m_WinUsbHandle = (WINUSB_INTERFACE_HANDLE)m_pHostTargetFile->GetCreateContext();
170         }
171     }
172 
173     //
174     // Get USB device descriptor
175     //
176     FxUsbUmInitDescriptorUrb(&urb,
177                              m_WinUsbHandle,
178                              USB_DEVICE_DESCRIPTOR_TYPE,
179                              sizeof(m_DeviceDescriptor),
180                              &m_DeviceDescriptor);
181     FxUsbUmFormatRequest(request.m_TrueRequest,
182                          &urb.UmUrbHeader,
183                          m_pHostTargetFile,
184                          TRUE);
185     status = SendSyncRequest(&request, 5);
186     if (!NT_SUCCESS(status)) {
187         goto Done;
188     }
189 
190     //
191     // Get USB configuration descriptor
192     //
193     FxUsbUmInitDescriptorUrb(&urb,
194                              m_WinUsbHandle,
195                              USB_CONFIGURATION_DESCRIPTOR_TYPE,
196                              sizeof(config),
197                              &config);
198     FxUsbUmFormatRequest(request.m_TrueRequest,
199                          &urb.UmUrbHeader,
200                          m_pHostTargetFile,
201                          TRUE);
202     status = SendSyncRequest(&request, 5);
203     if (!NT_SUCCESS(status)) {
204         goto Done;
205     }
206 
207     if (config.wTotalLength < sizeof(USB_CONFIGURATION_DESCRIPTOR)) {
208 
209         //
210         // Not enough info returned
211         //
212         status = STATUS_UNSUCCESSFUL;
213         DoTraceLevelMessage(
214             GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
215             "Could not retrieve config descriptor size, config.wTotalLength %d < "
216             "sizeof(config descriptor) (%d), %!STATUS!",
217             config.wTotalLength, sizeof(USB_CONFIGURATION_DESCRIPTOR), status);
218         goto Done;
219     }
220 
221     wTotalLength = config.wTotalLength;
222     m_ConfigDescriptor = (PUSB_CONFIGURATION_DESCRIPTOR)
223                              FxPoolAllocate(GetDriverGlobals(),
224                                             NonPagedPool,
225                                             wTotalLength);
226     if (NULL == m_ConfigDescriptor) {
227         status = STATUS_INSUFFICIENT_RESOURCES;
228         DoTraceLevelMessage(
229             GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
230             "Could not allocate %d bytes for config descriptor, %!STATUS!",
231             sizeof(USB_CONFIGURATION_DESCRIPTOR), status);
232         goto Done;
233     }
234 
235     //
236     // Get USB configuration descriptor and subsequent interface descriptors, etc.
237     //
238     FxUsbUmInitDescriptorUrb(&urb,
239                              m_WinUsbHandle,
240                              USB_CONFIGURATION_DESCRIPTOR_TYPE,
241                              wTotalLength,
242                              m_ConfigDescriptor);
243     FxUsbUmFormatRequest(request.m_TrueRequest,
244                          &urb.UmUrbHeader,
245                          m_pHostTargetFile,
246                          TRUE);
247     status = SendSyncRequest(&request, 5);
248     if (!NT_SUCCESS(status)) {
249         goto Done;
250     } else if (m_ConfigDescriptor->wTotalLength != wTotalLength) {
251         //
252         // Invalid wTotalLength
253         //
254         status = STATUS_DEVICE_DATA_ERROR;
255         DoTraceLevelMessage(
256             GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
257             "Defective USB device reported two different config descriptor "
258             "wTotalLength values: %d and %d, %!STATUS!",
259             wTotalLength, m_ConfigDescriptor->wTotalLength, status);
260         goto Done;
261     }
262 
263     m_NumInterfaces = m_ConfigDescriptor->bNumInterfaces;
264 
265     //
266     // Check to see if we are wait wake capable
267     //
268     if (m_ConfigDescriptor->bmAttributes & USB_CONFIG_REMOTE_WAKEUP) {
269         m_Traits |= WDF_USB_DEVICE_TRAIT_REMOTE_WAKE_CAPABLE;
270     }
271 
272     //
273     // Get the device status to check if device is self-powered.
274     //
275     WDF_USB_CONTROL_SETUP_PACKET_INIT_GET_STATUS(&setupPacket,
276                                                  BmRequestToDevice,
277                                                  0); // Device status
278 
279     buf.SetBuffer(&deviceStatus, sizeof(USHORT));
280 
281     status = FormatControlRequest(request2.m_TrueRequest,
282                                   &setupPacket,
283                                   &buf);
284     if (!NT_SUCCESS(status)) {
285         goto Done;
286     }
287 
288     status = SendSyncRequest(&request2, 5);
289     if (!NT_SUCCESS(status)) {
290         goto Done;
291     }
292 
293     if (deviceStatus & USB_GETSTATUS_SELF_POWERED) {
294         m_Traits |= WDF_USB_DEVICE_TRAIT_SELF_POWERED;
295     }
296 
297     //
298     // Get device speed information.
299     //
300     FxUsbUmInitInformationUrb(&urb,
301                               m_WinUsbHandle,
302                               sizeof(UCHAR),
303                               &deviceSpeed);
304     FxUsbUmFormatRequest(request.m_TrueRequest,
305                          &urb.UmUrbHeader,
306                          m_pHostTargetFile,
307                          TRUE);
308     status = SendSyncRequest(&request, 5);
309     if (!NT_SUCCESS(status)) {
310         goto Done;
311     }
312 
313     if (deviceSpeed == 3) {
314         m_Traits |= WDF_USB_DEVICE_TRAIT_AT_HIGH_SPEED;
315     }
316 
317     //
318     // User-mode events must be initialized manually.
319     //
320     status = m_InterfaceIterationLock.Initialize();
321     if (!NT_SUCCESS(status)) {
322         goto Done;
323     }
324 
325 Done:
326     return status;
327 }
328 
329 
330 
331 
332 _Must_inspect_result_
333 NTSTATUS
334 FxUsbDevice::GetString(
335     __in_ecount(*NumCharacters) PUSHORT String,
336     __in PUSHORT NumCharacters,
337     __in UCHAR StringIndex,
338     __in_opt USHORT LangID,
339     __in_opt WDFREQUEST Request,
340     __in_opt PWDF_REQUEST_SEND_OPTIONS Options
341     )
342 {
343     UMURB urb;
344     PUSB_STRING_DESCRIPTOR pDescriptor;
345     PVOID buffer = NULL;
346     USB_COMMON_DESCRIPTOR common;
347     ULONG length;
348     NTSTATUS status;
349 
350     if (String != NULL) {
351         length = sizeof(USB_STRING_DESCRIPTOR) + (*NumCharacters - 1) * sizeof(WCHAR);
352 
353         buffer = FxPoolAllocate(GetDriverGlobals(),
354                                 NonPagedPool,
355                                 length);
356 
357         if (buffer == NULL) {
358             status = STATUS_INSUFFICIENT_RESOURCES;
359             goto Done;
360         }
361 
362         RtlZeroMemory(buffer, length);
363         pDescriptor = (PUSB_STRING_DESCRIPTOR) buffer;
364     }
365     else {
366         RtlZeroMemory(&common, sizeof(common));
367 
368         length = sizeof(USB_COMMON_DESCRIPTOR);
369         pDescriptor = (PUSB_STRING_DESCRIPTOR) &common;
370     }
371 
372     FxUsbUmInitDescriptorUrb(&urb,
373                              m_WinUsbHandle,
374                              USB_STRING_DESCRIPTOR_TYPE,
375                              length,
376                              pDescriptor);
377     urb.UmUrbDescriptorRequest.Index = StringIndex;
378     urb.UmUrbDescriptorRequest.LanguageID = LangID;
379 
380     status = SendSyncUmUrb(&urb, 2, Request, Options);
381     if (NT_SUCCESS(status)) {
382         USHORT numChars;
383 
384         //
385         // Make sure we got an even number of bytes and that we got a header
386         //
387         if ((pDescriptor->bLength & 0x1) ||
388             pDescriptor->bLength < sizeof(USB_COMMON_DESCRIPTOR)) {
389             status = STATUS_DEVICE_DATA_ERROR;
390         }
391         else {
392             //
393             // bLength is the length of the entire descriptor.  Subtract off
394             // the descriptor header and then divide by the size of a WCHAR.
395             //
396             numChars =
397                 (pDescriptor->bLength - sizeof(USB_COMMON_DESCRIPTOR)) / sizeof(WCHAR);
398 
399             if (String != NULL) {
400                 if (*NumCharacters >= numChars) {
401                     length = numChars * sizeof(WCHAR);
402                 }
403                 else {
404                     length = *NumCharacters * sizeof(WCHAR);
405                     status = STATUS_BUFFER_OVERFLOW;
406                 }
407 
408                 *NumCharacters = numChars;
409                 RtlCopyMemory(String, pDescriptor->bString, length);
410             }
411             else {
412                 *NumCharacters = numChars;
413             }
414         }
415     }
416 
417     if (buffer != NULL) {
418         FxPoolFree(buffer);
419     }
420 
421 Done:
422 
423     return status;
424 }
425 
426 _Must_inspect_result_
427 NTSTATUS
428 FxUsbDevice::FormatStringRequest(
429     __in FxRequestBase* Request,
430     __in FxRequestBuffer *RequestBuffer,
431     __in UCHAR StringIndex,
432     __in USHORT LangID
433     )
434 /*++
435 
436 Routine Description:
437     Formats a request to retrieve a string from a string descriptor
438 
439 Arguments:
440     Request - request to format
441 
442     RequestBuffer - Buffer to be filled in when the request has completed
443 
444     StringIndex - index of the string
445 
446     LandID - language ID of the string to be retrieved
447 
448 Return Value:
449     NTSTATUS
450 
451   --*/
452 {
453     NTSTATUS status;
454     FxUsbDeviceStringContext* pContext;
455 
456     status = Request->ValidateTarget(this);
457     if (NT_SUCCESS(status)) {
458         DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
459                             "WDFUSBDEVICE %p, Request %p, setting target failed, "
460                             "%!STATUS!", GetHandle(), Request, status);
461 
462         return status;
463     }
464 
465     if (Request->HasContextType(FX_RCT_USB_STRING_REQUEST)) {
466         pContext = (FxUsbDeviceStringContext*) Request->GetContext();
467     }
468     else {
469         pContext = new(GetDriverGlobals()) FxUsbDeviceStringContext(FxUrbTypeLegacy);
470         if (pContext == NULL) {
471             return STATUS_INSUFFICIENT_RESOURCES;
472         }
473 
474         Request->SetContext(pContext);
475     }
476 
477     FxUsbUmInitDescriptorUrb(&pContext->m_UmUrb,
478                              m_WinUsbHandle,
479                              USB_STRING_DESCRIPTOR_TYPE,
480                              0,
481                              NULL);
482 
483     status = pContext->AllocateDescriptor(GetDriverGlobals(),
484                                           RequestBuffer->GetBufferLength());
485     if (!NT_SUCCESS(status)) {
486         return status;
487     }
488 
489     pContext->StoreAndReferenceMemory(RequestBuffer);
490     pContext->SetUrbInfo(StringIndex, LangID);
491 
492     FxUsbUmFormatRequest(Request, &pContext->m_UmUrb.UmUrbHeader, m_pHostTargetFile);
493 
494     return STATUS_SUCCESS;
495 }
496 
497 _Must_inspect_result_
498 NTSTATUS
499 FxUsbDevice::FormatControlRequest(
500     __in FxRequestBase* Request,
501     __in PWDF_USB_CONTROL_SETUP_PACKET SetupPacket,
502     __in FxRequestBuffer *RequestBuffer
503     )
504 {
505     FxUsbDeviceControlContext* pContext;
506     NTSTATUS status;
507     size_t bufferSize;
508 
509     bufferSize = RequestBuffer->GetBufferLength();
510 
511     //
512     // We can only transfer 2 bytes worth of data, so if the buffer is larger,
513     // fail here.
514     //
515     if (bufferSize > 0xFFFF) {
516         DoTraceLevelMessage(
517             GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
518             "Control transfer buffer is limited to 0xFFFF bytes in size, "
519             "%I64d requested ", bufferSize);
520 
521         return STATUS_INVALID_PARAMETER;
522     }
523 
524     status = Request->ValidateTarget(this);
525     if (!NT_SUCCESS(status)) {
526         DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
527                             "WDFUSBDEVICE %p, Request %p, setting target failed, "
528                             "%!STATUS!", GetHandle(), Request, status);
529         return status;
530     }
531 
532     if (Request->HasContextType(FX_RCT_USB_CONTROL_REQUEST)) {
533         pContext = (FxUsbDeviceControlContext*) Request->GetContext();
534     }
535     else {
536         pContext = new(GetDriverGlobals()) FxUsbDeviceControlContext(FxUrbTypeLegacy);
537         if (pContext == NULL) {
538             return STATUS_INSUFFICIENT_RESOURCES;
539         }
540 
541         Request->SetContext(pContext);
542     }
543 
544     FxUsbUmInitControlTransferUrb(&pContext->m_UmUrb,
545                                   m_WinUsbHandle,
546                                   0,
547                                   NULL);
548 
549     pContext->StoreAndReferenceMemory(this, RequestBuffer, SetupPacket);
550 
551     FxUsbUmFormatRequest(Request, &pContext->m_UmUrb.UmUrbHeader, m_pHostTargetFile);
552 
553     return STATUS_SUCCESS;
554 }
555 
556 VOID
557 FxUsbDeviceControlContext::StoreAndReferenceMemory(
558     __in FxUsbDevice* Device,
559     __in FxRequestBuffer* Buffer,
560     __in PWDF_USB_CONTROL_SETUP_PACKET SetupPacket
561     )
562 {
563     SetUsbType(WdfUsbRequestTypeDeviceControlTransfer);
564 
565     __super::StoreAndReferenceMemory(Buffer);
566 
567     //
568     // Convert WDF_USB_CONTROL_SETUP_PACKET to WINUSB_SETUP_PACKET
569     //
570     m_UmUrb.UmUrbControlTransfer.SetupPacket.RequestType = SetupPacket->Packet.bm.Byte;
571     m_UmUrb.UmUrbControlTransfer.SetupPacket.Request = SetupPacket->Packet.bRequest;
572     m_UmUrb.UmUrbControlTransfer.SetupPacket.Value = SetupPacket->Packet.wValue.Value;
573     m_UmUrb.UmUrbControlTransfer.SetupPacket.Index = SetupPacket->Packet.wIndex.Value;
574 
575     //
576     // Set the TransferBuffer values using what is stored in the Buffer
577     //
578     Buffer->AssignValues(&m_UmUrb.UmUrbControlTransfer.TransferBuffer,
579                          NULL,
580                          &m_UmUrb.UmUrbControlTransfer.TransferBufferLength);
581 
582     m_UmUrb.UmUrbControlTransfer.SetupPacket.Length =
583         (USHORT)m_UmUrb.UmUrbControlTransfer.TransferBufferLength;
584 }
585 
586 _Must_inspect_result_
587 NTSTATUS
588 FxUsbDevice::QueryUsbCapability(
589     __in
590     CONST GUID* CapabilityType,
591     __in
592     ULONG CapabilityBufferLength,
593     __drv_when(CapabilityBufferLength == 0, __out_opt)
594     __drv_when(CapabilityBufferLength != 0 && ResultLength == NULL, __out_bcount(CapabilityBufferLength))
595     __drv_when(CapabilityBufferLength != 0 && ResultLength != NULL, __out_bcount_part_opt(CapabilityBufferLength, *ResultLength))
596     PVOID CapabilityBuffer,
597     __out_opt
598     __drv_when(ResultLength != NULL,__deref_out_range(<=,CapabilityBufferLength))
599     PULONG ResultLength
600    )
601 {
602     NTSTATUS status;
603 
604     if (ResultLength != NULL) {
605         *ResultLength = 0;
606     }
607 
608     //
609     // We cannot send an actual query to the USB stack through winusb.
610     // However, we have the information to handle this query. It is not
611     // ideal to implement this API in this manner because we are making
612     // assumptions about the behavior of USB stack that can change in future.
613     // However, it is too late in the OS cycle to implement a correct solution.
614     // The ideal way is for winusb to expose this information. We should
615     // revisit this API in blue+1
616     //
617     if (RtlCompareMemory(CapabilityType,
618                          &GUID_USB_CAPABILITY_DEVICE_CONNECTION_HIGH_SPEED_COMPATIBLE,
619                          sizeof(GUID)) == sizeof(GUID)) {
620         if (m_Traits & WDF_USB_DEVICE_TRAIT_AT_HIGH_SPEED) {
621             status = STATUS_SUCCESS;
622         } else {
623             status = STATUS_NOT_SUPPORTED;
624         }
625     }
626     else if (RtlCompareMemory(CapabilityType,
627                                 &GUID_USB_CAPABILITY_DEVICE_CONNECTION_SUPER_SPEED_COMPATIBLE,
628                                 sizeof(GUID)) == sizeof(GUID)) {
629         if (m_DeviceDescriptor.bcdUSB >= 0x300) {
630             status = STATUS_SUCCESS;
631         } else {
632             status = STATUS_NOT_SUPPORTED;
633         }
634     }
635     else if (RtlCompareMemory(CapabilityType,
636                                 &GUID_USB_CAPABILITY_SELECTIVE_SUSPEND,
637                                 sizeof(GUID)) == sizeof(GUID)) {
638         //
639         // Both EHCI as well as XHCI stack support selective suspend.
640         // Since XHCI UCX interface is not open, there aren't any
641         // third party controller drivers to worry about. This can
642         // of course change in future
643         //
644         status = STATUS_SUCCESS;
645     }
646     else if (RtlCompareMemory(CapabilityType,
647                                 &GUID_USB_CAPABILITY_FUNCTION_SUSPEND,
648                                 sizeof(GUID)) == sizeof(GUID)) {
649         //
650         // Note that a SuperSpeed device will report a bcdUSB of 2.1
651         // when working on a 2.0 port. Therefore a bcdUSB of 3.0 also
652         // indicates that the device is actually working on 3.0, in
653         // which case we always support function suspend
654         //
655         if (m_DeviceDescriptor.bcdUSB >= 0x300) {
656             status = STATUS_SUCCESS;
657         } else {
658             status = STATUS_NOT_SUPPORTED;
659         }
660     }
661     else {
662         //
663         // We do not support chained MDLs or streams for a UMDF driver
664         // GUID_USB_CAPABILITY_CHAINED_MDLS
665         // GUID_USB_CAPABILITY_STATIC_STREAMS
666         //
667         status = STATUS_NOT_SUPPORTED;
668     }
669 
670     return status;
671 }
672 
673 _Must_inspect_result_
674 NTSTATUS
675 FxUsbDevice::SelectConfigSingle(
676     __in PWDF_OBJECT_ATTRIBUTES PipeAttributes,
677     __in PWDF_USB_DEVICE_SELECT_CONFIG_PARAMS Params
678     )
679 /*++
680 
681 Routine Description:
682     Since the device is already configured, all this routine
683     does is to make sure the alternate setting 0 is selected,
684     in case the client driver selected some other alternate
685     setting after the initial configuration
686 
687 Arguments:
688 
689 
690 Return Value:
691     NTSTATUS
692 
693   --*/
694 {
695     NTSTATUS status;
696 
697     RtlZeroMemory(&Params->Types.SingleInterface,
698                   sizeof(Params->Types.SingleInterface));
699 
700     if (m_NumInterfaces > 1) {
701         status = STATUS_INVALID_PARAMETER;
702 
703         DoTraceLevelMessage(
704             GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
705             "WDFUSBDEVICE %p cannot be auto configured for a single interface "
706             "since there are %d interfaces on the device, %!STATUS!",
707             GetHandle(), m_NumInterfaces, status);
708 
709         return status;
710     }
711 
712     //
713     // Use AlternateSetting 0 by default
714     //
715     if (m_Interfaces[0]->GetSettingDescriptor(0) == NULL) {
716         DoTraceLevelMessage(
717             GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
718             "WDFUSBDEVICE %p could not retrieve AlternateSetting 0 for "
719             "bInterfaceNumber %d", GetHandle(),
720             m_Interfaces[0]->m_InterfaceNumber);
721 
722         return STATUS_INVALID_PARAMETER;
723     }
724 
725     status = m_Interfaces[0]->CheckAndSelectSettingByIndex(0);
726 
727     if (!NT_SUCCESS(status)) {
728         DoTraceLevelMessage(
729             GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
730             "WDFUSBDEVICE %p set AlternateSetting 0 for interface 0"
731             "failed, %!STATUS!", GetHandle(), status);
732 
733         return status;
734     }
735 
736     if (PipeAttributes) {
737         status = m_Interfaces[0]->UpdatePipeAttributes(PipeAttributes);
738     }
739 
740     Params->Types.SingleInterface.ConfiguredUsbInterface =
741         m_Interfaces[0]->GetHandle();
742 
743     Params->Types.SingleInterface.NumberConfiguredPipes =
744         m_Interfaces[0]->GetNumConfiguredPipes();
745 
746     return status;
747 }
748 
749 _Must_inspect_result_
750 NTSTATUS
751 FxUsbDevice::SelectConfigMulti(
752     __in PWDF_OBJECT_ATTRIBUTES PipeAttributes,
753     __in PWDF_USB_DEVICE_SELECT_CONFIG_PARAMS Params
754     )
755 /*++
756 
757 Routine Description:
758     Since the device is already configured, all this routine
759     does is to make sure the alternate setting 0 is selected
760     for all interfaces, in case the client driver selected some
761     other alternate setting after the initial configuration
762 
763 
764 Arguments:
765     PipeAttributes - Should be NULL
766 
767     Params -
768 
769 Return Value:
770     NTSTATUS
771 
772   --*/
773 {
774     FxUsbInterface * pUsbInterface;
775     NTSTATUS status;
776     UCHAR i;
777     PFX_DRIVER_GLOBALS pFxDriverGlobals;
778 
779     pFxDriverGlobals = GetDriverGlobals();
780 
781     Params->Types.MultiInterface.NumberOfConfiguredInterfaces = 0;
782 
783     if (Params->Type == WdfUsbTargetDeviceSelectConfigTypeMultiInterface) {
784         for (i = 0; i < m_NumInterfaces; i++) {
785 
786             if (m_Interfaces[i]->GetSettingDescriptor(0) == NULL) {
787                 DoTraceLevelMessage(
788                     GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
789                     "WDFUSBDEVICE %p could not retrieve AlternateSetting 0 for "
790                     "bInterfaceNumber %d", GetHandle(),
791                     m_Interfaces[i]->m_InterfaceNumber);
792 
793                 status = STATUS_INVALID_PARAMETER;
794                 goto Done;
795             }
796 
797             status = m_Interfaces[i]->CheckAndSelectSettingByIndex(0);
798             if (!NT_SUCCESS(status)) {
799                 DoTraceLevelMessage(
800                     GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
801                     "WDFUSBDEVICE %p set AlternateSetting 0 for bInterfaceNumber %d"
802                     "failed, %!STATUS!",
803                     GetHandle(), m_Interfaces[i]->m_InterfaceNumber, status);
804                 goto Done;
805             }
806             if (PipeAttributes) {
807                 status = m_Interfaces[i]->UpdatePipeAttributes(PipeAttributes);
808             }
809         }
810     }
811     else {
812         //
813         // Type is WdfUsbTargetDeviceSelectConfigTypeInterfacesPairs
814         //
815         UCHAR interfacePairsNum = 0;
816         UCHAR bitArray[UCHAR_MAX/sizeof(UCHAR)];
817 
818         //
819         // initialize the bit array
820         //
821         RtlZeroMemory(bitArray, sizeof(bitArray));
822         //
823         // Build a list of descriptors from the Setting pairs
824         // passed in by the user. There could be interfaces not
825         // covered in the setting/interface pairs array passed.
826         // If that is the case return STATUS_INVALID_PARAMETER
827         //
828         for (i = 0; i < Params->Types.MultiInterface.NumberInterfaces ; i++) {
829             PWDF_USB_INTERFACE_SETTING_PAIR settingPair;
830             UCHAR interfaceNumber;
831             UCHAR altSettingIndex;
832 
833             settingPair = &Params->Types.MultiInterface.Pairs[i];
834 
835             FxObjectHandleGetPtr(GetDriverGlobals(),
836                                  settingPair->UsbInterface,
837                                  FX_TYPE_USB_INTERFACE ,
838                                  (PVOID*) &pUsbInterface);
839 
840             interfaceNumber = pUsbInterface->GetInterfaceNumber();
841             altSettingIndex = settingPair->SettingIndex;
842 
843             //
844             // do the following only if the bit is not already set
845             //
846             if (FxBitArraySet(&bitArray[0], interfaceNumber) == FALSE) {
847 
848                 if (pUsbInterface->GetSettingDescriptor(altSettingIndex) == NULL) {
849                     status = STATUS_INVALID_PARAMETER;
850                     DoTraceLevelMessage(
851                         GetDriverGlobals(), TRACE_LEVEL_ERROR,
852                         TRACINGIOTARGET,
853                         "WDFUSBDEVICE %p could not retrieve "
854                         "AlternateSetting %d for "
855                         "bInterfaceNumber %d, returning %!STATUS!",
856                         GetHandle(),
857                         altSettingIndex, interfaceNumber, status);
858                     goto Done;
859                 }
860 
861                 interfacePairsNum++;
862 
863                 //
864                 // Ensure alternate setting 0 is selected
865                 //
866                 status = pUsbInterface->CheckAndSelectSettingByIndex(
867                                 settingPair->SettingIndex);
868 
869                 if (!NT_SUCCESS(status)) {
870                     DoTraceLevelMessage(
871                         GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
872                         "WDFUSBDEVICE %p set AlternateSetting %d for bInterfaceNumber %d"
873                         "failed, %!STATUS!",
874                         GetHandle(), altSettingIndex, m_Interfaces[i]->m_InterfaceNumber,
875                         status);
876                     goto Done;
877                 }
878 
879                 if (PipeAttributes) {
880                     status = pUsbInterface->UpdatePipeAttributes(PipeAttributes);
881                 }
882             }
883 
884         }
885 
886         if (m_NumInterfaces > interfacePairsNum) {
887             status = STATUS_INVALID_PARAMETER;
888             DoTraceLevelMessage(
889                 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
890                 "WDFUSBDEVICE %p interface pairs set (%d) is not equal to actual "
891                 "# of interfaces (%d) reported by the device, %!STATUS!",
892                 GetObjectHandle(), interfacePairsNum, m_NumInterfaces, status);
893             goto Done;
894         }
895     } //WdfUsbTargetDeviceSelectConfigTypeInterfacesPairs
896 
897     status = STATUS_SUCCESS;
898     Params->Types.MultiInterface.NumberOfConfiguredInterfaces = m_NumInterfaces;
899 
900 Done:
901     return status;
902 }
903 
904 
905 _Must_inspect_result_
906 NTSTATUS
907 FxUsbDevice::Reset(
908     VOID
909     )
910 {
911     UMURB urb;
912     NTSTATUS status;
913 
914     RtlZeroMemory(&urb, sizeof(UMURB));
915 
916     urb.UmUrbSelectInterface.Hdr.Function = UMURB_FUNCTION_RESET_PORT;
917     urb.UmUrbSelectInterface.Hdr.Length = sizeof(_UMURB_HEADER);
918 
919     status = SendSyncUmUrb(&urb, 2);
920 
921     return status;
922 }
923 
924