1 /*-- 2 3 Copyright (C) Microsoft Corporation. All rights reserved. 4 5 Module Name: 6 7 scratch.c 8 9 Abstract: 10 11 Functions for using common scratch buffer 12 13 Environment: 14 15 kernel mode only 16 17 Notes: 18 19 20 Revision History: 21 22 --*/ 23 24 #include "stddef.h" 25 #include "string.h" 26 27 #include "ntddk.h" 28 #include "ntddstor.h" 29 #include "cdrom.h" 30 #include "ioctl.h" 31 #include "scratch.h" 32 #include "mmc.h" 33 34 #ifdef DEBUG_USE_WPP 35 #include "scratch.tmh" 36 #endif 37 38 // Forward declarations 39 EVT_WDF_REQUEST_COMPLETION_ROUTINE ScratchBuffer_ReadWriteCompletionRoutine; 40 41 #ifdef ALLOC_PRAGMA 42 43 #pragma alloc_text(PAGE, ScratchBuffer_Deallocate) 44 #pragma alloc_text(PAGE, ScratchBuffer_Allocate) 45 #pragma alloc_text(PAGE, ScratchBuffer_SetupSrb) 46 #pragma alloc_text(PAGE, ScratchBuffer_ExecuteCdbEx) 47 48 #endif 49 50 _IRQL_requires_max_(APC_LEVEL) 51 VOID 52 ScratchBuffer_Deallocate( 53 _Inout_ PCDROM_DEVICE_EXTENSION DeviceExtension 54 ) 55 /*++ 56 57 Routine Description: 58 59 release all resources allocated for scratch. 60 61 Arguments: 62 63 DeviceExtension - device extension 64 65 Return Value: 66 67 none 68 69 --*/ 70 { 71 PAGED_CODE (); 72 73 NT_ASSERT(DeviceExtension->ScratchContext.ScratchInUse == 0); 74 75 if (DeviceExtension->ScratchContext.ScratchHistory != NULL) 76 { 77 ExFreePool(DeviceExtension->ScratchContext.ScratchHistory); 78 DeviceExtension->ScratchContext.ScratchHistory = NULL; 79 } 80 if (DeviceExtension->ScratchContext.ScratchSense != NULL) 81 { 82 ExFreePool(DeviceExtension->ScratchContext.ScratchSense); 83 DeviceExtension->ScratchContext.ScratchSense = NULL; 84 } 85 if (DeviceExtension->ScratchContext.ScratchSrb != NULL) 86 { 87 ExFreePool(DeviceExtension->ScratchContext.ScratchSrb); 88 DeviceExtension->ScratchContext.ScratchSrb = NULL; 89 } 90 if (DeviceExtension->ScratchContext.ScratchBufferSize != 0) 91 { 92 DeviceExtension->ScratchContext.ScratchBufferSize = 0; 93 } 94 if (DeviceExtension->ScratchContext.ScratchBufferMdl != NULL) 95 { 96 IoFreeMdl(DeviceExtension->ScratchContext.ScratchBufferMdl); 97 DeviceExtension->ScratchContext.ScratchBufferMdl = NULL; 98 } 99 if (DeviceExtension->ScratchContext.ScratchBuffer != NULL) 100 { 101 ExFreePool(DeviceExtension->ScratchContext.ScratchBuffer); 102 DeviceExtension->ScratchContext.ScratchBuffer = NULL; 103 } 104 105 if (DeviceExtension->ScratchContext.PartialMdl != NULL) 106 { 107 IoFreeMdl(DeviceExtension->ScratchContext.PartialMdl); 108 DeviceExtension->ScratchContext.PartialMdl = NULL; 109 } 110 111 if (DeviceExtension->ScratchContext.ScratchRequest != NULL) 112 { 113 PIRP irp = WdfRequestWdmGetIrp(DeviceExtension->ScratchContext.ScratchRequest); 114 if (irp->MdlAddress) 115 { 116 irp->MdlAddress = NULL; 117 } 118 WdfObjectDelete(DeviceExtension->ScratchContext.ScratchRequest); 119 DeviceExtension->ScratchContext.ScratchRequest = NULL; 120 } 121 122 return; 123 } 124 125 _IRQL_requires_max_(APC_LEVEL) 126 BOOLEAN 127 ScratchBuffer_Allocate( 128 _Inout_ PCDROM_DEVICE_EXTENSION DeviceExtension 129 ) 130 /*++ 131 132 Routine Description: 133 134 allocate resources allocated for scratch. 135 136 Arguments: 137 138 DeviceExtension - device extension 139 140 Return Value: 141 142 none 143 144 --*/ 145 { 146 NTSTATUS status = STATUS_SUCCESS; 147 148 PAGED_CODE (); 149 150 NT_ASSERT(DeviceExtension->ScratchContext.ScratchInUse == 0); 151 152 // quick-exit if already allocated 153 if ((DeviceExtension->ScratchContext.ScratchBuffer != NULL) && 154 (DeviceExtension->ScratchContext.ScratchBufferMdl != NULL) && 155 (DeviceExtension->ScratchContext.ScratchBufferSize != 0) && 156 (DeviceExtension->ScratchContext.ScratchRequest != NULL) && 157 (DeviceExtension->ScratchContext.ScratchSrb != NULL) && 158 (DeviceExtension->ScratchContext.ScratchHistory != NULL) && 159 (DeviceExtension->ScratchContext.PartialMdl != NULL) 160 ) 161 { 162 return TRUE; 163 } 164 165 // validate max transfer already determined 166 NT_ASSERT(DeviceExtension->DeviceAdditionalData.MaxPageAlignedTransferBytes != 0); 167 168 // validate no partially-saved state 169 NT_ASSERT(DeviceExtension->ScratchContext.ScratchBuffer == NULL); 170 NT_ASSERT(DeviceExtension->ScratchContext.ScratchBufferMdl == NULL); 171 NT_ASSERT(DeviceExtension->ScratchContext.ScratchBufferSize == 0); 172 NT_ASSERT(DeviceExtension->ScratchContext.ScratchRequest == NULL); 173 NT_ASSERT(DeviceExtension->ScratchContext.PartialMdl == NULL); 174 175 // limit the scratch buffer to between 4k and 64k (so data length fits into USHORT -- req'd for many commands) 176 DeviceExtension->ScratchContext.ScratchBufferSize = min(DeviceExtension->DeviceAdditionalData.MaxPageAlignedTransferBytes, (64*1024)); 177 178 // allocate the buffer 179 if (NT_SUCCESS(status)) 180 { 181 DeviceExtension->ScratchContext.ScratchBuffer = ExAllocatePoolWithTag(NonPagedPoolNx, 182 DeviceExtension->ScratchContext.ScratchBufferSize, 183 CDROM_TAG_SCRATCH); 184 if (DeviceExtension->ScratchContext.ScratchBuffer == NULL) 185 { 186 status = STATUS_INSUFFICIENT_RESOURCES; 187 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT, 188 "Failed to allocate scratch buffer of %x bytes\n", 189 DeviceExtension->ScratchContext.ScratchBufferSize 190 )); 191 } 192 else if (BYTE_OFFSET(DeviceExtension->ScratchContext.ScratchBuffer) != 0) 193 { 194 status = STATUS_INTERNAL_ERROR; 195 TracePrint((TRACE_LEVEL_FATAL, TRACE_FLAG_INIT, 196 "Allocation of %x bytes non-paged pool was not " 197 "allocated on page boundary? STATUS_INTERNAL_ERROR\n", 198 DeviceExtension->ScratchContext.ScratchBufferSize 199 )); 200 } 201 } 202 203 // allocate the MDL 204 if (NT_SUCCESS(status)) 205 { 206 DeviceExtension->ScratchContext.ScratchBufferMdl = IoAllocateMdl(DeviceExtension->ScratchContext.ScratchBuffer, 207 DeviceExtension->ScratchContext.ScratchBufferSize, 208 FALSE, FALSE, NULL); 209 if (DeviceExtension->ScratchContext.ScratchBufferMdl == NULL) 210 { 211 status = STATUS_INSUFFICIENT_RESOURCES; 212 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT, 213 "Failed to allocate MDL for %x byte buffer\n", 214 DeviceExtension->ScratchContext.ScratchBufferSize 215 )); 216 } 217 else 218 { 219 MmBuildMdlForNonPagedPool(DeviceExtension->ScratchContext.ScratchBufferMdl); 220 } 221 } 222 223 // create the request 224 if (NT_SUCCESS(status)) 225 { 226 WDF_OBJECT_ATTRIBUTES attributes; 227 WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, 228 CDROM_REQUEST_CONTEXT); 229 230 status = WdfRequestCreate(&attributes, 231 DeviceExtension->IoTarget, 232 &DeviceExtension->ScratchContext.ScratchRequest); 233 234 if ((!NT_SUCCESS(status)) || 235 (DeviceExtension->ScratchContext.ScratchRequest == NULL)) 236 { 237 status = STATUS_INSUFFICIENT_RESOURCES; 238 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT, 239 "Failed to allocate scratch MDL \n")); 240 } 241 } 242 243 // allocate the srb 244 if (NT_SUCCESS(status)) 245 { 246 DeviceExtension->ScratchContext.ScratchSrb = ExAllocatePoolWithTag(NonPagedPoolNx, 247 sizeof(SCSI_REQUEST_BLOCK), 248 CDROM_TAG_SCRATCH); 249 250 if (DeviceExtension->ScratchContext.ScratchSrb == NULL) 251 { 252 status = STATUS_INSUFFICIENT_RESOURCES; 253 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT, 254 "Failed to allocate scratch SRB\n")); 255 } 256 } 257 258 // allocate the sense buffer 259 if (NT_SUCCESS(status)) 260 { 261 DeviceExtension->ScratchContext.ScratchSense = ExAllocatePoolWithTag(NonPagedPoolNx, 262 sizeof(SENSE_DATA), 263 CDROM_TAG_SCRATCH); 264 265 if (DeviceExtension->ScratchContext.ScratchSense == NULL) 266 { 267 status = STATUS_INSUFFICIENT_RESOURCES; 268 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT, 269 "Failed to allocate scratch sense data\n" 270 )); 271 } 272 } 273 274 // allocate the SRB history data 275 if (NT_SUCCESS(status)) 276 { 277 size_t allocationSize = sizeof(SRB_HISTORY) - sizeof(SRB_HISTORY_ITEM); 278 allocationSize += 20 * sizeof(SRB_HISTORY_ITEM); 279 280 DeviceExtension->ScratchContext.ScratchHistory = ExAllocatePoolWithTag(NonPagedPoolNx, 281 allocationSize, 282 CDROM_TAG_SCRATCH); 283 if (DeviceExtension->ScratchContext.ScratchHistory == NULL) 284 { 285 status = STATUS_INSUFFICIENT_RESOURCES; 286 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT, 287 "Failed to allocate scratch history buffer\n" 288 )); 289 } 290 else 291 { 292 // must be initialized here... 293 RtlZeroMemory(DeviceExtension->ScratchContext.ScratchHistory, allocationSize); 294 DeviceExtension->ScratchContext.ScratchHistory->TotalHistoryCount = 20; 295 } 296 } 297 298 // allocate the MDL 299 if (NT_SUCCESS(status)) 300 { 301 ULONG transferLength = 0; 302 303 status = RtlULongAdd(DeviceExtension->DeviceAdditionalData.MaxPageAlignedTransferBytes, PAGE_SIZE, &transferLength); 304 if (NT_SUCCESS(status)) 305 { 306 DeviceExtension->ScratchContext.PartialMdlIsBuilt = FALSE; 307 DeviceExtension->ScratchContext.PartialMdl = IoAllocateMdl(NULL, 308 transferLength, 309 FALSE, 310 FALSE, 311 NULL); 312 if (DeviceExtension->ScratchContext.PartialMdl == NULL) 313 { 314 status = STATUS_INSUFFICIENT_RESOURCES; 315 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT, 316 "Failed to allocate MDL for %x byte buffer\n", 317 DeviceExtension->ScratchContext.ScratchBufferSize 318 )); 319 } 320 else 321 { 322 NT_ASSERT(DeviceExtension->ScratchContext.PartialMdl->Size >= 323 (CSHORT)(sizeof(MDL) + BYTES_TO_PAGES(DeviceExtension->DeviceAdditionalData.MaxPageAlignedTransferBytes) * sizeof(PFN_NUMBER))); 324 } 325 } 326 else 327 { 328 status = STATUS_INTEGER_OVERFLOW; 329 } 330 } 331 332 // cleanup on failure 333 if (!NT_SUCCESS(status)) 334 { 335 ScratchBuffer_Deallocate(DeviceExtension); 336 } 337 338 return NT_SUCCESS(status); 339 } 340 341 342 VOID 343 ScratchBuffer_ResetItems( 344 _Inout_ PCDROM_DEVICE_EXTENSION DeviceExtension, 345 _In_ BOOLEAN ResetRequestHistory 346 ) 347 /*++ 348 349 Routine Description: 350 351 reset scratch items for reuse. 352 353 Arguments: 354 355 DeviceExtension - device extension 356 ResetRequestHistory - reset history fields or not 357 358 Return Value: 359 360 none 361 362 --*/ 363 { 364 NTSTATUS status = STATUS_SUCCESS; 365 WDF_REQUEST_REUSE_PARAMS reuseParams; 366 PIRP irp = NULL; 367 368 NT_ASSERT(DeviceExtension->ScratchContext.ScratchHistory != NULL); 369 NT_ASSERT(DeviceExtension->ScratchContext.ScratchSense != NULL); 370 NT_ASSERT(DeviceExtension->ScratchContext.ScratchSrb != NULL); 371 NT_ASSERT(DeviceExtension->ScratchContext.ScratchRequest != NULL); 372 NT_ASSERT(DeviceExtension->ScratchContext.ScratchBufferSize != 0); 373 NT_ASSERT(DeviceExtension->ScratchContext.ScratchBuffer != NULL); 374 NT_ASSERT(DeviceExtension->ScratchContext.ScratchBufferMdl != NULL); 375 NT_ASSERT(DeviceExtension->ScratchContext.ScratchInUse != 0); 376 377 irp = WdfRequestWdmGetIrp(DeviceExtension->ScratchContext.ScratchRequest); 378 379 if (ResetRequestHistory) 380 { 381 PSRB_HISTORY history = DeviceExtension->ScratchContext.ScratchHistory; 382 RtlZeroMemory(history->History, sizeof(SRB_HISTORY_ITEM) * history->TotalHistoryCount); 383 history->ClassDriverUse[0] = 0; 384 history->ClassDriverUse[1] = 0; 385 history->ClassDriverUse[2] = 0; 386 history->ClassDriverUse[3] = 0; 387 history->UsedHistoryCount = 0; 388 } 389 390 // re-use the KMDF request object 391 392 // deassign the MdlAddress, this is the value we assign explicitly. 393 // this is to prevent WdfRequestReuse to release the Mdl unexpectly. 394 if (irp->MdlAddress) 395 { 396 irp->MdlAddress = NULL; 397 } 398 399 WDF_REQUEST_REUSE_PARAMS_INIT(&reuseParams, WDF_REQUEST_REUSE_NO_FLAGS, STATUS_NOT_SUPPORTED); 400 status = WdfRequestReuse(DeviceExtension->ScratchContext.ScratchRequest, &reuseParams); 401 // WDF request to format the request befor sending it 402 if (NT_SUCCESS(status)) 403 { 404 // clean up completion routine. 405 WdfRequestSetCompletionRoutine(DeviceExtension->ScratchContext.ScratchRequest, NULL, NULL); 406 407 status = WdfIoTargetFormatRequestForInternalIoctlOthers(DeviceExtension->IoTarget, 408 DeviceExtension->ScratchContext.ScratchRequest, 409 IOCTL_SCSI_EXECUTE_IN, 410 NULL, NULL, 411 NULL, NULL, 412 NULL, NULL); 413 if (!NT_SUCCESS(status)) 414 { 415 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, 416 "ScratchBuffer_ResetItems: WdfIoTargetFormatRequestForInternalIoctlOthers failed, %!STATUS!\n", 417 status)); 418 } 419 } 420 421 RtlZeroMemory(DeviceExtension->ScratchContext.ScratchSense, sizeof(SENSE_DATA)); 422 RtlZeroMemory(DeviceExtension->ScratchContext.ScratchSrb, sizeof(SCSI_REQUEST_BLOCK)); 423 424 return; 425 } 426 427 428 NTSTATUS 429 ScratchBuffer_PerformNextReadWrite( 430 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, 431 _In_ BOOLEAN FirstTry 432 ) 433 /*++ 434 435 Routine Description: 436 437 This function asynchronously sends the next read/write SRB down the stack. 438 439 Arguments: 440 441 DeviceExtension - Device extension 442 443 Return Value: 444 445 none 446 447 --*/ 448 { 449 PCDROM_SCRATCH_READ_WRITE_CONTEXT readWriteContext = &DeviceExtension->ScratchContext.ScratchReadWriteContext; 450 PCDROM_REQUEST_CONTEXT requestContext = RequestGetContext(DeviceExtension->ScratchContext.ScratchRequest); 451 WDFREQUEST originalRequest = requestContext->OriginalRequest; 452 NTSTATUS status = STATUS_SUCCESS; 453 454 ULONG transferSize; 455 BOOLEAN usePartialMdl; 456 457 transferSize = min((readWriteContext->EntireXferLen - readWriteContext->TransferedBytes), readWriteContext->MaxLength); 458 459 if (FirstTry) 460 { 461 DeviceExtension->ScratchContext.NumRetries = 0; 462 } 463 464 ScratchBuffer_ResetItems(DeviceExtension, FALSE); 465 466 usePartialMdl = (readWriteContext->PacketsCount > 1 || readWriteContext->TransferedBytes > 0); 467 468 ScratchBuffer_SetupReadWriteSrb(DeviceExtension, 469 originalRequest, 470 readWriteContext->StartingOffset, 471 transferSize, 472 readWriteContext->DataBuffer, 473 readWriteContext->IsRead, 474 usePartialMdl 475 ); 476 477 WdfRequestSetCompletionRoutine(DeviceExtension->ScratchContext.ScratchRequest, 478 ScratchBuffer_ReadWriteCompletionRoutine, DeviceExtension); 479 480 status = ScratchBuffer_SendSrb(DeviceExtension, FALSE, (FirstTry ? &readWriteContext->SrbHistoryItem : NULL)); 481 482 return status; 483 } 484 485 486 VOID 487 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ 488 ScratchBuffer_ReadWriteTimerRoutine( 489 struct _KDPC *Dpc, 490 PVOID DeferredContext, 491 PVOID SystemArgument1, 492 PVOID SystemArgument2 493 ) 494 /*++ 495 496 Routine Description: 497 498 Timer routine for retrying read and write requests. 499 500 Arguments: 501 502 Timer - WDF timer 503 504 Return Value: 505 506 none 507 508 --*/ 509 { 510 PCDROM_DEVICE_EXTENSION deviceExtension = NULL; 511 PCDROM_SCRATCH_READ_WRITE_CONTEXT readWriteContext = NULL; 512 WDFREQUEST originalRequest = NULL; 513 PCDROM_REQUEST_CONTEXT requestContext = NULL; 514 NTSTATUS status = STATUS_SUCCESS; 515 KIRQL oldIrql; 516 517 UNREFERENCED_PARAMETER(Dpc); 518 UNREFERENCED_PARAMETER(SystemArgument1); 519 UNREFERENCED_PARAMETER(SystemArgument2); 520 521 if (DeferredContext == NULL) 522 { 523 // This is impossible, but definition of KDEFERRED_ROUTINE allows optional argument, 524 // and thus OACR will complain. 525 526 return; 527 } 528 529 originalRequest = (WDFREQUEST) DeferredContext; 530 requestContext = RequestGetContext(originalRequest); 531 532 KeAcquireSpinLock(&requestContext->ReadWriteCancelSpinLock, &oldIrql); 533 534 if (!requestContext->ReadWriteIsCompleted) 535 { 536 // As the first step, unregister the cancellation routine 537 status = WdfRequestUnmarkCancelable(originalRequest); 538 } 539 else 540 { 541 status = STATUS_CANCELLED; 542 } 543 544 KeReleaseSpinLock(&requestContext->ReadWriteCancelSpinLock, oldIrql); 545 546 if (status != STATUS_CANCELLED) 547 { 548 deviceExtension = requestContext->DeviceExtension; 549 readWriteContext = &deviceExtension->ScratchContext.ScratchReadWriteContext; 550 551 // We use timer only for retries, that's why the second parameter is always FALSE 552 status = ScratchBuffer_PerformNextReadWrite(deviceExtension, FALSE); 553 554 if (!NT_SUCCESS(status)) 555 { 556 ScratchBuffer_EndUse(deviceExtension); 557 RequestCompletion(deviceExtension, originalRequest, status, readWriteContext->TransferedBytes); 558 } 559 } 560 561 // 562 // Drop the extra reference 563 // 564 WdfObjectDereference(originalRequest); 565 } 566 567 568 EVT_WDF_REQUEST_CANCEL ScratchBuffer_ReadWriteEvtRequestCancel; 569 570 VOID 571 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ 572 ScratchBuffer_ReadWriteEvtRequestCancel( 573 _In_ WDFREQUEST Request 574 ) 575 /*++ 576 577 Routine Description: 578 579 Cancels a request waiting for the read/write timer to expire. This function does not 580 support cancellation of requests that have already been sent down. 581 582 Arguments: 583 584 Request - WDF request 585 586 Return Value: 587 588 none 589 590 --*/ 591 { 592 PCDROM_REQUEST_CONTEXT requestContext = RequestGetContext(Request); 593 PCDROM_DEVICE_EXTENSION deviceExtension = requestContext->DeviceExtension; 594 PCDROM_SCRATCH_READ_WRITE_CONTEXT readWriteContext = &deviceExtension->ScratchContext.ScratchReadWriteContext; 595 KIRQL oldIrql; 596 597 KeAcquireSpinLock(&requestContext->ReadWriteCancelSpinLock, &oldIrql); 598 599 if (KeCancelTimer(&requestContext->ReadWriteTimer)) 600 { 601 // 602 // Timer is canceled, we own the request. Drop the reference we took before 603 // queueing the timer. 604 // 605 WdfObjectDereference(Request); 606 } 607 else 608 { 609 // 610 // Timer will run and drop the reference but it won't complete the request 611 // because we set IsCompleted to TRUE 612 // 613 } 614 615 requestContext->ReadWriteIsCompleted = TRUE; 616 617 KeReleaseSpinLock(&requestContext->ReadWriteCancelSpinLock, oldIrql); 618 619 ScratchBuffer_EndUse(deviceExtension); 620 621 // If WdfTimerStop returned TRUE, it means this request was scheduled for a retry 622 // and the retry has not happened yet. We just need to cancel it and release the scratch buffer. 623 RequestCompletion(deviceExtension, Request, STATUS_CANCELLED, readWriteContext->TransferedBytes); 624 } 625 626 VOID 627 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ 628 ScratchBuffer_ReadWriteCompletionRoutine( 629 _In_ WDFREQUEST Request, 630 _In_ WDFIOTARGET Target, 631 _In_ PWDF_REQUEST_COMPLETION_PARAMS Params, 632 _In_ WDFCONTEXT Context 633 ) 634 /*++ 635 636 Routine Description: 637 638 Read/write request completion routine. 639 640 Arguments: 641 Request - WDF request 642 Target - The IO target the request was completed by. 643 Params - the request completion parameters 644 Context - context 645 646 Return Value: 647 648 none 649 650 --*/ 651 { 652 PCDROM_DEVICE_EXTENSION deviceExtension = (PCDROM_DEVICE_EXTENSION) Context; 653 PCDROM_SCRATCH_READ_WRITE_CONTEXT readWriteContext = &deviceExtension->ScratchContext.ScratchReadWriteContext; 654 NTSTATUS status = STATUS_SUCCESS; 655 PCDROM_REQUEST_CONTEXT requestContext = RequestGetContext(deviceExtension->ScratchContext.ScratchRequest); 656 WDFREQUEST originalRequest = requestContext->OriginalRequest; 657 658 if (!NT_SUCCESS(WdfRequestGetStatus(Request))) 659 { 660 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT, 661 "WdfRequestSend: %lx\n", 662 WdfRequestGetStatus(Request) 663 )); 664 } 665 666 UNREFERENCED_PARAMETER(Params); 667 UNREFERENCED_PARAMETER(Target); 668 669 // We are not calling ScratchBuffer_BeginUse / ScratchBuffer_EndUse in this function, because we already own 670 // the scratch buffer if this function is being called. 671 672 if ((deviceExtension->ScratchContext.ScratchSrb->SrbStatus == SRB_STATUS_ABORTED) && 673 (deviceExtension->ScratchContext.ScratchSrb->InternalStatus == STATUS_CANCELLED)) 674 { 675 // The request has been cancelled, just need to complete it 676 } 677 else if (SRB_STATUS(deviceExtension->ScratchContext.ScratchSrb->SrbStatus) != SRB_STATUS_SUCCESS) 678 { 679 // The SCSI command that we sent down has failed, retry it if necessary 680 BOOLEAN shouldRetry = TRUE; 681 LONGLONG retryIn100nsUnits = 0; 682 683 shouldRetry = RequestSenseInfoInterpretForScratchBuffer(deviceExtension, 684 deviceExtension->ScratchContext.NumRetries, 685 &status, 686 &retryIn100nsUnits); 687 688 if (shouldRetry) 689 { 690 deviceExtension->ScratchContext.NumRetries++; 691 692 if (retryIn100nsUnits == 0) 693 { 694 // We take a shortcut here by calling ScratchBuffer_PerformNextReadWrite directly: 695 // this helps to avoid unnecessary context switch. 696 status = ScratchBuffer_PerformNextReadWrite(deviceExtension, FALSE); 697 698 if (NT_SUCCESS(status)) 699 { 700 // We're not done with the request yet, no need to complete it now 701 return; 702 } 703 } 704 else 705 { 706 PCDROM_REQUEST_CONTEXT originalRequestContext = RequestGetContext(originalRequest); 707 KIRQL oldIrql; 708 709 // 710 // Initialize the spin lock and timer local to the original request. 711 // 712 if (!originalRequestContext->ReadWriteRetryInitialized) 713 { 714 KeInitializeSpinLock(&originalRequestContext->ReadWriteCancelSpinLock); 715 KeInitializeTimer(&originalRequestContext->ReadWriteTimer); 716 KeInitializeDpc(&originalRequestContext->ReadWriteDpc, ScratchBuffer_ReadWriteTimerRoutine, originalRequest); 717 originalRequestContext->ReadWriteRetryInitialized = TRUE; 718 } 719 720 KeAcquireSpinLock(&requestContext->ReadWriteCancelSpinLock, &oldIrql); 721 722 status = WdfRequestMarkCancelableEx(originalRequest, ScratchBuffer_ReadWriteEvtRequestCancel); 723 724 if (status == STATUS_CANCELLED) 725 { 726 requestContext->ReadWriteIsCompleted = TRUE; 727 728 KeReleaseSpinLock(&requestContext->ReadWriteCancelSpinLock, oldIrql); 729 } 730 else 731 { 732 LARGE_INTEGER t; 733 734 t.QuadPart = -retryIn100nsUnits; 735 736 WdfObjectReference(originalRequest); 737 738 // Use negative time to indicate that we want a relative delay 739 KeSetTimer(&originalRequestContext->ReadWriteTimer, 740 t, 741 &originalRequestContext->ReadWriteDpc 742 ); 743 744 KeReleaseSpinLock(&requestContext->ReadWriteCancelSpinLock, oldIrql); 745 746 return; 747 } 748 } 749 } 750 } 751 else 752 { 753 // The SCSI command has succeeded 754 readWriteContext->DataBuffer += deviceExtension->ScratchContext.ScratchSrb->DataTransferLength; 755 readWriteContext->StartingOffset.QuadPart += deviceExtension->ScratchContext.ScratchSrb->DataTransferLength; 756 readWriteContext->TransferedBytes += deviceExtension->ScratchContext.ScratchSrb->DataTransferLength; 757 readWriteContext->PacketsCount--; 758 759 // Update the SRB history item 760 if (readWriteContext->SrbHistoryItem) 761 { 762 ULONG senseSize; 763 764 // Query the tick count and store in the history 765 KeQueryTickCount(&readWriteContext->SrbHistoryItem->TickCountCompleted); 766 767 // Copy the SRB Status... 768 readWriteContext->SrbHistoryItem->SrbStatus = deviceExtension->ScratchContext.ScratchSrb->SrbStatus; 769 770 // Determine the amount of valid sense data 771 if (deviceExtension->ScratchContext.ScratchSrb->SenseInfoBufferLength >= 772 RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseLength)) 773 { 774 PSENSE_DATA sense = (PSENSE_DATA)deviceExtension->ScratchContext.ScratchSrb->SenseInfoBuffer; 775 senseSize = RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseLength) + 776 sense->AdditionalSenseLength; 777 senseSize = min(senseSize, sizeof(SENSE_DATA)); 778 } 779 else 780 { 781 senseSize = deviceExtension->ScratchContext.ScratchSrb->SenseInfoBufferLength; 782 } 783 784 // Normalize the sense data copy in the history 785 RtlZeroMemory(&(readWriteContext->SrbHistoryItem->NormalizedSenseData), sizeof(SENSE_DATA)); 786 RtlCopyMemory(&(readWriteContext->SrbHistoryItem->NormalizedSenseData), 787 deviceExtension->ScratchContext.ScratchSrb->SenseInfoBuffer, senseSize); 788 } 789 790 // Check whether we need to send more SCSI commands to complete the request 791 if (readWriteContext->PacketsCount > 0) 792 { 793 status = ScratchBuffer_PerformNextReadWrite(deviceExtension, TRUE); 794 795 if (NT_SUCCESS(status)) 796 { 797 // We're not done with the request yet, no need to complete it now 798 return; 799 } 800 } 801 } 802 803 ScratchBuffer_EndUse(deviceExtension); 804 805 806 RequestCompletion(deviceExtension, originalRequest, status, readWriteContext->TransferedBytes); 807 } 808 809 _IRQL_requires_max_(APC_LEVEL) 810 VOID 811 ScratchBuffer_SetupSrb( 812 _Inout_ PCDROM_DEVICE_EXTENSION DeviceExtension, 813 _In_opt_ WDFREQUEST OriginalRequest, 814 _In_ ULONG MaximumTransferLength, 815 _In_ BOOLEAN GetDataFromDevice 816 ) 817 /*++ 818 819 Routine Description: 820 821 setup scratch SRB for sending out. 822 823 Arguments: 824 825 DeviceExtension - device extension 826 OriginalRequest - original request delivered by WDF 827 MaximumTransferLength - transfer length 828 GetDataFromDevice - TRUE (get data from device); FALSE (send data to device) 829 830 Return Value: 831 832 none 833 834 --*/ 835 { 836 WDFREQUEST request = DeviceExtension->ScratchContext.ScratchRequest; 837 PIRP irp = WdfRequestWdmGetIrp(request); 838 PSCSI_REQUEST_BLOCK srb = DeviceExtension->ScratchContext.ScratchSrb; 839 PIO_STACK_LOCATION irpStack = NULL; 840 PCDROM_REQUEST_CONTEXT requestContext = RequestGetContext(request); 841 842 PAGED_CODE (); 843 844 requestContext->OriginalRequest = OriginalRequest; 845 846 // set to use the full scratch buffer via the scratch SRB 847 irpStack = IoGetNextIrpStackLocation(irp); 848 irpStack->MajorFunction = IRP_MJ_SCSI; 849 if (MaximumTransferLength == 0) 850 { 851 irpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_NONE; 852 } 853 else if (GetDataFromDevice) 854 { 855 irpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_IN; 856 } 857 else 858 { 859 irpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_OUT; 860 } 861 irpStack->Parameters.Scsi.Srb = srb; 862 863 if (MaximumTransferLength > 0) 864 { 865 // the Irp must show the MDL's address for the transfer 866 irp->MdlAddress = DeviceExtension->ScratchContext.ScratchBufferMdl; 867 868 srb->DataBuffer = DeviceExtension->ScratchContext.ScratchBuffer; 869 } 870 871 // prepare the SRB with default values 872 srb->Length = SCSI_REQUEST_BLOCK_SIZE; 873 srb->Function = SRB_FUNCTION_EXECUTE_SCSI; 874 srb->QueueAction = SRB_SIMPLE_TAG_REQUEST; 875 srb->SrbStatus = 0; 876 srb->ScsiStatus = 0; 877 srb->NextSrb = NULL; 878 srb->OriginalRequest = irp; 879 srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE; 880 srb->SenseInfoBuffer = DeviceExtension->ScratchContext.ScratchSense; 881 882 srb->CdbLength = 16; // to cause failures if not set correctly -- CD devices limited to 12 bytes for now... 883 884 srb->DataTransferLength = min(DeviceExtension->ScratchContext.ScratchBufferSize, MaximumTransferLength); 885 srb->TimeOutValue = DeviceExtension->TimeOutValue; 886 srb->SrbFlags = DeviceExtension->SrbFlags; 887 SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER); 888 SET_FLAG(srb->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE); 889 890 if (MaximumTransferLength == 0) 891 { 892 SET_FLAG(srb->SrbFlags, SRB_FLAGS_NO_DATA_TRANSFER); 893 } 894 else if (GetDataFromDevice) 895 { 896 SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_IN); 897 } 898 else 899 { 900 SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_OUT); 901 } 902 } 903 904 905 NTSTATUS 906 ScratchBuffer_SendSrb( 907 _Inout_ PCDROM_DEVICE_EXTENSION DeviceExtension, 908 _In_ BOOLEAN SynchronousSrb, 909 _When_(SynchronousSrb, _Pre_null_) 910 _When_(!SynchronousSrb, _In_opt_) 911 PSRB_HISTORY_ITEM *SrbHistoryItem 912 ) 913 /*++ 914 915 Routine Description: 916 917 Send the command from the scratch SRB to lower driver and retry if necessary. 918 919 Arguments: 920 921 DeviceExtension - device extension 922 SynchronousSrb - indicates whether the SRB needs to be sent synchronously or nor 923 SrbHistoryItem - storage for SRB history item, if this is an asynchronous request 924 925 Return Value: 926 927 none 928 929 --*/ 930 { 931 NTSTATUS status = STATUS_SUCCESS; 932 PSCSI_REQUEST_BLOCK srb = DeviceExtension->ScratchContext.ScratchSrb; 933 PSRB_HISTORY history = DeviceExtension->ScratchContext.ScratchHistory; 934 PSRB_HISTORY_ITEM item = NULL; 935 BOOLEAN requestCancelled = FALSE; 936 937 srb->InternalStatus = 0; 938 srb->SrbStatus = 0; 939 940 // allocate/update history pre-command, if it is a synchronous request or we were supplied 941 // with a storage for the history item 942 if (SynchronousSrb || SrbHistoryItem != NULL) 943 { 944 // sending a packet implies a new history unit is to be used. 945 NT_ASSERT( history->UsedHistoryCount <= history->TotalHistoryCount ); 946 947 // if already all used up, remove at least one history unit 948 if (history->UsedHistoryCount == history->TotalHistoryCount ) 949 { 950 CompressSrbHistoryData(history); 951 NT_ASSERT( history->UsedHistoryCount < history->TotalHistoryCount ); 952 } 953 954 // thus, since we are about to increment the count, it must now be less... 955 NT_ASSERT( history->UsedHistoryCount < history->TotalHistoryCount ); 956 957 // increment the number of history units in use 958 history->UsedHistoryCount++; 959 960 // determine index to use 961 item = &( history->History[ history->UsedHistoryCount-1 ] ); 962 963 if (SrbHistoryItem != NULL) 964 { 965 *SrbHistoryItem = item; 966 } 967 968 // zero out the history item 969 RtlZeroMemory(item, sizeof(SRB_HISTORY_ITEM)); 970 971 // Query the tick count and store in the history 972 KeQueryTickCount(&item->TickCountSent); 973 } 974 975 // get cancellation status; 976 { 977 PCDROM_REQUEST_CONTEXT requestContext = RequestGetContext(DeviceExtension->ScratchContext.ScratchRequest); 978 979 if (requestContext->OriginalRequest != NULL) 980 { 981 requestCancelled = WdfRequestIsCanceled(requestContext->OriginalRequest); 982 } 983 } 984 985 if (!requestCancelled) 986 { 987 status = RequestSend(DeviceExtension, 988 DeviceExtension->ScratchContext.ScratchRequest, 989 DeviceExtension->IoTarget, 990 SynchronousSrb ? WDF_REQUEST_SEND_OPTION_SYNCHRONOUS : 0, 991 NULL); 992 993 // If this is a synchronous request, update the history item immediately, including "normalized" sense data 994 if (SynchronousSrb) 995 { 996 ULONG senseSize; 997 998 // Query the tick count and store in the history 999 KeQueryTickCount(&item->TickCountCompleted); 1000 1001 // Copy the SRB Status 1002 item->SrbStatus = srb->SrbStatus; 1003 1004 // Determine the amount of valid sense data 1005 if (srb->SenseInfoBufferLength >= RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseLength)) 1006 { 1007 PSENSE_DATA sense = (PSENSE_DATA)srb->SenseInfoBuffer; 1008 senseSize = RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseLength) + 1009 sense->AdditionalSenseLength; 1010 senseSize = min(senseSize, sizeof(SENSE_DATA)); 1011 } 1012 else 1013 { 1014 senseSize = srb->SenseInfoBufferLength; 1015 } 1016 1017 // Normalize the sense data copy in the history 1018 RtlZeroMemory(&(item->NormalizedSenseData), sizeof(SENSE_DATA)); 1019 RtlCopyMemory(&(item->NormalizedSenseData), srb->SenseInfoBuffer, senseSize); 1020 } 1021 } 1022 else 1023 { 1024 DeviceExtension->ScratchContext.ScratchSrb->SrbStatus = SRB_STATUS_ABORTED; 1025 DeviceExtension->ScratchContext.ScratchSrb->InternalStatus = (ULONG)STATUS_CANCELLED; 1026 status = STATUS_CANCELLED; 1027 } 1028 1029 return status; 1030 } 1031 1032 VOID 1033 CompressSrbHistoryData( 1034 _Inout_ PSRB_HISTORY RequestHistory 1035 ) 1036 /*++ 1037 1038 Routine Description: 1039 1040 compress the SRB history data. 1041 1042 Arguments: 1043 1044 RequestHistory - SRB history data 1045 1046 Return Value: 1047 1048 RequestHistory - compressed history data 1049 1050 --*/ 1051 { 1052 ULONG i; 1053 NT_ASSERT( RequestHistory->UsedHistoryCount == RequestHistory->TotalHistoryCount ); 1054 ValidateSrbHistoryDataPresumptions(RequestHistory); 1055 1056 for (i=0; i < RequestHistory->UsedHistoryCount; i++) 1057 { 1058 // for each item... 1059 PSRB_HISTORY_ITEM toMatch = &( RequestHistory->History[i] ); 1060 // hint: read const qualifiers backwards. i.e. srbstatus is a const UCHAR 1061 // so, "UCHAR const * const x" is read "x is a const pointer to a const UCHAR" 1062 // unfortunately, "const UCHAR" is equivalent to "UCHAR const", which causes 1063 // people no end of confusion due to its widespread use. 1064 UCHAR const srbStatus = toMatch->SrbStatus; 1065 UCHAR const sense = toMatch->NormalizedSenseData.SenseKey; 1066 UCHAR const asc = toMatch->NormalizedSenseData.AdditionalSenseCode; 1067 UCHAR const ascq = toMatch->NormalizedSenseData.AdditionalSenseCodeQualifier; 1068 ULONG j; 1069 1070 // see if there are any at higher indices with identical Sense/ASC/ASCQ 1071 for (j = i+1; (toMatch->ClassDriverUse != 0xFF) && (j < RequestHistory->UsedHistoryCount); j++) 1072 { 1073 PSRB_HISTORY_ITEM found = &( RequestHistory->History[j] ); 1074 // close enough match? 1075 if ((srbStatus == found->SrbStatus) && 1076 (sense == found->NormalizedSenseData.SenseKey) && 1077 (asc == found->NormalizedSenseData.AdditionalSenseCode) && 1078 (ascq == found->NormalizedSenseData.AdditionalSenseCodeQualifier)) { 1079 1080 // add the fields to keep reasonable track of delay times. 1081 if (toMatch->MillisecondsDelayOnRetry + found->MillisecondsDelayOnRetry < toMatch->MillisecondsDelayOnRetry) { 1082 toMatch->MillisecondsDelayOnRetry = MAXULONG; 1083 } else { 1084 toMatch->MillisecondsDelayOnRetry += found->MillisecondsDelayOnRetry; 1085 } 1086 1087 // this found item cannot contain any compressed entries because 1088 // the first entry with a given set of sense/asc/ascq will always 1089 // either be full (0xFF) or be the only partially-full entry with 1090 // that sense/asc/ascq. 1091 NT_ASSERT(found->ClassDriverUse == 0); 1092 // add the counts so we still know how many retries total 1093 toMatch->ClassDriverUse++; 1094 1095 1096 // if not the last entry, need to move later entries earlier in the array 1097 if (j != RequestHistory->UsedHistoryCount-1) { 1098 // how many entries remain? 1099 SIZE_T remainingBytes = RequestHistory->UsedHistoryCount - 1 - j; 1100 remainingBytes *= sizeof(SRB_HISTORY_ITEM); 1101 1102 // note that MOVE is required due to overlapping entries 1103 RtlMoveMemory(found, found+1, remainingBytes); 1104 1105 // Finally, decrement the number of used history count and 1106 // decrement j to rescan the current location again 1107 --RequestHistory->UsedHistoryCount; 1108 --j; 1109 } // end moving of array elements around 1110 } // end of close enough match 1111 } // end j loop 1112 } // end i loop 1113 1114 // unable to compress duplicate sense/asc/ascq, so just lose the most recent data 1115 if (RequestHistory->UsedHistoryCount == RequestHistory->TotalHistoryCount) 1116 { 1117 PSRB_HISTORY_ITEM item = &( RequestHistory->History[ RequestHistory->TotalHistoryCount-1 ] ); 1118 RequestHistory->ClassDriverUse[0] += item->ClassDriverUse; // how many did we "lose"? 1119 RequestHistory->UsedHistoryCount--; 1120 } 1121 1122 // finally, zero any that are no longer in use 1123 NT_ASSERT( RequestHistory->UsedHistoryCount != RequestHistory->TotalHistoryCount); 1124 { 1125 SIZE_T bytesToZero = RequestHistory->TotalHistoryCount - RequestHistory->UsedHistoryCount; 1126 bytesToZero *= sizeof(SRB_HISTORY_ITEM); 1127 RtlZeroMemory(&(RequestHistory->History[RequestHistory->UsedHistoryCount]), bytesToZero); 1128 } 1129 1130 ValidateSrbHistoryDataPresumptions(RequestHistory); 1131 return; 1132 } 1133 1134 VOID 1135 ValidateSrbHistoryDataPresumptions( 1136 _In_ SRB_HISTORY const * RequestHistory 1137 ) 1138 { 1139 #if DBG 1140 // validate that all fully-compressed items are before any non-fully-compressed items of any particular sense/asc/ascq 1141 // validate that there is at most one partially-compressed item of any particular sense/asc/ascq 1142 // validate that all items of any particular sense/asc/ascq that are uncompressed are at the end 1143 // THUS: A(255) A(255) A( 40) A( 0) A( 0) is legal for all types with A as sense/asc/ascq 1144 // A(0) B(255) A( 0) B( 17) B( 0) is also legal because A/B are different types of error 1145 1146 ULONG i; 1147 for (i = 0; i < RequestHistory->UsedHistoryCount; i++) 1148 { 1149 SRB_HISTORY_ITEM const * toMatch = &( RequestHistory->History[i] ); 1150 UCHAR const srbStatus = toMatch->SrbStatus; 1151 UCHAR const sense = toMatch->NormalizedSenseData.SenseKey; 1152 UCHAR const asc = toMatch->NormalizedSenseData.AdditionalSenseCode; 1153 UCHAR const ascq = toMatch->NormalizedSenseData.AdditionalSenseCodeQualifier; 1154 ULONG j; 1155 1156 BOOLEAN foundPartiallyCompressedItem = 1157 (toMatch->ClassDriverUse != 0) && 1158 (toMatch->ClassDriverUse != 0xFF) ; 1159 BOOLEAN foundUncompressedItem = 1160 (toMatch->ClassDriverUse == 0) ; 1161 1162 for (j = i+1; j < RequestHistory->UsedHistoryCount; j++) 1163 { 1164 SRB_HISTORY_ITEM const * found = &( RequestHistory->History[j] ); 1165 if ((srbStatus == found->SrbStatus) && 1166 (sense == found->NormalizedSenseData.SenseKey) && 1167 (asc == found->NormalizedSenseData.AdditionalSenseCode) && 1168 (ascq == found->NormalizedSenseData.AdditionalSenseCodeQualifier) 1169 ) 1170 { 1171 // found a matching type, so validate ordering rules 1172 if (foundUncompressedItem && (found->ClassDriverUse != 0)) 1173 { 1174 DbgPrintEx(DPFLTR_CDROM_ID, DPFLTR_ERROR_LEVEL, 1175 "History data has compressed history following uncompressed history " 1176 "for srbstatus/sense/asc/ascq of %02x/%02x/%02x/%02x at indices %d (%08x) and %d (%08x)\n", 1177 srbStatus, sense, asc, ascq, 1178 i,i, j,j 1179 ); 1180 NT_ASSERT(FALSE); 1181 } 1182 else if (foundPartiallyCompressedItem && (found->ClassDriverUse == 0xFF)) 1183 { 1184 DbgPrintEx(DPFLTR_CDROM_ID, DPFLTR_ERROR_LEVEL, 1185 "History data has fully compressed history following partially compressed history " 1186 "for srbstatus/sense/asc/ascq of %02x/%02x/%02x/%02x at indices %d (%08x) and %d (%08x)\n", 1187 srbStatus, sense, asc, ascq, 1188 i,i, j,j 1189 ); 1190 NT_ASSERT(FALSE); 1191 } 1192 1193 // update if we have now found partially compressed and/or uncompressed items 1194 if (found->ClassDriverUse == 0) 1195 { 1196 foundUncompressedItem = TRUE; 1197 } 1198 else if (found->ClassDriverUse != 0xFF) 1199 { 1200 foundPartiallyCompressedItem = TRUE; 1201 } 1202 } // end match of (toMatch,found) 1203 } // end loop j 1204 } // end loop i 1205 #else 1206 UNREFERENCED_PARAMETER(RequestHistory); 1207 #endif 1208 return; 1209 } 1210 1211 VOID 1212 ScratchBuffer_SetupReadWriteSrb( 1213 _Inout_ PCDROM_DEVICE_EXTENSION DeviceExtension, 1214 _In_ WDFREQUEST OriginalRequest, 1215 _In_ LARGE_INTEGER StartingOffset, 1216 _In_ ULONG RequiredLength, 1217 _Inout_updates_bytes_(RequiredLength) UCHAR* DataBuffer, 1218 _In_ BOOLEAN IsReadRequest, 1219 _In_ BOOLEAN UsePartialMdl 1220 ) 1221 /*++ 1222 1223 Routine Description: 1224 1225 setup SRB for read/write request. 1226 1227 Arguments: 1228 1229 DeviceExtension - device extension 1230 OriginalRequest - read/write request 1231 StartingOffset - read/write starting offset 1232 DataBuffer - buffer for read/write 1233 IsReadRequest - TRUE (read); FALSE (write) 1234 1235 Return Value: 1236 1237 none 1238 1239 --*/ 1240 { 1241 //NOTE: R/W request not use the ScratchBuffer, instead, it uses the buffer associated with IRP. 1242 1243 PSCSI_REQUEST_BLOCK srb = DeviceExtension->ScratchContext.ScratchSrb; 1244 PCDB cdb = (PCDB)srb->Cdb; 1245 LARGE_INTEGER logicalBlockAddr; 1246 ULONG numTransferBlocks; 1247 1248 PIRP originalIrp = WdfRequestWdmGetIrp(OriginalRequest); 1249 1250 PIRP irp = WdfRequestWdmGetIrp(DeviceExtension->ScratchContext.ScratchRequest); 1251 PIO_STACK_LOCATION irpStack = NULL; 1252 1253 PCDROM_REQUEST_CONTEXT requestContext = RequestGetContext(DeviceExtension->ScratchContext.ScratchRequest); 1254 1255 requestContext->OriginalRequest = OriginalRequest; 1256 1257 1258 logicalBlockAddr.QuadPart = Int64ShrlMod32(StartingOffset.QuadPart, DeviceExtension->SectorShift); 1259 numTransferBlocks = RequiredLength >> DeviceExtension->SectorShift; 1260 1261 // set to use the full scratch buffer via the scratch SRB 1262 irpStack = IoGetNextIrpStackLocation(irp); 1263 irpStack->MajorFunction = IRP_MJ_SCSI; 1264 if (IsReadRequest) 1265 { 1266 irpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_IN; 1267 } 1268 else 1269 { 1270 irpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_OUT; 1271 } 1272 irpStack->Parameters.Scsi.Srb = srb; 1273 1274 // prepare the SRB with default values 1275 srb->Length = SCSI_REQUEST_BLOCK_SIZE; 1276 srb->Function = SRB_FUNCTION_EXECUTE_SCSI; 1277 srb->QueueAction = SRB_SIMPLE_TAG_REQUEST; 1278 srb->SrbStatus = 0; 1279 srb->ScsiStatus = 0; 1280 srb->NextSrb = NULL; 1281 srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE; 1282 srb->SenseInfoBuffer = DeviceExtension->ScratchContext.ScratchSense; 1283 1284 srb->DataBuffer = DataBuffer; 1285 srb->DataTransferLength = RequiredLength; 1286 1287 srb->QueueSortKey = logicalBlockAddr.LowPart; 1288 if (logicalBlockAddr.QuadPart > 0xFFFFFFFF) 1289 { 1290 // 1291 // If the requested LBA is more than max ULONG set the 1292 // QueueSortKey to the maximum value, so that these 1293 // requests can be added towards the end of the queue. 1294 // 1295 srb->QueueSortKey = 0xFFFFFFFF; 1296 } 1297 1298 srb->OriginalRequest = irp; 1299 srb->TimeOutValue = DeviceExtension->TimeOutValue; 1300 1301 if (RequestIsRealtimeStreaming(OriginalRequest, IsReadRequest) && 1302 !TEST_FLAG(DeviceExtension->PrivateFdoData->HackFlags, FDO_HACK_NO_STREAMING)) 1303 { 1304 if (IsReadRequest) 1305 { 1306 RtlZeroMemory(&cdb->READ12, sizeof(cdb->READ12)); 1307 REVERSE_BYTES(&cdb->READ12.LogicalBlock, &logicalBlockAddr.LowPart); 1308 REVERSE_BYTES(&cdb->READ12.TransferLength, &numTransferBlocks); 1309 cdb->READ12.Streaming = 1; 1310 cdb->READ12.OperationCode = SCSIOP_READ12; 1311 srb->CdbLength = sizeof(cdb->READ12); 1312 } 1313 else 1314 { 1315 RtlZeroMemory(&cdb->WRITE12, sizeof(cdb->WRITE12)); 1316 REVERSE_BYTES(&cdb->WRITE12.LogicalBlock, &logicalBlockAddr.LowPart); 1317 REVERSE_BYTES(&cdb->WRITE12.TransferLength, &numTransferBlocks); 1318 cdb->WRITE12.Streaming = 1; 1319 cdb->WRITE12.OperationCode = SCSIOP_WRITE12; 1320 srb->CdbLength = sizeof(cdb->WRITE12); 1321 } 1322 } 1323 else 1324 { 1325 RtlZeroMemory(&cdb->CDB10, sizeof(cdb->CDB10)); 1326 cdb->CDB10.LogicalBlockByte0 = ((PFOUR_BYTE)&logicalBlockAddr.LowPart)->Byte3; 1327 cdb->CDB10.LogicalBlockByte1 = ((PFOUR_BYTE)&logicalBlockAddr.LowPart)->Byte2; 1328 cdb->CDB10.LogicalBlockByte2 = ((PFOUR_BYTE)&logicalBlockAddr.LowPart)->Byte1; 1329 cdb->CDB10.LogicalBlockByte3 = ((PFOUR_BYTE)&logicalBlockAddr.LowPart)->Byte0; 1330 cdb->CDB10.TransferBlocksMsb = ((PFOUR_BYTE)&numTransferBlocks)->Byte1; 1331 cdb->CDB10.TransferBlocksLsb = ((PFOUR_BYTE)&numTransferBlocks)->Byte0; 1332 cdb->CDB10.OperationCode = (IsReadRequest) ? SCSIOP_READ : SCSIOP_WRITE; 1333 srb->CdbLength = sizeof(cdb->CDB10); 1334 } 1335 1336 // Set SRB and IRP flags 1337 srb->SrbFlags = DeviceExtension->SrbFlags; 1338 if (TEST_FLAG(originalIrp->Flags, IRP_PAGING_IO) || 1339 TEST_FLAG(originalIrp->Flags, IRP_SYNCHRONOUS_PAGING_IO)) 1340 { 1341 SET_FLAG(srb->SrbFlags, SRB_CLASS_FLAGS_PAGING); 1342 } 1343 1344 SET_FLAG(srb->SrbFlags, (IsReadRequest) ? SRB_FLAGS_DATA_IN : SRB_FLAGS_DATA_OUT); 1345 SET_FLAG(srb->SrbFlags, SRB_FLAGS_ADAPTER_CACHE_ENABLE); 1346 1347 // 1348 // If the request is not split, we can use the original IRP MDL. If the 1349 // request needs to be split, we need to use a partial MDL. The partial MDL 1350 // is needed because more than one driver might be mapping the same MDL 1351 // and this causes problems. 1352 // 1353 if (UsePartialMdl == FALSE) 1354 { 1355 irp->MdlAddress = originalIrp->MdlAddress; 1356 } 1357 else 1358 { 1359 if (DeviceExtension->ScratchContext.PartialMdlIsBuilt != FALSE) 1360 { 1361 MmPrepareMdlForReuse(DeviceExtension->ScratchContext.PartialMdl); 1362 } 1363 1364 IoBuildPartialMdl(originalIrp->MdlAddress, DeviceExtension->ScratchContext.PartialMdl, srb->DataBuffer, srb->DataTransferLength); 1365 DeviceExtension->ScratchContext.PartialMdlIsBuilt = TRUE; 1366 irp->MdlAddress = DeviceExtension->ScratchContext.PartialMdl; 1367 } 1368 1369 //DBGLOGSENDPACKET(Pkt); 1370 //HISTORYLOGSENDPACKET(Pkt); 1371 1372 // 1373 // Set the original irp here for SFIO. 1374 // 1375 srb->SrbExtension = (PVOID)(originalIrp); 1376 1377 return; 1378 } 1379 1380 _IRQL_requires_max_(APC_LEVEL) 1381 NTSTATUS 1382 ScratchBuffer_ExecuteCdbEx( 1383 _Inout_ PCDROM_DEVICE_EXTENSION DeviceExtension, 1384 _In_opt_ WDFREQUEST OriginalRequest, 1385 _In_ ULONG TransferSize, 1386 _In_ BOOLEAN GetDataFromDevice, 1387 _In_ PCDB Cdb, 1388 _In_ UCHAR OprationLength, 1389 _In_ ULONG TimeoutValue 1390 ) 1391 /*++ 1392 1393 Routine Description: 1394 1395 Use Scratch buffer to send the Cdb, check error and retry if necessary. 1396 1397 Arguments: 1398 1399 DeviceExtension - device context 1400 OriginalRequest - original request that requires this CDB operation 1401 TransferSize - Data transfer size required 1402 GetFromDevice - TRUE if getting data from device. 1403 Cdb - SCSI command 1404 OprationLength - SCSI command length: 6, 10 or 12 1405 TimeoutValue - if > 0, use it as timeout value for command 1406 if 0, use the default device timeout value 1407 1408 Return Value: 1409 1410 NTSTATUS 1411 1412 --*/ 1413 { 1414 NTSTATUS status = STATUS_SUCCESS; 1415 PSCSI_REQUEST_BLOCK srb = DeviceExtension->ScratchContext.ScratchSrb; 1416 PCDB cdb = (PCDB)(srb->Cdb); 1417 1418 BOOLEAN shouldRetry = TRUE; 1419 ULONG timesAlreadyRetried = 0; 1420 LONGLONG retryIn100nsUnits = 0; 1421 1422 PAGED_CODE (); 1423 1424 while (shouldRetry) 1425 { 1426 ScratchBuffer_SetupSrb(DeviceExtension, OriginalRequest, TransferSize, GetDataFromDevice); 1427 1428 // Set up the SRB/CDB 1429 RtlCopyMemory(cdb, Cdb, sizeof(CDB)); 1430 1431 srb->CdbLength = OprationLength; 1432 1433 if (TimeoutValue > 0) 1434 { 1435 srb->TimeOutValue = TimeoutValue; 1436 } 1437 1438 ScratchBuffer_SendSrb(DeviceExtension, TRUE, NULL); 1439 1440 if ((DeviceExtension->ScratchContext.ScratchSrb->SrbStatus == SRB_STATUS_ABORTED) && 1441 (DeviceExtension->ScratchContext.ScratchSrb->InternalStatus == STATUS_CANCELLED)) 1442 { 1443 shouldRetry = FALSE; 1444 status = STATUS_CANCELLED; 1445 } 1446 else 1447 { 1448 shouldRetry = RequestSenseInfoInterpretForScratchBuffer(DeviceExtension, 1449 timesAlreadyRetried, 1450 &status, 1451 &retryIn100nsUnits); 1452 if (shouldRetry) 1453 { 1454 LARGE_INTEGER t; 1455 t.QuadPart = -retryIn100nsUnits; 1456 timesAlreadyRetried++; 1457 KeDelayExecutionThread(KernelMode, FALSE, &t); 1458 // keep items clean 1459 ScratchBuffer_ResetItems(DeviceExtension, FALSE); 1460 } 1461 } 1462 } 1463 1464 return status; 1465 } 1466 1467 1468