1 /*++
2 
3 Copyright (c) Microsoft Corporation
4 
5 Module Name:
6 
7     usbutil.cpp
8 
9 Abstract:
10 
11 
12 Author:
13 
14 Environment:
15 
16     Both kernel and user mode
17 
18 Revision History:
19 
20 --*/
21 
22 #include "fxusbpch.hpp"
23 
24 extern "C" {
25 #include "UsbUtil.tmh"
26 }
27 
28 VOID
29 FxFormatUsbRequest(
30     __in FxRequestBase* Request,
31     __in PURB Urb,
32     __in FX_URB_TYPE FxUrbType,
33     __drv_when(FxUrbType == FxUrbTypeUsbdAllocated, __in)
34     __drv_when(FxUrbType != FxUrbTypeUsbdAllocated, __in_opt)
35          USBD_HANDLE UsbdHandle
36     )
37 {
38     FxIrp* irp;
39 
40     ASSERT(Urb->UrbHeader.Length >= sizeof(_URB_HEADER));
41 
42     irp = Request->GetSubmitFxIrp();
43     irp->ClearNextStackLocation();
44     irp->SetMajorFunction(IRP_MJ_INTERNAL_DEVICE_CONTROL);
45     irp->SetParameterIoctlCode(IOCTL_INTERNAL_USB_SUBMIT_URB);
46 
47 
48 
49 
50 
51     if (FxUrbType == FxUrbTypeUsbdAllocated) {
52         USBD_AssignUrbToIoStackLocation(UsbdHandle, irp->GetNextIrpStackLocation(), Urb);
53     }
54     else {
55         irp->SetNextStackParameterOthersArgument1(Urb);
56     }
57 
58     Request->VerifierSetFormatted();
59 }
60 
61 NTSTATUS
62 FxFormatUrbRequest(
63     __in PFX_DRIVER_GLOBALS FxDriverGlobals,
64     __in FxIoTarget* Target,
65     __in FxRequestBase* Request,
66     __in FxRequestBuffer* Buffer,
67     __in FX_URB_TYPE FxUrbType,
68     __drv_when(FxUrbType == FxUrbTypeUsbdAllocated, __in)
69     __drv_when(FxUrbType != FxUrbTypeUsbdAllocated, __in_opt)
70          USBD_HANDLE UsbdHandle
71     )
72 {
73     FxUsbUrbContext* pContext;
74     NTSTATUS status;
75 
76     status = Request->ValidateTarget(Target);
77     if (!NT_SUCCESS(status)) {
78         DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIOTARGET,
79                             "FormatUrbRequest:  Target %p, Request %p, "
80                             "setting target failed, %!STATUS!",
81                             Target, Request, status);
82 
83         return status;
84     }
85 
86     if (Request->HasContextType(FX_RCT_USB_URB_REQUEST)) {
87         pContext = (FxUsbUrbContext*) Request->GetContext();
88     }
89     else {
90         pContext = new(Target->GetDriverGlobals()) FxUsbUrbContext();
91 
92         if (pContext == NULL) {
93             return STATUS_INSUFFICIENT_RESOURCES;
94         }
95 
96         Request->SetContext(pContext);
97     }
98 
99     //
100     // Always set the memory after determining the context.  This way we can
101     // free a previously referenced memory object if necessary.
102     //
103     pContext->StoreAndReferenceMemory(Buffer);
104 
105     FxFormatUsbRequest(Request, pContext->m_pUrb, FxUrbType, UsbdHandle);
106 
107     return STATUS_SUCCESS;
108 }
109 
110 NTSTATUS
111 FxUsbValidateConfigDescriptorHeaders(
112     __in PFX_DRIVER_GLOBALS FxDriverGlobals,
113     __in PUSB_CONFIGURATION_DESCRIPTOR ConfigDescriptor,
114     __in size_t ConfigDescriptorLength
115     )
116 {
117     PUCHAR pCur, pEnd;
118 
119     pCur = (PUCHAR) ConfigDescriptor;
120     pEnd = pCur + ConfigDescriptorLength;
121 
122     while (pCur < pEnd) {
123         PUSB_COMMON_DESCRIPTOR pDescriptor = (PUSB_COMMON_DESCRIPTOR) pCur;
124 
125         //
126         // Make sure we can safely deref bLength, bDescriptorType
127         //
128         if (pCur + sizeof(USB_COMMON_DESCRIPTOR) > pEnd) {
129             DoTraceLevelMessage(
130                 FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIOTARGET,
131                 "USB Configuration packet contains bad data, expected "
132                 "at least %d remaining bytes in config descriptor at "
133                 "offset %I64d, total size is %I64d",
134                 sizeof(USB_COMMON_DESCRIPTOR), pCur-(PUCHAR)ConfigDescriptor,
135                 ConfigDescriptorLength
136                 );
137             return STATUS_INVALID_PARAMETER;
138         }
139 
140         //
141         // Make sure bLength is within bounds of the config descriptor
142         //
143         if ((pCur + pDescriptor->bLength) > pEnd) {
144             DoTraceLevelMessage(
145                 FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIOTARGET,
146                 "USB Configuration packet contains bad data, descriptor at offset "
147                 "%I64d specified bLength %d, only %I64d bytes remaining in config "
148                 "descriptor, total size is %I64d",
149                 pCur-(PUCHAR)ConfigDescriptor, pDescriptor->bLength, pEnd-pCur,
150                 ConfigDescriptorLength
151                 );
152             return STATUS_INVALID_PARAMETER;
153         }
154 
155         if (pDescriptor->bLength == 0) {
156             DoTraceLevelMessage(
157                 FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIOTARGET,
158                 "USB Configuration packet contains bad data, descriptor at offset "
159                 "%I64d contains bLength == 0, this is due to a broken device or driver",
160                 pCur - (PUCHAR) ConfigDescriptor
161                 );
162             return STATUS_INVALID_PARAMETER;
163         }
164 
165 
166         pCur += pDescriptor->bLength;
167     }
168 
169     return STATUS_SUCCESS;
170 }
171 
172 
173 PUSB_COMMON_DESCRIPTOR
174 FxUsbFindDescriptorType(
175     __in PVOID Buffer,
176     __in size_t BufferLength,
177     __in PVOID Start,
178     __in LONG DescriptorType
179     )
180 {
181     PUSB_COMMON_DESCRIPTOR descriptor;
182     PUCHAR pCur, pEnd;
183 
184     pCur = (PUCHAR) Start;
185     pEnd = ((PUCHAR) Buffer) + BufferLength;
186 
187     while (pCur < pEnd) {
188         //
189         // Check to see if the current point is the desired descriptor type
190         //
191         descriptor = (PUSB_COMMON_DESCRIPTOR) pCur;
192 
193         if (descriptor->bDescriptorType == DescriptorType) {
194             return descriptor;
195         }
196 
197         pCur += descriptor->bLength;
198     }
199 
200     //
201     // Iterated to the end and found nothing
202     //
203     return NULL;
204 }
205 
206 NTSTATUS
207 FxUsbValidateDescriptorType(
208     __in PFX_DRIVER_GLOBALS FxDriverGlobals,
209     __in PUSB_CONFIGURATION_DESCRIPTOR ConfigDescriptor,
210     __in PVOID Start,
211     __in PVOID End,
212     __in LONG DescriptorType,
213     __in size_t SizeToValidate,
214     __in FxUsbValidateDescriptorOp Op,
215     __in ULONG MaximumNumDescriptorsToValidate
216     )
217 /*++
218 
219 Routine Description:
220     Validates the size of all descriptors within [Start, End] are of the correct
221     size.  Correct can either be == or >= SizeToValidate. Furthermore, the caller
222     can specify that validation only occur for the first N descriptors found
223 
224 Arguments:
225     FxDriverDriverGlobals - client driver globals
226     ConfigDescriptor - the entire config descriptor of the usb device
227     Start - the beginning of the buffer to start to validating descriptors within
228     End - end of the buffer of validation
229     DescriptorType - the type of descriptor to validate
230     SizeToValidate - the size of the descriptor required. Op determines if the
231                      check is == or >=
232     Op - the operation, == or >=, to perform for the size validation
233     MaximumNumDescriptorsToValidate - limit of the number of descriptors to validate,
234                                       if zero, all instances are validated
235 
236 Return Value:
237     NTSTATUS - !NT_SUCCESS if validation fails, NT_SUCCESS upon success
238 
239 --*/
240 {
241     PUSB_COMMON_DESCRIPTOR pDescriptor = NULL;
242     PVOID pCur = Start;
243     ULONG i = 1;
244 
245     while ((pDescriptor = FxUsbFindDescriptorType(Start,
246                                                   (PUCHAR)End-(PUCHAR)Start,
247                                                   pCur,
248                                                   DescriptorType
249                                                   )) != NULL) {
250         //
251         // Make sure bLength is the correct value
252         //
253         if (Op == FxUsbValidateDescriptorOpEqual) {
254             if (pDescriptor->bLength != SizeToValidate) {
255                 DoTraceLevelMessage(
256                     FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIOTARGET,
257                     "USB Configuration packet contains bad data, found descriptor "
258                     "#%d of type %d at offset %I64d, expected bLength of %I64d, found %d",
259                     i, DescriptorType, ((PUCHAR)pDescriptor)-((PUCHAR)ConfigDescriptor),
260                     SizeToValidate, pDescriptor->bLength
261                     );
262                 return STATUS_INVALID_PARAMETER;
263             }
264         }
265         else if (Op == FxUsbValidateDescriptorOpAtLeast) {
266             if (pDescriptor->bLength < SizeToValidate) {
267                 DoTraceLevelMessage(
268                     FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIOTARGET,
269                     "USB Configuration packet contains bad data, found descriptor "
270                     "#%d of type %d at offset %I64d, expected minimum bLength of %I64d, "
271                     "found %d",
272                     i, DescriptorType, ((PUCHAR)pDescriptor)-((PUCHAR)ConfigDescriptor),
273                     SizeToValidate, pDescriptor->bLength
274                     );
275                 return STATUS_INVALID_PARAMETER;
276             }
277         }
278 
279         //
280         // No need to validate WDF_PTR_ADD_OFFSET(pDescriptor, pDescriptor->bLength) > End
281         // because FxUsbValidateConfigDescriptorHeaders already has done so. End is either
282         // 1) == end of the config descriptor, in which case FxUsbValidateConfigDescriptorHeaders
283         //    directly validated bLength
284         // or
285         // 2) End is somewere in the middle of the config descriptor and ASSUMED to be the start
286         //    of a common descriptor header. To find that value of End, we would have had to use
287         //    pDescriptor->bLength to get to End, we would not overrun
288         //
289 
290         //
291         // i is one based, so the current value is Nth descriptor we have validated. If
292         // the caller wants to limit the number of descriptors validated by indicating a
293         // Max > 0, stop with success if the condition is met.
294         //
295         if (MaximumNumDescriptorsToValidate > 0 && i == MaximumNumDescriptorsToValidate) {
296             break;
297         }
298 
299         pCur = WDF_PTR_ADD_OFFSET(pDescriptor, pDescriptor->bLength);
300         i++;
301     }
302 
303     return STATUS_SUCCESS;
304 }
305 
306 PUSB_INTERFACE_DESCRIPTOR
307 FxUsbParseConfigurationDescriptor(
308     __in PUSB_CONFIGURATION_DESCRIPTOR ConfigDesc,
309     __in UCHAR InterfaceNumber,
310     __in UCHAR AlternateSetting
311     )
312 {
313     PUSB_INTERFACE_DESCRIPTOR found, usbInterface;
314     PVOID pStart;
315 
316     found = NULL,
317 
318     ASSERT(ConfigDesc->bDescriptorType == USB_CONFIGURATION_DESCRIPTOR_TYPE);
319 
320     //
321     // Walk the table of descriptors looking for an interface descriptor with
322     // parameters matching those passed in.
323     //
324     pStart = ConfigDesc;
325 
326     do {
327         //
328         // Search for descriptor type 'interface'
329         //
330         usbInterface = (PUSB_INTERFACE_DESCRIPTOR)
331             FxUsbFindDescriptorType(ConfigDesc,
332                                     ConfigDesc->wTotalLength,
333                                     pStart,
334                                     USB_INTERFACE_DESCRIPTOR_TYPE);
335 
336         //
337         // Check to see if have a matching descriptor by eliminating mismatches
338         //
339         if (usbInterface != NULL) {
340             found = usbInterface;
341 
342             if (InterfaceNumber != -1 &&
343                 usbInterface->bInterfaceNumber != InterfaceNumber) {
344                 found = NULL;
345             }
346 
347             if (AlternateSetting != -1 &&
348                 usbInterface->bAlternateSetting != AlternateSetting) {
349                 found = NULL;
350             }
351 
352             pStart = ((PUCHAR)usbInterface) + usbInterface->bLength;
353         }
354 
355         if (found != NULL) {
356             break;
357         }
358     } while (usbInterface!= NULL);
359 
360     return found;
361 }
362 
363 PURB
364 FxUsbCreateConfigRequest(
365     __in PFX_DRIVER_GLOBALS FxDriverGlobals,
366     __in PUSB_CONFIGURATION_DESCRIPTOR ConfigDesc,
367     __in PUSBD_INTERFACE_LIST_ENTRY InterfaceList,
368     __in ULONG DefaultMaxPacketSize
369     )
370 {
371     PURB urb;
372     PUSBD_INTERFACE_LIST_ENTRY pList;
373     USHORT size;
374 
375     //
376     // Our mission here is to construct a URB of the proper
377     // size and format for a select_configuration request.
378     //
379     // This function uses the configuration descriptor as a
380     // reference and builds a URB with interface_information
381     // structures for each interface requested in the interface
382     // list passed in
383     //
384     // NOTE: the config descriptor may contain interfaces that
385     // the caller does not specify in the list -- in this case
386     // the other interfaces will be ignored.
387     //
388 
389     //
390     // First figure out how many interfaces we are dealing with
391     //
392     pList = InterfaceList;
393 
394     //
395     // For a multiple interface configuration, GET_SELECT_CONFIGURATION_REQUEST_SIZE
396     // doesn't work as expected.  This is because it subtracts one from totalPipes
397     // to compensate for the embedded USBD_PIPE_INFORMATION in USBD_INTERFACE_INFORMATION.
398     // A multiple interface device will have more then one embedded
399     // USBD_PIPE_INFORMATION in its configuration request and any of these interfaces
400     // might not have any pipes on them.
401     //
402     // To fix, just iterate and compute the size incrementally.
403     //
404     if (InterfaceList[0].InterfaceDescriptor != NULL) {
405         size = sizeof(_URB_SELECT_CONFIGURATION) - sizeof(USBD_INTERFACE_INFORMATION);
406 
407         while (pList->InterfaceDescriptor != NULL) {
408             NTSTATUS status;
409 
410             status = RtlUShortAdd(size,
411                                   GET_USBD_INTERFACE_SIZE(
412                                         pList->InterfaceDescriptor->bNumEndpoints),
413                                   &size);
414             if (!NT_SUCCESS(status)) {
415                 DoTraceLevelMessage(
416                     FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIOTARGET,
417                     "InterfaceList %p, NumEndPoints 0x%x, "
418                     "Integer overflow while calculating interface size, %!STATUS!",
419                     InterfaceList,
420                     pList->InterfaceDescriptor->bNumEndpoints,
421                     status);
422                 return NULL;
423             }
424             pList++;
425         }
426     }
427     else {
428         size = sizeof(_URB_SELECT_CONFIGURATION);
429     }
430 
431     //
432     // Make sure we are dealing with a value that fits into a USHORT
433     //
434     ASSERT(size <= 0xFFFF);
435 
436     urb = (PURB) FxPoolAllocate(FxDriverGlobals, NonPagedPool, size);
437 
438     if (urb != NULL) {
439         PUCHAR pCur;
440 
441         //
442         // now all we have to do is initialize the urb
443         //
444         RtlZeroMemory(urb, size);
445 
446         pList = InterfaceList;
447 
448         pCur = (PUCHAR) &urb->UrbSelectConfiguration.Interface;
449         while (pList->InterfaceDescriptor != NULL) {
450             PUSB_INTERFACE_DESCRIPTOR pInterfaceDesc;
451             PUSBD_INTERFACE_INFORMATION pInterfaceInfo;
452 
453             pInterfaceDesc = pList->InterfaceDescriptor;
454 
455             pInterfaceInfo = (PUSBD_INTERFACE_INFORMATION) pCur;
456 #pragma prefast(suppress: __WARNING_BUFFER_OVERFLOW, "Esp:675");
457             pInterfaceInfo->InterfaceNumber = pInterfaceDesc->bInterfaceNumber;
458             pInterfaceInfo->AlternateSetting = pInterfaceDesc->bAlternateSetting;
459             pInterfaceInfo->NumberOfPipes = pInterfaceDesc->bNumEndpoints;
460             pInterfaceInfo->Length = (USHORT)
461                 GET_USBD_INTERFACE_SIZE(pInterfaceDesc->bNumEndpoints);
462 
463             for (LONG j = 0; j < pInterfaceDesc->bNumEndpoints; j++) {
464                 pInterfaceInfo->Pipes[j].PipeFlags = 0;
465                 pInterfaceInfo->Pipes[j].MaximumTransferSize = DefaultMaxPacketSize;
466             }
467 
468             ASSERT(pCur + pInterfaceInfo->Length <= ((PUCHAR) urb) + size);
469 
470             pList->Interface = pInterfaceInfo;
471 
472             pList++;
473             pCur += pInterfaceInfo->Length;
474         }
475 
476         urb->UrbHeader.Length   = size;
477         urb->UrbHeader.Function = URB_FUNCTION_SELECT_CONFIGURATION;
478         urb->UrbSelectConfiguration.ConfigurationDescriptor = ConfigDesc;
479     }
480 
481     return urb;
482 }
483 
484 #if (FX_CORE_MODE == FX_CORE_USER_MODE)
485 VOID
486 FxUsbUmFormatRequest(
487     __in FxRequestBase* Request,
488     __in_xcount(Urb->Length) PUMURB_HEADER Urb,
489     __in IWudfFile* HostFile,
490     __in BOOLEAN Reuse
491     )
492 /*++
493 
494 Routine Description:
495     Formats a request to become a USB request.
496     The next stack location is formatted with the UM URB
497     and dispatcher is set to be the USB dispatcher
498 
499 Arguments:
500     Request - Request to be formatted
501     Urb - UM URB the Request is to be formatted with
502           the pointer is passed as the header so
503           that SAL checkers don't complain on bigger size usage than what's passed in
504           (Total size of the URB union can be more than the the specific member that is passed in)
505     File - The host file used for internal I/O.
506 
507 Return Value:
508     None
509 
510 --*/
511 {
512     FX_VERIFY(INTERNAL, CHECK_NOT_NULL(Request));
513     FX_VERIFY(INTERNAL, CHECK_NOT_NULL(Urb));
514     FX_VERIFY(INTERNAL, CHECK_TODO(Urb->Length >= sizeof(_UMURB_HEADER)));
515     FX_VERIFY(INTERNAL, CHECK_NOT_NULL(HostFile));
516 
517     IWudfIoIrp* pIoIrp = NULL;
518     IWudfIrp* pIrp = Request->GetSubmitIrp();
519 
520     if (Reuse) {
521         //
522         // This allows us to use the same FxSyncRequest
523         // stack object multiple times.
524         //
525         Request->GetSubmitFxIrp()->Reuse(STATUS_SUCCESS);
526         Request->ClearFieldsForReuse();
527     }
528 
529     HRESULT hrQI = pIrp->QueryInterface(IID_IWudfIoIrp, (PVOID*)&pIoIrp);
530     FX_VERIFY(INTERNAL, CHECK_QI(hrQI, pIoIrp));
531 
532     pIoIrp->SetTypeForNextStackLocation(UMINT::WdfRequestInternalIoctl);
533 
534 
535 
536 
537     pIoIrp->SetDeviceIoControlParametersForNextStackLocation(IOCTL_INETRNAL_USB_SUBMIT_UMURB,
538                                                              0,
539                                                              0);
540 
541     pIoIrp->SetOtherParametersForNextStackLocation((PVOID*)&Urb,
542                                                    NULL,
543                                                    NULL,
544                                                    NULL);
545 
546     pIoIrp->SetFileForNextIrpStackLocation(HostFile);
547 
548     //
549     // Release reference taken by QI
550     //
551     SAFE_RELEASE(pIoIrp);
552     Request->VerifierSetFormatted();
553 }
554 
555 VOID
556 FxUsbUmInitDescriptorUrb(
557     __inout PUMURB UmUrb,
558     __in WINUSB_INTERFACE_HANDLE WinUsbHandle,
559     __in UCHAR DescriptorType,
560     __in ULONG BufferLength,
561     __in PVOID Buffer
562     )
563 {
564     RtlZeroMemory(UmUrb, sizeof(UMURB));
565 
566     UmUrb->UmUrbDescriptorRequest.Hdr.InterfaceHandle = WinUsbHandle;
567     UmUrb->UmUrbDescriptorRequest.Hdr.Function = UMURB_FUNCTION_GET_DESCRIPTOR;
568     UmUrb->UmUrbDescriptorRequest.Hdr.Length = sizeof(_UMURB_DESCRIPTOR_REQUEST);
569 
570     UmUrb->UmUrbDescriptorRequest.DescriptorType = DescriptorType;
571     UmUrb->UmUrbDescriptorRequest.Index = 0;
572     UmUrb->UmUrbDescriptorRequest.LanguageID = 0;
573     UmUrb->UmUrbDescriptorRequest.BufferLength = BufferLength;
574     UmUrb->UmUrbDescriptorRequest.Buffer = Buffer;
575 }
576 
577 VOID
578 FxUsbUmInitControlTransferUrb(
579     __inout PUMURB UmUrb,
580     __in WINUSB_INTERFACE_HANDLE WinUsbHandle,
581     __in ULONG BufferLength,
582     __in PVOID Buffer
583     )
584 {
585     RtlZeroMemory(UmUrb, sizeof(UMURB));
586 
587     UmUrb->UmUrbControlTransfer.Hdr.InterfaceHandle = WinUsbHandle;
588     UmUrb->UmUrbControlTransfer.Hdr.Function = UMURB_FUNCTION_CONTROL_TRANSFER;
589     UmUrb->UmUrbControlTransfer.Hdr.Length = sizeof(_UMURB_CONTROL_TRANSFER);
590 
591     UmUrb->UmUrbControlTransfer.TransferBufferLength = BufferLength;
592     UmUrb->UmUrbControlTransfer.TransferBuffer = Buffer;
593 }
594 
595 VOID
596 FxUsbUmInitInformationUrb(
597     __inout PUMURB UmUrb,
598     __in WINUSB_INTERFACE_HANDLE WinUsbHandle,
599     __in ULONG BufferLength,
600     __in PVOID Buffer
601     )
602 {
603     RtlZeroMemory(UmUrb, sizeof(UMURB));
604 
605     UmUrb->UmUrbDeviceInformation.Hdr.InterfaceHandle = WinUsbHandle;
606     UmUrb->UmUrbDeviceInformation.Hdr.Function = UMURB_FUNCTION_GET_DEVICE_INFORMATION;
607     UmUrb->UmUrbDeviceInformation.Hdr.Length = sizeof(_UMURB_DEVICE_INFORMATION);
608 
609     UmUrb->UmUrbDeviceInformation.InformationType = DEVICE_SPEED;
610     UmUrb->UmUrbDeviceInformation.BufferLength = BufferLength;
611     UmUrb->UmUrbDeviceInformation.Buffer = Buffer;
612 }
613 #endif
614 
615