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