1 /*++ 2 3 Copyright (C) Microsoft Corporation, 1991 - 1999 4 5 Module Name: 6 7 autorun.c 8 9 Abstract: 10 11 Code for support of media change detection in the class driver 12 13 Environment: 14 15 kernel mode only 16 17 Notes: 18 19 20 Revision History: 21 22 --*/ 23 24 #include "classp.h" 25 26 #include <wmidata.h> 27 28 #define GESN_TIMEOUT_VALUE (0x4) 29 #define GESN_BUFFER_SIZE (0x8) 30 #define MAXIMUM_IMMEDIATE_MCN_RETRIES (0x20) 31 #define MCN_REG_SUBKEY_NAME (L"MediaChangeNotification") 32 #define MCN_REG_AUTORUN_DISABLE_INSTANCE_NAME (L"AlwaysDisableMCN") 33 #define MCN_REG_AUTORUN_ENABLE_INSTANCE_NAME (L"AlwaysEnableMCN") 34 35 GUID StoragePredictFailureEventGuid = WMI_STORAGE_PREDICT_FAILURE_EVENT_GUID; 36 37 // 38 // Only send polling irp when device is fully powered up and a 39 // power down irp is not in progress. 40 // 41 // NOTE: This helps close a window in time where a polling irp could cause 42 // a drive to spin up right after it has powered down. The problem is 43 // that SCSIPORT, ATAPI and SBP2 will be in the process of powering 44 // down (which may take a few seconds), but won't know that. It would 45 // then get a polling irp which will be put into its queue since it 46 // the disk isn't powered down yet. Once the disk is powered down it 47 // will find the polling irp in the queue and then power up the 48 // device to do the poll. They do not want to check if the polling 49 // irp has the SRB_NO_KEEP_AWAKE flag here since it is in a critical 50 // path and would slow down all I/Os. A better way to fix this 51 // would be to serialize the polling and power down irps so that 52 // only one of them is sent to the device at a time. 53 // 54 #define ClasspCanSendPollingIrp(fdoExtension) \ 55 ((fdoExtension->DevicePowerState == PowerDeviceD0) && \ 56 (! fdoExtension->PowerDownInProgress) ) 57 58 BOOLEAN 59 NTAPI 60 ClasspIsMediaChangeDisabledDueToHardwareLimitation( 61 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, 62 IN PUNICODE_STRING RegistryPath 63 ); 64 65 NTSTATUS 66 NTAPI 67 ClasspMediaChangeDeviceInstanceOverride( 68 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, 69 OUT PBOOLEAN Enabled 70 ); 71 72 BOOLEAN 73 NTAPI 74 ClasspIsMediaChangeDisabledForClass( 75 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, 76 IN PUNICODE_STRING RegistryPath 77 ); 78 79 VOID 80 NTAPI 81 ClasspSetMediaChangeStateEx( 82 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, 83 IN MEDIA_CHANGE_DETECTION_STATE NewState, 84 IN BOOLEAN Wait, 85 IN BOOLEAN KnownStateChange // can ignore oldstate == unknown 86 ); 87 88 NTSTATUS 89 NTAPI 90 ClasspMediaChangeRegistryCallBack( 91 IN PWSTR ValueName, 92 IN ULONG ValueType, 93 IN PVOID ValueData, 94 IN ULONG ValueLength, 95 IN PVOID Context, 96 IN PVOID EntryContext 97 ); 98 99 VOID 100 NTAPI 101 ClasspSendMediaStateIrp( 102 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, 103 IN PMEDIA_CHANGE_DETECTION_INFO Info, 104 IN ULONG CountDown 105 ); 106 107 IO_WORKITEM_ROUTINE ClasspFailurePredict; 108 109 NTSTATUS 110 NTAPI 111 ClasspInitializePolling( 112 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, 113 IN BOOLEAN AllowDriveToSleep 114 ); 115 116 117 #if ALLOC_PRAGMA 118 119 #pragma alloc_text(PAGE, ClassInitializeMediaChangeDetection) 120 #pragma alloc_text(PAGE, ClassEnableMediaChangeDetection) 121 #pragma alloc_text(PAGE, ClassDisableMediaChangeDetection) 122 #pragma alloc_text(PAGE, ClassCleanupMediaChangeDetection) 123 #pragma alloc_text(PAGE, ClasspMediaChangeRegistryCallBack) 124 #pragma alloc_text(PAGE, ClasspInitializePolling) 125 126 #pragma alloc_text(PAGE, ClasspIsMediaChangeDisabledDueToHardwareLimitation) 127 #pragma alloc_text(PAGE, ClasspMediaChangeDeviceInstanceOverride) 128 #pragma alloc_text(PAGE, ClasspIsMediaChangeDisabledForClass) 129 130 #pragma alloc_text(PAGE, ClassSetFailurePredictionPoll) 131 #pragma alloc_text(PAGE, ClasspDisableTimer) 132 #pragma alloc_text(PAGE, ClasspEnableTimer) 133 134 #endif 135 136 // ISSUE -- make this public? 137 VOID 138 NTAPI 139 ClassSendEjectionNotification( 140 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension 141 ) 142 { 143 // 144 // For post-NT5.1 work, need to move EjectSynchronizationEvent 145 // to be a MUTEX so we can attempt to grab it here and benefit 146 // from deadlock detection. This will allow checking if the media 147 // has been locked by programs before broadcasting these events. 148 // (what's the point of broadcasting if the media is not locked?) 149 // 150 // This would currently only be a slight optimization. For post-NT5.1, 151 // it would allow us to send a single PERSISTENT_PREVENT to MMC devices, 152 // thereby cleaning up a lot of the ejection code. Then, when the 153 // ejection request occured, we could see if any locks for the media 154 // existed. if locked, broadcast. if not, we send the eject irp. 155 // 156 157 // 158 // for now, just always broadcast. make this a public routine, 159 // so class drivers can add special hacks to broadcast this for their 160 // non-MMC-compliant devices also from sense codes. 161 // 162 163 DBGTRACE(ClassDebugTrace, ("ClassSendEjectionNotification: media EJECT_REQUEST")); 164 ClasspSendNotification(FdoExtension, 165 &GUID_IO_MEDIA_EJECT_REQUEST, 166 0, 167 NULL); 168 return; 169 } 170 171 VOID 172 NTAPI 173 ClasspSendNotification( 174 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, 175 IN const GUID * Guid, 176 IN ULONG ExtraDataSize, 177 IN PVOID ExtraData 178 ) 179 { 180 PTARGET_DEVICE_CUSTOM_NOTIFICATION notification; 181 ULONG requiredSize; 182 183 requiredSize = 184 (sizeof(TARGET_DEVICE_CUSTOM_NOTIFICATION) - sizeof(UCHAR)) + 185 ExtraDataSize; 186 187 if (requiredSize > 0x0000ffff) { 188 // MAX_USHORT, max total size for these events! 189 KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugWarning, 190 "Error sending event: size too large! (%x)\n", 191 requiredSize)); 192 return; 193 } 194 195 notification = ExAllocatePoolWithTag(NonPagedPool, 196 requiredSize, 197 'oNcS'); 198 199 // 200 // if none allocated, exit 201 // 202 203 if (notification == NULL) { 204 return; 205 } 206 207 // 208 // Prepare and send the request! 209 // 210 211 RtlZeroMemory(notification, requiredSize); 212 notification->Version = 1; 213 notification->Size = (USHORT)(requiredSize); 214 notification->FileObject = NULL; 215 notification->NameBufferOffset = -1; 216 notification->Event = *Guid; 217 RtlCopyMemory(notification->CustomDataBuffer, ExtraData, ExtraDataSize); 218 219 IoReportTargetDeviceChangeAsynchronous(FdoExtension->LowerPdo, 220 notification, 221 NULL, NULL); 222 223 ExFreePool(notification); 224 notification = NULL; 225 return; 226 } 227 228 /*++//////////////////////////////////////////////////////////////////////////// 229 230 ClasspInterpretGesnData() 231 232 Routine Description: 233 234 This routine will interpret the data returned for a GESN command, and 235 (if appropriate) set the media change event, and broadcast the 236 appropriate events to user mode for applications who care. 237 238 Arguments: 239 240 FdoExtension - the device 241 242 DataBuffer - the resulting data from a GESN event. 243 requires at least EIGHT valid bytes (header == 4, data == 4) 244 245 ResendImmediately - whether or not to immediately resend the request. 246 this should be FALSE if there was no event, FALSE if the reported 247 event was of the DEVICE BUSY class, else true. 248 249 Return Value: 250 251 None 252 253 Notes: 254 255 DataBuffer must be at least four bytes of valid data (header == 4 bytes), 256 and have at least eight bytes of allocated memory (all events == 4 bytes). 257 258 The call to StartNextPacket may occur before this routine is completed. 259 the operational change notifications are informational in nature, and 260 while useful, are not necessary to ensure proper operation. For example, 261 if the device morphs to no longer supporting WRITE commands, all further 262 write commands will fail. There exists a small timing window wherein 263 IOCTL_IS_DISK_WRITABLE may be called and get an incorrect response. If 264 a device supports software write protect, it is expected that the 265 application can handle such a case. 266 267 NOTE: perhaps setting the updaterequired byte to one should be done here. 268 if so, it relies upon the setting of a 32-byte value to be an atomic 269 operation. unfortunately, there is no simple way to notify a class driver 270 which wants to know that the device behavior requires updating. 271 272 Not ready events may be sent every second. For example, if we were 273 to minimize the number of asynchronous notifications, an application may 274 register just after a large busy time was reported. This would then 275 prevent the application from knowing the device was busy until some 276 arbitrarily chosen timeout has occurred. Also, the GESN request would 277 have to still occur, since it checks for non-busy events (such as user 278 keybutton presses and media change events) as well. The specification 279 states that the lower-numered events get reported first, so busy events, 280 while repeating, will only be reported when all other events have been 281 cleared from the device. 282 283 --*/ 284 VOID 285 NTAPI 286 ClasspInterpretGesnData( 287 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, 288 IN PNOTIFICATION_EVENT_STATUS_HEADER Header, 289 IN PBOOLEAN ResendImmediately 290 ) 291 { 292 PMEDIA_CHANGE_DETECTION_INFO info; 293 LONG dataLength; 294 LONG requiredLength; 295 296 info = FdoExtension->MediaChangeDetectionInfo; 297 298 // 299 // note: don't allocate anything in this routine so that we can 300 // always just 'return'. 301 // 302 303 *ResendImmediately = FALSE; 304 305 if (Header->NEA) { 306 return; 307 } 308 if (Header->NotificationClass == NOTIFICATION_NO_CLASS_EVENTS) { 309 return; 310 } 311 312 // 313 // HACKHACK - REF #0001 314 // This loop is only taken initially, due to the inability to reliably 315 // auto-detect drives that report events correctly at boot. When we 316 // detect this behavior during the normal course of running, we will 317 // disable the hack, allowing more efficient use of the system. This 318 // should occur "nearly" instantly, as the drive should have multiple 319 // events queue'd (ie. power, morphing, media). 320 // 321 322 if (info->Gesn.HackEventMask) { 323 324 // 325 // all events use the low four bytes of zero to indicate 326 // that there was no change in status. 327 // 328 329 UCHAR thisEvent = Header->ClassEventData[0] & 0xf; 330 UCHAR lowestSetBit; 331 UCHAR thisEventBit = (1 << Header->NotificationClass); 332 333 ASSERT(TEST_FLAG(info->Gesn.EventMask, thisEventBit)); 334 335 336 // 337 // some bit magic here... this results in the lowest set bit only 338 // 339 340 lowestSetBit = info->Gesn.EventMask; 341 lowestSetBit &= (info->Gesn.EventMask - 1); 342 lowestSetBit ^= (info->Gesn.EventMask); 343 344 if (thisEventBit != lowestSetBit) { 345 346 // 347 // HACKHACK - REF #0001 348 // the first time we ever see an event set that is not the lowest 349 // set bit in the request (iow, highest priority), we know that the 350 // hack is no longer required, as the device is ignoring "no change" 351 // events when a real event is waiting in the other requested queues. 352 // 353 354 KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN, 355 "Classpnp => GESN::NONE: Compliant drive found, " 356 "removing GESN hack (%x, %x)\n", 357 thisEventBit, info->Gesn.EventMask)); 358 359 info->Gesn.HackEventMask = FALSE; 360 361 } else if (thisEvent == 0) { 362 363 // 364 // HACKHACK - REF #0001 365 // note: this hack prevents poorly implemented firmware from constantly 366 // returning "No Event". we do this by cycling through the 367 // supported list of events here. 368 // 369 370 SET_FLAG(info->Gesn.NoChangeEventMask, thisEventBit); 371 CLEAR_FLAG(info->Gesn.EventMask, thisEventBit); 372 373 // 374 // if we have cycled through all supported event types, then 375 // we need to reset the events we are asking about. else we 376 // want to resend this request immediately in case there was 377 // another event pending. 378 // 379 380 if (info->Gesn.EventMask == 0) { 381 info->Gesn.EventMask = info->Gesn.NoChangeEventMask; 382 info->Gesn.NoChangeEventMask = 0; 383 } else { 384 *ResendImmediately = TRUE; 385 } 386 return; 387 } 388 389 } // end if (info->Gesn.HackEventMask) 390 391 dataLength = 392 (Header->EventDataLength[0] << 8) | 393 (Header->EventDataLength[1] & 0xff); 394 dataLength -= 2; 395 requiredLength = 4; // all events are four bytes 396 397 if (dataLength < requiredLength) { 398 KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugWarning, 399 "Classpnp => GESN returned only %x bytes data for fdo %p\n", 400 dataLength, FdoExtension->DeviceObject)); 401 return; 402 } 403 if (dataLength != requiredLength) { 404 KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugWarning, 405 "Classpnp => GESN returned too many (%x) bytes data for fdo %p\n", 406 dataLength, FdoExtension->DeviceObject)); 407 dataLength = 4; 408 } 409 410 /* 411 ClasspSendNotification(FdoExtension, 412 &GUID_IO_GENERIC_GESN_EVENT, 413 sizeof(NOTIFICATION_EVENT_STATUS_HEADER) + dataLength, 414 Header) 415 */ 416 417 switch (Header->NotificationClass) { 418 419 case NOTIFICATION_EXTERNAL_REQUEST_CLASS_EVENTS: { // 0x3 420 421 PNOTIFICATION_EXTERNAL_STATUS externalInfo = 422 (PNOTIFICATION_EXTERNAL_STATUS)(Header->ClassEventData); 423 DEVICE_EVENT_EXTERNAL_REQUEST externalData; 424 425 // 426 // unfortunately, due to time constraints, we will only notify 427 // about keys being pressed, and not released. this makes keys 428 // single-function, but simplifies the code significantly. 429 // 430 431 if (externalInfo->ExternalEvent != 432 NOTIFICATION_EXTERNAL_EVENT_BUTTON_DOWN) { 433 break; 434 } 435 436 *ResendImmediately = TRUE; 437 KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN, 438 "Classpnp => GESN::EXTERNAL: Event: %x Status %x Req %x\n", 439 externalInfo->ExternalEvent, externalInfo->ExternalStatus, 440 (externalInfo->Request[0] << 8) | externalInfo->Request[1] 441 )); 442 443 RtlZeroMemory(&externalData, sizeof(DEVICE_EVENT_EXTERNAL_REQUEST)); 444 externalData.Version = 1; 445 externalData.DeviceClass = 0; 446 externalData.ButtonStatus = externalInfo->ExternalEvent; 447 externalData.Request = 448 (externalInfo->Request[0] << 8) | 449 (externalInfo->Request[1] & 0xff); 450 KeQuerySystemTime(&(externalData.SystemTime)); 451 externalData.SystemTime.QuadPart *= (LONGLONG)KeQueryTimeIncrement(); 452 453 DBGTRACE(ClassDebugTrace, ("ClasspInterpretGesnData: media DEVICE_EXTERNAL_REQUEST")); 454 ClasspSendNotification(FdoExtension, 455 &GUID_IO_DEVICE_EXTERNAL_REQUEST, 456 sizeof(DEVICE_EVENT_EXTERNAL_REQUEST), 457 &externalData); 458 return; 459 } 460 461 case NOTIFICATION_MEDIA_STATUS_CLASS_EVENTS: { // 0x4 462 463 PNOTIFICATION_MEDIA_STATUS mediaInfo = 464 (PNOTIFICATION_MEDIA_STATUS)(Header->ClassEventData); 465 466 if (mediaInfo->MediaEvent == NOTIFICATION_MEDIA_EVENT_NO_CHANGE) { 467 break; 468 } 469 470 *ResendImmediately = TRUE; 471 KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN, 472 "Classpnp => GESN::MEDIA: Event: %x Status %x\n", 473 mediaInfo->MediaEvent, mediaInfo->MediaStatus)); 474 475 if ((mediaInfo->MediaEvent == NOTIFICATION_MEDIA_EVENT_NEW_MEDIA) || 476 (mediaInfo->MediaEvent == NOTIFICATION_MEDIA_EVENT_MEDIA_CHANGE)) { 477 478 479 if (TEST_FLAG(FdoExtension->DeviceObject->Characteristics, 480 FILE_REMOVABLE_MEDIA) && 481 (ClassGetVpb(FdoExtension->DeviceObject) != NULL) && 482 (ClassGetVpb(FdoExtension->DeviceObject)->Flags & VPB_MOUNTED) 483 ) { 484 485 SET_FLAG(FdoExtension->DeviceObject->Flags, DO_VERIFY_VOLUME); 486 487 } 488 InterlockedIncrement((PLONG)&FdoExtension->MediaChangeCount); 489 ClasspSetMediaChangeStateEx(FdoExtension, 490 MediaPresent, 491 FALSE, 492 TRUE); 493 494 } else if (mediaInfo->MediaEvent == NOTIFICATION_MEDIA_EVENT_MEDIA_REMOVAL) { 495 496 ClasspSetMediaChangeStateEx(FdoExtension, 497 MediaNotPresent, 498 FALSE, 499 TRUE); 500 501 } else if (mediaInfo->MediaEvent == NOTIFICATION_MEDIA_EVENT_EJECT_REQUEST) { 502 503 KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugError, 504 "Classpnp => GESN Ejection request received!\n")); 505 ClassSendEjectionNotification(FdoExtension); 506 507 } 508 break; 509 510 } 511 512 case NOTIFICATION_DEVICE_BUSY_CLASS_EVENTS: { // lowest priority events... 513 514 PNOTIFICATION_BUSY_STATUS busyInfo = 515 (PNOTIFICATION_BUSY_STATUS)(Header->ClassEventData); 516 DEVICE_EVENT_BECOMING_READY busyData; 517 518 // 519 // NOTE: we never actually need to immediately retry for these 520 // events: if one exists, the device is busy, and if not, 521 // we still don't want to retry. 522 // 523 524 if (busyInfo->DeviceBusyStatus == NOTIFICATION_BUSY_STATUS_NO_EVENT) { 525 break; 526 } 527 528 // 529 // else we want to report the approximated time till it's ready. 530 // 531 532 RtlZeroMemory(&busyData, sizeof(DEVICE_EVENT_BECOMING_READY)); 533 busyData.Version = 1; 534 busyData.Reason = busyInfo->DeviceBusyStatus; 535 busyData.Estimated100msToReady = (busyInfo->Time[0] << 8) | 536 (busyInfo->Time[1] & 0xff); 537 538 KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN, 539 "Classpnp => GESN::BUSY: Event: %x Status %x Time %x\n", 540 busyInfo->DeviceBusyEvent, busyInfo->DeviceBusyStatus, 541 busyData.Estimated100msToReady 542 )); 543 544 DBGTRACE(ClassDebugTrace, ("ClasspInterpretGesnData: media BECOMING_READY")); 545 ClasspSendNotification(FdoExtension, 546 &GUID_IO_DEVICE_BECOMING_READY, 547 sizeof(DEVICE_EVENT_BECOMING_READY), 548 &busyData); 549 break; 550 } 551 552 default: { 553 554 break; 555 556 } 557 558 } // end switch on notification class 559 return; 560 } 561 562 /*++//////////////////////////////////////////////////////////////////////////// 563 564 ClasspInternalSetMediaChangeState() 565 566 Routine Description: 567 568 This routine will (if appropriate) set the media change event for the 569 device. The event will be set if the media state is changed and 570 media change events are enabled. Otherwise the media state will be 571 tracked but the event will not be set. 572 573 This routine will lock out the other media change routines if possible 574 but if not a media change notification may be lost after the enable has 575 been completed. 576 577 Arguments: 578 579 FdoExtension - the device 580 581 MediaPresent - indicates whether the device has media inserted into it 582 (TRUE) or not (FALSE). 583 584 Return Value: 585 586 none 587 588 --*/ 589 VOID 590 NTAPI 591 ClasspInternalSetMediaChangeState( 592 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, 593 IN MEDIA_CHANGE_DETECTION_STATE NewState, 594 IN BOOLEAN KnownStateChange // can ignore oldstate == unknown 595 ) 596 { 597 #if DBG 598 PCSTR states[] = {"Unknown", "Present", "Not Present"}; 599 #endif 600 MEDIA_CHANGE_DETECTION_STATE oldMediaState; 601 PMEDIA_CHANGE_DETECTION_INFO info = FdoExtension->MediaChangeDetectionInfo; 602 ULONG data; 603 //NTSTATUS status; 604 605 ASSERT((NewState >= MediaUnknown) && (NewState <= MediaNotPresent)); 606 607 if(info == NULL) { 608 return; 609 } 610 611 oldMediaState = InterlockedExchange( 612 (PLONG)(&info->MediaChangeDetectionState), 613 (LONG)NewState); 614 615 if((oldMediaState == MediaUnknown) && (!KnownStateChange)) { 616 617 // 618 // The media was in an indeterminate state before - don't notify for 619 // this change. 620 // 621 622 DebugPrint((ClassDebugMCN, 623 "ClassSetMediaChangeState: State was unknown - this may " 624 "not be a change\n")); 625 return; 626 627 } else if(oldMediaState == NewState) { 628 629 // 630 // Media is in the same state it was before. 631 // 632 633 return; 634 } 635 636 if(info->MediaChangeDetectionDisableCount != 0) { 637 638 DBGTRACE(ClassDebugMCN, 639 ("ClassSetMediaChangeState: MCN not enabled, state " 640 "changed from %s to %s\n", 641 states[oldMediaState], states[NewState])); 642 return; 643 644 } 645 646 DBGTRACE(ClassDebugMCN, 647 ("ClassSetMediaChangeState: State change from %s to %s\n", 648 states[oldMediaState], states[NewState])); 649 650 // 651 // make the data useful -- it used to always be zero. 652 // 653 data = FdoExtension->MediaChangeCount; 654 655 if (NewState == MediaPresent) { 656 657 DBGTRACE(ClassDebugTrace, ("ClasspInternalSetMediaChangeState: media ARRIVAL")); 658 ClasspSendNotification(FdoExtension, 659 &GUID_IO_MEDIA_ARRIVAL, 660 sizeof(ULONG), 661 &data); 662 663 } 664 else if (NewState == MediaNotPresent) { 665 666 DBGTRACE(ClassDebugTrace, ("ClasspInternalSetMediaChangeState: media REMOVAL")); 667 ClasspSendNotification(FdoExtension, 668 &GUID_IO_MEDIA_REMOVAL, 669 sizeof(ULONG), 670 &data); 671 672 } else { 673 674 // 675 // Don't notify of changed going to unknown. 676 // 677 678 return; 679 } 680 681 return; 682 } // end ClasspInternalSetMediaChangeState() 683 684 /*++//////////////////////////////////////////////////////////////////////////// 685 686 ClassSetMediaChangeState() 687 688 Routine Description: 689 690 This routine will (if appropriate) set the media change event for the 691 device. The event will be set if the media state is changed and 692 media change events are enabled. Otherwise the media state will be 693 tracked but the event will not be set. 694 695 This routine will lock out the other media change routines if possible 696 but if not a media change notification may be lost after the enable has 697 been completed. 698 699 Arguments: 700 701 FdoExtension - the device 702 703 MediaPresent - indicates whether the device has media inserted into it 704 (TRUE) or not (FALSE). 705 706 Wait - indicates whether the function should wait until it can acquire 707 the synchronization lock or not. 708 709 Return Value: 710 711 none 712 713 --*/ 714 VOID 715 NTAPI 716 ClasspSetMediaChangeStateEx( 717 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, 718 IN MEDIA_CHANGE_DETECTION_STATE NewState, 719 IN BOOLEAN Wait, 720 IN BOOLEAN KnownStateChange // can ignore oldstate == unknown 721 ) 722 { 723 PMEDIA_CHANGE_DETECTION_INFO info = FdoExtension->MediaChangeDetectionInfo; 724 LARGE_INTEGER zero; 725 NTSTATUS status; 726 727 DBGTRACE(ClassDebugMCN, ("> ClasspSetMediaChangeStateEx")); 728 729 // 730 // Reset SMART status on media removal as the old status may not be 731 // valid when there is no media in the device or when new media is 732 // inserted. 733 // 734 735 if (NewState == MediaNotPresent) { 736 737 FdoExtension->FailurePredicted = FALSE; 738 FdoExtension->FailureReason = 0; 739 740 } 741 742 743 zero.QuadPart = 0; 744 745 if(info == NULL) { 746 return; 747 } 748 749 status = KeWaitForMutexObject(&info->MediaChangeMutex, 750 Executive, 751 KernelMode, 752 FALSE, 753 ((Wait != FALSE) ? NULL : &zero)); 754 755 if(status == STATUS_TIMEOUT) { 756 757 // 758 // Someone else is in the process of setting the media state 759 // 760 761 DBGWARN(("ClasspSetMediaChangeStateEx - timed out waiting for mutex")); 762 return; 763 } 764 765 // 766 // Change the media present state and signal an event, if applicable 767 // 768 769 ClasspInternalSetMediaChangeState(FdoExtension, NewState, KnownStateChange); 770 771 KeReleaseMutex(&info->MediaChangeMutex, FALSE); 772 773 DBGTRACE(ClassDebugMCN, ("< ClasspSetMediaChangeStateEx")); 774 775 return; 776 } // end ClassSetMediaChangeStateEx() 777 778 VOID 779 NTAPI 780 ClassSetMediaChangeState( 781 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, 782 IN MEDIA_CHANGE_DETECTION_STATE NewState, 783 IN BOOLEAN Wait 784 ) 785 { 786 ClasspSetMediaChangeStateEx(FdoExtension, NewState, Wait, FALSE); 787 return; 788 } 789 790 /*++//////////////////////////////////////////////////////////////////////////// 791 792 ClasspMediaChangeDetectionCompletion() 793 794 Routine Description: 795 796 This routine handles the completion of the test unit ready irps used to 797 determine if the media has changed. If the media has changed, this code 798 signals the named event to wake up other system services that react to 799 media change (aka AutoPlay). 800 801 Arguments: 802 803 DeviceObject - the object for the completion 804 Irp - the IRP being completed 805 Context - the SRB from the IRP 806 807 Return Value: 808 809 NTSTATUS 810 811 --*/ 812 NTSTATUS 813 NTAPI 814 ClasspMediaChangeDetectionCompletion( 815 PDEVICE_OBJECT DeviceObject, 816 PIRP Irp, 817 PVOID Context 818 ) 819 { 820 PSCSI_REQUEST_BLOCK srb = Context; 821 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension; 822 PCLASS_PRIVATE_FDO_DATA fdoData; 823 PMEDIA_CHANGE_DETECTION_INFO info; 824 //PIO_STACK_LOCATION nextIrpStack; 825 NTSTATUS status; 826 BOOLEAN retryImmediately = FALSE; 827 828 // 829 // Since the class driver created this request, it's completion routine 830 // will not get a valid device object handed in. Use the one in the 831 // irp stack instead 832 // 833 834 DeviceObject = IoGetCurrentIrpStackLocation(Irp)->DeviceObject; 835 fdoExtension = DeviceObject->DeviceExtension; 836 fdoData = fdoExtension->PrivateFdoData; 837 info = fdoExtension->MediaChangeDetectionInfo; 838 839 ASSERT(info->MediaChangeIrp != NULL); 840 ASSERT(!TEST_FLAG(srb->SrbStatus, SRB_STATUS_QUEUE_FROZEN)); 841 DBGTRACE(ClassDebugMCN, ("> ClasspMediaChangeDetectionCompletion: Device %p completed MCN irp %p.", DeviceObject, Irp)); 842 843 /* 844 * HACK for IoMega 2GB Jaz drive: 845 * This drive spins down on its own to preserve the media. 846 * When spun down, TUR fails with 2/4/0 (SCSI_SENSE_NOT_READY/SCSI_ADSENSE_LUN_NOT_READY/?). 847 * ClassInterpretSenseInfo would then call ClassSendStartUnit to spin the media up, which defeats the 848 * purpose of the spindown. 849 * So in this case, make this into a successful TUR. 850 * This allows the drive to stay spun down until it is actually accessed again. 851 * (If the media were actually removed, TUR would fail with 2/3a/0 ). 852 * This hack only applies to drives with the CAUSE_NOT_REPORTABLE_HACK bit set; this 853 * is set by disk.sys when HackCauseNotReportableHack is set for the drive in its BadControllers list. 854 */ 855 if ((SRB_STATUS(srb->SrbStatus) != SRB_STATUS_SUCCESS) && 856 TEST_FLAG(fdoExtension->ScanForSpecialFlags, CLASS_SPECIAL_CAUSE_NOT_REPORTABLE_HACK) && 857 (srb->SenseInfoBufferLength >= RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseCode))){ 858 859 PSENSE_DATA senseData = srb->SenseInfoBuffer; 860 861 if ((senseData->SenseKey == SCSI_SENSE_NOT_READY) && 862 (senseData->AdditionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY)){ 863 srb->SrbStatus = SRB_STATUS_SUCCESS; 864 } 865 } 866 867 868 // 869 // use ClassInterpretSenseInfo() to check for media state, and also 870 // to call ClassError() with correct parameters. 871 // 872 status = STATUS_SUCCESS; 873 if (SRB_STATUS(srb->SrbStatus) != SRB_STATUS_SUCCESS) { 874 875 DBGTRACE(ClassDebugMCN, ("ClasspMediaChangeDetectionCompletion - failed - srb status=%s, sense=%s/%s/%s.", DBGGETSRBSTATUSSTR(srb), DBGGETSENSECODESTR(srb), DBGGETADSENSECODESTR(srb), DBGGETADSENSEQUALIFIERSTR(srb))); 876 877 ClassInterpretSenseInfo(DeviceObject, 878 srb, 879 IRP_MJ_SCSI, 880 0, 881 0, 882 &status, 883 NULL); 884 885 } 886 else { 887 888 fdoData->LoggedTURFailureSinceLastIO = FALSE; 889 890 if (!info->Gesn.Supported) { 891 892 DBGTRACE(ClassDebugMCN, ("ClasspMediaChangeDetectionCompletion - succeeded and GESN NOT supported, setting MediaPresent.")); 893 894 // 895 // success != media for GESN case 896 // 897 898 ClassSetMediaChangeState(fdoExtension, MediaPresent, FALSE); 899 900 } 901 else { 902 DBGTRACE(ClassDebugMCN, ("ClasspMediaChangeDetectionCompletion - succeeded (GESN supported).")); 903 } 904 } 905 906 if (info->Gesn.Supported) { 907 908 if (status == STATUS_DATA_OVERRUN) { 909 DBGTRACE(ClassDebugMCN, ("ClasspMediaChangeDetectionCompletion - Overrun")); 910 status = STATUS_SUCCESS; 911 } 912 913 if (!NT_SUCCESS(status)) { 914 DBGTRACE(ClassDebugMCN, ("ClasspMediaChangeDetectionCompletion: GESN failed with status %x", status)); 915 } else { 916 917 // 918 // for GESN, need to interpret the results of the data. 919 // this may also require an immediate retry 920 // 921 922 if (Irp->IoStatus.Information == 8 ) { 923 ClasspInterpretGesnData(fdoExtension, 924 (PVOID)info->Gesn.Buffer, 925 &retryImmediately); 926 } 927 928 } // end of NT_SUCCESS(status) 929 930 } // end of Info->Gesn.Supported 931 932 // 933 // free port-allocated sense buffer, if any. 934 // 935 936 if (PORT_ALLOCATED_SENSE(fdoExtension, srb)) { 937 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, srb); 938 } 939 940 // 941 // Remember the IRP and SRB for use the next time. 942 // 943 944 ASSERT(IoGetNextIrpStackLocation(Irp)); 945 IoGetNextIrpStackLocation(Irp)->Parameters.Scsi.Srb = srb; 946 947 // 948 // Reset the MCN timer. 949 // 950 951 ClassResetMediaChangeTimer(fdoExtension); 952 953 // 954 // run a sanity check to make sure we're not recursing continuously 955 // 956 957 if (retryImmediately) { 958 959 info->MediaChangeRetryCount++; 960 if (info->MediaChangeRetryCount > MAXIMUM_IMMEDIATE_MCN_RETRIES) { 961 ASSERT(!"Recursing too often in MCN?"); 962 info->MediaChangeRetryCount = 0; 963 retryImmediately = FALSE; 964 } 965 966 } else { 967 968 info->MediaChangeRetryCount = 0; 969 970 } 971 972 973 // 974 // release the remove lock.... 975 // 976 977 { 978 UCHAR uniqueValue; 979 ClassAcquireRemoveLock(DeviceObject, (PIRP)(&uniqueValue)); 980 ClassReleaseRemoveLock(DeviceObject, Irp); 981 982 983 // 984 // set the irp as not in use 985 // 986 { 987 volatile LONG irpWasInUse; 988 irpWasInUse = InterlockedCompareExchange(&info->MediaChangeIrpInUse, 0, 1); 989 #if _MSC_FULL_VER != 13009111 // This compiler always takes the wrong path here. 990 ASSERT(irpWasInUse); 991 #endif 992 } 993 994 // 995 // now send it again before we release our last remove lock 996 // 997 998 if (retryImmediately) { 999 ClasspSendMediaStateIrp(fdoExtension, info, 0); 1000 } 1001 else { 1002 DBGTRACE(ClassDebugMCN, ("ClasspMediaChangeDetectionCompletion - not retrying immediately")); 1003 } 1004 1005 // 1006 // release the temporary remove lock 1007 // 1008 1009 ClassReleaseRemoveLock(DeviceObject, (PIRP)(&uniqueValue)); 1010 } 1011 1012 DBGTRACE(ClassDebugMCN, ("< ClasspMediaChangeDetectionCompletion")); 1013 1014 return STATUS_MORE_PROCESSING_REQUIRED; 1015 } 1016 1017 /*++//////////////////////////////////////////////////////////////////////////// 1018 1019 ClasspSendTestUnitIrp() - ISSUE-2000/02/20-henrygab - not documented 1020 1021 Routine Description: 1022 1023 This routine 1024 1025 Arguments: 1026 1027 DeviceObject - 1028 Irp - 1029 1030 Return Value: 1031 1032 1033 --*/ 1034 PIRP 1035 NTAPI 1036 ClasspPrepareMcnIrp( 1037 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, 1038 IN PMEDIA_CHANGE_DETECTION_INFO Info, 1039 IN BOOLEAN UseGesn 1040 ) 1041 { 1042 PSCSI_REQUEST_BLOCK srb; 1043 PIO_STACK_LOCATION irpStack; 1044 PIO_STACK_LOCATION nextIrpStack; 1045 NTSTATUS status; 1046 PCDB cdb; 1047 PIRP irp; 1048 PVOID buffer; 1049 1050 // 1051 // Setup the IRP to perform a test unit ready. 1052 // 1053 1054 irp = Info->MediaChangeIrp; 1055 1056 ASSERT(irp); 1057 1058 if (irp == NULL) { 1059 return NULL; 1060 } 1061 1062 // 1063 // don't keep sending this if the device is being removed. 1064 // 1065 1066 status = ClassAcquireRemoveLock(FdoExtension->DeviceObject, irp); 1067 if (status == REMOVE_COMPLETE) { 1068 ASSERT(status != REMOVE_COMPLETE); 1069 return NULL; 1070 } 1071 else if (status == REMOVE_PENDING) { 1072 ClassReleaseRemoveLock(FdoExtension->DeviceObject, irp); 1073 return NULL; 1074 } 1075 else { 1076 ASSERT(status == NO_REMOVE); 1077 } 1078 1079 irp->IoStatus.Status = STATUS_SUCCESS; 1080 irp->IoStatus.Information = 0; 1081 irp->Flags = 0; 1082 irp->UserBuffer = NULL; 1083 1084 // 1085 // If the irp is sent down when the volume needs to be 1086 // verified, CdRomUpdateGeometryCompletion won't complete 1087 // it since it's not associated with a thread. Marking 1088 // it to override the verify causes it always be sent 1089 // to the port driver 1090 // 1091 1092 irpStack = IoGetCurrentIrpStackLocation(irp); 1093 irpStack->Flags |= SL_OVERRIDE_VERIFY_VOLUME; 1094 1095 nextIrpStack = IoGetNextIrpStackLocation(irp); 1096 nextIrpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; 1097 nextIrpStack->Parameters.Scsi.Srb = &(Info->MediaChangeSrb); 1098 1099 // 1100 // Prepare the SRB for execution. 1101 // 1102 1103 srb = nextIrpStack->Parameters.Scsi.Srb; 1104 buffer = srb->SenseInfoBuffer; 1105 RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK)); 1106 RtlZeroMemory(buffer, SENSE_BUFFER_SIZE); 1107 1108 1109 srb->QueueTag = SP_UNTAGGED; 1110 srb->QueueAction = SRB_SIMPLE_TAG_REQUEST; 1111 srb->Length = sizeof(SCSI_REQUEST_BLOCK); 1112 srb->Function = SRB_FUNCTION_EXECUTE_SCSI; 1113 srb->SenseInfoBuffer = buffer; 1114 srb->SrbStatus = 0; 1115 srb->ScsiStatus = 0; 1116 srb->OriginalRequest = irp; 1117 srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE; 1118 1119 srb->SrbFlags = FdoExtension->SrbFlags; 1120 SET_FLAG(srb->SrbFlags, Info->SrbFlags); 1121 1122 srb->TimeOutValue = FdoExtension->TimeOutValue * 2; 1123 1124 if (srb->TimeOutValue == 0) { 1125 1126 if (FdoExtension->TimeOutValue == 0) { 1127 1128 KdPrintEx((DPFLTR_CLASSPNP_ID, DPFLTR_ERROR_LEVEL, 1129 "ClassSendTestUnitIrp: FdoExtension->TimeOutValue " 1130 "is set to zero?! -- resetting to 10\n")); 1131 srb->TimeOutValue = 10 * 2; // reasonable default 1132 1133 } else { 1134 1135 KdPrintEx((DPFLTR_CLASSPNP_ID, DPFLTR_ERROR_LEVEL, 1136 "ClassSendTestUnitIrp: Someone set " 1137 "srb->TimeOutValue to zero?! -- resetting to %x\n", 1138 FdoExtension->TimeOutValue * 2)); 1139 srb->TimeOutValue = FdoExtension->TimeOutValue * 2; 1140 1141 } 1142 1143 } 1144 1145 if (!UseGesn) { 1146 1147 srb->CdbLength = 6; 1148 srb->DataTransferLength = 0; 1149 SET_FLAG(srb->SrbFlags, SRB_FLAGS_NO_DATA_TRANSFER); 1150 nextIrpStack->Parameters.DeviceIoControl.IoControlCode = 1151 IOCTL_SCSI_EXECUTE_NONE; 1152 srb->DataBuffer = NULL; 1153 srb->DataTransferLength = 0; 1154 irp->MdlAddress = NULL; 1155 1156 cdb = (PCDB) &srb->Cdb[0]; 1157 cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY; 1158 1159 } else { 1160 1161 ASSERT(Info->Gesn.Buffer); 1162 1163 srb->TimeOutValue = GESN_TIMEOUT_VALUE; // much shorter timeout for GESN 1164 1165 srb->CdbLength = 10; 1166 SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_IN); 1167 nextIrpStack->Parameters.DeviceIoControl.IoControlCode = 1168 IOCTL_SCSI_EXECUTE_IN; 1169 srb->DataBuffer = Info->Gesn.Buffer; 1170 srb->DataTransferLength = Info->Gesn.BufferSize; 1171 irp->MdlAddress = Info->Gesn.Mdl; 1172 1173 cdb = (PCDB) &srb->Cdb[0]; 1174 cdb->GET_EVENT_STATUS_NOTIFICATION.OperationCode = 1175 SCSIOP_GET_EVENT_STATUS; 1176 cdb->GET_EVENT_STATUS_NOTIFICATION.Immediate = 1; 1177 cdb->GET_EVENT_STATUS_NOTIFICATION.EventListLength[0] = 1178 (UCHAR)((Info->Gesn.BufferSize) >> 8); 1179 cdb->GET_EVENT_STATUS_NOTIFICATION.EventListLength[1] = 1180 (UCHAR)((Info->Gesn.BufferSize) & 0xff); 1181 cdb->GET_EVENT_STATUS_NOTIFICATION.NotificationClassRequest = 1182 Info->Gesn.EventMask; 1183 1184 } 1185 1186 IoSetCompletionRoutine(irp, 1187 ClasspMediaChangeDetectionCompletion, 1188 srb, 1189 TRUE, 1190 TRUE, 1191 TRUE); 1192 1193 return irp; 1194 1195 } 1196 1197 /*++//////////////////////////////////////////////////////////////////////////// 1198 1199 ClasspSendMediaStateIrp() - ISSUE-2000/02/20-henrygab - not documented 1200 1201 Routine Description: 1202 1203 This routine 1204 1205 Arguments: 1206 1207 DeviceObject - 1208 Irp - 1209 1210 Return Value: 1211 1212 --*/ 1213 VOID 1214 NTAPI 1215 ClasspSendMediaStateIrp( 1216 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, 1217 IN PMEDIA_CHANGE_DETECTION_INFO Info, 1218 IN ULONG CountDown 1219 ) 1220 { 1221 BOOLEAN requestPending = FALSE; 1222 LONG irpInUse; 1223 //LARGE_INTEGER zero; 1224 //NTSTATUS status; 1225 1226 DBGTRACE(ClassDebugMCN, ("> ClasspSendMediaStateIrp")); 1227 1228 if (((FdoExtension->CommonExtension.CurrentState != IRP_MN_START_DEVICE) || 1229 (FdoExtension->DevicePowerState != PowerDeviceD0) 1230 ) && 1231 (!Info->MediaChangeIrpLost)) { 1232 1233 // 1234 // the device may be stopped, powered down, or otherwise queueing io, 1235 // so should not timeout the autorun irp (yet) -- set to zero ticks. 1236 // scattered code relies upon this to not prematurely "lose" an 1237 // autoplay irp that was queued. 1238 // 1239 1240 Info->MediaChangeIrpTimeInUse = 0; 1241 } 1242 1243 // 1244 // if the irp is not in use, mark it as such. 1245 // 1246 1247 irpInUse = InterlockedCompareExchange(&Info->MediaChangeIrpInUse, 1, 0); 1248 1249 if (irpInUse) { 1250 1251 LONG timeInUse; 1252 1253 timeInUse = InterlockedIncrement(&Info->MediaChangeIrpTimeInUse); 1254 1255 DebugPrint((ClassDebugMCN, "ClasspSendMediaStateIrp: irp in use for " 1256 "%x seconds when synchronizing for MCD\n", timeInUse)); 1257 1258 if (Info->MediaChangeIrpLost == FALSE) { 1259 1260 if (timeInUse > MEDIA_CHANGE_TIMEOUT_TIME) { 1261 1262 // 1263 // currently set to five minutes. hard to imagine a drive 1264 // taking that long to spin up. 1265 // 1266 1267 DebugPrint((ClassDebugError, 1268 "CdRom%d: Media Change Notification has lost " 1269 "it's irp and doesn't know where to find it. " 1270 "Leave it alone and it'll come home dragging " 1271 "it's stack behind it.\n", 1272 FdoExtension->DeviceNumber)); 1273 Info->MediaChangeIrpLost = TRUE; 1274 } 1275 } 1276 1277 DBGTRACE(ClassDebugMCN, ("< ClasspSendMediaStateIrp - irpInUse")); 1278 return; 1279 1280 } 1281 1282 TRY { 1283 1284 if (Info->MediaChangeDetectionDisableCount != 0) { 1285 DebugPrint((ClassDebugTrace, "ClassCheckMediaState: device %p has " 1286 " detection disabled \n", FdoExtension->DeviceObject)); 1287 LEAVE; 1288 } 1289 1290 if (FdoExtension->DevicePowerState != PowerDeviceD0) { 1291 1292 if (TEST_FLAG(Info->SrbFlags, SRB_FLAGS_NO_KEEP_AWAKE)) { 1293 DebugPrint((ClassDebugMCN, 1294 "ClassCheckMediaState: device %p is powered " 1295 "down and flags are set to let it sleep\n", 1296 FdoExtension->DeviceObject)); 1297 ClassResetMediaChangeTimer(FdoExtension); 1298 LEAVE; 1299 } 1300 1301 // 1302 // NOTE: we don't increment the time in use until our power state 1303 // changes above. this way, we won't "lose" the autoplay irp. 1304 // it's up to the lower driver to determine if powering up is a 1305 // good idea. 1306 // 1307 1308 DebugPrint((ClassDebugMCN, 1309 "ClassCheckMediaState: device %p needs to powerup " 1310 "to handle this io (may take a few extra seconds).\n", 1311 FdoExtension->DeviceObject)); 1312 1313 } 1314 1315 Info->MediaChangeIrpTimeInUse = 0; 1316 Info->MediaChangeIrpLost = FALSE; 1317 1318 if (CountDown == 0) { 1319 1320 PIRP irp; 1321 1322 DebugPrint((ClassDebugTrace, 1323 "ClassCheckMediaState: timer expired\n")); 1324 1325 if (Info->MediaChangeDetectionDisableCount != 0) { 1326 DebugPrint((ClassDebugTrace, 1327 "ClassCheckMediaState: detection disabled\n")); 1328 LEAVE; 1329 } 1330 1331 // 1332 // Prepare the IRP for the test unit ready 1333 // 1334 1335 irp = ClasspPrepareMcnIrp(FdoExtension, 1336 Info, 1337 Info->Gesn.Supported); 1338 1339 // 1340 // Issue the request. 1341 // 1342 1343 DebugPrint((ClassDebugTrace, 1344 "ClasspSendMediaStateIrp: Device %p getting TUR " 1345 " irp %p\n", FdoExtension->DeviceObject, irp)); 1346 1347 if (irp == NULL) { 1348 LEAVE; 1349 } 1350 1351 1352 // 1353 // note: if we send it to the class dispatch routines, there is 1354 // a timing window here (since they grab the remove lock) 1355 // where we'd be removed. ELIMINATE the window by grabbing 1356 // the lock ourselves above and sending it to the lower 1357 // device object directly or to the device's StartIo 1358 // routine (which doesn't acquire the lock). 1359 // 1360 1361 requestPending = TRUE; 1362 1363 DBGTRACE(ClassDebugMCN, (" ClasspSendMediaStateIrp - calling IoCallDriver.")); 1364 IoCallDriver(FdoExtension->CommonExtension.LowerDeviceObject, irp); 1365 } 1366 1367 } FINALLY { 1368 1369 if(requestPending == FALSE) { 1370 irpInUse = InterlockedCompareExchange(&Info->MediaChangeIrpInUse, 0, 1); 1371 #if _MSC_FULL_VER != 13009111 // This compiler always takes the wrong path here. 1372 ASSERT(irpInUse); 1373 #endif 1374 } 1375 1376 } 1377 1378 DBGTRACE(ClassDebugMCN, ("< ClasspSendMediaStateIrp")); 1379 1380 return; 1381 } // end ClasspSendMediaStateIrp() 1382 1383 /*++//////////////////////////////////////////////////////////////////////////// 1384 1385 ClassCheckMediaState() 1386 1387 Routine Description: 1388 1389 This routine is called by the class driver to test for a media change 1390 condition and/or poll for disk failure prediction. It should be called 1391 from the class driver's IO timer routine once per second. 1392 1393 Arguments: 1394 1395 FdoExtension - the device extension 1396 1397 Return Value: 1398 1399 none 1400 1401 --*/ 1402 VOID 1403 NTAPI 1404 ClassCheckMediaState( 1405 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension 1406 ) 1407 { 1408 PMEDIA_CHANGE_DETECTION_INFO info = FdoExtension->MediaChangeDetectionInfo; 1409 LONG countDown; 1410 1411 if(info == NULL) { 1412 DebugPrint((ClassDebugTrace, 1413 "ClassCheckMediaState: detection not enabled\n")); 1414 return; 1415 } 1416 1417 // 1418 // Media change support is active and the IRP is waiting. Decrement the 1419 // timer. There is no MP protection on the timer counter. This code 1420 // is the only code that will manipulate the timer counter and only one 1421 // instance of it should be running at any given time. 1422 // 1423 1424 countDown = InterlockedDecrement(&(info->MediaChangeCountDown)); 1425 1426 // 1427 // Try to acquire the media change event. If we can't do it immediately 1428 // then bail out and assume the caller will try again later. 1429 // 1430 ClasspSendMediaStateIrp(FdoExtension, 1431 info, 1432 countDown); 1433 1434 return; 1435 } // end ClassCheckMediaState() 1436 1437 /*++//////////////////////////////////////////////////////////////////////////// 1438 1439 ClassResetMediaChangeTimer() 1440 1441 Routine Description: 1442 1443 Resets the media change count down timer to the default number of seconds. 1444 1445 Arguments: 1446 1447 FdoExtension - the device to reset the timer for 1448 1449 Return Value: 1450 1451 None 1452 1453 --*/ 1454 VOID 1455 NTAPI 1456 ClassResetMediaChangeTimer( 1457 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension 1458 ) 1459 { 1460 PMEDIA_CHANGE_DETECTION_INFO info = FdoExtension->MediaChangeDetectionInfo; 1461 1462 if(info != NULL) { 1463 InterlockedExchange(&(info->MediaChangeCountDown), 1464 MEDIA_CHANGE_DEFAULT_TIME); 1465 } 1466 return; 1467 } // end ClassResetMediaChangeTimer() 1468 1469 /*++//////////////////////////////////////////////////////////////////////////// 1470 1471 ClasspInitializePolling() - ISSUE-2000/02/20-henrygab - not documented 1472 1473 Routine Description: 1474 1475 This routine 1476 1477 Arguments: 1478 1479 DeviceObject - 1480 Irp - 1481 1482 Return Value: 1483 1484 --*/ 1485 NTSTATUS 1486 NTAPI 1487 ClasspInitializePolling( 1488 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, 1489 IN BOOLEAN AllowDriveToSleep 1490 ) 1491 { 1492 PDEVICE_OBJECT fdo = FdoExtension->DeviceObject; 1493 //PCLASS_PRIVATE_FDO_DATA fdoData = FdoExtension->PrivateFdoData; 1494 1495 //ULONG size; 1496 PMEDIA_CHANGE_DETECTION_INFO info; 1497 PIRP irp; 1498 1499 PAGED_CODE(); 1500 1501 if (FdoExtension->MediaChangeDetectionInfo != NULL) { 1502 return STATUS_SUCCESS; 1503 } 1504 1505 info = ExAllocatePoolWithTag(NonPagedPool, 1506 sizeof(MEDIA_CHANGE_DETECTION_INFO), 1507 CLASS_TAG_MEDIA_CHANGE_DETECTION); 1508 1509 if(info != NULL) { 1510 RtlZeroMemory(info, sizeof(MEDIA_CHANGE_DETECTION_INFO)); 1511 1512 FdoExtension->KernelModeMcnContext.FileObject = (PVOID)-1; 1513 FdoExtension->KernelModeMcnContext.DeviceObject = (PVOID)-1; 1514 FdoExtension->KernelModeMcnContext.LockCount = 0; 1515 FdoExtension->KernelModeMcnContext.McnDisableCount = 0; 1516 1517 /* 1518 * Allocate an IRP to carry the Test-Unit-Ready. 1519 * Allocate an extra IRP stack location 1520 * so we can cache our device object in the top location. 1521 */ 1522 irp = IoAllocateIrp((CCHAR)(fdo->StackSize+1), FALSE); 1523 1524 if (irp != NULL) { 1525 1526 PVOID buffer; 1527 1528 buffer = ExAllocatePoolWithTag( 1529 NonPagedPoolCacheAligned, 1530 SENSE_BUFFER_SIZE, 1531 CLASS_TAG_MEDIA_CHANGE_DETECTION); 1532 1533 if (buffer != NULL) { 1534 PIO_STACK_LOCATION irpStack; 1535 PSCSI_REQUEST_BLOCK srb; 1536 //PCDB cdb; 1537 1538 srb = &(info->MediaChangeSrb); 1539 info->MediaChangeIrp = irp; 1540 info->SenseBuffer = buffer; 1541 1542 /* 1543 * For the driver that creates an IRP, there is no 'current' stack location. 1544 * Step down one IRP stack location so that the extra top one 1545 * becomes our 'current' one. 1546 */ 1547 IoSetNextIrpStackLocation(irp); 1548 1549 /* 1550 * Cache our device object in the extra top IRP stack location 1551 * so we have it in our completion routine. 1552 */ 1553 irpStack = IoGetCurrentIrpStackLocation(irp); 1554 irpStack->DeviceObject = fdo; 1555 1556 /* 1557 * Now start setting up the next IRP stack location for the call like any driver would. 1558 */ 1559 irpStack = IoGetNextIrpStackLocation(irp); 1560 irpStack->Parameters.Scsi.Srb = srb; 1561 info->MediaChangeIrp = irp; 1562 1563 // 1564 // Initialize the SRB 1565 // 1566 1567 RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK)); 1568 1569 // 1570 // Initialize and set up the sense information buffer 1571 // 1572 1573 RtlZeroMemory(buffer, SENSE_BUFFER_SIZE); 1574 srb->SenseInfoBuffer = buffer; 1575 srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE; 1576 1577 // 1578 // Set default values for the media change notification 1579 // configuration. 1580 // 1581 1582 info->MediaChangeCountDown = MEDIA_CHANGE_DEFAULT_TIME; 1583 info->MediaChangeDetectionDisableCount = 0; 1584 1585 // 1586 // Assume that there is initially no media in the device 1587 // only notify upper layers if there is something there 1588 // 1589 1590 info->MediaChangeDetectionState = MediaUnknown; 1591 1592 info->MediaChangeIrpTimeInUse = 0; 1593 info->MediaChangeIrpLost = FALSE; 1594 1595 // 1596 // setup all extra flags we'll be setting for this irp 1597 // 1598 info->SrbFlags = 0; 1599 if (AllowDriveToSleep) { 1600 SET_FLAG(info->SrbFlags, SRB_FLAGS_NO_KEEP_AWAKE); 1601 } 1602 SET_FLAG(info->SrbFlags, SRB_CLASS_FLAGS_LOW_PRIORITY); 1603 SET_FLAG(info->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE); 1604 SET_FLAG(info->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER); 1605 1606 KeInitializeMutex(&info->MediaChangeMutex, 0x100); 1607 1608 // 1609 // It is ok to support media change events on this 1610 // device. 1611 // 1612 1613 FdoExtension->MediaChangeDetectionInfo = info; 1614 1615 // 1616 // NOTE: the DeviceType is FILE_DEVICE_CD_ROM even 1617 // when the device supports DVD (no need to 1618 // check for FILE_DEVICE_DVD, as it's not a 1619 // valid check). 1620 // 1621 1622 if (FdoExtension->DeviceObject->DeviceType == FILE_DEVICE_CD_ROM){ 1623 1624 NTSTATUS status; 1625 1626 KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN, 1627 "ClasspInitializePolling: Testing for GESN\n")); 1628 status = ClasspInitializeGesn(FdoExtension, info); 1629 if (NT_SUCCESS(status)) { 1630 KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN, 1631 "ClasspInitializePolling: GESN available " 1632 "for %p\n", FdoExtension->DeviceObject)); 1633 ASSERT(info->Gesn.Supported ); 1634 ASSERT(info->Gesn.Buffer != NULL); 1635 ASSERT(info->Gesn.BufferSize != 0); 1636 ASSERT(info->Gesn.EventMask != 0); 1637 // must return here, for ASSERTs to be valid. 1638 return STATUS_SUCCESS; 1639 } 1640 KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN, 1641 "ClasspInitializePolling: GESN *NOT* available " 1642 "for %p\n", FdoExtension->DeviceObject)); 1643 } 1644 1645 ASSERT(info->Gesn.Supported == 0); 1646 ASSERT(info->Gesn.Buffer == NULL); 1647 ASSERT(info->Gesn.BufferSize == 0); 1648 ASSERT(info->Gesn.EventMask == 0); 1649 info->Gesn.Supported = 0; // just in case.... 1650 return STATUS_SUCCESS; 1651 } 1652 1653 IoFreeIrp(irp); 1654 } 1655 1656 ExFreePool(info); 1657 } 1658 1659 // 1660 // nothing to free here 1661 // 1662 return STATUS_INSUFFICIENT_RESOURCES; 1663 1664 } // end ClasspInitializePolling() 1665 1666 NTSTATUS 1667 NTAPI 1668 ClasspInitializeGesn( 1669 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, 1670 IN PMEDIA_CHANGE_DETECTION_INFO Info 1671 ) 1672 { 1673 PNOTIFICATION_EVENT_STATUS_HEADER header; 1674 CLASS_DETECTION_STATE detectionState = ClassDetectionUnknown; 1675 PSTORAGE_ADAPTER_DESCRIPTOR adapterDescriptor; 1676 NTSTATUS status = STATUS_NOT_SUPPORTED; 1677 PIRP irp; 1678 KEVENT event; 1679 BOOLEAN retryImmediately; 1680 ULONG i; 1681 ULONG atapiResets; 1682 1683 1684 PAGED_CODE(); 1685 ASSERT(Info == FdoExtension->MediaChangeDetectionInfo); 1686 1687 // 1688 // read if we already know the abilities of the device 1689 // 1690 1691 ClassGetDeviceParameter(FdoExtension, 1692 CLASSP_REG_SUBKEY_NAME, 1693 CLASSP_REG_MMC_DETECTION_VALUE_NAME, 1694 (PULONG)&detectionState); 1695 1696 if (detectionState == ClassDetectionUnsupported) { 1697 goto ExitWithError; 1698 } 1699 1700 // 1701 // check if the device has a hack flag saying never to try this. 1702 // 1703 1704 if (TEST_FLAG(FdoExtension->PrivateFdoData->HackFlags, 1705 FDO_HACK_GESN_IS_BAD)) { 1706 1707 detectionState = ClassDetectionUnsupported; 1708 ClassSetDeviceParameter(FdoExtension, 1709 CLASSP_REG_SUBKEY_NAME, 1710 CLASSP_REG_MMC_DETECTION_VALUE_NAME, 1711 ClassDetectionSupported); 1712 goto ExitWithError; 1713 1714 } 1715 1716 1717 // 1718 // else go through the process since we allocate buffers and 1719 // get all sorts of device settings. 1720 // 1721 1722 if (Info->Gesn.Buffer == NULL) { 1723 Info->Gesn.Buffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, 1724 GESN_BUFFER_SIZE, 1725 '??cS'); 1726 } 1727 if (Info->Gesn.Buffer == NULL) { 1728 status = STATUS_INSUFFICIENT_RESOURCES; 1729 goto ExitWithError; 1730 } 1731 if (Info->Gesn.Mdl != NULL) { 1732 IoFreeMdl(Info->Gesn.Mdl); 1733 } 1734 Info->Gesn.Mdl = IoAllocateMdl(Info->Gesn.Buffer, 1735 GESN_BUFFER_SIZE, 1736 FALSE, FALSE, NULL); 1737 if (Info->Gesn.Mdl == NULL) { 1738 status = STATUS_INSUFFICIENT_RESOURCES; 1739 goto ExitWithError; 1740 } 1741 1742 MmBuildMdlForNonPagedPool(Info->Gesn.Mdl); 1743 Info->Gesn.BufferSize = GESN_BUFFER_SIZE; 1744 Info->Gesn.EventMask = 0; 1745 1746 // 1747 // all items are prepared to use GESN (except the event mask, so don't 1748 // optimize this part out!). 1749 // 1750 // now see if it really works. we have to loop through this because 1751 // many SAMSUNG (and one COMPAQ) drives timeout when requesting 1752 // NOT_READY events, even when the IMMEDIATE bit is set. :( 1753 // 1754 // using a drive list is cumbersome, so this might fix the problem. 1755 // 1756 1757 adapterDescriptor = FdoExtension->AdapterDescriptor; 1758 atapiResets = 0; 1759 retryImmediately = TRUE; 1760 for (i = 0; (i < 16) && (retryImmediately != FALSE); i++) { 1761 1762 irp = ClasspPrepareMcnIrp(FdoExtension, Info, TRUE); 1763 if (irp == NULL) { 1764 status = STATUS_INSUFFICIENT_RESOURCES; 1765 goto ExitWithError; 1766 } 1767 1768 ASSERT(TEST_FLAG(Info->MediaChangeSrb.SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE)); 1769 1770 // 1771 // replace the completion routine with a different one this time... 1772 // 1773 1774 IoSetCompletionRoutine(irp, 1775 ClassSignalCompletion, 1776 &event, 1777 TRUE, TRUE, TRUE); 1778 KeInitializeEvent(&event, SynchronizationEvent, FALSE); 1779 1780 status = IoCallDriver(FdoExtension->CommonExtension.LowerDeviceObject, irp); 1781 1782 if (status == STATUS_PENDING) { 1783 status = KeWaitForSingleObject(&event, 1784 Executive, 1785 KernelMode, 1786 FALSE, 1787 NULL); 1788 ASSERT(NT_SUCCESS(status)); 1789 } 1790 ClassReleaseRemoveLock(FdoExtension->DeviceObject, irp); 1791 1792 if (SRB_STATUS(Info->MediaChangeSrb.SrbStatus) != SRB_STATUS_SUCCESS) { 1793 ClassInterpretSenseInfo(FdoExtension->DeviceObject, 1794 &(Info->MediaChangeSrb), 1795 IRP_MJ_SCSI, 1796 0, 1797 0, 1798 &status, 1799 NULL); 1800 } 1801 1802 if ((adapterDescriptor->BusType == BusTypeAtapi) && 1803 (Info->MediaChangeSrb.SrbStatus == SRB_STATUS_BUS_RESET) 1804 ) { 1805 1806 // 1807 // ATAPI unfortunately returns SRB_STATUS_BUS_RESET instead 1808 // of SRB_STATUS_TIMEOUT, so we cannot differentiate between 1809 // the two. if we get this status four time consecutively, 1810 // stop trying this command. it is too late to change ATAPI 1811 // at this point, so special-case this here. (07/10/2001) 1812 // NOTE: any value more than 4 may cause the device to be 1813 // marked missing. 1814 // 1815 1816 atapiResets++; 1817 if (atapiResets >= 4) { 1818 status = STATUS_IO_DEVICE_ERROR; 1819 goto ExitWithError; 1820 } 1821 } 1822 1823 if (status == STATUS_DATA_OVERRUN) { 1824 status = STATUS_SUCCESS; 1825 } 1826 1827 if ((status == STATUS_INVALID_DEVICE_REQUEST) || 1828 (status == STATUS_TIMEOUT) || 1829 (status == STATUS_IO_DEVICE_ERROR) || 1830 (status == STATUS_IO_TIMEOUT) 1831 ) { 1832 1833 // 1834 // with these error codes, we don't ever want to try this command 1835 // again on this device, since it reacts poorly. 1836 // 1837 1838 ClassSetDeviceParameter(FdoExtension, 1839 CLASSP_REG_SUBKEY_NAME, 1840 CLASSP_REG_MMC_DETECTION_VALUE_NAME, 1841 ClassDetectionUnsupported); 1842 KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugWarning, 1843 "Classpnp => GESN test failed %x for fdo %p\n", 1844 status, FdoExtension->DeviceObject)); 1845 goto ExitWithError; 1846 1847 1848 } 1849 1850 if (!NT_SUCCESS(status)) { 1851 1852 // 1853 // this may be other errors that should not disable GESN 1854 // for all future start_device calls. 1855 // 1856 1857 KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugWarning, 1858 "Classpnp => GESN test failed %x for fdo %p\n", 1859 status, FdoExtension->DeviceObject)); 1860 goto ExitWithError; 1861 } 1862 1863 if (i == 0) { 1864 1865 // 1866 // the first time, the request was just retrieving a mask of 1867 // available bits. use this to mask future requests. 1868 // 1869 1870 header = (PNOTIFICATION_EVENT_STATUS_HEADER)(Info->Gesn.Buffer); 1871 1872 KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN, 1873 "Classpnp => Fdo %p supports event mask %x\n", 1874 FdoExtension->DeviceObject, header->SupportedEventClasses)); 1875 1876 1877 if (TEST_FLAG(header->SupportedEventClasses, 1878 NOTIFICATION_MEDIA_STATUS_CLASS_MASK)) { 1879 KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN, 1880 "Classpnp => GESN supports MCN\n")); 1881 } 1882 if (TEST_FLAG(header->SupportedEventClasses, 1883 NOTIFICATION_DEVICE_BUSY_CLASS_MASK)) { 1884 KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN, 1885 "Classpnp => GESN supports DeviceBusy\n")); 1886 } 1887 Info->Gesn.EventMask = header->SupportedEventClasses; 1888 1889 // 1890 // realistically, we are only considering the following events: 1891 // EXTERNAL REQUEST - this is being tested for play/stop/etc. 1892 // MEDIA STATUS - autorun and ejection requests. 1893 // DEVICE BUSY - to allow us to predict when media will be ready. 1894 // therefore, we should not bother querying for the other, 1895 // unknown events. clear all but the above flags. 1896 // 1897 1898 Info->Gesn.EventMask &= 1899 NOTIFICATION_EXTERNAL_REQUEST_CLASS_MASK | 1900 NOTIFICATION_MEDIA_STATUS_CLASS_MASK | 1901 NOTIFICATION_DEVICE_BUSY_CLASS_MASK ; 1902 1903 1904 // 1905 // HACKHACK - REF #0001 1906 // Some devices will *never* report an event if we've also requested 1907 // that it report lower-priority events. this is due to a 1908 // misunderstanding in the specification wherein a "No Change" is 1909 // interpreted to be a real event. what should occur is that the 1910 // device should ignore "No Change" events when multiple event types 1911 // are requested unless there are no other events waiting. this 1912 // greatly reduces the number of requests that the host must send 1913 // to determine if an event has occurred. Since we must work on all 1914 // drives, default to enabling the hack until we find evidence of 1915 // proper firmware. 1916 // 1917 1918 if (CountOfSetBitsUChar(Info->Gesn.EventMask) == 1) { 1919 KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN, 1920 "Classpnp => GESN hack %s for FDO %p\n", 1921 "not required", FdoExtension->DeviceObject)); 1922 } else { 1923 KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN, 1924 "Classpnp => GESN hack %s for FDO %p\n", 1925 "enabled", FdoExtension->DeviceObject)); 1926 Info->Gesn.HackEventMask = 1; 1927 } 1928 1929 } else { 1930 1931 // 1932 // not the first time looping through, so interpret the results. 1933 // 1934 1935 ClasspInterpretGesnData(FdoExtension, 1936 (PVOID)Info->Gesn.Buffer, 1937 &retryImmediately); 1938 1939 } 1940 1941 } // end loop of GESN requests.... 1942 1943 // 1944 // we can only use this if it can be relied upon for media changes, 1945 // since we are (by definition) no longer going to be polling via 1946 // a TEST_UNIT_READY irp, and drives will not report UNIT ATTENTION 1947 // for this command (although a filter driver, such as one for burning 1948 // cd's, might still fake those errors). 1949 // 1950 // since we also rely upon NOT_READY events to change the cursor 1951 // into a "wait" cursor, we can't use GESN without NOT_READY support. 1952 // 1953 1954 if (TEST_FLAG(Info->Gesn.EventMask, 1955 NOTIFICATION_MEDIA_STATUS_CLASS_MASK) && 1956 TEST_FLAG(Info->Gesn.EventMask, 1957 NOTIFICATION_DEVICE_BUSY_CLASS_MASK) 1958 ) { 1959 1960 KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN, 1961 "Classpnp => Enabling GESN support for fdo %p\n", 1962 FdoExtension->DeviceObject)); 1963 Info->Gesn.Supported = TRUE; 1964 1965 ClassSetDeviceParameter(FdoExtension, 1966 CLASSP_REG_SUBKEY_NAME, 1967 CLASSP_REG_MMC_DETECTION_VALUE_NAME, 1968 ClassDetectionSupported); 1969 1970 return STATUS_SUCCESS; 1971 1972 } 1973 1974 KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN, 1975 "Classpnp => GESN available but not enabled for fdo %p\n", 1976 FdoExtension->DeviceObject)); 1977 goto ExitWithError; 1978 1979 // fall through... 1980 1981 ExitWithError: 1982 if (Info->Gesn.Mdl) { 1983 IoFreeMdl(Info->Gesn.Mdl); 1984 Info->Gesn.Mdl = NULL; 1985 } 1986 if (Info->Gesn.Buffer) { 1987 ExFreePool(Info->Gesn.Buffer); 1988 Info->Gesn.Buffer = NULL; 1989 } 1990 Info->Gesn.Supported = 0; 1991 Info->Gesn.EventMask = 0; 1992 Info->Gesn.BufferSize = 0; 1993 return STATUS_NOT_SUPPORTED; 1994 1995 } 1996 1997 /*++//////////////////////////////////////////////////////////////////////////// 1998 1999 ClassInitializeTestUnitPolling() 2000 2001 Routine Description: 2002 2003 This routine will initialize MCN regardless of the settings stored 2004 in the registry. This should be used with caution, as some devices 2005 react badly to constant io. (i.e. never spin down, continuously cycling 2006 media in changers, ejection of media, etc.) It is highly suggested to 2007 use ClassInitializeMediaChangeDetection() instead. 2008 2009 Arguments: 2010 2011 FdoExtension is the device to poll 2012 2013 AllowDriveToSleep says whether to attempt to allow the drive to sleep 2014 or not. This only affects system-known spin down states, so if a 2015 drive spins itself down, this has no effect until the system spins 2016 it down. 2017 2018 Return Value: 2019 2020 --*/ 2021 NTSTATUS 2022 NTAPI 2023 ClassInitializeTestUnitPolling( 2024 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, 2025 IN BOOLEAN AllowDriveToSleep 2026 ) 2027 { 2028 return ClasspInitializePolling(FdoExtension, AllowDriveToSleep); 2029 } // end ClassInitializeTestUnitPolling() 2030 2031 /*++//////////////////////////////////////////////////////////////////////////// 2032 2033 ClassInitializeMediaChangeDetection() 2034 2035 Routine Description: 2036 2037 This routine checks to see if it is safe to initialize MCN (the back end 2038 to autorun) for a given device. It will then check the device-type wide 2039 key "Autorun" in the service key (for legacy reasons), and then look in 2040 the device-specific key to potentially override that setting. 2041 2042 If MCN is to be enabled, all necessary structures and memory are 2043 allocated and initialized. 2044 2045 This routine MUST be called only from the ClassInit() callback. 2046 2047 Arguments: 2048 2049 FdoExtension - the device to initialize MCN for, if appropriate 2050 2051 EventPrefix - unused, legacy argument. Set to zero. 2052 2053 Return Value: 2054 2055 --*/ 2056 VOID 2057 NTAPI 2058 ClassInitializeMediaChangeDetection( 2059 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, 2060 IN PUCHAR EventPrefix 2061 ) 2062 { 2063 PDEVICE_OBJECT fdo = FdoExtension->DeviceObject; 2064 NTSTATUS status; 2065 2066 PCLASS_DRIVER_EXTENSION driverExtension = ClassGetDriverExtension( 2067 fdo->DriverObject); 2068 2069 BOOLEAN disabledForBadHardware; 2070 BOOLEAN disabled; 2071 BOOLEAN instanceOverride; 2072 2073 PAGED_CODE(); 2074 2075 // 2076 // NOTE: This assumes that ClassInitializeMediaChangeDetection is always 2077 // called in the context of the ClassInitDevice callback. If called 2078 // after then this check will have already been made and the 2079 // once a second timer will not have been enabled. 2080 // 2081 2082 disabledForBadHardware = ClasspIsMediaChangeDisabledDueToHardwareLimitation( 2083 FdoExtension, 2084 &(driverExtension->RegistryPath) 2085 ); 2086 2087 if (disabledForBadHardware) { 2088 DebugPrint((ClassDebugMCN, 2089 "ClassInitializeMCN: Disabled due to hardware" 2090 "limitations for this device")); 2091 return; 2092 } 2093 2094 // 2095 // autorun should now be enabled by default for all media types. 2096 // 2097 2098 disabled = ClasspIsMediaChangeDisabledForClass( 2099 FdoExtension, 2100 &(driverExtension->RegistryPath) 2101 ); 2102 2103 DebugPrint((ClassDebugMCN, 2104 "ClassInitializeMCN: Class MCN is %s\n", 2105 (disabled ? "disabled" : "enabled"))); 2106 2107 status = ClasspMediaChangeDeviceInstanceOverride( 2108 FdoExtension, 2109 &instanceOverride); // default value 2110 2111 if (!NT_SUCCESS(status)) { 2112 DebugPrint((ClassDebugMCN, 2113 "ClassInitializeMCN: Instance using default\n")); 2114 } else { 2115 DebugPrint((ClassDebugMCN, 2116 "ClassInitializeMCN: Instance override: %s MCN\n", 2117 (instanceOverride ? "Enabling" : "Disabling"))); 2118 disabled = !instanceOverride; 2119 } 2120 2121 DebugPrint((ClassDebugMCN, 2122 "ClassInitializeMCN: Instance MCN is %s\n", 2123 (disabled ? "disabled" : "enabled"))); 2124 2125 if (disabled) { 2126 return; 2127 } 2128 2129 // 2130 // if the drive is not a CDROM, allow the drive to sleep 2131 // 2132 if (FdoExtension->DeviceObject->DeviceType == FILE_DEVICE_CD_ROM) { 2133 ClasspInitializePolling(FdoExtension, FALSE); 2134 } else { 2135 ClasspInitializePolling(FdoExtension, TRUE); 2136 } 2137 2138 return; 2139 } // end ClassInitializeMediaChangeDetection() 2140 2141 /*++//////////////////////////////////////////////////////////////////////////// 2142 2143 ClasspMediaChangeDeviceInstanceOverride() 2144 2145 Routine Description: 2146 2147 The user can override the global setting to enable or disable Autorun on a 2148 specific cdrom device via the control panel. This routine checks and/or 2149 sets this value. 2150 2151 Arguments: 2152 2153 FdoExtension - the device to set/get the value for 2154 Value - the value to use in a set 2155 SetValue - whether to set the value 2156 2157 Return Value: 2158 2159 TRUE - Autorun is disabled 2160 FALSE - Autorun is not disabled (Default) 2161 2162 --*/ 2163 NTSTATUS 2164 NTAPI 2165 ClasspMediaChangeDeviceInstanceOverride( 2166 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, 2167 OUT PBOOLEAN Enabled 2168 ) 2169 { 2170 HANDLE deviceParameterHandle; // cdrom instance key 2171 HANDLE driverParameterHandle; // cdrom specific key 2172 RTL_QUERY_REGISTRY_TABLE queryTable[3]; 2173 OBJECT_ATTRIBUTES objectAttributes; 2174 UNICODE_STRING subkeyName; 2175 NTSTATUS status; 2176 ULONG alwaysEnable; 2177 ULONG alwaysDisable; 2178 ULONG i; 2179 2180 2181 PAGED_CODE(); 2182 2183 deviceParameterHandle = NULL; 2184 driverParameterHandle = NULL; 2185 status = STATUS_UNSUCCESSFUL; 2186 alwaysEnable = FALSE; 2187 alwaysDisable = FALSE; 2188 2189 TRY { 2190 2191 status = IoOpenDeviceRegistryKey( FdoExtension->LowerPdo, 2192 PLUGPLAY_REGKEY_DEVICE, 2193 KEY_ALL_ACCESS, 2194 &deviceParameterHandle 2195 ); 2196 if (!NT_SUCCESS(status)) { 2197 2198 // 2199 // this can occur when a new device is added to the system 2200 // this is due to cdrom.sys being an 'essential' driver 2201 // 2202 DebugPrint((ClassDebugMCN, 2203 "ClassMediaChangeDeviceInstanceDisabled: " 2204 "Could not open device registry key [%lx]\n", status)); 2205 LEAVE; 2206 } 2207 2208 RtlInitUnicodeString(&subkeyName, MCN_REG_SUBKEY_NAME); 2209 InitializeObjectAttributes(&objectAttributes, 2210 &subkeyName, 2211 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 2212 deviceParameterHandle, 2213 (PSECURITY_DESCRIPTOR) NULL); 2214 2215 status = ZwCreateKey(&driverParameterHandle, 2216 KEY_READ, 2217 &objectAttributes, 2218 0, 2219 (PUNICODE_STRING) NULL, 2220 REG_OPTION_NON_VOLATILE, 2221 NULL); 2222 2223 if (!NT_SUCCESS(status)) { 2224 DebugPrint((ClassDebugMCN, 2225 "ClassMediaChangeDeviceInstanceDisabled: " 2226 "subkey could not be created. %lx\n", status)); 2227 LEAVE; 2228 } 2229 2230 // 2231 // Default to not changing autorun behavior, based upon setting 2232 // registryValue to zero. 2233 // 2234 2235 for (i=0;i<2;i++) { 2236 2237 RtlZeroMemory(&queryTable[0], sizeof(queryTable)); 2238 2239 queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT; 2240 queryTable[0].DefaultType = REG_DWORD; 2241 queryTable[0].DefaultLength = 0; 2242 2243 if (i==0) { 2244 queryTable[0].Name = MCN_REG_AUTORUN_DISABLE_INSTANCE_NAME; 2245 queryTable[0].EntryContext = &alwaysDisable; 2246 queryTable[0].DefaultData = &alwaysDisable; 2247 } else { 2248 queryTable[0].Name = MCN_REG_AUTORUN_ENABLE_INSTANCE_NAME; 2249 queryTable[0].EntryContext = &alwaysEnable; 2250 queryTable[0].DefaultData = &alwaysEnable; 2251 } 2252 2253 // 2254 // don't care if it succeeds, since we set defaults above 2255 // 2256 2257 RtlQueryRegistryValues(RTL_REGISTRY_HANDLE, 2258 (PWSTR)driverParameterHandle, 2259 queryTable, 2260 NULL, 2261 NULL); 2262 } 2263 2264 } FINALLY { 2265 2266 if (driverParameterHandle) ZwClose(driverParameterHandle); 2267 if (deviceParameterHandle) ZwClose(deviceParameterHandle); 2268 2269 } 2270 2271 if (alwaysEnable && alwaysDisable) { 2272 2273 DebugPrint((ClassDebugMCN, 2274 "ClassMediaChangeDeviceInstanceDisabled: %s selected\n", 2275 "Both Enable and Disable set -- DISABLE")); 2276 ASSERT(NT_SUCCESS(status)); 2277 status = STATUS_SUCCESS; 2278 *Enabled = FALSE; 2279 2280 } else if (alwaysDisable) { 2281 2282 DebugPrint((ClassDebugMCN, 2283 "ClassMediaChangeDeviceInstanceDisabled: %s selected\n", 2284 "DISABLE")); 2285 ASSERT(NT_SUCCESS(status)); 2286 status = STATUS_SUCCESS; 2287 *Enabled = FALSE; 2288 2289 } else if (alwaysEnable) { 2290 2291 DebugPrint((ClassDebugMCN, 2292 "ClassMediaChangeDeviceInstanceDisabled: %s selected\n", 2293 "ENABLE")); 2294 ASSERT(NT_SUCCESS(status)); 2295 status = STATUS_SUCCESS; 2296 *Enabled = TRUE; 2297 2298 } else { 2299 2300 DebugPrint((ClassDebugMCN, 2301 "ClassMediaChangeDeviceInstanceDisabled: %s selected\n", 2302 "DEFAULT")); 2303 status = STATUS_UNSUCCESSFUL; 2304 2305 } 2306 2307 return status; 2308 2309 } // end ClasspMediaChangeDeviceInstanceOverride() 2310 2311 /*++//////////////////////////////////////////////////////////////////////////// 2312 2313 ClasspIsMediaChangeDisabledDueToHardwareLimitation() 2314 2315 Routine Description: 2316 2317 The key AutoRunAlwaysDisable contains a MULTI_SZ of hardware IDs for 2318 which to never enable MediaChangeNotification. 2319 2320 The user can override the global setting to enable or disable Autorun on a 2321 specific cdrom device via the control panel. 2322 2323 Arguments: 2324 2325 FdoExtension - 2326 RegistryPath - pointer to the unicode string inside 2327 ...\CurrentControlSet\Services\Cdrom 2328 2329 Return Value: 2330 2331 TRUE - no autorun. 2332 FALSE - Autorun may be enabled 2333 2334 --*/ 2335 BOOLEAN 2336 NTAPI 2337 ClasspIsMediaChangeDisabledDueToHardwareLimitation( 2338 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, 2339 IN PUNICODE_STRING RegistryPath 2340 ) 2341 { 2342 PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor = FdoExtension->DeviceDescriptor; 2343 OBJECT_ATTRIBUTES objectAttributes; 2344 HANDLE serviceKey = NULL; 2345 RTL_QUERY_REGISTRY_TABLE parameters[2]; 2346 2347 UNICODE_STRING deviceUnicodeString; 2348 ANSI_STRING deviceString; 2349 ULONG mediaChangeNotificationDisabled = FALSE; 2350 2351 NTSTATUS status; 2352 2353 2354 PAGED_CODE(); 2355 2356 // 2357 // open the service key. 2358 // 2359 2360 InitializeObjectAttributes(&objectAttributes, 2361 RegistryPath, 2362 OBJ_CASE_INSENSITIVE, 2363 NULL, 2364 NULL); 2365 2366 status = ZwOpenKey(&serviceKey, 2367 KEY_READ, 2368 &objectAttributes); 2369 2370 ASSERT(NT_SUCCESS(status)); 2371 2372 2373 if(!NT_SUCCESS(status)) { 2374 2375 // 2376 // always take the safe path. if we can't open the service key, 2377 // disable autorun 2378 // 2379 2380 return TRUE; 2381 2382 } 2383 2384 TRY { 2385 // 2386 // Determine if drive is in a list of those requiring 2387 // autorun to be disabled. this is stored in a REG_MULTI_SZ 2388 // named AutoRunAlwaysDisable. this is required as some autochangers 2389 // must load the disc to reply to ChkVerify request, causing them 2390 // to cycle discs continuously. 2391 // 2392 2393 PWSTR nullMultiSz; 2394 PCSTR vendorId; 2395 PCSTR productId; 2396 PCSTR revisionId; 2397 ULONG length; 2398 ULONG offset; 2399 2400 deviceString.Buffer = NULL; 2401 deviceUnicodeString.Buffer = NULL; 2402 2403 // 2404 // there may be nothing to check against 2405 // 2406 2407 if ((deviceDescriptor->VendorIdOffset == 0) && 2408 (deviceDescriptor->ProductIdOffset == 0)) { 2409 LEAVE; 2410 } 2411 2412 length = 0; 2413 2414 if (deviceDescriptor->VendorIdOffset == 0) { 2415 vendorId = NULL; 2416 } else { 2417 vendorId = (PCSTR) deviceDescriptor + deviceDescriptor->VendorIdOffset; 2418 length = strlen(vendorId); 2419 } 2420 2421 if ( deviceDescriptor->ProductIdOffset == 0 ) { 2422 productId = NULL; 2423 } else { 2424 productId = (PCSTR) deviceDescriptor + deviceDescriptor->ProductIdOffset; 2425 length += strlen(productId); 2426 } 2427 2428 if ( deviceDescriptor->ProductRevisionOffset == 0 ) { 2429 revisionId = NULL; 2430 } else { 2431 revisionId = (PCSTR) deviceDescriptor + deviceDescriptor->ProductRevisionOffset; 2432 length += strlen(revisionId); 2433 } 2434 2435 // 2436 // allocate a buffer for the string 2437 // 2438 2439 deviceString.Length = (USHORT)( length ); 2440 deviceString.MaximumLength = deviceString.Length + 1; 2441 deviceString.Buffer = ExAllocatePoolWithTag( NonPagedPool, 2442 deviceString.MaximumLength, 2443 CLASS_TAG_AUTORUN_DISABLE 2444 ); 2445 if (deviceString.Buffer == NULL) { 2446 DebugPrint((ClassDebugMCN, 2447 "ClassMediaChangeDisabledForHardware: Unable to alloc " 2448 "string buffer\n" )); 2449 LEAVE; 2450 } 2451 2452 // 2453 // copy strings to the buffer 2454 // 2455 offset = 0; 2456 2457 if (vendorId != NULL) { 2458 RtlCopyMemory(deviceString.Buffer + offset, 2459 vendorId, 2460 strlen(vendorId)); 2461 offset += strlen(vendorId); 2462 } 2463 2464 if ( productId != NULL ) { 2465 RtlCopyMemory(deviceString.Buffer + offset, 2466 productId, 2467 strlen(productId)); 2468 offset += strlen(productId); 2469 } 2470 if ( revisionId != NULL ) { 2471 RtlCopyMemory(deviceString.Buffer + offset, 2472 revisionId, 2473 strlen(revisionId)); 2474 offset += strlen(revisionId); 2475 } 2476 2477 ASSERT(offset == deviceString.Length); 2478 2479 deviceString.Buffer[deviceString.Length] = '\0'; // Null-terminated 2480 2481 // 2482 // convert to unicode as registry deals with unicode strings 2483 // 2484 2485 status = RtlAnsiStringToUnicodeString( &deviceUnicodeString, 2486 &deviceString, 2487 TRUE 2488 ); 2489 if (!NT_SUCCESS(status)) { 2490 DebugPrint((ClassDebugMCN, 2491 "ClassMediaChangeDisabledForHardware: cannot convert " 2492 "to unicode %lx\n", status)); 2493 LEAVE; 2494 } 2495 2496 // 2497 // query the value, setting valueFound to true if found 2498 // 2499 2500 RtlZeroMemory(parameters, sizeof(parameters)); 2501 2502 nullMultiSz = L"\0"; 2503 parameters[0].QueryRoutine = ClasspMediaChangeRegistryCallBack; 2504 parameters[0].Flags = RTL_QUERY_REGISTRY_REQUIRED; 2505 parameters[0].Name = L"AutoRunAlwaysDisable"; 2506 parameters[0].EntryContext = &mediaChangeNotificationDisabled; 2507 parameters[0].DefaultType = REG_MULTI_SZ; 2508 parameters[0].DefaultData = nullMultiSz; 2509 parameters[0].DefaultLength = 0; 2510 2511 status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE, 2512 serviceKey, 2513 parameters, 2514 &deviceUnicodeString, 2515 NULL); 2516 2517 if ( !NT_SUCCESS(status) ) { 2518 LEAVE; 2519 } 2520 2521 } FINALLY { 2522 2523 if (deviceString.Buffer != NULL) { 2524 ExFreePool( deviceString.Buffer ); 2525 } 2526 if (deviceUnicodeString.Buffer != NULL) { 2527 RtlFreeUnicodeString( &deviceUnicodeString ); 2528 } 2529 2530 ZwClose(serviceKey); 2531 } 2532 2533 if (mediaChangeNotificationDisabled) { 2534 DebugPrint((ClassDebugMCN, "ClassMediaChangeDisabledForHardware: " 2535 "Device is on disable list\n")); 2536 return TRUE; 2537 } 2538 return FALSE; 2539 2540 } // end ClasspIsMediaChangeDisabledDueToHardwareLimitation() 2541 2542 /*++//////////////////////////////////////////////////////////////////////////// 2543 2544 ClasspIsMediaChangeDisabledForClass() 2545 2546 Routine Description: 2547 2548 The user must specify that AutoPlay is to run on the platform 2549 by setting the registry value HKEY_LOCAL_MACHINE\System\CurrentControlSet\ 2550 Services\<SERVICE>\Autorun:REG_DWORD:1. 2551 2552 The user can override the global setting to enable or disable Autorun on a 2553 specific cdrom device via the control panel. 2554 2555 Arguments: 2556 2557 FdoExtension - 2558 RegistryPath - pointer to the unicode string inside 2559 ...\CurrentControlSet\Services\Cdrom 2560 2561 Return Value: 2562 2563 TRUE - Autorun is disabled for this class 2564 FALSE - Autorun is enabled for this class 2565 2566 --*/ 2567 BOOLEAN 2568 NTAPI 2569 ClasspIsMediaChangeDisabledForClass( 2570 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, 2571 IN PUNICODE_STRING RegistryPath 2572 ) 2573 { 2574 //PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor = FdoExtension->DeviceDescriptor; 2575 2576 OBJECT_ATTRIBUTES objectAttributes; 2577 HANDLE serviceKey = NULL; 2578 HANDLE parametersKey = NULL; 2579 RTL_QUERY_REGISTRY_TABLE parameters[3]; 2580 2581 UNICODE_STRING paramStr; 2582 //UNICODE_STRING deviceUnicodeString; 2583 //ANSI_STRING deviceString; 2584 2585 // 2586 // Default to ENABLING MediaChangeNotification (!) 2587 // 2588 2589 ULONG mcnRegistryValue = 1; 2590 2591 NTSTATUS status; 2592 2593 2594 PAGED_CODE(); 2595 2596 // 2597 // open the service key. 2598 // 2599 2600 InitializeObjectAttributes(&objectAttributes, 2601 RegistryPath, 2602 OBJ_CASE_INSENSITIVE, 2603 NULL, 2604 NULL); 2605 2606 status = ZwOpenKey(&serviceKey, 2607 KEY_READ, 2608 &objectAttributes); 2609 2610 ASSERT(NT_SUCCESS(status)); 2611 2612 if(!NT_SUCCESS(status)) { 2613 2614 // 2615 // return the default value, which is the 2616 // inverse of the registry setting default 2617 // since this routine asks if it's disabled 2618 // 2619 2620 DebugPrint((ClassDebugMCN, "ClassCheckServiceMCN: Defaulting to %s\n", 2621 (mcnRegistryValue ? "Enabled" : "Disabled"))); 2622 return (BOOLEAN)(!mcnRegistryValue); 2623 2624 } 2625 2626 RtlZeroMemory(parameters, sizeof(parameters)); 2627 2628 // 2629 // Open the parameters key (if any) beneath the services key. 2630 // 2631 2632 RtlInitUnicodeString(¶mStr, L"Parameters"); 2633 2634 InitializeObjectAttributes(&objectAttributes, 2635 ¶mStr, 2636 OBJ_CASE_INSENSITIVE, 2637 serviceKey, 2638 NULL); 2639 2640 status = ZwOpenKey(¶metersKey, 2641 KEY_READ, 2642 &objectAttributes); 2643 2644 if (!NT_SUCCESS(status)) { 2645 parametersKey = NULL; 2646 } 2647 2648 2649 2650 // 2651 // Check for the Autorun value. 2652 // 2653 2654 parameters[0].Flags = RTL_QUERY_REGISTRY_DIRECT; 2655 parameters[0].Name = L"Autorun"; 2656 parameters[0].EntryContext = &mcnRegistryValue; 2657 parameters[0].DefaultType = REG_DWORD; 2658 parameters[0].DefaultData = &mcnRegistryValue; 2659 parameters[0].DefaultLength = sizeof(ULONG); 2660 2661 status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE | RTL_REGISTRY_OPTIONAL, 2662 serviceKey, 2663 parameters, 2664 NULL, 2665 NULL); 2666 2667 DebugPrint((ClassDebugMCN, "ClassCheckServiceMCN: " 2668 "<Service>/Autorun flag = %d\n", mcnRegistryValue)); 2669 2670 if(parametersKey != NULL) { 2671 2672 status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE | RTL_REGISTRY_OPTIONAL, 2673 parametersKey, 2674 parameters, 2675 NULL, 2676 NULL); 2677 DebugPrint((ClassDebugMCN, "ClassCheckServiceMCN: " 2678 "<Service>/Parameters/Autorun flag = %d\n", 2679 mcnRegistryValue)); 2680 ZwClose(parametersKey); 2681 2682 } 2683 ZwClose(serviceKey); 2684 2685 DebugPrint((ClassDebugMCN, "ClassCheckServiceMCN: " 2686 "Autoplay for device %p is %s\n", 2687 FdoExtension->DeviceObject, 2688 (mcnRegistryValue ? "on" : "off") 2689 )); 2690 2691 // 2692 // return if it is _disabled_, which is the 2693 // inverse of the registry setting 2694 // 2695 2696 return (BOOLEAN)(!mcnRegistryValue); 2697 } // end ClasspIsMediaChangeDisabledForClass() 2698 2699 /*++//////////////////////////////////////////////////////////////////////////// 2700 2701 ClassEnableMediaChangeDetection() ISSUE-2000/02/20-henrygab - why public? 2702 ClassEnableMediaChangeDetection() ISSUE-2000/02/20-henrygab - not documented 2703 2704 Routine Description: 2705 2706 This routine 2707 2708 Arguments: 2709 2710 DeviceObject - 2711 Irp - 2712 2713 Return Value: 2714 2715 --*/ 2716 VOID 2717 NTAPI 2718 ClassEnableMediaChangeDetection( 2719 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension 2720 ) 2721 { 2722 PMEDIA_CHANGE_DETECTION_INFO info = FdoExtension->MediaChangeDetectionInfo; 2723 LONG oldCount; 2724 2725 PAGED_CODE(); 2726 2727 if(info == NULL) { 2728 DebugPrint((ClassDebugMCN, 2729 "ClassEnableMediaChangeDetection: not initialized\n")); 2730 return; 2731 } 2732 2733 KeWaitForMutexObject(&info->MediaChangeMutex, 2734 UserRequest, 2735 KernelMode, 2736 FALSE, 2737 NULL); 2738 2739 oldCount = --info->MediaChangeDetectionDisableCount; 2740 2741 ASSERT(oldCount >= 0); 2742 2743 DebugPrint((ClassDebugMCN, "ClassEnableMediaChangeDetection: Disable count " 2744 "reduced to %d - ", 2745 info->MediaChangeDetectionDisableCount)); 2746 2747 if(oldCount == 0) { 2748 2749 // 2750 // We don't know what state the media is in anymore. 2751 // 2752 2753 ClasspInternalSetMediaChangeState(FdoExtension, 2754 MediaUnknown, 2755 FALSE 2756 ); 2757 2758 // 2759 // Reset the MCN timer. 2760 // 2761 2762 ClassResetMediaChangeTimer(FdoExtension); 2763 2764 DebugPrint((ClassDebugMCN, "MCD is enabled\n")); 2765 2766 } else { 2767 2768 DebugPrint((ClassDebugMCN, "MCD still disabled\n")); 2769 2770 } 2771 2772 2773 // 2774 // Let something else run. 2775 // 2776 2777 KeReleaseMutex(&info->MediaChangeMutex, FALSE); 2778 2779 return; 2780 } // end ClassEnableMediaChangeDetection() 2781 2782 /*++//////////////////////////////////////////////////////////////////////////// 2783 2784 ClassDisableMediaChangeDetection() ISSUE-2000/02/20-henrygab - why public? 2785 ClassDisableMediaChangeDetection() ISSUE-2000/02/20-henrygab - not documented 2786 2787 Routine Description: 2788 2789 This routine 2790 2791 Arguments: 2792 2793 DeviceObject - 2794 Irp - 2795 2796 Return Value: 2797 2798 --*/ 2799 ULONG BreakOnMcnDisable = FALSE; 2800 2801 VOID 2802 NTAPI 2803 ClassDisableMediaChangeDetection( 2804 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension 2805 ) 2806 { 2807 PMEDIA_CHANGE_DETECTION_INFO info = FdoExtension->MediaChangeDetectionInfo; 2808 2809 PAGED_CODE(); 2810 2811 if(info == NULL) { 2812 return; 2813 } 2814 2815 KeWaitForMutexObject(&info->MediaChangeMutex, 2816 UserRequest, 2817 KernelMode, 2818 FALSE, 2819 NULL); 2820 2821 info->MediaChangeDetectionDisableCount++; 2822 2823 DebugPrint((ClassDebugMCN, "ClassDisableMediaChangeDetection: " 2824 "disable count is %d\n", 2825 info->MediaChangeDetectionDisableCount)); 2826 2827 KeReleaseMutex(&info->MediaChangeMutex, FALSE); 2828 2829 return; 2830 } // end ClassDisableMediaChangeDetection() 2831 2832 /*++//////////////////////////////////////////////////////////////////////////// 2833 2834 ClassCleanupMediaChangeDetection() ISSUE-2000/02/20-henrygab - why public?! 2835 2836 Routine Description: 2837 2838 This routine will cleanup any resources allocated for MCN. It is called 2839 by classpnp during remove device, and therefore is not typically required 2840 by external drivers. 2841 2842 Arguments: 2843 2844 Return Value: 2845 2846 --*/ 2847 VOID 2848 NTAPI 2849 ClassCleanupMediaChangeDetection( 2850 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension 2851 ) 2852 { 2853 PMEDIA_CHANGE_DETECTION_INFO info = FdoExtension->MediaChangeDetectionInfo; 2854 2855 PAGED_CODE() 2856 2857 if(info == NULL) { 2858 return; 2859 } 2860 2861 FdoExtension->MediaChangeDetectionInfo = NULL; 2862 2863 if (info->Gesn.Buffer) { 2864 ExFreePool(info->Gesn.Buffer); 2865 } 2866 IoFreeIrp(info->MediaChangeIrp); 2867 ExFreePool(info->SenseBuffer); 2868 ExFreePool(info); 2869 return; 2870 } // end ClassCleanupMediaChangeDetection() 2871 2872 /*++//////////////////////////////////////////////////////////////////////////// 2873 2874 ClasspMcnControl() - ISSUE-2000/02/20-henrygab - not documented 2875 2876 Routine Description: 2877 2878 This routine 2879 2880 Arguments: 2881 2882 DeviceObject - 2883 Irp - 2884 2885 Return Value: 2886 2887 --*/ 2888 NTSTATUS 2889 NTAPI 2890 ClasspMcnControl( 2891 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, 2892 IN PIRP Irp, 2893 IN PSCSI_REQUEST_BLOCK Srb 2894 ) 2895 { 2896 PCOMMON_DEVICE_EXTENSION commonExtension = 2897 (PCOMMON_DEVICE_EXTENSION) FdoExtension; 2898 2899 PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); 2900 PPREVENT_MEDIA_REMOVAL request = Irp->AssociatedIrp.SystemBuffer; 2901 2902 PFILE_OBJECT fileObject = irpStack->FileObject; 2903 PFILE_OBJECT_EXTENSION fsContext = NULL; 2904 2905 NTSTATUS status = STATUS_SUCCESS; 2906 2907 PAGED_CODE(); 2908 2909 // 2910 // Check to make sure we have a file object extension to keep track of this 2911 // request. If not we'll fail it before synchronizing. 2912 // 2913 2914 TRY { 2915 2916 if(fileObject != NULL) { 2917 fsContext = ClasspGetFsContext(commonExtension, fileObject); 2918 }else if(Irp->RequestorMode == KernelMode) { // && fileObject == NULL 2919 fsContext = &FdoExtension->KernelModeMcnContext; 2920 } 2921 2922 if (fsContext == NULL) { 2923 2924 // 2925 // This handle isn't setup correctly. We can't let the 2926 // operation go. 2927 // 2928 2929 status = STATUS_INVALID_PARAMETER; 2930 LEAVE; 2931 } 2932 2933 if(request->PreventMediaRemoval) { 2934 2935 // 2936 // This is a lock command. Reissue the command in case bus or 2937 // device was reset and the lock was cleared. 2938 // 2939 2940 ClassDisableMediaChangeDetection(FdoExtension); 2941 InterlockedIncrement((PLONG)&fsContext->McnDisableCount); 2942 2943 } else { 2944 2945 if(fsContext->McnDisableCount == 0) { 2946 status = STATUS_INVALID_DEVICE_STATE; 2947 LEAVE; 2948 } 2949 2950 InterlockedDecrement((PLONG)&fsContext->McnDisableCount); 2951 ClassEnableMediaChangeDetection(FdoExtension); 2952 } 2953 2954 } FINALLY { 2955 2956 Irp->IoStatus.Status = status; 2957 2958 if(Srb) { 2959 ExFreePool(Srb); 2960 } 2961 2962 ClassReleaseRemoveLock(FdoExtension->DeviceObject, Irp); 2963 ClassCompleteRequest(FdoExtension->DeviceObject, 2964 Irp, 2965 IO_NO_INCREMENT); 2966 } 2967 return status; 2968 } // end ClasspMcnControl( 2969 2970 /*++//////////////////////////////////////////////////////////////////////////// 2971 2972 ClasspMediaChangeRegistryCallBack() 2973 2974 Routine Description: 2975 2976 This callback for a registry SZ or MULTI_SZ is called once for each 2977 SZ in the value. It will attempt to match the data with the 2978 UNICODE_STRING passed in as Context, and modify EntryContext if a 2979 match is found. Written for ClasspCheckRegistryForMediaChangeCompletion 2980 2981 Arguments: 2982 2983 ValueName - name of the key that was opened 2984 ValueType - type of data stored in the value (REG_SZ for this routine) 2985 ValueData - data in the registry, in this case a wide string 2986 ValueLength - length of the data including the terminating null 2987 Context - unicode string to compare against ValueData 2988 EntryContext - should be initialized to 0, will be set to 1 if match found 2989 2990 Return Value: 2991 2992 STATUS_SUCCESS 2993 EntryContext will be 1 if found 2994 2995 --*/ 2996 NTSTATUS 2997 NTAPI 2998 ClasspMediaChangeRegistryCallBack( 2999 IN PWSTR ValueName, 3000 IN ULONG ValueType, 3001 IN PVOID ValueData, 3002 IN ULONG ValueLength, 3003 IN PVOID Context, 3004 IN PVOID EntryContext 3005 ) 3006 { 3007 PULONG valueFound; 3008 PUNICODE_STRING deviceString; 3009 PWSTR keyValue; 3010 3011 PAGED_CODE(); 3012 UNREFERENCED_PARAMETER(ValueName); 3013 3014 3015 // 3016 // if we have already set the value to true, exit 3017 // 3018 3019 valueFound = EntryContext; 3020 if ((*valueFound) != 0) { 3021 DebugPrint((ClassDebugMCN, "ClasspMcnRegCB: already set to true\n")); 3022 return STATUS_SUCCESS; 3023 } 3024 3025 if (ValueLength == sizeof(WCHAR)) { 3026 DebugPrint((ClassDebugError, "ClasspMcnRegCB: NULL string should " 3027 "never be passed to registry call-back!\n")); 3028 return STATUS_SUCCESS; 3029 } 3030 3031 3032 // 3033 // if the data is not a terminated string, exit 3034 // 3035 3036 if (ValueType != REG_SZ) { 3037 return STATUS_SUCCESS; 3038 } 3039 3040 deviceString = Context; 3041 keyValue = ValueData; 3042 ValueLength -= sizeof(WCHAR); // ignore the null character 3043 3044 // 3045 // do not compare more memory than is in deviceString 3046 // 3047 3048 if (ValueLength > deviceString->Length) { 3049 ValueLength = deviceString->Length; 3050 } 3051 3052 // 3053 // if the strings match, disable autorun 3054 // 3055 3056 if (RtlCompareMemory(deviceString->Buffer, keyValue, ValueLength) == ValueLength) { 3057 DebugPrint((ClassDebugMCN, "ClasspRegMcnCB: Match found\n")); 3058 DebugPrint((ClassDebugMCN, "ClasspRegMcnCB: DeviceString at %p\n", 3059 deviceString->Buffer)); 3060 DebugPrint((ClassDebugMCN, "ClasspRegMcnCB: KeyValue at %p\n", 3061 keyValue)); 3062 (*valueFound) = TRUE; 3063 } 3064 3065 return STATUS_SUCCESS; 3066 } // end ClasspMediaChangeRegistryCallBack() 3067 3068 /*++//////////////////////////////////////////////////////////////////////////// 3069 3070 ClasspTimerTick() - ISSUE-2000/02/20-henrygab - not documented 3071 3072 Routine Description: 3073 3074 This routine 3075 3076 Arguments: 3077 3078 DeviceObject - 3079 Irp - 3080 3081 Return Value: 3082 3083 --*/ 3084 VOID 3085 NTAPI 3086 ClasspTimerTick( 3087 PDEVICE_OBJECT DeviceObject, 3088 PVOID Context 3089 ) 3090 { 3091 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension; 3092 PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; 3093 ULONG isRemoved; 3094 3095 ASSERT(commonExtension->IsFdo); 3096 3097 // 3098 // Do any media change work 3099 // 3100 isRemoved = ClassAcquireRemoveLock(DeviceObject, (PIRP)ClasspTimerTick); 3101 3102 // 3103 // We stop the timer before deleting the device. It's safe to keep going 3104 // if the flag value is REMOVE_PENDING because the removal thread will be 3105 // blocked trying to stop the timer. 3106 // 3107 3108 ASSERT(isRemoved != REMOVE_COMPLETE); 3109 3110 // 3111 // This routine is reasonably safe even if the device object has a pending 3112 // remove 3113 3114 if(!isRemoved) { 3115 3116 PFAILURE_PREDICTION_INFO info = fdoExtension->FailurePredictionInfo; 3117 3118 // 3119 // Do any media change detection work 3120 // 3121 3122 if (fdoExtension->MediaChangeDetectionInfo != NULL) { 3123 3124 ClassCheckMediaState(fdoExtension); 3125 3126 } 3127 3128 // 3129 // Do any failure prediction work 3130 // 3131 if ((info != NULL) && (info->Method != FailurePredictionNone)) { 3132 3133 ULONG countDown; 3134 //ULONG active; 3135 3136 if (ClasspCanSendPollingIrp(fdoExtension)) { 3137 3138 // 3139 // Synchronization is not required here since the Interlocked 3140 // locked instruction guarantees atomicity. Other code that 3141 // resets CountDown uses InterlockedExchange which is also 3142 // atomic. 3143 // 3144 countDown = InterlockedDecrement((PLONG)&info->CountDown); 3145 if (countDown == 0) { 3146 3147 DebugPrint((4, "ClasspTimerTick: Send FP irp for %p\n", 3148 DeviceObject)); 3149 3150 if(info->WorkQueueItem == NULL) { 3151 3152 info->WorkQueueItem = 3153 IoAllocateWorkItem(fdoExtension->DeviceObject); 3154 3155 if(info->WorkQueueItem == NULL) { 3156 3157 // 3158 // Set the countdown to one minute in the future. 3159 // we'll try again then in the hopes there's more 3160 // free memory. 3161 // 3162 3163 DebugPrint((1, "ClassTimerTick: Couldn't allocate " 3164 "item - try again in one minute\n")); 3165 InterlockedExchange((PLONG)&info->CountDown, 60); 3166 3167 } else { 3168 3169 // 3170 // Grab the remove lock so that removal will block 3171 // until the work item is done. 3172 // 3173 3174 ClassAcquireRemoveLock(fdoExtension->DeviceObject, 3175 info->WorkQueueItem); 3176 3177 IoQueueWorkItem(info->WorkQueueItem, 3178 ClasspFailurePredict, 3179 DelayedWorkQueue, 3180 info); 3181 } 3182 3183 } else { 3184 3185 DebugPrint((3, "ClasspTimerTick: Failure " 3186 "Prediction work item is " 3187 "already active for device %p\n", 3188 DeviceObject)); 3189 3190 } 3191 } // end (countdown == 0) 3192 3193 } else { 3194 // 3195 // If device is sleeping then just rearm polling timer 3196 DebugPrint((4, "ClassTimerTick, SHHHH!!! device is %p is sleeping\n", 3197 DeviceObject)); 3198 } 3199 3200 } // end failure prediction polling 3201 3202 // 3203 // Give driver a chance to do its own specific work 3204 // 3205 3206 if (commonExtension->DriverExtension->InitData.ClassTick != NULL) { 3207 3208 commonExtension->DriverExtension->InitData.ClassTick(DeviceObject); 3209 3210 } // end device specific tick handler 3211 } // end check for removed 3212 3213 ClassReleaseRemoveLock(DeviceObject, (PIRP)ClasspTimerTick); 3214 } // end ClasspTimerTick() 3215 3216 /*++//////////////////////////////////////////////////////////////////////////// 3217 3218 ClasspEnableTimer() - ISSUE-2000/02/20-henrygab - not documented 3219 3220 Routine Description: 3221 3222 This routine 3223 3224 Arguments: 3225 3226 DeviceObject - 3227 Irp - 3228 3229 Return Value: 3230 3231 --*/ 3232 NTSTATUS 3233 NTAPI 3234 ClasspEnableTimer( 3235 PDEVICE_OBJECT DeviceObject 3236 ) 3237 { 3238 NTSTATUS status; 3239 3240 PAGED_CODE(); 3241 3242 if (DeviceObject->Timer == NULL) { 3243 3244 status = IoInitializeTimer(DeviceObject, ClasspTimerTick, NULL); 3245 3246 } else { 3247 3248 status = STATUS_SUCCESS; 3249 3250 } 3251 3252 if (NT_SUCCESS(status)) { 3253 3254 IoStartTimer(DeviceObject); 3255 DebugPrint((1, "ClasspEnableTimer: Once a second timer enabled " 3256 "for device %p\n", DeviceObject)); 3257 3258 } 3259 3260 DebugPrint((1, "ClasspEnableTimer: Device %p, Status %lx " 3261 "initializing timer\n", DeviceObject, status)); 3262 3263 return status; 3264 3265 } // end ClasspEnableTimer() 3266 3267 /*++//////////////////////////////////////////////////////////////////////////// 3268 3269 ClasspDisableTimer() - ISSUE-2000/02/20-henrygab - not documented 3270 3271 Routine Description: 3272 3273 This routine 3274 3275 Arguments: 3276 3277 DeviceObject - 3278 Irp - 3279 3280 Return Value: 3281 3282 --*/ 3283 NTSTATUS 3284 NTAPI 3285 ClasspDisableTimer( 3286 PDEVICE_OBJECT DeviceObject 3287 ) 3288 { 3289 //PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension; 3290 //PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension; 3291 //PMEDIA_CHANGE_DETECTION_INFO mCDInfo = fdoExtension->MediaChangeDetectionInfo; 3292 //PFAILURE_PREDICTION_INFO fPInfo = fdoExtension->FailurePredictionInfo; 3293 //NTSTATUS status; 3294 3295 PAGED_CODE(); 3296 3297 if (DeviceObject->Timer != NULL) { 3298 3299 // 3300 // we are only going to stop the actual timer in remove device routine. 3301 // it is the responsibility of the code within the timer routine to 3302 // check if the device is removed and not processing io for the final 3303 // call. 3304 // this keeps the code clean and prevents lots of bugs. 3305 // 3306 3307 3308 IoStopTimer(DeviceObject); 3309 DebugPrint((3, "ClasspDisableTimer: Once a second timer disabled " 3310 "for device %p\n", DeviceObject)); 3311 3312 } else { 3313 3314 DebugPrint((1, "ClasspDisableTimer: Timer never enabled\n")); 3315 3316 } 3317 3318 return STATUS_SUCCESS; 3319 } // end ClasspDisableTimer() 3320 3321 /*++//////////////////////////////////////////////////////////////////////////// 3322 3323 ClasspFailurePredict() - ISSUE-2000/02/20-henrygab - not documented 3324 3325 Routine Description: 3326 3327 This routine 3328 3329 Arguments: 3330 3331 DeviceObject - 3332 Irp - 3333 3334 Return Value: 3335 3336 Note: this function can be called (via the workitem callback) after the paging device is shut down, 3337 so it must be PAGE LOCKED. 3338 --*/ 3339 VOID 3340 NTAPI 3341 ClasspFailurePredict( 3342 IN PDEVICE_OBJECT DeviceObject, 3343 IN PVOID Context 3344 ) 3345 { 3346 PFAILURE_PREDICTION_INFO info = Context; 3347 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension; 3348 PIO_WORKITEM workItem; 3349 STORAGE_PREDICT_FAILURE checkFailure; 3350 SCSI_ADDRESS scsiAddress; 3351 3352 NTSTATUS status; 3353 3354 ASSERT(info != NULL); 3355 3356 DebugPrint((1, "ClasspFailurePredict: Polling for failure\n")); 3357 3358 // 3359 // Mark the work item as inactive and reset the countdown timer. we 3360 // can't risk freeing the work item until we've released the remove-lock 3361 // though - if we do it might get resused as a tag before we can release 3362 // the lock. 3363 // 3364 3365 InterlockedExchange((PLONG)&info->CountDown, info->Period); 3366 workItem = InterlockedExchangePointer((PVOID*)&info->WorkQueueItem, NULL); 3367 3368 if (ClasspCanSendPollingIrp(fdoExtension)) { 3369 3370 KEVENT event; 3371 PDEVICE_OBJECT topOfStack; 3372 PIRP irp = NULL; 3373 IO_STATUS_BLOCK ioStatus; 3374 3375 KeInitializeEvent(&event, SynchronizationEvent, FALSE); 3376 3377 topOfStack = IoGetAttachedDeviceReference(DeviceObject); 3378 3379 // 3380 // Send down irp to see if drive is predicting failure 3381 // 3382 3383 irp = IoBuildDeviceIoControlRequest( 3384 IOCTL_STORAGE_PREDICT_FAILURE, 3385 topOfStack, 3386 NULL, 3387 0, 3388 &checkFailure, 3389 sizeof(STORAGE_PREDICT_FAILURE), 3390 FALSE, 3391 &event, 3392 &ioStatus); 3393 3394 3395 if (irp != NULL) { 3396 status = IoCallDriver(topOfStack, irp); 3397 if (status == STATUS_PENDING) { 3398 KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); 3399 status = ioStatus.Status; 3400 } 3401 } else { 3402 status = STATUS_INSUFFICIENT_RESOURCES; 3403 } 3404 3405 if (NT_SUCCESS(status) && (checkFailure.PredictFailure)) { 3406 3407 checkFailure.PredictFailure = 512; 3408 3409 // 3410 // Send down irp to get scsi address 3411 // 3412 KeInitializeEvent(&event, SynchronizationEvent, FALSE); 3413 3414 RtlZeroMemory(&scsiAddress, sizeof(SCSI_ADDRESS)); 3415 irp = IoBuildDeviceIoControlRequest( 3416 IOCTL_SCSI_GET_ADDRESS, 3417 topOfStack, 3418 NULL, 3419 0, 3420 &scsiAddress, 3421 sizeof(SCSI_ADDRESS), 3422 FALSE, 3423 &event, 3424 &ioStatus); 3425 3426 if (irp != NULL) { 3427 status = IoCallDriver(topOfStack, irp); 3428 if (status == STATUS_PENDING) { 3429 KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); 3430 status = ioStatus.Status; 3431 } 3432 } 3433 3434 ClassNotifyFailurePredicted(fdoExtension, 3435 (PUCHAR)&checkFailure, 3436 sizeof(checkFailure), 3437 (BOOLEAN)(fdoExtension->FailurePredicted == FALSE), 3438 2, 3439 scsiAddress.PathId, 3440 scsiAddress.TargetId, 3441 scsiAddress.Lun); 3442 3443 fdoExtension->FailurePredicted = TRUE; 3444 3445 } 3446 3447 ObDereferenceObject(topOfStack); 3448 } 3449 3450 ClassReleaseRemoveLock(DeviceObject, (PIRP) workItem); 3451 IoFreeWorkItem(workItem); 3452 return; 3453 } // end ClasspFailurePredict() 3454 3455 /*++//////////////////////////////////////////////////////////////////////////// 3456 3457 ClassNotifyFailurePredicted() ISSUE-alanwar-2000/02/20 - not documented 3458 3459 Routine Description: 3460 3461 Arguments: 3462 3463 Return Value: 3464 3465 --*/ 3466 VOID 3467 NTAPI 3468 ClassNotifyFailurePredicted( 3469 PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, 3470 PUCHAR Buffer, 3471 ULONG BufferSize, 3472 BOOLEAN LogError, 3473 ULONG UniqueErrorValue, 3474 UCHAR PathId, 3475 UCHAR TargetId, 3476 UCHAR Lun 3477 ) 3478 { 3479 PIO_ERROR_LOG_PACKET logEntry; 3480 3481 DebugPrint((1, "ClasspFailurePredictPollCompletion: Failure predicted for device %p\n", FdoExtension->DeviceObject)); 3482 3483 // 3484 // Fire off a WMI event 3485 // 3486 ClassWmiFireEvent(FdoExtension->DeviceObject, 3487 &StoragePredictFailureEventGuid, 3488 0, 3489 BufferSize, 3490 Buffer); 3491 3492 // 3493 // Log an error into the eventlog 3494 // 3495 3496 if (LogError) 3497 { 3498 logEntry = IoAllocateErrorLogEntry( 3499 FdoExtension->DeviceObject, 3500 sizeof(IO_ERROR_LOG_PACKET) + (3 * sizeof(ULONG))); 3501 3502 if (logEntry != NULL) 3503 { 3504 3505 logEntry->FinalStatus = STATUS_SUCCESS; 3506 logEntry->ErrorCode = IO_WRN_FAILURE_PREDICTED; 3507 logEntry->SequenceNumber = 0; 3508 logEntry->MajorFunctionCode = IRP_MJ_DEVICE_CONTROL; 3509 logEntry->IoControlCode = IOCTL_STORAGE_PREDICT_FAILURE; 3510 logEntry->RetryCount = 0; 3511 logEntry->UniqueErrorValue = UniqueErrorValue; 3512 logEntry->DumpDataSize = 3; 3513 3514 logEntry->DumpData[0] = PathId; 3515 logEntry->DumpData[1] = TargetId; 3516 logEntry->DumpData[2] = Lun; 3517 3518 // 3519 // Write the error log packet. 3520 // 3521 3522 IoWriteErrorLogEntry(logEntry); 3523 } 3524 } 3525 } // end ClassNotifyFailurePredicted() 3526 3527 /*++//////////////////////////////////////////////////////////////////////////// 3528 3529 ClassSetFailurePredictionPoll() 3530 3531 Routine Description: 3532 3533 This routine enables polling for failure prediction, setting the timer 3534 to fire every N seconds as specified by the PollingPeriod. 3535 3536 Arguments: 3537 3538 FdoExtension - the device to setup failure prediction for. 3539 3540 FailurePredictionMethod - specific failure prediction method to use 3541 if set to FailurePredictionNone, will disable failure detection 3542 3543 PollingPeriod - if 0 then no change to current polling timer 3544 3545 Return Value: 3546 3547 NT Status 3548 3549 --*/ 3550 NTSTATUS 3551 NTAPI 3552 ClassSetFailurePredictionPoll( 3553 PFUNCTIONAL_DEVICE_EXTENSION FdoExtension, 3554 FAILURE_PREDICTION_METHOD FailurePredictionMethod, 3555 ULONG PollingPeriod 3556 ) 3557 { 3558 PFAILURE_PREDICTION_INFO info; 3559 NTSTATUS status; 3560 //DEVICE_POWER_STATE powerState; 3561 3562 PAGED_CODE(); 3563 3564 if (FdoExtension->FailurePredictionInfo == NULL) { 3565 3566 if (FailurePredictionMethod != FailurePredictionNone) { 3567 3568 info = ExAllocatePoolWithTag(NonPagedPool, 3569 sizeof(FAILURE_PREDICTION_INFO), 3570 CLASS_TAG_FAILURE_PREDICT); 3571 3572 if (info == NULL) { 3573 3574 return STATUS_INSUFFICIENT_RESOURCES; 3575 3576 } 3577 3578 KeInitializeEvent(&info->Event, SynchronizationEvent, TRUE); 3579 3580 info->WorkQueueItem = NULL; 3581 info->Period = DEFAULT_FAILURE_PREDICTION_PERIOD; 3582 3583 } else { 3584 3585 // 3586 // FaultPrediction has not been previously initialized, nor 3587 // is it being initialized now. No need to do anything. 3588 // 3589 return STATUS_SUCCESS; 3590 3591 } 3592 3593 FdoExtension->FailurePredictionInfo = info; 3594 3595 } else { 3596 3597 info = FdoExtension->FailurePredictionInfo; 3598 3599 } 3600 3601 KeWaitForSingleObject(&info->Event, 3602 UserRequest, 3603 UserMode, 3604 FALSE, 3605 NULL); 3606 3607 3608 // 3609 // Reset polling period and counter. Setup failure detection type 3610 // 3611 3612 if (PollingPeriod != 0) { 3613 3614 InterlockedExchange((PLONG)&info->Period, PollingPeriod); 3615 3616 } 3617 3618 InterlockedExchange((PLONG)&info->CountDown, info->Period); 3619 3620 info->Method = FailurePredictionMethod; 3621 if (FailurePredictionMethod != FailurePredictionNone) { 3622 3623 status = ClasspEnableTimer(FdoExtension->DeviceObject); 3624 3625 if (NT_SUCCESS(status)) { 3626 DebugPrint((3, "ClassEnableFailurePredictPoll: Enabled for " 3627 "device %p\n", FdoExtension->DeviceObject)); 3628 } 3629 3630 } else { 3631 3632 status = ClasspDisableTimer(FdoExtension->DeviceObject); 3633 DebugPrint((3, "ClassEnableFailurePredictPoll: Disabled for " 3634 "device %p\n", FdoExtension->DeviceObject)); 3635 status = STATUS_SUCCESS; 3636 3637 } 3638 3639 KeSetEvent(&info->Event, IO_NO_INCREMENT, FALSE); 3640 3641 return status; 3642 } // end ClassSetFailurePredictionPoll() 3643