1 // 2 // Copyright (C) Microsoft. All rights reserved. 3 // 4 #ifndef _FXUSBPIPE_H_ 5 #define _FXUSBPIPE_H_ 6 7 #include "fxusbrequestcontext.hpp" 8 #include "fxusbinterface.hpp" 9 10 // 11 // Technically, EHCI can support 4MB, but the usb driver stack doesn't 12 // allocate enough TDs for such a transfer, here I arbitrarily chose 2MB 13 // 14 enum FxUsbPipeMaxTransferSize { 15 FxUsbPipeHighSpeedMaxTransferSize = 2*1024*1024 , 16 FxUsbPipeLowSpeedMaxTransferSize = 256 * 1024, 17 FxUsbPipeControlMaxTransferSize = 4*1024 18 }; 19 20 struct FxUsbPipeTransferContext : public FxUsbRequestContext { 21 FxUsbPipeTransferContext( 22 __in FX_URB_TYPE UrbType 23 ); 24 25 ~FxUsbPipeTransferContext( 26 VOID 27 ); 28 29 __checkReturn 30 NTSTATUS 31 AllocateUrb( 32 __in USBD_HANDLE USBDHandle 33 ); 34 35 virtual 36 VOID 37 Dispose( 38 VOID 39 ); 40 41 virtual 42 VOID 43 CopyParameters( 44 __in FxRequestBase* Request 45 ); 46 47 virtual 48 VOID 49 StoreAndReferenceMemory( 50 __in FxRequestBuffer* Buffer 51 ); 52 53 virtual 54 VOID 55 ReleaseAndRestore( 56 __in FxRequestBase* Request 57 ); 58 59 VOID 60 SetUrbInfo( 61 __in USBD_PIPE_HANDLE PipeHandle, 62 __in ULONG TransferFlags 63 ); 64 65 USBD_STATUS 66 GetUsbdStatus( 67 VOID 68 ); 69 70 ULONG 71 GetUrbTransferLength( 72 VOID 73 ) 74 { 75 #if (FX_CORE_MODE == FX_CORE_KERNEL_MODE) 76 return m_Urb->TransferBufferLength; 77 #elif (FX_CORE_MODE == FX_CORE_USER_MODE) 78 return m_UmUrb.UmUrbBulkOrInterruptTransfer.TransferBufferLength; 79 #endif 80 } 81 82 private: 83 USBD_HANDLE m_USBDHandle; 84 85 public: 86 _URB_BULK_OR_INTERRUPT_TRANSFER m_UrbLegacy; 87 88 // 89 // m_Urb will either point to m_UrbLegacy or one allocated by USBD_UrbAllocate 90 // 91 _URB_BULK_OR_INTERRUPT_TRANSFER* m_Urb; 92 93 PMDL m_PartialMdl; 94 95 BOOLEAN m_UnlockPages; 96 }; 97 98 struct FxUsbUrbContext : public FxUsbRequestContext { 99 FxUsbUrbContext( 100 VOID 101 ); 102 103 USBD_STATUS 104 GetUsbdStatus( 105 VOID 106 ); 107 108 virtual 109 VOID 110 StoreAndReferenceMemory( 111 __in FxRequestBuffer* Buffer 112 ); 113 114 virtual 115 VOID 116 ReleaseAndRestore( 117 __in FxRequestBase* Request 118 ); 119 120 PURB m_pUrb; 121 }; 122 123 struct FxUsbPipeRequestContext : public FxUsbRequestContext { 124 FxUsbPipeRequestContext( 125 __in FX_URB_TYPE FxUrbType 126 ); 127 128 ~FxUsbPipeRequestContext( 129 VOID 130 ); 131 132 __checkReturn 133 NTSTATUS 134 AllocateUrb( 135 __in USBD_HANDLE USBDHandle 136 ); 137 138 virtual 139 VOID 140 Dispose( 141 VOID 142 ); 143 144 VOID 145 SetInfo( 146 __in WDF_USB_REQUEST_TYPE Type, 147 __in USBD_PIPE_HANDLE PipeHandle, 148 __in USHORT Function 149 ); 150 151 #if (FX_CORE_MODE == FX_CORE_USER_MODE) 152 VOID 153 SetInfo( 154 __in WDF_USB_REQUEST_TYPE Type, 155 __in WINUSB_INTERFACE_HANDLE WinUsbHandle, 156 __in UCHAR PipeId, 157 __in USHORT Function 158 ); 159 #endif 160 161 USBD_STATUS 162 GetUsbdStatus( 163 VOID 164 ); 165 166 private: 167 USBD_HANDLE m_USBDHandle; 168 169 public: 170 _URB_PIPE_REQUEST m_UrbLegacy; 171 172 // 173 // m_Urb will either point to m_UrbLegacy or one allocated by USBD_UrbAllocate 174 // 175 _URB_PIPE_REQUEST* m_Urb; 176 177 }; 178 179 struct FxUsbPipeRepeatReader { 180 // 181 // Request used to send IRPs 182 // 183 FxRequest* Request; 184 185 // 186 // IRP out of Request. Store it off so we don't have to call 187 // Request->GetSubmitIrp() everytime. 188 // 189 MdIrp RequestIrp; 190 191 // 192 // The containing parent 193 // 194 FxUsbPipeContinuousReader* Parent; 195 196 #if (FX_CORE_MODE == FX_CORE_KERNEL_MODE) 197 // 198 // DPC to queue ourselves to when a read completes so that we don't spin in 199 // the same thread repeatedly. 200 // 201 KDPC Dpc; 202 #elif (FX_CORE_MODE == FX_CORE_USER_MODE) 203 // 204 // Workitem to queue ourselves to when a read completes so that we don't recurse in 205 // the same thread repeatedly. 206 // 207 MxWorkItem m_ReadWorkItem; 208 209 // 210 // Check if the CR got called on a recursive call 211 // 212 LONG ThreadOwnerId; 213 #endif 214 215 // 216 // Event that is set when the reader has completed and is not 217 // 218 MxEvent ReadCompletedEvent; 219 }; 220 221 #define NUM_PENDING_READS_DEFAULT (2) 222 #define NUM_PENDING_READS_MAX (10) 223 224 // 225 // Work-item callback flags 226 // 227 #define FX_USB_WORKITEM_IN_PROGRESS (0x00000001) 228 #define FX_USB_WORKITEM_RERUN (0x00000002) 229 230 // 231 // In theory this can be a base class independent of bus type, but this is 232 // easier for now and there is no need on another bus type yet. 233 // 234 struct FxUsbPipeContinuousReader : public FxStump { 235 public: 236 FxUsbPipeContinuousReader( 237 __in FxUsbPipe* Pipe, 238 __in UCHAR NumReaders 239 ); 240 241 ~FxUsbPipeContinuousReader(); 242 243 _Must_inspect_result_ 244 NTSTATUS 245 Config( 246 __in PWDF_USB_CONTINUOUS_READER_CONFIG Config, 247 __in size_t TotalBufferLength 248 ); 249 250 PVOID 251 operator new( 252 __in size_t Size, 253 __in PFX_DRIVER_GLOBALS FxDriverGlobals, 254 __range(1, NUM_PENDING_READS_MAX) ULONG NumReaders 255 ); 256 257 _Must_inspect_result_ 258 NTSTATUS 259 FormatRepeater( 260 __in FxUsbPipeRepeatReader* Repeater 261 ); 262 263 VOID 264 CancelRepeaters( 265 VOID 266 ); 267 268 ULONG 269 ResubmitRepeater( 270 __in FxUsbPipeRepeatReader* Repeater, 271 __out NTSTATUS* Status 272 ); 273 274 protected: 275 VOID 276 DeleteMemory( 277 __in FxRequestBase* Request 278 ) 279 { 280 FxRequestContext* pContext; 281 282 pContext = Request->GetContext(); 283 if (pContext != NULL && pContext->m_RequestMemory != NULL) { 284 pContext->m_RequestMemory->Delete(); 285 // 286 // NOTE: Don't NULL out the m_RequestMemory member as this will 287 // prevent Reuse from releasing a reference on the m_RequestMemory object 288 // and hence these memory objects will not be freed. 289 // 290 } 291 } 292 293 BOOLEAN 294 QueueWorkItemLocked( 295 __in FxUsbPipeRepeatReader* Repeater 296 ); 297 298 __inline 299 VOID 300 FxUsbPipeRequestWorkItemHandler( 301 __in FxUsbPipeRepeatReader* FailedRepeater 302 ); 303 304 static 305 MdDeferredRoutineType 306 _FxUsbPipeContinuousReadDpc; 307 308 static 309 MX_WORKITEM_ROUTINE 310 _ReadWorkItem; 311 312 313 static 314 EVT_WDF_REQUEST_COMPLETION_ROUTINE 315 _FxUsbPipeRequestComplete; 316 317 static 318 EVT_SYSTEMWORKITEM 319 _FxUsbPipeRequestWorkItemThunk; 320 321 public: 322 // 323 // Completion routine for the client 324 // 325 PFN_WDF_USB_READER_COMPLETION_ROUTINE m_ReadCompleteCallback; 326 327 // 328 // Context for completion routine 329 // 330 WDFCONTEXT m_ReadCompleteContext; 331 332 // 333 // Callback to invoke when a reader fails 334 // 335 PFN_WDF_USB_READERS_FAILED m_ReadersFailedCallback; 336 337 // 338 // The owning pipe 339 // 340 FxUsbPipe* m_Pipe; 341 342 // 343 // Lookaside list from which we will allocate buffers for each new read 344 // 345 FxLookasideList* m_Lookaside; 346 347 // 348 // The devobj we are sending requests to 349 // 350 MdDeviceObject m_TargetDevice; 351 352 // 353 // Offsets and length into the memory buffers created by the lookaside list 354 // 355 WDFMEMORY_OFFSET m_Offsets; 356 357 // 358 // Work item to queue when we hit various errors 359 // 360 FxSystemWorkItem* m_WorkItem; 361 362 // 363 // Work item re-run context. 364 // 365 PVOID m_WorkItemRerunContext; 366 367 // 368 // This is a pointer to the work-item's thread object. This value is 369 // used for not deadlocking when misbehaved drivers (< v1.9) call 370 // WdfIoTargetStop from EvtUsbTargetPipeReadersFailed callback. 371 // 372 volatile POINTER_ALIGNMENT MxThread m_WorkItemThread; 373 374 // 375 // Work item flags (see FX_USB_WORKITEM_Xxx defines). 376 // 377 ULONG m_WorkItemFlags; 378 379 // 380 // Number of readers who have failed due to internal allocation errors 381 // 382 UCHAR m_NumFailedReaders; 383 384 // 385 // Number of readers 386 // 387 UCHAR m_NumReaders; 388 389 // 390 // Value to use with InterlockedXxx to test to see if a work item has been 391 // queued or not 392 // 393 BOOLEAN m_WorkItemQueued; 394 395 // 396 // Track whether the readers should be submitted when moving into the start 397 // state. We cannot just track a start -> start transition and not send 398 // the readers on that particular state transition because the first time 399 // we need to send the readers, the target is already in the started state 400 // 401 BOOLEAN m_ReadersSubmitted; 402 403 // 404 // Open ended array of readers. MUST be the last element in this structure. 405 // 406 FxUsbPipeRepeatReader m_Readers[1]; 407 }; 408 409 class FxUsbPipe : public FxIoTarget { 410 public: 411 friend FxUsbDevice; 412 friend FxUsbInterface; 413 friend FxUsbPipeContinuousReader; 414 415 FxUsbPipe( 416 __in PFX_DRIVER_GLOBALS FxDriverGlobals, 417 __in FxUsbDevice* UsbDevice 418 ); 419 420 VOID 421 InitPipe( 422 __in PUSBD_PIPE_INFORMATION PipeInfo, 423 __in UCHAR InterfaceNumber, 424 __in FxUsbInterface* UsbInterface 425 ); 426 427 #if (FX_CORE_MODE == FX_CORE_USER_MODE) 428 VOID 429 InitPipe( 430 __in PWINUSB_PIPE_INFORMATION PipeInfo, 431 __in UCHAR InterfaceNumber, 432 __in FxUsbInterface* UsbInterface 433 ); 434 #endif 435 436 _Must_inspect_result_ 437 virtual 438 NTSTATUS 439 GotoStartState( 440 __in PLIST_ENTRY RequestListHead, 441 __in BOOLEAN Lock = TRUE 442 ); 443 444 virtual 445 VOID 446 GotoStopState( 447 __in WDF_IO_TARGET_SENT_IO_ACTION Action, 448 __in PSINGLE_LIST_ENTRY SentRequestListHead, 449 __out PBOOLEAN Wait, 450 __in BOOLEAN LockSelf 451 ); 452 453 VOID 454 GotoPurgeState( 455 __in WDF_IO_TARGET_PURGE_IO_ACTION Action, 456 __in PLIST_ENTRY PendedRequestListHead, 457 __in PSINGLE_LIST_ENTRY SentRequestListHead, 458 __out PBOOLEAN Wait, 459 __in BOOLEAN LockSelf 460 ); 461 462 virtual 463 VOID 464 GotoRemoveState( 465 __in WDF_IO_TARGET_STATE NewState, 466 __in PLIST_ENTRY PendedRequestListHead, 467 __in PSINGLE_LIST_ENTRY SentRequestListHead, 468 __in BOOLEAN Lock, 469 __out PBOOLEAN Wait 470 ); 471 472 virtual 473 VOID 474 WaitForSentIoToComplete( 475 VOID 476 ); 477 478 __inline 479 VOID 480 SetNoCheckPacketSize( 481 VOID 482 ) 483 { 484 m_CheckPacketSize = FALSE; 485 } 486 487 VOID 488 GetInformation( 489 __out PWDF_USB_PIPE_INFORMATION PipeInformation 490 ); 491 492 BOOLEAN 493 IsType( 494 __in WDF_USB_PIPE_TYPE Type 495 ); 496 497 498 WDF_USB_PIPE_TYPE 499 GetType( 500 VOID 501 ); 502 503 WDFUSBPIPE 504 GetHandle( 505 VOID 506 ) 507 { 508 return (WDFUSBPIPE) GetObjectHandle(); 509 } 510 511 __inline 512 BOOLEAN 513 IsInEndpoint( 514 VOID 515 ) 516 { 517 // 518 // USB_ENDPOINT_DIRECTION_IN just does a bitwise compre so it could 519 // return 0 or some non zero value. Make sure the non zero value is 520 // TRUE 521 // 522 #if (FX_CORE_MODE == FX_CORE_KERNEL_MODE) 523 return USB_ENDPOINT_DIRECTION_IN(m_PipeInformation.EndpointAddress) ? TRUE : FALSE; 524 #elif (FX_CORE_MODE == FX_CORE_USER_MODE) 525 return USB_ENDPOINT_DIRECTION_IN(m_PipeInformationUm.PipeId) ? TRUE : FALSE; 526 #endif 527 } 528 529 __inline 530 BOOLEAN 531 IsOutEndpoint( 532 VOID 533 ) 534 { 535 // 536 // USB_ENDPOINT_DIRECTION_OUT just does a bitwise compre so it could 537 // return 0 or some non zero value. Make sure the non zero value is 538 // TRUE 539 // 540 #if (FX_CORE_MODE == FX_CORE_KERNEL_MODE) 541 return USB_ENDPOINT_DIRECTION_OUT(m_PipeInformation.EndpointAddress) ? TRUE : FALSE; 542 #elif (FX_CORE_MODE == FX_CORE_USER_MODE) 543 return USB_ENDPOINT_DIRECTION_OUT(m_PipeInformationUm.PipeId) ? TRUE : FALSE; 544 #endif 545 } 546 547 _Must_inspect_result_ 548 NTSTATUS 549 InitContinuousReader( 550 __in PWDF_USB_CONTINUOUS_READER_CONFIG Config, 551 __in size_t TotalBufferLength 552 ); 553 554 ULONG 555 GetMaxPacketSize( 556 VOID 557 ) 558 { 559 #if (FX_CORE_MODE == FX_CORE_KERNEL_MODE) 560 return m_PipeInformation.MaximumPacketSize; 561 #elif (FX_CORE_MODE == FX_CORE_USER_MODE) 562 return m_PipeInformationUm.MaximumPacketSize; 563 #endif 564 } 565 566 _Must_inspect_result_ 567 NTSTATUS 568 ValidateTransferLength( 569 __in size_t Length 570 ) 571 { 572 // 573 // Assumes this is not a control pipe 574 // 575 if (m_CheckPacketSize && 576 #if (FX_CORE_MODE == FX_CORE_KERNEL_MODE) 577 (Length % m_PipeInformation.MaximumPacketSize) != 0) { 578 #elif (FX_CORE_MODE == FX_CORE_USER_MODE) 579 (Length % m_PipeInformationUm.MaximumPacketSize) != 0) { 580 #endif 581 return STATUS_INVALID_BUFFER_SIZE; 582 } 583 else { 584 return STATUS_SUCCESS; 585 } 586 } 587 588 _Must_inspect_result_ 589 NTSTATUS 590 FormatTransferRequest( 591 __in FxRequestBase* Request, 592 __in FxRequestBuffer* Buffer, 593 __in ULONG TransferFlags = 0 594 ); 595 596 _Must_inspect_result_ 597 NTSTATUS 598 FormatAbortRequest( 599 __in FxRequestBase* Request 600 ); 601 602 _Must_inspect_result_ 603 NTSTATUS 604 FormatResetRequest( 605 __in FxRequestBase* Request 606 ); 607 608 static 609 _Must_inspect_result_ 610 NTSTATUS 611 _FormatTransfer( 612 __in PFX_DRIVER_GLOBALS FxDriverGlobals, 613 __in WDFUSBPIPE Pipe, 614 __in WDFREQUEST Request, 615 __in_opt WDFMEMORY TransferMemory, 616 __in_opt PWDFMEMORY_OFFSET TransferOffsets, 617 __in ULONG Flags 618 ); 619 620 static 621 _Must_inspect_result_ 622 NTSTATUS 623 _SendTransfer( 624 __in PFX_DRIVER_GLOBALS FxDriverGlobals, 625 __in WDFUSBPIPE Pipe, 626 __in_opt WDFREQUEST Request, 627 __in_opt PWDF_REQUEST_SEND_OPTIONS RequestOptions, 628 __in_opt PWDF_MEMORY_DESCRIPTOR MemoryDescriptor, 629 __out_opt PULONG BytesTransferred, 630 __in ULONG Flags 631 ); 632 633 USBD_PIPE_HANDLE 634 WdmGetPipeHandle( 635 VOID 636 ) 637 { 638 return m_PipeInformation.PipeHandle; 639 } 640 641 static 642 WDF_USB_PIPE_TYPE 643 _UsbdPipeTypeToWdf( 644 __in USBD_PIPE_TYPE UsbdPipeType 645 ) 646 { 647 const static WDF_USB_PIPE_TYPE types[] = { 648 WdfUsbPipeTypeControl, // UsbdPipeTypeControl 649 WdfUsbPipeTypeIsochronous, // UsbdPipeTypeIsochronous 650 WdfUsbPipeTypeBulk, // UsbdPipeTypeBulk 651 WdfUsbPipeTypeInterrupt, // UsbdPipeTypeInterrupt 652 }; 653 654 if (UsbdPipeType < sizeof(types)/sizeof(types[0])) { 655 return types[UsbdPipeType]; 656 } 657 else { 658 return WdfUsbPipeTypeInvalid; 659 } 660 } 661 662 NTSTATUS 663 Reset( 664 VOID 665 ); 666 667 USBD_HANDLE 668 GetUSBDHandle( 669 VOID 670 ) 671 { 672 return m_USBDHandle; 673 } 674 675 FX_URB_TYPE 676 GetUrbType( 677 VOID 678 ) 679 { 680 return m_UrbType; 681 } 682 683 public: 684 // 685 // Link for FxUsbDevice to use to hold a list of Pipes 686 // 687 LIST_ENTRY m_ListEntry; 688 689 protected: 690 ~FxUsbPipe(); 691 692 virtual 693 BOOLEAN 694 Dispose( 695 VOID 696 ); 697 698 FxUsbDevice* m_UsbDevice; 699 700 FxUsbInterface* m_UsbInterface; 701 702 // 703 // If the pipe does not have a continuous reader, this field is NULL. 704 // It is also cleared within the pipe's Dispose function after deleting 705 // the continuous reader to prevent misbehaved drivers from 706 // crashing the system when they call WdfIoTargetStop from their usb pipe's 707 // destroy callback. 708 // 709 FxUsbPipeContinuousReader* m_Reader; 710 711 // 712 // Information about this pipe 713 // 714 USBD_PIPE_INFORMATION m_PipeInformation; 715 716 #if (FX_CORE_MODE == FX_CORE_USER_MODE) 717 718 719 720 WINUSB_PIPE_INFORMATION m_PipeInformationUm; 721 #endif 722 723 // 724 // Interface associated with this pipe 725 // 726 UCHAR m_InterfaceNumber; 727 728 // 729 // Indicates if we should check that the buffer being trasnfered is of a 730 // multiple of max packet size. 731 // 732 BOOLEAN m_CheckPacketSize; 733 734 // 735 // The USBD_HANDLE exchanged by FxUsbDevice 736 // 737 USBD_HANDLE m_USBDHandle; 738 739 // 740 // If the client driver submits an URB to do a USB transfer, this field indicates 741 // the type of that Urb 742 // 743 FX_URB_TYPE m_UrbType; 744 745 }; 746 747 #endif // _FXUSBPIPE_H_ 748