1 /*-- 2 3 Copyright (C) Microsoft Corporation. All rights reserved. 4 5 Module Name: 6 7 init.c 8 9 Abstract: 10 11 Initialization routines for CDROM 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 #include "devpkey.h" 29 30 #include "cdrom.h" 31 #include "scratch.h" 32 #include "mmc.h" 33 34 #ifdef DEBUG_USE_WPP 35 #include "init.tmh" 36 #endif 37 38 _IRQL_requires_max_(APC_LEVEL) 39 NTSTATUS 40 DeviceInitAllocateBuffers( 41 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension 42 ); 43 44 _IRQL_requires_max_(PASSIVE_LEVEL) 45 NTSTATUS 46 DeviceRetrieveScsiAddress( 47 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, 48 _In_ PSCSI_ADDRESS ScsiAddress 49 ); 50 51 _IRQL_requires_max_(PASSIVE_LEVEL) 52 NTSTATUS 53 DeviceRetrieveDescriptorsAndTransferLength( 54 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension 55 ); 56 57 _IRQL_requires_max_(APC_LEVEL) 58 VOID 59 DeviceScanSpecialDevices( 60 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension 61 ); 62 63 _IRQL_requires_max_(PASSIVE_LEVEL) 64 NTSTATUS 65 DeviceInitMmcContext( 66 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension 67 ); 68 69 _IRQL_requires_max_(PASSIVE_LEVEL) 70 NTSTATUS 71 DeviceGetMmcSupportInfo( 72 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, 73 _Out_ PBOOLEAN IsMmcDevice 74 ); 75 76 #if (NTDDI_VERSION >= NTDDI_WIN8) 77 _IRQL_requires_max_(PASSIVE_LEVEL) 78 NTSTATUS 79 DeviceIsPortable( 80 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, 81 _Out_ PBOOLEAN IsPortable 82 ); 83 #endif 84 85 86 #ifdef ALLOC_PRAGMA 87 88 #pragma alloc_text(PAGE, DeviceClaimRelease) 89 #pragma alloc_text(PAGE, DeviceEvtSelfManagedIoInit) 90 91 #pragma alloc_text(PAGE, DeviceInitReleaseQueueContext) 92 #pragma alloc_text(PAGE, DeviceInitAllocateBuffers) 93 #pragma alloc_text(PAGE, DeviceInitPowerContext) 94 #pragma alloc_text(PAGE, DeviceCreateWellKnownName) 95 #pragma alloc_text(PAGE, DeviceRetrieveScsiAddress) 96 #pragma alloc_text(PAGE, DeviceRetrieveDescriptorsAndTransferLength) 97 #pragma alloc_text(PAGE, DeviceInitializeHotplugInfo) 98 #pragma alloc_text(PAGE, DeviceScanSpecialDevices) 99 #pragma alloc_text(PAGE, DeviceGetTimeOutValueFromRegistry) 100 #pragma alloc_text(PAGE, DeviceGetMmcSupportInfo) 101 #pragma alloc_text(PAGE, DeviceRetrieveDescriptor) 102 #pragma alloc_text(PAGE, DeviceRetrieveHackFlagsFromRegistry) 103 #pragma alloc_text(PAGE, DeviceScanForSpecial) 104 #pragma alloc_text(PAGE, DeviceHackFlagsScan) 105 #pragma alloc_text(PAGE, DeviceInitMmcContext) 106 #pragma alloc_text(PAGE, ScanForSpecialHandler) 107 #pragma alloc_text(PAGE, DeviceSetRawReadInfo) 108 #pragma alloc_text(PAGE, DeviceInitializeDvd) 109 #pragma alloc_text(PAGE, DeviceCacheDeviceInquiryData) 110 111 #if (NTDDI_VERSION >= NTDDI_WIN8) 112 #pragma alloc_text(PAGE, DeviceIsPortable) 113 #endif 114 115 #endif 116 117 #pragma warning(push) 118 #pragma warning(disable:4152) // nonstandard extension, function/data pointer conversion in expression 119 #pragma warning(disable:26000) // read overflow reported because of pointer type conversion 120 121 _IRQL_requires_max_(PASSIVE_LEVEL) 122 NTSTATUS 123 DeviceClaimRelease( 124 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, 125 _In_ BOOLEAN Release 126 ) 127 /*++ 128 129 Routine Description: 130 131 This function claims a device in the port driver. The port driver object 132 is updated with the correct driver object if the device is successfully 133 claimed. 134 135 Arguments: 136 137 Device - The WDFDEVICE that needs to be claimed or released. 138 139 Release - Indicates the logical unit should be released rather than claimed. 140 141 Return Value: 142 143 Returns a status indicating success or failure of the operation. 144 145 --*/ 146 { 147 NTSTATUS status; 148 SCSI_REQUEST_BLOCK srb = {0}; 149 WDF_MEMORY_DESCRIPTOR descriptor; 150 WDFREQUEST request; 151 WDF_OBJECT_ATTRIBUTES attributes; 152 153 PAGED_CODE(); 154 155 //Create a request 156 WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, 157 CDROM_REQUEST_CONTEXT); 158 159 status = WdfRequestCreate(&attributes, 160 DeviceExtension->IoTarget, 161 &request); 162 163 if (NT_SUCCESS(status)) 164 { 165 //fill up srb structure 166 srb.OriginalRequest = WdfRequestWdmGetIrp(request); 167 NT_ASSERT(srb.OriginalRequest != NULL); 168 169 srb.Length = sizeof(SCSI_REQUEST_BLOCK); 170 171 srb.Function = Release 172 ? SRB_FUNCTION_RELEASE_DEVICE 173 : SRB_FUNCTION_CLAIM_DEVICE; 174 175 176 WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&descriptor, 177 &srb, 178 sizeof(srb)); 179 180 status = WdfIoTargetSendInternalIoctlOthersSynchronously(DeviceExtension->IoTarget, 181 request, 182 IOCTL_SCSI_EXECUTE_NONE, 183 &descriptor, 184 NULL, 185 NULL, 186 NULL, 187 NULL); 188 189 NT_ASSERT(!TEST_FLAG(srb.SrbFlags, SRB_FLAGS_FREE_SENSE_BUFFER)); 190 191 // The request should be deleted. 192 WdfObjectDelete(request); 193 194 if (!NT_SUCCESS(status)) 195 { 196 TracePrint((TRACE_LEVEL_FATAL, TRACE_FLAG_PNP, 197 "DeviceClaimRelease: Failed to %s device, status: 0x%X\n", 198 Release ? "Release" : "Claim", 199 status)); 200 } 201 } 202 else 203 { 204 TracePrint((TRACE_LEVEL_FATAL, TRACE_FLAG_PNP, 205 "DeviceClaimRelease: Failed to create request, status: 0x%X\n", 206 status)); 207 } 208 209 if (Release) 210 { 211 // We only release the device when we don't want to manage it. 212 // The failure status does not matter. 213 status = STATUS_SUCCESS; 214 } 215 216 return status; 217 } // end DeviceClaimRelease() 218 219 220 NTSTATUS 221 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ 222 DeviceEvtSelfManagedIoInit( 223 _In_ WDFDEVICE Device 224 ) 225 /*++ 226 227 Routine Description: 228 229 This routine is called only once after the device is added in system, so it's used to do 230 hardware-dependent device initialization work and resource allocation. 231 If this routine fails, DeviceEvtSelfManagedIoCleanup will be invoked by the framework. 232 233 Arguments: 234 235 Device - Handle to device object 236 237 Return Value: 238 239 NTSTATUS 240 241 --*/ 242 { 243 NTSTATUS status = STATUS_SUCCESS; 244 PCDROM_DEVICE_EXTENSION deviceExtension = NULL; 245 246 PAGED_CODE(); 247 248 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, 249 "DeviceEvtSelfManagedIoInit: WDFDEVICE %p is being started.\n", 250 Device)); 251 252 deviceExtension = DeviceGetExtension(Device); 253 254 // 1. Set/retrieve basic information, some of the following operations may depend on it 255 if (NT_SUCCESS(status)) 256 { 257 // We do not care if this function fails, SCSI address is mainly for debugging/tracing purposes. 258 (VOID) DeviceRetrieveScsiAddress(deviceExtension, &deviceExtension->ScsiAddress); 259 } 260 261 if (NT_SUCCESS(status)) 262 { 263 status = DeviceRetrieveDescriptorsAndTransferLength(deviceExtension); 264 } 265 266 if (NT_SUCCESS(status)) 267 { 268 // This function should be called after DeviceRetrieveDescriptorsAndTransferLength() 269 // It depends on MaxTransferLenth fields. 270 status = DeviceInitAllocateBuffers(deviceExtension); 271 } 272 273 // 2. The following functions depend on the allocated buffers. 274 275 // perf re-enable after failing. Q: Is this one used by cdrom.sys? 276 if (NT_SUCCESS(status)) 277 { 278 // allow perf to be re-enabled after a given number of failed IOs 279 // require this number to be at least CLASS_PERF_RESTORE_MINIMUM 280 ULONG t = CLASS_PERF_RESTORE_MINIMUM; 281 282 DeviceGetParameter(deviceExtension, 283 CLASSP_REG_SUBKEY_NAME, 284 CLASSP_REG_PERF_RESTORE_VALUE_NAME, 285 &t); 286 if (t >= CLASS_PERF_RESTORE_MINIMUM) 287 { 288 deviceExtension->PrivateFdoData->Perf.ReEnableThreshhold = t; 289 } 290 } 291 292 // 3. Retrieve information about special devices and hack flags. 293 if (NT_SUCCESS(status)) 294 { 295 DeviceRetrieveHackFlagsFromRegistry(deviceExtension); 296 // scan for bad items. 297 DeviceScanForSpecial(deviceExtension, CdRomBadItems, DeviceHackFlagsScan); 298 // Check to see if it's a special device that needs special error process. 299 DeviceScanSpecialDevices(deviceExtension); // may send command to device 300 } 301 302 // 4. Initialize the hotplug information only after the ScanForSpecial routines, 303 // as it relies upon the hack flags - deviceExtension->PrivateFdoData->HackFlags. 304 if (NT_SUCCESS(status)) 305 { 306 status = DeviceInitializeHotplugInfo(deviceExtension); 307 } 308 309 if (NT_SUCCESS(status)) 310 { 311 // cache the device's inquiry data 312 status = DeviceCacheDeviceInquiryData(deviceExtension); 313 if (!NT_SUCCESS(status)) 314 { 315 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT, 316 "Failed to cache the device's inquiry data, failng %!STATUS!\n", 317 status 318 )); 319 } 320 } 321 322 // 5. Initialize MMC context, media change notification stuff and read media capacity 323 if (NT_SUCCESS(status)) 324 { 325 status = DeviceInitializeMediaChangeDetection(deviceExtension); 326 } 327 if (NT_SUCCESS(status)) 328 { 329 status = DeviceInitMmcContext(deviceExtension); 330 } 331 if (NT_SUCCESS(status)) 332 { 333 status = DeviceInitializeZPODD(deviceExtension); 334 } 335 if (NT_SUCCESS(status)) 336 { 337 // Do READ CAPACITY. This SCSI command returns the last sector address 338 // on the device and the bytes per sector. These are used to calculate 339 // the drive capacity in bytes. 340 status = MediaReadCapacity(Device); 341 342 // If READ CAPACITY succeeded, we can safely conclude that there is a media present 343 if (NT_SUCCESS(status)) 344 { 345 DeviceSetMediaChangeStateEx(deviceExtension, 346 MediaPresent, 347 NULL); 348 } 349 350 // READ CAPACITY is not critical for init, ignore all errors occuring during its execution 351 status = STATUS_SUCCESS; 352 } 353 354 // 6. Perform DVD-specific initialization 355 if (NT_SUCCESS(status)) 356 { 357 status = DeviceInitializeDvd(Device); 358 } 359 360 // 7. Miscellaneous initialization actions 361 if (NT_SUCCESS(status)) 362 { 363 if (deviceExtension->PrivateFdoData != NULL) 364 { 365 deviceExtension->PrivateFdoData->Perf.OriginalSrbFlags = deviceExtension->SrbFlags; 366 } 367 368 if (deviceExtension->DeviceAdditionalData.Mmc.IsWriter) 369 { 370 // OPC can really take this long per IMAPIv1 timeout.... 371 deviceExtension->TimeOutValue = max(deviceExtension->TimeOutValue, SCSI_CDROM_OPC_TIMEOUT); 372 } 373 } 374 375 // 8. Enable the main timer, create ARC name as needed 376 if (NT_SUCCESS(status)) 377 { 378 // Device successfully added and initialized, increase CdRomCount. 379 IoGetConfigurationInformation()->CdRomCount++; 380 381 deviceExtension->IsInitialized = TRUE; 382 383 DeviceEnableMainTimer(deviceExtension); 384 385 } 386 387 #if (NTDDI_VERSION >= NTDDI_WIN8) 388 // 9. Set volume interface properties 389 if (NT_SUCCESS(status)) 390 { 391 BOOLEAN isCritical = FALSE; 392 BOOLEAN isPortable = FALSE; 393 BOOLEAN isRemovable = TEST_FLAG(deviceExtension->DeviceObject->Characteristics, FILE_REMOVABLE_MEDIA); 394 DEVPROP_BOOLEAN propCritical = DEVPROP_FALSE; 395 DEVPROP_BOOLEAN propPortable = DEVPROP_FALSE; 396 DEVPROP_BOOLEAN propRemovable = DEVPROP_FALSE; 397 398 status = DeviceIsPortable(deviceExtension, &isPortable); 399 400 if (NT_SUCCESS(status)) 401 { 402 if (isPortable) { 403 SET_FLAG(deviceExtension->DeviceObject->Characteristics, FILE_PORTABLE_DEVICE); 404 } 405 406 propPortable = isPortable ? DEVPROP_TRUE : DEVPROP_FALSE; 407 408 status = IoSetDeviceInterfacePropertyData(&deviceExtension->MountedDeviceInterfaceName, 409 &DEVPKEY_Storage_Portable, 410 0, 411 0, 412 DEVPROP_TYPE_BOOLEAN, 413 sizeof(DEVPROP_BOOLEAN), 414 &propPortable); 415 } 416 417 if (NT_SUCCESS(status)) 418 { 419 propRemovable = isRemovable ? DEVPROP_TRUE : DEVPROP_FALSE; 420 421 status = IoSetDeviceInterfacePropertyData(&deviceExtension->MountedDeviceInterfaceName, 422 &DEVPKEY_Storage_Removable_Media, 423 0, 424 0, 425 DEVPROP_TYPE_BOOLEAN, 426 sizeof(DEVPROP_BOOLEAN), 427 &propRemovable); 428 } 429 430 if (NT_SUCCESS(status)) 431 { 432 isCritical = TEST_FLAG(deviceExtension->DeviceObject->Flags, 433 (DO_SYSTEM_SYSTEM_PARTITION | 434 DO_SYSTEM_BOOT_PARTITION | 435 DO_SYSTEM_CRITICAL_PARTITION)); 436 437 propCritical = isCritical ? DEVPROP_TRUE : DEVPROP_FALSE; 438 439 status = IoSetDeviceInterfacePropertyData(&deviceExtension->MountedDeviceInterfaceName, 440 &DEVPKEY_Storage_System_Critical, 441 0, 442 0, 443 DEVPROP_TYPE_BOOLEAN, 444 sizeof(DEVPROP_BOOLEAN), 445 &propCritical); 446 } 447 448 } 449 #endif 450 451 return status; 452 } 453 454 455 _IRQL_requires_max_(APC_LEVEL) 456 NTSTATUS 457 DeviceInitReleaseQueueContext( 458 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension 459 ) 460 /*++ 461 462 Routine Description: 463 464 Part of device initialize routine. Initialize ReleaseQueue related stuff. 465 466 Arguments: 467 468 DeviceExtension - device extension of WDFDEVICE. 469 470 Return Value: 471 472 NTSTATUS 473 474 --*/ 475 { 476 NTSTATUS status = STATUS_SUCCESS; 477 WDF_OBJECT_ATTRIBUTES attributes; 478 479 PAGED_CODE(); 480 481 WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, 482 CDROM_REQUEST_CONTEXT); 483 attributes.ParentObject = DeviceExtension->Device; 484 485 status = WdfRequestCreate(&attributes, 486 DeviceExtension->IoTarget, 487 &(DeviceExtension->ReleaseQueueRequest)); 488 489 if (!NT_SUCCESS(status)) 490 { 491 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP, "Cannot create the release queue request\n")); 492 493 return status; 494 } 495 496 // Initialize ReleaseQueueInputMemory, a wrapper around ReleaseQueueSrb 497 WDF_OBJECT_ATTRIBUTES_INIT(&attributes); 498 attributes.ParentObject = DeviceExtension->ReleaseQueueRequest; 499 500 status = WdfMemoryCreatePreallocated(&attributes, 501 &DeviceExtension->ReleaseQueueSrb, 502 sizeof(SCSI_REQUEST_BLOCK), 503 &DeviceExtension->ReleaseQueueInputMemory); 504 if (!NT_SUCCESS(status)) 505 { 506 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP, "Failed to allocate ReleaseQueueSrb.\n")); 507 508 return status; 509 } 510 511 // Preformat the release queue request here to ensure that this call will never 512 // fail during an actual release of the queue. 513 if (NT_SUCCESS(status)) 514 { 515 status = WdfIoTargetFormatRequestForInternalIoctlOthers(DeviceExtension->IoTarget, 516 DeviceExtension->ReleaseQueueRequest, 517 IOCTL_SCSI_EXECUTE_NONE, 518 DeviceExtension->ReleaseQueueInputMemory, 519 NULL, 520 NULL, 521 NULL, 522 NULL, 523 NULL); 524 } 525 526 // Set a CompletionRoutine callback function for ReleaseQueueRequest. 527 if (NT_SUCCESS(status)) 528 { 529 WdfRequestSetCompletionRoutine(DeviceExtension->ReleaseQueueRequest, 530 DeviceReleaseQueueCompletion, 531 DeviceExtension->Device); 532 } 533 534 // Create a spinlock for ReleaseQueueRequest 535 WDF_OBJECT_ATTRIBUTES_INIT(&attributes); 536 attributes.ParentObject = DeviceExtension->Device; 537 538 status = WdfSpinLockCreate(&attributes, 539 &(DeviceExtension->ReleaseQueueSpinLock)); 540 541 if (!NT_SUCCESS(status)) 542 { 543 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP, 544 "DeviceInitReleaseQueueContext: Cannot create the release queue spinlock\n")); 545 546 return status; 547 } 548 549 // Initialize miscellaneous ReleaseQueue related fields 550 DeviceExtension->ReleaseQueueNeeded = FALSE; 551 DeviceExtension->ReleaseQueueInProgress = FALSE; 552 DeviceExtension->ReleaseQueueSrb.Length = sizeof(SCSI_REQUEST_BLOCK); 553 554 return status; 555 } 556 557 _IRQL_requires_max_(APC_LEVEL) 558 NTSTATUS 559 DeviceInitPowerContext( 560 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension 561 ) 562 /*++ 563 564 Routine Description: 565 566 Part of device initialize routine. Initialize PowerContext related stuff. 567 568 Arguments: 569 570 DeviceExtension - device extension of WDFDEVICE. 571 572 Return Value: 573 574 NTSTATUS 575 576 --*/ 577 { 578 NTSTATUS status = STATUS_SUCCESS; 579 WDF_OBJECT_ATTRIBUTES attributes; 580 581 PAGED_CODE(); 582 583 // create request object for Power operations 584 585 WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, 586 CDROM_REQUEST_CONTEXT); 587 attributes.ParentObject = DeviceExtension->Device; 588 589 status = WdfRequestCreate(&attributes, 590 DeviceExtension->IoTarget, 591 &(DeviceExtension->PowerContext.PowerRequest) ); 592 593 if (!NT_SUCCESS(status)) 594 { 595 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP, "Cannot create the power request object.\n")); 596 597 return status; 598 } 599 600 // Preformat the power request. With this being done, we never need to worry about 601 // WdfIoTargetFormatRequestForInternalIoctlOthers ever failing later. 602 status = WdfIoTargetFormatRequestForInternalIoctlOthers(DeviceExtension->IoTarget, 603 DeviceExtension->PowerContext.PowerRequest, 604 IOCTL_SCSI_EXECUTE_IN, 605 NULL, NULL, 606 NULL, NULL, 607 NULL, NULL); 608 return status; 609 } 610 611 _IRQL_requires_max_(APC_LEVEL) 612 NTSTATUS 613 DeviceCreateWellKnownName( 614 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension 615 ) 616 /*++ 617 618 Routine Description: 619 620 This routine creates a symbolic link to the cdrom device object 621 under \dosdevices. The number of the cdrom device does not neccessarily 622 match between \dosdevices and \device, but usually will be the same. 623 624 Saves the buffer 625 626 Arguments: 627 628 DeviceObject - 629 630 Return Value: 631 632 NTSTATUS 633 634 --*/ 635 { 636 NTSTATUS status = STATUS_SUCCESS; 637 UNICODE_STRING unicodeLinkName = {0}; 638 WCHAR wideLinkName[64] = {0}; 639 PWCHAR savedName; 640 641 LONG cdromNumber = DeviceExtension->DeviceNumber; 642 643 PAGED_CODE(); 644 645 // if already linked, assert then return 646 if (DeviceExtension->DeviceAdditionalData.WellKnownName.Buffer != NULL) 647 { 648 649 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT, 650 "DeviceCreateWellKnownName: link already exists %p\n", 651 DeviceExtension->DeviceAdditionalData.WellKnownName.Buffer)); 652 653 NT_ASSERT(FALSE); 654 655 return STATUS_UNSUCCESSFUL; 656 } 657 658 // find an unused CdRomNN to link to. 659 // It's doing this way because the same might be used for other device in another driver. 660 do 661 { 662 status = RtlStringCchPrintfW((NTSTRSAFE_PWSTR)wideLinkName, 663 RTL_NUMBER_OF(wideLinkName), 664 L"\\DosDevices\\CdRom%d", 665 cdromNumber); 666 if (!NT_SUCCESS(status)) 667 { 668 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT, 669 "DeviceCreateWellKnownName: Format symbolic link failed with error: 0x%X\n", status)); 670 return status; 671 } 672 673 RtlInitUnicodeString(&unicodeLinkName, wideLinkName); 674 675 status = WdfDeviceCreateSymbolicLink(DeviceExtension->Device, 676 &unicodeLinkName); 677 678 cdromNumber++; 679 680 } while((status == STATUS_OBJECT_NAME_COLLISION) || 681 (status == STATUS_OBJECT_NAME_EXISTS)); 682 683 if (!NT_SUCCESS(status)) 684 { 685 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT, 686 "DeviceCreateWellKnownName: Error %lx linking %wZ to " 687 "device %wZ\n", 688 status, 689 &unicodeLinkName, 690 &(DeviceExtension->DeviceName))); 691 return status; 692 } 693 694 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT, 695 "DeviceCreateWellKnownName: successfully linked %wZ " 696 "to device %wZ\n", 697 &unicodeLinkName, 698 &(DeviceExtension->DeviceName))); 699 700 // Save away the symbolic link name in the driver data block. We need 701 // it so we can delete the link when the device is removed. 702 savedName = ExAllocatePoolWithTag(PagedPool, 703 unicodeLinkName.MaximumLength, 704 CDROM_TAG_STRINGS); 705 706 if (savedName == NULL) 707 { 708 // Test Note: test path should excise here to see if the symbolic is deleted by framework. 709 // IoDeleteSymbolicLink(&unicodeLinkName); 710 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT, 711 "DeviceCreateWellKnownName: unable to allocate memory.\n")); 712 713 return STATUS_INSUFFICIENT_RESOURCES; 714 } 715 716 RtlZeroMemory(savedName, unicodeLinkName.MaximumLength); 717 RtlCopyMemory(savedName, unicodeLinkName.Buffer, unicodeLinkName.MaximumLength); 718 719 RtlInitUnicodeString(&(DeviceExtension->DeviceAdditionalData.WellKnownName), savedName); 720 721 // the name was saved and the link created 722 723 return STATUS_SUCCESS; 724 } 725 726 _IRQL_requires_max_(PASSIVE_LEVEL) 727 NTSTATUS 728 DeviceRetrieveScsiAddress( 729 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, 730 _In_ PSCSI_ADDRESS ScsiAddress 731 ) 732 /*++ 733 734 Routine Description: 735 736 retrieve SCSI address information and put into device extension 737 738 Arguments: 739 740 DeviceExtension - device context. 741 ScsiAddress - the buffer to put the scsi address info. 742 743 Return Value: 744 745 NTSTATUS 746 747 --*/ 748 { 749 NTSTATUS status; 750 WDF_MEMORY_DESCRIPTOR outputDescriptor; 751 752 PAGED_CODE(); 753 754 if ((DeviceExtension == NULL) || 755 (ScsiAddress == NULL)) 756 { 757 return STATUS_INVALID_PARAMETER; 758 } 759 760 //Get IOTARGET for sending request to port driver. 761 WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&outputDescriptor, 762 (PVOID)ScsiAddress, 763 sizeof(SCSI_ADDRESS)); 764 765 status = WdfIoTargetSendIoctlSynchronously(DeviceExtension->IoTarget, 766 NULL, 767 IOCTL_SCSI_GET_ADDRESS, 768 NULL, 769 &outputDescriptor, 770 NULL, 771 NULL); 772 773 if (!NT_SUCCESS(status)) 774 { 775 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT, 776 "DeviceRetrieveScsiAddress: Get Address failed %lx\n", 777 status)); 778 } 779 else 780 { 781 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT, 782 "GetAddress: Port %x, Path %x, Target %x, Lun %x\n", 783 ScsiAddress->PortNumber, 784 ScsiAddress->PathId, 785 ScsiAddress->TargetId, 786 ScsiAddress->Lun)); 787 } 788 789 return status; 790 } 791 792 _IRQL_requires_max_(APC_LEVEL) 793 NTSTATUS 794 DeviceInitAllocateBuffers( 795 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension 796 ) 797 /*++ 798 799 Routine Description: 800 801 Part of device initialize routine. 802 Allocate all buffers in Device Extension. 803 804 Arguments: 805 806 DeviceExtension - device extension of WDFDEVICE. 807 808 Return Value: 809 810 NTSTATUS 811 812 --*/ 813 { 814 NTSTATUS status = STATUS_SUCCESS; 815 PVOID senseData = NULL; 816 817 PAGED_CODE(); 818 819 // allocate a private extension for class data 820 if (DeviceExtension->PrivateFdoData == NULL) 821 { 822 DeviceExtension->PrivateFdoData = ExAllocatePoolWithTag(NonPagedPoolNx, 823 sizeof(CDROM_PRIVATE_FDO_DATA), 824 CDROM_TAG_PRIVATE_DATA); 825 } 826 827 if (DeviceExtension->PrivateFdoData == NULL) 828 { 829 status = STATUS_INSUFFICIENT_RESOURCES; 830 } 831 else 832 { 833 // initialize the struct's various fields. 834 RtlZeroMemory(DeviceExtension->PrivateFdoData, sizeof(CDROM_PRIVATE_FDO_DATA)); 835 } 836 837 // Allocate request sense buffer. 838 senseData = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned, 839 SENSE_BUFFER_SIZE, 840 CDROM_TAG_SENSE_INFO); 841 842 if (senseData == NULL) 843 { 844 // The buffer cannot be allocated. 845 status = STATUS_INSUFFICIENT_RESOURCES; 846 } 847 else 848 { 849 // Set the sense data pointer in the device extension. 850 DeviceExtension->SenseData = senseData; 851 } 852 853 // Allocate scratch buffer -- Must occur after determining 854 // max transfer size, but before other CD specific detection 855 // (which relies upon this buffer). 856 if (!ScratchBuffer_Allocate(DeviceExtension)) 857 { 858 status = STATUS_INSUFFICIENT_RESOURCES; 859 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT, 860 "Failed to allocate scratch buffer, failing %!STATUS!\n", 861 status 862 )); 863 } 864 865 return status; 866 } 867 868 _IRQL_requires_max_(PASSIVE_LEVEL) 869 NTSTATUS 870 DeviceRetrieveDescriptorsAndTransferLength( 871 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension 872 ) 873 /*++ 874 875 Routine Description: 876 877 Part of device initialize routine. 878 Retrieve Device Descriptor and Adaptor Descriptor. 879 880 Arguments: 881 882 DeviceExtension - device extension of WDFDEVICE. 883 884 Return Value: 885 886 NTSTATUS 887 888 --*/ 889 { 890 NTSTATUS status = STATUS_SUCCESS; 891 STORAGE_PROPERTY_ID propertyId; 892 893 PAGED_CODE(); 894 895 if (NT_SUCCESS(status)) 896 { 897 // Call port driver to get adapter capabilities. 898 propertyId = StorageAdapterProperty; 899 900 status = DeviceRetrieveDescriptor(DeviceExtension->Device, 901 &propertyId, 902 (PSTORAGE_DESCRIPTOR_HEADER*)&DeviceExtension->AdapterDescriptor); 903 } 904 if (NT_SUCCESS(status)) 905 { 906 // Call port driver to get device descriptor. 907 propertyId = StorageDeviceProperty; 908 909 status = DeviceRetrieveDescriptor(DeviceExtension->Device, 910 &propertyId, 911 (PSTORAGE_DESCRIPTOR_HEADER*)&DeviceExtension->DeviceDescriptor); 912 } 913 if (NT_SUCCESS(status)) 914 { 915 // Call port driver to get device power property. 916 // Not all port drivers support this property, and it's not fatal if this query fails. 917 propertyId = StorageDevicePowerProperty; 918 919 (void) DeviceRetrieveDescriptor(DeviceExtension->Device, 920 &propertyId, 921 (PSTORAGE_DESCRIPTOR_HEADER*)&DeviceExtension->PowerDescriptor); 922 } 923 924 if (NT_SUCCESS(status)) 925 { 926 // Determine the maximum page-aligned and non-page-aligned transfer 927 // lengths here, so we needn't do this in common READ/WRITE code paths 928 929 // start with the number of pages the adapter can support 930 ULONG maxAlignedTransfer = DeviceExtension->AdapterDescriptor->MaximumPhysicalPages; 931 ULONG maxUnalignedTransfer = DeviceExtension->AdapterDescriptor->MaximumPhysicalPages; 932 933 934 // Unaligned buffers could cross a page boundary. 935 if (maxUnalignedTransfer > 1) 936 { 937 maxUnalignedTransfer--; 938 } 939 940 // if we'd overflow multiplying by page size, just max out the 941 // transfer length allowed by the number of pages limit. 942 if (maxAlignedTransfer >= (((ULONG)-1) / PAGE_SIZE)) 943 { 944 maxAlignedTransfer = (ULONG)-1; 945 } 946 else 947 { 948 maxAlignedTransfer *= PAGE_SIZE; 949 } 950 951 if (maxUnalignedTransfer >= (((ULONG)-1) / PAGE_SIZE)) 952 { 953 maxUnalignedTransfer = (ULONG)-1; 954 } 955 else 956 { 957 maxUnalignedTransfer *= PAGE_SIZE; 958 } 959 960 // finally, take the smaller of the above and the adapter's 961 // reported maximum number of bytes per transfer. 962 maxAlignedTransfer = min(maxAlignedTransfer, DeviceExtension->AdapterDescriptor->MaximumTransferLength); 963 maxUnalignedTransfer = min(maxUnalignedTransfer, DeviceExtension->AdapterDescriptor->MaximumTransferLength); 964 965 // Make sure the values are reasonable and not zero. 966 maxAlignedTransfer = max(maxAlignedTransfer, PAGE_SIZE); 967 maxUnalignedTransfer = max(maxUnalignedTransfer, PAGE_SIZE); 968 969 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT, 970 "Device %p Max aligned/unaligned transfer size is %x/%x\n", 971 DeviceExtension->Device, 972 maxAlignedTransfer, 973 maxUnalignedTransfer 974 )); 975 DeviceExtension->DeviceAdditionalData.MaxPageAlignedTransferBytes = maxAlignedTransfer; 976 DeviceExtension->DeviceAdditionalData.MaxUnalignedTransferBytes = maxUnalignedTransfer; 977 } 978 else 979 { 980 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP, "DeviceRetrieveDescriptorsAndTransferLength failed %lx\n", status)); 981 } 982 983 return status; 984 } 985 986 987 _IRQL_requires_max_(PASSIVE_LEVEL) 988 NTSTATUS 989 DeviceRetrieveDescriptor( 990 _In_ WDFDEVICE Device, 991 _In_ PSTORAGE_PROPERTY_ID PropertyId, 992 _Outptr_ PSTORAGE_DESCRIPTOR_HEADER* Descriptor 993 ) 994 /*++ 995 996 Routine Description: 997 998 This routine will perform a query for the specified property id and will 999 allocate a non-paged buffer to store the data in. It is the responsibility 1000 of the caller to ensure that this buffer is freed. 1001 1002 This routine must be run at IRQL_PASSIVE_LEVEL 1003 1004 Arguments: 1005 1006 Device - the device object 1007 PropertyId - type of property to retrieve 1008 Descriptor - buffer allocated in this function to hold the descriptor data 1009 1010 Return Value: 1011 1012 status 1013 1014 --*/ 1015 { 1016 NTSTATUS status; 1017 WDF_MEMORY_DESCRIPTOR memoryDescriptor; 1018 1019 STORAGE_PROPERTY_QUERY query = {0}; 1020 ULONG bufferLength = 0; 1021 1022 PSTORAGE_DESCRIPTOR_HEADER descriptor = NULL; 1023 PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device); 1024 1025 PAGED_CODE(); 1026 1027 // Set the passed-in descriptor pointer to NULL as default 1028 *Descriptor = NULL; 1029 1030 // On the first pass we just want to get the first few 1031 // bytes of the descriptor so we can read it's size 1032 query.PropertyId = *PropertyId; 1033 query.QueryType = PropertyStandardQuery; 1034 1035 descriptor = (PVOID)&query; 1036 1037 NT_ASSERT(sizeof(STORAGE_PROPERTY_QUERY) >= (sizeof(ULONG)*2)); 1038 1039 WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memoryDescriptor, 1040 (PVOID)&query, 1041 sizeof(STORAGE_PROPERTY_QUERY)); 1042 1043 status = WdfIoTargetSendIoctlSynchronously(deviceExtension->IoTarget, 1044 NULL, 1045 IOCTL_STORAGE_QUERY_PROPERTY, 1046 &memoryDescriptor, 1047 &memoryDescriptor, 1048 NULL, 1049 NULL); 1050 1051 if(!NT_SUCCESS(status)) 1052 { 1053 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT, "DeviceRetrieveDescriptor: error %lx trying to " 1054 "query properties #1\n", status)); 1055 return status; 1056 } 1057 1058 if (descriptor->Size == 0) 1059 { 1060 // This DebugPrint is to help third-party driver writers 1061 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT, "DeviceRetrieveDescriptor: size returned was zero?! (status " 1062 "%x\n", status)); 1063 return STATUS_UNSUCCESSFUL; 1064 } 1065 1066 // This time we know how much data there is so we can 1067 // allocate a buffer of the correct size 1068 bufferLength = descriptor->Size; 1069 NT_ASSERT(bufferLength >= sizeof(STORAGE_PROPERTY_QUERY)); 1070 bufferLength = max(bufferLength, sizeof(STORAGE_PROPERTY_QUERY)); 1071 1072 descriptor = ExAllocatePoolWithTag(NonPagedPoolNx, bufferLength, CDROM_TAG_DESCRIPTOR); 1073 1074 if(descriptor == NULL) 1075 { 1076 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT, "DeviceRetrieveDescriptor: unable to memory for descriptor " 1077 "(%d bytes)\n", bufferLength)); 1078 return STATUS_INSUFFICIENT_RESOURCES; 1079 } 1080 1081 // setup the query again, as it was overwritten above 1082 RtlZeroMemory(&query, sizeof(STORAGE_PROPERTY_QUERY)); 1083 query.PropertyId = *PropertyId; 1084 query.QueryType = PropertyStandardQuery; 1085 1086 // copy the input to the new outputbuffer 1087 RtlCopyMemory(descriptor, 1088 &query, 1089 sizeof(STORAGE_PROPERTY_QUERY)); 1090 1091 WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memoryDescriptor, 1092 (PVOID)descriptor, 1093 bufferLength); 1094 1095 status = WdfIoTargetSendIoctlSynchronously(deviceExtension->IoTarget, 1096 NULL, 1097 IOCTL_STORAGE_QUERY_PROPERTY, 1098 &memoryDescriptor, 1099 &memoryDescriptor, 1100 NULL, 1101 NULL); 1102 1103 if(!NT_SUCCESS(status)) 1104 { 1105 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT, "DeviceRetrieveDescriptor: error %lx trying to " 1106 "query properties #1\n", status)); 1107 FREE_POOL(descriptor); 1108 1109 return status; 1110 } 1111 1112 // return the memory we've allocated to the caller 1113 *Descriptor = descriptor; 1114 1115 return status; 1116 } // end DeviceRetrieveDescriptor() 1117 1118 _IRQL_requires_max_(PASSIVE_LEVEL) 1119 VOID 1120 DeviceRetrieveHackFlagsFromRegistry( 1121 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension 1122 ) 1123 /*++ 1124 1125 Routine Description: 1126 1127 try to retrieve hack flages from registry and put the information in 1128 device extension. 1129 1130 Arguments: 1131 1132 DeviceExtension - the device context 1133 1134 Return Value: 1135 1136 none 1137 1138 --*/ 1139 { 1140 NTSTATUS status = STATUS_SUCCESS; 1141 WDFKEY hardwareKey = NULL; 1142 WDFKEY subKey = NULL; 1143 ULONG deviceHacks = 0; 1144 1145 DECLARE_CONST_UNICODE_STRING(subKeyName, CLASSP_REG_SUBKEY_NAME); 1146 DECLARE_CONST_UNICODE_STRING(valueName, CLASSP_REG_HACK_VALUE_NAME); 1147 1148 PAGED_CODE(); 1149 1150 status = WdfDeviceOpenRegistryKey(DeviceExtension->Device, 1151 PLUGPLAY_REGKEY_DEVICE, 1152 KEY_READ, 1153 WDF_NO_OBJECT_ATTRIBUTES, 1154 &hardwareKey); 1155 if (NT_SUCCESS(status)) 1156 { 1157 status = WdfRegistryOpenKey(hardwareKey, 1158 &subKeyName, 1159 KEY_READ, 1160 WDF_NO_OBJECT_ATTRIBUTES, 1161 &subKey); 1162 1163 if (NT_SUCCESS(status)) 1164 { 1165 status = WdfRegistryQueryULong(subKey, 1166 &valueName, 1167 &deviceHacks); 1168 if (NT_SUCCESS(status)) 1169 { 1170 // remove unknown values and save... 1171 CLEAR_FLAG(deviceHacks, FDO_HACK_INVALID_FLAGS); 1172 SET_FLAG(DeviceExtension->PrivateFdoData->HackFlags, deviceHacks); 1173 } 1174 1175 WdfRegistryClose(subKey); 1176 } 1177 1178 WdfRegistryClose(hardwareKey); 1179 } 1180 1181 1182 // 1183 // we should modify the system hive to include another key for us to grab 1184 // settings from. in this case: Classpnp\HackFlags 1185 // 1186 // the use of a DWORD value for the HackFlags allows 32 hacks w/o 1187 // significant use of the registry, and also reduces OEM exposure. 1188 // 1189 // definition of bit flags: 1190 // 0x00000001 -- Device succeeds PREVENT_MEDIUM_REMOVAL, but 1191 // cannot actually prevent removal. 1192 // 0x00000002 -- Device hard-hangs or times out for GESN requests. 1193 // 0x00000008 -- Device does not support RESERVE(6) and RELEASE(6). 1194 // 0x00000010 -- Device may incorrecly report operational changes in GESN. 1195 // 0x00000020 -- Device does not support streaming READ(12) / WRITE(12). 1196 // 0x00000040 -- Device does not support asynchronous notification. 1197 // 0xffffff80 -- Currently reserved, may be used later. 1198 // 1199 1200 return; 1201 } 1202 1203 _IRQL_requires_max_(APC_LEVEL) 1204 VOID DeviceScanForSpecial( 1205 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, 1206 _In_ CDROM_SCAN_FOR_SPECIAL_INFO DeviceList[], 1207 _In_ PCDROM_SCAN_FOR_SPECIAL_HANDLER Function) 1208 /*++ 1209 1210 Routine Description: 1211 1212 scan the list of devices that should be hacked or not supported. 1213 1214 Arguments: 1215 1216 DeviceExtension - the device context 1217 DeviceList - the device list 1218 Function - function used to scan from the list. 1219 1220 Return Value: 1221 1222 none 1223 1224 --*/ 1225 { 1226 PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor; 1227 PUCHAR vendorId; 1228 PUCHAR productId; 1229 PUCHAR productRevision; 1230 UCHAR nullString[] = ""; 1231 1232 PAGED_CODE(); 1233 NT_ASSERT(DeviceList); 1234 NT_ASSERT(Function); 1235 1236 if (DeviceList == NULL) 1237 { 1238 return; 1239 } 1240 if (Function == NULL) 1241 { 1242 return; 1243 } 1244 1245 deviceDescriptor = DeviceExtension->DeviceDescriptor; 1246 1247 // SCSI sets offsets to -1, ATAPI sets to 0. check for both. 1248 if (deviceDescriptor->VendorIdOffset != 0 && 1249 deviceDescriptor->VendorIdOffset != -1) 1250 { 1251 vendorId = ((PUCHAR)deviceDescriptor); 1252 vendorId += deviceDescriptor->VendorIdOffset; 1253 } 1254 else 1255 { 1256 vendorId = nullString; 1257 } 1258 1259 if (deviceDescriptor->ProductIdOffset != 0 && 1260 deviceDescriptor->ProductIdOffset != -1) 1261 { 1262 productId = ((PUCHAR)deviceDescriptor); 1263 productId += deviceDescriptor->ProductIdOffset; 1264 } 1265 else 1266 { 1267 productId = nullString; 1268 } 1269 1270 if (deviceDescriptor->ProductRevisionOffset != 0 && 1271 deviceDescriptor->ProductRevisionOffset != -1) 1272 { 1273 productRevision = ((PUCHAR)deviceDescriptor); 1274 productRevision += deviceDescriptor->ProductRevisionOffset; 1275 } 1276 else 1277 { 1278 productRevision = nullString; 1279 } 1280 1281 // loop while the device list is valid (not null-filled) 1282 for (;(DeviceList->VendorId != NULL || 1283 DeviceList->ProductId != NULL || 1284 DeviceList->ProductRevision != NULL); DeviceList++) 1285 { 1286 if (StringsAreMatched(DeviceList->VendorId, (LPSTR)vendorId) && 1287 StringsAreMatched(DeviceList->ProductId, (LPSTR)productId) && 1288 StringsAreMatched(DeviceList->ProductRevision, (LPSTR)productRevision) 1289 ) 1290 { 1291 1292 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT, "DeviceScanForSpecial: Found matching " 1293 "controller Ven: %s Prod: %s Rev: %s\n", 1294 (LPCSTR)vendorId, (LPCSTR)productId, (LPCSTR)productRevision)); 1295 1296 // pass the context to the call back routine and exit 1297 (Function)(DeviceExtension, DeviceList->Data); 1298 1299 // for CHK builds, try to prevent wierd stacks by having a debug 1300 // print here. it's a hack, but i know of no other way to prevent 1301 // the stack from being wrong. 1302 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT, "DeviceScanForSpecial: " 1303 "completed callback\n")); 1304 return; 1305 1306 } // else the strings did not match 1307 1308 } // none of the devices matched. 1309 1310 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT, "DeviceScanForSpecial: no match found for %p\n", 1311 DeviceExtension->DeviceObject)); 1312 return; 1313 1314 } // end DeviceScanForSpecial() 1315 1316 _IRQL_requires_max_(APC_LEVEL) 1317 VOID 1318 DeviceHackFlagsScan( 1319 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, 1320 _In_ ULONG_PTR Data 1321 ) 1322 { 1323 PAGED_CODE(); 1324 1325 // remove invalid flags and save 1326 CLEAR_FLAG(Data, FDO_HACK_INVALID_FLAGS); 1327 SET_FLAG(DeviceExtension->PrivateFdoData->HackFlags, Data); 1328 1329 return; 1330 } 1331 1332 1333 _IRQL_requires_max_(PASSIVE_LEVEL) 1334 NTSTATUS 1335 DeviceInitializeHotplugInfo( 1336 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension 1337 ) 1338 /*++ 1339 1340 Routine Description: 1341 1342 Retrieve information into struc STORAGE_HOTPLUG_INFO in DeviceExtension 1343 initialize the hotplug information only after the ScanForSpecial routines, 1344 as it relies upon the hack flags - DeviceExtension->PrivateFdoData->HackFlags. 1345 1346 Arguments: 1347 1348 DeviceExtension - the device context 1349 1350 Return Value: 1351 1352 NTSTATUS 1353 1354 --*/ 1355 { 1356 NTSTATUS status = STATUS_SUCCESS; 1357 PCDROM_PRIVATE_FDO_DATA fdoData = DeviceExtension->PrivateFdoData; 1358 DEVICE_REMOVAL_POLICY deviceRemovalPolicy = 0; 1359 ULONG resultLength = 0; 1360 ULONG writeCacheOverride; 1361 1362 PAGED_CODE(); 1363 1364 // start with some default settings 1365 RtlZeroMemory(&(fdoData->HotplugInfo), sizeof(STORAGE_HOTPLUG_INFO)); 1366 1367 // set the size (aka version) 1368 fdoData->HotplugInfo.Size = sizeof(STORAGE_HOTPLUG_INFO); 1369 1370 // set if the device has removable media 1371 if (DeviceExtension->DeviceDescriptor->RemovableMedia) 1372 { 1373 fdoData->HotplugInfo.MediaRemovable = TRUE; 1374 } 1375 else 1376 { 1377 fdoData->HotplugInfo.MediaRemovable = FALSE; 1378 } 1379 1380 // 1381 // this refers to devices which, for reasons not yet understood, 1382 // do not fail PREVENT_MEDIA_REMOVAL requests even though they 1383 // have no way to lock the media into the drive. this allows 1384 // the filesystems to turn off delayed-write caching for these 1385 // devices as well. 1386 // 1387 1388 if (TEST_FLAG(DeviceExtension->PrivateFdoData->HackFlags, FDO_HACK_CANNOT_LOCK_MEDIA)) 1389 { 1390 fdoData->HotplugInfo.MediaHotplug = TRUE; 1391 } 1392 else 1393 { 1394 fdoData->HotplugInfo.MediaHotplug = FALSE; 1395 } 1396 1397 // Query the default removal policy from the kernel 1398 status = WdfDeviceQueryProperty(DeviceExtension->Device, 1399 DevicePropertyRemovalPolicy, 1400 sizeof(DEVICE_REMOVAL_POLICY), 1401 (PVOID)&deviceRemovalPolicy, 1402 &resultLength); 1403 if (NT_SUCCESS(status)) 1404 { 1405 if (resultLength != sizeof(DEVICE_REMOVAL_POLICY)) 1406 { 1407 status = STATUS_UNSUCCESSFUL; 1408 } 1409 } 1410 1411 if (NT_SUCCESS(status)) 1412 { 1413 // Look into the registry to see if the user has chosen 1414 // to override the default setting for the removal policy. 1415 // User can override only if the default removal policy is 1416 // orderly or suprise removal. 1417 1418 if ((deviceRemovalPolicy == RemovalPolicyExpectOrderlyRemoval) || 1419 (deviceRemovalPolicy == RemovalPolicyExpectSurpriseRemoval)) 1420 { 1421 DEVICE_REMOVAL_POLICY userRemovalPolicy = 0; 1422 1423 DeviceGetParameter(DeviceExtension, 1424 CLASSP_REG_SUBKEY_NAME, 1425 CLASSP_REG_REMOVAL_POLICY_VALUE_NAME, 1426 (PULONG)&userRemovalPolicy); 1427 1428 // Validate the override value and use it only if it is an 1429 // allowed value. 1430 if ((userRemovalPolicy == RemovalPolicyExpectOrderlyRemoval) || 1431 (userRemovalPolicy == RemovalPolicyExpectSurpriseRemoval)) 1432 { 1433 deviceRemovalPolicy = userRemovalPolicy; 1434 } 1435 } 1436 1437 // use this info to set the DeviceHotplug setting 1438 // don't rely on DeviceCapabilities, since it can't properly 1439 // determine device relations, etc. let the kernel figure this 1440 // stuff out instead. 1441 if (deviceRemovalPolicy == RemovalPolicyExpectSurpriseRemoval) 1442 { 1443 fdoData->HotplugInfo.DeviceHotplug = TRUE; 1444 } 1445 else 1446 { 1447 fdoData->HotplugInfo.DeviceHotplug = FALSE; 1448 } 1449 1450 // this refers to the *filesystem* caching, but has to be included 1451 // here since it's a per-device setting. this may change to be 1452 // stored by the system in the future. 1453 writeCacheOverride = FALSE; 1454 DeviceGetParameter(DeviceExtension, 1455 CLASSP_REG_SUBKEY_NAME, 1456 CLASSP_REG_WRITE_CACHE_VALUE_NAME, 1457 &writeCacheOverride); 1458 1459 if (writeCacheOverride) 1460 { 1461 fdoData->HotplugInfo.WriteCacheEnableOverride = TRUE; 1462 } 1463 else 1464 { 1465 fdoData->HotplugInfo.WriteCacheEnableOverride = FALSE; 1466 } 1467 } 1468 1469 if (!NT_SUCCESS(status)) 1470 { 1471 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT, "Could not initialize hotplug information %lx\n", status)); 1472 } 1473 1474 return status; 1475 } 1476 1477 1478 _IRQL_requires_max_(PASSIVE_LEVEL) 1479 NTSTATUS 1480 DeviceInitMmcContext( 1481 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension 1482 ) 1483 /*++ 1484 1485 Routine Description: 1486 1487 This routine initializes and populates the internal data structures that are 1488 used to discover various MMC-defined capabilities of the device. 1489 1490 This routine will not clean up allocate resources if it fails - that 1491 is left for device stop/removal routines 1492 1493 Arguments: 1494 1495 DeviceExtension - device extension 1496 1497 Return Value: 1498 1499 NTSTATUS 1500 1501 --*/ 1502 { 1503 NTSTATUS status = STATUS_SUCCESS; 1504 1505 PAGED_CODE(); 1506 1507 DeviceExtension->DeviceAdditionalData.Mmc.IsMmc = FALSE; 1508 DeviceExtension->DeviceAdditionalData.Mmc.IsAACS = FALSE; 1509 DeviceExtension->DeviceAdditionalData.Mmc.IsWriter = FALSE; 1510 DeviceExtension->DeviceAdditionalData.Mmc.WriteAllowed = FALSE; 1511 DeviceExtension->DeviceAdditionalData.Mmc.IsCssDvd = FALSE; 1512 DeviceExtension->DeviceAdditionalData.DriveDeviceType = FILE_DEVICE_CD_ROM; 1513 1514 // Determine if the drive is MMC-Capable 1515 if (NT_SUCCESS(status)) 1516 { 1517 status = DeviceGetMmcSupportInfo(DeviceExtension, 1518 &DeviceExtension->DeviceAdditionalData.Mmc.IsMmc); 1519 1520 if (!NT_SUCCESS(status)) 1521 { 1522 //Currently, only low resource error comes here. 1523 //That is a success case for unsupporting this command. 1524 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT, 1525 "DeviceInitMmcContext: Failed to get the support info for GET CONFIGURATION " 1526 "command, failng %!STATUS!\n", status 1527 )); 1528 1529 DeviceExtension->DeviceAdditionalData.Mmc.IsMmc = FALSE; 1530 status = STATUS_SUCCESS; 1531 } 1532 } 1533 1534 if (NT_SUCCESS(status) && DeviceExtension->DeviceAdditionalData.Mmc.IsMmc) 1535 { 1536 // the drive supports at least a subset of MMC commands 1537 // (and therefore supports READ_CD, etc...) 1538 1539 // allocate a buffer for all the capabilities and such 1540 status = DeviceAllocateMmcResources(DeviceExtension->Device); 1541 } 1542 1543 if (NT_SUCCESS(status) && DeviceExtension->DeviceAdditionalData.Mmc.IsMmc) 1544 { 1545 PFEATURE_HEADER header = NULL; 1546 FEATURE_NUMBER validationSchema; 1547 ULONG blockingFactor; 1548 1549 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT, 1550 "DeviceInitMmcContext: FDO %p GET CONFIGURATION buffer %p\n", 1551 DeviceExtension->Device, 1552 DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBuffer 1553 )); 1554 1555 // Update several properties using the retrieved Configuration Data. 1556 1557 // print all the feature pages (DBG only) 1558 DevicePrintAllFeaturePages(DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBuffer, 1559 DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBufferSize); 1560 1561 // if AACS feature exists, enable AACS flag in the driver 1562 header = DeviceFindFeaturePage(DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBuffer, 1563 DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBufferSize, 1564 FeatureAACS); 1565 if (header) 1566 { 1567 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT, 1568 "DeviceInitMmcContext: Reporting AACS support for device due to " 1569 "GET CONFIGURATION showing support\n" 1570 )); 1571 DeviceExtension->DeviceAdditionalData.Mmc.IsAACS = TRUE; 1572 } 1573 1574 #ifdef ENABLE_AACS_TESTING 1575 DeviceExtension->DeviceAdditionalData.Mmc.IsAACS = TRUE; // just force it true for testing 1576 #endif // ENABLE_AACS_TESTING 1577 1578 // Check if it's a DVD device 1579 header = DeviceFindFeaturePage(DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBuffer, 1580 DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBufferSize, 1581 FeatureDvdRead); 1582 if (header != NULL) 1583 { 1584 DeviceExtension->DeviceAdditionalData.DriveDeviceType = FILE_DEVICE_DVD; 1585 } 1586 1587 // check if drive is writer 1588 DeviceUpdateMmcWriteCapability(DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBuffer, 1589 DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBufferSize, 1590 FALSE, //Check if the drive has the ability to write. 1591 (PBOOLEAN)&(DeviceExtension->DeviceAdditionalData.Mmc.IsWriter), 1592 &validationSchema, 1593 &blockingFactor); 1594 1595 // check if there is a CSS protected DVD or CPPM-protected DVDAudio media in drive. 1596 header = DeviceFindFeaturePage(DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBuffer, 1597 DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBufferSize, 1598 FeatureDvdCSS); 1599 1600 DeviceExtension->DeviceAdditionalData.Mmc.IsCssDvd = (header != NULL) && (header->Current); 1601 1602 // Flag the StartIo routine to update its state and hook in the error handler 1603 DeviceExtension->DeviceAdditionalData.Mmc.UpdateState = CdromMmcUpdateRequired; 1604 DeviceExtension->DeviceAdditionalData.ErrorHandler = DeviceErrorHandlerForMmc; 1605 1606 SET_FLAG(DeviceExtension->DeviceFlags, DEV_SAFE_START_UNIT); 1607 1608 // Read the CDROM mode sense page to get additional info for raw read requests. 1609 // only valid for MMC devices 1610 DeviceSetRawReadInfo(DeviceExtension); 1611 } 1612 1613 // Set Read-Only device flag for non-MMC device. 1614 if (!(DeviceExtension->DeviceAdditionalData.Mmc.IsMmc)) 1615 { 1616 ULONG deviceCharacteristics = WdfDeviceGetCharacteristics(DeviceExtension->Device); 1617 1618 deviceCharacteristics |= FILE_READ_ONLY_DEVICE; 1619 1620 WdfDeviceSetCharacteristics(DeviceExtension->Device, deviceCharacteristics); 1621 1622 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT, 1623 "DeviceInitMmcContext: FDO %p Device is not an MMC compliant device, so setting " 1624 "to read-only (legacy) mode", 1625 DeviceExtension->Device 1626 )); 1627 } 1628 1629 // Set DEV_SAFE_START_UNIT flag for newer devices. 1630 if (DeviceExtension->DeviceAdditionalData.DriveDeviceType == FILE_DEVICE_DVD) 1631 { 1632 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT, 1633 "DeviceInitMmcContext: DVD Devices require START UNIT\n")); 1634 SET_FLAG(DeviceExtension->DeviceFlags, DEV_SAFE_START_UNIT); 1635 1636 } 1637 else if ((DeviceExtension->DeviceDescriptor->BusType != BusTypeScsi) && 1638 (DeviceExtension->DeviceDescriptor->BusType != BusTypeAtapi) && 1639 (DeviceExtension->DeviceDescriptor->BusType != BusTypeUnknown) 1640 ) 1641 { 1642 // devices on the newer busses require START_UNIT 1643 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT, 1644 "DeviceInitMmcContext: Devices for newer buses require START UNIT\n")); 1645 SET_FLAG(DeviceExtension->DeviceFlags, DEV_SAFE_START_UNIT); 1646 } 1647 1648 return status; 1649 } 1650 1651 1652 _IRQL_requires_max_(PASSIVE_LEVEL) 1653 ULONG 1654 DeviceGetTimeOutValueFromRegistry() 1655 /*++ 1656 1657 Routine Description: 1658 1659 get the device time out value from registry 1660 1661 Arguments: 1662 1663 None 1664 1665 Return Value: 1666 1667 ULONG - value of timeout 1668 1669 --*/ 1670 { 1671 NTSTATUS status; 1672 WDFKEY registryKey = NULL; 1673 ULONG timeOutValue = 0; 1674 1675 DECLARE_CONST_UNICODE_STRING(registryValueName, L"TimeOutValue"); 1676 1677 PAGED_CODE(); 1678 1679 // open the service key. 1680 status = WdfDriverOpenParametersRegistryKey(WdfGetDriver(), 1681 KEY_READ, 1682 WDF_NO_OBJECT_ATTRIBUTES, 1683 ®istryKey); 1684 1685 if (NT_SUCCESS(status)) 1686 { 1687 status = WdfRegistryQueryULong(registryKey, 1688 ®istryValueName, 1689 &timeOutValue); 1690 1691 WdfRegistryClose(registryKey); 1692 } 1693 1694 if (!NT_SUCCESS(status)) 1695 { 1696 timeOutValue = 0; 1697 } 1698 1699 return timeOutValue; 1700 1701 } // end DeviceGetTimeOutValueFromRegistry() 1702 1703 1704 _IRQL_requires_max_(APC_LEVEL) 1705 VOID 1706 DeviceScanSpecialDevices( 1707 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension 1708 ) 1709 /*++ 1710 1711 Routine Description: 1712 1713 This function checks to see if an SCSI logical unit requires an special 1714 initialization or error processing. 1715 1716 Arguments: 1717 1718 Device - device object. 1719 1720 Return Value: 1721 1722 None. 1723 1724 --*/ 1725 { 1726 1727 PAGED_CODE(); 1728 1729 // set our hack flags 1730 DeviceScanForSpecial(DeviceExtension, CdromHackItems, ScanForSpecialHandler); 1731 1732 // 1733 // All CDRom's can ignore the queue lock failure for power operations 1734 // and do not require handling the SpinUp case (unknown result of sending 1735 // a cdrom a START_UNIT command -- may eject disks?) 1736 // 1737 // We send the stop command mostly to stop outstanding asynch operations 1738 // (like audio playback) from running when the system is powered off. 1739 // Because of this and the unlikely chance that a PLAY command will be 1740 // sent in the window between the STOP and the time the machine powers down 1741 // we don't require queue locks. This is important because without them 1742 // classpnp's power routines will send the START_STOP_UNIT command to the 1743 // device whether or not it supports locking (atapi does not support locking 1744 // and if we requested them we would end up not stopping audio on atapi 1745 // devices). 1746 // SET_FLAG(deviceExtension->ScanForSpecialFlags, CDROM_SPECIAL_DISABLE_SPIN_UP); 1747 // SET_FLAG(deviceExtension->ScanForSpecialFlags, CDROM_SPECIAL_NO_QUEUE_LOCK); 1748 1749 if (TEST_FLAG(DeviceExtension->DeviceAdditionalData.HackFlags, CDROM_HACK_TOSHIBA_SD_W1101)) 1750 { 1751 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT, 1752 "DeviceScanSpecialDevices: Found Toshiba SD-W1101 DVD-RAM " 1753 "-- This drive will *NOT* support DVD-ROM playback.\n")); 1754 } 1755 else if (TEST_FLAG(DeviceExtension->DeviceAdditionalData.HackFlags, CDROM_HACK_HITACHI_GD_2000)) 1756 { 1757 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT, 1758 "DeviceScanSpecialDevices: Found Hitachi GD-2000\n")); 1759 1760 // Setup an error handler to spin up the drive when it idles out 1761 // since it seems to like to fail to spin itself back up on its 1762 // own for a REPORT_KEY command. It may also lose the AGIDs that 1763 // it has given, which will result in DVD playback failures. 1764 // This routine will just do what it can... 1765 DeviceExtension->DeviceAdditionalData.ErrorHandler = DeviceErrorHandlerForHitachiGD2000; 1766 1767 // this drive may require START_UNIT commands to spin 1768 // the drive up when it's spun itself down. 1769 SET_FLAG(DeviceExtension->DeviceFlags, DEV_SAFE_START_UNIT); 1770 } 1771 else if (TEST_FLAG(DeviceExtension->DeviceAdditionalData.HackFlags, CDROM_HACK_FUJITSU_FMCD_10x)) 1772 { 1773 // When Read command is issued to FMCD-101 or FMCD-102 and there is a music 1774 // cd in it. It takes longer time than SCSI_CDROM_TIMEOUT before returning 1775 // error status. 1776 DeviceExtension->TimeOutValue = 20; 1777 } 1778 else if (TEST_FLAG(DeviceExtension->DeviceAdditionalData.HackFlags, CDROM_HACK_DEC_RRD)) 1779 { 1780 NTSTATUS status; 1781 PMODE_PARM_READ_WRITE_DATA modeParameters; 1782 SCSI_REQUEST_BLOCK srb = {0}; 1783 PCDB cdb; 1784 1785 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT, 1786 "DeviceScanSpecialDevices: Found DEC RRD.\n")); 1787 1788 DeviceExtension->DeviceAdditionalData.IsDecRrd = TRUE; 1789 1790 // Setup an error handler to reinitialize the cd rom after it is reset? 1791 // 1792 //DeviceExtension->DevInfo->ClassError = DecRrdProcessError; 1793 1794 // Found a DEC RRD cd-rom. These devices do not pass MS HCT 1795 // multi-media tests because the DEC firmware modifieds the block 1796 // from the PC-standard 2K to 512. Change the block transfer size 1797 // back to the PC-standard 2K by using a mode select command. 1798 1799 modeParameters = ExAllocatePoolWithTag(NonPagedPoolNx, 1800 sizeof(MODE_PARM_READ_WRITE_DATA), 1801 CDROM_TAG_MODE_DATA); 1802 if (modeParameters == NULL) 1803 { 1804 return; 1805 } 1806 1807 RtlZeroMemory(modeParameters, sizeof(MODE_PARM_READ_WRITE_DATA)); 1808 RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK)); 1809 1810 // Set the block length to 2K. 1811 modeParameters->ParameterListHeader.BlockDescriptorLength = sizeof(MODE_PARAMETER_BLOCK); 1812 1813 // Set block length to 2K (0x0800) in Parameter Block. 1814 modeParameters->ParameterListBlock.BlockLength[0] = 0x00; //MSB 1815 modeParameters->ParameterListBlock.BlockLength[1] = 0x08; 1816 modeParameters->ParameterListBlock.BlockLength[2] = 0x00; //LSB 1817 1818 // Build the mode select CDB. 1819 srb.CdbLength = 6; 1820 srb.TimeOutValue = DeviceExtension->TimeOutValue; 1821 1822 cdb = (PCDB)srb.Cdb; 1823 cdb->MODE_SELECT.PFBit = 1; 1824 cdb->MODE_SELECT.OperationCode = SCSIOP_MODE_SELECT; 1825 cdb->MODE_SELECT.ParameterListLength = HITACHI_MODE_DATA_SIZE; 1826 1827 // Send the request to the device. 1828 status = DeviceSendSrbSynchronously(DeviceExtension->Device, 1829 &srb, 1830 modeParameters, 1831 sizeof(MODE_PARM_READ_WRITE_DATA), 1832 TRUE, 1833 NULL); 1834 1835 if (!NT_SUCCESS(status)) 1836 { 1837 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT, 1838 "DeviceScanSpecialDevices: Setting DEC RRD to 2K block" 1839 "size failed [%x]\n", status)); 1840 } 1841 1842 ExFreePool(modeParameters); 1843 } 1844 1845 return; 1846 } 1847 1848 _IRQL_requires_max_(APC_LEVEL) 1849 VOID 1850 ScanForSpecialHandler( 1851 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, 1852 _In_ ULONG_PTR HackFlags 1853 ) 1854 { 1855 PAGED_CODE(); 1856 1857 CLEAR_FLAG(HackFlags, CDROM_HACK_INVALID_FLAGS); 1858 1859 DeviceExtension->DeviceAdditionalData.HackFlags = HackFlags; 1860 1861 return; 1862 } 1863 1864 1865 _IRQL_requires_max_(PASSIVE_LEVEL) 1866 NTSTATUS 1867 DeviceCacheDeviceInquiryData( 1868 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension 1869 ) 1870 /*++ 1871 1872 Routine Description: 1873 1874 get inquiry data from device and cache it into device extension 1875 The first INQUIRY command sent is with 0x24 bytes required data, 1876 as ATAport driver always sends this to enumerate devices and 0x24 1877 bytes is the minimum data device should return by spec. 1878 1879 Arguments: 1880 1881 DeviceExtension - device extension. 1882 1883 Return Value: 1884 1885 NTSTATUS. 1886 1887 --*/ 1888 { 1889 NTSTATUS status = STATUS_SUCCESS; 1890 SCSI_REQUEST_BLOCK srb = {0}; 1891 PCDB cdb = (PCDB)(&srb.Cdb); 1892 PINQUIRYDATA tmpInquiry = NULL; 1893 1894 // by spec, device should return at least 36 bytes. 1895 ULONG requestedInquiryTransferBytes = MINIMUM_CDROM_INQUIRY_SIZE; 1896 BOOLEAN needResendCommand = TRUE; 1897 BOOLEAN portDriverHack = FALSE; 1898 1899 // this ensures that the strings vendorID, productID, and firmwareRevision 1900 // are all available in the inquiry data. In addition, MMC spec requires 1901 // all type 5 devices to have minimum 36 bytes of inquiry. 1902 static const UCHAR minInquiryAdditionalLength = 1903 MINIMUM_CDROM_INQUIRY_SIZE - 1904 RTL_SIZEOF_THROUGH_FIELD(INQUIRYDATA, AdditionalLength); 1905 1906 C_ASSERT( RTL_SIZEOF_THROUGH_FIELD(INQUIRYDATA, AdditionalLength) <= 8 ); 1907 C_ASSERT( RTL_SIZEOF_THROUGH_FIELD(INQUIRYDATA, ProductRevisionLevel) == MINIMUM_CDROM_INQUIRY_SIZE ); 1908 1909 PAGED_CODE(); 1910 1911 // short-circuit here for if already cached for this device 1912 // required to avoid use of scratch buffer after initialization 1913 // of MCN code. 1914 if (DeviceExtension->DeviceAdditionalData.CachedInquiryData != NULL) 1915 { 1916 NT_ASSERT(DeviceExtension->DeviceAdditionalData.CachedInquiryDataByteCount != 0); 1917 return STATUS_SUCCESS; 1918 } 1919 1920 // 1. retrieve the inquiry data length 1921 1922 // 1.1 allocate inquiry data buffer 1923 tmpInquiry = ExAllocatePoolWithTag(NonPagedPoolNx, 1924 requestedInquiryTransferBytes, 1925 CDROM_TAG_INQUIRY); 1926 if (tmpInquiry == NULL) 1927 { 1928 status = STATUS_INSUFFICIENT_RESOURCES; 1929 } 1930 1931 // 1.2 send INQUIRY command 1932 if (NT_SUCCESS(status)) 1933 { 1934 srb.CdbLength = 6; 1935 cdb->AsByte[0] = SCSIOP_INQUIRY; 1936 cdb->AsByte[3] = (UCHAR)( requestedInquiryTransferBytes >> (8*1) ); 1937 cdb->AsByte[4] = (UCHAR)( requestedInquiryTransferBytes >> (8*0) ); 1938 1939 status = DeviceSendSrbSynchronously(DeviceExtension->Device, 1940 &srb, 1941 tmpInquiry, 1942 requestedInquiryTransferBytes, 1943 FALSE, 1944 NULL); 1945 } 1946 1947 // 1.3 get required data length 1948 if (NT_SUCCESS(status)) 1949 { 1950 if ((requestedInquiryTransferBytes == srb.DataTransferLength) && 1951 (requestedInquiryTransferBytes == (tmpInquiry->AdditionalLength + 1952 RTL_SIZEOF_THROUGH_FIELD(INQUIRYDATA, AdditionalLength))) ) 1953 { 1954 // device has only 36 bytes of INQUIRY data. do not need to resend the command. 1955 needResendCommand = FALSE; 1956 } 1957 else 1958 { 1959 // workaround an ATAPI.SYS bug where additional length field is set to zero 1960 if (tmpInquiry->AdditionalLength == 0) 1961 { 1962 tmpInquiry->AdditionalLength = minInquiryAdditionalLength; 1963 portDriverHack = TRUE; 1964 } 1965 1966 requestedInquiryTransferBytes = 1967 tmpInquiry->AdditionalLength + 1968 RTL_SIZEOF_THROUGH_FIELD(INQUIRYDATA, AdditionalLength); 1969 1970 if (requestedInquiryTransferBytes >= MINIMUM_CDROM_INQUIRY_SIZE) 1971 { 1972 needResendCommand = TRUE; 1973 } 1974 else 1975 { 1976 needResendCommand = FALSE; 1977 //Length is small than minimum length, error out. 1978 status = STATUS_DEVICE_PROTOCOL_ERROR; 1979 } 1980 } 1981 } 1982 1983 // 2. retrieve the inquiry data if still needed. 1984 1985 // 2.1 Clean up. 1986 if (NT_SUCCESS(status) && needResendCommand) 1987 { 1988 FREE_POOL(tmpInquiry); 1989 RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK)); 1990 1991 tmpInquiry = ExAllocatePoolWithTag(NonPagedPoolNx, 1992 requestedInquiryTransferBytes, 1993 CDROM_TAG_INQUIRY); 1994 if (tmpInquiry == NULL) 1995 { 1996 status = STATUS_INSUFFICIENT_RESOURCES; 1997 } 1998 } 1999 2000 // 2.2 resend INQUIRY command 2001 if (NT_SUCCESS(status) && needResendCommand) 2002 { 2003 srb.CdbLength = 6; 2004 cdb->AsByte[0] = SCSIOP_INQUIRY; 2005 cdb->AsByte[3] = (UCHAR)( requestedInquiryTransferBytes >> (8*1) ); 2006 cdb->AsByte[4] = (UCHAR)( requestedInquiryTransferBytes >> (8*0) ); 2007 2008 status = DeviceSendSrbSynchronously( DeviceExtension->Device, 2009 &srb, 2010 tmpInquiry, 2011 requestedInquiryTransferBytes, 2012 FALSE, 2013 NULL); 2014 2015 if (!NT_SUCCESS(status)) 2016 { 2017 // Workaround for drive reports that it has more INQUIRY data than reality. 2018 if ((srb.SrbStatus == SRB_STATUS_DATA_OVERRUN) && 2019 (srb.DataTransferLength < requestedInquiryTransferBytes) && 2020 (srb.DataTransferLength >= MINIMUM_CDROM_INQUIRY_SIZE)) 2021 { 2022 //Port driver says buffer size mismatch (buffer underrun), 2023 //retry with the real buffer size it could return. 2024 requestedInquiryTransferBytes = srb.DataTransferLength; 2025 2026 FREE_POOL(tmpInquiry); 2027 RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK)); 2028 2029 tmpInquiry = ExAllocatePoolWithTag(NonPagedPoolNx, 2030 requestedInquiryTransferBytes, 2031 CDROM_TAG_INQUIRY); 2032 if (tmpInquiry == NULL) 2033 { 2034 status = STATUS_INSUFFICIENT_RESOURCES; 2035 } 2036 else 2037 { 2038 srb.CdbLength = 6; 2039 cdb->AsByte[0] = SCSIOP_INQUIRY; 2040 cdb->AsByte[3] = (UCHAR)( requestedInquiryTransferBytes >> (8*1) ); 2041 cdb->AsByte[4] = (UCHAR)( requestedInquiryTransferBytes >> (8*0) ); 2042 2043 status = DeviceSendSrbSynchronously(DeviceExtension->Device, 2044 &srb, 2045 tmpInquiry, 2046 requestedInquiryTransferBytes, 2047 FALSE, 2048 NULL); 2049 } 2050 } 2051 } 2052 2053 //Check the transferred data length for safe. 2054 if (NT_SUCCESS(status)) 2055 { 2056 requestedInquiryTransferBytes = srb.DataTransferLength; 2057 2058 if (requestedInquiryTransferBytes < MINIMUM_CDROM_INQUIRY_SIZE) 2059 { 2060 // should never occur 2061 status = STATUS_DEVICE_PROTOCOL_ERROR; 2062 } 2063 } 2064 2065 // ensure we got some non-zero data.... 2066 // This is done so we don't accidentally work around the 2067 // ATAPI.SYS bug when no data was transferred. 2068 if (NT_SUCCESS(status) && portDriverHack) 2069 { 2070 PULONG tmp = (PULONG)tmpInquiry; 2071 ULONG i = MINIMUM_CDROM_INQUIRY_SIZE / sizeof(ULONG); 2072 C_ASSERT( RTL_SIZEOF_THROUGH_FIELD(INQUIRYDATA, ProductRevisionLevel) % sizeof(ULONG) == 0 ); 2073 2074 // wouldn't you know it -- there is no RtlIsMemoryZero() function; Make one up. 2075 for ( ; i != 0; i--) 2076 { 2077 if (*tmp != 0) 2078 { 2079 break; // out of this inner FOR loop -- guarantees 'i != 0' 2080 } 2081 tmp++; 2082 } 2083 2084 if (i == 0) // all loop'd successfully 2085 { 2086 // should never occur to successfully get all zero'd data 2087 status = STATUS_DEVICE_PROTOCOL_ERROR; 2088 } 2089 } 2090 } 2091 2092 // if everything succeeded, then (and only then) modify the device extension 2093 if (NT_SUCCESS(status)) 2094 { 2095 DeviceExtension->DeviceAdditionalData.CachedInquiryData = tmpInquiry; 2096 DeviceExtension->DeviceAdditionalData.CachedInquiryDataByteCount = requestedInquiryTransferBytes; 2097 } 2098 else 2099 { 2100 FREE_POOL(tmpInquiry); 2101 } 2102 2103 return status; 2104 } 2105 2106 _IRQL_requires_max_(PASSIVE_LEVEL) 2107 NTSTATUS 2108 DeviceGetMmcSupportInfo( 2109 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, 2110 _Out_ PBOOLEAN IsMmcDevice 2111 ) 2112 /*++ 2113 2114 Routine Description: 2115 2116 check if the device is MMC capable. 2117 2118 Arguments: 2119 2120 DeviceExtension - device extension. 2121 2122 Return Value: 2123 2124 NTSTATUS. 2125 IsMmcDevice - TRUE (MMC capable); FALSE (not MMC device) 2126 2127 --*/ 2128 { 2129 NTSTATUS status; 2130 ULONG size; 2131 ULONG previouslyFailed; 2132 2133 PAGED_CODE(); 2134 2135 *IsMmcDevice = FALSE; 2136 2137 // read the registry in case the drive failed previously, 2138 // and a timeout is occurring. 2139 previouslyFailed = FALSE; 2140 DeviceGetParameter(DeviceExtension, 2141 CDROM_SUBKEY_NAME, 2142 CDROM_NON_MMC_DRIVE_NAME, 2143 &previouslyFailed); 2144 2145 if (previouslyFailed) 2146 { 2147 SET_FLAG(DeviceExtension->DeviceAdditionalData.HackFlags, CDROM_HACK_BAD_GET_CONFIG_SUPPORT); 2148 } 2149 2150 // read from the registry in case the drive reports bad profile lengths 2151 previouslyFailed = FALSE; 2152 DeviceGetParameter(DeviceExtension, 2153 CDROM_SUBKEY_NAME, 2154 CDROM_NON_MMC_VENDOR_SPECIFIC_PROFILE, 2155 &previouslyFailed); 2156 2157 if (previouslyFailed) 2158 { 2159 SET_FLAG(DeviceExtension->DeviceAdditionalData.HackFlags, CDROM_HACK_BAD_VENDOR_PROFILES); 2160 } 2161 2162 // check for the ProfileList feature to determine if the drive is MMC compliant 2163 // and set the "size" local variable to total GetConfig data size available. 2164 // NOTE: This will exit this function in some error paths. 2165 { 2166 GET_CONFIGURATION_HEADER localHeader = {0}; 2167 ULONG usable = 0; 2168 2169 status = DeviceGetConfiguration(DeviceExtension->Device, 2170 &localHeader, 2171 sizeof(GET_CONFIGURATION_HEADER), 2172 &usable, 2173 FeatureProfileList, 2174 SCSI_GET_CONFIGURATION_REQUEST_TYPE_ALL); 2175 2176 if (status == STATUS_INVALID_DEVICE_REQUEST || 2177 status == STATUS_NO_MEDIA_IN_DEVICE || 2178 status == STATUS_IO_DEVICE_ERROR || 2179 status == STATUS_IO_TIMEOUT) 2180 { 2181 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT, 2182 "GetConfiguration Failed (%x), device %p not mmc-compliant\n", 2183 status, DeviceExtension->DeviceObject 2184 )); 2185 2186 previouslyFailed = TRUE; 2187 DeviceSetParameter( DeviceExtension, 2188 CDROM_SUBKEY_NAME, 2189 CDROM_NON_MMC_DRIVE_NAME, 2190 previouslyFailed); 2191 2192 return STATUS_SUCCESS; 2193 } 2194 else if (!NT_SUCCESS(status)) 2195 { 2196 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT, 2197 "GetConfiguration Failed, status %x -- defaulting to -ROM\n", 2198 status)); 2199 2200 return STATUS_SUCCESS; 2201 } 2202 else if (usable < sizeof(GET_CONFIGURATION_HEADER)) 2203 { 2204 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT, 2205 "GetConfiguration Failed, returned only %x bytes!\n", usable)); 2206 previouslyFailed = TRUE; 2207 DeviceSetParameter( DeviceExtension, 2208 CDROM_SUBKEY_NAME, 2209 CDROM_NON_MMC_DRIVE_NAME, 2210 previouslyFailed); 2211 2212 return STATUS_SUCCESS; 2213 } 2214 2215 size = (localHeader.DataLength[0] << 24) | 2216 (localHeader.DataLength[1] << 16) | 2217 (localHeader.DataLength[2] << 8) | 2218 (localHeader.DataLength[3] << 0) ; 2219 2220 2221 if ((size <= 4) || (size + 4 < size)) 2222 { 2223 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT, 2224 "GetConfiguration Failed, claims MMC support but doesn't " 2225 "correctly return config length! (%x)\n", 2226 size 2227 )); 2228 previouslyFailed = TRUE; 2229 DeviceSetParameter( DeviceExtension, 2230 CDROM_SUBKEY_NAME, 2231 CDROM_NON_MMC_DRIVE_NAME, 2232 previouslyFailed); 2233 2234 return STATUS_SUCCESS; 2235 } 2236 else if ((size % 4) != 0) 2237 { 2238 if ((size % 2) != 0) 2239 { 2240 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT, 2241 "GetConfiguration Failed, returned odd number of bytes %x!\n", 2242 size 2243 )); 2244 previouslyFailed = TRUE; 2245 DeviceSetParameter( DeviceExtension, 2246 CDROM_SUBKEY_NAME, 2247 CDROM_NON_MMC_DRIVE_NAME, 2248 previouslyFailed); 2249 2250 return STATUS_SUCCESS; 2251 } 2252 else 2253 { 2254 if (TEST_FLAG(DeviceExtension->DeviceAdditionalData.HackFlags, CDROM_HACK_BAD_VENDOR_PROFILES)) 2255 { 2256 // we've already caught this and ASSERT'd once, so don't do it again 2257 } 2258 else 2259 { 2260 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT, 2261 "GetConfiguration returned a size that is not per spec (%x bytes), this is probably because of a vendor specific data header with a size not divisible by 4.\n", 2262 size 2263 )); 2264 previouslyFailed = TRUE; 2265 DeviceSetParameter(DeviceExtension, 2266 CDROM_SUBKEY_NAME, 2267 CDROM_NON_MMC_VENDOR_SPECIFIC_PROFILE, 2268 previouslyFailed); 2269 } 2270 } 2271 } 2272 2273 size += 4; // sizeof the datalength fields 2274 } 2275 2276 *IsMmcDevice = TRUE; 2277 2278 // This code doesn't handle total get config size over 64k 2279 NT_ASSERT( size <= MAXUSHORT ); 2280 2281 // Check for SCSI_GET_CONFIGURATION_REQUEST_TYPE_ONE support in the device. 2282 // NOTE: This will exit this function in some error paths. 2283 { 2284 ULONG featureSize = sizeof(GET_CONFIGURATION_HEADER)+sizeof(FEATURE_HEADER); 2285 ULONG usable = 0; 2286 2287 PGET_CONFIGURATION_HEADER configBuffer = ExAllocatePoolWithTag( 2288 NonPagedPoolNx, 2289 featureSize, 2290 CDROM_TAG_GET_CONFIG); 2291 2292 if (configBuffer == NULL) 2293 { 2294 return STATUS_INSUFFICIENT_RESOURCES; 2295 } 2296 2297 // read the registry in case the drive failed previously, 2298 // and a timeout is occurring. 2299 previouslyFailed = FALSE; 2300 DeviceGetParameter( DeviceExtension, 2301 CDROM_SUBKEY_NAME, 2302 CDROM_TYPE_ONE_GET_CONFIG_NAME, 2303 &previouslyFailed); 2304 2305 if (previouslyFailed) 2306 { 2307 SET_FLAG(DeviceExtension->DeviceAdditionalData.HackFlags, CDROM_HACK_BAD_TYPE_ONE_GET_CONFIG); 2308 FREE_POOL(configBuffer); 2309 return STATUS_SUCCESS; 2310 } 2311 2312 // Get only the config and feature header 2313 status = DeviceGetConfiguration(DeviceExtension->Device, 2314 configBuffer, 2315 featureSize, 2316 &usable, 2317 FeatureProfileList, 2318 SCSI_GET_CONFIGURATION_REQUEST_TYPE_ONE); 2319 2320 if (!NT_SUCCESS(status) || (usable < featureSize)) 2321 { 2322 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT, 2323 "Type One GetConfiguration Failed. Usable buffer size: %d\n", usable)); 2324 previouslyFailed = TRUE; 2325 } 2326 else 2327 { 2328 PFEATURE_HEADER featureHeader; 2329 ULONG totalAvailableBytes = 0; 2330 ULONG expectedAvailableBytes = 0; 2331 2332 REVERSE_BYTES(&totalAvailableBytes, configBuffer->DataLength); 2333 totalAvailableBytes += RTL_SIZEOF_THROUGH_FIELD(GET_CONFIGURATION_HEADER, DataLength); 2334 2335 featureHeader = (PFEATURE_HEADER) ((PUCHAR)configBuffer + sizeof(GET_CONFIGURATION_HEADER)); 2336 expectedAvailableBytes = sizeof(GET_CONFIGURATION_HEADER) + 2337 sizeof(FEATURE_HEADER) + 2338 featureHeader->AdditionalLength; 2339 2340 if (totalAvailableBytes > expectedAvailableBytes) 2341 { 2342 // Device is returning more than required size. Most likely the device 2343 // is returning TYPE ALL data. Set the flag to use TYPE ALL for TYPE ONE 2344 // requets 2345 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT, 2346 "Type One GetConfiguration Failed. " 2347 "Device returned %d bytes instead of %d bytes\n", 2348 size, featureSize)); 2349 2350 previouslyFailed = TRUE; 2351 } 2352 } 2353 2354 FREE_POOL(configBuffer); 2355 2356 if (previouslyFailed == TRUE) 2357 { 2358 DeviceSetParameter( DeviceExtension, 2359 CDROM_SUBKEY_NAME, 2360 CDROM_TYPE_ONE_GET_CONFIG_NAME, 2361 previouslyFailed); 2362 2363 SET_FLAG(DeviceExtension->DeviceAdditionalData.HackFlags, CDROM_HACK_BAD_TYPE_ONE_GET_CONFIG); 2364 } 2365 } 2366 2367 return status; 2368 } 2369 2370 2371 _IRQL_requires_max_(APC_LEVEL) 2372 NTSTATUS 2373 DeviceSetRawReadInfo( 2374 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension 2375 ) 2376 /*++ 2377 2378 Routine Description: 2379 2380 This routine reads the CDROM capabilities mode page and save information 2381 in the device extension needed for raw reads. 2382 NOTE: this function is only valid for MMC device 2383 2384 Arguments: 2385 2386 DeviceExtension - device context 2387 2388 Return Value: 2389 2390 NTSTATUS 2391 2392 --*/ 2393 { 2394 NTSTATUS status = STATUS_SUCCESS; 2395 PUCHAR buffer = NULL; 2396 ULONG count = 0; 2397 2398 PAGED_CODE(); 2399 2400 // Check whether the device can return C2 error flag bits and the block 2401 // error byte. If so, save this info and fill in appropriate flag during 2402 // raw read requests. 2403 2404 // Start by checking the GET_CONFIGURATION data 2405 { 2406 PFEATURE_DATA_CD_READ cdReadHeader = NULL; 2407 ULONG additionalLength = sizeof(FEATURE_DATA_CD_READ) - sizeof(FEATURE_HEADER); 2408 2409 cdReadHeader = DeviceFindFeaturePage(DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBuffer, 2410 DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBufferSize, 2411 FeatureCdRead); 2412 2413 if ((cdReadHeader != NULL) && 2414 (cdReadHeader->Header.AdditionalLength >= additionalLength) && 2415 (cdReadHeader->C2ErrorData) 2416 ) 2417 { 2418 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, 2419 "DeviceSetRawReadInfo: FDO %p GET_CONFIG shows ability to read C2 error bits\n", 2420 DeviceExtension->DeviceObject 2421 )); 2422 DeviceExtension->DeviceAdditionalData.Mmc.ReadCdC2Pointers = TRUE; // Device returns C2 error info. 2423 } 2424 } 2425 2426 // Unfortunately, the only way to check for the ability to read R-W subcode 2427 // information is via MODE_SENSE. Do so here, and check the C2 bit as well 2428 // in case the drive has a firmware bug where it fails to report this ability 2429 // in GET_CONFIG (which some drives do). 2430 for (count = 0; count < 6; count++) 2431 { 2432 SCSI_REQUEST_BLOCK srb = {0}; 2433 PCDB cdb = (PCDB)srb.Cdb; 2434 ULONG bufferLength = 0; 2435 2436 // Build the MODE SENSE CDB. Try 10-byte CDB first. 2437 if ((count/3) == 0) 2438 { 2439 bufferLength = sizeof(CDVD_CAPABILITIES_PAGE) + 2440 sizeof(MODE_PARAMETER_HEADER10) + 2441 sizeof(MODE_PARAMETER_BLOCK); 2442 2443 cdb->MODE_SENSE10.OperationCode = SCSIOP_MODE_SENSE10; 2444 cdb->MODE_SENSE10.Dbd = 1; 2445 cdb->MODE_SENSE10.PageCode = MODE_PAGE_CAPABILITIES; 2446 cdb->MODE_SENSE10.AllocationLength[0] = (UCHAR)(bufferLength >> 8); 2447 cdb->MODE_SENSE10.AllocationLength[1] = (UCHAR)(bufferLength >> 0); 2448 srb.CdbLength = 10; 2449 } 2450 else 2451 { 2452 bufferLength = sizeof(CDVD_CAPABILITIES_PAGE) + 2453 sizeof(MODE_PARAMETER_HEADER) + 2454 sizeof(MODE_PARAMETER_BLOCK); 2455 2456 cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE; 2457 cdb->MODE_SENSE.Dbd = 1; 2458 cdb->MODE_SENSE.PageCode = MODE_PAGE_CAPABILITIES; 2459 cdb->MODE_SENSE.AllocationLength = (UCHAR)bufferLength; 2460 srb.CdbLength = 6; 2461 } 2462 2463 // Set timeout value from device extension. 2464 srb.TimeOutValue = DeviceExtension->TimeOutValue; 2465 2466 buffer = ExAllocatePoolWithTag(NonPagedPoolNx, bufferLength, CDROM_TAG_MODE_DATA); 2467 2468 if (buffer == NULL) 2469 { 2470 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, 2471 "DeviceSetRawReadInfo: cannot allocate " 2472 "buffer, so not setting raw read info for FDO %p\n", 2473 DeviceExtension->DeviceObject 2474 )); 2475 status = STATUS_INSUFFICIENT_RESOURCES; 2476 goto FnExit; 2477 } 2478 2479 RtlZeroMemory(buffer, bufferLength); 2480 2481 status = DeviceSendSrbSynchronously(DeviceExtension->Device, 2482 &srb, 2483 buffer, 2484 bufferLength, 2485 FALSE, 2486 NULL); 2487 2488 if (NT_SUCCESS(status) || 2489 (status == STATUS_DATA_OVERRUN) || 2490 (status == STATUS_BUFFER_OVERFLOW)) 2491 { 2492 PCDVD_CAPABILITIES_PAGE capabilities = NULL; 2493 2494 // determine where the capabilities page really is 2495 if ((count/3) == 0) 2496 { 2497 PMODE_PARAMETER_HEADER10 p = (PMODE_PARAMETER_HEADER10)buffer; 2498 capabilities = (PCDVD_CAPABILITIES_PAGE)(buffer + 2499 sizeof(MODE_PARAMETER_HEADER10) + 2500 (p->BlockDescriptorLength[0] * 256) + 2501 p->BlockDescriptorLength[1]); 2502 } 2503 else 2504 { 2505 PMODE_PARAMETER_HEADER p = (PMODE_PARAMETER_HEADER)buffer; 2506 capabilities = (PCDVD_CAPABILITIES_PAGE)(buffer + 2507 sizeof(MODE_PARAMETER_HEADER) + 2508 p->BlockDescriptorLength); 2509 } 2510 2511 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, 2512 "DeviceSetRawReadInfo: FDO %p CDVD Capabilities buffer %p\n", 2513 DeviceExtension->DeviceObject, 2514 buffer 2515 )); 2516 2517 if (capabilities->PageCode == MODE_PAGE_CAPABILITIES) 2518 { 2519 if (capabilities->C2Pointers) 2520 { 2521 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, 2522 "DeviceSetRawReadInfo: FDO %p supports C2 error bits in READ_CD command\n", 2523 DeviceExtension->DeviceObject 2524 )); 2525 DeviceExtension->DeviceAdditionalData.Mmc.ReadCdC2Pointers = TRUE; 2526 } 2527 2528 if (capabilities->RWSupported) 2529 { 2530 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, 2531 "DeviceSetRawReadInfo: FDO %p supports raw subcode in READ_CD command\n", 2532 DeviceExtension->DeviceObject 2533 )); 2534 DeviceExtension->DeviceAdditionalData.Mmc.ReadCdSubCode = TRUE; 2535 } 2536 2537 break; 2538 } 2539 } 2540 2541 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL, 2542 "DeviceSetRawReadInfo: FDO %p failed %x byte mode sense, status %x\n", 2543 DeviceExtension->DeviceObject, 2544 (((count/3) == 0) ? 10 : 6), 2545 status 2546 )); 2547 2548 FREE_POOL(buffer); 2549 } 2550 2551 if (count == 6) 2552 { 2553 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL, 2554 "DeviceSetRawReadInfo: FDO %p couldn't get mode sense data\n", 2555 DeviceExtension->DeviceObject 2556 )); 2557 } 2558 2559 FnExit: 2560 2561 if (buffer) 2562 { 2563 FREE_POOL(buffer); 2564 } 2565 2566 return status; 2567 } 2568 2569 2570 _IRQL_requires_max_(APC_LEVEL) 2571 NTSTATUS 2572 DeviceInitializeDvd( 2573 _In_ WDFDEVICE Device 2574 ) 2575 /*++ 2576 2577 Routine Description: 2578 2579 This routine sets the region of DVD drive 2580 NOTE: this routine uses ScratchBuffer, it must be called after ScratchBuffer allocated. 2581 2582 Arguments: 2583 2584 Device - device object 2585 2586 Return Value: 2587 2588 NTSTATUS 2589 2590 --*/ 2591 2592 { 2593 NTSTATUS status = STATUS_SUCCESS; 2594 PCDROM_DEVICE_EXTENSION deviceExtension; 2595 PDVD_COPY_PROTECT_KEY copyProtectKey = NULL; 2596 PDVD_RPC_KEY rpcKey = NULL; 2597 ULONG bufferLen = 0; 2598 size_t bytesReturned; 2599 2600 PAGED_CODE(); 2601 2602 deviceExtension = DeviceGetExtension(Device); 2603 2604 // check to see if we have a DVD device 2605 if (deviceExtension->DeviceAdditionalData.DriveDeviceType != FILE_DEVICE_DVD) 2606 { 2607 return STATUS_SUCCESS; 2608 } 2609 2610 // we got a DVD drive. 2611 bufferLen = DVD_RPC_KEY_LENGTH; 2612 copyProtectKey = (PDVD_COPY_PROTECT_KEY)ExAllocatePoolWithTag(PagedPool, 2613 bufferLen, 2614 DVD_TAG_RPC2_CHECK); 2615 2616 if (copyProtectKey == NULL) 2617 { 2618 return STATUS_INSUFFICIENT_RESOURCES; 2619 } 2620 2621 // get the device region 2622 RtlZeroMemory (copyProtectKey, bufferLen); 2623 copyProtectKey->KeyLength = DVD_RPC_KEY_LENGTH; 2624 copyProtectKey->KeyType = DvdGetRpcKey; 2625 2626 // perform IOCTL_DVD_READ_KEY 2627 status = DvdStartSessionReadKey(deviceExtension, 2628 IOCTL_DVD_READ_KEY, 2629 NULL, 2630 copyProtectKey, 2631 DVD_RPC_KEY_LENGTH, 2632 copyProtectKey, 2633 DVD_RPC_KEY_LENGTH, 2634 &bytesReturned); 2635 2636 if (NT_SUCCESS(status)) 2637 { 2638 rpcKey = (PDVD_RPC_KEY)copyProtectKey->KeyData; 2639 2640 // TypeCode of zero means that no region has been set. 2641 if (rpcKey->TypeCode == 0) 2642 { 2643 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_PNP, 2644 "DVD Initialize (%p): must choose DVD region\n", 2645 Device)); 2646 deviceExtension->DeviceAdditionalData.PickDvdRegion = 1; 2647 2648 // set the device region code to be the same as region code on media. 2649 if (deviceExtension->DeviceAdditionalData.Mmc.IsCssDvd) 2650 { 2651 DevicePickDvdRegion(Device); 2652 } 2653 } 2654 } 2655 2656 FREE_POOL(copyProtectKey); 2657 2658 // return status of IOCTL_DVD_READ_KEY will be ignored. 2659 return STATUS_SUCCESS; 2660 } 2661 2662 2663 #if (NTDDI_VERSION >= NTDDI_WIN8) 2664 _IRQL_requires_max_(PASSIVE_LEVEL) 2665 NTSTATUS 2666 DeviceIsPortable( 2667 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, 2668 _Out_ PBOOLEAN IsPortable 2669 ) 2670 /*++ 2671 2672 Routine Description: 2673 2674 This routine checks if the volume is on a portable storage device. 2675 2676 Arguments: 2677 2678 DeviceExtension - device context 2679 IsPortable - device is portable 2680 2681 Return Value: 2682 2683 NTSTATUS. 2684 2685 --*/ 2686 2687 { 2688 DEVPROP_BOOLEAN isInternal = DEVPROP_FALSE; 2689 BOOLEAN isPortable = FALSE; 2690 ULONG size = 0; 2691 NTSTATUS status = STATUS_SUCCESS; 2692 DEVPROPTYPE type = DEVPROP_TYPE_EMPTY; 2693 2694 PAGED_CODE(); 2695 2696 *IsPortable = FALSE; 2697 2698 // Check to see if the underlying device object is in local machine container 2699 status = IoGetDevicePropertyData(DeviceExtension->LowerPdo, 2700 &DEVPKEY_Device_InLocalMachineContainer, 2701 0, 2702 0, 2703 sizeof(isInternal), 2704 &isInternal, 2705 &size, 2706 &type); 2707 2708 if (!NT_SUCCESS(status)) 2709 { 2710 goto Cleanup; 2711 } 2712 2713 NT_ASSERT(size == sizeof(isInternal)); 2714 NT_ASSERT(type == DEVPROP_TYPE_BOOLEAN); 2715 2716 // Volume is hot-pluggable if the disk pdo container id differs from that of root device 2717 if (isInternal == DEVPROP_TRUE) 2718 { 2719 goto Cleanup; 2720 } 2721 2722 isPortable = TRUE; 2723 2724 // Examine the bus type to ensure that this really is a fixed device 2725 if (DeviceExtension->DeviceDescriptor->BusType == BusTypeFibre || 2726 DeviceExtension->DeviceDescriptor->BusType == BusTypeiScsi || 2727 DeviceExtension->DeviceDescriptor->BusType == BusTypeRAID) 2728 { 2729 isPortable = FALSE; 2730 } 2731 2732 *IsPortable = isPortable; 2733 2734 Cleanup: 2735 2736 return status; 2737 } 2738 #endif 2739 2740 2741 #pragma warning(pop) // un-sets any local warning changes 2742 2743