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