1 /*++ 2 3 Copyright (C) Microsoft Corporation. All rights reserved. 4 5 Module Name: 6 7 autorun.c 8 9 Abstract: 10 11 Code for support of media change detection in the cd/dvd driver 12 13 Environment: 14 15 kernel mode only 16 17 Notes: 18 19 20 Revision History: 21 22 --*/ 23 24 #include "stddef.h" 25 #include "string.h" 26 27 #include "ntddk.h" 28 #include "ntddstor.h" 29 #include "cdrom.h" 30 #include "mmc.h" 31 #include "ioctl.h" 32 33 #include "ntstrsafe.h" 34 35 #ifdef DEBUG_USE_WPP 36 #include "autorun.tmh" 37 #endif 38 39 #define GESN_TIMEOUT_VALUE (0x4) 40 #define GESN_BUFFER_SIZE (0x8) 41 #define GESN_DEVICE_BUSY_LOWER_THRESHOLD_100_MS (2) 42 43 #define MAXIMUM_IMMEDIATE_MCN_RETRIES (0x20) 44 #define MCN_REG_SUBKEY_NAME (L"MediaChangeNotification") 45 #define MCN_REG_AUTORUN_DISABLE_INSTANCE_NAME (L"AlwaysDisableMCN") 46 #define MCN_REG_AUTORUN_ENABLE_INSTANCE_NAME (L"AlwaysEnableMCN") 47 48 // 49 // Only send polling irp when device is fully powered up and a 50 // power down irp is not in progress. 51 // 52 // NOTE: This helps close a window in time where a polling irp could cause 53 // a drive to spin up right after it has powered down. The problem is 54 // that SCSIPORT, ATAPI and SBP2 will be in the process of powering 55 // down (which may take a few seconds), but won't know that. It would 56 // then get a polling irp which will be put into its queue since it 57 // the disk isn't powered down yet. Once the disk is powered down it 58 // will find the polling irp in the queue and then power up the 59 // device to do the poll. They do not want to check if the polling 60 // irp has the SRB_NO_KEEP_AWAKE flag here since it is in a critical 61 // path and would slow down all I/Os. A better way to fix this 62 // would be to serialize the polling and power down irps so that 63 // only one of them is sent to the device at a time. 64 // 65 66 _IRQL_requires_max_(PASSIVE_LEVEL) 67 BOOLEAN 68 DeviceIsMediaChangeDisabledDueToHardwareLimitation( 69 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension 70 ); 71 72 _IRQL_requires_max_(PASSIVE_LEVEL) 73 BOOLEAN 74 DeviceIsMediaChangeDisabledForClass( 75 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension 76 ); 77 78 _IRQL_requires_max_(PASSIVE_LEVEL) 79 NTSTATUS 80 DeviceMediaChangeDeviceInstanceOverride( 81 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, 82 _Out_ PBOOLEAN Enabled 83 ); 84 85 _IRQL_requires_max_(PASSIVE_LEVEL) 86 NTSTATUS 87 DeviceInitializeMcn( 88 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, 89 _In_ BOOLEAN AllowDriveToSleep 90 ); 91 92 _IRQL_requires_max_(PASSIVE_LEVEL) 93 NTSTATUS 94 DeviceInitializeGesn( 95 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension 96 ); 97 98 _IRQL_requires_max_(APC_LEVEL) 99 NTSTATUS 100 GesnDataInterpret( 101 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, 102 _In_ PNOTIFICATION_EVENT_STATUS_HEADER Header, 103 _Out_ PBOOLEAN ResendImmediately 104 ); 105 106 RTL_QUERY_REGISTRY_ROUTINE DeviceMediaChangeRegistryCallBack; 107 108 EVT_WDF_WORKITEM DeviceDisableGesn; 109 110 #if ALLOC_PRAGMA 111 112 #pragma alloc_text(PAGE, DeviceInitializeMediaChangeDetection) 113 #pragma alloc_text(PAGE, DeviceEnableMediaChangeDetection) 114 #pragma alloc_text(PAGE, DeviceDisableMediaChangeDetection) 115 #pragma alloc_text(PAGE, DeviceSendDelayedMediaChangeNotifications) 116 #pragma alloc_text(PAGE, DeviceReleaseMcnResources) 117 #pragma alloc_text(PAGE, DeviceMediaChangeRegistryCallBack) 118 #pragma alloc_text(PAGE, DeviceInitializeMcn) 119 #pragma alloc_text(PAGE, DeviceDisableGesn) 120 121 #pragma alloc_text(PAGE, DeviceIsMediaChangeDisabledDueToHardwareLimitation) 122 #pragma alloc_text(PAGE, DeviceMediaChangeDeviceInstanceOverride) 123 #pragma alloc_text(PAGE, DeviceIsMediaChangeDisabledForClass) 124 125 #pragma alloc_text(PAGE, DeviceDisableMainTimer) 126 127 #pragma alloc_text(PAGE, GesnDataInterpret) 128 129 #pragma alloc_text(PAGE, RequestSetupMcnRequest) 130 #pragma alloc_text(PAGE, RequestPostWorkMcnRequest) 131 #pragma alloc_text(PAGE, RequestSendMcnRequest) 132 133 // 134 // DeviceEnableMainTimer is called by EvtDeviceD0Entry which can't be made pageable 135 // so neither is DeviceEnableMainTimer 136 // 137 //#pragma alloc_text(PAGE, DeviceEnableMainTimer) 138 139 #pragma alloc_text(PAGE, DeviceInitializeGesn) 140 141 #pragma alloc_text(PAGE, RequestHandleMcnControl) 142 143 #endif 144 145 #pragma warning(push) 146 #pragma warning(disable:4152) // nonstandard extension, function/data pointer conversion in expression 147 148 149 _IRQL_requires_max_(APC_LEVEL) 150 NTSTATUS 151 GesnDataInterpret( 152 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, 153 _In_ PNOTIFICATION_EVENT_STATUS_HEADER Header, 154 _Out_ PBOOLEAN ResendImmediately 155 ) 156 /*++ 157 158 Routine Description: 159 160 This routine will interpret the data returned for a GESN command, and 161 (if appropriate) set the media change event, and broadcast the 162 appropriate events to user mode for applications who care. 163 164 Arguments: 165 166 DeviceExtension - the device extension 167 168 Header - the resulting data from a GESN event. 169 requires at least EIGHT valid bytes (header == 4, data == 4) 170 171 ResendImmediately - whether or not to immediately resend the request. 172 this should be FALSE if there was no event, FALSE if the reported 173 event was of the DEVICE BUSY class, else true. 174 175 Return Value: 176 177 STATUS_SUCCESS if successful, an error code otherwise 178 179 Notes: 180 181 DataBuffer must be at least four bytes of valid data (header == 4 bytes), 182 and have at least eight bytes of allocated memory (all events == 4 bytes). 183 184 The call to StartNextPacket may occur before this routine is completed. 185 the operational change notifications are informational in nature, and 186 while useful, are not neccessary to ensure proper operation. For example, 187 if the device morphs to no longer supporting WRITE commands, all further 188 write commands will fail. There exists a small timing window wherein 189 IOCTL_IS_DISK_WRITABLE may be called and get an incorrect response. If 190 a device supports software write protect, it is expected that the 191 application can handle such a case. 192 193 NOTE: perhaps setting the updaterequired byte to one should be done here. 194 if so, it relies upon the setting of a 32-byte value to be an atomic 195 operation. unfortunately, there is no simple way to notify a class driver 196 which wants to know that the device behavior requires updating. 197 198 Not ready events may be sent every second. For example, if we were 199 to minimize the number of asynchronous notifications, an application may 200 register just after a large busy time was reported. This would then 201 prevent the application from knowing the device was busy until some 202 arbitrarily chosen timeout has occurred. Also, the GESN request would 203 have to still occur, since it checks for non-busy events (such as user 204 keybutton presses and media change events) as well. The specification 205 states that the lower-numered events get reported first, so busy events, 206 while repeating, will only be reported when all other events have been 207 cleared from the device. 208 209 --*/ 210 { 211 NTSTATUS status = STATUS_SUCCESS; 212 PMEDIA_CHANGE_DETECTION_INFO info = DeviceExtension->MediaChangeDetectionInfo; 213 LONG dataLength = 0; 214 LONG requiredLength = 0; 215 BOOLEAN inHomePosition = FALSE; 216 217 PAGED_CODE(); 218 219 // note: don't allocate anything in this routine so that we can 220 // always just 'return'. 221 *ResendImmediately = FALSE; 222 223 if (Header->NEA) 224 { 225 return status; 226 } 227 if (Header->NotificationClass == NOTIFICATION_NO_CLASS_EVENTS) 228 { 229 return status; 230 } 231 232 // HACKHACK - REF #0001 233 // This loop is only taken initially, due to the inability to reliably 234 // auto-detect drives that report events correctly at boot. When we 235 // detect this behavior during the normal course of running, we will 236 // disable the hack, allowing more efficient use of the system. This 237 // should occur "nearly" instantly, as the drive should have multiple 238 // events queue'd (ie. power, morphing, media). 239 if (info->Gesn.HackEventMask) 240 { 241 // all events use the low four bytes of zero to indicate 242 // that there was no change in status. 243 UCHAR thisEvent = Header->ClassEventData[0] & 0xf; 244 UCHAR lowestSetBit; 245 UCHAR thisEventBit = (1 << Header->NotificationClass); 246 247 if (!TEST_FLAG(info->Gesn.EventMask, thisEventBit)) 248 { 249 // The drive is reporting an event that wasn't requested 250 return STATUS_DEVICE_PROTOCOL_ERROR; 251 } 252 253 // some bit magic here... this results in the lowest set bit only 254 lowestSetBit = info->Gesn.EventMask; 255 lowestSetBit &= (info->Gesn.EventMask - 1); 256 lowestSetBit ^= (info->Gesn.EventMask); 257 258 if (thisEventBit != lowestSetBit) 259 { 260 // HACKHACK - REF #0001 261 // the first time we ever see an event set that is not the lowest 262 // set bit in the request (iow, highest priority), we know that the 263 // hack is no longer required, as the device is ignoring "no change" 264 // events when a real event is waiting in the other requested queues. 265 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, 266 "GESN::NONE: Compliant drive found, " 267 "removing GESN hack (%x, %x)\n", 268 thisEventBit, info->Gesn.EventMask)); 269 270 info->Gesn.HackEventMask = FALSE; 271 } 272 else if (thisEvent == 0) // NOTIFICATION_*_EVENT_NO_CHANGE 273 { 274 // HACKHACK - REF #0001 275 // note: this hack prevents poorly implemented firmware from constantly 276 // returning "No Event". we do this by cycling through the 277 // supported list of events here. 278 SET_FLAG(info->Gesn.NoChangeEventMask, thisEventBit); 279 CLEAR_FLAG(info->Gesn.EventMask, thisEventBit); 280 281 // if we have cycled through all supported event types, then 282 // we need to reset the events we are asking about. else we 283 // want to resend this request immediately in case there was 284 // another event pending. 285 if (info->Gesn.EventMask == 0) 286 { 287 info->Gesn.EventMask = info->Gesn.NoChangeEventMask; 288 info->Gesn.NoChangeEventMask = 0; 289 } 290 else 291 { 292 *ResendImmediately = TRUE; 293 } 294 return status; 295 } 296 297 } // end if (info->Gesn.HackEventMask) 298 299 dataLength = (Header->EventDataLength[0] << 8) | 300 (Header->EventDataLength[1] & 0xff); 301 dataLength -= 2; 302 requiredLength = 4; // all events are four bytes 303 304 if (dataLength < requiredLength) 305 { 306 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN, 307 "error - GESN returned only %x bytes data for fdo %p\n", 308 dataLength, DeviceExtension->DeviceObject)); 309 310 return STATUS_DEVICE_PROTOCOL_ERROR; 311 } 312 313 if (dataLength > requiredLength) 314 { 315 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN, 316 "error - GESN returned too many (%x) bytes data for fdo %p\n", 317 dataLength, DeviceExtension->DeviceObject)); 318 } 319 320 if ((Header->ClassEventData[0] & 0xf) == 0) 321 { 322 // a zero event is a "no change event, so do not retry 323 return status; 324 } 325 326 // because a event other than "no change" occurred, 327 // we should immediately resend this request. 328 *ResendImmediately = TRUE; 329 330 switch (Header->NotificationClass) 331 { 332 333 case NOTIFICATION_OPERATIONAL_CHANGE_CLASS_EVENTS: // 0x01 334 { 335 PNOTIFICATION_OPERATIONAL_STATUS opChangeInfo = 336 (PNOTIFICATION_OPERATIONAL_STATUS)(Header->ClassEventData); 337 ULONG event; 338 339 if (opChangeInfo->OperationalEvent == NOTIFICATION_OPERATIONAL_EVENT_CHANGE_REQUESTED) 340 { 341 break; 342 } 343 344 event = (opChangeInfo->Operation[0] << 8) | 345 (opChangeInfo->Operation[1] ) ; 346 347 // Workaround some hardware that is buggy but prevalent in the market 348 // This hardware has the property that it will report OpChange events repeatedly, 349 // causing us to retry immediately so quickly that we will eventually disable 350 // GESN to prevent an infinite loop. 351 // (only one valid OpChange event type now, only two ever defined) 352 if (info->MediaChangeRetryCount >= 4) 353 { 354 // 355 // HACKHACK - REF #0002 356 // Some drives incorrectly report OpChange/Change (001b/0001h) events 357 // continuously when the tray has been ejected. This causes this routine 358 // to set ResendImmediately to "TRUE", and that results in our cycling 359 // 32 times immediately resending. At that point, we give up detecting 360 // the infinite retry loop, and disable GESN on these drives. This 361 // prevents Media Eject Request (from eject button) from being reported. 362 // Thus, instead we should attempt to workaround this issue by detecting 363 // this behavior. 364 // 365 366 static UCHAR const OpChangeMask = 0x02; 367 368 // At least one device reports "temporarily busy" (which is useless) on eject 369 // At least one device reports "OpChange" repeatedly when re-inserting media 370 // All seem to work well using this workaround 371 372 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_MCN, 373 "GESN OpChange events are broken. Working around this problem in software (for WDFDEVICE %p)\n", 374 DeviceExtension->Device)); 375 376 // OpChange is not the only bit set -- Media class is required.... 377 NT_ASSERT(CountOfSetBitsUChar(info->Gesn.EventMask) != 1); 378 379 // Force the use of the hackhack (ref #0001) to workaround the 380 // issue noted this hackhack (ref #0002). 381 SET_FLAG(info->Gesn.NoChangeEventMask, OpChangeMask); 382 CLEAR_FLAG(info->Gesn.EventMask, OpChangeMask); 383 info->Gesn.HackEventMask = TRUE; 384 385 // don't request the opChange event again. use the method 386 // defined by hackhack (ref #0001) as the workaround. 387 if (info->Gesn.EventMask == 0) 388 { 389 info->Gesn.EventMask = info->Gesn.NoChangeEventMask; 390 info->Gesn.NoChangeEventMask = 0; 391 *ResendImmediately = FALSE; 392 } 393 else 394 { 395 *ResendImmediately = TRUE; 396 } 397 398 break; 399 } 400 401 402 if ((event == NOTIFICATION_OPERATIONAL_OPCODE_FEATURE_ADDED) | 403 (event == NOTIFICATION_OPERATIONAL_OPCODE_FEATURE_CHANGE)) 404 { 405 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, 406 "GESN says features added/changed for WDFDEVICE %p\n", 407 DeviceExtension->Device)); 408 409 // don't notify that new media arrived, just set the 410 // DO_VERIFY to force a FS reload. 411 412 if (IsVolumeMounted(DeviceExtension->DeviceObject)) 413 { 414 SET_FLAG(DeviceExtension->DeviceObject->Flags, DO_VERIFY_VOLUME); 415 } 416 417 // Call error handler with 418 // a "fake" media change error in case it needs to update 419 // internal structures as though a media change occurred. 420 { 421 SCSI_REQUEST_BLOCK srb = {0}; 422 SENSE_DATA sense = {0}; 423 NTSTATUS tempStatus; 424 BOOLEAN retry; 425 426 tempStatus = STATUS_MEDIA_CHANGED; 427 retry = FALSE; 428 429 srb.CdbLength = 6; 430 srb.Length = sizeof(SCSI_REQUEST_BLOCK); 431 srb.SrbStatus = SRB_STATUS_AUTOSENSE_VALID | SRB_STATUS_ERROR; 432 srb.SenseInfoBuffer = &sense; 433 srb.SenseInfoBufferLength = sizeof(SENSE_DATA); 434 435 sense.AdditionalSenseLength = sizeof(SENSE_DATA) - 436 RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseLength); 437 438 sense.SenseKey = SCSI_SENSE_UNIT_ATTENTION; 439 sense.AdditionalSenseCode = SCSI_ADSENSE_MEDIUM_CHANGED; 440 441 if (DeviceExtension->DeviceAdditionalData.ErrorHandler) 442 { 443 DeviceExtension->DeviceAdditionalData.ErrorHandler(DeviceExtension, 444 &srb, 445 &tempStatus, 446 &retry); 447 } 448 } // end error handler 449 450 } 451 break; 452 } 453 454 case NOTIFICATION_EXTERNAL_REQUEST_CLASS_EVENTS: // 0x3 455 { 456 PNOTIFICATION_EXTERNAL_STATUS externalInfo = 457 (PNOTIFICATION_EXTERNAL_STATUS)(Header->ClassEventData); 458 DEVICE_EVENT_EXTERNAL_REQUEST externalData = {0}; 459 460 // unfortunately, due to time constraints, we will only notify 461 // about keys being pressed, and not released. this makes keys 462 // single-function, but simplifies the code significantly. 463 if (externalInfo->ExternalEvent != NOTIFICATION_EXTERNAL_EVENT_BUTTON_DOWN) 464 { 465 break; 466 } 467 468 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, 469 "GESN::EXTERNAL: Event: %x Status %x Req %x\n", 470 externalInfo->ExternalEvent, externalInfo->ExternalStatus, 471 (externalInfo->Request[0] << 8) | externalInfo->Request[1] 472 )); 473 474 externalData.Version = 1; 475 externalData.DeviceClass = 0; 476 externalData.ButtonStatus = externalInfo->ExternalEvent; 477 externalData.Request = (externalInfo->Request[0] << 8) | 478 (externalInfo->Request[1] & 0xff); 479 KeQuerySystemTime(&(externalData.SystemTime)); 480 externalData.SystemTime.QuadPart *= (LONGLONG)KeQueryTimeIncrement(); 481 482 DeviceSendNotification(DeviceExtension, 483 &GUID_IO_DEVICE_EXTERNAL_REQUEST, 484 sizeof(DEVICE_EVENT_EXTERNAL_REQUEST), 485 &externalData); 486 487 return status; 488 } 489 490 case NOTIFICATION_MEDIA_STATUS_CLASS_EVENTS: // 0x4 491 { 492 PNOTIFICATION_MEDIA_STATUS mediaInfo = 493 (PNOTIFICATION_MEDIA_STATUS)(Header->ClassEventData); 494 495 if ((mediaInfo->MediaEvent == NOTIFICATION_MEDIA_EVENT_NEW_MEDIA) || 496 (mediaInfo->MediaEvent == NOTIFICATION_MEDIA_EVENT_MEDIA_CHANGE)) 497 { 498 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, 499 "GESN::MEDIA ARRIVAL, Status %x\n", 500 mediaInfo->MediaStatus)); 501 502 if (IsVolumeMounted(DeviceExtension->DeviceObject)) 503 { 504 SET_FLAG(DeviceExtension->DeviceObject->Flags, DO_VERIFY_VOLUME); 505 } 506 DeviceSetMediaChangeStateEx(DeviceExtension, 507 MediaPresent, 508 NULL); 509 510 // If media is inserted into slot loading type, mark the device active 511 // to not power off. 512 if ((DeviceExtension->ZeroPowerODDInfo != NULL) && 513 (DeviceExtension->ZeroPowerODDInfo->LoadingMechanism == LOADING_MECHANISM_CADDY) && 514 (DeviceExtension->ZeroPowerODDInfo->Load == 0)) // Slot 515 { 516 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, 517 "GesnDataInterpret: MediaArrival event detected, device marked as active\n")); 518 519 DeviceMarkActive(DeviceExtension, TRUE, FALSE); 520 } 521 } 522 else if (mediaInfo->MediaEvent == NOTIFICATION_MEDIA_EVENT_MEDIA_REMOVAL) 523 { 524 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, 525 "GESN::MEDIA REMOVAL, Status %x\n", 526 mediaInfo->MediaStatus)); 527 528 DeviceSetMediaChangeStateEx(DeviceExtension, 529 MediaNotPresent, 530 NULL); 531 532 // If media is removed from slot loading type, start powering off the device 533 // if it is ZPODD capable. 534 if ((DeviceExtension->ZeroPowerODDInfo != NULL) && 535 (DeviceExtension->ZeroPowerODDInfo->LoadingMechanism == LOADING_MECHANISM_CADDY) && 536 (DeviceExtension->ZeroPowerODDInfo->Load == 0)) // Slot 537 { 538 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, 539 "GesnDataInterpret: MediaRemoval event detected, device marked as idle\n")); 540 541 DeviceMarkActive(DeviceExtension, FALSE, FALSE); 542 } 543 } 544 else if (mediaInfo->MediaEvent == NOTIFICATION_MEDIA_EVENT_EJECT_REQUEST) 545 { 546 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, 547 "GESN::MEDIA EJECTION, Status %x\n", 548 mediaInfo->MediaStatus)); 549 550 DeviceSendNotification(DeviceExtension, 551 &GUID_IO_MEDIA_EJECT_REQUEST, 552 0, 553 NULL); 554 } 555 556 break; 557 } 558 559 case NOTIFICATION_DEVICE_BUSY_CLASS_EVENTS: // lowest priority events... 560 { 561 PNOTIFICATION_BUSY_STATUS busyInfo = 562 (PNOTIFICATION_BUSY_STATUS)(Header->ClassEventData); 563 DEVICE_EVENT_BECOMING_READY busyData = {0}; 564 565 // else we want to report the approximated time till it's ready. 566 busyData.Version = 1; 567 busyData.Reason = busyInfo->DeviceBusyStatus; 568 busyData.Estimated100msToReady = (busyInfo->Time[0] << 8) | 569 (busyInfo->Time[1] & 0xff); 570 571 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, 572 "GESN::BUSY: Event: %x Status %x Time %x\n", 573 busyInfo->DeviceBusyEvent, busyInfo->DeviceBusyStatus, 574 busyData.Estimated100msToReady 575 )); 576 577 // Ignore the notification if the time is small 578 if (busyData.Estimated100msToReady >= GESN_DEVICE_BUSY_LOWER_THRESHOLD_100_MS) 579 { 580 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, 581 "GesnDataInterpret: media BECOMING_READY\n")); 582 583 DeviceSendNotification(DeviceExtension, 584 &GUID_IO_DEVICE_BECOMING_READY, 585 sizeof(DEVICE_EVENT_BECOMING_READY), 586 &busyData); 587 } 588 589 // If manual loading operation is observed for slot loading type, start powering off the device 590 // if it is ZPODD capable. 591 if ((DeviceExtension->ZeroPowerODDInfo != NULL) && 592 (DeviceExtension->ZeroPowerODDInfo->LoadingMechanism == LOADING_MECHANISM_TRAY) && 593 (DeviceExtension->ZeroPowerODDInfo->Load == 0) && // Drawer 594 (busyInfo->DeviceBusyEvent == NOTIFICATION_BUSY_EVENT_LO_CHANGE) && 595 (busyInfo->DeviceBusyStatus == NOTIFICATION_BUSY_STATUS_NO_EVENT)) 596 { 597 inHomePosition = DeviceZPODDIsInHomePosition(DeviceExtension); 598 599 if (inHomePosition == FALSE) 600 { 601 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, 602 "GesnDataInterpret: LoChange event detected, device marked as active\n")); 603 604 DeviceMarkActive(DeviceExtension, TRUE, FALSE); 605 } 606 else 607 { 608 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, 609 "GesnDataInterpret: LoChange event detected, device marked as idle\n")); 610 611 DeviceMarkActive(DeviceExtension, FALSE, FALSE); 612 } 613 } 614 615 break; 616 } 617 618 default: 619 { 620 break; 621 } 622 623 } // end switch on notification class 624 625 return status; 626 } 627 628 629 VOID 630 DeviceInternalSetMediaChangeState( 631 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, 632 _In_ MEDIA_CHANGE_DETECTION_STATE NewState, 633 _Inout_opt_ PMEDIA_CHANGE_DETECTION_STATE OldState 634 ) 635 /*++ 636 637 Routine Description: 638 639 This routine will (if appropriate) set the media change event for the 640 device. The event will be set if the media state is changed and 641 media change events are enabled. Otherwise the media state will be 642 tracked but the event will not be set. 643 644 This routine will lock out the other media change routines if possible 645 but if not a media change notification may be lost after the enable has 646 been completed. 647 648 Arguments: 649 650 DeviceExtension - the device extension 651 652 NewState - new state for setting 653 654 OldState - optional storage for the old state 655 656 Return Value: 657 658 none 659 660 --*/ 661 { 662 #if DBG 663 LPCSTR states[] = {"Unknown", "Present", "Not Present", "Unavailable"}; 664 #endif 665 MEDIA_CHANGE_DETECTION_STATE oldMediaState; 666 PMEDIA_CHANGE_DETECTION_INFO info = DeviceExtension->MediaChangeDetectionInfo; 667 CLASS_MEDIA_CHANGE_CONTEXT mcnContext; 668 669 if (!((NewState >= MediaUnknown) && (NewState <= MediaUnavailable))) 670 { 671 return; 672 } 673 674 if (info == NULL) 675 { 676 return; 677 } 678 679 oldMediaState = info->LastKnownMediaDetectionState; 680 if (OldState) 681 { 682 *OldState = oldMediaState; 683 } 684 685 info->LastKnownMediaDetectionState = NewState; 686 687 // Increment MediaChangeCount on transition to MediaPresent 688 if (NewState == MediaPresent && oldMediaState != NewState) 689 { 690 InterlockedIncrement((PLONG)&DeviceExtension->MediaChangeCount); 691 } 692 693 if (info->MediaChangeDetectionDisableCount != 0) 694 { 695 #if DBG 696 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, 697 "DeviceInternalSetMediaChangeState: MCN not enabled, state " 698 "changed from %s to %s\n", 699 states[oldMediaState], states[NewState])); 700 #endif 701 return; 702 } 703 #if DBG 704 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, 705 "DeviceInternalSetMediaChangeState: State change from %s to %s\n", 706 states[oldMediaState], states[NewState])); 707 #endif 708 709 if (info->LastReportedMediaDetectionState == info->LastKnownMediaDetectionState) 710 { 711 // Media is in the same state as we reported last time, no need to report again. 712 return; 713 } 714 715 // make the data useful -- it used to always be zero. 716 mcnContext.MediaChangeCount = DeviceExtension->MediaChangeCount; 717 mcnContext.NewState = NewState; 718 719 if (NewState == MediaPresent) 720 { 721 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, 722 "DeviceInternalSetMediaChangeState: Reporting media ARRIVAL\n")); 723 724 DeviceSendNotification(DeviceExtension, 725 &GUID_IO_MEDIA_ARRIVAL, 726 sizeof(CLASS_MEDIA_CHANGE_CONTEXT), 727 &mcnContext); 728 } 729 else if ((NewState == MediaNotPresent) || (NewState == MediaUnavailable)) 730 { 731 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, 732 "DeviceInternalSetMediaChangeState: Reporting media REMOVAL\n")); 733 DeviceSendNotification(DeviceExtension, 734 &GUID_IO_MEDIA_REMOVAL, 735 sizeof(CLASS_MEDIA_CHANGE_CONTEXT), 736 &mcnContext); 737 } 738 else 739 { 740 // Don't notify of changed going to unknown. 741 return; 742 } 743 744 info->LastReportedMediaDetectionState = info->LastKnownMediaDetectionState; 745 746 return; 747 } // end DeviceInternalSetMediaChangeState() 748 749 750 VOID 751 DeviceSetMediaChangeStateEx( 752 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, 753 _In_ MEDIA_CHANGE_DETECTION_STATE NewState, 754 _Inout_opt_ PMEDIA_CHANGE_DETECTION_STATE OldState 755 ) 756 /*++ 757 758 Routine Description: 759 760 This routine will (if appropriate) set the media change event for the 761 device. The event will be set if the media state is changed and 762 media change events are enabled. Otherwise the media state will be 763 tracked but the event will not be set. 764 765 Arguments: 766 767 DeviceExtension - the device extension 768 769 NewState - new state for setting 770 771 OldState - optional storage for the old state 772 773 Return Value: 774 775 none 776 777 --*/ 778 { 779 PMEDIA_CHANGE_DETECTION_INFO info = DeviceExtension->MediaChangeDetectionInfo; 780 LARGE_INTEGER zero; 781 NTSTATUS status; 782 783 // timeout value must be 0, as this function can be called at DISPATCH_LEVEL. 784 zero.QuadPart = 0; 785 786 if (info == NULL) 787 { 788 return; 789 } 790 791 status = KeWaitForMutexObject(&info->MediaChangeMutex, 792 Executive, 793 KernelMode, 794 FALSE, 795 &zero); 796 797 if (status == STATUS_TIMEOUT) 798 { 799 // Someone else is in the process of setting the media state. 800 return; 801 } 802 803 // Change the media present state and signal an event, if applicable 804 DeviceInternalSetMediaChangeState(DeviceExtension, NewState, OldState); 805 806 KeReleaseMutex(&info->MediaChangeMutex, FALSE); 807 808 return; 809 } // end DeviceSetMediaChangeStateEx() 810 811 812 _IRQL_requires_max_(APC_LEVEL) 813 VOID 814 DeviceSendDelayedMediaChangeNotifications( 815 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension 816 ) 817 /*++ 818 819 Routine Description: 820 821 This routine sends the so-called delayed media change notifications. 822 These notifications get accumulated while the MCN mechanism is disabled 823 and need to be sent to the application on MCN enabling, if MCN enabling 824 happens as a part of exclusive access unlock and the application has not 825 requested us explicitly to not send the delayed notifications. 826 827 Arguments: 828 829 DeviceExtension - the device extension 830 831 Return Value: 832 833 none 834 835 --*/ 836 { 837 PMEDIA_CHANGE_DETECTION_INFO info = DeviceExtension->MediaChangeDetectionInfo; 838 LARGE_INTEGER zero; 839 NTSTATUS status; 840 841 PAGED_CODE(); 842 843 zero.QuadPart = 0; 844 845 if (info == NULL) 846 { 847 return; 848 } 849 850 status = KeWaitForMutexObject(&info->MediaChangeMutex, 851 Executive, 852 KernelMode, 853 FALSE, 854 &zero); 855 856 if (status == STATUS_TIMEOUT) 857 { 858 // Someone else is in the process of setting the media state. 859 // That's totally okay, we'll send delayed notifications later. 860 return; 861 } 862 863 // If the last reported state and the last known state are different and 864 // MCN is enabled, generate a notification based on the last known state. 865 if ((info->LastKnownMediaDetectionState != info->LastReportedMediaDetectionState) && 866 (info->MediaChangeDetectionDisableCount == 0)) 867 { 868 DeviceInternalSetMediaChangeState(DeviceExtension, info->LastKnownMediaDetectionState, NULL); 869 } 870 871 KeReleaseMutex(&info->MediaChangeMutex, FALSE); 872 873 return; 874 } 875 876 877 _IRQL_requires_max_(APC_LEVEL) 878 NTSTATUS 879 RequestSetupMcnRequest( 880 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, 881 _In_ BOOLEAN UseGesn 882 ) 883 /*++ 884 885 Routine Description: 886 887 This routine sets up the fields of the request for MCN 888 889 Arguments: 890 DeviceExtension - device context 891 892 UseGesn - If TRUE, the device supports GESN and it's currently the mechanism for MCN 893 894 Return Value: 895 NTSTATUS 896 897 --*/ 898 { 899 NTSTATUS status = STATUS_SUCCESS; 900 PSCSI_REQUEST_BLOCK srb; 901 PIRP irp; 902 PIO_STACK_LOCATION nextIrpStack; 903 PCDB cdb; 904 PVOID buffer; 905 WDF_REQUEST_REUSE_PARAMS params; 906 PMEDIA_CHANGE_DETECTION_INFO info = DeviceExtension->MediaChangeDetectionInfo; 907 908 PAGED_CODE(); 909 910 irp = WdfRequestWdmGetIrp(info->MediaChangeRequest); 911 NT_ASSERT(irp != NULL); 912 913 // deassign the MdlAddress, this is the value we assign explicitly. 914 // this is to prevent WdfRequestReuse to release the Mdl unexpectly. 915 if (irp->MdlAddress) 916 { 917 irp->MdlAddress = NULL; 918 } 919 920 if (NT_SUCCESS(status)) 921 { 922 // Setup the IRP to perform a test unit ready. 923 WDF_REQUEST_REUSE_PARAMS_INIT(¶ms, 924 WDF_REQUEST_REUSE_NO_FLAGS, 925 STATUS_NOT_SUPPORTED); 926 927 status = WdfRequestReuse(info->MediaChangeRequest, ¶ms); 928 } 929 930 if (NT_SUCCESS(status)) 931 { 932 // Format the request. 933 status = WdfIoTargetFormatRequestForInternalIoctlOthers(DeviceExtension->IoTarget, 934 info->MediaChangeRequest, 935 IOCTL_SCSI_EXECUTE_IN, 936 NULL, NULL, 937 NULL, NULL, 938 NULL, NULL); 939 940 if (!NT_SUCCESS(status)) 941 { 942 TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, 943 "RequestSetupMcnRequest: WdfIoTargetFormatRequestForInternalIoctlOthers failed, %!STATUS!\n", 944 status)); 945 } 946 } 947 948 if (NT_SUCCESS(status)) 949 { 950 RequestClearSendTime(info->MediaChangeRequest); 951 952 nextIrpStack = IoGetNextIrpStackLocation(irp); 953 954 nextIrpStack->Flags = SL_OVERRIDE_VERIFY_VOLUME; 955 nextIrpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; 956 nextIrpStack->Parameters.Scsi.Srb = &(info->MediaChangeSrb); 957 958 // Prepare the SRB for execution. 959 srb = nextIrpStack->Parameters.Scsi.Srb; 960 buffer = info->SenseBuffer; 961 RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK)); 962 RtlZeroMemory(buffer, SENSE_BUFFER_SIZE); 963 964 srb->QueueTag = SP_UNTAGGED; 965 srb->QueueAction = SRB_SIMPLE_TAG_REQUEST; 966 srb->Length = sizeof(SCSI_REQUEST_BLOCK); 967 srb->Function = SRB_FUNCTION_EXECUTE_SCSI; 968 srb->SenseInfoBuffer = buffer; 969 srb->SrbStatus = 0; 970 srb->ScsiStatus = 0; 971 srb->OriginalRequest = irp; 972 srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE; 973 974 srb->SrbFlags = DeviceExtension->SrbFlags; 975 SET_FLAG(srb->SrbFlags, info->SrbFlags); 976 977 if (!UseGesn) 978 { 979 srb->TimeOutValue = CDROM_TEST_UNIT_READY_TIMEOUT; 980 srb->CdbLength = 6; 981 srb->DataTransferLength = 0; 982 SET_FLAG(srb->SrbFlags, SRB_FLAGS_NO_DATA_TRANSFER); 983 nextIrpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_NONE; 984 srb->DataBuffer = NULL; 985 srb->DataTransferLength = 0; 986 irp->MdlAddress = NULL; 987 988 cdb = (PCDB) &srb->Cdb[0]; 989 cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY; 990 } 991 else 992 { 993 NT_ASSERT(info->Gesn.Buffer); 994 995 srb->TimeOutValue = GESN_TIMEOUT_VALUE; // much shorter timeout for GESN 996 997 srb->CdbLength = 10; 998 SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_IN); 999 nextIrpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_IN; 1000 srb->DataBuffer = info->Gesn.Buffer; 1001 srb->DataTransferLength = info->Gesn.BufferSize; 1002 irp->MdlAddress = info->Gesn.Mdl; 1003 1004 cdb = (PCDB) &srb->Cdb[0]; 1005 cdb->GET_EVENT_STATUS_NOTIFICATION.OperationCode = SCSIOP_GET_EVENT_STATUS; 1006 cdb->GET_EVENT_STATUS_NOTIFICATION.Immediate = 1; 1007 cdb->GET_EVENT_STATUS_NOTIFICATION.EventListLength[0] = (UCHAR)((info->Gesn.BufferSize) >> 8); 1008 cdb->GET_EVENT_STATUS_NOTIFICATION.EventListLength[1] = (UCHAR)((info->Gesn.BufferSize) & 0xff); 1009 cdb->GET_EVENT_STATUS_NOTIFICATION.NotificationClassRequest = info->Gesn.EventMask; 1010 } 1011 } 1012 1013 return status; 1014 } 1015 1016 1017 VOID 1018 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ 1019 DeviceDisableGesn( 1020 _In_ WDFWORKITEM WorkItem 1021 ) 1022 /*++ 1023 1024 Routine Description: 1025 1026 Work item routine to set the hack flag in the registry to disable GESN 1027 This routine is invoked when the device reports TOO many events that affects system 1028 1029 Arguments: 1030 WorkItem - the work item be perfromed. 1031 1032 Return Value: 1033 None 1034 1035 --*/ 1036 { 1037 WDFDEVICE device = WdfWorkItemGetParentObject(WorkItem); 1038 PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(device); 1039 1040 PAGED_CODE(); 1041 1042 // 1043 // Set the hack flag in the registry 1044 // 1045 DeviceSetParameter(deviceExtension, 1046 CLASSP_REG_SUBKEY_NAME, 1047 CLASSP_REG_MMC_DETECTION_VALUE_NAME, 1048 CdromDetectionUnsupported); 1049 1050 WdfObjectDelete(WorkItem); 1051 1052 return; 1053 } 1054 1055 _IRQL_requires_max_(APC_LEVEL) 1056 BOOLEAN 1057 RequestPostWorkMcnRequest( 1058 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension 1059 ) 1060 /*++ 1061 1062 Routine Description: 1063 1064 This routine handles the completion of the test unit ready irps used to 1065 determine if the media has changed. If the media has changed, this code 1066 signals the named event to wake up other system services that react to 1067 media change (aka AutoPlay). 1068 1069 Arguments: 1070 1071 DeviceExtension - the device context 1072 1073 Return Value: 1074 1075 BOOLEAN - TRUE (needs retry); FALSE (shoule not retry) 1076 1077 --*/ 1078 { 1079 NTSTATUS status = STATUS_SUCCESS; 1080 PMEDIA_CHANGE_DETECTION_INFO info = DeviceExtension->MediaChangeDetectionInfo; 1081 PIRP irp; 1082 BOOLEAN retryImmediately = FALSE; 1083 1084 PAGED_CODE(); 1085 1086 NT_ASSERT(info->MediaChangeRequest != NULL); 1087 irp = WdfRequestWdmGetIrp(info->MediaChangeRequest); 1088 1089 NT_ASSERT(!TEST_FLAG(info->MediaChangeSrb.SrbStatus, SRB_STATUS_QUEUE_FROZEN)); 1090 1091 // use InterpretSenseInfo routine to check for media state, and also 1092 // to call ClassError() with correct parameters. 1093 if (SRB_STATUS(info->MediaChangeSrb.SrbStatus) != SRB_STATUS_SUCCESS) 1094 { 1095 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN, "MCN request - failed - srb status=%x, sense=%x/%x/%x.\n", 1096 info->MediaChangeSrb.SrbStatus, 1097 ((PSENSE_DATA)(info->MediaChangeSrb.SenseInfoBuffer))->SenseKey, 1098 ((PSENSE_DATA)(info->MediaChangeSrb.SenseInfoBuffer))->AdditionalSenseCode, 1099 ((PSENSE_DATA)(info->MediaChangeSrb.SenseInfoBuffer))->AdditionalSenseCodeQualifier)); 1100 1101 if (SRB_STATUS(info->MediaChangeSrb.SrbStatus) != SRB_STATUS_NOT_POWERED) 1102 { 1103 // Release the queue if it is frozen. 1104 if (info->MediaChangeSrb.SrbStatus & SRB_STATUS_QUEUE_FROZEN) 1105 { 1106 DeviceReleaseQueue(DeviceExtension->Device); 1107 } 1108 1109 RequestSenseInfoInterpret(DeviceExtension, 1110 info->MediaChangeRequest, 1111 &info->MediaChangeSrb, 1112 0, 1113 &status, 1114 NULL); 1115 } 1116 } 1117 else 1118 { 1119 DeviceExtension->PrivateFdoData->LoggedTURFailureSinceLastIO = FALSE; 1120 1121 if (!info->Gesn.Supported) 1122 { 1123 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, 1124 "MCN request - succeeded (GESN NOT supported, setting MediaPresent).\n")); 1125 1126 // success != media for GESN case 1127 DeviceSetMediaChangeStateEx(DeviceExtension, 1128 MediaPresent, 1129 NULL); 1130 } 1131 else 1132 { 1133 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN, 1134 "MCN request - succeeded (GESN supported).\n")); 1135 } 1136 } 1137 1138 if (info->Gesn.Supported) 1139 { 1140 if (status == STATUS_DATA_OVERRUN) 1141 { 1142 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "MCN Request - Data Overrun\n")); 1143 status = STATUS_SUCCESS; 1144 } 1145 1146 if (!NT_SUCCESS(status)) 1147 { 1148 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "MCN Request: GESN failed with status %x\n", status)); 1149 } 1150 else 1151 { 1152 // for GESN, need to interpret the results of the data. 1153 // this may also require an immediate retry 1154 if (irp->IoStatus.Information == 8 ) 1155 { 1156 GesnDataInterpret(DeviceExtension, 1157 (PVOID)info->Gesn.Buffer, 1158 &retryImmediately); 1159 } 1160 1161 } // end of NT_SUCCESS(status) 1162 1163 } // end of Info->Gesn.Supported 1164 1165 // free port-allocated sense buffer, if any. 1166 if (PORT_ALLOCATED_SENSE(DeviceExtension, &info->MediaChangeSrb)) 1167 { 1168 FREE_PORT_ALLOCATED_SENSE_BUFFER(DeviceExtension, &info->MediaChangeSrb); 1169 } 1170 1171 // Remember the IRP and SRB for use the next time. 1172 NT_ASSERT(IoGetNextIrpStackLocation(irp)); 1173 IoGetNextIrpStackLocation(irp)->Parameters.Scsi.Srb = &info->MediaChangeSrb; 1174 1175 // run a sanity check to make sure we're not recursing continuously 1176 if (retryImmediately) 1177 { 1178 info->MediaChangeRetryCount++; 1179 1180 if (info->MediaChangeRetryCount > MAXIMUM_IMMEDIATE_MCN_RETRIES) 1181 { 1182 // Disable GESN on this device. 1183 // Create a work item to set the value in the registry 1184 WDF_OBJECT_ATTRIBUTES attributes; 1185 WDF_WORKITEM_CONFIG workitemConfig; 1186 WDFWORKITEM workItem; 1187 1188 WDF_OBJECT_ATTRIBUTES_INIT(&attributes); 1189 attributes.ParentObject = DeviceExtension->Device; 1190 1191 WDF_WORKITEM_CONFIG_INIT(&workitemConfig, DeviceDisableGesn); 1192 workitemConfig.AutomaticSerialization = FALSE; 1193 1194 status = WdfWorkItemCreate(&workitemConfig, 1195 &attributes, 1196 &workItem); 1197 1198 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "MCN Request: Disabling GESN for WDFDEVICE %p\n", DeviceExtension->Device)); 1199 1200 if (NT_SUCCESS(status)) 1201 { 1202 WdfWorkItemEnqueue(workItem); 1203 } 1204 1205 info->Gesn.Supported = FALSE; 1206 info->Gesn.EventMask = 0; 1207 info->Gesn.BufferSize = 0; 1208 info->MediaChangeRetryCount = 0; 1209 retryImmediately = FALSE; 1210 // should we log an error in event log? 1211 } 1212 } 1213 else 1214 { 1215 info->MediaChangeRetryCount = 0; 1216 } 1217 1218 return retryImmediately; 1219 } 1220 1221 1222 _IRQL_requires_max_(APC_LEVEL) 1223 BOOLEAN 1224 RequestSendMcnRequest( 1225 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension 1226 ) 1227 /*++ 1228 1229 Routine Description: 1230 1231 This routine sends the formatted MCN request sychronizely to lower driver. 1232 1233 Arguments: 1234 1235 DeviceExtension - the device context 1236 1237 Return Value: 1238 BOOLEAN - TRUE (requst successfully sent); FALSE (request failed to send) 1239 1240 --*/ 1241 { 1242 BOOLEAN requestSent = FALSE; 1243 PMEDIA_CHANGE_DETECTION_INFO info = DeviceExtension->MediaChangeDetectionInfo; 1244 1245 PAGED_CODE(); 1246 1247 RequestSend(DeviceExtension, 1248 info->MediaChangeRequest, 1249 DeviceExtension->IoTarget, 1250 WDF_REQUEST_SEND_OPTION_SYNCHRONOUS, 1251 &requestSent); 1252 1253 return requestSent; 1254 } // end RequestSendMcnRequest() 1255 1256 1257 1258 _IRQL_requires_max_(PASSIVE_LEVEL) 1259 NTSTATUS 1260 DeviceInitializeMcn( 1261 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, 1262 _In_ BOOLEAN AllowDriveToSleep 1263 ) 1264 /*++ 1265 1266 Routine Description: 1267 1268 This routine initialize the contents of MCN structure. 1269 1270 Arguments: 1271 1272 DeviceExtension - the device extension 1273 1274 AllowDriveToSleep - for CDROM, this parameter should be always FALSE 1275 1276 Return Value: 1277 NTSTATUS 1278 1279 --*/ 1280 { 1281 NTSTATUS status = STATUS_SUCCESS; 1282 PMEDIA_CHANGE_DETECTION_INFO mediaChangeInfo = NULL; 1283 PIRP irp = NULL; 1284 PVOID senseBuffer = NULL; 1285 WDF_OBJECT_ATTRIBUTES attributes; 1286 1287 PAGED_CODE(); 1288 1289 if (DeviceExtension->MediaChangeDetectionInfo != NULL) 1290 { 1291 //Already initialized. 1292 return STATUS_SUCCESS; 1293 } 1294 1295 DeviceExtension->KernelModeMcnContext.FileObject = (PVOID)-1; 1296 DeviceExtension->KernelModeMcnContext.DeviceObject = (PVOID)-1; 1297 DeviceExtension->KernelModeMcnContext.LockCount = 0; 1298 DeviceExtension->KernelModeMcnContext.McnDisableCount = 0; 1299 1300 mediaChangeInfo = ExAllocatePoolWithTag(NonPagedPoolNx, 1301 sizeof(MEDIA_CHANGE_DETECTION_INFO), 1302 CDROM_TAG_MEDIA_CHANGE_DETECTION); 1303 1304 if (mediaChangeInfo == NULL) 1305 { 1306 status = STATUS_INSUFFICIENT_RESOURCES; 1307 } 1308 else 1309 { 1310 RtlZeroMemory(mediaChangeInfo, sizeof(MEDIA_CHANGE_DETECTION_INFO)); 1311 } 1312 1313 if (NT_SUCCESS(status)) 1314 { 1315 if ((DeviceExtension->PowerDescriptor != NULL) && 1316 (DeviceExtension->PowerDescriptor->AsynchronousNotificationSupported != FALSE) && 1317 (!TEST_FLAG(DeviceExtension->PrivateFdoData->HackFlags, FDO_HACK_NO_ASYNCHRONOUS_NOTIFICATION))) 1318 { 1319 mediaChangeInfo->AsynchronousNotificationSupported = TRUE; 1320 } 1321 } 1322 1323 // Allocate an IRP to carry the IOCTL_MCN_SYNC_FAKE_IOCTL. 1324 if (NT_SUCCESS(status)) 1325 { 1326 irp = IoAllocateIrp(DeviceExtension->DeviceObject->StackSize, FALSE); 1327 1328 if (irp == NULL) 1329 { 1330 status = STATUS_INSUFFICIENT_RESOURCES; 1331 } 1332 } 1333 1334 if (NT_SUCCESS(status)) 1335 { 1336 WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, 1337 CDROM_REQUEST_CONTEXT); 1338 attributes.ParentObject = DeviceExtension->Device; 1339 status = WdfRequestCreate(&attributes, 1340 DeviceExtension->IoTarget, 1341 &mediaChangeInfo->MediaChangeRequest); 1342 } 1343 1344 if (NT_SUCCESS(status)) 1345 { 1346 // Preformat the media change request. With this being done, we never need to worry about 1347 // WdfIoTargetFormatRequestForInternalIoctlOthers ever failing later. 1348 status = WdfIoTargetFormatRequestForInternalIoctlOthers(DeviceExtension->IoTarget, 1349 mediaChangeInfo->MediaChangeRequest, 1350 IOCTL_SCSI_EXECUTE_IN, 1351 NULL, NULL, 1352 NULL, NULL, 1353 NULL, NULL); 1354 } 1355 1356 if (NT_SUCCESS(status)) 1357 { 1358 senseBuffer = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned, 1359 SENSE_BUFFER_SIZE, 1360 CDROM_TAG_MEDIA_CHANGE_DETECTION); 1361 if (senseBuffer == NULL) 1362 { 1363 status = STATUS_INSUFFICIENT_RESOURCES; 1364 } 1365 } 1366 1367 if (NT_SUCCESS(status)) 1368 { 1369 mediaChangeInfo->MediaChangeSyncIrp = irp; 1370 mediaChangeInfo->SenseBuffer = senseBuffer; 1371 1372 // Set default values for the media change notification 1373 // configuration. 1374 mediaChangeInfo->MediaChangeDetectionDisableCount = 0; 1375 1376 // Assume that there is initially no media in the device 1377 // only notify upper layers if there is something there 1378 mediaChangeInfo->LastKnownMediaDetectionState = MediaUnknown; 1379 mediaChangeInfo->LastReportedMediaDetectionState = MediaUnknown; 1380 1381 // setup all extra flags we'll be setting for this irp 1382 mediaChangeInfo->SrbFlags = 0; 1383 1384 SET_FLAG(mediaChangeInfo->SrbFlags, SRB_CLASS_FLAGS_LOW_PRIORITY); 1385 SET_FLAG(mediaChangeInfo->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE); 1386 SET_FLAG(mediaChangeInfo->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER); 1387 1388 if (AllowDriveToSleep) //FALSE for CD/DVD devices 1389 { 1390 SET_FLAG(mediaChangeInfo->SrbFlags, SRB_FLAGS_NO_KEEP_AWAKE); 1391 } 1392 1393 KeInitializeMutex(&mediaChangeInfo->MediaChangeMutex, 0x100); 1394 1395 // It is ok to support media change events on this device. 1396 DeviceExtension->MediaChangeDetectionInfo = mediaChangeInfo; 1397 1398 // check the device supports GESN or not, initialize GESN structure if it supports. 1399 { 1400 // This is only valid for type5 devices. 1401 NTSTATUS tempStatus = STATUS_SUCCESS; 1402 1403 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, 1404 "DeviceInitializeMcn: Testing for GESN\n")); 1405 tempStatus = DeviceInitializeGesn(DeviceExtension); 1406 1407 if (NT_SUCCESS(tempStatus)) 1408 { 1409 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, 1410 "DeviceInitializeMcn: GESN available for %p\n", 1411 DeviceExtension->DeviceObject)); 1412 NT_ASSERT(mediaChangeInfo->Gesn.Supported ); 1413 NT_ASSERT(mediaChangeInfo->Gesn.Buffer != NULL); 1414 NT_ASSERT(mediaChangeInfo->Gesn.BufferSize != 0); 1415 NT_ASSERT(mediaChangeInfo->Gesn.EventMask != 0); 1416 } 1417 else 1418 { 1419 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, 1420 "DeviceInitializeMcn: GESN *NOT* available for %p\n", 1421 DeviceExtension->DeviceObject)); 1422 NT_ASSERT(!mediaChangeInfo->Gesn.Supported); 1423 NT_ASSERT(mediaChangeInfo->Gesn.Buffer == NULL); 1424 NT_ASSERT(mediaChangeInfo->Gesn.BufferSize == 0); 1425 NT_ASSERT(mediaChangeInfo->Gesn.EventMask == 0); 1426 mediaChangeInfo->Gesn.Supported = FALSE; // just in case.... 1427 } 1428 } 1429 } 1430 1431 if (NT_SUCCESS(status)) 1432 { 1433 // Register for display state change on AOAC capable systems so we can put the 1434 // device to low power state when not required. 1435 if (mediaChangeInfo->DisplayStateCallbackHandle == NULL) 1436 { 1437 POWER_PLATFORM_INFORMATION PlatformInfo = {0}; 1438 1439 status = ZwPowerInformation(PlatformInformation, 1440 NULL, 1441 0, 1442 &PlatformInfo, 1443 sizeof(PlatformInfo)); 1444 1445 if (NT_SUCCESS(status) && PlatformInfo.AoAc) 1446 { 1447 PoRegisterPowerSettingCallback(DeviceExtension->DeviceObject, 1448 &GUID_CONSOLE_DISPLAY_STATE, 1449 &DevicePowerSettingCallback, 1450 DeviceExtension, 1451 &mediaChangeInfo->DisplayStateCallbackHandle); 1452 } 1453 1454 // Ignore any failures above. 1455 status = STATUS_SUCCESS; 1456 } 1457 } 1458 1459 if (!NT_SUCCESS(status)) 1460 { 1461 if (irp != NULL) 1462 { 1463 IoFreeIrp(irp); 1464 } 1465 FREE_POOL(senseBuffer); 1466 FREE_POOL(mediaChangeInfo); 1467 } 1468 1469 return status; 1470 1471 } // end DeviceInitializeMcn() 1472 1473 1474 _IRQL_requires_max_(PASSIVE_LEVEL) 1475 NTSTATUS 1476 DeviceInitializeGesn( 1477 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension 1478 ) 1479 /*++ 1480 1481 Routine Description: 1482 1483 This routine initialize the contents of GESN structure. 1484 1485 Arguments: 1486 1487 DeviceExtension - the device extension 1488 1489 Return Value: 1490 NTSTATUS 1491 1492 --*/ 1493 { 1494 NTSTATUS status = STATUS_SUCCESS; 1495 PNOTIFICATION_EVENT_STATUS_HEADER header = NULL; 1496 CDROM_DETECTION_STATE detectionState = CdromDetectionUnknown; 1497 PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor = DeviceExtension->DeviceDescriptor; 1498 BOOLEAN retryImmediately = TRUE; 1499 ULONG i = 0; 1500 ULONG atapiResets = 0; 1501 PMEDIA_CHANGE_DETECTION_INFO info = DeviceExtension->MediaChangeDetectionInfo; 1502 1503 PAGED_CODE(); 1504 1505 NT_ASSERT(info != NULL); 1506 1507 // read if we already know the abilities of the device 1508 DeviceGetParameter(DeviceExtension, 1509 CLASSP_REG_SUBKEY_NAME, 1510 CLASSP_REG_MMC_DETECTION_VALUE_NAME, 1511 (PULONG)&detectionState); 1512 1513 if (detectionState == CdromDetectionUnsupported) 1514 { 1515 status = STATUS_NOT_SUPPORTED; 1516 } 1517 1518 // check if the device has a hack flag saying never to try this. 1519 if (NT_SUCCESS(status) && 1520 (TEST_FLAG(DeviceExtension->PrivateFdoData->HackFlags, FDO_HACK_GESN_IS_BAD)) ) 1521 { 1522 DeviceSetParameter(DeviceExtension, 1523 CLASSP_REG_SUBKEY_NAME, 1524 CLASSP_REG_MMC_DETECTION_VALUE_NAME, 1525 CdromDetectionUnsupported); 1526 status = STATUS_NOT_SUPPORTED; 1527 } 1528 1529 // else go through the process since we allocate buffers and 1530 // get all sorts of device settings. 1531 if (NT_SUCCESS(status)) 1532 { 1533 if (info->Gesn.Buffer == NULL) 1534 { 1535 info->Gesn.Buffer = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned, 1536 GESN_BUFFER_SIZE, 1537 CDROM_TAG_GESN); 1538 } 1539 1540 if (info->Gesn.Buffer == NULL) 1541 { 1542 status = STATUS_INSUFFICIENT_RESOURCES; 1543 } 1544 } 1545 1546 if (NT_SUCCESS(status)) 1547 { 1548 if (info->Gesn.Mdl != NULL) 1549 { 1550 IoFreeMdl(info->Gesn.Mdl); 1551 } 1552 1553 info->Gesn.Mdl = IoAllocateMdl(info->Gesn.Buffer, 1554 GESN_BUFFER_SIZE, 1555 FALSE, 1556 FALSE, 1557 NULL); 1558 if (info->Gesn.Mdl == NULL) 1559 { 1560 status = STATUS_INSUFFICIENT_RESOURCES; 1561 } 1562 } 1563 1564 if (NT_SUCCESS(status)) 1565 { 1566 MmBuildMdlForNonPagedPool(info->Gesn.Mdl); 1567 info->Gesn.BufferSize = GESN_BUFFER_SIZE; 1568 info->Gesn.EventMask = 0; 1569 1570 // all items are prepared to use GESN (except the event mask, so don't 1571 // optimize this part out!). 1572 // 1573 // now see if it really works. we have to loop through this because 1574 // many SAMSUNG (and one COMPAQ) drives timeout when requesting 1575 // NOT_READY events, even when the IMMEDIATE bit is set. :( 1576 // 1577 // using a drive list is cumbersome, so this might fix the problem. 1578 for (i = 0; (i < 16) && retryImmediately; i++) 1579 { 1580 status = RequestSetupMcnRequest(DeviceExtension, TRUE); 1581 1582 if (!NT_SUCCESS(status)) 1583 { 1584 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN, 1585 "Setup Mcn request failed %x for WDFDEVICE %p\n", 1586 status, DeviceExtension->Device)); 1587 break; 1588 } 1589 1590 NT_ASSERT(TEST_FLAG(info->MediaChangeSrb.SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE)); 1591 1592 status = DeviceSendRequestSynchronously(DeviceExtension->Device, info->MediaChangeRequest, TRUE); 1593 1594 if (SRB_STATUS(info->MediaChangeSrb.SrbStatus) != SRB_STATUS_SUCCESS) 1595 { 1596 // Release the queue if it is frozen. 1597 if (info->MediaChangeSrb.SrbStatus & SRB_STATUS_QUEUE_FROZEN) 1598 { 1599 DeviceReleaseQueue(DeviceExtension->Device); 1600 } 1601 1602 RequestSenseInfoInterpret(DeviceExtension, 1603 info->MediaChangeRequest, 1604 &(info->MediaChangeSrb), 1605 0, 1606 &status, 1607 NULL); 1608 } 1609 1610 if ((deviceDescriptor->BusType == BusTypeAtapi) && 1611 (info->MediaChangeSrb.SrbStatus == SRB_STATUS_BUS_RESET)) 1612 { 1613 // 1614 // ATAPI unfortunately returns SRB_STATUS_BUS_RESET instead 1615 // of SRB_STATUS_TIMEOUT, so we cannot differentiate between 1616 // the two. if we get this status four time consecutively, 1617 // stop trying this command. it is too late to change ATAPI 1618 // at this point, so special-case this here. (07/10/2001) 1619 // NOTE: any value more than 4 may cause the device to be 1620 // marked missing. 1621 // 1622 atapiResets++; 1623 if (atapiResets >= 4) 1624 { 1625 status = STATUS_IO_DEVICE_ERROR; 1626 break; 1627 } 1628 } 1629 1630 if (status == STATUS_DATA_OVERRUN) 1631 { 1632 status = STATUS_SUCCESS; 1633 } 1634 1635 if ((status == STATUS_INVALID_DEVICE_REQUEST) || 1636 (status == STATUS_TIMEOUT) || 1637 (status == STATUS_IO_DEVICE_ERROR) || 1638 (status == STATUS_IO_TIMEOUT)) 1639 { 1640 // with these error codes, we don't ever want to try this command 1641 // again on this device, since it reacts poorly. 1642 DeviceSetParameter( DeviceExtension, 1643 CLASSP_REG_SUBKEY_NAME, 1644 CLASSP_REG_MMC_DETECTION_VALUE_NAME, 1645 CdromDetectionUnsupported); 1646 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN, 1647 "GESN test failed %x for WDFDEVICE %p\n", 1648 status, DeviceExtension->Device)); 1649 break; 1650 } 1651 1652 if (!NT_SUCCESS(status)) 1653 { 1654 // this may be other errors that should not disable GESN 1655 // for all future start_device calls. 1656 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN, 1657 "GESN test failed %x for WDFDEVICE %p\n", 1658 status, DeviceExtension->Device)); 1659 break; 1660 } 1661 else if (i == 0) 1662 { 1663 // the first time, the request was just retrieving a mask of 1664 // available bits. use this to mask future requests. 1665 header = (PNOTIFICATION_EVENT_STATUS_HEADER)(info->Gesn.Buffer); 1666 1667 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, 1668 "WDFDEVICE %p supports event mask %x\n", 1669 DeviceExtension->Device, header->SupportedEventClasses)); 1670 1671 if (TEST_FLAG(header->SupportedEventClasses, 1672 NOTIFICATION_MEDIA_STATUS_CLASS_MASK)) 1673 { 1674 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, 1675 "GESN supports MCN\n")); 1676 } 1677 if (TEST_FLAG(header->SupportedEventClasses, 1678 NOTIFICATION_DEVICE_BUSY_CLASS_MASK)) 1679 { 1680 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, 1681 "GESN supports DeviceBusy\n")); 1682 } 1683 if (TEST_FLAG(header->SupportedEventClasses, 1684 NOTIFICATION_OPERATIONAL_CHANGE_CLASS_MASK)) 1685 { 1686 if (TEST_FLAG(DeviceExtension->PrivateFdoData->HackFlags, 1687 FDO_HACK_GESN_IGNORE_OPCHANGE)) 1688 { 1689 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, 1690 "GESN supports OpChange, but must ignore these events for compatibility\n")); 1691 CLEAR_FLAG(header->SupportedEventClasses, 1692 NOTIFICATION_OPERATIONAL_CHANGE_CLASS_MASK); 1693 } 1694 else 1695 { 1696 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, 1697 "GESN supports OpChange\n")); 1698 } 1699 } 1700 info->Gesn.EventMask = header->SupportedEventClasses; 1701 1702 // 1703 // realistically, we are only considering the following events: 1704 // EXTERNAL REQUEST - this is being tested for play/stop/etc. 1705 // MEDIA STATUS - autorun and ejection requests. 1706 // DEVICE BUSY - to allow us to predict when media will be ready. 1707 // therefore, we should not bother querying for the other, 1708 // unknown events. clear all but the above flags. 1709 // 1710 info->Gesn.EventMask &= NOTIFICATION_OPERATIONAL_CHANGE_CLASS_MASK | 1711 NOTIFICATION_EXTERNAL_REQUEST_CLASS_MASK | 1712 NOTIFICATION_MEDIA_STATUS_CLASS_MASK | 1713 NOTIFICATION_DEVICE_BUSY_CLASS_MASK ; 1714 1715 1716 // 1717 // HACKHACK - REF #0001 1718 // Some devices will *never* report an event if we've also requested 1719 // that it report lower-priority events. this is due to a 1720 // misunderstanding in the specification wherein a "No Change" is 1721 // interpreted to be a real event. what should occur is that the 1722 // device should ignore "No Change" events when multiple event types 1723 // are requested unless there are no other events waiting. this 1724 // greatly reduces the number of requests that the host must send 1725 // to determine if an event has occurred. Since we must work on all 1726 // drives, default to enabling the hack until we find evidence of 1727 // proper firmware. 1728 // 1729 if (info->Gesn.EventMask == 0) 1730 { 1731 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, 1732 "GESN supported, but not mask we care about (%x) for FDO %p\n", 1733 header->SupportedEventClasses, 1734 DeviceExtension->DeviceObject)); 1735 // NOTE: the status is still status_sucess. 1736 break; 1737 } 1738 else if (CountOfSetBitsUChar(info->Gesn.EventMask) == 1) 1739 { 1740 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, 1741 "GESN hack not required for FDO %p\n", 1742 DeviceExtension->DeviceObject)); 1743 } 1744 else 1745 { 1746 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, 1747 "GESN hack enabled for FDO %p\n", 1748 DeviceExtension->DeviceObject)); 1749 info->Gesn.HackEventMask = 1; 1750 } 1751 } 1752 else 1753 { 1754 // i > 0; not the first time looping through, so interpret the results. 1755 status = GesnDataInterpret(DeviceExtension, 1756 (PVOID)info->Gesn.Buffer, 1757 &retryImmediately); 1758 1759 if (!NT_SUCCESS(status)) 1760 { 1761 // This drive does not support GESN correctly 1762 DeviceSetParameter( DeviceExtension, 1763 CLASSP_REG_SUBKEY_NAME, 1764 CLASSP_REG_MMC_DETECTION_VALUE_NAME, 1765 CdromDetectionUnsupported); 1766 break; 1767 } 1768 } 1769 } // end 'for' loop of GESN requests.... 1770 } 1771 1772 if (NT_SUCCESS(status)) 1773 { 1774 // 1775 // we can only use this if it can be relied upon for media changes, 1776 // since we are (by definition) no longer going to be polling via 1777 // a TEST_UNIT_READY irp, and drives will not report UNIT ATTENTION 1778 // for this command (although a filter driver, such as one for burning 1779 // cd's, might still fake those errors). 1780 // 1781 // since we also rely upon NOT_READY events to change the cursor 1782 // into a "wait" cursor; GESN is still more reliable than other 1783 // methods, and includes eject button requests, so we'll use it 1784 // without DEVICE_BUSY in Windows Vista. 1785 // 1786 1787 if (TEST_FLAG(info->Gesn.EventMask, NOTIFICATION_MEDIA_STATUS_CLASS_MASK)) 1788 { 1789 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, 1790 "Enabling GESN support for WDFDEVICE %p\n", 1791 DeviceExtension->Device)); 1792 info->Gesn.Supported = TRUE; 1793 1794 DeviceSetParameter( DeviceExtension, 1795 CLASSP_REG_SUBKEY_NAME, 1796 CLASSP_REG_MMC_DETECTION_VALUE_NAME, 1797 CdromDetectionSupported); 1798 } 1799 else 1800 { 1801 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, 1802 "GESN available but not enabled for WDFDEVICE %p\n", 1803 DeviceExtension->Device)); 1804 status = STATUS_NOT_SUPPORTED; 1805 } 1806 } 1807 1808 if (!NT_SUCCESS(status)) 1809 { 1810 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN, 1811 "GESN support detection failed for WDFDEVICE %p with status %08x\n", 1812 DeviceExtension->Device, status)); 1813 1814 if (info->Gesn.Mdl) 1815 { 1816 PIRP irp = WdfRequestWdmGetIrp(info->MediaChangeRequest); 1817 1818 IoFreeMdl(info->Gesn.Mdl); 1819 info->Gesn.Mdl = NULL; 1820 irp->MdlAddress = NULL; 1821 } 1822 1823 FREE_POOL(info->Gesn.Buffer); 1824 info->Gesn.Supported = FALSE; 1825 info->Gesn.EventMask = 0; 1826 info->Gesn.BufferSize = 0; 1827 } 1828 1829 return status; 1830 } 1831 1832 1833 _IRQL_requires_max_(PASSIVE_LEVEL) 1834 NTSTATUS 1835 DeviceInitializeMediaChangeDetection( 1836 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension 1837 ) 1838 /*++ 1839 1840 Routine Description: 1841 1842 This routine checks to see if it is safe to initialize MCN (the back end 1843 to autorun) for a given device. It will then check the device-type wide 1844 key "Autorun" in the service key (for legacy reasons), and then look in 1845 the device-specific key to potentially override that setting. 1846 1847 If MCN is to be enabled, all neccessary structures and memory are 1848 allocated and initialized. 1849 1850 This routine MUST be called only from the DeviceInit...() . 1851 1852 Arguments: 1853 1854 DeviceExtension - the device to initialize MCN for, if appropriate 1855 1856 Return Value: 1857 1858 NTSTATUS 1859 1860 --*/ 1861 { 1862 NTSTATUS status = STATUS_SUCCESS; 1863 1864 BOOLEAN disabled = FALSE; 1865 BOOLEAN instanceOverride; 1866 1867 PAGED_CODE(); 1868 1869 // NOTE: This assumes that DeviceInitializeMediaChangeDetection is always 1870 // called in the context of the DeviceInitDevicePhase2. If called 1871 // after then this check will have already been made and the 1872 // once a second timer will not have been enabled. 1873 1874 disabled = DeviceIsMediaChangeDisabledDueToHardwareLimitation(DeviceExtension); 1875 1876 if (disabled) 1877 { 1878 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, 1879 "DeviceInitializeMediaChangeDetection: Disabled due to hardware" 1880 "limitations for this device\n")); 1881 } 1882 else 1883 { 1884 // autorun should now be enabled by default for all media types. 1885 disabled = DeviceIsMediaChangeDisabledForClass(DeviceExtension); 1886 1887 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, 1888 "DeviceInitializeMediaChangeDetection: MCN is %s\n", 1889 (disabled ? "disabled" : "enabled"))); 1890 1891 status = DeviceMediaChangeDeviceInstanceOverride(DeviceExtension, 1892 &instanceOverride); // default value 1893 1894 if (!NT_SUCCESS(status)) 1895 { 1896 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, 1897 "DeviceInitializeMediaChangeDetection: Instance using default\n")); 1898 } 1899 else 1900 { 1901 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, 1902 "DeviceInitializeMediaChangeDetection: Instance override: %s MCN\n", 1903 (instanceOverride ? "Enabling" : "Disabling"))); 1904 disabled = !instanceOverride; 1905 } 1906 1907 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, 1908 "DeviceInitializeMediaChangeDetection: Instance MCN is %s\n", 1909 (disabled ? "disabled" : "enabled"))); 1910 } 1911 1912 if (!disabled) 1913 { 1914 // if the drive is not a CDROM, allow the drive to sleep 1915 status = DeviceInitializeMcn(DeviceExtension, FALSE); 1916 } 1917 1918 return status; 1919 } // end DeviceInitializeMediaChangeDetection() 1920 1921 1922 _IRQL_requires_max_(PASSIVE_LEVEL) 1923 NTSTATUS 1924 DeviceMediaChangeDeviceInstanceOverride( 1925 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, 1926 _Out_ PBOOLEAN Enabled 1927 ) 1928 /*++ 1929 1930 Routine Description: 1931 1932 The user can override the global setting to enable or disable Autorun on a 1933 specific cdrom device via the control panel. This routine checks and/or 1934 sets this value. 1935 1936 Arguments: 1937 1938 DeviceExtension - the device to set/get the value for 1939 1940 Enabled - TRUE (Autorun is enabled) 1941 FALSE (Autorun is disabled) 1942 Return Value: 1943 NTSTATUS 1944 1945 --*/ 1946 { 1947 NTSTATUS status = STATUS_UNSUCCESSFUL; 1948 WDFKEY deviceKey = NULL; 1949 WDFKEY subKey = NULL; 1950 1951 UNICODE_STRING subkeyName; 1952 UNICODE_STRING enableMcnValueName; 1953 UNICODE_STRING disableMcnValueName; 1954 ULONG alwaysEnable = 0; 1955 ULONG alwaysDisable = 0; 1956 1957 PAGED_CODE(); 1958 1959 status = WdfDeviceOpenRegistryKey(DeviceExtension->Device, 1960 PLUGPLAY_REGKEY_DEVICE, 1961 KEY_ALL_ACCESS, 1962 WDF_NO_OBJECT_ATTRIBUTES, 1963 &deviceKey); 1964 if (!NT_SUCCESS(status)) 1965 { 1966 // this can occur when a new device is added to the system 1967 // this is due to cdrom.sys being an 'essential' driver 1968 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, 1969 "DeviceMediaChangeDeviceInstanceOverride: " 1970 "Could not open device registry key [%lx]\n", status)); 1971 } 1972 else 1973 { 1974 RtlInitUnicodeString(&subkeyName, MCN_REG_SUBKEY_NAME); 1975 1976 status = WdfRegistryOpenKey(deviceKey, 1977 &subkeyName, 1978 KEY_READ, 1979 WDF_NO_OBJECT_ATTRIBUTES, 1980 &subKey); 1981 } 1982 1983 if (!NT_SUCCESS(status)) 1984 { 1985 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, 1986 "DeviceMediaChangeDeviceInstanceOverride: " 1987 "subkey could not be created. %lx\n", status)); 1988 } 1989 else 1990 { 1991 // Default to not changing autorun behavior, based upon setting 1992 // registryValue to zero. 1993 RtlInitUnicodeString(&enableMcnValueName, MCN_REG_AUTORUN_ENABLE_INSTANCE_NAME); 1994 RtlInitUnicodeString(&disableMcnValueName, MCN_REG_AUTORUN_DISABLE_INSTANCE_NAME); 1995 1996 // Ignore failures on reading of subkeys 1997 (VOID) WdfRegistryQueryULong(subKey, 1998 &enableMcnValueName, 1999 &alwaysEnable); 2000 (VOID) WdfRegistryQueryULong(subKey, 2001 &disableMcnValueName, 2002 &alwaysDisable); 2003 } 2004 2005 // set return value and cleanup 2006 2007 if (subKey != NULL) 2008 { 2009 WdfRegistryClose(subKey); 2010 } 2011 2012 if (deviceKey != NULL) 2013 { 2014 WdfRegistryClose(deviceKey); 2015 } 2016 2017 if (alwaysEnable && alwaysDisable) 2018 { 2019 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, 2020 "DeviceMediaChangeDeviceInstanceOverride: %s selected\n", 2021 "Both Enable and Disable set -- DISABLE")); 2022 NT_ASSERT(NT_SUCCESS(status)); 2023 status = STATUS_SUCCESS; 2024 *Enabled = FALSE; 2025 } 2026 else if (alwaysDisable) 2027 { 2028 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, 2029 "DeviceMediaChangeDeviceInstanceOverride: %s selected\n", 2030 "DISABLE")); 2031 NT_ASSERT(NT_SUCCESS(status)); 2032 status = STATUS_SUCCESS; 2033 *Enabled = FALSE; 2034 } 2035 else if (alwaysEnable) 2036 { 2037 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, 2038 "DeviceMediaChangeDeviceInstanceOverride: %s selected\n", 2039 "ENABLE")); 2040 NT_ASSERT(NT_SUCCESS(status)); 2041 status = STATUS_SUCCESS; 2042 *Enabled = TRUE; 2043 } 2044 else 2045 { 2046 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, 2047 "DeviceMediaChangeDeviceInstanceOverride: %s selected\n", 2048 "DEFAULT")); 2049 status = STATUS_UNSUCCESSFUL; 2050 } 2051 2052 return status; 2053 } // end DeviceMediaChangeDeviceInstanceOverride() 2054 2055 2056 NTSTATUS 2057 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ 2058 DeviceMediaChangeRegistryCallBack( 2059 _In_z_ PWSTR ValueName, 2060 _In_ ULONG ValueType, 2061 _In_reads_bytes_opt_(ValueLength) PVOID ValueData, 2062 _In_ ULONG ValueLength, 2063 _In_opt_ PVOID Context, 2064 _In_opt_ PVOID EntryContext 2065 ) 2066 /*++ 2067 2068 Routine Description: 2069 2070 This callback for a registry SZ or MULTI_SZ is called once for each 2071 SZ in the value. It will attempt to match the data with the 2072 UNICODE_STRING passed in as Context, and modify EntryContext if a 2073 match is found. Written for ClasspCheckRegistryForMediaChangeCompletion 2074 2075 Arguments: 2076 2077 ValueName - name of the key that was opened 2078 ValueType - type of data stored in the value (REG_SZ for this routine) 2079 ValueData - data in the registry, in this case a wide string 2080 ValueLength - length of the data including the terminating null 2081 Context - unicode string to compare against ValueData 2082 EntryContext - should be initialized to 0, will be set to 1 if match found 2083 2084 Return Value: 2085 2086 STATUS_SUCCESS 2087 EntryContext will be 1 if found 2088 2089 --*/ 2090 { 2091 PULONG valueFound; 2092 PUNICODE_STRING deviceString; 2093 PWSTR keyValue; 2094 2095 PAGED_CODE(); 2096 2097 UNREFERENCED_PARAMETER(ValueName); 2098 2099 if ((Context == NULL) || (EntryContext == NULL)) 2100 { 2101 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN, 2102 "DeviceMediaChangeRegistryCallBack: NULL context should never be passed to registry call-back!\n")); 2103 2104 return STATUS_SUCCESS; 2105 } 2106 2107 // if we have already set the value to true, exit 2108 valueFound = EntryContext; 2109 if ((*valueFound) != 0) 2110 { 2111 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, 2112 "DeviceMediaChangeRegistryCallBack: already set to true\n")); 2113 return STATUS_SUCCESS; 2114 } 2115 2116 if (ValueLength == sizeof(WCHAR)) 2117 { 2118 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN, 2119 "DeviceMediaChangeRegistryCallBack: NULL string should never be passed to registry call-back!\n")); 2120 return STATUS_SUCCESS; 2121 } 2122 2123 // if the data is not a terminated string, exit 2124 if (ValueType != REG_SZ) 2125 { 2126 return STATUS_SUCCESS; 2127 } 2128 2129 deviceString = Context; 2130 keyValue = ValueData; 2131 ValueLength -= sizeof(WCHAR); // ignore the null character 2132 2133 // do not compare more memory than is in deviceString 2134 if (ValueLength > deviceString->Length) 2135 { 2136 ValueLength = deviceString->Length; 2137 } 2138 2139 if (keyValue == NULL) 2140 { 2141 return STATUS_SUCCESS; 2142 } 2143 2144 // if the strings match, disable autorun 2145 if (RtlCompareMemory(deviceString->Buffer, keyValue, ValueLength) == ValueLength) 2146 { 2147 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN, "DeviceMediaChangeRegistryCallBack: Match found\n")); 2148 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN, "DeviceMediaChangeRegistryCallBack: DeviceString at %p\n", 2149 deviceString->Buffer)); 2150 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN, 2151 "DeviceMediaChangeRegistryCallBack: KeyValue at %p\n", 2152 keyValue)); 2153 (*valueFound) = TRUE; 2154 } 2155 2156 return STATUS_SUCCESS; 2157 } // end DeviceMediaChangeRegistryCallBack() 2158 2159 2160 _IRQL_requires_max_(PASSIVE_LEVEL) 2161 BOOLEAN 2162 DeviceIsMediaChangeDisabledDueToHardwareLimitation( 2163 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension 2164 ) 2165 /*++ 2166 2167 Routine Description: 2168 2169 The key AutoRunAlwaysDisable contains a MULTI_SZ of hardware IDs for 2170 which to never enable MediaChangeNotification. 2171 2172 The user can override the global setting to enable or disable Autorun on a 2173 specific cdrom device via the control panel. 2174 2175 NOTE: It's intended that not use WdfRegistryQueryMultiString in this funciton, 2176 as it's much more complicated.(needs WDFCOLLECTION, WDFSTRING other than 2177 UNICODE_STRING) 2178 2179 Arguments: 2180 2181 FdoExtension - 2182 RegistryPath - pointer to the unicode string inside 2183 ...\CurrentControlSet\Services\Cdrom 2184 2185 Return Value: 2186 2187 TRUE - no autorun. 2188 FALSE - Autorun may be enabled 2189 2190 --*/ 2191 { 2192 NTSTATUS status; 2193 2194 PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor = DeviceExtension->DeviceDescriptor; 2195 WDFKEY wdfKey; 2196 HANDLE serviceKey = NULL; 2197 RTL_QUERY_REGISTRY_TABLE parameters[2] = {0}; 2198 2199 UNICODE_STRING deviceUnicodeString = {0}; 2200 ANSI_STRING deviceString = {0}; 2201 ULONG mediaChangeNotificationDisabled = 0; 2202 2203 PAGED_CODE(); 2204 2205 // open the service key. 2206 status = WdfDriverOpenParametersRegistryKey(WdfGetDriver(), 2207 KEY_ALL_ACCESS, 2208 WDF_NO_OBJECT_ATTRIBUTES, 2209 &wdfKey); 2210 2211 if(!NT_SUCCESS(status)) 2212 { 2213 // NT_ASSERT(FALSE); __REACTOS__ : allow to fail (for 1st stage setup) 2214 2215 // always take the safe path. if we can't open the service key, disable autorun 2216 return TRUE; 2217 } 2218 2219 if(NT_SUCCESS(status)) 2220 { 2221 // Determine if drive is in a list of those requiring 2222 // autorun to be disabled. this is stored in a REG_MULTI_SZ 2223 // named AutoRunAlwaysDisable. this is required as some autochangers 2224 // must load the disc to reply to ChkVerify request, causing them 2225 // to cycle discs continuously. 2226 2227 PWSTR nullMultiSz; 2228 PUCHAR vendorId = NULL; 2229 PUCHAR productId = NULL; 2230 PUCHAR revisionId = NULL; 2231 size_t length; 2232 size_t offset; 2233 2234 deviceString.Buffer = NULL; 2235 deviceUnicodeString.Buffer = NULL; 2236 2237 serviceKey = WdfRegistryWdmGetHandle(wdfKey); 2238 2239 // there may be nothing to check against 2240 if ((deviceDescriptor->VendorIdOffset == 0) && 2241 (deviceDescriptor->ProductIdOffset == 0)) 2242 { 2243 // no valid data in device extension. 2244 status = STATUS_INTERNAL_ERROR; 2245 } 2246 2247 // build deviceString using VendorId, Model and Revision. 2248 // this string will be used to checked if it's one of devices in registry disable list. 2249 if (NT_SUCCESS(status)) 2250 { 2251 length = 0; 2252 2253 if (deviceDescriptor->VendorIdOffset == 0) 2254 { 2255 vendorId = NULL; 2256 } 2257 else 2258 { 2259 vendorId = (PUCHAR) deviceDescriptor + deviceDescriptor->VendorIdOffset; 2260 length = strlen((LPCSTR)vendorId); 2261 } 2262 2263 if ( deviceDescriptor->ProductIdOffset == 0 ) 2264 { 2265 productId = NULL; 2266 } 2267 else 2268 { 2269 productId = (PUCHAR) deviceDescriptor + deviceDescriptor->ProductIdOffset; 2270 length += strlen((LPCSTR)productId); 2271 } 2272 2273 if ( deviceDescriptor->ProductRevisionOffset == 0 ) 2274 { 2275 revisionId = NULL; 2276 } 2277 else 2278 { 2279 revisionId = (PUCHAR) deviceDescriptor + deviceDescriptor->ProductRevisionOffset; 2280 length += strlen((LPCSTR)revisionId); 2281 } 2282 2283 // allocate a buffer for the string 2284 deviceString.Length = (USHORT)( length ); 2285 deviceString.MaximumLength = deviceString.Length + 1; 2286 deviceString.Buffer = (PCHAR)ExAllocatePoolWithTag( NonPagedPoolNx, 2287 deviceString.MaximumLength, 2288 CDROM_TAG_AUTORUN_DISABLE 2289 ); 2290 if (deviceString.Buffer == NULL) 2291 { 2292 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, 2293 "DeviceIsMediaChangeDisabledDueToHardwareLimitation: Unable to alloc string buffer\n" )); 2294 status = STATUS_INTERNAL_ERROR; 2295 } 2296 } 2297 2298 if (NT_SUCCESS(status)) 2299 { 2300 // copy strings to the buffer 2301 offset = 0; 2302 2303 if (vendorId != NULL) 2304 { 2305 RtlCopyMemory(deviceString.Buffer + offset, 2306 vendorId, 2307 strlen((LPCSTR)vendorId)); 2308 offset += strlen((LPCSTR)vendorId); 2309 } 2310 2311 if ( productId != NULL ) 2312 { 2313 RtlCopyMemory(deviceString.Buffer + offset, 2314 productId, 2315 strlen((LPCSTR)productId)); 2316 offset += strlen((LPCSTR)productId); 2317 } 2318 if ( revisionId != NULL ) 2319 { 2320 RtlCopyMemory(deviceString.Buffer + offset, 2321 revisionId, 2322 strlen((LPCSTR)revisionId)); 2323 offset += strlen((LPCSTR)revisionId); 2324 } 2325 2326 NT_ASSERT(offset == deviceString.Length); 2327 2328 #pragma warning(suppress:6386) // Not an issue as deviceString.Buffer is of size deviceString.MaximumLength, which is equal to (deviceString.Length + 1) 2329 deviceString.Buffer[deviceString.Length] = '\0'; // Null-terminated 2330 2331 // convert to unicode as registry deals with unicode strings 2332 status = RtlAnsiStringToUnicodeString( &deviceUnicodeString, 2333 &deviceString, 2334 TRUE 2335 ); 2336 if (!NT_SUCCESS(status)) 2337 { 2338 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, 2339 "DeviceIsMediaChangeDisabledDueToHardwareLimitation: cannot convert " 2340 "to unicode %lx\n", status)); 2341 } 2342 } 2343 2344 if (NT_SUCCESS(status)) 2345 { 2346 // query the value, setting valueFound to true if found 2347 nullMultiSz = L"\0"; 2348 parameters[0].QueryRoutine = DeviceMediaChangeRegistryCallBack; 2349 parameters[0].Flags = RTL_QUERY_REGISTRY_REQUIRED; 2350 parameters[0].Name = L"AutoRunAlwaysDisable"; 2351 parameters[0].EntryContext = &mediaChangeNotificationDisabled; 2352 parameters[0].DefaultType = REG_MULTI_SZ; 2353 parameters[0].DefaultData = nullMultiSz; 2354 parameters[0].DefaultLength = 0; 2355 2356 status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE, 2357 serviceKey, 2358 parameters, 2359 &deviceUnicodeString, 2360 NULL); 2361 UNREFERENCED_PARAMETER(status); //defensive coding, avoid PREFAST warning. 2362 } 2363 } 2364 2365 // Cleanup 2366 { 2367 2368 FREE_POOL( deviceString.Buffer ); 2369 if (deviceUnicodeString.Buffer != NULL) 2370 { 2371 RtlFreeUnicodeString( &deviceUnicodeString ); 2372 } 2373 2374 // handle serviceKey will be closed by framework while it closes registry key. 2375 WdfRegistryClose(wdfKey); 2376 } 2377 2378 if (mediaChangeNotificationDisabled > 0) 2379 { 2380 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, 2381 "DeviceIsMediaChangeDisabledDueToHardwareLimitation: Device is on MCN disable list\n")); 2382 } 2383 2384 return (mediaChangeNotificationDisabled > 0); 2385 2386 } // end DeviceIsMediaChangeDisabledDueToHardwareLimitation() 2387 2388 2389 _IRQL_requires_max_(PASSIVE_LEVEL) 2390 BOOLEAN 2391 DeviceIsMediaChangeDisabledForClass( 2392 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension 2393 ) 2394 /*++ 2395 2396 Routine Description: 2397 2398 The user must specify that AutoPlay is to run on the platform 2399 by setting the registry value HKEY_LOCAL_MACHINE\System\CurrentControlSet\ 2400 Services\<SERVICE>\Autorun:REG_DWORD:1. 2401 2402 The user can override the global setting to enable or disable Autorun on a 2403 specific cdrom device via the control panel. 2404 2405 Arguments: 2406 2407 DeviceExtension - device extension 2408 2409 Return Value: 2410 2411 TRUE - Autorun is disabled for this class 2412 FALSE - Autorun is enabled for this class 2413 2414 --*/ 2415 { 2416 NTSTATUS status; 2417 WDFKEY serviceKey = NULL; 2418 WDFKEY parametersKey = NULL; 2419 2420 UNICODE_STRING parameterKeyName; 2421 UNICODE_STRING valueName; 2422 2423 // Default to ENABLING MediaChangeNotification (!) 2424 ULONG mcnRegistryValue = 1; 2425 2426 PAGED_CODE(); 2427 2428 // open the service key. 2429 status = WdfDriverOpenParametersRegistryKey(WdfGetDriver(), 2430 KEY_ALL_ACCESS, 2431 WDF_NO_OBJECT_ATTRIBUTES, 2432 &serviceKey); 2433 if(!NT_SUCCESS(status)) 2434 { 2435 // return the default value, which is the inverse of the registry setting default 2436 // since this routine asks if it's disabled 2437 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, 2438 "DeviceIsMediaChangeDisabledForClass: Defaulting to %s\n", 2439 (mcnRegistryValue ? "Enabled" : "Disabled"))); 2440 return (BOOLEAN)(mcnRegistryValue == 0); 2441 } 2442 else 2443 { 2444 // Open the parameters key (if any) beneath the services key. 2445 RtlInitUnicodeString(¶meterKeyName, L"Parameters"); 2446 2447 status = WdfRegistryOpenKey(serviceKey, 2448 ¶meterKeyName, 2449 KEY_READ, 2450 WDF_NO_OBJECT_ATTRIBUTES, 2451 ¶metersKey); 2452 } 2453 2454 if (!NT_SUCCESS(status)) 2455 { 2456 parametersKey = NULL; 2457 } 2458 2459 RtlInitUnicodeString(&valueName, L"Autorun"); 2460 // ignore failures 2461 status = WdfRegistryQueryULong(serviceKey, 2462 &valueName, 2463 &mcnRegistryValue); 2464 2465 if (NT_SUCCESS(status)) 2466 { 2467 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN, 2468 "DeviceIsMediaChangeDisabledForClass: <Service>/Autorun flag = %d\n", 2469 mcnRegistryValue)); 2470 } 2471 2472 if (parametersKey != NULL) 2473 { 2474 status = WdfRegistryQueryULong(parametersKey, 2475 &valueName, 2476 &mcnRegistryValue); 2477 2478 if (NT_SUCCESS(status)) 2479 { 2480 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN, 2481 "DeviceIsMediaChangeDisabledForClass: <Service>/Parameters/Autorun flag = %d\n", 2482 mcnRegistryValue)); 2483 } 2484 2485 WdfRegistryClose(parametersKey); 2486 } 2487 2488 WdfRegistryClose(serviceKey); 2489 2490 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, 2491 "DeviceIsMediaChangeDisabledForClass: Autoplay for device %p is %s\n", 2492 DeviceExtension->DeviceObject, 2493 (mcnRegistryValue ? "on" : "off") 2494 )); 2495 2496 // return if it is _disabled_, which is the 2497 // inverse of the registry setting 2498 2499 return (BOOLEAN)(!mcnRegistryValue); 2500 } // end DeviceIsMediaChangeDisabledForClass() 2501 2502 2503 _IRQL_requires_max_(APC_LEVEL) 2504 VOID 2505 DeviceEnableMediaChangeDetection( 2506 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, 2507 _Inout_ PFILE_OBJECT_CONTEXT FileObjectContext, 2508 _In_ BOOLEAN IgnorePreviousMediaChanges 2509 ) 2510 /*++ 2511 2512 Routine Description: 2513 2514 When the disable count decrease to 0, enable the MCN 2515 2516 Arguments: 2517 2518 DeviceExtension - the device context 2519 2520 FileObjectContext - the file object context 2521 2522 IgnorePreviousMediaChanges - ignore all previous media changes 2523 2524 Return Value: 2525 None. 2526 2527 --*/ 2528 { 2529 PMEDIA_CHANGE_DETECTION_INFO info = DeviceExtension->MediaChangeDetectionInfo; 2530 LONG oldCount; 2531 2532 PAGED_CODE(); 2533 2534 if (FileObjectContext) 2535 { 2536 InterlockedDecrement((PLONG)&(FileObjectContext->McnDisableCount)); 2537 } 2538 2539 if (info == NULL) 2540 { 2541 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, 2542 "DeviceEnableMediaChangeDetection: not initialized\n")); 2543 return; 2544 } 2545 2546 (VOID) KeWaitForMutexObject(&info->MediaChangeMutex, 2547 UserRequest, 2548 KernelMode, 2549 FALSE, 2550 NULL); 2551 2552 oldCount = --info->MediaChangeDetectionDisableCount; 2553 2554 NT_ASSERT(oldCount >= 0); 2555 2556 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN, 2557 "DeviceEnableMediaChangeDetection: Disable count reduced to %d - \n", 2558 info->MediaChangeDetectionDisableCount)); 2559 2560 if (oldCount == 0) 2561 { 2562 if (IgnorePreviousMediaChanges) 2563 { 2564 info->LastReportedMediaDetectionState = info->LastKnownMediaDetectionState; 2565 } 2566 2567 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "MCN is enabled\n")); 2568 } 2569 else 2570 { 2571 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN, "MCD still disabled\n")); 2572 } 2573 2574 // Let something else run. 2575 KeReleaseMutex(&info->MediaChangeMutex, FALSE); 2576 2577 return; 2578 } // end DeviceEnableMediaChangeDetection() 2579 2580 2581 _IRQL_requires_max_(APC_LEVEL) 2582 VOID 2583 DeviceDisableMediaChangeDetection( 2584 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, 2585 _Inout_ PFILE_OBJECT_CONTEXT FileObjectContext 2586 ) 2587 /*++ 2588 2589 Routine Description: 2590 2591 Increase the disable count. 2592 2593 Arguments: 2594 2595 DeviceExtension - the device context 2596 2597 FileObjectContext - the file object context 2598 2599 Return Value: 2600 None. 2601 2602 --*/ 2603 { 2604 PMEDIA_CHANGE_DETECTION_INFO info = DeviceExtension->MediaChangeDetectionInfo; 2605 2606 PAGED_CODE(); 2607 2608 if (FileObjectContext) 2609 { 2610 InterlockedIncrement((PLONG)&(FileObjectContext->McnDisableCount)); 2611 } 2612 2613 if (info == NULL) 2614 { 2615 return; 2616 } 2617 2618 (VOID) KeWaitForMutexObject(&info->MediaChangeMutex, 2619 UserRequest, 2620 KernelMode, 2621 FALSE, 2622 NULL); 2623 2624 info->MediaChangeDetectionDisableCount++; 2625 2626 TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN, 2627 "DisableMediaChangeDetection: disable count is %d\n", 2628 info->MediaChangeDetectionDisableCount)); 2629 2630 KeReleaseMutex(&info->MediaChangeMutex, FALSE); 2631 2632 return; 2633 } // end DeviceDisableMediaChangeDetection() 2634 2635 2636 _IRQL_requires_max_(APC_LEVEL) 2637 VOID 2638 DeviceReleaseMcnResources( 2639 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension 2640 ) 2641 /*++ 2642 2643 Routine Description: 2644 2645 This routine will cleanup any resources allocated for MCN. It is called 2646 by classpnp during remove device, and therefore is not typically required 2647 by external drivers. 2648 2649 Arguments: 2650 2651 DeviceExtension - the device context 2652 2653 Return Value: 2654 None. 2655 2656 --*/ 2657 { 2658 PMEDIA_CHANGE_DETECTION_INFO info = DeviceExtension->MediaChangeDetectionInfo; 2659 2660 PAGED_CODE() 2661 2662 if(info == NULL) 2663 { 2664 return; 2665 } 2666 2667 if (info->Gesn.Mdl) 2668 { 2669 PIRP irp = WdfRequestWdmGetIrp(info->MediaChangeRequest); 2670 IoFreeMdl(info->Gesn.Mdl); 2671 irp->MdlAddress = NULL; 2672 } 2673 IoFreeIrp(info->MediaChangeSyncIrp); 2674 FREE_POOL(info->Gesn.Buffer); 2675 FREE_POOL(info->SenseBuffer); 2676 2677 if (info->DisplayStateCallbackHandle) 2678 { 2679 PoUnregisterPowerSettingCallback(info->DisplayStateCallbackHandle); 2680 info->DisplayStateCallbackHandle = NULL; 2681 } 2682 2683 FREE_POOL(info); 2684 2685 DeviceExtension->MediaChangeDetectionInfo = NULL; 2686 2687 return; 2688 } // end DeviceReleaseMcnResources() 2689 2690 2691 IO_COMPLETION_ROUTINE RequestMcnSyncIrpCompletion; 2692 2693 NTSTATUS 2694 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ 2695 RequestMcnSyncIrpCompletion( 2696 _In_ PDEVICE_OBJECT DeviceObject, 2697 _In_ PIRP Irp, 2698 _In_reads_opt_(_Inexpressible_("varies")) PVOID Context 2699 ) 2700 /*++ 2701 2702 Routine Description: 2703 2704 The MCN work finishes, reset the fields to allow another MCN request 2705 be scheduled. 2706 2707 Arguments: 2708 2709 DeviceObject - device that the completion routine fires on. 2710 2711 Irp - The irp to be completed. 2712 2713 Context - IRP context 2714 2715 Return Value: 2716 NTSTATUS 2717 2718 --*/ 2719 { 2720 PCDROM_DEVICE_EXTENSION DeviceExtension = NULL; 2721 PMEDIA_CHANGE_DETECTION_INFO info = NULL; 2722 2723 if (Context == NULL) 2724 { 2725 // this will never happen, but code must be there to prevent OACR warnings. 2726 return STATUS_MORE_PROCESSING_REQUIRED; 2727 } 2728 2729 DeviceExtension = (PCDROM_DEVICE_EXTENSION) Context; 2730 info = DeviceExtension->MediaChangeDetectionInfo; 2731 2732 #ifndef DEBUG 2733 UNREFERENCED_PARAMETER(Irp); 2734 #endif 2735 UNREFERENCED_PARAMETER(DeviceObject); 2736 2737 NT_ASSERT(Irp == info->MediaChangeSyncIrp); 2738 2739 IoReuseIrp(info->MediaChangeSyncIrp, STATUS_NOT_SUPPORTED); 2740 2741 // reset the value to let timer routine be able to send the next request. 2742 InterlockedCompareExchange((PLONG)&(info->MediaChangeRequestInUse), 0, 1); 2743 2744 return STATUS_MORE_PROCESSING_REQUIRED; 2745 } 2746 2747 2748 VOID 2749 RequestSetupMcnSyncIrp( 2750 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension 2751 ) 2752 /*++ 2753 2754 Routine Description: 2755 2756 setup the MCN synchronization irp. 2757 2758 Arguments: 2759 2760 DeviceExtension - the device context 2761 2762 Return Value: 2763 None 2764 2765 --*/ 2766 { 2767 PIRP irp = NULL; 2768 PIO_STACK_LOCATION irpStack = NULL; 2769 PIO_STACK_LOCATION nextIrpStack = NULL; 2770 2771 irp = DeviceExtension->MediaChangeDetectionInfo->MediaChangeSyncIrp; 2772 NT_ASSERT(irp != NULL); 2773 2774 // 2775 // For the driver that creates an IRP, there is no 'current' stack location. 2776 // Step down one IRP stack location so that the extra top one 2777 // becomes our 'current' one. 2778 // 2779 IoSetNextIrpStackLocation(irp); 2780 2781 /* 2782 * Cache our device object in the extra top IRP stack location 2783 * so we have it in our completion routine. 2784 */ 2785 irpStack = IoGetCurrentIrpStackLocation(irp); 2786 irpStack->DeviceObject = DeviceExtension->DeviceObject; 2787 2788 // 2789 // If the irp is sent down when the volume needs to be 2790 // verified, CdRomUpdateGeometryCompletion won't complete 2791 // it since it's not associated with a thread. Marking 2792 // it to override the verify causes it always be sent 2793 // to the port driver 2794 // 2795 nextIrpStack = IoGetNextIrpStackLocation(irp); 2796 2797 SET_FLAG(nextIrpStack->Flags, SL_OVERRIDE_VERIFY_VOLUME); 2798 2799 nextIrpStack->MajorFunction = IRP_MJ_DEVICE_CONTROL; 2800 // pick up this IOCTL code as it's not normaly seen for CD/DVD drive and does not require input. 2801 // set other fields to make this IOCTL recognizable by CDROM.SYS 2802 nextIrpStack->Parameters.Others.Argument1 = RequestSetupMcnSyncIrp; 2803 nextIrpStack->Parameters.Others.Argument2 = RequestSetupMcnSyncIrp; 2804 nextIrpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_MCN_SYNC_FAKE_IOCTL; //Argument3. 2805 nextIrpStack->Parameters.Others.Argument4 = RequestSetupMcnSyncIrp; 2806 2807 IoSetCompletionRoutine(irp, 2808 RequestMcnSyncIrpCompletion, 2809 DeviceExtension, 2810 TRUE, 2811 TRUE, 2812 TRUE); 2813 2814 return; 2815 } 2816 2817 2818 VOID 2819 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */ 2820 DeviceMainTimerTickHandler( 2821 _In_ WDFTIMER Timer 2822 ) 2823 /*++ 2824 2825 Routine Description: 2826 2827 This routine setup a sync irp and send it to the serial queue. 2828 Serial queue will process MCN when receive this sync irp. 2829 2830 Arguments: 2831 2832 Timer - the timer object that fires. 2833 2834 Return Value: 2835 None 2836 2837 --*/ 2838 { 2839 PCDROM_DEVICE_EXTENSION deviceExtension = NULL; 2840 size_t dataLength = 0; 2841 2842 deviceExtension = WdfObjectGetTypedContext(WdfTimerGetParentObject(Timer), CDROM_DEVICE_EXTENSION); 2843 2844 (void) RequestHandleEventNotification(deviceExtension, NULL, NULL, &dataLength); 2845 2846 return; 2847 } // end DeviceMainTimerTickHandler() 2848 2849 2850 _IRQL_requires_max_(APC_LEVEL) 2851 NTSTATUS 2852 DeviceEnableMainTimer( 2853 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension 2854 ) 2855 /*++ 2856 2857 Routine Description: 2858 2859 This routine will allocate timer related resources on the first time call. 2860 Start the timer. 2861 2862 Arguments: 2863 2864 DeviceExtension - the device context 2865 2866 Return Value: 2867 NTSTATUS 2868 2869 --*/ 2870 { 2871 NTSTATUS status = STATUS_SUCCESS; 2872 2873 if ((DeviceExtension->MediaChangeDetectionInfo == NULL) || 2874 (DeviceExtension->MediaChangeDetectionInfo->AsynchronousNotificationSupported != FALSE)) 2875 { 2876 // Asynchronous Notification is enabled, timer not needed. 2877 return status; 2878 } 2879 2880 if (DeviceExtension->MainTimer == NULL) 2881 { 2882 //create main timer object. 2883 WDF_TIMER_CONFIG timerConfig; 2884 WDF_OBJECT_ATTRIBUTES timerAttributes; 2885 2886 WDF_TIMER_CONFIG_INIT(&timerConfig, DeviceMainTimerTickHandler); 2887 2888 // Polling frequently on virtual optical devices created by Hyper-V will 2889 // cause a significant perf / power hit. These devices need to be polled 2890 // less frequently for device state changes. 2891 if (TEST_FLAG(DeviceExtension->DeviceAdditionalData.HackFlags, CDROM_HACK_MSFT_VIRTUAL_ODD)) 2892 { 2893 timerConfig.Period = 2000; // 2 seconds, in milliseconds. 2894 } 2895 else 2896 { 2897 timerConfig.Period = 1000; // 1 second, in milliseconds. 2898 } 2899 2900 timerConfig.TolerableDelay = 500; // 0.5 seconds, in milliseconds 2901 2902 //Set the autoSerialization to FALSE, as the parent device's 2903 //execute level is WdfExecutionLevelPassive. 2904 timerConfig.AutomaticSerialization = FALSE; 2905 2906 WDF_OBJECT_ATTRIBUTES_INIT(&timerAttributes); 2907 timerAttributes.ParentObject = DeviceExtension->Device; 2908 timerAttributes.ExecutionLevel = WdfExecutionLevelInheritFromParent; 2909 2910 status = WdfTimerCreate(&timerConfig, 2911 &timerAttributes, 2912 &DeviceExtension->MainTimer); 2913 } 2914 2915 if (NT_SUCCESS(status)) 2916 { 2917 WdfTimerStart(DeviceExtension->MainTimer,WDF_REL_TIMEOUT_IN_MS(100)); 2918 2919 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, 2920 "DeviceEnableMainTimer: Once a second timer enabled for WDFDEVICE %p\n", 2921 DeviceExtension->Device)); 2922 } 2923 else 2924 { 2925 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, 2926 "DeviceEnableMainTimer: WDFDEVICE %p, Status %lx initializing timer\n", 2927 DeviceExtension->Device, status)); 2928 } 2929 2930 return status; 2931 } // end DeviceEnableMainTimer() 2932 2933 2934 _IRQL_requires_max_(PASSIVE_LEVEL) 2935 VOID 2936 DeviceDisableMainTimer( 2937 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension 2938 ) 2939 /*++ 2940 2941 Routine Description: 2942 2943 stop the timer. 2944 2945 Arguments: 2946 2947 DeviceExtension - device context 2948 2949 Return Value: 2950 None 2951 2952 --*/ 2953 { 2954 PAGED_CODE(); 2955 2956 if ((DeviceExtension->MediaChangeDetectionInfo == NULL) || 2957 (DeviceExtension->MediaChangeDetectionInfo->AsynchronousNotificationSupported != FALSE)) 2958 { 2959 // Asynchronous Notification is enabled, timer not needed. 2960 return; 2961 } 2962 2963 if (DeviceExtension->MainTimer != NULL) 2964 { 2965 // 2966 // we are only going to stop the actual timer in remove device routine. 2967 // it is the responsibility of the code within the timer routine to 2968 // check if the device is removed and not processing io for the final 2969 // call. 2970 // this keeps the code clean and prevents lots of bugs. 2971 // 2972 WdfTimerStop(DeviceExtension->MainTimer,TRUE); 2973 2974 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, 2975 "DeviceDisableMainTimer: Once a second timer disabled for device %p\n", 2976 DeviceExtension->Device)); 2977 } 2978 else 2979 { 2980 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN, 2981 "DeviceDisableMainTimer: Timer never enabled\n")); 2982 } 2983 2984 return; 2985 } // end DeviceDisableMainTimer() 2986 2987 2988 _IRQL_requires_max_(APC_LEVEL) 2989 NTSTATUS 2990 RequestHandleMcnControl( 2991 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, 2992 _In_ WDFREQUEST Request, 2993 _Out_ size_t * DataLength 2994 ) 2995 /*++ 2996 2997 Routine Description: 2998 2999 This routine handles the process of IOCTL_STORAGE_MCN_CONTROL 3000 3001 Arguments: 3002 3003 DeviceExtension - device context 3004 3005 Request - request object 3006 3007 RequestParameters - request parameters 3008 3009 DataLength - data transferred 3010 3011 Return Value: 3012 NTSTATUS 3013 3014 --*/ 3015 { 3016 NTSTATUS status = STATUS_SUCCESS; 3017 WDFFILEOBJECT fileObject = NULL; 3018 PFILE_OBJECT_CONTEXT fileObjectContext = NULL; 3019 PPREVENT_MEDIA_REMOVAL mediaRemoval = NULL; 3020 3021 PAGED_CODE(); 3022 3023 *DataLength = 0; 3024 3025 status = WdfRequestRetrieveInputBuffer(Request, 3026 sizeof(PREVENT_MEDIA_REMOVAL), 3027 &mediaRemoval, 3028 NULL); 3029 3030 if (NT_SUCCESS(status)) 3031 { 3032 fileObject = WdfRequestGetFileObject(Request); 3033 3034 // Check to make sure we have a file object extension to keep track of this 3035 // request. If not we'll fail it before synchronizing. 3036 if (fileObject != NULL) 3037 { 3038 fileObjectContext = FileObjectGetContext(fileObject); 3039 } 3040 3041 if ((fileObjectContext == NULL) && 3042 (WdfRequestGetRequestorMode(Request) == KernelMode)) 3043 { 3044 fileObjectContext = &DeviceExtension->KernelModeMcnContext; 3045 } 3046 3047 if (fileObjectContext == NULL) 3048 { 3049 // This handle isn't setup correctly. We can't let the 3050 // operation go. 3051 status = STATUS_INVALID_PARAMETER; 3052 } 3053 } 3054 3055 if (NT_SUCCESS(status)) 3056 { 3057 if (mediaRemoval->PreventMediaRemoval) 3058 { 3059 // This is a lock command. Reissue the command in case bus or 3060 // device was reset and the lock was cleared. 3061 DeviceDisableMediaChangeDetection(DeviceExtension, fileObjectContext); 3062 } 3063 else 3064 { 3065 if (fileObjectContext->McnDisableCount == 0) 3066 { 3067 status = STATUS_INVALID_DEVICE_STATE; 3068 } 3069 else 3070 { 3071 DeviceEnableMediaChangeDetection(DeviceExtension, fileObjectContext, TRUE); 3072 } 3073 } 3074 } 3075 3076 return status; 3077 } // end RequestHandleMcnControl() 3078 3079 #pragma warning(pop) // un-sets any local warning changes 3080 3081