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
FxUsbValidateConfigDescriptorHeaders(__in PFX_DRIVER_GLOBALS FxDriverGlobals,__in PUSB_CONFIGURATION_DESCRIPTOR ConfigDescriptor,__in size_t ConfigDescriptorLength)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
FxUsbFindDescriptorType(__in PVOID Buffer,__in size_t BufferLength,__in PVOID Start,__in LONG DescriptorType)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
FxUsbValidateDescriptorType(__in PFX_DRIVER_GLOBALS FxDriverGlobals,__in PUSB_CONFIGURATION_DESCRIPTOR ConfigDescriptor,__in PVOID Start,__in PVOID End,__in LONG DescriptorType,__in size_t SizeToValidate,__in FxUsbValidateDescriptorOp Op,__in ULONG MaximumNumDescriptorsToValidate)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
FxUsbParseConfigurationDescriptor(__in PUSB_CONFIGURATION_DESCRIPTOR ConfigDesc,__in UCHAR InterfaceNumber,__in UCHAR AlternateSetting)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
FxUsbCreateConfigRequest(__in PFX_DRIVER_GLOBALS FxDriverGlobals,__in PUSB_CONFIGURATION_DESCRIPTOR ConfigDesc,__in PUSBD_INTERFACE_LIST_ENTRY InterfaceList,__in ULONG DefaultMaxPacketSize)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
FxUsbUmInitDescriptorUrb(__inout PUMURB UmUrb,__in WINUSB_INTERFACE_HANDLE WinUsbHandle,__in UCHAR DescriptorType,__in ULONG BufferLength,__in PVOID Buffer)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
FxUsbUmInitControlTransferUrb(__inout PUMURB UmUrb,__in WINUSB_INTERFACE_HANDLE WinUsbHandle,__in ULONG BufferLength,__in PVOID Buffer)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
FxUsbUmInitInformationUrb(__inout PUMURB UmUrb,__in WINUSB_INTERFACE_HANDLE WinUsbHandle,__in ULONG BufferLength,__in PVOID Buffer)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