1 /*++ 2 3 Copyright (C) Microsoft Corporation. All rights reserved. 4 5 Module Name: 6 7 common.c 8 9 Abstract: 10 11 shared private routines for cdrom.sys 12 13 Environment: 14 15 kernel mode only 16 17 Notes: 18 19 20 Revision History: 21 22 --*/ 23 24 25 #include "ntddk.h" 26 #include "ntddstor.h" 27 #include "ntstrsafe.h" 28 29 #include "cdrom.h" 30 #include "scratch.h" 31 32 33 #ifdef DEBUG_USE_WPP 34 #include "common.tmh" 35 #endif 36 37 #ifdef ALLOC_PRAGMA 38 39 #pragma alloc_text(PAGE, DeviceGetParameter) 40 #pragma alloc_text(PAGE, DeviceSetParameter) 41 #pragma alloc_text(PAGE, DeviceSendSrbSynchronously) 42 #pragma alloc_text(PAGE, DevicePickDvdRegion) 43 #pragma alloc_text(PAGE, StringsAreMatched) 44 #pragma alloc_text(PAGE, PerformEjectionControl) 45 #pragma alloc_text(PAGE, DeviceFindFeaturePage) 46 #pragma alloc_text(PAGE, DevicePrintAllFeaturePages) 47 #pragma alloc_text(PAGE, DeviceRegisterInterface) 48 #pragma alloc_text(PAGE, DeviceRestoreDefaultSpeed) 49 #pragma alloc_text(PAGE, DeviceSendRequestSynchronously) 50 #pragma alloc_text(PAGE, MediaReadCapacity) 51 #pragma alloc_text(PAGE, MediaReadCapacityDataInterpret) 52 #pragma alloc_text(PAGE, DeviceRetrieveModeSenseUsingScratch) 53 #pragma alloc_text(PAGE, ModeSenseFindSpecificPage) 54 #pragma alloc_text(PAGE, DeviceUnlockExclusive) 55 56 #endif 57 58 LPCSTR LockTypeStrings[] = {"Simple", 59 "Secure", 60 "Internal" 61 }; 62 63 VOID 64 RequestSetReceivedTime( 65 _In_ WDFREQUEST Request 66 ) 67 { 68 PCDROM_REQUEST_CONTEXT requestContext = RequestGetContext(Request); 69 LARGE_INTEGER temp; 70 71 KeQueryTickCount(&temp); 72 73 requestContext->TimeReceived = temp; 74 75 return; 76 } 77 78 VOID 79 RequestSetSentTime( 80 _In_ WDFREQUEST Request 81 ) 82 { 83 PCDROM_REQUEST_CONTEXT requestContext = RequestGetContext(Request); 84 LARGE_INTEGER temp; 85 86 KeQueryTickCount(&temp); 87 88 if (requestContext->TimeSentDownFirstTime.QuadPart == 0) 89 { 90 requestContext->TimeSentDownFirstTime = temp; 91 } 92 93 requestContext->TimeSentDownLasttTime = temp; 94 95 if (requestContext->OriginalRequest != NULL) 96 { 97 PCDROM_REQUEST_CONTEXT originalRequestContext = RequestGetContext(requestContext->OriginalRequest); 98 99 if (originalRequestContext->TimeSentDownFirstTime.QuadPart == 0) 100 { 101 originalRequestContext->TimeSentDownFirstTime = temp; 102 } 103 104 originalRequestContext->TimeSentDownLasttTime = temp; 105 } 106 107 return; 108 } 109 110 VOID 111 RequestClearSendTime( 112 _In_ WDFREQUEST Request 113 ) 114 /* 115 Routine Description: 116 117 This function is used to clean SentTime fields in reusable request context. 118 119 Arguments: 120 Request - 121 122 Return Value: 123 N/A 124 125 */ 126 { 127 PCDROM_REQUEST_CONTEXT requestContext = RequestGetContext(Request); 128 129 requestContext->TimeSentDownFirstTime.QuadPart = 0; 130 requestContext->TimeSentDownLasttTime.QuadPart = 0; 131 132 return; 133 } 134 135 _IRQL_requires_max_(PASSIVE_LEVEL) 136 VOID 137 DeviceGetParameter( 138 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, 139 _In_opt_ PWSTR SubkeyName, 140 _In_ PWSTR ParameterName, 141 _Inout_ PULONG ParameterValue // also default value 142 ) 143 /*++ 144 Routine Description: 145 146 retrieve device parameter from registry. 147 148 Arguments: 149 150 DeviceExtension - device context. 151 152 SubkeyName - name of subkey 153 154 ParameterName - the registry parameter to be retrieved 155 156 Return Value: 157 158 ParameterValue - registry value retrieved 159 160 --*/ 161 { 162 NTSTATUS status; 163 WDFKEY rootKey = NULL; 164 WDFKEY subKey = NULL; 165 UNICODE_STRING registrySubKeyName; 166 UNICODE_STRING registryValueName; 167 ULONG defaultParameterValue; 168 169 PAGED_CODE(); 170 171 RtlInitUnicodeString(®istryValueName, ParameterName); 172 173 if (SubkeyName != NULL) 174 { 175 RtlInitUnicodeString(®istrySubKeyName, SubkeyName); 176 } 177 178 // open the hardware key 179 status = WdfDeviceOpenRegistryKey(DeviceExtension->Device, 180 PLUGPLAY_REGKEY_DEVICE, 181 KEY_READ, 182 WDF_NO_OBJECT_ATTRIBUTES, 183 &rootKey); 184 185 // open the sub key 186 if (NT_SUCCESS(status) && (SubkeyName != NULL)) 187 { 188 status = WdfRegistryOpenKey(rootKey, 189 ®istrySubKeyName, 190 KEY_READ, 191 WDF_NO_OBJECT_ATTRIBUTES, 192 &subKey); 193 194 if (!NT_SUCCESS(status)) 195 { 196 WdfRegistryClose(rootKey); 197 rootKey = NULL; 198 } 199 } 200 201 if (NT_SUCCESS(status) && (rootKey != NULL)) 202 { 203 defaultParameterValue = *ParameterValue; 204 205 status = WdfRegistryQueryULong((subKey != NULL) ? subKey : rootKey, 206 ®istryValueName, 207 ParameterValue); 208 209 if (!NT_SUCCESS(status)) 210 { 211 *ParameterValue = defaultParameterValue; // use default value 212 } 213 } 214 215 // close what we open 216 if (subKey != NULL) 217 { 218 WdfRegistryClose(subKey); 219 subKey = NULL; 220 } 221 222 if (rootKey != NULL) 223 { 224 WdfRegistryClose(rootKey); 225 rootKey = NULL; 226 } 227 228 // Windows 2000 SP3 uses the driver-specific key, so look in there 229 if (!NT_SUCCESS(status)) 230 { 231 // open the software key 232 status = WdfDeviceOpenRegistryKey(DeviceExtension->Device, 233 PLUGPLAY_REGKEY_DRIVER, 234 KEY_READ, 235 WDF_NO_OBJECT_ATTRIBUTES, 236 &rootKey); 237 238 // open the sub key 239 if (NT_SUCCESS(status) && (SubkeyName != NULL)) 240 { 241 status = WdfRegistryOpenKey(rootKey, 242 ®istrySubKeyName, 243 KEY_READ, 244 WDF_NO_OBJECT_ATTRIBUTES, 245 &subKey); 246 247 if (!NT_SUCCESS(status)) 248 { 249 WdfRegistryClose(rootKey); 250 rootKey = NULL; 251 } 252 } 253 254 if (NT_SUCCESS(status) && (rootKey != NULL)) 255 { 256 defaultParameterValue = *ParameterValue; 257 258 status = WdfRegistryQueryULong((subKey != NULL) ? subKey : rootKey, 259 ®istryValueName, 260 ParameterValue); 261 262 if (!NT_SUCCESS(status)) 263 { 264 *ParameterValue = defaultParameterValue; // use default value 265 } 266 else 267 { 268 // Migrate the value over to the device-specific key 269 DeviceSetParameter(DeviceExtension, SubkeyName, ParameterName, *ParameterValue); 270 } 271 } 272 273 // close what we open 274 if (subKey != NULL) 275 { 276 WdfRegistryClose(subKey); 277 subKey = NULL; 278 } 279 280 if (rootKey != NULL) 281 { 282 WdfRegistryClose(rootKey); 283 rootKey = NULL; 284 } 285 } 286 287 return; 288 289 } // end DeviceetParameter() 290 291 292 _IRQL_requires_max_(PASSIVE_LEVEL) 293 NTSTATUS 294 DeviceSetParameter( 295 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, 296 _In_opt_z_ PWSTR SubkeyName, 297 _In_ PWSTR ParameterName, 298 _In_ ULONG ParameterValue 299 ) 300 /*++ 301 Routine Description: 302 303 set parameter to registry. 304 305 Arguments: 306 307 DeviceExtension - device context. 308 309 SubkeyName - name of subkey 310 311 ParameterName - the registry parameter to be retrieved 312 313 ParameterValue - registry value to be set 314 315 Return Value: 316 NTSTATUS 317 318 --*/ 319 { 320 NTSTATUS status; 321 WDFKEY rootKey = NULL; 322 WDFKEY subKey = NULL; 323 UNICODE_STRING registrySubKeyName; 324 UNICODE_STRING registryValueName; 325 326 PAGED_CODE(); 327 328 RtlInitUnicodeString(®istryValueName, ParameterName); 329 330 if (SubkeyName != NULL) 331 { 332 RtlInitUnicodeString(®istrySubKeyName, SubkeyName); 333 } 334 335 // open the hardware key 336 status = WdfDeviceOpenRegistryKey(DeviceExtension->Device, 337 PLUGPLAY_REGKEY_DEVICE, 338 KEY_READ | KEY_WRITE, 339 WDF_NO_OBJECT_ATTRIBUTES, 340 &rootKey); 341 342 // open the sub key 343 if (NT_SUCCESS(status) && (SubkeyName != NULL)) 344 { 345 status = WdfRegistryOpenKey(rootKey, 346 ®istrySubKeyName, 347 KEY_READ | KEY_WRITE, 348 WDF_NO_OBJECT_ATTRIBUTES, 349 &subKey); 350 351 if (!NT_SUCCESS(status)) 352 { 353 WdfRegistryClose(rootKey); 354 rootKey = NULL; 355 } 356 } 357 358 if (NT_SUCCESS(status) && (rootKey != NULL)) 359 { 360 status = WdfRegistryAssignULong((subKey != NULL) ? subKey : rootKey, 361 ®istryValueName, 362 ParameterValue); 363 } 364 365 // close what we open 366 if (subKey != NULL) 367 { 368 WdfRegistryClose(subKey); 369 subKey = NULL; 370 } 371 372 if (rootKey != NULL) 373 { 374 WdfRegistryClose(rootKey); 375 rootKey = NULL; 376 } 377 378 return status; 379 380 } // end DeviceSetParameter() 381 382 383 _IRQL_requires_max_(APC_LEVEL) 384 NTSTATUS 385 DeviceSendRequestSynchronously( 386 _In_ WDFDEVICE Device, 387 _In_ WDFREQUEST Request, 388 _In_ BOOLEAN RequestFormated 389 ) 390 /*++ 391 Routine Description: 392 393 send a request to lower driver synchronously. 394 395 Arguments: 396 397 Device - device object. 398 399 Request - request object 400 401 RequestFormated - if the request is already formatted, will no do it in this function 402 403 Return Value: 404 NTSTATUS 405 406 --*/ 407 { 408 NTSTATUS status = STATUS_SUCCESS; 409 PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device); 410 BOOLEAN requestCancelled = FALSE; 411 PCDROM_REQUEST_CONTEXT requestContext = RequestGetContext(Request); 412 413 PAGED_CODE(); 414 415 if (!RequestFormated) 416 { 417 // set request up for sending down 418 WdfRequestFormatRequestUsingCurrentType(Request); 419 } 420 421 // get cancellation status for the original request 422 if (requestContext->OriginalRequest != NULL) 423 { 424 requestCancelled = WdfRequestIsCanceled(requestContext->OriginalRequest); 425 } 426 427 if (!requestCancelled) 428 { 429 status = RequestSend(deviceExtension, 430 Request, 431 deviceExtension->IoTarget, 432 WDF_REQUEST_SEND_OPTION_SYNCHRONOUS, 433 NULL); 434 } 435 else 436 { 437 status = STATUS_CANCELLED; 438 } 439 440 return status; 441 } 442 443 444 _IRQL_requires_max_(PASSIVE_LEVEL) 445 NTSTATUS 446 DeviceSendSrbSynchronously( 447 _In_ WDFDEVICE Device, 448 _In_ PSCSI_REQUEST_BLOCK Srb, 449 _In_opt_ PVOID BufferAddress, 450 _In_ ULONG BufferLength, 451 _In_ BOOLEAN WriteToDevice, 452 _In_opt_ WDFREQUEST OriginalRequest 453 ) 454 /*++ 455 Routine Description: 456 457 Send a SRB structure to lower driver synchronously. 458 459 Process of this function: 460 1. Allocate SenseBuffer; Create Request; Allocate MDL 461 2. Do following loop if necessary 462 2.1 Reuse Request 463 2.2 Format Srb, Irp 464 2.3 Send Request 465 2.4 Error Intepret and retry decision making. 466 3. Release all allocated resosurces. 467 468 Arguments: 469 470 Device - device object. 471 472 Request - request object 473 474 RequestFormated - if the request is already formatted, will no do it in this function 475 476 Return Value: 477 NTSTATUS 478 479 NOTE: 480 The caller needs to setup following fields before calling this routine. 481 srb.CdbLength 482 srb.TimeOutValue 483 cdb 484 485 BufferLength and WriteToDevice to control the data direction of the device 486 BufferLength = 0: No data transfer 487 BufferLenth != 0 && !WriteToDevice: get data from device 488 BufferLenth != 0 && WriteToDevice: send data to device 489 --*/ 490 { 491 NTSTATUS status; 492 PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device); 493 PCDROM_PRIVATE_FDO_DATA fdoData = deviceExtension->PrivateFdoData; 494 PUCHAR senseInfoBuffer = NULL; 495 ULONG retryCount = 0; 496 BOOLEAN retry = FALSE; 497 ULONG ioctlCode = 0; 498 WDFREQUEST request = NULL; 499 PIRP irp = NULL; 500 PIO_STACK_LOCATION nextStack = NULL; 501 PMDL mdlAddress = NULL; 502 BOOLEAN memoryLocked = FALSE; 503 WDF_OBJECT_ATTRIBUTES attributes; 504 PZERO_POWER_ODD_INFO zpoddInfo = deviceExtension->ZeroPowerODDInfo; 505 506 PAGED_CODE(); 507 508 // NOTE: This code is only pagable because we are not freezing 509 // the queue. Allowing the queue to be frozen from a pagable 510 // routine could leave the queue frozen as we try to page in 511 // the code to unfreeze the queue. The result would be a nice 512 // case of deadlock. Therefore, since we are unfreezing the 513 // queue regardless of the result, just set the NO_FREEZE_QUEUE 514 // flag in the SRB. 515 NT_ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL); 516 517 //1. allocate SenseBuffer and initiate Srb common fields 518 // these fields will not be changed by lower driver. 519 { 520 // Write length to SRB. 521 Srb->Length = sizeof(SCSI_REQUEST_BLOCK); 522 Srb->Function = SRB_FUNCTION_EXECUTE_SCSI; 523 Srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE; 524 525 // Sense buffer is in aligned nonpaged pool. 526 senseInfoBuffer = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned, 527 SENSE_BUFFER_SIZE, 528 CDROM_TAG_SENSE_INFO); 529 530 if (senseInfoBuffer == NULL) 531 { 532 status = STATUS_INSUFFICIENT_RESOURCES; 533 534 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, 535 "DeviceSendSrbSynchronously: Can't allocate MDL\n")); 536 537 goto Exit; 538 } 539 540 Srb->SenseInfoBuffer = senseInfoBuffer; 541 Srb->DataBuffer = BufferAddress; 542 543 // set timeout value to default value if it's not specifically set by caller. 544 if (Srb->TimeOutValue == 0) 545 { 546 Srb->TimeOutValue = deviceExtension->TimeOutValue; 547 } 548 } 549 550 //2. Create Request object 551 { 552 WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, 553 CDROM_REQUEST_CONTEXT); 554 555 status = WdfRequestCreate(&attributes, 556 deviceExtension->IoTarget, 557 &request); 558 559 if (!NT_SUCCESS(status)) 560 { 561 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, 562 "DeviceSendSrbSynchronously: Can't create request: %lx\n", 563 status)); 564 565 goto Exit; 566 } 567 568 irp = WdfRequestWdmGetIrp(request); 569 } 570 571 // 3. Build an MDL for the data buffer and stick it into the irp. 572 if (BufferAddress != NULL) 573 { 574 mdlAddress = IoAllocateMdl( BufferAddress, 575 BufferLength, 576 FALSE, 577 FALSE, 578 irp ); 579 if (mdlAddress == NULL) 580 { 581 status = STATUS_INSUFFICIENT_RESOURCES; 582 583 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, 584 "DeviceSendSrbSynchronously: Can't allocate MDL\n")); 585 586 goto Exit; 587 } 588 589 _SEH2_TRY 590 { 591 MmProbeAndLockPages(mdlAddress, 592 KernelMode, 593 (WriteToDevice ? IoReadAccess : IoWriteAccess)); 594 } 595 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 596 { 597 status = _SEH2_GetExceptionCode(); 598 599 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, 600 "DeviceSendSrbSynchronously: Exception %lx locking buffer\n", status)); 601 602 _SEH2_YIELD(goto Exit); 603 } 604 _SEH2_END; 605 606 memoryLocked = TRUE; 607 } 608 609 // 4. Format Srb, Irp; Send request and retry when necessary 610 do 611 { 612 // clear the control variable. 613 retry = FALSE; 614 615 // 4.1 reuse the request object; set originalRequest field. 616 { 617 WDF_REQUEST_REUSE_PARAMS params; 618 PCDROM_REQUEST_CONTEXT requestContext = NULL; 619 620 // deassign the MdlAddress, this is the value we assign explicitly. 621 // doing this can prevent WdfRequestReuse to release the Mdl unexpectly. 622 if (irp->MdlAddress) 623 { 624 irp->MdlAddress = NULL; 625 } 626 627 WDF_REQUEST_REUSE_PARAMS_INIT(¶ms, 628 WDF_REQUEST_REUSE_NO_FLAGS, 629 STATUS_SUCCESS); 630 631 status = WdfRequestReuse(request, ¶ms); 632 633 if (!NT_SUCCESS(status)) 634 { 635 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, 636 "DeviceSendSrbSynchronously: WdfRequestReuse failed, %!STATUS!\n", 637 status)); 638 // exit the loop. 639 break; 640 } 641 642 // WDF requests to format the request befor sending it 643 status = WdfIoTargetFormatRequestForInternalIoctlOthers(deviceExtension->IoTarget, 644 request, 645 ioctlCode, 646 NULL, NULL, 647 NULL, NULL, 648 NULL, NULL); 649 650 if (!NT_SUCCESS(status)) 651 { 652 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, 653 "DeviceSendSrbSynchronously: WdfIoTargetFormatRequestForInternalIoctlOthers failed, %!STATUS!\n", 654 status)); 655 // exit the loop. 656 break; 657 } 658 659 requestContext = RequestGetContext(request); 660 requestContext->OriginalRequest = OriginalRequest; 661 } 662 663 // 4.2 Format Srb and Irp 664 { 665 Srb->OriginalRequest = irp; 666 Srb->QueueAction = SRB_SIMPLE_TAG_REQUEST; 667 Srb->DataTransferLength = BufferLength; 668 Srb->SrbFlags = deviceExtension->SrbFlags; 669 670 // Disable synchronous transfer for these requests. 671 SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER); 672 SET_FLAG(Srb->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE); 673 674 if (BufferAddress != NULL) 675 { 676 if (WriteToDevice) 677 { 678 SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DATA_OUT); 679 ioctlCode = IOCTL_SCSI_EXECUTE_OUT; 680 } 681 else 682 { 683 SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DATA_IN); 684 ioctlCode = IOCTL_SCSI_EXECUTE_IN; 685 } 686 } 687 else 688 { 689 ioctlCode = IOCTL_SCSI_EXECUTE_NONE; 690 } 691 692 693 // Zero out status. 694 Srb->ScsiStatus = 0; 695 Srb->SrbStatus = 0; 696 Srb->NextSrb = NULL; 697 698 // irp related fields 699 irp->MdlAddress = mdlAddress; 700 701 nextStack = IoGetNextIrpStackLocation(irp); 702 703 nextStack->MajorFunction = IRP_MJ_SCSI; 704 nextStack->Parameters.DeviceIoControl.IoControlCode = ioctlCode; 705 nextStack->Parameters.Scsi.Srb = Srb; 706 } 707 708 // 4.3 send Request to lower driver. 709 status = DeviceSendRequestSynchronously(Device, request, TRUE); 710 711 if (status != STATUS_CANCELLED) 712 { 713 NT_ASSERT(SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_PENDING); 714 NT_ASSERT(status != STATUS_PENDING); 715 NT_ASSERT(!(Srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN)); 716 717 // 4.4 error process. 718 if (SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_SUCCESS) 719 { 720 LONGLONG retryIntervalIn100ns = 0; 721 722 // Update status and determine if request should be retried. 723 retry = RequestSenseInfoInterpret(deviceExtension, 724 request, 725 Srb, 726 retryCount, 727 &status, 728 &retryIntervalIn100ns); 729 730 if (retry) 731 { 732 LARGE_INTEGER t; 733 t.QuadPart = -retryIntervalIn100ns; 734 retryCount++; 735 KeDelayExecutionThread(KernelMode, FALSE, &t); 736 } 737 } 738 else 739 { 740 // Request succeeded. 741 fdoData->LoggedTURFailureSinceLastIO = FALSE; 742 status = STATUS_SUCCESS; 743 retry = FALSE; 744 } 745 } 746 } while(retry); 747 748 if ((zpoddInfo != NULL) && 749 (zpoddInfo->MonitorStartStopUnit != FALSE) && 750 (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_SUCCESS)) 751 { 752 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, 753 "DeviceSendSrbSynchronously: soft eject detected, device marked as active\n")); 754 755 DeviceMarkActive(deviceExtension, TRUE, FALSE); 756 } 757 758 // 5. Release all allocated resources. 759 760 // required even though we allocated our own, since the port driver may 761 // have allocated one also 762 if (PORT_ALLOCATED_SENSE(deviceExtension, Srb)) 763 { 764 FREE_PORT_ALLOCATED_SENSE_BUFFER(deviceExtension, Srb); 765 } 766 767 Exit: 768 769 if (senseInfoBuffer != NULL) 770 { 771 FREE_POOL(senseInfoBuffer); 772 } 773 774 Srb->SenseInfoBuffer = NULL; 775 Srb->SenseInfoBufferLength = 0; 776 777 if (mdlAddress) 778 { 779 if (memoryLocked) 780 { 781 MmUnlockPages(mdlAddress); 782 memoryLocked = FALSE; 783 } 784 785 IoFreeMdl(mdlAddress); 786 irp->MdlAddress = NULL; 787 } 788 789 if (request) 790 { 791 WdfObjectDelete(request); 792 } 793 794 return status; 795 } 796 797 798 VOID 799 DeviceSendNotification( 800 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, 801 _In_ const GUID* Guid, 802 _In_ ULONG ExtraDataSize, 803 _In_opt_ PVOID ExtraData 804 ) 805 /*++ 806 Routine Description: 807 808 send notification to other components 809 810 Arguments: 811 812 DeviceExtension - device context. 813 814 Guid - GUID for the notification 815 816 ExtraDataSize - data size along with notification 817 818 ExtraData - data buffer send with notification 819 820 Return Value: 821 None 822 823 --*/ 824 { 825 PTARGET_DEVICE_CUSTOM_NOTIFICATION notification; 826 ULONG requiredSize; 827 NTSTATUS status; 828 829 status = RtlULongAdd((sizeof(TARGET_DEVICE_CUSTOM_NOTIFICATION) - sizeof(UCHAR)), 830 ExtraDataSize, 831 &requiredSize); 832 833 if (!(NT_SUCCESS(status)) || (requiredSize > 0x0000ffff)) 834 { 835 // MAX_USHORT, max total size for these events! 836 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN, 837 "Error sending event: size too large! (%x)\n", 838 requiredSize)); 839 return; 840 } 841 842 notification = ExAllocatePoolWithTag(NonPagedPoolNx, 843 requiredSize, 844 CDROM_TAG_NOTIFICATION); 845 846 // if none allocated, exit 847 if (notification == NULL) 848 { 849 return; 850 } 851 852 // Prepare and send the request! 853 RtlZeroMemory(notification, requiredSize); 854 notification->Version = 1; 855 notification->Size = (USHORT)(requiredSize); 856 notification->FileObject = NULL; 857 notification->NameBufferOffset = -1; 858 notification->Event = *Guid; 859 860 if (ExtraData != NULL) 861 { 862 RtlCopyMemory(notification->CustomDataBuffer, ExtraData, ExtraDataSize); 863 } 864 865 IoReportTargetDeviceChangeAsynchronous(DeviceExtension->LowerPdo, 866 notification, 867 NULL, 868 NULL); 869 870 FREE_POOL(notification); 871 872 return; 873 } 874 875 876 VOID 877 DeviceSendStartUnit( 878 _In_ WDFDEVICE Device 879 ) 880 /*++ 881 882 Routine Description: 883 884 Send command to SCSI unit to start or power up. 885 Because this command is issued asynchronounsly, that is, without 886 waiting on it to complete, the IMMEDIATE flag is not set. This 887 means that the CDB will not return until the drive has powered up. 888 This should keep subsequent requests from being submitted to the 889 device before it has completely spun up. 890 891 This routine is called from the InterpretSense routine, when a 892 request sense returns data indicating that a drive must be 893 powered up. 894 895 This routine may also be called from a class driver's error handler, 896 or anytime a non-critical start device should be sent to the device. 897 898 Arguments: 899 900 Device - The device object. 901 902 Return Value: 903 904 None. 905 906 --*/ 907 { 908 NTSTATUS status = STATUS_SUCCESS; 909 PCDROM_DEVICE_EXTENSION deviceExtension = NULL; 910 WDF_OBJECT_ATTRIBUTES attributes; 911 WDFREQUEST startUnitRequest = NULL; 912 WDFMEMORY inputMemory = NULL; 913 914 PCOMPLETION_CONTEXT context = NULL; 915 PSCSI_REQUEST_BLOCK srb = NULL; 916 PCDB cdb = NULL; 917 918 deviceExtension = DeviceGetExtension(Device); 919 920 if (NT_SUCCESS(status)) 921 { 922 // Allocate Srb from nonpaged pool. 923 context = ExAllocatePoolWithTag(NonPagedPoolNx, 924 sizeof(COMPLETION_CONTEXT), 925 CDROM_TAG_COMPLETION_CONTEXT); 926 927 if (context == NULL) 928 { 929 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, 930 "DeviceSendStartUnit: Failed to allocate completion context\n")); 931 932 status = STATUS_INTERNAL_ERROR; 933 } 934 } 935 936 if (NT_SUCCESS(status)) 937 { 938 // Save the device object in the context for use by the completion 939 // routine. 940 context->Device = Device; 941 srb = &context->Srb; 942 943 // Zero out srb. 944 RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK)); 945 946 // setup SRB structure. 947 srb->Length = sizeof(SCSI_REQUEST_BLOCK); 948 srb->Function = SRB_FUNCTION_EXECUTE_SCSI; 949 srb->TimeOutValue = START_UNIT_TIMEOUT; 950 951 srb->SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER | 952 SRB_FLAGS_DISABLE_SYNCH_TRANSFER; 953 954 // setup CDB 955 srb->CdbLength = 6; 956 cdb = (PCDB)srb->Cdb; 957 958 cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT; 959 cdb->START_STOP.Start = 1; 960 cdb->START_STOP.Immediate = 0; 961 cdb->START_STOP.LogicalUnitNumber = srb->Lun; 962 963 //Create Request for sending down to port driver 964 WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, 965 CDROM_REQUEST_CONTEXT); 966 attributes.ParentObject = deviceExtension->IoTarget; 967 968 status = WdfRequestCreate(&attributes, 969 deviceExtension->IoTarget, 970 &startUnitRequest); 971 } 972 973 if (NT_SUCCESS(status)) 974 { 975 srb->OriginalRequest = WdfRequestWdmGetIrp(startUnitRequest); 976 NT_ASSERT(srb->OriginalRequest != NULL); 977 978 //Prepare the request 979 WDF_OBJECT_ATTRIBUTES_INIT(&attributes); 980 attributes.ParentObject = startUnitRequest; 981 982 status = WdfMemoryCreatePreallocated(&attributes, 983 (PVOID)srb, 984 sizeof(SCSI_REQUEST_BLOCK), 985 &inputMemory); 986 } 987 988 if (NT_SUCCESS(status)) 989 { 990 status = WdfIoTargetFormatRequestForInternalIoctlOthers(deviceExtension->IoTarget, 991 startUnitRequest, 992 IOCTL_SCSI_EXECUTE_NONE, 993 inputMemory, 994 NULL, 995 NULL, 996 NULL, 997 NULL, 998 NULL); 999 } 1000 1001 if (NT_SUCCESS(status)) 1002 { 1003 // Set a CompletionRoutine callback function. 1004 WdfRequestSetCompletionRoutine(startUnitRequest, 1005 DeviceAsynchronousCompletion, 1006 context); 1007 1008 status = RequestSend(deviceExtension, 1009 startUnitRequest, 1010 deviceExtension->IoTarget, 1011 0, 1012 NULL); 1013 } 1014 1015 // release resources when failed. 1016 if (!NT_SUCCESS(status)) 1017 { 1018 FREE_POOL(context); 1019 if (startUnitRequest != NULL) 1020 { 1021 WdfObjectDelete(startUnitRequest); 1022 } 1023 } 1024 1025 return; 1026 } // end StartUnit() 1027 1028 1029 VOID 1030 DeviceSendIoctlAsynchronously( 1031 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, 1032 _In_ ULONG IoControlCode, 1033 _In_ PDEVICE_OBJECT TargetDeviceObject 1034 ) 1035 /*++ 1036 1037 Routine Description: 1038 1039 Send an IOCTL asynchronously 1040 1041 Arguments: 1042 1043 DeviceExtension - device context. 1044 IoControlCode - IOCTL code. 1045 TargetDeviceObject - target device object. 1046 1047 Return Value: 1048 1049 None. 1050 1051 --*/ 1052 { 1053 PIRP irp = NULL; 1054 PIO_STACK_LOCATION nextIrpStack = NULL; 1055 1056 irp = IoAllocateIrp(DeviceExtension->DeviceObject->StackSize, FALSE); 1057 1058 if (irp != NULL) 1059 { 1060 nextIrpStack = IoGetNextIrpStackLocation(irp); 1061 1062 nextIrpStack->MajorFunction = IRP_MJ_DEVICE_CONTROL; 1063 1064 nextIrpStack->Parameters.DeviceIoControl.OutputBufferLength = 0; 1065 nextIrpStack->Parameters.DeviceIoControl.InputBufferLength = 0; 1066 nextIrpStack->Parameters.DeviceIoControl.IoControlCode = IoControlCode; 1067 nextIrpStack->Parameters.DeviceIoControl.Type3InputBuffer = NULL; 1068 1069 IoSetCompletionRoutine(irp, 1070 RequestAsynchronousIrpCompletion, 1071 DeviceExtension, 1072 TRUE, 1073 TRUE, 1074 TRUE); 1075 1076 (VOID) IoCallDriver(TargetDeviceObject, irp); 1077 } 1078 } 1079 1080 NTSTATUS 1081 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ 1082 RequestAsynchronousIrpCompletion( 1083 _In_ PDEVICE_OBJECT DeviceObject, 1084 _In_ PIRP Irp, 1085 _In_reads_opt_(_Inexpressible_("varies")) PVOID Context 1086 ) 1087 /*++ 1088 1089 Routine Description: 1090 1091 Free the Irp. 1092 1093 Arguments: 1094 1095 DeviceObject - device that the completion routine fires on. 1096 1097 Irp - The irp to be completed. 1098 1099 Context - IRP context 1100 1101 Return Value: 1102 NTSTATUS 1103 1104 --*/ 1105 { 1106 UNREFERENCED_PARAMETER(DeviceObject); 1107 UNREFERENCED_PARAMETER(Context); 1108 1109 IoFreeIrp(Irp); 1110 1111 return STATUS_MORE_PROCESSING_REQUIRED; 1112 } 1113 1114 VOID 1115 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ 1116 DeviceAsynchronousCompletion( 1117 _In_ WDFREQUEST Request, 1118 _In_ WDFIOTARGET Target, 1119 _In_ PWDF_REQUEST_COMPLETION_PARAMS Params, 1120 _In_ WDFCONTEXT Context 1121 ) 1122 /*++ 1123 1124 Routine Description: 1125 1126 This routine is called when an asynchronous I/O request 1127 which was issused by the class driver completes. Examples of such requests 1128 are release queue or START UNIT. This routine releases the queue if 1129 necessary. It then frees the context and the IRP. 1130 1131 Arguments: 1132 1133 DeviceObject - The device object for the logical unit; however since this 1134 is the top stack location the value is NULL. 1135 1136 Irp - Supplies a pointer to the Irp to be processed. 1137 1138 Context - Supplies the context to be used to process this request. 1139 1140 Return Value: 1141 1142 None. 1143 1144 --*/ 1145 { 1146 PCOMPLETION_CONTEXT context = (PCOMPLETION_CONTEXT)Context; 1147 PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(context->Device); 1148 1149 UNREFERENCED_PARAMETER(Target); 1150 UNREFERENCED_PARAMETER(Params); 1151 1152 // If this is an execute srb, then check the return status and make sure. 1153 // the queue is not frozen. 1154 if (context->Srb.Function == SRB_FUNCTION_EXECUTE_SCSI) 1155 { 1156 // Check for a frozen queue. 1157 if (context->Srb.SrbStatus & SRB_STATUS_QUEUE_FROZEN) 1158 { 1159 // Unfreeze the queue getting the device object from the context. 1160 DeviceReleaseQueue(context->Device); 1161 } 1162 } 1163 1164 // free port-allocated sense buffer if we can detect 1165 // 1166 if (PORT_ALLOCATED_SENSE(deviceExtension, &context->Srb)) 1167 { 1168 FREE_PORT_ALLOCATED_SENSE_BUFFER(deviceExtension, &context->Srb); 1169 } 1170 1171 FREE_POOL(context); 1172 1173 WdfObjectDelete(Request); 1174 1175 } // end DeviceAsynchronousCompletion() 1176 1177 1178 VOID 1179 DeviceReleaseQueue( 1180 _In_ WDFDEVICE Device 1181 ) 1182 /*++ 1183 1184 Routine Description: 1185 1186 This routine issues an internal device control command 1187 to the port driver to release a frozen queue. The call 1188 is issued asynchronously as DeviceReleaseQueue will be invoked 1189 from the IO completion DPC (and will have no context to 1190 wait for a synchronous call to complete). 1191 1192 This routine must be called with the remove lock held. 1193 1194 Arguments: 1195 1196 Device - The functional device object for the device with the frozen queue. 1197 1198 Return Value: 1199 1200 None. 1201 1202 --*/ 1203 { 1204 PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device); 1205 PSCSI_REQUEST_BLOCK srb = NULL; 1206 KIRQL currentIrql; 1207 1208 // we raise irql seperately so we're not swapped out or suspended 1209 // while holding the release queue irp in this routine. this lets 1210 // us release the spin lock before lowering irql. 1211 KeRaiseIrql(DISPATCH_LEVEL, ¤tIrql); 1212 1213 WdfSpinLockAcquire(deviceExtension->ReleaseQueueSpinLock); 1214 1215 if (deviceExtension->ReleaseQueueInProgress) 1216 { 1217 // Someone is already doing this work - just set the flag to indicate that 1218 // we need to release the queue again. 1219 deviceExtension->ReleaseQueueNeeded = TRUE; 1220 WdfSpinLockRelease(deviceExtension->ReleaseQueueSpinLock); 1221 KeLowerIrql(currentIrql); 1222 1223 return; 1224 } 1225 1226 // Mark that there is a release queue in progress and drop the spinlock. 1227 deviceExtension->ReleaseQueueInProgress = TRUE; 1228 1229 WdfSpinLockRelease(deviceExtension->ReleaseQueueSpinLock); 1230 1231 srb = &(deviceExtension->ReleaseQueueSrb); 1232 1233 // Optical media are removable, so we just flush the queue. This will also release it. 1234 srb->Function = SRB_FUNCTION_FLUSH_QUEUE; 1235 1236 srb->OriginalRequest = WdfRequestWdmGetIrp(deviceExtension->ReleaseQueueRequest); 1237 1238 // Set a CompletionRoutine callback function. 1239 WdfRequestSetCompletionRoutine(deviceExtension->ReleaseQueueRequest, 1240 DeviceReleaseQueueCompletion, 1241 Device); 1242 // Send the request. If an error occurs, complete the request. 1243 RequestSend(deviceExtension, 1244 deviceExtension->ReleaseQueueRequest, 1245 deviceExtension->IoTarget, 1246 WDF_REQUEST_SEND_OPTION_IGNORE_TARGET_STATE, 1247 NULL); 1248 1249 KeLowerIrql(currentIrql); 1250 1251 return; 1252 1253 } // end DeviceReleaseQueue() 1254 1255 VOID 1256 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ 1257 DeviceReleaseQueueCompletion( 1258 _In_ WDFREQUEST Request, 1259 _In_ WDFIOTARGET Target, 1260 _In_ PWDF_REQUEST_COMPLETION_PARAMS Params, 1261 _In_ WDFCONTEXT Context 1262 ) 1263 /*++ 1264 1265 Routine Description: 1266 1267 This routine is called when an asynchronous release queue request which 1268 was issused in DeviceReleaseQueue completes. This routine prepares for 1269 the next release queue request and resends it if necessary. 1270 1271 Arguments: 1272 1273 Request - The completed request. 1274 1275 Target - IoTarget object 1276 1277 Params - Completion parameters 1278 1279 Context - WDFDEVICE object handle. 1280 1281 Return Value: 1282 1283 None. 1284 1285 --*/ 1286 { 1287 NTSTATUS status; 1288 WDFDEVICE device = Context; 1289 PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(device); 1290 1291 BOOLEAN releaseQueueNeeded = FALSE; 1292 WDF_REQUEST_REUSE_PARAMS params = {0}; 1293 1294 UNREFERENCED_PARAMETER(Target); 1295 UNREFERENCED_PARAMETER(Params); 1296 1297 WDF_REQUEST_REUSE_PARAMS_INIT(¶ms, 1298 WDF_REQUEST_REUSE_NO_FLAGS, 1299 STATUS_SUCCESS); 1300 1301 // Grab the spinlock and clear the release queue in progress flag so others 1302 // can run. Save (and clear) the state of the release queue needed flag 1303 // so that we can issue a new release queue outside the spinlock. 1304 WdfSpinLockAcquire(deviceExtension->ReleaseQueueSpinLock); 1305 1306 releaseQueueNeeded = deviceExtension->ReleaseQueueNeeded; 1307 1308 deviceExtension->ReleaseQueueNeeded = FALSE; 1309 deviceExtension->ReleaseQueueInProgress = FALSE; 1310 1311 // Reuse the ReleaseQueueRequest for the next time. 1312 status = WdfRequestReuse(Request,¶ms); 1313 1314 if (NT_SUCCESS(status)) 1315 { 1316 // Preformat the ReleaseQueueRequest for the next time. 1317 // This should always succeed because it was already preformatted once during device initialization 1318 status = WdfIoTargetFormatRequestForInternalIoctlOthers(deviceExtension->IoTarget, 1319 Request, 1320 IOCTL_SCSI_EXECUTE_NONE, 1321 deviceExtension->ReleaseQueueInputMemory, 1322 NULL, 1323 NULL, 1324 NULL, 1325 NULL, 1326 NULL); 1327 } 1328 1329 if (!NT_SUCCESS(status)) 1330 { 1331 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, 1332 "DeviceReleaseQueueCompletion: WdfIoTargetFormatRequestForInternalIoctlOthers failed, %!STATUS!\n", 1333 status)); 1334 } 1335 1336 RequestClearSendTime(Request); 1337 1338 WdfSpinLockRelease(deviceExtension->ReleaseQueueSpinLock); 1339 1340 // If we need a release queue then issue one now. Another processor may 1341 // have already started one in which case we'll try to issue this one after 1342 // it is done - but we should never recurse more than one deep. 1343 if (releaseQueueNeeded) 1344 { 1345 DeviceReleaseQueue(device); 1346 } 1347 1348 return; 1349 1350 } // DeviceReleaseQueueCompletion() 1351 1352 1353 // 1354 // In order to provide better performance without the need to reboot, 1355 // we need to implement a self-adjusting method to set and clear the 1356 // srb flags based upon current performance. 1357 // 1358 // whenever there is an error, immediately grab the spin lock. the 1359 // MP perf hit here is acceptable, since we're in an error path. this 1360 // is also neccessary because we are guaranteed to be modifying the 1361 // SRB flags here, setting SuccessfulIO to zero, and incrementing the 1362 // actual error count (which is always done within this spinlock). 1363 // 1364 // whenever there is no error, increment a counter. if there have been 1365 // errors on the device, and we've enabled dynamic perf, *and* we've 1366 // just crossed the perf threshhold, then grab the spin lock and 1367 // double check that the threshhold has, indeed been hit(*). then 1368 // decrement the error count, and if it's dropped sufficiently, undo 1369 // some of the safety changes made in the SRB flags due to the errors. 1370 // 1371 // * this works in all cases. even if lots of ios occur after the 1372 // previous guy went in and cleared the successfulio counter, that 1373 // just means that we've hit the threshhold again, and so it's proper 1374 // to run the inner loop again. 1375 // 1376 1377 VOID 1378 DevicePerfIncrementErrorCount( 1379 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension 1380 ) 1381 { 1382 PCDROM_PRIVATE_FDO_DATA fdoData = DeviceExtension->PrivateFdoData; 1383 KIRQL oldIrql; 1384 ULONG errors; 1385 1386 KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql); 1387 1388 fdoData->Perf.SuccessfulIO = 0; // implicit interlock 1389 errors = InterlockedIncrement((PLONG)&DeviceExtension->ErrorCount); 1390 1391 if (errors >= CLASS_ERROR_LEVEL_1) 1392 { 1393 // If the error count has exceeded the error limit, then disable 1394 // any tagged queuing, multiple requests per lu queueing 1395 // and sychronous data transfers. 1396 // 1397 // Clearing the no queue freeze flag prevents the port driver 1398 // from sending multiple requests per logical unit. 1399 CLEAR_FLAG(DeviceExtension->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE); 1400 CLEAR_FLAG(DeviceExtension->SrbFlags, SRB_FLAGS_QUEUE_ACTION_ENABLE); 1401 1402 SET_FLAG(DeviceExtension->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER); 1403 1404 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, 1405 "PerfIncrementErrorCount: Too many errors; disabling tagged queuing and " 1406 "synchronous data tranfers.\n")); 1407 } 1408 1409 if (errors >= CLASS_ERROR_LEVEL_2) 1410 { 1411 // If a second threshold is reached, disable disconnects. 1412 SET_FLAG(DeviceExtension->SrbFlags, SRB_FLAGS_DISABLE_DISCONNECT); 1413 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, 1414 "PerfIncrementErrorCount: Too many errors; disabling disconnects.\n")); 1415 } 1416 1417 KeReleaseSpinLock(&fdoData->SpinLock, oldIrql); 1418 return; 1419 } 1420 1421 1422 _IRQL_requires_max_(APC_LEVEL) 1423 PVOID 1424 DeviceFindFeaturePage( 1425 _In_reads_bytes_(Length) PGET_CONFIGURATION_HEADER FeatureBuffer, 1426 _In_ ULONG const Length, 1427 _In_ FEATURE_NUMBER const Feature 1428 ) 1429 /*++ 1430 Routine Description: 1431 1432 find the specific feature page in the buffer 1433 1434 Arguments: 1435 1436 FeatureBuffer - buffer contains the device feature set. 1437 1438 Length - buffer length 1439 1440 Feature - the feature number looking for. 1441 1442 Return Value: 1443 1444 PVOID - pointer to the starting location of the specific feature in buffer. 1445 1446 --*/ 1447 { 1448 PUCHAR buffer; 1449 PUCHAR limit; 1450 ULONG validLength; 1451 1452 PAGED_CODE(); 1453 1454 if (Length < sizeof(GET_CONFIGURATION_HEADER) + sizeof(FEATURE_HEADER)) 1455 { 1456 return NULL; 1457 } 1458 1459 // Calculate the length of valid data available in the 1460 // capabilities buffer from the DataLength field 1461 REVERSE_BYTES(&validLength, FeatureBuffer->DataLength); 1462 1463 validLength += RTL_SIZEOF_THROUGH_FIELD(GET_CONFIGURATION_HEADER, DataLength); 1464 1465 // set limit to point to first illegal address 1466 limit = (PUCHAR)FeatureBuffer; 1467 limit += min(Length, validLength); 1468 1469 // set buffer to point to first page 1470 buffer = FeatureBuffer->Data; 1471 1472 // loop through each page until we find the requested one, or 1473 // until it's not safe to access the entire feature header 1474 // (if equal, have exactly enough for the feature header) 1475 while (buffer + sizeof(FEATURE_HEADER) <= limit) 1476 { 1477 PFEATURE_HEADER header = (PFEATURE_HEADER)buffer; 1478 FEATURE_NUMBER thisFeature; 1479 1480 thisFeature = (header->FeatureCode[0] << 8) | 1481 (header->FeatureCode[1]); 1482 1483 if (thisFeature == Feature) 1484 { 1485 PUCHAR temp; 1486 1487 // if don't have enough memory to safely access all the feature 1488 // information, return NULL 1489 temp = buffer; 1490 temp += sizeof(FEATURE_HEADER); 1491 temp += header->AdditionalLength; 1492 1493 if (temp > limit) 1494 { 1495 // this means the transfer was cut-off, an insufficiently 1496 // small buffer was given, or other arbitrary error. since 1497 // it's not safe to view the amount of data (even though 1498 // the header is safe) in this feature, pretend it wasn't 1499 // transferred at all... 1500 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, 1501 "Feature %x exists, but not safe to access all its data. returning NULL\n", 1502 Feature)); 1503 return NULL; 1504 } 1505 else 1506 { 1507 return buffer; 1508 } 1509 } 1510 1511 if ((header->AdditionalLength % 4) && 1512 !(Feature >= 0xff00 && Feature <= 0xffff)) 1513 { 1514 return NULL; 1515 } 1516 1517 buffer += sizeof(FEATURE_HEADER); 1518 buffer += header->AdditionalLength; 1519 } 1520 1521 return NULL; 1522 } 1523 1524 1525 _IRQL_requires_max_(APC_LEVEL) 1526 VOID 1527 DevicePrintAllFeaturePages( 1528 _In_reads_bytes_(Usable) PGET_CONFIGURATION_HEADER Buffer, 1529 _In_ ULONG const Usable 1530 ) 1531 /*++ 1532 Routine Description: 1533 1534 print out all feature pages in the buffer 1535 1536 Arguments: 1537 1538 Buffer - buffer contains the device feature set. 1539 1540 Usable - 1541 1542 Return Value: 1543 1544 none 1545 1546 --*/ 1547 { 1548 #if DBG 1549 PFEATURE_HEADER header; 1550 1551 PAGED_CODE(); 1552 1553 //////////////////////////////////////////////////////////////////////////////// 1554 // items expected to ALWAYS be current if they exist 1555 //////////////////////////////////////////////////////////////////////////////// 1556 1557 header = DeviceFindFeaturePage(Buffer, Usable, FeatureProfileList); 1558 if (header != NULL) { 1559 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT, 1560 "CdromGetConfiguration: CurrentProfile %x " 1561 "with %x bytes of data at %p\n", 1562 Buffer->CurrentProfile[0] << 8 | 1563 Buffer->CurrentProfile[1], 1564 Usable, Buffer)); 1565 } 1566 1567 header = DeviceFindFeaturePage(Buffer, Usable, FeatureCore); 1568 if (header) { 1569 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT, 1570 "CdromGetConfiguration: %s %s\n", 1571 (header->Current ? 1572 "Currently supports" : "Is able to support"), 1573 "CORE Features" 1574 )); 1575 } 1576 1577 header = DeviceFindFeaturePage(Buffer, Usable, FeatureMorphing); 1578 if (header) { 1579 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT, 1580 "CdromGetConfiguration: %s %s\n", 1581 (header->Current ? 1582 "Currently supports" : "Is able to support"), 1583 "Morphing" 1584 )); 1585 } 1586 1587 header = DeviceFindFeaturePage(Buffer, Usable, FeatureRemovableMedium); 1588 if (header) { 1589 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT, 1590 "CdromGetConfiguration: %s %s\n", 1591 (header->Current ? 1592 "Currently supports" : "Is able to support"), 1593 "Removable Medium" 1594 )); 1595 } 1596 1597 header = DeviceFindFeaturePage(Buffer, Usable, FeaturePowerManagement); 1598 if (header) { 1599 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT, 1600 "CdromGetConfiguration: %s %s\n", 1601 (header->Current ? 1602 "Currently supports" : "Is able to support"), 1603 "Power Management" 1604 )); 1605 } 1606 1607 header = DeviceFindFeaturePage(Buffer, Usable, FeatureEmbeddedChanger); 1608 if (header) { 1609 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT, 1610 "CdromGetConfiguration: %s %s\n", 1611 (header->Current ? 1612 "Currently supports" : "Is able to support"), 1613 "Embedded Changer" 1614 )); 1615 } 1616 1617 header = DeviceFindFeaturePage(Buffer, Usable, FeatureMicrocodeUpgrade); 1618 if (header) { 1619 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT, 1620 "CdromGetConfiguration: %s %s\n", 1621 (header->Current ? 1622 "Currently supports" : "Is able to support"), 1623 "Microcode Update" 1624 )); 1625 } 1626 1627 header = DeviceFindFeaturePage(Buffer, Usable, FeatureTimeout); 1628 if (header) { 1629 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT, 1630 "CdromGetConfiguration: %s %s\n", 1631 (header->Current ? 1632 "Currently supports" : "Is able to support"), 1633 "Timeouts" 1634 )); 1635 } 1636 1637 header = DeviceFindFeaturePage(Buffer, Usable, FeatureLogicalUnitSerialNumber); 1638 if (header) { 1639 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT, 1640 "CdromGetConfiguration: %s %s\n", 1641 (header->Current ? 1642 "Currently supports" : "Is able to support"), 1643 "LUN Serial Number" 1644 )); 1645 } 1646 1647 header = DeviceFindFeaturePage(Buffer, Usable, FeatureFirmwareDate); 1648 if (header) { 1649 1650 ULONG featureSize = header->AdditionalLength; 1651 featureSize += RTL_SIZEOF_THROUGH_FIELD(FEATURE_HEADER, AdditionalLength); 1652 1653 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT, 1654 "CdromGetConfiguration: %s %s\n", 1655 (header->Current ? 1656 "Currently supports" : "Is able to support"), 1657 "Firmware Date" 1658 )); 1659 1660 if (featureSize >= RTL_SIZEOF_THROUGH_FIELD(FEATURE_DATA_FIRMWARE_DATE, Minute)) 1661 { 1662 PFEATURE_DATA_FIRMWARE_DATE date = (PFEATURE_DATA_FIRMWARE_DATE)header; 1663 // show date as "YYYY/MM/DD hh:mm", which is 18 chars (17+NULL) 1664 UCHAR dateString[18] = { 0 }; 1665 dateString[ 0] = date->Year[0]; 1666 dateString[ 1] = date->Year[1]; 1667 dateString[ 2] = date->Year[2]; 1668 dateString[ 3] = date->Year[3]; 1669 dateString[ 4] = '/'; 1670 dateString[ 5] = date->Month[0]; 1671 dateString[ 6] = date->Month[1]; 1672 dateString[ 7] = '/'; 1673 dateString[ 8] = date->Day[0]; 1674 dateString[ 9] = date->Day[1]; 1675 dateString[10] = ' '; 1676 dateString[11] = ' '; 1677 dateString[12] = date->Hour[0]; 1678 dateString[13] = date->Hour[1]; 1679 dateString[14] = ':'; 1680 dateString[15] = date->Minute[0]; 1681 dateString[16] = date->Minute[1]; 1682 dateString[17] = 0; 1683 // SECONDS IS NOT AVAILABLE ON EARLY IMPLEMENTATIONS -- ignore it 1684 //dateString[17] = ':'; 1685 //dateString[18] = date->Seconds[0]; 1686 //dateString[19] = date->Seconds[1]; 1687 //dateString[20] = 0; 1688 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT, 1689 "CdromGetConfiguration: Firmware Date/Time %s (UTC)\n", 1690 (PCSTR)dateString 1691 )); 1692 } 1693 } 1694 1695 //////////////////////////////////////////////////////////////////////////////// 1696 // items expected not to always be current 1697 //////////////////////////////////////////////////////////////////////////////// 1698 1699 1700 header = DeviceFindFeaturePage(Buffer, Usable, FeatureWriteProtect); 1701 if (header) { 1702 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_GENERAL, 1703 "CdromGetConfiguration: %s %s\n", 1704 (header->Current ? 1705 "Currently supports" : "Is able to support"), 1706 "Software Write Protect" 1707 )); 1708 } 1709 1710 header = DeviceFindFeaturePage(Buffer, Usable, FeatureRandomReadable); 1711 if (header) { 1712 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT, 1713 "CdromGetConfiguration: %s %s\n", 1714 (header->Current ? 1715 "Currently supports" : "Is able to support"), 1716 "Random Reads" 1717 )); 1718 } 1719 1720 header = DeviceFindFeaturePage(Buffer, Usable, FeatureMultiRead); 1721 if (header) { 1722 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT, 1723 "CdromGetConfiguration: %s %s\n", 1724 (header->Current ? 1725 "Currently supports" : "Is able to support"), 1726 "Multi-Read" 1727 )); 1728 } 1729 1730 header = DeviceFindFeaturePage(Buffer, Usable, FeatureCdRead); 1731 if (header) { 1732 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT, 1733 "CdromGetConfiguration: %s %s\n", 1734 (header->Current ? 1735 "Currently supports" : "Is able to support"), 1736 "reading from CD-ROM/R/RW" 1737 )); 1738 } 1739 1740 header = DeviceFindFeaturePage(Buffer, Usable, FeatureDvdRead); 1741 if (header) { 1742 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT, 1743 "CdromGetConfiguration: %s %s\n", 1744 (header->Current ? 1745 "Currently supports" : "Is able to support"), 1746 "DVD Structure Reads" 1747 )); 1748 } 1749 1750 header = DeviceFindFeaturePage(Buffer, Usable, FeatureRandomWritable); 1751 if (header) { 1752 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT, 1753 "CdromGetConfiguration: %s %s\n", 1754 (header->Current ? 1755 "Currently supports" : "Is able to support"), 1756 "Random Writes" 1757 )); 1758 } 1759 1760 header = DeviceFindFeaturePage(Buffer, Usable, FeatureIncrementalStreamingWritable); 1761 if (header) { 1762 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT, 1763 "CdromGetConfiguration: %s %s\n", 1764 (header->Current ? 1765 "Currently supports" : "Is able to support"), 1766 "Incremental Streaming Writing" 1767 )); 1768 } 1769 1770 header = DeviceFindFeaturePage(Buffer, Usable, FeatureSectorErasable); 1771 if (header) { 1772 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT, 1773 "CdromGetConfiguration: %s %s\n", 1774 (header->Current ? 1775 "Currently supports" : "Is able to support"), 1776 "Sector Erasable Media" 1777 )); 1778 } 1779 1780 header = DeviceFindFeaturePage(Buffer, Usable, FeatureFormattable); 1781 if (header) { 1782 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT, 1783 "CdromGetConfiguration: %s %s\n", 1784 (header->Current ? 1785 "Currently supports" : "Is able to support"), 1786 "Formatting" 1787 )); 1788 } 1789 1790 header = DeviceFindFeaturePage(Buffer, Usable, FeatureDefectManagement); 1791 if (header) { 1792 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT, 1793 "CdromGetConfiguration: %s %s\n", 1794 (header->Current ? 1795 "Currently supports" : "Is able to support"), 1796 "defect management" 1797 )); 1798 } 1799 1800 header = DeviceFindFeaturePage(Buffer, Usable, FeatureWriteOnce); 1801 if (header) { 1802 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT, 1803 "CdromGetConfiguration: %s %s\n", 1804 (header->Current ? 1805 "Currently supports" : "Is able to support"), 1806 "Write Once Media" 1807 )); 1808 } 1809 1810 header = DeviceFindFeaturePage(Buffer, Usable, FeatureRestrictedOverwrite); 1811 if (header) { 1812 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT, 1813 "CdromGetConfiguration: %s %s\n", 1814 (header->Current ? 1815 "Currently supports" : "Is able to support"), 1816 "Restricted Overwrites" 1817 )); 1818 } 1819 1820 header = DeviceFindFeaturePage(Buffer, Usable, FeatureCdrwCAVWrite); 1821 if (header) { 1822 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT, 1823 "CdromGetConfiguration: %s %s\n", 1824 (header->Current ? 1825 "Currently supports" : "Is able to support"), 1826 "CD-RW CAV recording" 1827 )); 1828 } 1829 1830 header = DeviceFindFeaturePage(Buffer, Usable, FeatureMrw); 1831 if (header) { 1832 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT, 1833 "CdromGetConfiguration: %s %s\n", 1834 (header->Current ? 1835 "Currently supports" : "Is able to support"), 1836 "Mount Rainier media" 1837 )); 1838 } 1839 1840 header = DeviceFindFeaturePage(Buffer, Usable, FeatureEnhancedDefectReporting); 1841 if (header) { 1842 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT, 1843 "CdromGetConfiguration: %s %s\n", 1844 (header->Current ? 1845 "Currently supports" : "Is able to support"), 1846 "Enhanced Defect Reporting" 1847 )); 1848 } 1849 1850 header = DeviceFindFeaturePage(Buffer, Usable, FeatureDvdPlusRW); 1851 if (header) { 1852 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT, 1853 "CdromGetConfiguration: %s %s\n", 1854 (header->Current ? 1855 "Currently supports" : "Is able to support"), 1856 "DVD+RW media" 1857 )); 1858 } 1859 1860 header = DeviceFindFeaturePage(Buffer, Usable, FeatureRigidRestrictedOverwrite); 1861 if (header) { 1862 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT, 1863 "CdromGetConfiguration: %s %s\n", 1864 (header->Current ? 1865 "Currently supports" : "Is able to support"), 1866 "Rigid Restricted Overwrite" 1867 )); 1868 } 1869 1870 header = DeviceFindFeaturePage(Buffer, Usable, FeatureCdTrackAtOnce); 1871 if (header) { 1872 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT, 1873 "CdromGetConfiguration: %s %s\n", 1874 (header->Current ? 1875 "Currently supports" : "Is able to support"), 1876 "CD Recording (Track At Once)" 1877 )); 1878 } 1879 1880 header = DeviceFindFeaturePage(Buffer, Usable, FeatureCdMastering); 1881 if (header) { 1882 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT, 1883 "CdromGetConfiguration: %s %s\n", 1884 (header->Current ? 1885 "Currently supports" : "Is able to support"), 1886 "CD Recording (Mastering)" 1887 )); 1888 } 1889 1890 header = DeviceFindFeaturePage(Buffer, Usable, FeatureDvdRecordableWrite); 1891 if (header) { 1892 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT, 1893 "CdromGetConfiguration: %s %s\n", 1894 (header->Current ? 1895 "Currently supports" : "Is able to support"), 1896 "DVD Recording (Mastering)" 1897 )); 1898 } 1899 1900 header = DeviceFindFeaturePage(Buffer, Usable, FeatureDDCDRead); 1901 if (header) { 1902 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT, 1903 "CdromGetConfiguration: %s %s\n", 1904 (header->Current ? 1905 "Currently supports" : "Is able to support"), 1906 "DD CD Reading" 1907 )); 1908 } 1909 1910 header = DeviceFindFeaturePage(Buffer, Usable, FeatureDDCDRWrite); 1911 if (header) { 1912 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT, 1913 "CdromGetConfiguration: %s %s\n", 1914 (header->Current ? 1915 "Currently supports" : "Is able to support"), 1916 "DD CD-R Writing" 1917 )); 1918 } 1919 1920 header = DeviceFindFeaturePage(Buffer, Usable, FeatureDDCDRWWrite); 1921 if (header) { 1922 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT, 1923 "CdromGetConfiguration: %s %s\n", 1924 (header->Current ? 1925 "Currently supports" : "Is able to support"), 1926 "DD CD-RW Writing" 1927 )); 1928 } 1929 1930 header = DeviceFindFeaturePage(Buffer, Usable, FeatureLayerJumpRecording); 1931 if (header) { 1932 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT, 1933 "CdromGetConfiguration: %s %s\n", 1934 (header->Current ? 1935 "Currently supports" : "Is able to support"), 1936 "Layer Jump Recording" 1937 )); 1938 } 1939 1940 header = DeviceFindFeaturePage(Buffer, Usable, FeatureHDDVDRead); 1941 if (header) { 1942 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT, 1943 "CdromGetConfiguration: %s %s\n", 1944 (header->Current ? 1945 "Currently supports" : "Is able to support"), 1946 "HD-DVD Reading" 1947 )); 1948 } 1949 1950 header = DeviceFindFeaturePage(Buffer, Usable, FeatureHDDVDWrite); 1951 if (header) { 1952 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT, 1953 "CdromGetConfiguration: %s %s\n", 1954 (header->Current ? 1955 "Currently supports" : "Is able to support"), 1956 "HD-DVD Writing" 1957 )); 1958 } 1959 1960 1961 header = DeviceFindFeaturePage(Buffer, Usable, FeatureSMART); 1962 if (header) { 1963 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT, 1964 "CdromGetConfiguration: %s %s\n", 1965 (header->Current ? 1966 "Currently supports" : "Is able to support"), 1967 "S.M.A.R.T." 1968 )); 1969 } 1970 1971 header = DeviceFindFeaturePage(Buffer, Usable, FeatureCDAudioAnalogPlay); 1972 if (header) { 1973 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT, 1974 "CdromGetConfiguration: %s %s\n", 1975 (header->Current ? 1976 "Currently supports" : "Is able to support"), 1977 "Analogue CD Audio Operations" 1978 )); 1979 } 1980 1981 header = DeviceFindFeaturePage(Buffer, Usable, FeatureDvdCSS); 1982 if (header) { 1983 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT, 1984 "CdromGetConfiguration: %s %s\n", 1985 (header->Current ? 1986 "Currently supports" : "Is able to support"), 1987 "DVD CSS" 1988 )); 1989 } 1990 1991 header = DeviceFindFeaturePage(Buffer, Usable, FeatureRealTimeStreaming); 1992 if (header) { 1993 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT, 1994 "CdromGetConfiguration: %s %s\n", 1995 (header->Current ? 1996 "Currently supports" : "Is able to support"), 1997 "Real-time Streaming Reads" 1998 )); 1999 } 2000 2001 header = DeviceFindFeaturePage(Buffer, Usable, FeatureDiscControlBlocks); 2002 if (header) { 2003 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT, 2004 "CdromGetConfiguration: %s %s\n", 2005 (header->Current ? 2006 "Currently supports" : "Is able to support"), 2007 "DVD Disc Control Blocks" 2008 )); 2009 } 2010 2011 header = DeviceFindFeaturePage(Buffer, Usable, FeatureDvdCPRM); 2012 if (header) { 2013 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT, 2014 "CdromGetConfiguration: %s %s\n", 2015 (header->Current ? 2016 "Currently supports" : "Is able to support"), 2017 "DVD CPRM" 2018 )); 2019 } 2020 2021 header = DeviceFindFeaturePage(Buffer, Usable, FeatureAACS); 2022 if (header) { 2023 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT, 2024 "CdromGetConfiguration: %s %s\n", 2025 (header->Current ? 2026 "Currently supports" : "Is able to support"), 2027 "AACS" 2028 )); 2029 } 2030 2031 #else 2032 PAGED_CODE(); 2033 2034 UNREFERENCED_PARAMETER(Usable); 2035 UNREFERENCED_PARAMETER(Buffer); 2036 2037 #endif // DBG 2038 return; 2039 } 2040 2041 2042 _IRQL_requires_max_(PASSIVE_LEVEL) 2043 NTSTATUS 2044 MediaReadCapacity( 2045 _In_ WDFDEVICE Device 2046 ) 2047 /*++ 2048 Routine Description: 2049 2050 Get media capacity 2051 2052 Arguments: 2053 2054 Device - the device that owns the media 2055 2056 Return Value: 2057 2058 NTSTATUS 2059 2060 --*/ 2061 { 2062 NTSTATUS status; 2063 SCSI_REQUEST_BLOCK srb; 2064 PCDB cdb = NULL; 2065 READ_CAPACITY_DATA capacityData; 2066 2067 PAGED_CODE(); 2068 2069 RtlZeroMemory(&srb, sizeof(srb)); 2070 RtlZeroMemory(&capacityData, sizeof(capacityData)); 2071 2072 cdb = (PCDB)(&srb.Cdb); 2073 2074 //Prepare SCSI command fields 2075 srb.CdbLength = 10; 2076 srb.TimeOutValue = CDROM_READ_CAPACITY_TIMEOUT; 2077 cdb->CDB10.OperationCode = SCSIOP_READ_CAPACITY; 2078 2079 status = DeviceSendSrbSynchronously(Device, 2080 &srb, 2081 &capacityData, 2082 sizeof(READ_CAPACITY_DATA), 2083 FALSE, 2084 NULL); 2085 2086 //Remember the result 2087 if (!NT_SUCCESS(status)) 2088 { 2089 //Set the BytesPerBlock to zero, this is for safe as if error happens this field should stay zero (no change). 2090 //it will be treated as error case in MediaReadCapacityDataInterpret() 2091 capacityData.BytesPerBlock = 0; 2092 } 2093 2094 MediaReadCapacityDataInterpret(Device, &capacityData); 2095 2096 return status; 2097 } 2098 2099 2100 _IRQL_requires_max_(APC_LEVEL) 2101 VOID 2102 MediaReadCapacityDataInterpret( 2103 _In_ WDFDEVICE Device, 2104 _In_ PREAD_CAPACITY_DATA ReadCapacityBuffer 2105 ) 2106 /*++ 2107 Routine Description: 2108 2109 Interpret media capacity and set corresponding fields in device context 2110 2111 Arguments: 2112 2113 Device - the device that owns the media 2114 2115 ReadCapacityBuffer - data buffer of capacity 2116 2117 Return Value: 2118 2119 none 2120 2121 --*/ 2122 { 2123 PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device); 2124 ULONG lastSector = 0; 2125 ULONG bps = 0; 2126 ULONG lastBit = 0; 2127 ULONG bytesPerBlock = 0; 2128 BOOLEAN errorHappened = FALSE; 2129 2130 PAGED_CODE(); 2131 2132 NT_ASSERT(ReadCapacityBuffer); 2133 2134 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, 2135 "MediaReadCapacityDataInterpret: Entering\n")); 2136 2137 // Swizzle bytes from Read Capacity and translate into 2138 // the necessary geometry information in the device extension. 2139 bytesPerBlock = ReadCapacityBuffer->BytesPerBlock; 2140 2141 ((PFOUR_BYTE)&bps)->Byte0 = ((PFOUR_BYTE)&bytesPerBlock)->Byte3; 2142 ((PFOUR_BYTE)&bps)->Byte1 = ((PFOUR_BYTE)&bytesPerBlock)->Byte2; 2143 ((PFOUR_BYTE)&bps)->Byte2 = ((PFOUR_BYTE)&bytesPerBlock)->Byte1; 2144 ((PFOUR_BYTE)&bps)->Byte3 = ((PFOUR_BYTE)&bytesPerBlock)->Byte0; 2145 2146 // Insure that bps is a power of 2. 2147 // This corrects a problem with the HP 4020i CDR where it 2148 // returns an incorrect number for bytes per sector. 2149 if (!bps) 2150 { 2151 // Set disk geometry to default values (per ISO 9660). 2152 bps = 2048; 2153 errorHappened = TRUE; 2154 } 2155 else 2156 { 2157 lastBit = (ULONG)(-1); 2158 while (bps) 2159 { 2160 lastBit++; 2161 bps = (bps >> 1); 2162 } 2163 bps = (1 << lastBit); 2164 } 2165 2166 deviceExtension->DiskGeometry.BytesPerSector = bps; 2167 2168 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, 2169 "MediaReadCapacityDataInterpret: Calculated bps %#x\n", 2170 deviceExtension->DiskGeometry.BytesPerSector)); 2171 2172 // Copy last sector in reverse byte order. 2173 bytesPerBlock = ReadCapacityBuffer->LogicalBlockAddress; 2174 2175 ((PFOUR_BYTE)&lastSector)->Byte0 = ((PFOUR_BYTE)&bytesPerBlock)->Byte3; 2176 ((PFOUR_BYTE)&lastSector)->Byte1 = ((PFOUR_BYTE)&bytesPerBlock)->Byte2; 2177 ((PFOUR_BYTE)&lastSector)->Byte2 = ((PFOUR_BYTE)&bytesPerBlock)->Byte1; 2178 ((PFOUR_BYTE)&lastSector)->Byte3 = ((PFOUR_BYTE)&bytesPerBlock)->Byte0; 2179 2180 // Calculate sector to byte shift. 2181 WHICH_BIT(bps, deviceExtension->SectorShift); 2182 2183 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_GENERAL, 2184 "MediaReadCapacityDataInterpret: Sector size is %d\n", 2185 deviceExtension->DiskGeometry.BytesPerSector)); 2186 2187 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, 2188 "MediaReadCapacityDataInterpret: Number of Sectors is %d\n", 2189 lastSector + 1)); 2190 2191 // Calculate media capacity in bytes. 2192 if (errorHappened) 2193 { 2194 // Set disk geometry to default values (per ISO 9660). 2195 deviceExtension->PartitionLength.QuadPart = (LONGLONG)(0x7fffffff); 2196 } 2197 else 2198 { 2199 deviceExtension->PartitionLength.QuadPart = (LONGLONG)(lastSector + 1); 2200 deviceExtension->PartitionLength.QuadPart = 2201 (deviceExtension->PartitionLength.QuadPart << deviceExtension->SectorShift); 2202 } 2203 2204 // we've defaulted to 32/64 forever. don't want to change this now... 2205 deviceExtension->DiskGeometry.TracksPerCylinder = 0x40; 2206 deviceExtension->DiskGeometry.SectorsPerTrack = 0x20; 2207 2208 // Calculate number of cylinders. 2209 deviceExtension->DiskGeometry.Cylinders.QuadPart = (LONGLONG)((lastSector + 1) / (32 * 64)); 2210 2211 deviceExtension->DiskGeometry.MediaType = RemovableMedia; 2212 2213 return; 2214 } 2215 2216 2217 _IRQL_requires_max_(APC_LEVEL) 2218 VOID 2219 DevicePickDvdRegion( 2220 _In_ WDFDEVICE Device 2221 ) 2222 /*++ 2223 2224 Routine Description: 2225 2226 pick a default dvd region 2227 2228 Arguments: 2229 2230 Device - Device Object 2231 2232 Return Value: 2233 2234 none 2235 2236 --*/ 2237 { 2238 NTSTATUS status; 2239 PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device); 2240 2241 // these five pointers all point to dvdReadStructure or part of 2242 // its data, so don't deallocate them more than once! 2243 PDVD_READ_STRUCTURE dvdReadStructure; 2244 PDVD_COPY_PROTECT_KEY copyProtectKey; 2245 PDVD_COPYRIGHT_DESCRIPTOR dvdCopyRight; 2246 PDVD_RPC_KEY rpcKey; 2247 PDVD_SET_RPC_KEY dvdRpcKey; 2248 2249 size_t bytesReturned = 0; 2250 ULONG bufferLen = 0; 2251 UCHAR mediaRegion = 0; 2252 ULONG pickDvdRegion = 0; 2253 ULONG defaultDvdRegion = 0; 2254 ULONG dvdRegion = 0; 2255 WDFKEY registryKey = NULL; 2256 2257 DECLARE_CONST_UNICODE_STRING(registryValueName, DVD_DEFAULT_REGION); 2258 2259 PAGED_CODE(); 2260 2261 if ((pickDvdRegion = InterlockedExchange((PLONG)&deviceExtension->DeviceAdditionalData.PickDvdRegion, 0)) == 0) 2262 { 2263 // it was non-zero, so either another thread will do this, or 2264 // we no longer need to pick a region 2265 return; 2266 } 2267 2268 bufferLen = max( 2269 max(sizeof(DVD_DESCRIPTOR_HEADER) + 2270 sizeof(DVD_COPYRIGHT_DESCRIPTOR), 2271 sizeof(DVD_READ_STRUCTURE) 2272 ), 2273 max(DVD_RPC_KEY_LENGTH, 2274 DVD_SET_RPC_KEY_LENGTH 2275 ) 2276 ); 2277 2278 dvdReadStructure = (PDVD_READ_STRUCTURE) 2279 ExAllocatePoolWithTag(PagedPool, bufferLen, DVD_TAG_DVD_REGION); 2280 2281 if (dvdReadStructure == NULL) 2282 { 2283 InterlockedExchange((PLONG)&deviceExtension->DeviceAdditionalData.PickDvdRegion, pickDvdRegion); 2284 return; 2285 } 2286 2287 copyProtectKey = (PDVD_COPY_PROTECT_KEY)dvdReadStructure; 2288 2289 dvdCopyRight = (PDVD_COPYRIGHT_DESCRIPTOR) 2290 ((PDVD_DESCRIPTOR_HEADER)dvdReadStructure)->Data; 2291 2292 // get the media region 2293 RtlZeroMemory (dvdReadStructure, bufferLen); 2294 dvdReadStructure->Format = DvdCopyrightDescriptor; 2295 2296 // Build and send a request for READ_KEY 2297 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL, 2298 "DevicePickDvdRegion (%p): Getting Copyright Descriptor\n", 2299 Device)); 2300 2301 status = ReadDvdStructure(deviceExtension, 2302 NULL, 2303 dvdReadStructure, 2304 sizeof(DVD_READ_STRUCTURE), 2305 dvdReadStructure, 2306 sizeof(DVD_DESCRIPTOR_HEADER) + sizeof(DVD_COPYRIGHT_DESCRIPTOR), 2307 &bytesReturned); 2308 2309 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL, 2310 "DevicePickDvdRegion (%p): Got Copyright Descriptor %x\n", 2311 Device, status)); 2312 2313 if ((NT_SUCCESS(status)) && 2314 (dvdCopyRight->CopyrightProtectionType == 0x01)) 2315 { 2316 // keep the media region bitmap around 2317 // a 1 means ok to play 2318 if (dvdCopyRight->RegionManagementInformation == 0xff) 2319 { 2320 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, 2321 "DevicePickDvdRegion (%p): RegionManagementInformation " 2322 "is set to dis-allow playback for all regions. This is " 2323 "most likely a poorly authored disc. defaulting to all " 2324 "region disc for purpose of choosing initial region\n", 2325 Device)); 2326 dvdCopyRight->RegionManagementInformation = 0; 2327 } 2328 2329 mediaRegion = ~dvdCopyRight->RegionManagementInformation; 2330 } 2331 else 2332 { 2333 // can't automatically pick a default region on a drive without media, so just exit 2334 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL, 2335 "DevicePickDvdRegion (%p): failed to auto-choose a region due to status %x getting copyright descriptor\n", 2336 Device, status)); 2337 goto getout; 2338 } 2339 2340 // get the device region 2341 RtlZeroMemory (copyProtectKey, bufferLen); 2342 copyProtectKey->KeyLength = DVD_RPC_KEY_LENGTH; 2343 copyProtectKey->KeyType = DvdGetRpcKey; 2344 2345 // Build and send a request for READ_KEY for RPC key 2346 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL, 2347 "DevicePickDvdRegion (%p): Getting RpcKey\n", 2348 Device)); 2349 status = DvdStartSessionReadKey(deviceExtension, 2350 IOCTL_DVD_READ_KEY, 2351 NULL, 2352 copyProtectKey, 2353 DVD_RPC_KEY_LENGTH, 2354 copyProtectKey, 2355 DVD_RPC_KEY_LENGTH, 2356 &bytesReturned); 2357 2358 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL, 2359 "DevicePickDvdRegion (%p): Got RpcKey %x\n", 2360 Device, status)); 2361 2362 if (!NT_SUCCESS(status)) 2363 { 2364 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL, 2365 "DevicePickDvdRegion (%p): failed to get RpcKey from " 2366 "a DVD Device\n", Device)); 2367 goto getout; 2368 } 2369 2370 // so we now have what we can get for the media region and the 2371 // drive region. we will not set a region if the drive has one 2372 // set already (mask is not all 1's), nor will we set a region 2373 // if there are no more user resets available. 2374 rpcKey = (PDVD_RPC_KEY)copyProtectKey->KeyData; 2375 2376 if (rpcKey->RegionMask != 0xff) 2377 { 2378 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, 2379 "DevicePickDvdRegion (%p): not picking a region since " 2380 "it is already chosen\n", Device)); 2381 goto getout; 2382 } 2383 2384 if (rpcKey->UserResetsAvailable <= 1) 2385 { 2386 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL, 2387 "DevicePickDvdRegion (%p): not picking a region since " 2388 "only one change remains\n", Device)); 2389 goto getout; 2390 } 2391 2392 // OOBE sets this key based upon the system locale 2393 status = WdfDriverOpenParametersRegistryKey(WdfGetDriver(), 2394 KEY_READ, 2395 WDF_NO_OBJECT_ATTRIBUTES, 2396 ®istryKey); 2397 2398 if (NT_SUCCESS(status)) 2399 { 2400 status = WdfRegistryQueryULong(registryKey, 2401 ®istryValueName, 2402 &defaultDvdRegion); 2403 2404 WdfRegistryClose(registryKey); 2405 } 2406 2407 if (!NT_SUCCESS(status)) 2408 { 2409 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL, 2410 "DevicePickDvdRegion (%p): failed to read registry value due to status %x\n", 2411 Device, status)); 2412 2413 // by default the default Dvd region is 0 2414 defaultDvdRegion = 0; 2415 status = STATUS_SUCCESS; 2416 } 2417 2418 if (defaultDvdRegion > DVD_MAX_REGION) 2419 { 2420 // the registry has a bogus default 2421 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL, 2422 "DevicePickDvdRegion (%p): registry has a bogus default " 2423 "region value of %x\n", Device, defaultDvdRegion)); 2424 2425 defaultDvdRegion = 0; 2426 } 2427 2428 // if defaultDvdRegion == 0, it means no default. 2429 2430 // we will select the initial dvd region for the user 2431 2432 if ((defaultDvdRegion != 0) && 2433 (mediaRegion & (1 << (defaultDvdRegion - 1)))) 2434 { 2435 // first choice: 2436 // the media has region that matches 2437 // the default dvd region. 2438 dvdRegion = (1 << (defaultDvdRegion - 1)); 2439 2440 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, 2441 "DevicePickDvdRegion (%p): Choice #1: media matches " 2442 "drive's default, chose region %x\n", Device, dvdRegion)); 2443 } 2444 else if (mediaRegion) 2445 { 2446 // second choice: 2447 // pick the lowest region number from the media 2448 UCHAR mask = 1; 2449 dvdRegion = 0; 2450 2451 while (mediaRegion && !dvdRegion) 2452 { 2453 // pick the lowest bit 2454 dvdRegion = mediaRegion & mask; 2455 mask <<= 1; 2456 } 2457 2458 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, 2459 "DevicePickDvdRegion (%p): Choice #2: choosing lowest " 2460 "media region %x\n", Device, dvdRegion)); 2461 } 2462 else if (defaultDvdRegion) 2463 { 2464 // third choice: 2465 // default dvd region from the dvd class installer 2466 dvdRegion = (1 << (defaultDvdRegion - 1)); 2467 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, 2468 "DevicePickDvdRegion (%p): Choice #3: using default " 2469 "region for this install %x\n", Device, dvdRegion)); 2470 } 2471 else 2472 { 2473 // unable to pick one for the user -- this should rarely 2474 // happen, since the proppage dvd class installer sets 2475 // the key based upon the system locale 2476 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL, 2477 "DevicePickDvdRegion (%p): Choice #4: failed to choose " 2478 "a media region\n", Device)); 2479 goto getout; 2480 } 2481 2482 // now that we've chosen a region, set the region by sending the 2483 // appropriate request to the drive 2484 RtlZeroMemory (copyProtectKey, bufferLen); 2485 copyProtectKey->KeyLength = DVD_SET_RPC_KEY_LENGTH; 2486 copyProtectKey->KeyType = DvdSetRpcKey; 2487 dvdRpcKey = (PDVD_SET_RPC_KEY)copyProtectKey->KeyData; 2488 dvdRpcKey->PreferredDriveRegionCode = (UCHAR)~dvdRegion; 2489 2490 // Build and send request for SEND_KEY 2491 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL, 2492 "DevicePickDvdRegion (%p): Sending new Rpc Key to region %x\n", 2493 Device, dvdRegion)); 2494 2495 status = DvdSendKey(deviceExtension, 2496 NULL, 2497 copyProtectKey, 2498 DVD_SET_RPC_KEY_LENGTH, 2499 &bytesReturned); 2500 2501 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL, 2502 "DevicePickDvdRegion (%p): Sent new Rpc Key %x\n", 2503 Device, status)); 2504 2505 if (!NT_SUCCESS(status)) 2506 { 2507 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL, "DevicePickDvdRegion (%p): unable to set dvd initial " 2508 " region code (%x)\n", Device, status)); 2509 } 2510 else 2511 { 2512 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL, "DevicePickDvdRegion (%p): Successfully set dvd " 2513 "initial region\n", Device)); 2514 2515 pickDvdRegion = 0; 2516 } 2517 2518 getout: 2519 if (dvdReadStructure) 2520 { 2521 FREE_POOL(dvdReadStructure); 2522 } 2523 2524 // update the new PickDvdRegion value 2525 InterlockedExchange((PLONG)&deviceExtension->DeviceAdditionalData.PickDvdRegion, pickDvdRegion); 2526 2527 return; 2528 } 2529 2530 _IRQL_requires_max_(PASSIVE_LEVEL) 2531 NTSTATUS 2532 DeviceRegisterInterface( 2533 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, 2534 _In_ CDROM_DEVICE_INTERFACES InterfaceType 2535 ) 2536 /*++ 2537 Routine Description: 2538 2539 used to register device class interface or mount device interface 2540 2541 Arguments: 2542 2543 DeviceExtension - device context 2544 2545 InterfaceType - interface type to be registered. 2546 2547 Return Value: 2548 2549 NTSTATUS 2550 2551 --*/ 2552 { 2553 NTSTATUS status; 2554 WDFSTRING string = NULL; 2555 GUID* interfaceGuid = NULL; 2556 PUNICODE_STRING savingString = NULL; 2557 BOOLEAN setRestricted = FALSE; 2558 UNICODE_STRING localString; 2559 2560 PAGED_CODE(); 2561 2562 //Get parameters 2563 switch(InterfaceType) 2564 { 2565 case CdRomDeviceInterface: 2566 interfaceGuid = (LPGUID)&GUID_DEVINTERFACE_CDROM; 2567 setRestricted = TRUE; 2568 savingString = &localString; 2569 break; 2570 case MountedDeviceInterface: 2571 interfaceGuid = (LPGUID)&MOUNTDEV_MOUNTED_DEVICE_GUID; 2572 savingString = &(DeviceExtension->MountedDeviceInterfaceName); 2573 break; 2574 default: 2575 return STATUS_INVALID_PARAMETER; 2576 } 2577 2578 status = WdfDeviceCreateDeviceInterface(DeviceExtension->Device, 2579 interfaceGuid, 2580 NULL); 2581 2582 if (!NT_SUCCESS(status)) 2583 { 2584 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT, 2585 "DeviceRegisterInterface: Unable to register cdrom " 2586 "DCA for fdo %p type: %s [%lx]\n", 2587 DeviceExtension->Device, 2588 (InterfaceType == CdRomDeviceInterface)? "CdRom Interface" : "Mounted Device Interface", 2589 status)); 2590 } 2591 2592 // Retrieve interface string 2593 if (NT_SUCCESS(status)) 2594 { 2595 // The string object will be released when its parent object is released. 2596 WDF_OBJECT_ATTRIBUTES attributes; 2597 2598 WDF_OBJECT_ATTRIBUTES_INIT(&attributes); 2599 attributes.ParentObject = DeviceExtension->Device; 2600 2601 status = WdfStringCreate(WDF_NO_OBJECT_ATTRIBUTES, 2602 NULL, 2603 &string); 2604 } 2605 2606 if (NT_SUCCESS(status)) 2607 { 2608 status = WdfDeviceRetrieveDeviceInterfaceString(DeviceExtension->Device, 2609 interfaceGuid, 2610 NULL, 2611 string); 2612 } 2613 2614 if (NT_SUCCESS(status)) 2615 { 2616 WdfStringGetUnicodeString(string, savingString); 2617 2618 if (setRestricted) { 2619 2620 2621 WdfObjectDelete(string); 2622 } 2623 } 2624 2625 return status; 2626 } // end DeviceRegisterInterface() 2627 2628 2629 VOID 2630 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ 2631 DeviceRestoreDefaultSpeed( 2632 _In_ WDFWORKITEM WorkItem 2633 ) 2634 /*++ 2635 2636 Routine Description: 2637 2638 This workitem is called on a media change when the CDROM device 2639 speed should be restored to the default value. 2640 2641 Arguments: 2642 2643 Fdo - Supplies the device object for the CDROM device. 2644 WorkItem - Supplies the pointer to the workitem. 2645 2646 Return Value: 2647 2648 None 2649 2650 --*/ 2651 { 2652 NTSTATUS status; 2653 WDFDEVICE device = WdfWorkItemGetParentObject(WorkItem); 2654 PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(device); 2655 PPERFORMANCE_DESCRIPTOR perfDescriptor; 2656 ULONG transferLength = sizeof(PERFORMANCE_DESCRIPTOR); 2657 SCSI_REQUEST_BLOCK srb = {0}; 2658 PCDB cdb = (PCDB)srb.Cdb; 2659 2660 PAGED_CODE(); 2661 2662 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DeviceRestoreDefaultSpeed: Restore device speed for %p\n", device)); 2663 2664 perfDescriptor = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned, 2665 transferLength, 2666 CDROM_TAG_STREAM); 2667 if (perfDescriptor == NULL) 2668 { 2669 return; 2670 } 2671 2672 RtlZeroMemory(perfDescriptor, transferLength); 2673 2674 perfDescriptor->RestoreDefaults = TRUE; 2675 2676 srb.TimeOutValue = deviceExtension->TimeOutValue; 2677 2678 srb.CdbLength = 12; 2679 cdb->SET_STREAMING.OperationCode = SCSIOP_SET_STREAMING; 2680 REVERSE_BYTES_SHORT(&cdb->SET_STREAMING.ParameterListLength, &transferLength); 2681 2682 status = DeviceSendSrbSynchronously(device, 2683 &srb, 2684 perfDescriptor, 2685 transferLength, 2686 TRUE, 2687 NULL); 2688 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, 2689 "DeviceRestoreDefaultSpeed: Set Streaming command completed with status: 0x%X\n", status)); 2690 2691 FREE_POOL(perfDescriptor); 2692 WdfObjectDelete(WorkItem); 2693 2694 return; 2695 } 2696 2697 // custom string match -- careful! 2698 _IRQL_requires_max_(APC_LEVEL) 2699 BOOLEAN 2700 StringsAreMatched( 2701 _In_opt_z_ PCHAR StringToMatch, 2702 _In_z_ PCHAR TargetString 2703 ) 2704 /*++ 2705 2706 Routine Description: 2707 2708 compares if two strings are identical 2709 2710 Arguments: 2711 2712 StringToMatch - source string. 2713 TargetString - target string. 2714 2715 Return Value: 2716 2717 BOOLEAN - TRUE (identical); FALSE (not match) 2718 2719 --*/ 2720 { 2721 size_t length; 2722 2723 PAGED_CODE(); 2724 2725 NT_ASSERT(TargetString); 2726 2727 // if no match requested, return TRUE 2728 if (StringToMatch == NULL) 2729 { 2730 return TRUE; 2731 } 2732 2733 // cache the string length for efficiency 2734 length = strlen(StringToMatch); 2735 2736 // ZERO-length strings may only match zero-length strings 2737 if (length == 0) 2738 { 2739 return (strlen(TargetString) == 0); 2740 } 2741 2742 // strncmp returns zero if the strings match 2743 return (strncmp(StringToMatch, TargetString, length) == 0); 2744 } 2745 2746 2747 NTSTATUS 2748 RequestSetContextFields( 2749 _In_ WDFREQUEST Request, 2750 _In_ PSYNC_HANDLER Handler 2751 ) 2752 /*++ 2753 2754 Routine Description: 2755 2756 set the request object context fields 2757 2758 Arguments: 2759 2760 Request - request object. 2761 Handler - the function that finally handles this request. 2762 2763 Return Value: 2764 2765 NTSTATUS 2766 2767 --*/ 2768 { 2769 NTSTATUS status = STATUS_SUCCESS; 2770 PCDROM_REQUEST_CONTEXT requestContext = RequestGetContext(Request); 2771 PKEVENT syncEvent = NULL; 2772 2773 syncEvent = ExAllocatePoolWithTag(NonPagedPoolNx, 2774 sizeof(KEVENT), 2775 CDROM_TAG_SYNC_EVENT); 2776 2777 if (syncEvent == NULL) 2778 { 2779 // memory allocation failed. 2780 status = STATUS_INSUFFICIENT_RESOURCES; 2781 } 2782 else 2783 { 2784 // now, put the special synchronization information into the context 2785 requestContext->SyncRequired = TRUE; 2786 requestContext->SyncEvent = syncEvent; 2787 requestContext->SyncCallback = Handler; 2788 2789 status = STATUS_SUCCESS; 2790 } 2791 2792 return status; 2793 } 2794 2795 2796 NTSTATUS 2797 RequestDuidGetDeviceIdProperty( 2798 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, 2799 _In_ WDFREQUEST Request, 2800 _In_ WDF_REQUEST_PARAMETERS RequestParameters, 2801 _Out_ size_t * DataLength 2802 ) 2803 /*++ 2804 2805 Routine Description: 2806 2807 2808 2809 Arguments: 2810 2811 DeviceExtension - device context 2812 Request - request object. 2813 RequestParameters - request parameter 2814 DataLength - transferred data length. 2815 2816 Return Value: 2817 2818 NTSTATUS 2819 2820 --*/ 2821 { 2822 NTSTATUS status = STATUS_SUCCESS; 2823 PSTORAGE_DEVICE_ID_DESCRIPTOR deviceIdDescriptor = NULL; 2824 PSTORAGE_DESCRIPTOR_HEADER descHeader = NULL; 2825 STORAGE_PROPERTY_ID propertyId = StorageDeviceIdProperty; 2826 2827 *DataLength = 0; 2828 2829 // Get the VPD page 83h data. 2830 status = DeviceRetrieveDescriptor(DeviceExtension->Device, 2831 &propertyId, 2832 (PSTORAGE_DESCRIPTOR_HEADER*)&deviceIdDescriptor); 2833 2834 if (NT_SUCCESS(status) && (deviceIdDescriptor == NULL)) 2835 { 2836 status = STATUS_NOT_FOUND; 2837 } 2838 2839 if (NT_SUCCESS(status)) 2840 { 2841 status = WdfRequestRetrieveOutputBuffer(Request, 2842 RequestParameters.Parameters.DeviceIoControl.OutputBufferLength, 2843 &descHeader, 2844 NULL); 2845 } 2846 2847 if (NT_SUCCESS(status)) 2848 { 2849 PSTORAGE_DEVICE_UNIQUE_IDENTIFIER storageDuid = NULL; 2850 ULONG offset = descHeader->Size; 2851 PUCHAR dest = (PUCHAR)descHeader + offset; 2852 size_t outputBufferSize; 2853 2854 outputBufferSize = RequestParameters.Parameters.DeviceIoControl.OutputBufferLength; 2855 2856 // Adjust required size and potential destination location. 2857 status = RtlULongAdd(descHeader->Size, deviceIdDescriptor->Size, &descHeader->Size); 2858 2859 if (NT_SUCCESS(status) && 2860 (outputBufferSize < descHeader->Size)) 2861 { 2862 // Output buffer is too small. Return error and make sure 2863 // the caller gets info about required buffer size. 2864 *DataLength = descHeader->Size; 2865 status = STATUS_BUFFER_OVERFLOW; 2866 } 2867 2868 if (NT_SUCCESS(status)) 2869 { 2870 storageDuid = (PSTORAGE_DEVICE_UNIQUE_IDENTIFIER)descHeader; 2871 storageDuid->StorageDeviceIdOffset = offset; 2872 2873 RtlCopyMemory(dest, 2874 deviceIdDescriptor, 2875 deviceIdDescriptor->Size); 2876 2877 *DataLength = storageDuid->Size; 2878 status = STATUS_SUCCESS; 2879 } 2880 2881 FREE_POOL(deviceIdDescriptor); 2882 } 2883 2884 return status; 2885 } 2886 2887 NTSTATUS 2888 RequestDuidGetDeviceProperty( 2889 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, 2890 _In_ WDFREQUEST Request, 2891 _In_ WDF_REQUEST_PARAMETERS RequestParameters, 2892 _Out_ size_t * DataLength 2893 ) 2894 /*++ 2895 2896 Routine Description: 2897 2898 2899 2900 Arguments: 2901 2902 DeviceExtension - device context 2903 Request - request object. 2904 RequestParameters - request parameter 2905 DataLength - transferred data length. 2906 2907 Return Value: 2908 2909 NTSTATUS 2910 2911 --*/ 2912 { 2913 NTSTATUS status = STATUS_SUCCESS; 2914 PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor = DeviceExtension->DeviceDescriptor; 2915 PSTORAGE_DESCRIPTOR_HEADER descHeader = NULL; 2916 PSTORAGE_DEVICE_UNIQUE_IDENTIFIER storageDuid; 2917 PUCHAR dest = NULL; 2918 2919 if (deviceDescriptor == NULL) 2920 { 2921 status = STATUS_NOT_FOUND; 2922 } 2923 2924 if (NT_SUCCESS(status)) 2925 { 2926 status = WdfRequestRetrieveOutputBuffer(Request, 2927 RequestParameters.Parameters.DeviceIoControl.OutputBufferLength, 2928 &descHeader, 2929 NULL); 2930 } 2931 2932 if (NT_SUCCESS(status) && 2933 (deviceDescriptor->SerialNumberOffset == 0)) 2934 { 2935 status = STATUS_NOT_FOUND; 2936 } 2937 2938 // Use this info only if serial number is available. 2939 if (NT_SUCCESS(status)) 2940 { 2941 ULONG offset = descHeader->Size; 2942 size_t outputBufferSize = RequestParameters.Parameters.DeviceIoControl.OutputBufferLength; 2943 2944 // Adjust required size and potential destination location. 2945 dest = (PUCHAR)descHeader + offset; 2946 2947 status = RtlULongAdd(descHeader->Size, deviceDescriptor->Size, &descHeader->Size); 2948 2949 if (NT_SUCCESS(status) && 2950 (outputBufferSize < descHeader->Size)) 2951 { 2952 // Output buffer is too small. Return error and make sure 2953 // the caller get info about required buffer size. 2954 *DataLength = descHeader->Size; 2955 status = STATUS_BUFFER_OVERFLOW; 2956 } 2957 2958 if (NT_SUCCESS(status)) 2959 { 2960 storageDuid = (PSTORAGE_DEVICE_UNIQUE_IDENTIFIER)descHeader; 2961 storageDuid->StorageDeviceOffset = offset; 2962 2963 RtlCopyMemory(dest, 2964 deviceDescriptor, 2965 deviceDescriptor->Size); 2966 2967 *DataLength = storageDuid->Size; 2968 status = STATUS_SUCCESS; 2969 } 2970 } 2971 2972 return status; 2973 } 2974 2975 _IRQL_requires_max_(APC_LEVEL) 2976 ULONG 2977 DeviceRetrieveModeSenseUsingScratch( 2978 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, 2979 _In_reads_bytes_(Length) PCHAR ModeSenseBuffer, 2980 _In_ ULONG Length, 2981 _In_ UCHAR PageCode, 2982 _In_ UCHAR PageControl 2983 ) 2984 /*++ 2985 2986 Routine Description: 2987 2988 retrieve mode sense informaiton of the device 2989 2990 Arguments: 2991 2992 DeviceExtension - device context 2993 ModeSenseBuffer - buffer to savee the mode sense info. 2994 Length - buffer length 2995 PageCode - . 2996 PageControl - 2997 2998 Return Value: 2999 3000 ULONG - transferred data length 3001 3002 --*/ 3003 { 3004 NTSTATUS status = STATUS_SUCCESS; 3005 ULONG transferSize = min(Length, DeviceExtension->ScratchContext.ScratchBufferSize); 3006 CDB cdb; 3007 3008 PAGED_CODE(); 3009 3010 ScratchBuffer_BeginUse(DeviceExtension); 3011 3012 RtlZeroMemory(&cdb, sizeof(CDB)); 3013 // Set up the CDB 3014 cdb.MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE; 3015 cdb.MODE_SENSE.PageCode = PageCode; 3016 cdb.MODE_SENSE.Pc = PageControl; 3017 cdb.MODE_SENSE.AllocationLength = (UCHAR)transferSize; 3018 3019 status = ScratchBuffer_ExecuteCdb(DeviceExtension, NULL, transferSize, TRUE, &cdb, 6); 3020 3021 if (NT_SUCCESS(status)) 3022 { 3023 transferSize = min(Length, DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength); 3024 RtlCopyMemory(ModeSenseBuffer, 3025 DeviceExtension->ScratchContext.ScratchBuffer, 3026 transferSize); 3027 } 3028 3029 ScratchBuffer_EndUse(DeviceExtension); 3030 3031 return transferSize; 3032 } 3033 3034 _IRQL_requires_max_(APC_LEVEL) 3035 PVOID 3036 ModeSenseFindSpecificPage( 3037 _In_reads_bytes_(Length) PCHAR ModeSenseBuffer, 3038 _In_ size_t Length, 3039 _In_ UCHAR PageMode, 3040 _In_ BOOLEAN Use6BytesCdb 3041 ) 3042 /*++ 3043 3044 Routine Description: 3045 3046 This routine scans through the mode sense data and finds the requested 3047 mode sense page code. 3048 3049 Arguments: 3050 ModeSenseBuffer - Supplies a pointer to the mode sense data. 3051 3052 Length - Indicates the length of valid data. 3053 3054 PageMode - Supplies the page mode to be searched for. 3055 3056 Use6BytesCdb - Indicates whether 6 or 10 byte mode sense was used. 3057 3058 Return Value: 3059 3060 A pointer to the the requested mode page. If the mode page was not found 3061 then NULL is return. 3062 3063 --*/ 3064 { 3065 PCHAR limit; 3066 ULONG parameterHeaderLength; 3067 PVOID result = NULL; 3068 3069 PAGED_CODE(); 3070 3071 limit = ModeSenseBuffer + Length; 3072 parameterHeaderLength = (Use6BytesCdb) 3073 ? sizeof(MODE_PARAMETER_HEADER) 3074 : sizeof(MODE_PARAMETER_HEADER10); 3075 3076 if (Length >= parameterHeaderLength) 3077 { 3078 PMODE_PARAMETER_HEADER10 modeParam10; 3079 ULONG blockDescriptorLength; 3080 3081 // Skip the mode select header and block descriptors. 3082 if (Use6BytesCdb) 3083 { 3084 blockDescriptorLength = ((PMODE_PARAMETER_HEADER)ModeSenseBuffer)->BlockDescriptorLength; 3085 } 3086 else 3087 { 3088 modeParam10 = (PMODE_PARAMETER_HEADER10) ModeSenseBuffer; 3089 blockDescriptorLength = modeParam10->BlockDescriptorLength[1]; 3090 } 3091 3092 ModeSenseBuffer += parameterHeaderLength + blockDescriptorLength; 3093 3094 // ModeSenseBuffer now points at pages. Walk the pages looking for the 3095 // requested page until the limit is reached. 3096 while (ModeSenseBuffer + 3097 RTL_SIZEOF_THROUGH_FIELD(MODE_DISCONNECT_PAGE, PageLength) < limit) 3098 { 3099 if (((PMODE_DISCONNECT_PAGE) ModeSenseBuffer)->PageCode == PageMode) 3100 { 3101 // found the mode page. make sure it's safe to touch it all 3102 // before returning the pointer to caller 3103 if (ModeSenseBuffer + ((PMODE_DISCONNECT_PAGE)ModeSenseBuffer)->PageLength > limit) 3104 { 3105 // Return NULL since the page is not safe to access in full 3106 result = NULL; 3107 } 3108 else 3109 { 3110 result = ModeSenseBuffer; 3111 } 3112 break; 3113 } 3114 3115 // Advance to the next page which is 4-byte-aligned offset after this page. 3116 ModeSenseBuffer += ((PMODE_DISCONNECT_PAGE) ModeSenseBuffer)->PageLength + 3117 RTL_SIZEOF_THROUGH_FIELD(MODE_DISCONNECT_PAGE, PageLength); 3118 } 3119 } 3120 3121 return result; 3122 } // end ModeSenseFindSpecificPage() 3123 3124 3125 _IRQL_requires_max_(PASSIVE_LEVEL) 3126 NTSTATUS 3127 PerformEjectionControl( 3128 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, 3129 _In_ WDFREQUEST Request, 3130 _In_ MEDIA_LOCK_TYPE LockType, 3131 _In_ BOOLEAN Lock 3132 ) 3133 /*++ 3134 3135 Routine Description: 3136 3137 ejection control process 3138 3139 Arguments: 3140 3141 DeviceExtension - device extension 3142 Request - WDF request to be used for communication with the device 3143 LockType - the type of lock 3144 Lock - if TRUE, lock the device; if FALSE, unlock it 3145 3146 Return Value: 3147 3148 NTSTATUS 3149 3150 --*/ 3151 { 3152 NTSTATUS status; 3153 PFILE_OBJECT_CONTEXT fileObjectContext = NULL; 3154 SCSI_REQUEST_BLOCK srb; 3155 PCDB cdb = NULL; 3156 3157 LONG newLockCount = 0; 3158 LONG newProtectedLockCount = 0; 3159 LONG newInternalLockCount = 0; 3160 LONG newFileLockCount = 0; 3161 BOOLEAN countChanged = FALSE; 3162 BOOLEAN previouslyLocked = FALSE; 3163 BOOLEAN nowLocked = FALSE; 3164 3165 PAGED_CODE(); 3166 3167 // Prevent race conditions while working with lock counts 3168 status = WdfWaitLockAcquire(DeviceExtension->EjectSynchronizationLock, NULL); 3169 if (!NT_SUCCESS(status)) 3170 { 3171 NT_ASSERT(FALSE); 3172 } 3173 3174 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, 3175 "PerformEjectionControl: " 3176 "Received request for %s lock type\n", 3177 LockTypeStrings[LockType] 3178 )); 3179 3180 3181 // If this is a "secured" request, retrieve the file object context 3182 if (LockType == SecureMediaLock) 3183 { 3184 WDFFILEOBJECT fileObject = NULL; 3185 3186 fileObject = WdfRequestGetFileObject(Request); 3187 3188 if (fileObject == NULL) 3189 { 3190 status = STATUS_INVALID_HANDLE; 3191 3192 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, 3193 "FileObject does not match to the one in IRP_MJ_CREATE, KMDF returns NULL\n")); 3194 goto Exit; 3195 } 3196 3197 fileObjectContext = FileObjectGetContext(fileObject); 3198 NT_ASSERT(fileObjectContext != NULL); 3199 } 3200 3201 // Lock counts should never fall below 0 3202 NT_ASSERT(DeviceExtension->LockCount >= 0); 3203 NT_ASSERT(DeviceExtension->ProtectedLockCount >= 0); 3204 NT_ASSERT(DeviceExtension->InternalLockCount >= 0); 3205 3206 // Get the current lock counts 3207 newLockCount = DeviceExtension->LockCount; 3208 newProtectedLockCount = DeviceExtension->ProtectedLockCount; 3209 newInternalLockCount = DeviceExtension->InternalLockCount; 3210 if (fileObjectContext) 3211 { 3212 // fileObjectContext->LockCount is ULONG and should always >= 0 3213 newFileLockCount = fileObjectContext->LockCount; 3214 } 3215 3216 // Determine which lock counts need to be changed and how 3217 if (Lock && LockType == SimpleMediaLock) 3218 { 3219 newLockCount++; 3220 countChanged = TRUE; 3221 } 3222 else if (Lock && LockType == SecureMediaLock) 3223 { 3224 newFileLockCount++; 3225 newProtectedLockCount++; 3226 countChanged = TRUE; 3227 } 3228 else if (Lock && LockType == InternalMediaLock) 3229 { 3230 newInternalLockCount++; 3231 countChanged = TRUE; 3232 } 3233 else if (!Lock && LockType == SimpleMediaLock) 3234 { 3235 if (newLockCount != 0) 3236 { 3237 newLockCount--; 3238 countChanged = TRUE; 3239 } 3240 } 3241 else if (!Lock && LockType == SecureMediaLock) 3242 { 3243 if ( (newFileLockCount == 0) || (newProtectedLockCount == 0) ) 3244 { 3245 status = STATUS_INVALID_DEVICE_STATE; 3246 goto Exit; 3247 } 3248 newFileLockCount--; 3249 newProtectedLockCount--; 3250 countChanged = TRUE; 3251 } 3252 else if (!Lock && LockType == InternalMediaLock) 3253 { 3254 NT_ASSERT(newInternalLockCount != 0); 3255 newInternalLockCount--; 3256 countChanged = TRUE; 3257 } 3258 3259 if ( (DeviceExtension->LockCount != 0) || 3260 (DeviceExtension->ProtectedLockCount != 0) || 3261 (DeviceExtension->InternalLockCount != 0) ) 3262 { 3263 previouslyLocked = TRUE; 3264 } 3265 if ( (newLockCount != 0) || 3266 (newProtectedLockCount != 0) || 3267 (newInternalLockCount != 0) ) 3268 { 3269 nowLocked = TRUE; 3270 } 3271 3272 // Only send command down to device when necessary 3273 if (previouslyLocked != nowLocked) 3274 { 3275 // Compose and send the PREVENT ALLOW MEDIA REMOVAL command. 3276 RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK)); 3277 3278 srb.CdbLength = 6; 3279 srb.TimeOutValue = DeviceExtension->TimeOutValue; 3280 3281 cdb = (PCDB)&srb.Cdb; 3282 cdb->MEDIA_REMOVAL.OperationCode = SCSIOP_MEDIUM_REMOVAL; 3283 cdb->MEDIA_REMOVAL.Prevent = Lock; 3284 3285 status = DeviceSendSrbSynchronously(DeviceExtension->Device, 3286 &srb, 3287 NULL, 3288 0, 3289 FALSE, 3290 Request); 3291 } 3292 3293 Exit: 3294 3295 // Store the updated lock counts on success 3296 if (countChanged && NT_SUCCESS(status)) 3297 { 3298 DeviceExtension->LockCount = newLockCount; 3299 DeviceExtension->ProtectedLockCount = newProtectedLockCount; 3300 DeviceExtension->InternalLockCount = newInternalLockCount; 3301 if (fileObjectContext) 3302 { 3303 fileObjectContext->LockCount = newFileLockCount; 3304 } 3305 } 3306 3307 WdfWaitLockRelease(DeviceExtension->EjectSynchronizationLock); 3308 3309 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, 3310 "PerformEjectionControl: %!STATUS!, " 3311 "Count Changed: %d, Command Sent: %d, " 3312 "Current Counts: Internal: %x Secure: %x Simple: %x\n", 3313 status, 3314 countChanged, 3315 previouslyLocked != nowLocked, 3316 DeviceExtension->InternalLockCount, 3317 DeviceExtension->ProtectedLockCount, 3318 DeviceExtension->LockCount 3319 )); 3320 3321 return status; 3322 } 3323 3324 3325 _IRQL_requires_max_(APC_LEVEL) 3326 NTSTATUS 3327 DeviceUnlockExclusive( 3328 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, 3329 _In_ WDFFILEOBJECT FileObject, 3330 _In_ BOOLEAN IgnorePreviousMediaChanges 3331 ) 3332 /*++ 3333 3334 Routine Description: 3335 3336 to unlock the exclusive lock 3337 3338 Arguments: 3339 3340 DeviceExtension - device context 3341 FileObject - file object that currently holds the lock 3342 IgnorePreviousMediaChanges - if TRUE, ignore previously accumulated media changes 3343 3344 Return Value: 3345 3346 NTSTATUS 3347 3348 --*/ 3349 { 3350 NTSTATUS status = STATUS_SUCCESS; 3351 PCDROM_DATA cdData = &DeviceExtension->DeviceAdditionalData; 3352 PMEDIA_CHANGE_DETECTION_INFO info = DeviceExtension->MediaChangeDetectionInfo; 3353 BOOLEAN ANPending = 0; 3354 LONG requestInUse = 0; 3355 3356 PAGED_CODE(); 3357 3358 if (!EXCLUSIVE_MODE(cdData)) 3359 { 3360 // Device is not locked for exclusive access. 3361 // Can not process unlock request. 3362 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, 3363 "RequestHandleExclusiveAccessUnlockDevice: Device not locked for exclusive access, can't unlock device.\n")); 3364 status = STATUS_INVALID_DEVICE_REQUEST; 3365 } 3366 else if (!EXCLUSIVE_OWNER(cdData, FileObject)) 3367 { 3368 // Request not from the exclusive owner, can't unlock the device. 3369 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, 3370 "RequestHandleExclusiveAccessUnlockDevice: Unable to unlock device, invalid file object\n")); 3371 3372 status = STATUS_INVALID_HANDLE; 3373 } 3374 3375 if (NT_SUCCESS(status)) 3376 { 3377 // Unless we were explicitly requested not to do so, generate a media removal notification 3378 // followed by a media arrival notification similar to volume lock/unlock file system events. 3379 if (!IgnorePreviousMediaChanges) 3380 { 3381 MEDIA_CHANGE_DETECTION_STATE previousMediaState = MediaUnknown; 3382 3383 // Change the media state to "unavailable", which will cause a removal notification if the media 3384 // was previously present. At the same time, store the previous state in previousMediaState. 3385 DeviceSetMediaChangeStateEx(DeviceExtension, MediaUnavailable, &previousMediaState); 3386 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, 3387 "DeviceUnlockExclusive: Changing the media state to MediaUnavailable\n")); 3388 3389 // Restore the previous media state, which will cause a media arrival notification if the media 3390 // was originally present. 3391 DeviceSetMediaChangeStateEx(DeviceExtension, previousMediaState, NULL); 3392 } 3393 3394 // Set DO_VERIFY_VOLUME so that the file system will remount on it. 3395 if (IsVolumeMounted(DeviceExtension->DeviceObject)) 3396 { 3397 SET_FLAG(DeviceExtension->DeviceObject->Flags, DO_VERIFY_VOLUME); 3398 } 3399 3400 // Set MMC state to update required 3401 cdData->Mmc.WriteAllowed = FALSE; 3402 cdData->Mmc.UpdateState = CdromMmcUpdateRequired; 3403 3404 // Send unlock notification 3405 DeviceSendNotification(DeviceExtension, 3406 &GUID_IO_CDROM_EXCLUSIVE_UNLOCK, 3407 0, 3408 NULL); 3409 3410 InterlockedExchangePointer((PVOID)&cdData->ExclusiveOwner, NULL); 3411 3412 if ((info != NULL) && (info->AsynchronousNotificationSupported != FALSE)) 3413 { 3414 ANPending = info->ANSignalPendingDueToExclusiveLock; 3415 info->ANSignalPendingDueToExclusiveLock = FALSE; 3416 3417 if ((ANPending != FALSE) && (info->MediaChangeDetectionDisableCount == 0)) 3418 { 3419 // if the request is not in use, mark it as such. 3420 requestInUse = InterlockedCompareExchange((PLONG)&info->MediaChangeRequestInUse, 1, 0); 3421 3422 if (requestInUse == 0) 3423 { 3424 // The last MCN finished. ok to issue the new one. 3425 RequestSetupMcnSyncIrp(DeviceExtension); 3426 3427 // The irp will go into KMDF framework and a request will be created there to represent it. 3428 IoCallDriver(DeviceExtension->DeviceObject, info->MediaChangeSyncIrp); 3429 } 3430 } 3431 } 3432 } 3433 3434 return status; 3435 } 3436 3437 3438 VOID 3439 RequestCompletion( 3440 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, 3441 _In_ WDFREQUEST Request, 3442 _In_ NTSTATUS Status, 3443 _In_ ULONG_PTR Information 3444 ) 3445 { 3446 #ifdef DBG 3447 ULONG ioctlCode = 0; 3448 WDF_REQUEST_PARAMETERS requestParameters; 3449 3450 // Get the Request parameters 3451 WDF_REQUEST_PARAMETERS_INIT(&requestParameters); 3452 WdfRequestGetParameters(Request, &requestParameters); 3453 3454 if (requestParameters.Type == WdfRequestTypeDeviceControl) 3455 { 3456 ioctlCode = requestParameters.Parameters.DeviceIoControl.IoControlCode; 3457 3458 if (requestParameters.Parameters.DeviceIoControl.IoControlCode != IOCTL_MCN_SYNC_FAKE_IOCTL) 3459 { 3460 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, 3461 "Request complete - IOCTL - code: %X; Status: %X; Information: %X\n", 3462 ioctlCode, 3463 Status, 3464 (ULONG)Information)); 3465 } 3466 else 3467 { 3468 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_GENERAL, 3469 "Request complete - IOCTL - code: %X; Status: %X; Information: %X\n", 3470 ioctlCode, 3471 Status, 3472 (ULONG)Information)); 3473 } 3474 } 3475 else if (requestParameters.Type == WdfRequestTypeRead) 3476 { 3477 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, 3478 "Request complete - READ - Starting Offset: %X; Length: %X; Transferred Length: %X; Status: %X\n", 3479 (ULONG)requestParameters.Parameters.Read.DeviceOffset, 3480 (ULONG)requestParameters.Parameters.Read.Length, 3481 (ULONG)Information, 3482 Status)); 3483 } 3484 else if (requestParameters.Type == WdfRequestTypeWrite) 3485 { 3486 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, 3487 "Request complete - WRITE - Starting Offset: %X; Length: %X; Transferred Length: %X; Status: %X\n", 3488 (ULONG)requestParameters.Parameters.Write.DeviceOffset, 3489 (ULONG)requestParameters.Parameters.Write.Length, 3490 (ULONG)Information, 3491 Status)); 3492 } 3493 #endif 3494 3495 if (IoIsErrorUserInduced(Status)) 3496 { 3497 PIRP irp = WdfRequestWdmGetIrp(Request); 3498 if (irp->Tail.Overlay.Thread) 3499 { 3500 IoSetHardErrorOrVerifyDevice(irp, DeviceExtension->DeviceObject); 3501 } 3502 } 3503 3504 if (!NT_SUCCESS(Status) && DeviceExtension->SurpriseRemoved == TRUE) 3505 { 3506 // IMAPI expects ERROR_DEV_NOT_EXISTS if recorder has been surprised removed, 3507 // or it will retry WRITE commands for up to 3 minutes 3508 // CDROM behavior should be consistent for all requests, including SCSI pass-through 3509 Status = STATUS_DEVICE_DOES_NOT_EXIST; 3510 } 3511 3512 WdfRequestCompleteWithInformation(Request, Status, Information); 3513 3514 return; 3515 } 3516 3517 3518 VOID 3519 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ 3520 RequestDummyCompletionRoutine( 3521 _In_ WDFREQUEST Request, 3522 _In_ WDFIOTARGET Target, 3523 _In_ PWDF_REQUEST_COMPLETION_PARAMS Params, 3524 _In_ WDFCONTEXT Context 3525 ) 3526 /*++ 3527 3528 Routine Description: 3529 3530 This is a dummy competion routine that simply calls WdfRequestComplete. We have to use 3531 this dummy competion routine instead of WDF_REQUEST_SEND_OPTION_SEND_AND_FORGET, because 3532 the latter causes the framework to not check if the I/O target is closed or not. 3533 3534 Arguments: 3535 3536 Request - completed request 3537 Target - the I/O target that completed the request 3538 Params - request parameters 3539 Context - not used 3540 3541 Return Value: 3542 3543 none 3544 3545 --*/ 3546 { 3547 UNREFERENCED_PARAMETER(Target); 3548 UNREFERENCED_PARAMETER(Params); 3549 UNREFERENCED_PARAMETER(Context); 3550 3551 WdfRequestCompleteWithInformation(Request, 3552 WdfRequestGetStatus(Request), 3553 WdfRequestGetInformation(Request)); 3554 } 3555 3556 3557 _IRQL_requires_max_(DISPATCH_LEVEL) 3558 NTSTATUS 3559 DeviceSendPowerDownProcessRequest( 3560 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, 3561 _In_opt_ PFN_WDF_REQUEST_COMPLETION_ROUTINE CompletionRoutine, 3562 _In_opt_ PVOID Context 3563 ) 3564 /*++ 3565 3566 Routine Description: 3567 3568 This function is called during processing power down request. 3569 It is used to send either SYNC CACHE command or STOP UNIT command. 3570 3571 Caller should set proper value in deviceExtension->PowerContext.PowerChangeState.PowerDown 3572 to trigger the correct command be sent. 3573 3574 Arguments: 3575 3576 DeviceExtension - 3577 3578 CompletionRoutine - Completion routine that needs to be set for the request 3579 3580 Context - Completion context associated with the completion routine 3581 3582 Return Value: 3583 3584 NTSTATUS 3585 3586 --*/ 3587 { 3588 NTSTATUS status; 3589 BOOLEAN requestSent = FALSE; 3590 3591 BOOLEAN shouldRetry = TRUE; 3592 PCDB cdb = (PCDB)DeviceExtension->PowerContext.Srb.Cdb; 3593 ULONG timeoutValue = DeviceExtension->TimeOutValue; 3594 ULONG retryCount = 1; 3595 3596 // reset some fields. 3597 DeviceExtension->PowerContext.RetryIntervalIn100ns = 0; 3598 status = PowerContextReuseRequest(DeviceExtension); 3599 RequestClearSendTime(DeviceExtension->PowerContext.PowerRequest); 3600 3601 if (!NT_SUCCESS(status)) 3602 { 3603 return status; 3604 } 3605 3606 // set proper timeout value and max retry count. 3607 switch(DeviceExtension->PowerContext.PowerChangeState.PowerDown) 3608 { 3609 case PowerDownDeviceInitial: 3610 case PowerDownDeviceQuiesced: 3611 case PowerDownDeviceStopped: 3612 break; 3613 3614 case PowerDownDeviceLocked: 3615 // Case of issuing SYNC CACHE command. Do not use power irp timeout remaining time in this case 3616 // as we want to give best try on SYNC CACHE command. 3617 retryCount = MAXIMUM_RETRIES; 3618 timeoutValue = DeviceExtension->TimeOutValue; 3619 break; 3620 3621 case PowerDownDeviceFlushed: 3622 { 3623 // Case of issuing STOP UNIT command 3624 // As "Imme" bit is set to '1', this command should be completed in short time. 3625 // This command is at low importance, failure of this command has very small impact. 3626 ULONG secondsRemaining = 0; 3627 3628 #if (WINVER >= 0x0601) 3629 // this API is introduced in Windows7 3630 PoQueryWatchdogTime(DeviceExtension->LowerPdo, &secondsRemaining); 3631 #endif 3632 3633 if (secondsRemaining == 0) 3634 { 3635 // not able to retrieve remaining time from PoQueryWatchdogTime API, use default values. 3636 retryCount = MAXIMUM_RETRIES; 3637 timeoutValue = SCSI_CDROM_TIMEOUT; 3638 } 3639 else 3640 { 3641 // plan to leave about 30 seconds to lower level drivers if possible. 3642 if (secondsRemaining >= 32) 3643 { 3644 retryCount = (secondsRemaining - 30)/SCSI_CDROM_TIMEOUT + 1; 3645 timeoutValue = SCSI_CDROM_TIMEOUT; 3646 3647 if (retryCount > MAXIMUM_RETRIES) 3648 { 3649 retryCount = MAXIMUM_RETRIES; 3650 } 3651 3652 if (retryCount == 1) 3653 { 3654 timeoutValue = secondsRemaining - 30; 3655 } 3656 } 3657 else 3658 { 3659 // issue the command with minimal timeout value and do not retry on it. 3660 retryCount = 1; 3661 timeoutValue = 2; 3662 } 3663 } 3664 } 3665 break; 3666 default: 3667 NT_ASSERT( FALSE ); 3668 status = STATUS_NOT_IMPLEMENTED; 3669 return status; 3670 } 3671 3672 DeviceExtension->PowerContext.RetryCount = retryCount; 3673 3674 // issue command. 3675 while (shouldRetry) 3676 { 3677 3678 // set SRB fields. 3679 DeviceExtension->PowerContext.Srb.SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER | 3680 SRB_FLAGS_DISABLE_SYNCH_TRANSFER | 3681 SRB_FLAGS_NO_QUEUE_FREEZE | 3682 SRB_FLAGS_BYPASS_LOCKED_QUEUE | 3683 SRB_FLAGS_D3_PROCESSING; 3684 3685 DeviceExtension->PowerContext.Srb.Function = SRB_FUNCTION_EXECUTE_SCSI; 3686 DeviceExtension->PowerContext.Srb.TimeOutValue = timeoutValue; 3687 3688 if (DeviceExtension->PowerContext.PowerChangeState.PowerDown == PowerDownDeviceInitial) 3689 { 3690 DeviceExtension->PowerContext.Srb.Function = SRB_FUNCTION_LOCK_QUEUE; 3691 } 3692 else if (DeviceExtension->PowerContext.PowerChangeState.PowerDown == PowerDownDeviceLocked) 3693 { 3694 DeviceExtension->PowerContext.Srb.Function = SRB_FUNCTION_QUIESCE_DEVICE; 3695 } 3696 else if (DeviceExtension->PowerContext.PowerChangeState.PowerDown == PowerDownDeviceQuiesced) 3697 { 3698 // Case of issuing SYNC CACHE command. 3699 DeviceExtension->PowerContext.Srb.CdbLength = 10; 3700 cdb->SYNCHRONIZE_CACHE10.OperationCode = SCSIOP_SYNCHRONIZE_CACHE; 3701 } 3702 else if (DeviceExtension->PowerContext.PowerChangeState.PowerDown == PowerDownDeviceFlushed) 3703 { 3704 // Case of issuing STOP UNIT command. 3705 DeviceExtension->PowerContext.Srb.CdbLength = 6; 3706 cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT; 3707 cdb->START_STOP.Start = 0; 3708 cdb->START_STOP.Immediate = 1; 3709 } 3710 else if (DeviceExtension->PowerContext.PowerChangeState.PowerDown == PowerDownDeviceStopped) 3711 { 3712 DeviceExtension->PowerContext.Srb.Function = SRB_FUNCTION_UNLOCK_QUEUE; 3713 } 3714 3715 // Set up completion routine and context if requested 3716 if (CompletionRoutine) 3717 { 3718 WdfRequestSetCompletionRoutine(DeviceExtension->PowerContext.PowerRequest, 3719 CompletionRoutine, 3720 Context); 3721 } 3722 3723 status = RequestSend(DeviceExtension, 3724 DeviceExtension->PowerContext.PowerRequest, 3725 DeviceExtension->IoTarget, 3726 CompletionRoutine ? 0 : WDF_REQUEST_SEND_OPTION_SYNCHRONOUS, 3727 &requestSent); 3728 3729 if (requestSent) 3730 { 3731 if ((CompletionRoutine == NULL) && 3732 (SRB_STATUS(DeviceExtension->PowerContext.Srb.SrbStatus) != SRB_STATUS_SUCCESS)) 3733 { 3734 TracePrint((TRACE_LEVEL_ERROR, 3735 TRACE_FLAG_POWER, 3736 "%p\tError occured when issuing %s command to device. Srb %p, Status %x\n", 3737 DeviceExtension->PowerContext.PowerRequest, 3738 (DeviceExtension->PowerContext.PowerChangeState.PowerDown == PowerDownDeviceQuiesced) ? "SYNC CACHE" : "STOP UNIT", 3739 &DeviceExtension->PowerContext.Srb, 3740 DeviceExtension->PowerContext.Srb.SrbStatus)); 3741 3742 NT_ASSERT(!(TEST_FLAG(DeviceExtension->PowerContext.Srb.SrbStatus, SRB_STATUS_QUEUE_FROZEN))); 3743 3744 shouldRetry = RequestSenseInfoInterpret(DeviceExtension, 3745 DeviceExtension->PowerContext.PowerRequest, 3746 &(DeviceExtension->PowerContext.Srb), 3747 retryCount - DeviceExtension->PowerContext.RetryCount, 3748 &status, 3749 &(DeviceExtension->PowerContext.RetryIntervalIn100ns)); 3750 3751 if (shouldRetry && (DeviceExtension->PowerContext.RetryCount-- == 0)) 3752 { 3753 shouldRetry = FALSE; 3754 } 3755 } 3756 else 3757 { 3758 // succeeded, do not need to retry. 3759 shouldRetry = FALSE; 3760 } 3761 3762 } 3763 else 3764 { 3765 // request failed to be sent 3766 shouldRetry = FALSE; 3767 } 3768 3769 if (shouldRetry) 3770 { 3771 LARGE_INTEGER t; 3772 t.QuadPart = -DeviceExtension->PowerContext.RetryIntervalIn100ns; 3773 KeDelayExecutionThread(KernelMode, FALSE, &t); 3774 3775 status = PowerContextReuseRequest(DeviceExtension); 3776 if (!NT_SUCCESS(status)) 3777 { 3778 shouldRetry = FALSE; 3779 } 3780 } 3781 } 3782 3783 if (DeviceExtension->PowerContext.PowerChangeState.PowerDown == PowerDownDeviceQuiesced) 3784 { 3785 // record SYNC CACHE command completion time stamp. 3786 KeQueryTickCount(&DeviceExtension->PowerContext.Step1CompleteTime); 3787 } 3788 3789 return status; 3790 } 3791 3792 NTSTATUS 3793 RequestSend( 3794 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, 3795 _In_ WDFREQUEST Request, 3796 _In_ WDFIOTARGET IoTarget, 3797 _In_ ULONG Flags, 3798 _Out_opt_ PBOOLEAN RequestSent 3799 ) 3800 /*++ 3801 3802 Routine Description: 3803 3804 Send the request to the target, wake up the device from Zero Power state if necessary. 3805 3806 Arguments: 3807 3808 DeviceExtension - device extension 3809 Request - the request to be sent 3810 IoTarget - target of the above request 3811 Flags - flags for the operation 3812 RequestSent - optional, if the request was sent 3813 3814 Return Value: 3815 3816 NTSTATUS 3817 3818 --*/ 3819 { 3820 NTSTATUS status = STATUS_SUCCESS; 3821 BOOLEAN requestSent = FALSE; 3822 WDF_REQUEST_SEND_OPTIONS options; 3823 3824 UNREFERENCED_PARAMETER(DeviceExtension); 3825 3826 if ((DeviceExtension->ZeroPowerODDInfo != NULL) && 3827 (DeviceExtension->ZeroPowerODDInfo->InZeroPowerState != FALSE)) 3828 { 3829 } 3830 3831 // Now send down the request 3832 if (NT_SUCCESS(status)) 3833 { 3834 WDF_REQUEST_SEND_OPTIONS_INIT(&options, Flags); 3835 3836 RequestSetSentTime(Request); 3837 3838 // send request and check status 3839 3840 // Disable SDV warning about infinitely waiting in caller's context: 3841 // 1. Some requests (such as SCSI_PASS_THROUGH, contains buffer from user space) need to be sent down in caller�s context. 3842 // Consequently, these requests wait in caller�s context until they are allowed to be sent down. 3843 // 2. Considering the situation that during sleep, a request can be hold by storage port driver. When system resumes, any time out value (if we set using KMDF time out value) might be expires. 3844 // This will cause the waiting request being failed (behavior change). We�d rather not set time out value. 3845 3846 _Analysis_assume_(options.Timeout != 0); 3847 requestSent = WdfRequestSend(Request, IoTarget, &options); 3848 _Analysis_assume_(options.Timeout == 0); 3849 3850 // If WdfRequestSend fails, or if the WDF_REQUEST_SEND_OPTION_SYNCHRONOUS flag is set, 3851 // the driver can call WdfRequestGetStatus immediately after calling WdfRequestSend. 3852 if ((requestSent == FALSE) || 3853 (Flags & WDF_REQUEST_SEND_OPTION_SYNCHRONOUS)) 3854 { 3855 status = WdfRequestGetStatus(Request); 3856 3857 if (requestSent == FALSE) 3858 { 3859 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, 3860 "WdfRequestSend failed: %lx\n", 3861 status 3862 )); 3863 } 3864 } 3865 else 3866 { 3867 status = STATUS_SUCCESS; 3868 } 3869 3870 if (RequestSent != NULL) 3871 { 3872 *RequestSent = requestSent; 3873 } 3874 } 3875 3876 return status; 3877 } 3878 3879