3 Copyright (C) Microsoft Corporation, 1991 - 1999
11 Code for support of media change detection in the class driver
28 #define GESN_TIMEOUT_VALUE (0x4)
29 #define GESN_BUFFER_SIZE (0x8)
30 #define MAXIMUM_IMMEDIATE_MCN_RETRIES (0x20)
31 #define MCN_REG_SUBKEY_NAME (L"MediaChangeNotification")
32 #define MCN_REG_AUTORUN_DISABLE_INSTANCE_NAME (L"AlwaysDisableMCN")
33 #define MCN_REG_AUTORUN_ENABLE_INSTANCE_NAME (L"AlwaysEnableMCN")
35 GUID StoragePredictFailureEventGuid
= WMI_STORAGE_PREDICT_FAILURE_EVENT_GUID
;
38 // Only send polling irp when device is fully powered up and a
39 // power down irp is not in progress.
41 // NOTE: This helps close a window in time where a polling irp could cause
42 // a drive to spin up right after it has powered down. The problem is
43 // that SCSIPORT, ATAPI and SBP2 will be in the process of powering
44 // down (which may take a few seconds), but won't know that. It would
45 // then get a polling irp which will be put into its queue since it
46 // the disk isn't powered down yet. Once the disk is powered down it
47 // will find the polling irp in the queue and then power up the
48 // device to do the poll. They do not want to check if the polling
49 // irp has the SRB_NO_KEEP_AWAKE flag here since it is in a critical
50 // path and would slow down all I/Os. A better way to fix this
51 // would be to serialize the polling and power down irps so that
52 // only one of them is sent to the device at a time.
54 #define ClasspCanSendPollingIrp(fdoExtension) \
55 ((fdoExtension->DevicePowerState == PowerDeviceD0) && \
56 (! fdoExtension->PowerDownInProgress) )
60 ClasspIsMediaChangeDisabledDueToHardwareLimitation(
61 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
62 IN PUNICODE_STRING RegistryPath
67 ClasspMediaChangeDeviceInstanceOverride(
68 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
74 ClasspIsMediaChangeDisabledForClass(
75 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
76 IN PUNICODE_STRING RegistryPath
81 ClasspSetMediaChangeStateEx(
82 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
83 IN MEDIA_CHANGE_DETECTION_STATE NewState
,
85 IN BOOLEAN KnownStateChange
// can ignore oldstate == unknown
90 ClasspMediaChangeRegistryCallBack(
101 ClasspSendMediaStateIrp(
102 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
103 IN PMEDIA_CHANGE_DETECTION_INFO Info
,
107 IO_WORKITEM_ROUTINE ClasspFailurePredict
;
111 ClasspInitializePolling(
112 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
113 IN BOOLEAN AllowDriveToSleep
119 #pragma alloc_text(PAGE, ClassInitializeMediaChangeDetection)
120 #pragma alloc_text(PAGE, ClassEnableMediaChangeDetection)
121 #pragma alloc_text(PAGE, ClassDisableMediaChangeDetection)
122 #pragma alloc_text(PAGE, ClassCleanupMediaChangeDetection)
123 #pragma alloc_text(PAGE, ClasspMediaChangeRegistryCallBack)
124 #pragma alloc_text(PAGE, ClasspInitializePolling)
126 #pragma alloc_text(PAGE, ClasspIsMediaChangeDisabledDueToHardwareLimitation)
127 #pragma alloc_text(PAGE, ClasspMediaChangeDeviceInstanceOverride)
128 #pragma alloc_text(PAGE, ClasspIsMediaChangeDisabledForClass)
130 #pragma alloc_text(PAGE, ClassSetFailurePredictionPoll)
131 #pragma alloc_text(PAGE, ClasspDisableTimer)
132 #pragma alloc_text(PAGE, ClasspEnableTimer)
136 // ISSUE -- make this public?
139 ClassSendEjectionNotification(
140 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
144 // For post-NT5.1 work, need to move EjectSynchronizationEvent
145 // to be a MUTEX so we can attempt to grab it here and benefit
146 // from deadlock detection. This will allow checking if the media
147 // has been locked by programs before broadcasting these events.
148 // (what's the point of broadcasting if the media is not locked?)
150 // This would currently only be a slight optimization. For post-NT5.1,
151 // it would allow us to send a single PERSISTENT_PREVENT to MMC devices,
152 // thereby cleaning up a lot of the ejection code. Then, when the
153 // ejection request occured, we could see if any locks for the media
154 // existed. if locked, broadcast. if not, we send the eject irp.
158 // for now, just always broadcast. make this a public routine,
159 // so class drivers can add special hacks to broadcast this for their
160 // non-MMC-compliant devices also from sense codes.
163 DBGTRACE(ClassDebugTrace
, ("ClassSendEjectionNotification: media EJECT_REQUEST"));
164 ClasspSendNotification(FdoExtension
,
165 &GUID_IO_MEDIA_EJECT_REQUEST
,
173 ClasspSendNotification(
174 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
175 IN
const GUID
* Guid
,
176 IN ULONG ExtraDataSize
,
180 PTARGET_DEVICE_CUSTOM_NOTIFICATION notification
;
184 (sizeof(TARGET_DEVICE_CUSTOM_NOTIFICATION
) - sizeof(UCHAR
)) +
187 if (requiredSize
> 0x0000ffff) {
188 // MAX_USHORT, max total size for these events!
189 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugWarning
,
190 "Error sending event: size too large! (%x)\n",
195 notification
= ExAllocatePoolWithTag(NonPagedPool
,
200 // if none allocated, exit
203 if (notification
== NULL
) {
208 // Prepare and send the request!
211 RtlZeroMemory(notification
, requiredSize
);
212 notification
->Version
= 1;
213 notification
->Size
= (USHORT
)(requiredSize
);
214 notification
->FileObject
= NULL
;
215 notification
->NameBufferOffset
= -1;
216 notification
->Event
= *Guid
;
217 RtlCopyMemory(notification
->CustomDataBuffer
, ExtraData
, ExtraDataSize
);
219 IoReportTargetDeviceChangeAsynchronous(FdoExtension
->LowerPdo
,
223 ExFreePool(notification
);
228 /*++////////////////////////////////////////////////////////////////////////////
230 ClasspInterpretGesnData()
234 This routine will interpret the data returned for a GESN command, and
235 (if appropriate) set the media change event, and broadcast the
236 appropriate events to user mode for applications who care.
240 FdoExtension - the device
242 DataBuffer - the resulting data from a GESN event.
243 requires at least EIGHT valid bytes (header == 4, data == 4)
245 ResendImmediately - whether or not to immediately resend the request.
246 this should be FALSE if there was no event, FALSE if the reported
247 event was of the DEVICE BUSY class, else true.
255 DataBuffer must be at least four bytes of valid data (header == 4 bytes),
256 and have at least eight bytes of allocated memory (all events == 4 bytes).
258 The call to StartNextPacket may occur before this routine is completed.
259 the operational change notifications are informational in nature, and
260 while useful, are not neccessary to ensure proper operation. For example,
261 if the device morphs to no longer supporting WRITE commands, all further
262 write commands will fail. There exists a small timing window wherein
263 IOCTL_IS_DISK_WRITABLE may be called and get an incorrect response. If
264 a device supports software write protect, it is expected that the
265 application can handle such a case.
267 NOTE: perhaps setting the updaterequired byte to one should be done here.
268 if so, it relies upon the setting of a 32-byte value to be an atomic
269 operation. unfortunately, there is no simple way to notify a class driver
270 which wants to know that the device behavior requires updating.
272 Not ready events may be sent every second. For example, if we were
273 to minimize the number of asynchronous notifications, an application may
274 register just after a large busy time was reported. This would then
275 prevent the application from knowing the device was busy until some
276 arbitrarily chosen timeout has occurred. Also, the GESN request would
277 have to still occur, since it checks for non-busy events (such as user
278 keybutton presses and media change events) as well. The specification
279 states that the lower-numered events get reported first, so busy events,
280 while repeating, will only be reported when all other events have been
281 cleared from the device.
286 ClasspInterpretGesnData(
287 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
288 IN PNOTIFICATION_EVENT_STATUS_HEADER Header
,
289 IN PBOOLEAN ResendImmediately
292 PMEDIA_CHANGE_DETECTION_INFO info
;
296 info
= FdoExtension
->MediaChangeDetectionInfo
;
299 // note: don't allocate anything in this routine so that we can
300 // always just 'return'.
303 *ResendImmediately
= FALSE
;
308 if (Header
->NotificationClass
== NOTIFICATION_NO_CLASS_EVENTS
) {
313 // HACKHACK - REF #0001
314 // This loop is only taken initially, due to the inability to reliably
315 // auto-detect drives that report events correctly at boot. When we
316 // detect this behavior during the normal course of running, we will
317 // disable the hack, allowing more efficient use of the system. This
318 // should occur "nearly" instantly, as the drive should have multiple
319 // events queue'd (ie. power, morphing, media).
322 if (info
->Gesn
.HackEventMask
) {
325 // all events use the low four bytes of zero to indicate
326 // that there was no change in status.
329 UCHAR thisEvent
= Header
->ClassEventData
[0] & 0xf;
331 UCHAR thisEventBit
= (1 << Header
->NotificationClass
);
333 ASSERT(TEST_FLAG(info
->Gesn
.EventMask
, thisEventBit
));
337 // some bit magic here... this results in the lowest set bit only
340 lowestSetBit
= info
->Gesn
.EventMask
;
341 lowestSetBit
&= (info
->Gesn
.EventMask
- 1);
342 lowestSetBit
^= (info
->Gesn
.EventMask
);
344 if (thisEventBit
!= lowestSetBit
) {
347 // HACKHACK - REF #0001
348 // the first time we ever see an event set that is not the lowest
349 // set bit in the request (iow, highest priority), we know that the
350 // hack is no longer required, as the device is ignoring "no change"
351 // events when a real event is waiting in the other requested queues.
354 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
355 "Classpnp => GESN::NONE: Compliant drive found, "
356 "removing GESN hack (%x, %x)\n",
357 thisEventBit
, info
->Gesn
.EventMask
));
359 info
->Gesn
.HackEventMask
= FALSE
;
361 } else if (thisEvent
== 0) {
364 // HACKHACK - REF #0001
365 // note: this hack prevents poorly implemented firmware from constantly
366 // returning "No Event". we do this by cycling through the
367 // supported list of events here.
370 SET_FLAG(info
->Gesn
.NoChangeEventMask
, thisEventBit
);
371 CLEAR_FLAG(info
->Gesn
.EventMask
, thisEventBit
);
374 // if we have cycled through all supported event types, then
375 // we need to reset the events we are asking about. else we
376 // want to resend this request immediately in case there was
377 // another event pending.
380 if (info
->Gesn
.EventMask
== 0) {
381 info
->Gesn
.EventMask
= info
->Gesn
.NoChangeEventMask
;
382 info
->Gesn
.NoChangeEventMask
= 0;
384 *ResendImmediately
= TRUE
;
389 } // end if (info->Gesn.HackEventMask)
392 (Header
->EventDataLength
[0] << 8) |
393 (Header
->EventDataLength
[1] & 0xff);
395 requiredLength
= 4; // all events are four bytes
397 if (dataLength
< requiredLength
) {
398 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugWarning
,
399 "Classpnp => GESN returned only %x bytes data for fdo %p\n",
400 dataLength
, FdoExtension
->DeviceObject
));
403 if (dataLength
!= requiredLength
) {
404 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugWarning
,
405 "Classpnp => GESN returned too many (%x) bytes data for fdo %p\n",
406 dataLength
, FdoExtension
->DeviceObject
));
411 ClasspSendNotification(FdoExtension,
412 &GUID_IO_GENERIC_GESN_EVENT,
413 sizeof(NOTIFICATION_EVENT_STATUS_HEADER) + dataLength,
417 switch (Header
->NotificationClass
) {
419 case NOTIFICATION_EXTERNAL_REQUEST_CLASS_EVENTS
: { // 0x3
421 PNOTIFICATION_EXTERNAL_STATUS externalInfo
=
422 (PNOTIFICATION_EXTERNAL_STATUS
)(Header
->ClassEventData
);
423 DEVICE_EVENT_EXTERNAL_REQUEST externalData
;
426 // unfortunately, due to time constraints, we will only notify
427 // about keys being pressed, and not released. this makes keys
428 // single-function, but simplifies the code significantly.
431 if (externalInfo
->ExternalEvent
!=
432 NOTIFICATION_EXTERNAL_EVENT_BUTTON_DOWN
) {
436 *ResendImmediately
= TRUE
;
437 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
438 "Classpnp => GESN::EXTERNAL: Event: %x Status %x Req %x\n",
439 externalInfo
->ExternalEvent
, externalInfo
->ExternalStatus
,
440 (externalInfo
->Request
[0] << 8) | externalInfo
->Request
[1]
443 RtlZeroMemory(&externalData
, sizeof(DEVICE_EVENT_EXTERNAL_REQUEST
));
444 externalData
.Version
= 1;
445 externalData
.DeviceClass
= 0;
446 externalData
.ButtonStatus
= externalInfo
->ExternalEvent
;
447 externalData
.Request
=
448 (externalInfo
->Request
[0] << 8) |
449 (externalInfo
->Request
[1] & 0xff);
450 KeQuerySystemTime(&(externalData
.SystemTime
));
451 externalData
.SystemTime
.QuadPart
*= (LONGLONG
)KeQueryTimeIncrement();
453 DBGTRACE(ClassDebugTrace
, ("ClasspInterpretGesnData: media DEVICE_EXTERNAL_REQUEST"));
454 ClasspSendNotification(FdoExtension
,
455 &GUID_IO_DEVICE_EXTERNAL_REQUEST
,
456 sizeof(DEVICE_EVENT_EXTERNAL_REQUEST
),
461 case NOTIFICATION_MEDIA_STATUS_CLASS_EVENTS
: { // 0x4
463 PNOTIFICATION_MEDIA_STATUS mediaInfo
=
464 (PNOTIFICATION_MEDIA_STATUS
)(Header
->ClassEventData
);
466 if (mediaInfo
->MediaEvent
== NOTIFICATION_MEDIA_EVENT_NO_CHANGE
) {
470 *ResendImmediately
= TRUE
;
471 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
472 "Classpnp => GESN::MEDIA: Event: %x Status %x\n",
473 mediaInfo
->MediaEvent
, mediaInfo
->MediaStatus
));
475 if ((mediaInfo
->MediaEvent
== NOTIFICATION_MEDIA_EVENT_NEW_MEDIA
) ||
476 (mediaInfo
->MediaEvent
== NOTIFICATION_MEDIA_EVENT_MEDIA_CHANGE
)) {
479 if (TEST_FLAG(FdoExtension
->DeviceObject
->Characteristics
,
480 FILE_REMOVABLE_MEDIA
) &&
481 (ClassGetVpb(FdoExtension
->DeviceObject
) != NULL
) &&
482 (ClassGetVpb(FdoExtension
->DeviceObject
)->Flags
& VPB_MOUNTED
)
485 SET_FLAG(FdoExtension
->DeviceObject
->Flags
, DO_VERIFY_VOLUME
);
488 InterlockedIncrement((PLONG
)&FdoExtension
->MediaChangeCount
);
489 ClasspSetMediaChangeStateEx(FdoExtension
,
494 } else if (mediaInfo
->MediaEvent
== NOTIFICATION_MEDIA_EVENT_MEDIA_REMOVAL
) {
496 ClasspSetMediaChangeStateEx(FdoExtension
,
501 } else if (mediaInfo
->MediaEvent
== NOTIFICATION_MEDIA_EVENT_EJECT_REQUEST
) {
503 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugError
,
504 "Classpnp => GESN Ejection request received!\n"));
505 ClassSendEjectionNotification(FdoExtension
);
512 case NOTIFICATION_DEVICE_BUSY_CLASS_EVENTS
: { // lowest priority events...
514 PNOTIFICATION_BUSY_STATUS busyInfo
=
515 (PNOTIFICATION_BUSY_STATUS
)(Header
->ClassEventData
);
516 DEVICE_EVENT_BECOMING_READY busyData
;
519 // NOTE: we never actually need to immediately retry for these
520 // events: if one exists, the device is busy, and if not,
521 // we still don't want to retry.
524 if (busyInfo
->DeviceBusyStatus
== NOTIFICATION_BUSY_STATUS_NO_EVENT
) {
529 // else we want to report the approximated time till it's ready.
532 RtlZeroMemory(&busyData
, sizeof(DEVICE_EVENT_BECOMING_READY
));
533 busyData
.Version
= 1;
534 busyData
.Reason
= busyInfo
->DeviceBusyStatus
;
535 busyData
.Estimated100msToReady
= (busyInfo
->Time
[0] << 8) |
536 (busyInfo
->Time
[1] & 0xff);
538 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
539 "Classpnp => GESN::BUSY: Event: %x Status %x Time %x\n",
540 busyInfo
->DeviceBusyEvent
, busyInfo
->DeviceBusyStatus
,
541 busyData
.Estimated100msToReady
544 DBGTRACE(ClassDebugTrace
, ("ClasspInterpretGesnData: media BECOMING_READY"));
545 ClasspSendNotification(FdoExtension
,
546 &GUID_IO_DEVICE_BECOMING_READY
,
547 sizeof(DEVICE_EVENT_BECOMING_READY
),
558 } // end switch on notification class
562 /*++////////////////////////////////////////////////////////////////////////////
564 ClasspInternalSetMediaChangeState()
568 This routine will (if appropriate) set the media change event for the
569 device. The event will be set if the media state is changed and
570 media change events are enabled. Otherwise the media state will be
571 tracked but the event will not be set.
573 This routine will lock out the other media change routines if possible
574 but if not a media change notification may be lost after the enable has
579 FdoExtension - the device
581 MediaPresent - indicates whether the device has media inserted into it
582 (TRUE) or not (FALSE).
591 ClasspInternalSetMediaChangeState(
592 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
593 IN MEDIA_CHANGE_DETECTION_STATE NewState
,
594 IN BOOLEAN KnownStateChange
// can ignore oldstate == unknown
598 PCSTR states
[] = {"Unknown", "Present", "Not Present"};
600 MEDIA_CHANGE_DETECTION_STATE oldMediaState
;
601 PMEDIA_CHANGE_DETECTION_INFO info
= FdoExtension
->MediaChangeDetectionInfo
;
605 ASSERT((NewState
>= MediaUnknown
) && (NewState
<= MediaNotPresent
));
611 oldMediaState
= InterlockedExchange(
612 (PLONG
)(&info
->MediaChangeDetectionState
),
615 if((oldMediaState
== MediaUnknown
) && (!KnownStateChange
)) {
618 // The media was in an indeterminate state before - don't notify for
622 DebugPrint((ClassDebugMCN
,
623 "ClassSetMediaChangeState: State was unknown - this may "
624 "not be a change\n"));
627 } else if(oldMediaState
== NewState
) {
630 // Media is in the same state it was before.
636 if(info
->MediaChangeDetectionDisableCount
!= 0) {
638 DBGTRACE(ClassDebugMCN
,
639 ("ClassSetMediaChangeState: MCN not enabled, state "
640 "changed from %s to %s\n",
641 states
[oldMediaState
], states
[NewState
]));
646 DBGTRACE(ClassDebugMCN
,
647 ("ClassSetMediaChangeState: State change from %s to %s\n",
648 states
[oldMediaState
], states
[NewState
]));
651 // make the data useful -- it used to always be zero.
653 data
= FdoExtension
->MediaChangeCount
;
655 if (NewState
== MediaPresent
) {
657 DBGTRACE(ClassDebugTrace
, ("ClasspInternalSetMediaChangeState: media ARRIVAL"));
658 ClasspSendNotification(FdoExtension
,
659 &GUID_IO_MEDIA_ARRIVAL
,
664 else if (NewState
== MediaNotPresent
) {
666 DBGTRACE(ClassDebugTrace
, ("ClasspInternalSetMediaChangeState: media REMOVAL"));
667 ClasspSendNotification(FdoExtension
,
668 &GUID_IO_MEDIA_REMOVAL
,
675 // Don't notify of changed going to unknown.
682 } // end ClasspInternalSetMediaChangeState()
684 /*++////////////////////////////////////////////////////////////////////////////
686 ClassSetMediaChangeState()
690 This routine will (if appropriate) set the media change event for the
691 device. The event will be set if the media state is changed and
692 media change events are enabled. Otherwise the media state will be
693 tracked but the event will not be set.
695 This routine will lock out the other media change routines if possible
696 but if not a media change notification may be lost after the enable has
701 FdoExtension - the device
703 MediaPresent - indicates whether the device has media inserted into it
704 (TRUE) or not (FALSE).
706 Wait - indicates whether the function should wait until it can acquire
707 the synchronization lock or not.
716 ClasspSetMediaChangeStateEx(
717 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
718 IN MEDIA_CHANGE_DETECTION_STATE NewState
,
720 IN BOOLEAN KnownStateChange
// can ignore oldstate == unknown
723 PMEDIA_CHANGE_DETECTION_INFO info
= FdoExtension
->MediaChangeDetectionInfo
;
727 DBGTRACE(ClassDebugMCN
, ("> ClasspSetMediaChangeStateEx"));
730 // Reset SMART status on media removal as the old status may not be
731 // valid when there is no media in the device or when new media is
735 if (NewState
== MediaNotPresent
) {
737 FdoExtension
->FailurePredicted
= FALSE
;
738 FdoExtension
->FailureReason
= 0;
749 status
= KeWaitForMutexObject(&info
->MediaChangeMutex
,
753 ((Wait
== TRUE
) ? NULL
: &zero
));
755 if(status
== STATUS_TIMEOUT
) {
758 // Someone else is in the process of setting the media state
761 DBGWARN(("ClasspSetMediaChangeStateEx - timed out waiting for mutex"));
766 // Change the media present state and signal an event, if applicable
769 ClasspInternalSetMediaChangeState(FdoExtension
, NewState
, KnownStateChange
);
771 KeReleaseMutex(&info
->MediaChangeMutex
, FALSE
);
773 DBGTRACE(ClassDebugMCN
, ("< ClasspSetMediaChangeStateEx"));
776 } // end ClassSetMediaChangeStateEx()
780 ClassSetMediaChangeState(
781 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
782 IN MEDIA_CHANGE_DETECTION_STATE NewState
,
786 ClasspSetMediaChangeStateEx(FdoExtension
, NewState
, Wait
, FALSE
);
790 /*++////////////////////////////////////////////////////////////////////////////
792 ClasspMediaChangeDetectionCompletion()
796 This routine handles the completion of the test unit ready irps used to
797 determine if the media has changed. If the media has changed, this code
798 signals the named event to wake up other system services that react to
799 media change (aka AutoPlay).
803 DeviceObject - the object for the completion
804 Irp - the IRP being completed
805 Context - the SRB from the IRP
814 ClasspMediaChangeDetectionCompletion(
815 PDEVICE_OBJECT DeviceObject
,
820 PSCSI_REQUEST_BLOCK srb
= Context
;
821 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
;
822 PCLASS_PRIVATE_FDO_DATA fdoData
;
823 PMEDIA_CHANGE_DETECTION_INFO info
;
824 //PIO_STACK_LOCATION nextIrpStack;
826 BOOLEAN retryImmediately
= FALSE
;
829 // Since the class driver created this request, it's completion routine
830 // will not get a valid device object handed in. Use the one in the
834 DeviceObject
= IoGetCurrentIrpStackLocation(Irp
)->DeviceObject
;
835 fdoExtension
= DeviceObject
->DeviceExtension
;
836 fdoData
= fdoExtension
->PrivateFdoData
;
837 info
= fdoExtension
->MediaChangeDetectionInfo
;
839 ASSERT(info
->MediaChangeIrp
!= NULL
);
840 ASSERT(!TEST_FLAG(srb
->SrbStatus
, SRB_STATUS_QUEUE_FROZEN
));
841 DBGTRACE(ClassDebugMCN
, ("> ClasspMediaChangeDetectionCompletion: Device %p completed MCN irp %p.", DeviceObject
, Irp
));
844 * HACK for IoMega 2GB Jaz drive:
845 * This drive spins down on its own to preserve the media.
846 * When spun down, TUR fails with 2/4/0 (SCSI_SENSE_NOT_READY/SCSI_ADSENSE_LUN_NOT_READY/?).
847 * ClassInterpretSenseInfo would then call ClassSendStartUnit to spin the media up, which defeats the
848 * purpose of the spindown.
849 * So in this case, make this into a successful TUR.
850 * This allows the drive to stay spun down until it is actually accessed again.
851 * (If the media were actually removed, TUR would fail with 2/3a/0 ).
852 * This hack only applies to drives with the CAUSE_NOT_REPORTABLE_HACK bit set; this
853 * is set by disk.sys when HackCauseNotReportableHack is set for the drive in its BadControllers list.
855 if ((SRB_STATUS(srb
->SrbStatus
) != SRB_STATUS_SUCCESS
) &&
856 TEST_FLAG(fdoExtension
->ScanForSpecialFlags
, CLASS_SPECIAL_CAUSE_NOT_REPORTABLE_HACK
) &&
857 (srb
->SenseInfoBufferLength
>= RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA
, AdditionalSenseCode
))){
859 PSENSE_DATA senseData
= srb
->SenseInfoBuffer
;
861 if ((senseData
->SenseKey
== SCSI_SENSE_NOT_READY
) &&
862 (senseData
->AdditionalSenseCode
== SCSI_ADSENSE_LUN_NOT_READY
)){
863 srb
->SrbStatus
= SRB_STATUS_SUCCESS
;
869 // use ClassInterpretSenseInfo() to check for media state, and also
870 // to call ClassError() with correct parameters.
872 status
= STATUS_SUCCESS
;
873 if (SRB_STATUS(srb
->SrbStatus
) != SRB_STATUS_SUCCESS
) {
875 DBGTRACE(ClassDebugMCN
, ("ClasspMediaChangeDetectionCompletion - failed - srb status=%s, sense=%s/%s/%s.", DBGGETSRBSTATUSSTR(srb
), DBGGETSENSECODESTR(srb
), DBGGETADSENSECODESTR(srb
), DBGGETADSENSEQUALIFIERSTR(srb
)));
877 ClassInterpretSenseInfo(DeviceObject
,
888 fdoData
->LoggedTURFailureSinceLastIO
= FALSE
;
890 if (!info
->Gesn
.Supported
) {
892 DBGTRACE(ClassDebugMCN
, ("ClasspMediaChangeDetectionCompletion - succeeded and GESN NOT supported, setting MediaPresent."));
895 // success != media for GESN case
898 ClassSetMediaChangeState(fdoExtension
, MediaPresent
, FALSE
);
902 DBGTRACE(ClassDebugMCN
, ("ClasspMediaChangeDetectionCompletion - succeeded (GESN supported)."));
906 if (info
->Gesn
.Supported
) {
908 if (status
== STATUS_DATA_OVERRUN
) {
909 DBGTRACE(ClassDebugMCN
, ("ClasspMediaChangeDetectionCompletion - Overrun"));
910 status
= STATUS_SUCCESS
;
913 if (!NT_SUCCESS(status
)) {
914 DBGTRACE(ClassDebugMCN
, ("ClasspMediaChangeDetectionCompletion: GESN failed with status %x", status
));
918 // for GESN, need to interpret the results of the data.
919 // this may also require an immediate retry
922 if (Irp
->IoStatus
.Information
== 8 ) {
923 ClasspInterpretGesnData(fdoExtension
,
924 (PVOID
)info
->Gesn
.Buffer
,
928 } // end of NT_SUCCESS(status)
930 } // end of Info->Gesn.Supported
933 // free port-allocated sense buffer, if any.
936 if (PORT_ALLOCATED_SENSE(fdoExtension
, srb
)) {
937 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension
, srb
);
941 // Remember the IRP and SRB for use the next time.
944 ASSERT(IoGetNextIrpStackLocation(Irp
));
945 IoGetNextIrpStackLocation(Irp
)->Parameters
.Scsi
.Srb
= srb
;
948 // Reset the MCN timer.
951 ClassResetMediaChangeTimer(fdoExtension
);
954 // run a sanity check to make sure we're not recursing continuously
957 if (retryImmediately
) {
959 info
->MediaChangeRetryCount
++;
960 if (info
->MediaChangeRetryCount
> MAXIMUM_IMMEDIATE_MCN_RETRIES
) {
961 ASSERT(!"Recursing too often in MCN?");
962 info
->MediaChangeRetryCount
= 0;
963 retryImmediately
= FALSE
;
968 info
->MediaChangeRetryCount
= 0;
974 // release the remove lock....
979 ClassAcquireRemoveLock(DeviceObject
, (PIRP
)(&uniqueValue
));
980 ClassReleaseRemoveLock(DeviceObject
, Irp
);
984 // set the irp as not in use
987 volatile LONG irpWasInUse
;
988 irpWasInUse
= InterlockedCompareExchange(&info
->MediaChangeIrpInUse
, 0, 1);
989 #if _MSC_FULL_VER != 13009111 // This compiler always takes the wrong path here.
995 // now send it again before we release our last remove lock
998 if (retryImmediately
) {
999 ClasspSendMediaStateIrp(fdoExtension
, info
, 0);
1002 DBGTRACE(ClassDebugMCN
, ("ClasspMediaChangeDetectionCompletion - not retrying immediately"));
1006 // release the temporary remove lock
1009 ClassReleaseRemoveLock(DeviceObject
, (PIRP
)(&uniqueValue
));
1012 DBGTRACE(ClassDebugMCN
, ("< ClasspMediaChangeDetectionCompletion"));
1014 return STATUS_MORE_PROCESSING_REQUIRED
;
1017 /*++////////////////////////////////////////////////////////////////////////////
1019 ClasspSendTestUnitIrp() - ISSUE-2000/02/20-henrygab - not documented
1021 Routine Description:
1036 ClasspPrepareMcnIrp(
1037 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
1038 IN PMEDIA_CHANGE_DETECTION_INFO Info
,
1042 PSCSI_REQUEST_BLOCK srb
;
1043 PIO_STACK_LOCATION irpStack
;
1044 PIO_STACK_LOCATION nextIrpStack
;
1051 // Setup the IRP to perform a test unit ready.
1054 irp
= Info
->MediaChangeIrp
;
1063 // don't keep sending this if the device is being removed.
1066 status
= ClassAcquireRemoveLock(FdoExtension
->DeviceObject
, irp
);
1067 if (status
== REMOVE_COMPLETE
) {
1068 ASSERT(status
!= REMOVE_COMPLETE
);
1071 else if (status
== REMOVE_PENDING
) {
1072 ClassReleaseRemoveLock(FdoExtension
->DeviceObject
, irp
);
1076 ASSERT(status
== NO_REMOVE
);
1079 irp
->IoStatus
.Status
= STATUS_SUCCESS
;
1080 irp
->IoStatus
.Information
= 0;
1082 irp
->UserBuffer
= NULL
;
1085 // If the irp is sent down when the volume needs to be
1086 // verified, CdRomUpdateGeometryCompletion won't complete
1087 // it since it's not associated with a thread. Marking
1088 // it to override the verify causes it always be sent
1089 // to the port driver
1092 irpStack
= IoGetCurrentIrpStackLocation(irp
);
1093 irpStack
->Flags
|= SL_OVERRIDE_VERIFY_VOLUME
;
1095 nextIrpStack
= IoGetNextIrpStackLocation(irp
);
1096 nextIrpStack
->MajorFunction
= IRP_MJ_INTERNAL_DEVICE_CONTROL
;
1097 nextIrpStack
->Parameters
.Scsi
.Srb
= &(Info
->MediaChangeSrb
);
1100 // Prepare the SRB for execution.
1103 srb
= nextIrpStack
->Parameters
.Scsi
.Srb
;
1104 buffer
= srb
->SenseInfoBuffer
;
1105 RtlZeroMemory(srb
, sizeof(SCSI_REQUEST_BLOCK
));
1106 RtlZeroMemory(buffer
, SENSE_BUFFER_SIZE
);
1109 srb
->QueueTag
= SP_UNTAGGED
;
1110 srb
->QueueAction
= SRB_SIMPLE_TAG_REQUEST
;
1111 srb
->Length
= sizeof(SCSI_REQUEST_BLOCK
);
1112 srb
->Function
= SRB_FUNCTION_EXECUTE_SCSI
;
1113 srb
->SenseInfoBuffer
= buffer
;
1115 srb
->ScsiStatus
= 0;
1116 srb
->OriginalRequest
= irp
;
1117 srb
->SenseInfoBufferLength
= SENSE_BUFFER_SIZE
;
1119 srb
->SrbFlags
= FdoExtension
->SrbFlags
;
1120 SET_FLAG(srb
->SrbFlags
, Info
->SrbFlags
);
1122 srb
->TimeOutValue
= FdoExtension
->TimeOutValue
* 2;
1124 if (srb
->TimeOutValue
== 0) {
1126 if (FdoExtension
->TimeOutValue
== 0) {
1128 KdPrintEx((DPFLTR_CLASSPNP_ID
, DPFLTR_ERROR_LEVEL
,
1129 "ClassSendTestUnitIrp: FdoExtension->TimeOutValue "
1130 "is set to zero?! -- resetting to 10\n"));
1131 srb
->TimeOutValue
= 10 * 2; // reasonable default
1135 KdPrintEx((DPFLTR_CLASSPNP_ID
, DPFLTR_ERROR_LEVEL
,
1136 "ClassSendTestUnitIrp: Someone set "
1137 "srb->TimeOutValue to zero?! -- resetting to %x\n",
1138 FdoExtension
->TimeOutValue
* 2));
1139 srb
->TimeOutValue
= FdoExtension
->TimeOutValue
* 2;
1148 srb
->DataTransferLength
= 0;
1149 SET_FLAG(srb
->SrbFlags
, SRB_FLAGS_NO_DATA_TRANSFER
);
1150 nextIrpStack
->Parameters
.DeviceIoControl
.IoControlCode
=
1151 IOCTL_SCSI_EXECUTE_NONE
;
1152 srb
->DataBuffer
= NULL
;
1153 srb
->DataTransferLength
= 0;
1154 irp
->MdlAddress
= NULL
;
1156 cdb
= (PCDB
) &srb
->Cdb
[0];
1157 cdb
->CDB6GENERIC
.OperationCode
= SCSIOP_TEST_UNIT_READY
;
1161 ASSERT(Info
->Gesn
.Buffer
);
1163 srb
->TimeOutValue
= GESN_TIMEOUT_VALUE
; // much shorter timeout for GESN
1165 srb
->CdbLength
= 10;
1166 SET_FLAG(srb
->SrbFlags
, SRB_FLAGS_DATA_IN
);
1167 nextIrpStack
->Parameters
.DeviceIoControl
.IoControlCode
=
1168 IOCTL_SCSI_EXECUTE_IN
;
1169 srb
->DataBuffer
= Info
->Gesn
.Buffer
;
1170 srb
->DataTransferLength
= Info
->Gesn
.BufferSize
;
1171 irp
->MdlAddress
= Info
->Gesn
.Mdl
;
1173 cdb
= (PCDB
) &srb
->Cdb
[0];
1174 cdb
->GET_EVENT_STATUS_NOTIFICATION
.OperationCode
=
1175 SCSIOP_GET_EVENT_STATUS
;
1176 cdb
->GET_EVENT_STATUS_NOTIFICATION
.Immediate
= 1;
1177 cdb
->GET_EVENT_STATUS_NOTIFICATION
.EventListLength
[0] =
1178 (UCHAR
)((Info
->Gesn
.BufferSize
) >> 8);
1179 cdb
->GET_EVENT_STATUS_NOTIFICATION
.EventListLength
[1] =
1180 (UCHAR
)((Info
->Gesn
.BufferSize
) & 0xff);
1181 cdb
->GET_EVENT_STATUS_NOTIFICATION
.NotificationClassRequest
=
1182 Info
->Gesn
.EventMask
;
1186 IoSetCompletionRoutine(irp
,
1187 ClasspMediaChangeDetectionCompletion
,
1197 /*++////////////////////////////////////////////////////////////////////////////
1199 ClasspSendMediaStateIrp() - ISSUE-2000/02/20-henrygab - not documented
1201 Routine Description:
1215 ClasspSendMediaStateIrp(
1216 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
1217 IN PMEDIA_CHANGE_DETECTION_INFO Info
,
1221 BOOLEAN requestPending
= FALSE
;
1223 //LARGE_INTEGER zero;
1226 DBGTRACE(ClassDebugMCN
, ("> ClasspSendMediaStateIrp"));
1228 if (((FdoExtension
->CommonExtension
.CurrentState
!= IRP_MN_START_DEVICE
) ||
1229 (FdoExtension
->DevicePowerState
!= PowerDeviceD0
)
1231 (!Info
->MediaChangeIrpLost
)) {
1234 // the device may be stopped, powered down, or otherwise queueing io,
1235 // so should not timeout the autorun irp (yet) -- set to zero ticks.
1236 // scattered code relies upon this to not prematurely "lose" an
1237 // autoplay irp that was queued.
1240 Info
->MediaChangeIrpTimeInUse
= 0;
1244 // if the irp is not in use, mark it as such.
1247 irpInUse
= InterlockedCompareExchange(&Info
->MediaChangeIrpInUse
, 1, 0);
1253 timeInUse
= InterlockedIncrement(&Info
->MediaChangeIrpTimeInUse
);
1255 DebugPrint((ClassDebugMCN
, "ClasspSendMediaStateIrp: irp in use for "
1256 "%x seconds when synchronizing for MCD\n", timeInUse
));
1258 if (Info
->MediaChangeIrpLost
== FALSE
) {
1260 if (timeInUse
> MEDIA_CHANGE_TIMEOUT_TIME
) {
1263 // currently set to five minutes. hard to imagine a drive
1264 // taking that long to spin up.
1267 DebugPrint((ClassDebugError
,
1268 "CdRom%d: Media Change Notification has lost "
1269 "it's irp and doesn't know where to find it. "
1270 "Leave it alone and it'll come home dragging "
1271 "it's stack behind it.\n",
1272 FdoExtension
->DeviceNumber
));
1273 Info
->MediaChangeIrpLost
= TRUE
;
1277 DBGTRACE(ClassDebugMCN
, ("< ClasspSendMediaStateIrp - irpInUse"));
1284 if (Info
->MediaChangeDetectionDisableCount
!= 0) {
1285 DebugPrint((ClassDebugTrace
, "ClassCheckMediaState: device %p has "
1286 " detection disabled \n", FdoExtension
->DeviceObject
));
1290 if (FdoExtension
->DevicePowerState
!= PowerDeviceD0
) {
1292 if (TEST_FLAG(Info
->SrbFlags
, SRB_FLAGS_NO_KEEP_AWAKE
)) {
1293 DebugPrint((ClassDebugMCN
,
1294 "ClassCheckMediaState: device %p is powered "
1295 "down and flags are set to let it sleep\n",
1296 FdoExtension
->DeviceObject
));
1297 ClassResetMediaChangeTimer(FdoExtension
);
1302 // NOTE: we don't increment the time in use until our power state
1303 // changes above. this way, we won't "lose" the autoplay irp.
1304 // it's up to the lower driver to determine if powering up is a
1308 DebugPrint((ClassDebugMCN
,
1309 "ClassCheckMediaState: device %p needs to powerup "
1310 "to handle this io (may take a few extra seconds).\n",
1311 FdoExtension
->DeviceObject
));
1315 Info
->MediaChangeIrpTimeInUse
= 0;
1316 Info
->MediaChangeIrpLost
= FALSE
;
1318 if (CountDown
== 0) {
1322 DebugPrint((ClassDebugTrace
,
1323 "ClassCheckMediaState: timer expired\n"));
1325 if (Info
->MediaChangeDetectionDisableCount
!= 0) {
1326 DebugPrint((ClassDebugTrace
,
1327 "ClassCheckMediaState: detection disabled\n"));
1332 // Prepare the IRP for the test unit ready
1335 irp
= ClasspPrepareMcnIrp(FdoExtension
,
1337 Info
->Gesn
.Supported
);
1340 // Issue the request.
1343 DebugPrint((ClassDebugTrace
,
1344 "ClasspSendMediaStateIrp: Device %p getting TUR "
1345 " irp %p\n", FdoExtension
->DeviceObject
, irp
));
1353 // note: if we send it to the class dispatch routines, there is
1354 // a timing window here (since they grab the remove lock)
1355 // where we'd be removed. ELIMINATE the window by grabbing
1356 // the lock ourselves above and sending it to the lower
1357 // device object directly or to the device's StartIo
1358 // routine (which doesn't acquire the lock).
1361 requestPending
= TRUE
;
1363 DBGTRACE(ClassDebugMCN
, (" ClasspSendMediaStateIrp - calling IoCallDriver."));
1364 IoCallDriver(FdoExtension
->CommonExtension
.LowerDeviceObject
, irp
);
1369 if(requestPending
== FALSE
) {
1370 irpInUse
= InterlockedCompareExchange(&Info
->MediaChangeIrpInUse
, 0, 1);
1371 #if _MSC_FULL_VER != 13009111 // This compiler always takes the wrong path here.
1378 DBGTRACE(ClassDebugMCN
, ("< ClasspSendMediaStateIrp"));
1381 } // end ClasspSendMediaStateIrp()
1383 /*++////////////////////////////////////////////////////////////////////////////
1385 ClassCheckMediaState()
1387 Routine Description:
1389 This routine is called by the class driver to test for a media change
1390 condition and/or poll for disk failure prediction. It should be called
1391 from the class driver's IO timer routine once per second.
1395 FdoExtension - the device extension
1404 ClassCheckMediaState(
1405 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
1408 PMEDIA_CHANGE_DETECTION_INFO info
= FdoExtension
->MediaChangeDetectionInfo
;
1412 DebugPrint((ClassDebugTrace
,
1413 "ClassCheckMediaState: detection not enabled\n"));
1418 // Media change support is active and the IRP is waiting. Decrement the
1419 // timer. There is no MP protection on the timer counter. This code
1420 // is the only code that will manipulate the timer counter and only one
1421 // instance of it should be running at any given time.
1424 countDown
= InterlockedDecrement(&(info
->MediaChangeCountDown
));
1427 // Try to acquire the media change event. If we can't do it immediately
1428 // then bail out and assume the caller will try again later.
1430 ClasspSendMediaStateIrp(FdoExtension
,
1435 } // end ClassCheckMediaState()
1437 /*++////////////////////////////////////////////////////////////////////////////
1439 ClassResetMediaChangeTimer()
1441 Routine Description:
1443 Resets the media change count down timer to the default number of seconds.
1447 FdoExtension - the device to reset the timer for
1456 ClassResetMediaChangeTimer(
1457 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
1460 PMEDIA_CHANGE_DETECTION_INFO info
= FdoExtension
->MediaChangeDetectionInfo
;
1463 InterlockedExchange(&(info
->MediaChangeCountDown
),
1464 MEDIA_CHANGE_DEFAULT_TIME
);
1467 } // end ClassResetMediaChangeTimer()
1469 /*++////////////////////////////////////////////////////////////////////////////
1471 ClasspInitializePolling() - ISSUE-2000/02/20-henrygab - not documented
1473 Routine Description:
1487 ClasspInitializePolling(
1488 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
1489 IN BOOLEAN AllowDriveToSleep
1492 PDEVICE_OBJECT fdo
= FdoExtension
->DeviceObject
;
1493 //PCLASS_PRIVATE_FDO_DATA fdoData = FdoExtension->PrivateFdoData;
1496 PMEDIA_CHANGE_DETECTION_INFO info
;
1501 if (FdoExtension
->MediaChangeDetectionInfo
!= NULL
) {
1502 return STATUS_SUCCESS
;
1505 info
= ExAllocatePoolWithTag(NonPagedPool
,
1506 sizeof(MEDIA_CHANGE_DETECTION_INFO
),
1507 CLASS_TAG_MEDIA_CHANGE_DETECTION
);
1510 RtlZeroMemory(info
, sizeof(MEDIA_CHANGE_DETECTION_INFO
));
1512 FdoExtension
->KernelModeMcnContext
.FileObject
= (PVOID
)-1;
1513 FdoExtension
->KernelModeMcnContext
.DeviceObject
= (PVOID
)-1;
1514 FdoExtension
->KernelModeMcnContext
.LockCount
= 0;
1515 FdoExtension
->KernelModeMcnContext
.McnDisableCount
= 0;
1518 * Allocate an IRP to carry the Test-Unit-Ready.
1519 * Allocate an extra IRP stack location
1520 * so we can cache our device object in the top location.
1522 irp
= IoAllocateIrp((CCHAR
)(fdo
->StackSize
+1), FALSE
);
1528 buffer
= ExAllocatePoolWithTag(
1529 NonPagedPoolCacheAligned
,
1531 CLASS_TAG_MEDIA_CHANGE_DETECTION
);
1533 if (buffer
!= NULL
) {
1534 PIO_STACK_LOCATION irpStack
;
1535 PSCSI_REQUEST_BLOCK srb
;
1538 srb
= &(info
->MediaChangeSrb
);
1539 info
->MediaChangeIrp
= irp
;
1540 info
->SenseBuffer
= buffer
;
1543 * For the driver that creates an IRP, there is no 'current' stack location.
1544 * Step down one IRP stack location so that the extra top one
1545 * becomes our 'current' one.
1547 IoSetNextIrpStackLocation(irp
);
1550 * Cache our device object in the extra top IRP stack location
1551 * so we have it in our completion routine.
1553 irpStack
= IoGetCurrentIrpStackLocation(irp
);
1554 irpStack
->DeviceObject
= fdo
;
1557 * Now start setting up the next IRP stack location for the call like any driver would.
1559 irpStack
= IoGetNextIrpStackLocation(irp
);
1560 irpStack
->Parameters
.Scsi
.Srb
= srb
;
1561 info
->MediaChangeIrp
= irp
;
1564 // Initialize the SRB
1567 RtlZeroMemory(srb
, sizeof(SCSI_REQUEST_BLOCK
));
1570 // Initialize and set up the sense information buffer
1573 RtlZeroMemory(buffer
, SENSE_BUFFER_SIZE
);
1574 srb
->SenseInfoBuffer
= buffer
;
1575 srb
->SenseInfoBufferLength
= SENSE_BUFFER_SIZE
;
1578 // Set default values for the media change notification
1582 info
->MediaChangeCountDown
= MEDIA_CHANGE_DEFAULT_TIME
;
1583 info
->MediaChangeDetectionDisableCount
= 0;
1586 // Assume that there is initially no media in the device
1587 // only notify upper layers if there is something there
1590 info
->MediaChangeDetectionState
= MediaUnknown
;
1592 info
->MediaChangeIrpTimeInUse
= 0;
1593 info
->MediaChangeIrpLost
= FALSE
;
1596 // setup all extra flags we'll be setting for this irp
1599 if (AllowDriveToSleep
) {
1600 SET_FLAG(info
->SrbFlags
, SRB_FLAGS_NO_KEEP_AWAKE
);
1602 SET_FLAG(info
->SrbFlags
, SRB_CLASS_FLAGS_LOW_PRIORITY
);
1603 SET_FLAG(info
->SrbFlags
, SRB_FLAGS_NO_QUEUE_FREEZE
);
1604 SET_FLAG(info
->SrbFlags
, SRB_FLAGS_DISABLE_SYNCH_TRANSFER
);
1606 KeInitializeMutex(&info
->MediaChangeMutex
, 0x100);
1609 // It is ok to support media change events on this
1613 FdoExtension
->MediaChangeDetectionInfo
= info
;
1616 // NOTE: the DeviceType is FILE_DEVICE_CD_ROM even
1617 // when the device supports DVD (no need to
1618 // check for FILE_DEVICE_DVD, as it's not a
1622 if (FdoExtension
->DeviceObject
->DeviceType
== FILE_DEVICE_CD_ROM
){
1626 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
1627 "ClasspInitializePolling: Testing for GESN\n"));
1628 status
= ClasspInitializeGesn(FdoExtension
, info
);
1629 if (NT_SUCCESS(status
)) {
1630 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
1631 "ClasspInitializePolling: GESN available "
1632 "for %p\n", FdoExtension
->DeviceObject
));
1633 ASSERT(info
->Gesn
.Supported
);
1634 ASSERT(info
->Gesn
.Buffer
!= NULL
);
1635 ASSERT(info
->Gesn
.BufferSize
!= 0);
1636 ASSERT(info
->Gesn
.EventMask
!= 0);
1637 // must return here, for ASSERTs to be valid.
1638 return STATUS_SUCCESS
;
1640 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
1641 "ClasspInitializePolling: GESN *NOT* available "
1642 "for %p\n", FdoExtension
->DeviceObject
));
1645 ASSERT(info
->Gesn
.Supported
== 0);
1646 ASSERT(info
->Gesn
.Buffer
== NULL
);
1647 ASSERT(info
->Gesn
.BufferSize
== 0);
1648 ASSERT(info
->Gesn
.EventMask
== 0);
1649 info
->Gesn
.Supported
= 0; // just in case....
1650 return STATUS_SUCCESS
;
1660 // nothing to free here
1662 return STATUS_INSUFFICIENT_RESOURCES
;
1664 } // end ClasspInitializePolling()
1668 ClasspInitializeGesn(
1669 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
1670 IN PMEDIA_CHANGE_DETECTION_INFO Info
1673 PNOTIFICATION_EVENT_STATUS_HEADER header
;
1674 CLASS_DETECTION_STATE detectionState
= ClassDetectionUnknown
;
1675 PSTORAGE_ADAPTER_DESCRIPTOR adapterDescriptor
;
1676 NTSTATUS status
= STATUS_NOT_SUPPORTED
;
1679 BOOLEAN retryImmediately
;
1685 ASSERT(Info
== FdoExtension
->MediaChangeDetectionInfo
);
1688 // read if we already know the abilities of the device
1691 ClassGetDeviceParameter(FdoExtension
,
1692 CLASSP_REG_SUBKEY_NAME
,
1693 CLASSP_REG_MMC_DETECTION_VALUE_NAME
,
1694 (PULONG
)&detectionState
);
1696 if (detectionState
== ClassDetectionUnsupported
) {
1701 // check if the device has a hack flag saying never to try this.
1704 if (TEST_FLAG(FdoExtension
->PrivateFdoData
->HackFlags
,
1705 FDO_HACK_GESN_IS_BAD
)) {
1707 detectionState
= ClassDetectionUnsupported
;
1708 ClassSetDeviceParameter(FdoExtension
,
1709 CLASSP_REG_SUBKEY_NAME
,
1710 CLASSP_REG_MMC_DETECTION_VALUE_NAME
,
1711 ClassDetectionSupported
);
1718 // else go through the process since we allocate buffers and
1719 // get all sorts of device settings.
1722 if (Info
->Gesn
.Buffer
== NULL
) {
1723 Info
->Gesn
.Buffer
= ExAllocatePoolWithTag(NonPagedPoolCacheAligned
,
1727 if (Info
->Gesn
.Buffer
== NULL
) {
1728 status
= STATUS_INSUFFICIENT_RESOURCES
;
1731 if (Info
->Gesn
.Mdl
!= NULL
) {
1732 IoFreeMdl(Info
->Gesn
.Mdl
);
1734 Info
->Gesn
.Mdl
= IoAllocateMdl(Info
->Gesn
.Buffer
,
1736 FALSE
, FALSE
, NULL
);
1737 if (Info
->Gesn
.Mdl
== NULL
) {
1738 status
= STATUS_INSUFFICIENT_RESOURCES
;
1742 MmBuildMdlForNonPagedPool(Info
->Gesn
.Mdl
);
1743 Info
->Gesn
.BufferSize
= GESN_BUFFER_SIZE
;
1744 Info
->Gesn
.EventMask
= 0;
1747 // all items are prepared to use GESN (except the event mask, so don't
1748 // optimize this part out!).
1750 // now see if it really works. we have to loop through this because
1751 // many SAMSUNG (and one COMPAQ) drives timeout when requesting
1752 // NOT_READY events, even when the IMMEDIATE bit is set. :(
1754 // using a drive list is cumbersome, so this might fix the problem.
1757 adapterDescriptor
= FdoExtension
->AdapterDescriptor
;
1759 retryImmediately
= TRUE
;
1760 for (i
= 0; i
< 16 && retryImmediately
== TRUE
; i
++) {
1762 irp
= ClasspPrepareMcnIrp(FdoExtension
, Info
, TRUE
);
1764 status
= STATUS_INSUFFICIENT_RESOURCES
;
1768 ASSERT(TEST_FLAG(Info
->MediaChangeSrb
.SrbFlags
, SRB_FLAGS_NO_QUEUE_FREEZE
));
1771 // replace the completion routine with a different one this time...
1774 IoSetCompletionRoutine(irp
,
1775 ClassSignalCompletion
,
1778 KeInitializeEvent(&event
, SynchronizationEvent
, FALSE
);
1780 status
= IoCallDriver(FdoExtension
->CommonExtension
.LowerDeviceObject
, irp
);
1782 if (status
== STATUS_PENDING
) {
1783 status
= KeWaitForSingleObject(&event
,
1788 ASSERT(NT_SUCCESS(status
));
1790 ClassReleaseRemoveLock(FdoExtension
->DeviceObject
, irp
);
1792 if (SRB_STATUS(Info
->MediaChangeSrb
.SrbStatus
) != SRB_STATUS_SUCCESS
) {
1793 ClassInterpretSenseInfo(FdoExtension
->DeviceObject
,
1794 &(Info
->MediaChangeSrb
),
1802 if ((adapterDescriptor
->BusType
== BusTypeAtapi
) &&
1803 (Info
->MediaChangeSrb
.SrbStatus
== SRB_STATUS_BUS_RESET
)
1807 // ATAPI unfortunately returns SRB_STATUS_BUS_RESET instead
1808 // of SRB_STATUS_TIMEOUT, so we cannot differentiate between
1809 // the two. if we get this status four time consecutively,
1810 // stop trying this command. it is too late to change ATAPI
1811 // at this point, so special-case this here. (07/10/2001)
1812 // NOTE: any value more than 4 may cause the device to be
1817 if (atapiResets
>= 4) {
1818 status
= STATUS_IO_DEVICE_ERROR
;
1823 if (status
== STATUS_DATA_OVERRUN
) {
1824 status
= STATUS_SUCCESS
;
1827 if ((status
== STATUS_INVALID_DEVICE_REQUEST
) ||
1828 (status
== STATUS_TIMEOUT
) ||
1829 (status
== STATUS_IO_DEVICE_ERROR
) ||
1830 (status
== STATUS_IO_TIMEOUT
)
1834 // with these error codes, we don't ever want to try this command
1835 // again on this device, since it reacts poorly.
1838 ClassSetDeviceParameter(FdoExtension
,
1839 CLASSP_REG_SUBKEY_NAME
,
1840 CLASSP_REG_MMC_DETECTION_VALUE_NAME
,
1841 ClassDetectionUnsupported
);
1842 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugWarning
,
1843 "Classpnp => GESN test failed %x for fdo %p\n",
1844 status
, FdoExtension
->DeviceObject
));
1850 if (!NT_SUCCESS(status
)) {
1853 // this may be other errors that should not disable GESN
1854 // for all future start_device calls.
1857 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugWarning
,
1858 "Classpnp => GESN test failed %x for fdo %p\n",
1859 status
, FdoExtension
->DeviceObject
));
1866 // the first time, the request was just retrieving a mask of
1867 // available bits. use this to mask future requests.
1870 header
= (PNOTIFICATION_EVENT_STATUS_HEADER
)(Info
->Gesn
.Buffer
);
1872 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
1873 "Classpnp => Fdo %p supports event mask %x\n",
1874 FdoExtension
->DeviceObject
, header
->SupportedEventClasses
));
1877 if (TEST_FLAG(header
->SupportedEventClasses
,
1878 NOTIFICATION_MEDIA_STATUS_CLASS_MASK
)) {
1879 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
1880 "Classpnp => GESN supports MCN\n"));
1882 if (TEST_FLAG(header
->SupportedEventClasses
,
1883 NOTIFICATION_DEVICE_BUSY_CLASS_MASK
)) {
1884 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
1885 "Classpnp => GESN supports DeviceBusy\n"));
1887 Info
->Gesn
.EventMask
= header
->SupportedEventClasses
;
1890 // realistically, we are only considering the following events:
1891 // EXTERNAL REQUEST - this is being tested for play/stop/etc.
1892 // MEDIA STATUS - autorun and ejection requests.
1893 // DEVICE BUSY - to allow us to predict when media will be ready.
1894 // therefore, we should not bother querying for the other,
1895 // unknown events. clear all but the above flags.
1898 Info
->Gesn
.EventMask
&=
1899 NOTIFICATION_EXTERNAL_REQUEST_CLASS_MASK
|
1900 NOTIFICATION_MEDIA_STATUS_CLASS_MASK
|
1901 NOTIFICATION_DEVICE_BUSY_CLASS_MASK
;
1905 // HACKHACK - REF #0001
1906 // Some devices will *never* report an event if we've also requested
1907 // that it report lower-priority events. this is due to a
1908 // misunderstanding in the specification wherein a "No Change" is
1909 // interpreted to be a real event. what should occur is that the
1910 // device should ignore "No Change" events when multiple event types
1911 // are requested unless there are no other events waiting. this
1912 // greatly reduces the number of requests that the host must send
1913 // to determine if an event has occurred. Since we must work on all
1914 // drives, default to enabling the hack until we find evidence of
1918 if (CountOfSetBitsUChar(Info
->Gesn
.EventMask
) == 1) {
1919 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
1920 "Classpnp => GESN hack %s for FDO %p\n",
1921 "not required", FdoExtension
->DeviceObject
));
1923 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
1924 "Classpnp => GESN hack %s for FDO %p\n",
1925 "enabled", FdoExtension
->DeviceObject
));
1926 Info
->Gesn
.HackEventMask
= 1;
1932 // not the first time looping through, so interpret the results.
1935 ClasspInterpretGesnData(FdoExtension
,
1936 (PVOID
)Info
->Gesn
.Buffer
,
1941 } // end loop of GESN requests....
1944 // we can only use this if it can be relied upon for media changes,
1945 // since we are (by definition) no longer going to be polling via
1946 // a TEST_UNIT_READY irp, and drives will not report UNIT ATTENTION
1947 // for this command (although a filter driver, such as one for burning
1948 // cd's, might still fake those errors).
1950 // since we also rely upon NOT_READY events to change the cursor
1951 // into a "wait" cursor, we can't use GESN without NOT_READY support.
1954 if (TEST_FLAG(Info
->Gesn
.EventMask
,
1955 NOTIFICATION_MEDIA_STATUS_CLASS_MASK
) &&
1956 TEST_FLAG(Info
->Gesn
.EventMask
,
1957 NOTIFICATION_DEVICE_BUSY_CLASS_MASK
)
1960 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
1961 "Classpnp => Enabling GESN support for fdo %p\n",
1962 FdoExtension
->DeviceObject
));
1963 Info
->Gesn
.Supported
= TRUE
;
1965 ClassSetDeviceParameter(FdoExtension
,
1966 CLASSP_REG_SUBKEY_NAME
,
1967 CLASSP_REG_MMC_DETECTION_VALUE_NAME
,
1968 ClassDetectionSupported
);
1970 return STATUS_SUCCESS
;
1974 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
1975 "Classpnp => GESN available but not enabled for fdo %p\n",
1976 FdoExtension
->DeviceObject
));
1982 if (Info
->Gesn
.Mdl
) {
1983 IoFreeMdl(Info
->Gesn
.Mdl
);
1984 Info
->Gesn
.Mdl
= NULL
;
1986 if (Info
->Gesn
.Buffer
) {
1987 ExFreePool(Info
->Gesn
.Buffer
);
1988 Info
->Gesn
.Buffer
= NULL
;
1990 Info
->Gesn
.Supported
= 0;
1991 Info
->Gesn
.EventMask
= 0;
1992 Info
->Gesn
.BufferSize
= 0;
1993 return STATUS_NOT_SUPPORTED
;
1997 /*++////////////////////////////////////////////////////////////////////////////
1999 ClassInitializeTestUnitPolling()
2001 Routine Description:
2003 This routine will initialize MCN regardless of the settings stored
2004 in the registry. This should be used with caution, as some devices
2005 react badly to constant io. (i.e. never spin down, continuously cycling
2006 media in changers, ejection of media, etc.) It is highly suggested to
2007 use ClassInitializeMediaChangeDetection() instead.
2011 FdoExtension is the device to poll
2013 AllowDriveToSleep says whether to attempt to allow the drive to sleep
2014 or not. This only affects system-known spin down states, so if a
2015 drive spins itself down, this has no effect until the system spins
2023 ClassInitializeTestUnitPolling(
2024 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
2025 IN BOOLEAN AllowDriveToSleep
2028 return ClasspInitializePolling(FdoExtension
, AllowDriveToSleep
);
2029 } // end ClassInitializeTestUnitPolling()
2031 /*++////////////////////////////////////////////////////////////////////////////
2033 ClassInitializeMediaChangeDetection()
2035 Routine Description:
2037 This routine checks to see if it is safe to initialize MCN (the back end
2038 to autorun) for a given device. It will then check the device-type wide
2039 key "Autorun" in the service key (for legacy reasons), and then look in
2040 the device-specific key to potentially override that setting.
2042 If MCN is to be enabled, all neccessary structures and memory are
2043 allocated and initialized.
2045 This routine MUST be called only from the ClassInit() callback.
2049 FdoExtension - the device to initialize MCN for, if appropriate
2051 EventPrefix - unused, legacy argument. Set to zero.
2058 ClassInitializeMediaChangeDetection(
2059 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
2060 IN PUCHAR EventPrefix
2063 PDEVICE_OBJECT fdo
= FdoExtension
->DeviceObject
;
2066 PCLASS_DRIVER_EXTENSION driverExtension
= ClassGetDriverExtension(
2069 BOOLEAN disabledForBadHardware
;
2071 BOOLEAN instanceOverride
;
2076 // NOTE: This assumes that ClassInitializeMediaChangeDetection is always
2077 // called in the context of the ClassInitDevice callback. If called
2078 // after then this check will have already been made and the
2079 // once a second timer will not have been enabled.
2082 disabledForBadHardware
= ClasspIsMediaChangeDisabledDueToHardwareLimitation(
2084 &(driverExtension
->RegistryPath
)
2087 if (disabledForBadHardware
) {
2088 DebugPrint((ClassDebugMCN
,
2089 "ClassInitializeMCN: Disabled due to hardware"
2090 "limitations for this device"));
2095 // autorun should now be enabled by default for all media types.
2098 disabled
= ClasspIsMediaChangeDisabledForClass(
2100 &(driverExtension
->RegistryPath
)
2103 DebugPrint((ClassDebugMCN
,
2104 "ClassInitializeMCN: Class MCN is %s\n",
2105 (disabled
? "disabled" : "enabled")));
2107 status
= ClasspMediaChangeDeviceInstanceOverride(
2109 &instanceOverride
); // default value
2111 if (!NT_SUCCESS(status
)) {
2112 DebugPrint((ClassDebugMCN
,
2113 "ClassInitializeMCN: Instance using default\n"));
2115 DebugPrint((ClassDebugMCN
,
2116 "ClassInitializeMCN: Instance override: %s MCN\n",
2117 (instanceOverride
? "Enabling" : "Disabling")));
2118 disabled
= !instanceOverride
;
2121 DebugPrint((ClassDebugMCN
,
2122 "ClassInitializeMCN: Instance MCN is %s\n",
2123 (disabled
? "disabled" : "enabled")));
2130 // if the drive is not a CDROM, allow the drive to sleep
2132 if (FdoExtension
->DeviceObject
->DeviceType
== FILE_DEVICE_CD_ROM
) {
2133 ClasspInitializePolling(FdoExtension
, FALSE
);
2135 ClasspInitializePolling(FdoExtension
, TRUE
);
2139 } // end ClassInitializeMediaChangeDetection()
2141 /*++////////////////////////////////////////////////////////////////////////////
2143 ClasspMediaChangeDeviceInstanceOverride()
2145 Routine Description:
2147 The user can override the global setting to enable or disable Autorun on a
2148 specific cdrom device via the control panel. This routine checks and/or
2153 FdoExtension - the device to set/get the value for
2154 Value - the value to use in a set
2155 SetValue - whether to set the value
2159 TRUE - Autorun is disabled
2160 FALSE - Autorun is not disabled (Default)
2165 ClasspMediaChangeDeviceInstanceOverride(
2166 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
2167 OUT PBOOLEAN Enabled
2170 HANDLE deviceParameterHandle
; // cdrom instance key
2171 HANDLE driverParameterHandle
; // cdrom specific key
2172 RTL_QUERY_REGISTRY_TABLE queryTable
[3];
2173 OBJECT_ATTRIBUTES objectAttributes
;
2174 UNICODE_STRING subkeyName
;
2177 ULONG alwaysDisable
;
2183 deviceParameterHandle
= NULL
;
2184 driverParameterHandle
= NULL
;
2185 status
= STATUS_UNSUCCESSFUL
;
2186 alwaysEnable
= FALSE
;
2187 alwaysDisable
= FALSE
;
2191 status
= IoOpenDeviceRegistryKey( FdoExtension
->LowerPdo
,
2192 PLUGPLAY_REGKEY_DEVICE
,
2194 &deviceParameterHandle
2196 if (!NT_SUCCESS(status
)) {
2199 // this can occur when a new device is added to the system
2200 // this is due to cdrom.sys being an 'essential' driver
2202 DebugPrint((ClassDebugMCN
,
2203 "ClassMediaChangeDeviceInstanceDisabled: "
2204 "Could not open device registry key [%lx]\n", status
));
2208 RtlInitUnicodeString(&subkeyName
, MCN_REG_SUBKEY_NAME
);
2209 InitializeObjectAttributes(&objectAttributes
,
2211 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
2212 deviceParameterHandle
,
2213 (PSECURITY_DESCRIPTOR
) NULL
);
2215 status
= ZwCreateKey(&driverParameterHandle
,
2219 (PUNICODE_STRING
) NULL
,
2220 REG_OPTION_NON_VOLATILE
,
2223 if (!NT_SUCCESS(status
)) {
2224 DebugPrint((ClassDebugMCN
,
2225 "ClassMediaChangeDeviceInstanceDisabled: "
2226 "subkey could not be created. %lx\n", status
));
2231 // Default to not changing autorun behavior, based upon setting
2232 // registryValue to zero.
2237 RtlZeroMemory(&queryTable
[0], sizeof(queryTable
));
2239 queryTable
[0].Flags
= RTL_QUERY_REGISTRY_DIRECT
;
2240 queryTable
[0].DefaultType
= REG_DWORD
;
2241 queryTable
[0].DefaultLength
= 0;
2244 queryTable
[0].Name
= MCN_REG_AUTORUN_DISABLE_INSTANCE_NAME
;
2245 queryTable
[0].EntryContext
= &alwaysDisable
;
2246 queryTable
[0].DefaultData
= &alwaysDisable
;
2248 queryTable
[0].Name
= MCN_REG_AUTORUN_ENABLE_INSTANCE_NAME
;
2249 queryTable
[0].EntryContext
= &alwaysEnable
;
2250 queryTable
[0].DefaultData
= &alwaysEnable
;
2254 // don't care if it succeeds, since we set defaults above
2257 RtlQueryRegistryValues(RTL_REGISTRY_HANDLE
,
2258 (PWSTR
)driverParameterHandle
,
2266 if (driverParameterHandle
) ZwClose(driverParameterHandle
);
2267 if (deviceParameterHandle
) ZwClose(deviceParameterHandle
);
2271 if (alwaysEnable
&& alwaysDisable
) {
2273 DebugPrint((ClassDebugMCN
,
2274 "ClassMediaChangeDeviceInstanceDisabled: %s selected\n",
2275 "Both Enable and Disable set -- DISABLE"));
2276 ASSERT(NT_SUCCESS(status
));
2277 status
= STATUS_SUCCESS
;
2280 } else if (alwaysDisable
) {
2282 DebugPrint((ClassDebugMCN
,
2283 "ClassMediaChangeDeviceInstanceDisabled: %s selected\n",
2285 ASSERT(NT_SUCCESS(status
));
2286 status
= STATUS_SUCCESS
;
2289 } else if (alwaysEnable
) {
2291 DebugPrint((ClassDebugMCN
,
2292 "ClassMediaChangeDeviceInstanceDisabled: %s selected\n",
2294 ASSERT(NT_SUCCESS(status
));
2295 status
= STATUS_SUCCESS
;
2300 DebugPrint((ClassDebugMCN
,
2301 "ClassMediaChangeDeviceInstanceDisabled: %s selected\n",
2303 status
= STATUS_UNSUCCESSFUL
;
2309 } // end ClasspMediaChangeDeviceInstanceOverride()
2311 /*++////////////////////////////////////////////////////////////////////////////
2313 ClasspIsMediaChangeDisabledDueToHardwareLimitation()
2315 Routine Description:
2317 The key AutoRunAlwaysDisable contains a MULTI_SZ of hardware IDs for
2318 which to never enable MediaChangeNotification.
2320 The user can override the global setting to enable or disable Autorun on a
2321 specific cdrom device via the control panel.
2326 RegistryPath - pointer to the unicode string inside
2327 ...\CurrentControlSet\Services\Cdrom
2332 FALSE - Autorun may be enabled
2337 ClasspIsMediaChangeDisabledDueToHardwareLimitation(
2338 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
2339 IN PUNICODE_STRING RegistryPath
2342 PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor
= FdoExtension
->DeviceDescriptor
;
2343 OBJECT_ATTRIBUTES objectAttributes
;
2344 HANDLE serviceKey
= NULL
;
2345 RTL_QUERY_REGISTRY_TABLE parameters
[2];
2347 UNICODE_STRING deviceUnicodeString
;
2348 ANSI_STRING deviceString
;
2349 ULONG mediaChangeNotificationDisabled
= FALSE
;
2357 // open the service key.
2360 InitializeObjectAttributes(&objectAttributes
,
2362 OBJ_CASE_INSENSITIVE
,
2366 status
= ZwOpenKey(&serviceKey
,
2370 ASSERT(NT_SUCCESS(status
));
2373 if(!NT_SUCCESS(status
)) {
2376 // always take the safe path. if we can't open the service key,
2386 // Determine if drive is in a list of those requiring
2387 // autorun to be disabled. this is stored in a REG_MULTI_SZ
2388 // named AutoRunAlwaysDisable. this is required as some autochangers
2389 // must load the disc to reply to ChkVerify request, causing them
2390 // to cycle discs continuously.
2400 deviceString
.Buffer
= NULL
;
2401 deviceUnicodeString
.Buffer
= NULL
;
2404 // there may be nothing to check against
2407 if ((deviceDescriptor
->VendorIdOffset
== 0) &&
2408 (deviceDescriptor
->ProductIdOffset
== 0)) {
2414 if (deviceDescriptor
->VendorIdOffset
== 0) {
2417 vendorId
= (PCSTR
) deviceDescriptor
+ deviceDescriptor
->VendorIdOffset
;
2418 length
= strlen(vendorId
);
2421 if ( deviceDescriptor
->ProductIdOffset
== 0 ) {
2424 productId
= (PCSTR
) deviceDescriptor
+ deviceDescriptor
->ProductIdOffset
;
2425 length
+= strlen(productId
);
2428 if ( deviceDescriptor
->ProductRevisionOffset
== 0 ) {
2431 revisionId
= (PCSTR
) deviceDescriptor
+ deviceDescriptor
->ProductRevisionOffset
;
2432 length
+= strlen(revisionId
);
2436 // allocate a buffer for the string
2439 deviceString
.Length
= (USHORT
)( length
);
2440 deviceString
.MaximumLength
= deviceString
.Length
+ 1;
2441 deviceString
.Buffer
= ExAllocatePoolWithTag( NonPagedPool
,
2442 deviceString
.MaximumLength
,
2443 CLASS_TAG_AUTORUN_DISABLE
2445 if (deviceString
.Buffer
== NULL
) {
2446 DebugPrint((ClassDebugMCN
,
2447 "ClassMediaChangeDisabledForHardware: Unable to alloc "
2448 "string buffer\n" ));
2453 // copy strings to the buffer
2457 if (vendorId
!= NULL
) {
2458 RtlCopyMemory(deviceString
.Buffer
+ offset
,
2461 offset
+= strlen(vendorId
);
2464 if ( productId
!= NULL
) {
2465 RtlCopyMemory(deviceString
.Buffer
+ offset
,
2468 offset
+= strlen(productId
);
2470 if ( revisionId
!= NULL
) {
2471 RtlCopyMemory(deviceString
.Buffer
+ offset
,
2473 strlen(revisionId
));
2474 offset
+= strlen(revisionId
);
2477 ASSERT(offset
== deviceString
.Length
);
2479 deviceString
.Buffer
[deviceString
.Length
] = '\0'; // Null-terminated
2482 // convert to unicode as registry deals with unicode strings
2485 status
= RtlAnsiStringToUnicodeString( &deviceUnicodeString
,
2489 if (!NT_SUCCESS(status
)) {
2490 DebugPrint((ClassDebugMCN
,
2491 "ClassMediaChangeDisabledForHardware: cannot convert "
2492 "to unicode %lx\n", status
));
2497 // query the value, setting valueFound to true if found
2500 RtlZeroMemory(parameters
, sizeof(parameters
));
2502 nullMultiSz
= L
"\0";
2503 parameters
[0].QueryRoutine
= ClasspMediaChangeRegistryCallBack
;
2504 parameters
[0].Flags
= RTL_QUERY_REGISTRY_REQUIRED
;
2505 parameters
[0].Name
= L
"AutoRunAlwaysDisable";
2506 parameters
[0].EntryContext
= &mediaChangeNotificationDisabled
;
2507 parameters
[0].DefaultType
= REG_MULTI_SZ
;
2508 parameters
[0].DefaultData
= nullMultiSz
;
2509 parameters
[0].DefaultLength
= 0;
2511 status
= RtlQueryRegistryValues(RTL_REGISTRY_HANDLE
,
2514 &deviceUnicodeString
,
2517 if ( !NT_SUCCESS(status
) ) {
2523 if (deviceString
.Buffer
!= NULL
) {
2524 ExFreePool( deviceString
.Buffer
);
2526 if (deviceUnicodeString
.Buffer
!= NULL
) {
2527 RtlFreeUnicodeString( &deviceUnicodeString
);
2530 ZwClose(serviceKey
);
2533 if (mediaChangeNotificationDisabled
) {
2534 DebugPrint((ClassDebugMCN
, "ClassMediaChangeDisabledForHardware: "
2535 "Device is on disable list\n"));
2540 } // end ClasspIsMediaChangeDisabledDueToHardwareLimitation()
2542 /*++////////////////////////////////////////////////////////////////////////////
2544 ClasspIsMediaChangeDisabledForClass()
2546 Routine Description:
2548 The user must specify that AutoPlay is to run on the platform
2549 by setting the registry value HKEY_LOCAL_MACHINE\System\CurrentControlSet\
2550 Services\<SERVICE>\Autorun:REG_DWORD:1.
2552 The user can override the global setting to enable or disable Autorun on a
2553 specific cdrom device via the control panel.
2558 RegistryPath - pointer to the unicode string inside
2559 ...\CurrentControlSet\Services\Cdrom
2563 TRUE - Autorun is disabled for this class
2564 FALSE - Autorun is enabled for this class
2569 ClasspIsMediaChangeDisabledForClass(
2570 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
2571 IN PUNICODE_STRING RegistryPath
2574 //PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor = FdoExtension->DeviceDescriptor;
2576 OBJECT_ATTRIBUTES objectAttributes
;
2577 HANDLE serviceKey
= NULL
;
2578 HANDLE parametersKey
= NULL
;
2579 RTL_QUERY_REGISTRY_TABLE parameters
[3];
2581 UNICODE_STRING paramStr
;
2582 //UNICODE_STRING deviceUnicodeString;
2583 //ANSI_STRING deviceString;
2586 // Default to ENABLING MediaChangeNotification (!)
2589 ULONG mcnRegistryValue
= 1;
2597 // open the service key.
2600 InitializeObjectAttributes(&objectAttributes
,
2602 OBJ_CASE_INSENSITIVE
,
2606 status
= ZwOpenKey(&serviceKey
,
2610 ASSERT(NT_SUCCESS(status
));
2612 if(!NT_SUCCESS(status
)) {
2615 // return the default value, which is the
2616 // inverse of the registry setting default
2617 // since this routine asks if it's disabled
2620 DebugPrint((ClassDebugMCN
, "ClassCheckServiceMCN: Defaulting to %s\n",
2621 (mcnRegistryValue
? "Enabled" : "Disabled")));
2622 return (BOOLEAN
)(!mcnRegistryValue
);
2626 RtlZeroMemory(parameters
, sizeof(parameters
));
2629 // Open the parameters key (if any) beneath the services key.
2632 RtlInitUnicodeString(¶mStr
, L
"Parameters");
2634 InitializeObjectAttributes(&objectAttributes
,
2636 OBJ_CASE_INSENSITIVE
,
2640 status
= ZwOpenKey(¶metersKey
,
2644 if (!NT_SUCCESS(status
)) {
2645 parametersKey
= NULL
;
2651 // Check for the Autorun value.
2654 parameters
[0].Flags
= RTL_QUERY_REGISTRY_DIRECT
;
2655 parameters
[0].Name
= L
"Autorun";
2656 parameters
[0].EntryContext
= &mcnRegistryValue
;
2657 parameters
[0].DefaultType
= REG_DWORD
;
2658 parameters
[0].DefaultData
= &mcnRegistryValue
;
2659 parameters
[0].DefaultLength
= sizeof(ULONG
);
2661 status
= RtlQueryRegistryValues(RTL_REGISTRY_HANDLE
| RTL_REGISTRY_OPTIONAL
,
2667 DebugPrint((ClassDebugMCN
, "ClassCheckServiceMCN: "
2668 "<Service>/Autorun flag = %d\n", mcnRegistryValue
));
2670 if(parametersKey
!= NULL
) {
2672 status
= RtlQueryRegistryValues(RTL_REGISTRY_HANDLE
| RTL_REGISTRY_OPTIONAL
,
2677 DebugPrint((ClassDebugMCN
, "ClassCheckServiceMCN: "
2678 "<Service>/Parameters/Autorun flag = %d\n",
2680 ZwClose(parametersKey
);
2683 ZwClose(serviceKey
);
2685 DebugPrint((ClassDebugMCN
, "ClassCheckServiceMCN: "
2686 "Autoplay for device %p is %s\n",
2687 FdoExtension
->DeviceObject
,
2688 (mcnRegistryValue
? "on" : "off")
2692 // return if it is _disabled_, which is the
2693 // inverse of the registry setting
2696 return (BOOLEAN
)(!mcnRegistryValue
);
2697 } // end ClasspIsMediaChangeDisabledForClass()
2699 /*++////////////////////////////////////////////////////////////////////////////
2701 ClassEnableMediaChangeDetection() ISSUE-2000/02/20-henrygab - why public?
2702 ClassEnableMediaChangeDetection() ISSUE-2000/02/20-henrygab - not documented
2704 Routine Description:
2718 ClassEnableMediaChangeDetection(
2719 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
2722 PMEDIA_CHANGE_DETECTION_INFO info
= FdoExtension
->MediaChangeDetectionInfo
;
2728 DebugPrint((ClassDebugMCN
,
2729 "ClassEnableMediaChangeDetection: not initialized\n"));
2733 KeWaitForMutexObject(&info
->MediaChangeMutex
,
2739 oldCount
= --info
->MediaChangeDetectionDisableCount
;
2741 ASSERT(oldCount
>= 0);
2743 DebugPrint((ClassDebugMCN
, "ClassEnableMediaChangeDetection: Disable count "
2745 info
->MediaChangeDetectionDisableCount
));
2750 // We don't know what state the media is in anymore.
2753 ClasspInternalSetMediaChangeState(FdoExtension
,
2759 // Reset the MCN timer.
2762 ClassResetMediaChangeTimer(FdoExtension
);
2764 DebugPrint((ClassDebugMCN
, "MCD is enabled\n"));
2768 DebugPrint((ClassDebugMCN
, "MCD still disabled\n"));
2774 // Let something else run.
2777 KeReleaseMutex(&info
->MediaChangeMutex
, FALSE
);
2780 } // end ClassEnableMediaChangeDetection()
2782 /*++////////////////////////////////////////////////////////////////////////////
2784 ClassDisableMediaChangeDetection() ISSUE-2000/02/20-henrygab - why public?
2785 ClassDisableMediaChangeDetection() ISSUE-2000/02/20-henrygab - not documented
2787 Routine Description:
2799 ULONG BreakOnMcnDisable
= FALSE
;
2803 ClassDisableMediaChangeDetection(
2804 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
2807 PMEDIA_CHANGE_DETECTION_INFO info
= FdoExtension
->MediaChangeDetectionInfo
;
2815 KeWaitForMutexObject(&info
->MediaChangeMutex
,
2821 info
->MediaChangeDetectionDisableCount
++;
2823 DebugPrint((ClassDebugMCN
, "ClassDisableMediaChangeDetection: "
2824 "disable count is %d\n",
2825 info
->MediaChangeDetectionDisableCount
));
2827 KeReleaseMutex(&info
->MediaChangeMutex
, FALSE
);
2830 } // end ClassDisableMediaChangeDetection()
2832 /*++////////////////////////////////////////////////////////////////////////////
2834 ClassCleanupMediaChangeDetection() ISSUE-2000/02/20-henrygab - why public?!
2836 Routine Description:
2838 This routine will cleanup any resources allocated for MCN. It is called
2839 by classpnp during remove device, and therefore is not typically required
2840 by external drivers.
2849 ClassCleanupMediaChangeDetection(
2850 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
2853 PMEDIA_CHANGE_DETECTION_INFO info
= FdoExtension
->MediaChangeDetectionInfo
;
2861 FdoExtension
->MediaChangeDetectionInfo
= NULL
;
2863 if (info
->Gesn
.Buffer
) {
2864 ExFreePool(info
->Gesn
.Buffer
);
2866 IoFreeIrp(info
->MediaChangeIrp
);
2867 ExFreePool(info
->SenseBuffer
);
2870 } // end ClassCleanupMediaChangeDetection()
2872 /*++////////////////////////////////////////////////////////////////////////////
2874 ClasspMcnControl() - ISSUE-2000/02/20-henrygab - not documented
2876 Routine Description:
2891 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
2893 IN PSCSI_REQUEST_BLOCK Srb
2896 PCOMMON_DEVICE_EXTENSION commonExtension
=
2897 (PCOMMON_DEVICE_EXTENSION
) FdoExtension
;
2899 PIO_STACK_LOCATION irpStack
= IoGetCurrentIrpStackLocation(Irp
);
2900 PPREVENT_MEDIA_REMOVAL request
= Irp
->AssociatedIrp
.SystemBuffer
;
2902 PFILE_OBJECT fileObject
= irpStack
->FileObject
;
2903 PFILE_OBJECT_EXTENSION fsContext
= NULL
;
2905 NTSTATUS status
= STATUS_SUCCESS
;
2910 // Check to make sure we have a file object extension to keep track of this
2911 // request. If not we'll fail it before synchronizing.
2916 if(fileObject
!= NULL
) {
2917 fsContext
= ClasspGetFsContext(commonExtension
, fileObject
);
2918 }else if(Irp
->RequestorMode
== KernelMode
) { // && fileObject == NULL
2919 fsContext
= &FdoExtension
->KernelModeMcnContext
;
2922 if (fsContext
== NULL
) {
2925 // This handle isn't setup correctly. We can't let the
2929 status
= STATUS_INVALID_PARAMETER
;
2933 if(request
->PreventMediaRemoval
) {
2936 // This is a lock command. Reissue the command in case bus or
2937 // device was reset and the lock was cleared.
2940 ClassDisableMediaChangeDetection(FdoExtension
);
2941 InterlockedIncrement((PLONG
)&fsContext
->McnDisableCount
);
2945 if(fsContext
->McnDisableCount
== 0) {
2946 status
= STATUS_INVALID_DEVICE_STATE
;
2950 InterlockedDecrement((PLONG
)&fsContext
->McnDisableCount
);
2951 ClassEnableMediaChangeDetection(FdoExtension
);
2956 Irp
->IoStatus
.Status
= status
;
2962 ClassReleaseRemoveLock(FdoExtension
->DeviceObject
, Irp
);
2963 ClassCompleteRequest(FdoExtension
->DeviceObject
,
2968 } // end ClasspMcnControl(
2970 /*++////////////////////////////////////////////////////////////////////////////
2972 ClasspMediaChangeRegistryCallBack()
2974 Routine Description:
2976 This callback for a registry SZ or MULTI_SZ is called once for each
2977 SZ in the value. It will attempt to match the data with the
2978 UNICODE_STRING passed in as Context, and modify EntryContext if a
2979 match is found. Written for ClasspCheckRegistryForMediaChangeCompletion
2983 ValueName - name of the key that was opened
2984 ValueType - type of data stored in the value (REG_SZ for this routine)
2985 ValueData - data in the registry, in this case a wide string
2986 ValueLength - length of the data including the terminating null
2987 Context - unicode string to compare against ValueData
2988 EntryContext - should be initialized to 0, will be set to 1 if match found
2993 EntryContext will be 1 if found
2998 ClasspMediaChangeRegistryCallBack(
3002 IN ULONG ValueLength
,
3004 IN PVOID EntryContext
3008 PUNICODE_STRING deviceString
;
3012 UNREFERENCED_PARAMETER(ValueName
);
3016 // if we have already set the value to true, exit
3019 valueFound
= EntryContext
;
3020 if ((*valueFound
) != 0) {
3021 DebugPrint((ClassDebugMCN
, "ClasspMcnRegCB: already set to true\n"));
3022 return STATUS_SUCCESS
;
3025 if (ValueLength
== sizeof(WCHAR
)) {
3026 DebugPrint((ClassDebugError
, "ClasspMcnRegCB: NULL string should "
3027 "never be passed to registry call-back!\n"));
3028 return STATUS_SUCCESS
;
3033 // if the data is not a terminated string, exit
3036 if (ValueType
!= REG_SZ
) {
3037 return STATUS_SUCCESS
;
3040 deviceString
= Context
;
3041 keyValue
= ValueData
;
3042 ValueLength
-= sizeof(WCHAR
); // ignore the null character
3045 // do not compare more memory than is in deviceString
3048 if (ValueLength
> deviceString
->Length
) {
3049 ValueLength
= deviceString
->Length
;
3053 // if the strings match, disable autorun
3056 if (RtlCompareMemory(deviceString
->Buffer
, keyValue
, ValueLength
) == ValueLength
) {
3057 DebugPrint((ClassDebugMCN
, "ClasspRegMcnCB: Match found\n"));
3058 DebugPrint((ClassDebugMCN
, "ClasspRegMcnCB: DeviceString at %p\n",
3059 deviceString
->Buffer
));
3060 DebugPrint((ClassDebugMCN
, "ClasspRegMcnCB: KeyValue at %p\n",
3062 (*valueFound
) = TRUE
;
3065 return STATUS_SUCCESS
;
3066 } // end ClasspMediaChangeRegistryCallBack()
3068 /*++////////////////////////////////////////////////////////////////////////////
3070 ClasspTimerTick() - ISSUE-2000/02/20-henrygab - not documented
3072 Routine Description:
3087 PDEVICE_OBJECT DeviceObject
,
3091 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= DeviceObject
->DeviceExtension
;
3092 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
3095 ASSERT(commonExtension
->IsFdo
);
3098 // Do any media change work
3100 isRemoved
= ClassAcquireRemoveLock(DeviceObject
, (PIRP
)ClasspTimerTick
);
3103 // We stop the timer before deleting the device. It's safe to keep going
3104 // if the flag value is REMOVE_PENDING because the removal thread will be
3105 // blocked trying to stop the timer.
3108 ASSERT(isRemoved
!= REMOVE_COMPLETE
);
3111 // This routine is reasonably safe even if the device object has a pending
3116 PFAILURE_PREDICTION_INFO info
= fdoExtension
->FailurePredictionInfo
;
3119 // Do any media change detection work
3122 if (fdoExtension
->MediaChangeDetectionInfo
!= NULL
) {
3124 ClassCheckMediaState(fdoExtension
);
3129 // Do any failure prediction work
3131 if ((info
!= NULL
) && (info
->Method
!= FailurePredictionNone
)) {
3136 if (ClasspCanSendPollingIrp(fdoExtension
)) {
3139 // Synchronization is not required here since the Interlocked
3140 // locked instruction guarantees atomicity. Other code that
3141 // resets CountDown uses InterlockedExchange which is also
3144 countDown
= InterlockedDecrement((PLONG
)&info
->CountDown
);
3145 if (countDown
== 0) {
3147 DebugPrint((4, "ClasspTimerTick: Send FP irp for %p\n",
3150 if(info
->WorkQueueItem
== NULL
) {
3152 info
->WorkQueueItem
=
3153 IoAllocateWorkItem(fdoExtension
->DeviceObject
);
3155 if(info
->WorkQueueItem
== NULL
) {
3158 // Set the countdown to one minute in the future.
3159 // we'll try again then in the hopes there's more
3163 DebugPrint((1, "ClassTimerTick: Couldn't allocate "
3164 "item - try again in one minute\n"));
3165 InterlockedExchange((PLONG
)&info
->CountDown
, 60);
3170 // Grab the remove lock so that removal will block
3171 // until the work item is done.
3174 ClassAcquireRemoveLock(fdoExtension
->DeviceObject
,
3175 info
->WorkQueueItem
);
3177 IoQueueWorkItem(info
->WorkQueueItem
,
3178 ClasspFailurePredict
,
3185 DebugPrint((3, "ClasspTimerTick: Failure "
3186 "Prediction work item is "
3187 "already active for device %p\n",
3191 } // end (countdown == 0)
3195 // If device is sleeping then just rearm polling timer
3196 DebugPrint((4, "ClassTimerTick, SHHHH!!! device is %p is sleeping\n",
3200 } // end failure prediction polling
3203 // Give driver a chance to do its own specific work
3206 if (commonExtension
->DriverExtension
->InitData
.ClassTick
!= NULL
) {
3208 commonExtension
->DriverExtension
->InitData
.ClassTick(DeviceObject
);
3210 } // end device specific tick handler
3211 } // end check for removed
3213 ClassReleaseRemoveLock(DeviceObject
, (PIRP
)ClasspTimerTick
);
3214 } // end ClasspTimerTick()
3216 /*++////////////////////////////////////////////////////////////////////////////
3218 ClasspEnableTimer() - ISSUE-2000/02/20-henrygab - not documented
3220 Routine Description:
3235 PDEVICE_OBJECT DeviceObject
3242 if (DeviceObject
->Timer
== NULL
) {
3244 status
= IoInitializeTimer(DeviceObject
, ClasspTimerTick
, NULL
);
3248 status
= STATUS_SUCCESS
;
3252 if (NT_SUCCESS(status
)) {
3254 IoStartTimer(DeviceObject
);
3255 DebugPrint((1, "ClasspEnableTimer: Once a second timer enabled "
3256 "for device %p\n", DeviceObject
));
3260 DebugPrint((1, "ClasspEnableTimer: Device %p, Status %lx "
3261 "initializing timer\n", DeviceObject
, status
));
3265 } // end ClasspEnableTimer()
3267 /*++////////////////////////////////////////////////////////////////////////////
3269 ClasspDisableTimer() - ISSUE-2000/02/20-henrygab - not documented
3271 Routine Description:
3286 PDEVICE_OBJECT DeviceObject
3289 //PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
3290 //PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
3291 //PMEDIA_CHANGE_DETECTION_INFO mCDInfo = fdoExtension->MediaChangeDetectionInfo;
3292 //PFAILURE_PREDICTION_INFO fPInfo = fdoExtension->FailurePredictionInfo;
3297 if (DeviceObject
->Timer
!= NULL
) {
3300 // we are only going to stop the actual timer in remove device routine.
3301 // it is the responsibility of the code within the timer routine to
3302 // check if the device is removed and not processing io for the final
3304 // this keeps the code clean and prevents lots of bugs.
3308 IoStopTimer(DeviceObject
);
3309 DebugPrint((3, "ClasspDisableTimer: Once a second timer disabled "
3310 "for device %p\n", DeviceObject
));
3314 DebugPrint((1, "ClasspDisableTimer: Timer never enabled\n"));
3318 return STATUS_SUCCESS
;
3319 } // end ClasspDisableTimer()
3321 /*++////////////////////////////////////////////////////////////////////////////
3323 ClasspFailurePredict() - ISSUE-2000/02/20-henrygab - not documented
3325 Routine Description:
3336 Note: this function can be called (via the workitem callback) after the paging device is shut down,
3337 so it must be PAGE LOCKED.
3341 ClasspFailurePredict(
3342 IN PDEVICE_OBJECT DeviceObject
,
3346 PFAILURE_PREDICTION_INFO info
= Context
;
3347 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= DeviceObject
->DeviceExtension
;
3348 PIO_WORKITEM workItem
;
3349 STORAGE_PREDICT_FAILURE checkFailure
;
3350 SCSI_ADDRESS scsiAddress
;
3354 ASSERT(info
!= NULL
);
3356 DebugPrint((1, "ClasspFailurePredict: Polling for failure\n"));
3359 // Mark the work item as inactive and reset the countdown timer. we
3360 // can't risk freeing the work item until we've released the remove-lock
3361 // though - if we do it might get resused as a tag before we can release
3365 InterlockedExchange((PLONG
)&info
->CountDown
, info
->Period
);
3366 workItem
= InterlockedExchangePointer(&info
->WorkQueueItem
, NULL
);
3368 if (ClasspCanSendPollingIrp(fdoExtension
)) {
3371 PDEVICE_OBJECT topOfStack
;
3373 IO_STATUS_BLOCK ioStatus
;
3375 KeInitializeEvent(&event
, SynchronizationEvent
, FALSE
);
3377 topOfStack
= IoGetAttachedDeviceReference(DeviceObject
);
3380 // Send down irp to see if drive is predicting failure
3383 irp
= IoBuildDeviceIoControlRequest(
3384 IOCTL_STORAGE_PREDICT_FAILURE
,
3389 sizeof(STORAGE_PREDICT_FAILURE
),
3396 status
= IoCallDriver(topOfStack
, irp
);
3397 if (status
== STATUS_PENDING
) {
3398 KeWaitForSingleObject(&event
, Executive
, KernelMode
, FALSE
, NULL
);
3399 status
= ioStatus
.Status
;
3402 status
= STATUS_INSUFFICIENT_RESOURCES
;
3405 if (NT_SUCCESS(status
) && (checkFailure
.PredictFailure
)) {
3407 checkFailure
.PredictFailure
= 512;
3410 // Send down irp to get scsi address
3412 KeInitializeEvent(&event
, SynchronizationEvent
, FALSE
);
3414 RtlZeroMemory(&scsiAddress
, sizeof(SCSI_ADDRESS
));
3415 irp
= IoBuildDeviceIoControlRequest(
3416 IOCTL_SCSI_GET_ADDRESS
,
3421 sizeof(SCSI_ADDRESS
),
3427 status
= IoCallDriver(topOfStack
, irp
);
3428 if (status
== STATUS_PENDING
) {
3429 KeWaitForSingleObject(&event
, Executive
, KernelMode
, FALSE
, NULL
);
3430 status
= ioStatus
.Status
;
3434 ClassNotifyFailurePredicted(fdoExtension
,
3435 (PUCHAR
)&checkFailure
,
3436 sizeof(checkFailure
),
3437 (BOOLEAN
)(fdoExtension
->FailurePredicted
== FALSE
),
3440 scsiAddress
.TargetId
,
3443 fdoExtension
->FailurePredicted
= TRUE
;
3447 ObDereferenceObject(topOfStack
);
3450 ClassReleaseRemoveLock(DeviceObject
, (PIRP
) workItem
);
3451 IoFreeWorkItem(workItem
);
3453 } // end ClasspFailurePredict()
3455 /*++////////////////////////////////////////////////////////////////////////////
3457 ClassNotifyFailurePredicted() ISSUE-alanwar-2000/02/20 - not documented
3459 Routine Description:
3468 ClassNotifyFailurePredicted(
3469 PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
3473 ULONG UniqueErrorValue
,
3479 PIO_ERROR_LOG_PACKET logEntry
;
3481 DebugPrint((1, "ClasspFailurePredictPollCompletion: Failure predicted for device %p\n", FdoExtension
->DeviceObject
));
3484 // Fire off a WMI event
3486 ClassWmiFireEvent(FdoExtension
->DeviceObject
,
3487 &StoragePredictFailureEventGuid
,
3493 // Log an error into the eventlog
3498 logEntry
= IoAllocateErrorLogEntry(
3499 FdoExtension
->DeviceObject
,
3500 sizeof(IO_ERROR_LOG_PACKET
) + (3 * sizeof(ULONG
)));
3502 if (logEntry
!= NULL
)
3505 logEntry
->FinalStatus
= STATUS_SUCCESS
;
3506 logEntry
->ErrorCode
= IO_WRN_FAILURE_PREDICTED
;
3507 logEntry
->SequenceNumber
= 0;
3508 logEntry
->MajorFunctionCode
= IRP_MJ_DEVICE_CONTROL
;
3509 logEntry
->IoControlCode
= IOCTL_STORAGE_PREDICT_FAILURE
;
3510 logEntry
->RetryCount
= 0;
3511 logEntry
->UniqueErrorValue
= UniqueErrorValue
;
3512 logEntry
->DumpDataSize
= 3;
3514 logEntry
->DumpData
[0] = PathId
;
3515 logEntry
->DumpData
[1] = TargetId
;
3516 logEntry
->DumpData
[2] = Lun
;
3519 // Write the error log packet.
3522 IoWriteErrorLogEntry(logEntry
);
3525 } // end ClassNotifyFailurePredicted()
3527 /*++////////////////////////////////////////////////////////////////////////////
3529 ClassSetFailurePredictionPoll()
3531 Routine Description:
3533 This routine enables polling for failure prediction, setting the timer
3534 to fire every N seconds as specified by the PollingPeriod.
3538 FdoExtension - the device to setup failure prediction for.
3540 FailurePredictionMethod - specific failure prediction method to use
3541 if set to FailurePredictionNone, will disable failure detection
3543 PollingPeriod - if 0 then no change to current polling timer
3552 ClassSetFailurePredictionPoll(
3553 PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
3554 FAILURE_PREDICTION_METHOD FailurePredictionMethod
,
3558 PFAILURE_PREDICTION_INFO info
;
3560 //DEVICE_POWER_STATE powerState;
3564 if (FdoExtension
->FailurePredictionInfo
== NULL
) {
3566 if (FailurePredictionMethod
!= FailurePredictionNone
) {
3568 info
= ExAllocatePoolWithTag(NonPagedPool
,
3569 sizeof(FAILURE_PREDICTION_INFO
),
3570 CLASS_TAG_FAILURE_PREDICT
);
3574 return STATUS_INSUFFICIENT_RESOURCES
;
3578 KeInitializeEvent(&info
->Event
, SynchronizationEvent
, TRUE
);
3580 info
->WorkQueueItem
= NULL
;
3581 info
->Period
= DEFAULT_FAILURE_PREDICTION_PERIOD
;
3586 // FaultPrediction has not been previously initialized, nor
3587 // is it being initialized now. No need to do anything.
3589 return STATUS_SUCCESS
;
3593 FdoExtension
->FailurePredictionInfo
= info
;
3597 info
= FdoExtension
->FailurePredictionInfo
;
3601 KeWaitForSingleObject(&info
->Event
,
3609 // Reset polling period and counter. Setup failure detection type
3612 if (PollingPeriod
!= 0) {
3614 InterlockedExchange((PLONG
)&info
->Period
, PollingPeriod
);
3618 InterlockedExchange((PLONG
)&info
->CountDown
, info
->Period
);
3620 info
->Method
= FailurePredictionMethod
;
3621 if (FailurePredictionMethod
!= FailurePredictionNone
) {
3623 status
= ClasspEnableTimer(FdoExtension
->DeviceObject
);
3625 if (NT_SUCCESS(status
)) {
3626 DebugPrint((3, "ClassEnableFailurePredictPoll: Enabled for "
3627 "device %p\n", FdoExtension
->DeviceObject
));
3632 status
= ClasspDisableTimer(FdoExtension
->DeviceObject
);
3633 DebugPrint((3, "ClassEnableFailurePredictPoll: Disabled for "
3634 "device %p\n", FdoExtension
->DeviceObject
));
3635 status
= STATUS_SUCCESS
;
3639 KeSetEvent(&info
->Event
, IO_NO_INCREMENT
, FALSE
);
3642 } // end ClassSetFailurePredictionPoll()