3 Copyright (C) Microsoft Corporation, 1991 - 1999
11 Code for support of media change detection in the class driver
27 #define GESN_TIMEOUT_VALUE (0x4)
28 #define GESN_BUFFER_SIZE (0x8)
29 #define MAXIMUM_IMMEDIATE_MCN_RETRIES (0x20)
30 #define MCN_REG_SUBKEY_NAME (L"MediaChangeNotification")
31 #define MCN_REG_AUTORUN_DISABLE_INSTANCE_NAME (L"AlwaysDisableMCN")
32 #define MCN_REG_AUTORUN_ENABLE_INSTANCE_NAME (L"AlwaysEnableMCN")
34 GUID StoragePredictFailureEventGuid
= WMI_STORAGE_PREDICT_FAILURE_EVENT_GUID
;
37 // Only send polling irp when device is fully powered up and a
38 // power down irp is not in progress.
40 // NOTE: This helps close a window in time where a polling irp could cause
41 // a drive to spin up right after it has powered down. The problem is
42 // that SCSIPORT, ATAPI and SBP2 will be in the process of powering
43 // down (which may take a few seconds), but won't know that. It would
44 // then get a polling irp which will be put into its queue since it
45 // the disk isn't powered down yet. Once the disk is powered down it
46 // will find the polling irp in the queue and then power up the
47 // device to do the poll. They do not want to check if the polling
48 // irp has the SRB_NO_KEEP_AWAKE flag here since it is in a critical
49 // path and would slow down all I/Os. A better way to fix this
50 // would be to serialize the polling and power down irps so that
51 // only one of them is sent to the device at a time.
53 #define ClasspCanSendPollingIrp(fdoExtension) \
54 ((fdoExtension->DevicePowerState == PowerDeviceD0) && \
55 (! fdoExtension->PowerDownInProgress) )
59 ClasspIsMediaChangeDisabledDueToHardwareLimitation(
60 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
61 IN PUNICODE_STRING RegistryPath
66 ClasspMediaChangeDeviceInstanceOverride(
67 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
73 ClasspIsMediaChangeDisabledForClass(
74 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
75 IN PUNICODE_STRING RegistryPath
80 ClasspSetMediaChangeStateEx(
81 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
82 IN MEDIA_CHANGE_DETECTION_STATE NewState
,
84 IN BOOLEAN KnownStateChange
// can ignore oldstate == unknown
89 ClasspMediaChangeRegistryCallBack(
100 ClasspSendMediaStateIrp(
101 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
102 IN PMEDIA_CHANGE_DETECTION_INFO Info
,
108 ClasspFailurePredict(
109 IN PDEVICE_OBJECT DeviceObject
,
110 IN PFAILURE_PREDICTION_INFO Info
115 ClasspInitializePolling(
116 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
117 IN BOOLEAN AllowDriveToSleep
123 #pragma alloc_text(PAGE, ClassInitializeMediaChangeDetection)
124 #pragma alloc_text(PAGE, ClassEnableMediaChangeDetection)
125 #pragma alloc_text(PAGE, ClassDisableMediaChangeDetection)
126 #pragma alloc_text(PAGE, ClassCleanupMediaChangeDetection)
127 #pragma alloc_text(PAGE, ClasspMediaChangeRegistryCallBack)
128 #pragma alloc_text(PAGE, ClasspInitializePolling)
130 #pragma alloc_text(PAGE, ClasspIsMediaChangeDisabledDueToHardwareLimitation)
131 #pragma alloc_text(PAGE, ClasspMediaChangeDeviceInstanceOverride)
132 #pragma alloc_text(PAGE, ClasspIsMediaChangeDisabledForClass)
134 #pragma alloc_text(PAGE, ClassSetFailurePredictionPoll)
135 #pragma alloc_text(PAGE, ClasspDisableTimer)
136 #pragma alloc_text(PAGE, ClasspEnableTimer)
140 // ISSUE -- make this public?
143 ClassSendEjectionNotification(
144 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
148 // For post-NT5.1 work, need to move EjectSynchronizationEvent
149 // to be a MUTEX so we can attempt to grab it here and benefit
150 // from deadlock detection. This will allow checking if the media
151 // has been locked by programs before broadcasting these events.
152 // (what's the point of broadcasting if the media is not locked?)
154 // This would currently only be a slight optimization. For post-NT5.1,
155 // it would allow us to send a single PERSISTENT_PREVENT to MMC devices,
156 // thereby cleaning up a lot of the ejection code. Then, when the
157 // ejection request occured, we could see if any locks for the media
158 // existed. if locked, broadcast. if not, we send the eject irp.
162 // for now, just always broadcast. make this a public routine,
163 // so class drivers can add special hacks to broadcast this for their
164 // non-MMC-compliant devices also from sense codes.
167 DBGTRACE(ClassDebugTrace
, ("ClassSendEjectionNotification: media EJECT_REQUEST"));
168 ClasspSendNotification(FdoExtension
,
169 &GUID_IO_MEDIA_EJECT_REQUEST
,
177 ClasspSendNotification(
178 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
179 IN
const GUID
* Guid
,
180 IN ULONG ExtraDataSize
,
184 PTARGET_DEVICE_CUSTOM_NOTIFICATION notification
;
188 (sizeof(TARGET_DEVICE_CUSTOM_NOTIFICATION
) - sizeof(UCHAR
)) +
191 if (requiredSize
> 0x0000ffff) {
192 // MAX_USHORT, max total size for these events!
193 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugWarning
,
194 "Error sending event: size too large! (%x)\n",
199 notification
= ExAllocatePoolWithTag(NonPagedPool
,
204 // if none allocated, exit
207 if (notification
== NULL
) {
212 // Prepare and send the request!
215 RtlZeroMemory(notification
, requiredSize
);
216 notification
->Version
= 1;
217 notification
->Size
= (USHORT
)(requiredSize
);
218 notification
->FileObject
= NULL
;
219 notification
->NameBufferOffset
= -1;
220 notification
->Event
= *Guid
;
221 RtlCopyMemory(notification
->CustomDataBuffer
, ExtraData
, ExtraDataSize
);
223 IoReportTargetDeviceChangeAsynchronous(FdoExtension
->LowerPdo
,
227 ExFreePool(notification
);
232 /*++////////////////////////////////////////////////////////////////////////////
234 ClasspInterpretGesnData()
238 This routine will interpret the data returned for a GESN command, and
239 (if appropriate) set the media change event, and broadcast the
240 appropriate events to user mode for applications who care.
244 FdoExtension - the device
246 DataBuffer - the resulting data from a GESN event.
247 requires at least EIGHT valid bytes (header == 4, data == 4)
249 ResendImmediately - whether or not to immediately resend the request.
250 this should be FALSE if there was no event, FALSE if the reported
251 event was of the DEVICE BUSY class, else true.
259 DataBuffer must be at least four bytes of valid data (header == 4 bytes),
260 and have at least eight bytes of allocated memory (all events == 4 bytes).
262 The call to StartNextPacket may occur before this routine is completed.
263 the operational change notifications are informational in nature, and
264 while useful, are not neccessary to ensure proper operation. For example,
265 if the device morphs to no longer supporting WRITE commands, all further
266 write commands will fail. There exists a small timing window wherein
267 IOCTL_IS_DISK_WRITABLE may be called and get an incorrect response. If
268 a device supports software write protect, it is expected that the
269 application can handle such a case.
271 NOTE: perhaps setting the updaterequired byte to one should be done here.
272 if so, it relies upon the setting of a 32-byte value to be an atomic
273 operation. unfortunately, there is no simple way to notify a class driver
274 which wants to know that the device behavior requires updating.
276 Not ready events may be sent every second. For example, if we were
277 to minimize the number of asynchronous notifications, an application may
278 register just after a large busy time was reported. This would then
279 prevent the application from knowing the device was busy until some
280 arbitrarily chosen timeout has occurred. Also, the GESN request would
281 have to still occur, since it checks for non-busy events (such as user
282 keybutton presses and media change events) as well. The specification
283 states that the lower-numered events get reported first, so busy events,
284 while repeating, will only be reported when all other events have been
285 cleared from the device.
290 ClasspInterpretGesnData(
291 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
292 IN PNOTIFICATION_EVENT_STATUS_HEADER Header
,
293 IN PBOOLEAN ResendImmediately
296 PMEDIA_CHANGE_DETECTION_INFO info
;
300 info
= FdoExtension
->MediaChangeDetectionInfo
;
303 // note: don't allocate anything in this routine so that we can
304 // always just 'return'.
307 *ResendImmediately
= FALSE
;
312 if (Header
->NotificationClass
== NOTIFICATION_NO_CLASS_EVENTS
) {
317 // HACKHACK - REF #0001
318 // This loop is only taken initially, due to the inability to reliably
319 // auto-detect drives that report events correctly at boot. When we
320 // detect this behavior during the normal course of running, we will
321 // disable the hack, allowing more efficient use of the system. This
322 // should occur "nearly" instantly, as the drive should have multiple
323 // events queue'd (ie. power, morphing, media).
326 if (info
->Gesn
.HackEventMask
) {
329 // all events use the low four bytes of zero to indicate
330 // that there was no change in status.
333 UCHAR thisEvent
= Header
->ClassEventData
[0] & 0xf;
335 UCHAR thisEventBit
= (1 << Header
->NotificationClass
);
337 ASSERT(TEST_FLAG(info
->Gesn
.EventMask
, thisEventBit
));
341 // some bit magic here... this results in the lowest set bit only
344 lowestSetBit
= info
->Gesn
.EventMask
;
345 lowestSetBit
&= (info
->Gesn
.EventMask
- 1);
346 lowestSetBit
^= (info
->Gesn
.EventMask
);
348 if (thisEventBit
!= lowestSetBit
) {
351 // HACKHACK - REF #0001
352 // the first time we ever see an event set that is not the lowest
353 // set bit in the request (iow, highest priority), we know that the
354 // hack is no longer required, as the device is ignoring "no change"
355 // events when a real event is waiting in the other requested queues.
358 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
359 "Classpnp => GESN::NONE: Compliant drive found, "
360 "removing GESN hack (%x, %x)\n",
361 thisEventBit
, info
->Gesn
.EventMask
));
363 info
->Gesn
.HackEventMask
= FALSE
;
365 } else if (thisEvent
== 0) {
368 // HACKHACK - REF #0001
369 // note: this hack prevents poorly implemented firmware from constantly
370 // returning "No Event". we do this by cycling through the
371 // supported list of events here.
374 SET_FLAG(info
->Gesn
.NoChangeEventMask
, thisEventBit
);
375 CLEAR_FLAG(info
->Gesn
.EventMask
, thisEventBit
);
378 // if we have cycled through all supported event types, then
379 // we need to reset the events we are asking about. else we
380 // want to resend this request immediately in case there was
381 // another event pending.
384 if (info
->Gesn
.EventMask
== 0) {
385 info
->Gesn
.EventMask
= info
->Gesn
.NoChangeEventMask
;
386 info
->Gesn
.NoChangeEventMask
= 0;
388 *ResendImmediately
= TRUE
;
393 } // end if (info->Gesn.HackEventMask)
396 (Header
->EventDataLength
[0] << 8) |
397 (Header
->EventDataLength
[1] & 0xff);
399 requiredLength
= 4; // all events are four bytes
401 if (dataLength
< requiredLength
) {
402 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugWarning
,
403 "Classpnp => GESN returned only %x bytes data for fdo %p\n",
404 dataLength
, FdoExtension
->DeviceObject
));
407 if (dataLength
!= requiredLength
) {
408 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugWarning
,
409 "Classpnp => GESN returned too many (%x) bytes data for fdo %p\n",
410 dataLength
, FdoExtension
->DeviceObject
));
415 ClasspSendNotification(FdoExtension,
416 &GUID_IO_GENERIC_GESN_EVENT,
417 sizeof(NOTIFICATION_EVENT_STATUS_HEADER) + dataLength,
421 switch (Header
->NotificationClass
) {
423 case NOTIFICATION_EXTERNAL_REQUEST_CLASS_EVENTS
: { // 0x3
425 PNOTIFICATION_EXTERNAL_STATUS externalInfo
=
426 (PNOTIFICATION_EXTERNAL_STATUS
)(Header
->ClassEventData
);
427 DEVICE_EVENT_EXTERNAL_REQUEST externalData
;
430 // unfortunately, due to time constraints, we will only notify
431 // about keys being pressed, and not released. this makes keys
432 // single-function, but simplifies the code significantly.
435 if (externalInfo
->ExternalEvent
!=
436 NOTIFICATION_EXTERNAL_EVENT_BUTTON_DOWN
) {
440 *ResendImmediately
= TRUE
;
441 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
442 "Classpnp => GESN::EXTERNAL: Event: %x Status %x Req %x\n",
443 externalInfo
->ExternalEvent
, externalInfo
->ExternalStatus
,
444 (externalInfo
->Request
[0] >> 8) | externalInfo
->Request
[1]
447 RtlZeroMemory(&externalData
, sizeof(DEVICE_EVENT_EXTERNAL_REQUEST
));
448 externalData
.Version
= 1;
449 externalData
.DeviceClass
= 0;
450 externalData
.ButtonStatus
= externalInfo
->ExternalEvent
;
451 externalData
.Request
=
452 (externalInfo
->Request
[0] << 8) |
453 (externalInfo
->Request
[1] & 0xff);
454 KeQuerySystemTime(&(externalData
.SystemTime
));
455 externalData
.SystemTime
.QuadPart
*= (LONGLONG
)KeQueryTimeIncrement();
457 DBGTRACE(ClassDebugTrace
, ("ClasspInterpretGesnData: media DEVICE_EXTERNAL_REQUEST"));
458 ClasspSendNotification(FdoExtension
,
459 &GUID_IO_DEVICE_EXTERNAL_REQUEST
,
460 sizeof(DEVICE_EVENT_EXTERNAL_REQUEST
),
465 case NOTIFICATION_MEDIA_STATUS_CLASS_EVENTS
: { // 0x4
467 PNOTIFICATION_MEDIA_STATUS mediaInfo
=
468 (PNOTIFICATION_MEDIA_STATUS
)(Header
->ClassEventData
);
470 if (mediaInfo
->MediaEvent
== NOTIFICATION_MEDIA_EVENT_NO_CHANGE
) {
474 *ResendImmediately
= TRUE
;
475 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
476 "Classpnp => GESN::MEDIA: Event: %x Status %x\n",
477 mediaInfo
->MediaEvent
, mediaInfo
->MediaStatus
));
479 if ((mediaInfo
->MediaEvent
== NOTIFICATION_MEDIA_EVENT_NEW_MEDIA
) ||
480 (mediaInfo
->MediaEvent
== NOTIFICATION_MEDIA_EVENT_MEDIA_CHANGE
)) {
483 if (TEST_FLAG(FdoExtension
->DeviceObject
->Characteristics
,
484 FILE_REMOVABLE_MEDIA
) &&
485 (ClassGetVpb(FdoExtension
->DeviceObject
) != NULL
) &&
486 (ClassGetVpb(FdoExtension
->DeviceObject
)->Flags
& VPB_MOUNTED
)
489 SET_FLAG(FdoExtension
->DeviceObject
->Flags
, DO_VERIFY_VOLUME
);
492 InterlockedIncrement(&FdoExtension
->MediaChangeCount
);
493 ClasspSetMediaChangeStateEx(FdoExtension
,
498 } else if (mediaInfo
->MediaEvent
== NOTIFICATION_MEDIA_EVENT_MEDIA_REMOVAL
) {
500 ClasspSetMediaChangeStateEx(FdoExtension
,
505 } else if (mediaInfo
->MediaEvent
== NOTIFICATION_MEDIA_EVENT_EJECT_REQUEST
) {
507 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugError
,
508 "Classpnp => GESN Ejection request received!\n"));
509 ClassSendEjectionNotification(FdoExtension
);
516 case NOTIFICATION_DEVICE_BUSY_CLASS_EVENTS
: { // lowest priority events...
518 PNOTIFICATION_BUSY_STATUS busyInfo
=
519 (PNOTIFICATION_BUSY_STATUS
)(Header
->ClassEventData
);
520 DEVICE_EVENT_BECOMING_READY busyData
;
523 // NOTE: we never actually need to immediately retry for these
524 // events: if one exists, the device is busy, and if not,
525 // we still don't want to retry.
528 if (busyInfo
->DeviceBusyStatus
== NOTIFICATION_BUSY_STATUS_NO_EVENT
) {
533 // else we want to report the approximated time till it's ready.
536 RtlZeroMemory(&busyData
, sizeof(DEVICE_EVENT_BECOMING_READY
));
537 busyData
.Version
= 1;
538 busyData
.Reason
= busyInfo
->DeviceBusyStatus
;
539 busyData
.Estimated100msToReady
= (busyInfo
->Time
[0] << 8) |
540 (busyInfo
->Time
[1] & 0xff);
542 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
543 "Classpnp => GESN::BUSY: Event: %x Status %x Time %x\n",
544 busyInfo
->DeviceBusyEvent
, busyInfo
->DeviceBusyStatus
,
545 busyData
.Estimated100msToReady
548 DBGTRACE(ClassDebugTrace
, ("ClasspInterpretGesnData: media BECOMING_READY"));
549 ClasspSendNotification(FdoExtension
,
550 &GUID_IO_DEVICE_BECOMING_READY
,
551 sizeof(DEVICE_EVENT_BECOMING_READY
),
562 } // end switch on notification class
566 /*++////////////////////////////////////////////////////////////////////////////
568 ClasspInternalSetMediaChangeState()
572 This routine will (if appropriate) set the media change event for the
573 device. The event will be set if the media state is changed and
574 media change events are enabled. Otherwise the media state will be
575 tracked but the event will not be set.
577 This routine will lock out the other media change routines if possible
578 but if not a media change notification may be lost after the enable has
583 FdoExtension - the device
585 MediaPresent - indicates whether the device has media inserted into it
586 (TRUE) or not (FALSE).
595 ClasspInternalSetMediaChangeState(
596 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
597 IN MEDIA_CHANGE_DETECTION_STATE NewState
,
598 IN BOOLEAN KnownStateChange
// can ignore oldstate == unknown
602 PUCHAR states
[] = {"Unknown", "Present", "Not Present"};
604 MEDIA_CHANGE_DETECTION_STATE oldMediaState
;
605 PMEDIA_CHANGE_DETECTION_INFO info
= FdoExtension
->MediaChangeDetectionInfo
;
609 ASSERT((NewState
>= MediaUnknown
) && (NewState
<= MediaNotPresent
));
615 oldMediaState
= InterlockedExchange(
616 (PLONG
)(&info
->MediaChangeDetectionState
),
619 if((oldMediaState
== MediaUnknown
) && (!KnownStateChange
)) {
622 // The media was in an indeterminate state before - don't notify for
626 DebugPrint((ClassDebugMCN
,
627 "ClassSetMediaChangeState: State was unknown - this may "
628 "not be a change\n"));
631 } else if(oldMediaState
== NewState
) {
634 // Media is in the same state it was before.
640 if(info
->MediaChangeDetectionDisableCount
!= 0) {
642 DBGTRACE(ClassDebugMCN
,
643 ("ClassSetMediaChangeState: MCN not enabled, state "
644 "changed from %s to %s\n",
645 states
[oldMediaState
], states
[NewState
]));
650 DBGTRACE(ClassDebugMCN
,
651 ("ClassSetMediaChangeState: State change from %s to %s\n",
652 states
[oldMediaState
], states
[NewState
]));
655 // make the data useful -- it used to always be zero.
657 data
= FdoExtension
->MediaChangeCount
;
659 if (NewState
== MediaPresent
) {
661 DBGTRACE(ClassDebugTrace
, ("ClasspInternalSetMediaChangeState: media ARRIVAL"));
662 ClasspSendNotification(FdoExtension
,
663 &GUID_IO_MEDIA_ARRIVAL
,
668 else if (NewState
== MediaNotPresent
) {
670 DBGTRACE(ClassDebugTrace
, ("ClasspInternalSetMediaChangeState: media REMOVAL"));
671 ClasspSendNotification(FdoExtension
,
672 &GUID_IO_MEDIA_REMOVAL
,
679 // Don't notify of changed going to unknown.
686 } // end ClasspInternalSetMediaChangeState()
688 /*++////////////////////////////////////////////////////////////////////////////
690 ClassSetMediaChangeState()
694 This routine will (if appropriate) set the media change event for the
695 device. The event will be set if the media state is changed and
696 media change events are enabled. Otherwise the media state will be
697 tracked but the event will not be set.
699 This routine will lock out the other media change routines if possible
700 but if not a media change notification may be lost after the enable has
705 FdoExtension - the device
707 MediaPresent - indicates whether the device has media inserted into it
708 (TRUE) or not (FALSE).
710 Wait - indicates whether the function should wait until it can acquire
711 the synchronization lock or not.
720 ClasspSetMediaChangeStateEx(
721 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
722 IN MEDIA_CHANGE_DETECTION_STATE NewState
,
724 IN BOOLEAN KnownStateChange
// can ignore oldstate == unknown
727 PMEDIA_CHANGE_DETECTION_INFO info
= FdoExtension
->MediaChangeDetectionInfo
;
731 DBGTRACE(ClassDebugMCN
, ("> ClasspSetMediaChangeStateEx"));
734 // Reset SMART status on media removal as the old status may not be
735 // valid when there is no media in the device or when new media is
739 if (NewState
== MediaNotPresent
) {
741 FdoExtension
->FailurePredicted
= FALSE
;
742 FdoExtension
->FailureReason
= 0;
753 status
= KeWaitForMutexObject(&info
->MediaChangeMutex
,
757 ((Wait
== TRUE
) ? NULL
: &zero
));
759 if(status
== STATUS_TIMEOUT
) {
762 // Someone else is in the process of setting the media state
765 DBGWARN(("ClasspSetMediaChangeStateEx - timed out waiting for mutex"));
770 // Change the media present state and signal an event, if applicable
773 ClasspInternalSetMediaChangeState(FdoExtension
, NewState
, KnownStateChange
);
775 KeReleaseMutex(&info
->MediaChangeMutex
, FALSE
);
777 DBGTRACE(ClassDebugMCN
, ("< ClasspSetMediaChangeStateEx"));
780 } // end ClassSetMediaChangeStateEx()
784 ClassSetMediaChangeState(
785 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
786 IN MEDIA_CHANGE_DETECTION_STATE NewState
,
790 ClasspSetMediaChangeStateEx(FdoExtension
, NewState
, Wait
, FALSE
);
794 /*++////////////////////////////////////////////////////////////////////////////
796 ClasspMediaChangeDetectionCompletion()
800 This routine handles the completion of the test unit ready irps used to
801 determine if the media has changed. If the media has changed, this code
802 signals the named event to wake up other system services that react to
803 media change (aka AutoPlay).
807 DeviceObject - the object for the completion
808 Irp - the IRP being completed
809 Context - the SRB from the IRP
818 ClasspMediaChangeDetectionCompletion(
819 PDEVICE_OBJECT DeviceObject
,
821 PSCSI_REQUEST_BLOCK Srb
824 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
;
825 PCLASS_PRIVATE_FDO_DATA fdoData
;
826 PMEDIA_CHANGE_DETECTION_INFO info
;
827 //PIO_STACK_LOCATION nextIrpStack;
829 BOOLEAN retryImmediately
= FALSE
;
832 // Since the class driver created this request, it's completion routine
833 // will not get a valid device object handed in. Use the one in the
837 DeviceObject
= IoGetCurrentIrpStackLocation(Irp
)->DeviceObject
;
838 fdoExtension
= DeviceObject
->DeviceExtension
;
839 fdoData
= fdoExtension
->PrivateFdoData
;
840 info
= fdoExtension
->MediaChangeDetectionInfo
;
842 ASSERT(info
->MediaChangeIrp
!= NULL
);
843 ASSERT(!TEST_FLAG(Srb
->SrbStatus
, SRB_STATUS_QUEUE_FROZEN
));
844 DBGTRACE(ClassDebugMCN
, ("> ClasspMediaChangeDetectionCompletion: Device %p completed MCN irp %p.", DeviceObject
, Irp
));
847 * HACK for IoMega 2GB Jaz drive:
848 * This drive spins down on its own to preserve the media.
849 * When spun down, TUR fails with 2/4/0 (SCSI_SENSE_NOT_READY/SCSI_ADSENSE_LUN_NOT_READY/?).
850 * ClassInterpretSenseInfo would then call ClassSendStartUnit to spin the media up, which defeats the
851 * purpose of the spindown.
852 * So in this case, make this into a successful TUR.
853 * This allows the drive to stay spun down until it is actually accessed again.
854 * (If the media were actually removed, TUR would fail with 2/3a/0 ).
855 * This hack only applies to drives with the CAUSE_NOT_REPORTABLE_HACK bit set; this
856 * is set by disk.sys when HackCauseNotReportableHack is set for the drive in its BadControllers list.
858 if ((SRB_STATUS(Srb
->SrbStatus
) != SRB_STATUS_SUCCESS
) &&
859 TEST_FLAG(fdoExtension
->ScanForSpecialFlags
, CLASS_SPECIAL_CAUSE_NOT_REPORTABLE_HACK
) &&
860 (Srb
->SenseInfoBufferLength
>= RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA
, AdditionalSenseCode
))){
862 PSENSE_DATA senseData
= Srb
->SenseInfoBuffer
;
864 if ((senseData
->SenseKey
== SCSI_SENSE_NOT_READY
) &&
865 (senseData
->AdditionalSenseCode
== SCSI_ADSENSE_LUN_NOT_READY
)){
866 Srb
->SrbStatus
= SRB_STATUS_SUCCESS
;
872 // use ClassInterpretSenseInfo() to check for media state, and also
873 // to call ClassError() with correct parameters.
875 status
= STATUS_SUCCESS
;
876 if (SRB_STATUS(Srb
->SrbStatus
) != SRB_STATUS_SUCCESS
) {
878 DBGTRACE(ClassDebugMCN
, ("ClasspMediaChangeDetectionCompletion - failed - srb status=%s, sense=%s/%s/%s.", DBGGETSRBSTATUSSTR(Srb
), DBGGETSENSECODESTR(Srb
), DBGGETADSENSECODESTR(Srb
), DBGGETADSENSEQUALIFIERSTR(Srb
)));
880 ClassInterpretSenseInfo(DeviceObject
,
891 fdoData
->LoggedTURFailureSinceLastIO
= FALSE
;
893 if (!info
->Gesn
.Supported
) {
895 DBGTRACE(ClassDebugMCN
, ("ClasspMediaChangeDetectionCompletion - succeeded and GESN NOT supported, setting MediaPresent."));
898 // success != media for GESN case
901 ClassSetMediaChangeState(fdoExtension
, MediaPresent
, FALSE
);
905 DBGTRACE(ClassDebugMCN
, ("ClasspMediaChangeDetectionCompletion - succeeded (GESN supported)."));
909 if (info
->Gesn
.Supported
) {
911 if (status
== STATUS_DATA_OVERRUN
) {
912 DBGTRACE(ClassDebugMCN
, ("ClasspMediaChangeDetectionCompletion - Overrun"));
913 status
= STATUS_SUCCESS
;
916 if (!NT_SUCCESS(status
)) {
917 DBGTRACE(ClassDebugMCN
, ("ClasspMediaChangeDetectionCompletion: GESN failed with status %x", status
));
921 // for GESN, need to interpret the results of the data.
922 // this may also require an immediate retry
925 if (Irp
->IoStatus
.Information
== 8 ) {
926 ClasspInterpretGesnData(fdoExtension
,
927 (PVOID
)info
->Gesn
.Buffer
,
931 } // end of NT_SUCCESS(status)
933 } // end of Info->Gesn.Supported
936 // free port-allocated sense buffer, if any.
939 if (PORT_ALLOCATED_SENSE(fdoExtension
, Srb
)) {
940 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension
, Srb
);
944 // Remember the IRP and SRB for use the next time.
947 ASSERT(IoGetNextIrpStackLocation(Irp
));
948 IoGetNextIrpStackLocation(Irp
)->Parameters
.Scsi
.Srb
= Srb
;
951 // Reset the MCN timer.
954 ClassResetMediaChangeTimer(fdoExtension
);
957 // run a sanity check to make sure we're not recursing continuously
960 if (retryImmediately
) {
962 info
->MediaChangeRetryCount
++;
963 if (info
->MediaChangeRetryCount
> MAXIMUM_IMMEDIATE_MCN_RETRIES
) {
964 ASSERT(!"Recursing too often in MCN?");
965 info
->MediaChangeRetryCount
= 0;
966 retryImmediately
= FALSE
;
971 info
->MediaChangeRetryCount
= 0;
977 // release the remove lock....
982 ClassAcquireRemoveLock(DeviceObject
, (PIRP
)(&uniqueValue
));
983 ClassReleaseRemoveLock(DeviceObject
, Irp
);
987 // set the irp as not in use
990 volatile LONG irpWasInUse
;
991 irpWasInUse
= InterlockedCompareExchange(&info
->MediaChangeIrpInUse
, 0, 1);
992 #if _MSC_FULL_VER != 13009111 // This compiler always takes the wrong path here.
998 // now send it again before we release our last remove lock
1001 if (retryImmediately
) {
1002 ClasspSendMediaStateIrp(fdoExtension
, info
, 0);
1005 DBGTRACE(ClassDebugMCN
, ("ClasspMediaChangeDetectionCompletion - not retrying immediately"));
1009 // release the temporary remove lock
1012 ClassReleaseRemoveLock(DeviceObject
, (PIRP
)(&uniqueValue
));
1015 DBGTRACE(ClassDebugMCN
, ("< ClasspMediaChangeDetectionCompletion"));
1017 return STATUS_MORE_PROCESSING_REQUIRED
;
1020 /*++////////////////////////////////////////////////////////////////////////////
1022 ClasspSendTestUnitIrp() - ISSUE-2000/02/20-henrygab - not documented
1024 Routine Description:
1039 ClasspPrepareMcnIrp(
1040 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
1041 IN PMEDIA_CHANGE_DETECTION_INFO Info
,
1045 PSCSI_REQUEST_BLOCK srb
;
1046 PIO_STACK_LOCATION irpStack
;
1047 PIO_STACK_LOCATION nextIrpStack
;
1054 // Setup the IRP to perform a test unit ready.
1057 irp
= Info
->MediaChangeIrp
;
1066 // don't keep sending this if the device is being removed.
1069 status
= ClassAcquireRemoveLock(FdoExtension
->DeviceObject
, irp
);
1070 if (status
== REMOVE_COMPLETE
) {
1071 ASSERT(status
!= REMOVE_COMPLETE
);
1074 else if (status
== REMOVE_PENDING
) {
1075 ClassReleaseRemoveLock(FdoExtension
->DeviceObject
, irp
);
1079 ASSERT(status
== NO_REMOVE
);
1082 irp
->IoStatus
.Status
= STATUS_SUCCESS
;
1083 irp
->IoStatus
.Information
= 0;
1085 irp
->UserBuffer
= NULL
;
1088 // If the irp is sent down when the volume needs to be
1089 // verified, CdRomUpdateGeometryCompletion won't complete
1090 // it since it's not associated with a thread. Marking
1091 // it to override the verify causes it always be sent
1092 // to the port driver
1095 irpStack
= IoGetCurrentIrpStackLocation(irp
);
1096 irpStack
->Flags
|= SL_OVERRIDE_VERIFY_VOLUME
;
1098 nextIrpStack
= IoGetNextIrpStackLocation(irp
);
1099 nextIrpStack
->MajorFunction
= IRP_MJ_INTERNAL_DEVICE_CONTROL
;
1100 nextIrpStack
->Parameters
.Scsi
.Srb
= &(Info
->MediaChangeSrb
);
1103 // Prepare the SRB for execution.
1106 srb
= nextIrpStack
->Parameters
.Scsi
.Srb
;
1107 buffer
= srb
->SenseInfoBuffer
;
1108 RtlZeroMemory(srb
, sizeof(SCSI_REQUEST_BLOCK
));
1109 RtlZeroMemory(buffer
, SENSE_BUFFER_SIZE
);
1112 srb
->QueueTag
= SP_UNTAGGED
;
1113 srb
->QueueAction
= SRB_SIMPLE_TAG_REQUEST
;
1114 srb
->Length
= sizeof(SCSI_REQUEST_BLOCK
);
1115 srb
->Function
= SRB_FUNCTION_EXECUTE_SCSI
;
1116 srb
->SenseInfoBuffer
= buffer
;
1118 srb
->ScsiStatus
= 0;
1119 srb
->OriginalRequest
= irp
;
1120 srb
->SenseInfoBufferLength
= SENSE_BUFFER_SIZE
;
1122 srb
->SrbFlags
= FdoExtension
->SrbFlags
;
1123 SET_FLAG(srb
->SrbFlags
, Info
->SrbFlags
);
1125 srb
->TimeOutValue
= FdoExtension
->TimeOutValue
* 2;
1127 if (srb
->TimeOutValue
== 0) {
1129 if (FdoExtension
->TimeOutValue
== 0) {
1131 KdPrintEx((DPFLTR_CLASSPNP_ID
, DPFLTR_ERROR_LEVEL
,
1132 "ClassSendTestUnitIrp: FdoExtension->TimeOutValue "
1133 "is set to zero?! -- resetting to 10\n"));
1134 srb
->TimeOutValue
= 10 * 2; // reasonable default
1138 KdPrintEx((DPFLTR_CLASSPNP_ID
, DPFLTR_ERROR_LEVEL
,
1139 "ClassSendTestUnitIrp: Someone set "
1140 "srb->TimeOutValue to zero?! -- resetting to %x\n",
1141 FdoExtension
->TimeOutValue
* 2));
1142 srb
->TimeOutValue
= FdoExtension
->TimeOutValue
* 2;
1151 srb
->DataTransferLength
= 0;
1152 SET_FLAG(srb
->SrbFlags
, SRB_FLAGS_NO_DATA_TRANSFER
);
1153 nextIrpStack
->Parameters
.DeviceIoControl
.IoControlCode
=
1154 IOCTL_SCSI_EXECUTE_NONE
;
1155 srb
->DataBuffer
= NULL
;
1156 srb
->DataTransferLength
= 0;
1157 irp
->MdlAddress
= NULL
;
1159 cdb
= (PCDB
) &srb
->Cdb
[0];
1160 cdb
->CDB6GENERIC
.OperationCode
= SCSIOP_TEST_UNIT_READY
;
1164 ASSERT(Info
->Gesn
.Buffer
);
1166 srb
->TimeOutValue
= GESN_TIMEOUT_VALUE
; // much shorter timeout for GESN
1168 srb
->CdbLength
= 10;
1169 SET_FLAG(srb
->SrbFlags
, SRB_FLAGS_DATA_IN
);
1170 nextIrpStack
->Parameters
.DeviceIoControl
.IoControlCode
=
1171 IOCTL_SCSI_EXECUTE_IN
;
1172 srb
->DataBuffer
= Info
->Gesn
.Buffer
;
1173 srb
->DataTransferLength
= Info
->Gesn
.BufferSize
;
1174 irp
->MdlAddress
= Info
->Gesn
.Mdl
;
1176 cdb
= (PCDB
) &srb
->Cdb
[0];
1177 cdb
->GET_EVENT_STATUS_NOTIFICATION
.OperationCode
=
1178 SCSIOP_GET_EVENT_STATUS
;
1179 cdb
->GET_EVENT_STATUS_NOTIFICATION
.Immediate
= 1;
1180 cdb
->GET_EVENT_STATUS_NOTIFICATION
.EventListLength
[0] =
1181 (UCHAR
)((Info
->Gesn
.BufferSize
) >> 8);
1182 cdb
->GET_EVENT_STATUS_NOTIFICATION
.EventListLength
[1] =
1183 (UCHAR
)((Info
->Gesn
.BufferSize
) & 0xff);
1184 cdb
->GET_EVENT_STATUS_NOTIFICATION
.NotificationClassRequest
=
1185 Info
->Gesn
.EventMask
;
1189 IoSetCompletionRoutine(irp
,
1190 ClasspMediaChangeDetectionCompletion
,
1200 /*++////////////////////////////////////////////////////////////////////////////
1202 ClasspSendMediaStateIrp() - ISSUE-2000/02/20-henrygab - not documented
1204 Routine Description:
1218 ClasspSendMediaStateIrp(
1219 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
1220 IN PMEDIA_CHANGE_DETECTION_INFO Info
,
1224 BOOLEAN requestPending
= FALSE
;
1226 //LARGE_INTEGER zero;
1229 DBGTRACE(ClassDebugMCN
, ("> ClasspSendMediaStateIrp"));
1231 if (((FdoExtension
->CommonExtension
.CurrentState
!= IRP_MN_START_DEVICE
) ||
1232 (FdoExtension
->DevicePowerState
!= PowerDeviceD0
)
1234 (!Info
->MediaChangeIrpLost
)) {
1237 // the device may be stopped, powered down, or otherwise queueing io,
1238 // so should not timeout the autorun irp (yet) -- set to zero ticks.
1239 // scattered code relies upon this to not prematurely "lose" an
1240 // autoplay irp that was queued.
1243 Info
->MediaChangeIrpTimeInUse
= 0;
1247 // if the irp is not in use, mark it as such.
1250 irpInUse
= InterlockedCompareExchange(&Info
->MediaChangeIrpInUse
, 1, 0);
1256 timeInUse
= InterlockedIncrement(&Info
->MediaChangeIrpTimeInUse
);
1258 DebugPrint((ClassDebugMCN
, "ClasspSendMediaStateIrp: irp in use for "
1259 "%x seconds when synchronizing for MCD\n", timeInUse
));
1261 if (Info
->MediaChangeIrpLost
== FALSE
) {
1263 if (timeInUse
> MEDIA_CHANGE_TIMEOUT_TIME
) {
1266 // currently set to five minutes. hard to imagine a drive
1267 // taking that long to spin up.
1270 DebugPrint((ClassDebugError
,
1271 "CdRom%d: Media Change Notification has lost "
1272 "it's irp and doesn't know where to find it. "
1273 "Leave it alone and it'll come home dragging "
1274 "it's stack behind it.\n",
1275 FdoExtension
->DeviceNumber
));
1276 Info
->MediaChangeIrpLost
= TRUE
;
1280 DBGTRACE(ClassDebugMCN
, ("< ClasspSendMediaStateIrp - irpInUse"));
1287 if (Info
->MediaChangeDetectionDisableCount
!= 0) {
1288 DebugPrint((ClassDebugTrace
, "ClassCheckMediaState: device %p has "
1289 " detection disabled \n", FdoExtension
->DeviceObject
));
1293 if (FdoExtension
->DevicePowerState
!= PowerDeviceD0
) {
1295 if (TEST_FLAG(Info
->SrbFlags
, SRB_FLAGS_NO_KEEP_AWAKE
)) {
1296 DebugPrint((ClassDebugMCN
,
1297 "ClassCheckMediaState: device %p is powered "
1298 "down and flags are set to let it sleep\n",
1299 FdoExtension
->DeviceObject
));
1300 ClassResetMediaChangeTimer(FdoExtension
);
1305 // NOTE: we don't increment the time in use until our power state
1306 // changes above. this way, we won't "lose" the autoplay irp.
1307 // it's up to the lower driver to determine if powering up is a
1311 DebugPrint((ClassDebugMCN
,
1312 "ClassCheckMediaState: device %p needs to powerup "
1313 "to handle this io (may take a few extra seconds).\n",
1314 FdoExtension
->DeviceObject
));
1318 Info
->MediaChangeIrpTimeInUse
= 0;
1319 Info
->MediaChangeIrpLost
= FALSE
;
1321 if (CountDown
== 0) {
1325 DebugPrint((ClassDebugTrace
,
1326 "ClassCheckMediaState: timer expired\n"));
1328 if (Info
->MediaChangeDetectionDisableCount
!= 0) {
1329 DebugPrint((ClassDebugTrace
,
1330 "ClassCheckMediaState: detection disabled\n"));
1335 // Prepare the IRP for the test unit ready
1338 irp
= ClasspPrepareMcnIrp(FdoExtension
,
1340 Info
->Gesn
.Supported
);
1343 // Issue the request.
1346 DebugPrint((ClassDebugTrace
,
1347 "ClasspSendMediaStateIrp: Device %p getting TUR "
1348 " irp %p\n", FdoExtension
->DeviceObject
, irp
));
1356 // note: if we send it to the class dispatch routines, there is
1357 // a timing window here (since they grab the remove lock)
1358 // where we'd be removed. ELIMINATE the window by grabbing
1359 // the lock ourselves above and sending it to the lower
1360 // device object directly or to the device's StartIo
1361 // routine (which doesn't acquire the lock).
1364 requestPending
= TRUE
;
1366 DBGTRACE(ClassDebugMCN
, (" ClasspSendMediaStateIrp - calling IoCallDriver."));
1367 IoCallDriver(FdoExtension
->CommonExtension
.LowerDeviceObject
, irp
);
1372 if(requestPending
== FALSE
) {
1373 irpInUse
= InterlockedCompareExchange(&Info
->MediaChangeIrpInUse
, 0, 1);
1374 #if _MSC_FULL_VER != 13009111 // This compiler always takes the wrong path here.
1381 DBGTRACE(ClassDebugMCN
, ("< ClasspSendMediaStateIrp"));
1384 } // end ClasspSendMediaStateIrp()
1386 /*++////////////////////////////////////////////////////////////////////////////
1388 ClassCheckMediaState()
1390 Routine Description:
1392 This routine is called by the class driver to test for a media change
1393 condition and/or poll for disk failure prediction. It should be called
1394 from the class driver's IO timer routine once per second.
1398 FdoExtension - the device extension
1407 ClassCheckMediaState(
1408 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
1411 PMEDIA_CHANGE_DETECTION_INFO info
= FdoExtension
->MediaChangeDetectionInfo
;
1415 DebugPrint((ClassDebugTrace
,
1416 "ClassCheckMediaState: detection not enabled\n"));
1421 // Media change support is active and the IRP is waiting. Decrement the
1422 // timer. There is no MP protection on the timer counter. This code
1423 // is the only code that will manipulate the timer counter and only one
1424 // instance of it should be running at any given time.
1427 countDown
= InterlockedDecrement(&(info
->MediaChangeCountDown
));
1430 // Try to acquire the media change event. If we can't do it immediately
1431 // then bail out and assume the caller will try again later.
1433 ClasspSendMediaStateIrp(FdoExtension
,
1438 } // end ClassCheckMediaState()
1440 /*++////////////////////////////////////////////////////////////////////////////
1442 ClassResetMediaChangeTimer()
1444 Routine Description:
1446 Resets the media change count down timer to the default number of seconds.
1450 FdoExtension - the device to reset the timer for
1459 ClassResetMediaChangeTimer(
1460 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
1463 PMEDIA_CHANGE_DETECTION_INFO info
= FdoExtension
->MediaChangeDetectionInfo
;
1466 InterlockedExchange(&(info
->MediaChangeCountDown
),
1467 MEDIA_CHANGE_DEFAULT_TIME
);
1470 } // end ClassResetMediaChangeTimer()
1472 /*++////////////////////////////////////////////////////////////////////////////
1474 ClasspInitializePolling() - ISSUE-2000/02/20-henrygab - not documented
1476 Routine Description:
1490 ClasspInitializePolling(
1491 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
1492 IN BOOLEAN AllowDriveToSleep
1495 PDEVICE_OBJECT fdo
= FdoExtension
->DeviceObject
;
1496 //PCLASS_PRIVATE_FDO_DATA fdoData = FdoExtension->PrivateFdoData;
1499 PMEDIA_CHANGE_DETECTION_INFO info
;
1504 if (FdoExtension
->MediaChangeDetectionInfo
!= NULL
) {
1505 return STATUS_SUCCESS
;
1508 info
= ExAllocatePoolWithTag(NonPagedPool
,
1509 sizeof(MEDIA_CHANGE_DETECTION_INFO
),
1510 CLASS_TAG_MEDIA_CHANGE_DETECTION
);
1513 RtlZeroMemory(info
, sizeof(MEDIA_CHANGE_DETECTION_INFO
));
1515 FdoExtension
->KernelModeMcnContext
.FileObject
= (PVOID
)-1;
1516 FdoExtension
->KernelModeMcnContext
.DeviceObject
= (PVOID
)-1;
1517 FdoExtension
->KernelModeMcnContext
.LockCount
= 0;
1518 FdoExtension
->KernelModeMcnContext
.McnDisableCount
= 0;
1521 * Allocate an IRP to carry the Test-Unit-Ready.
1522 * Allocate an extra IRP stack location
1523 * so we can cache our device object in the top location.
1525 irp
= IoAllocateIrp((CCHAR
)(fdo
->StackSize
+1), FALSE
);
1531 buffer
= ExAllocatePoolWithTag(
1532 NonPagedPoolCacheAligned
,
1534 CLASS_TAG_MEDIA_CHANGE_DETECTION
);
1536 if (buffer
!= NULL
) {
1537 PIO_STACK_LOCATION irpStack
;
1538 PSCSI_REQUEST_BLOCK srb
;
1541 srb
= &(info
->MediaChangeSrb
);
1542 info
->MediaChangeIrp
= irp
;
1543 info
->SenseBuffer
= buffer
;
1546 * For the driver that creates an IRP, there is no 'current' stack location.
1547 * Step down one IRP stack location so that the extra top one
1548 * becomes our 'current' one.
1550 IoSetNextIrpStackLocation(irp
);
1553 * Cache our device object in the extra top IRP stack location
1554 * so we have it in our completion routine.
1556 irpStack
= IoGetCurrentIrpStackLocation(irp
);
1557 irpStack
->DeviceObject
= fdo
;
1560 * Now start setting up the next IRP stack location for the call like any driver would.
1562 irpStack
= IoGetNextIrpStackLocation(irp
);
1563 irpStack
->Parameters
.Scsi
.Srb
= srb
;
1564 info
->MediaChangeIrp
= irp
;
1567 // Initialize the SRB
1570 RtlZeroMemory(srb
, sizeof(SCSI_REQUEST_BLOCK
));
1573 // Initialize and set up the sense information buffer
1576 RtlZeroMemory(buffer
, SENSE_BUFFER_SIZE
);
1577 srb
->SenseInfoBuffer
= buffer
;
1578 srb
->SenseInfoBufferLength
= SENSE_BUFFER_SIZE
;
1581 // Set default values for the media change notification
1585 info
->MediaChangeCountDown
= MEDIA_CHANGE_DEFAULT_TIME
;
1586 info
->MediaChangeDetectionDisableCount
= 0;
1589 // Assume that there is initially no media in the device
1590 // only notify upper layers if there is something there
1593 info
->MediaChangeDetectionState
= MediaUnknown
;
1595 info
->MediaChangeIrpTimeInUse
= 0;
1596 info
->MediaChangeIrpLost
= FALSE
;
1599 // setup all extra flags we'll be setting for this irp
1602 if (AllowDriveToSleep
) {
1603 SET_FLAG(info
->SrbFlags
, SRB_FLAGS_NO_KEEP_AWAKE
);
1605 SET_FLAG(info
->SrbFlags
, SRB_CLASS_FLAGS_LOW_PRIORITY
);
1606 SET_FLAG(info
->SrbFlags
, SRB_FLAGS_NO_QUEUE_FREEZE
);
1607 SET_FLAG(info
->SrbFlags
, SRB_FLAGS_DISABLE_SYNCH_TRANSFER
);
1609 KeInitializeMutex(&info
->MediaChangeMutex
, 0x100);
1612 // It is ok to support media change events on this
1616 FdoExtension
->MediaChangeDetectionInfo
= info
;
1619 // NOTE: the DeviceType is FILE_DEVICE_CD_ROM even
1620 // when the device supports DVD (no need to
1621 // check for FILE_DEVICE_DVD, as it's not a
1625 if (FdoExtension
->DeviceObject
->DeviceType
== FILE_DEVICE_CD_ROM
){
1629 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
1630 "ClasspInitializePolling: Testing for GESN\n"));
1631 status
= ClasspInitializeGesn(FdoExtension
, info
);
1632 if (NT_SUCCESS(status
)) {
1633 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
1634 "ClasspInitializePolling: GESN available "
1635 "for %p\n", FdoExtension
->DeviceObject
));
1636 ASSERT(info
->Gesn
.Supported
);
1637 ASSERT(info
->Gesn
.Buffer
!= NULL
);
1638 ASSERT(info
->Gesn
.BufferSize
!= 0);
1639 ASSERT(info
->Gesn
.EventMask
!= 0);
1640 // must return here, for ASSERTs to be valid.
1641 return STATUS_SUCCESS
;
1643 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
1644 "ClasspInitializePolling: GESN *NOT* available "
1645 "for %p\n", FdoExtension
->DeviceObject
));
1648 ASSERT(info
->Gesn
.Supported
== 0);
1649 ASSERT(info
->Gesn
.Buffer
== NULL
);
1650 ASSERT(info
->Gesn
.BufferSize
== 0);
1651 ASSERT(info
->Gesn
.EventMask
== 0);
1652 info
->Gesn
.Supported
= 0; // just in case....
1653 return STATUS_SUCCESS
;
1663 // nothing to free here
1665 return STATUS_INSUFFICIENT_RESOURCES
;
1667 } // end ClasspInitializePolling()
1671 ClasspInitializeGesn(
1672 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
1673 IN PMEDIA_CHANGE_DETECTION_INFO Info
1676 PNOTIFICATION_EVENT_STATUS_HEADER header
;
1677 CLASS_DETECTION_STATE detectionState
= ClassDetectionUnknown
;
1678 PSTORAGE_ADAPTER_DESCRIPTOR adapterDescriptor
;
1679 NTSTATUS status
= STATUS_NOT_SUPPORTED
;
1682 BOOLEAN retryImmediately
;
1688 ASSERT(Info
== FdoExtension
->MediaChangeDetectionInfo
);
1691 // read if we already know the abilities of the device
1694 ClassGetDeviceParameter(FdoExtension
,
1695 CLASSP_REG_SUBKEY_NAME
,
1696 CLASSP_REG_MMC_DETECTION_VALUE_NAME
,
1697 (PULONG
)&detectionState
);
1699 if (detectionState
== ClassDetectionUnsupported
) {
1704 // check if the device has a hack flag saying never to try this.
1707 if (TEST_FLAG(FdoExtension
->PrivateFdoData
->HackFlags
,
1708 FDO_HACK_GESN_IS_BAD
)) {
1710 detectionState
= ClassDetectionUnsupported
;
1711 ClassSetDeviceParameter(FdoExtension
,
1712 CLASSP_REG_SUBKEY_NAME
,
1713 CLASSP_REG_MMC_DETECTION_VALUE_NAME
,
1714 ClassDetectionSupported
);
1721 // else go through the process since we allocate buffers and
1722 // get all sorts of device settings.
1725 if (Info
->Gesn
.Buffer
== NULL
) {
1726 Info
->Gesn
.Buffer
= ExAllocatePoolWithTag(NonPagedPoolCacheAligned
,
1730 if (Info
->Gesn
.Buffer
== NULL
) {
1731 status
= STATUS_INSUFFICIENT_RESOURCES
;
1734 if (Info
->Gesn
.Mdl
!= NULL
) {
1735 IoFreeMdl(Info
->Gesn
.Mdl
);
1737 Info
->Gesn
.Mdl
= IoAllocateMdl(Info
->Gesn
.Buffer
,
1739 FALSE
, FALSE
, NULL
);
1740 if (Info
->Gesn
.Mdl
== NULL
) {
1741 status
= STATUS_INSUFFICIENT_RESOURCES
;
1745 MmBuildMdlForNonPagedPool(Info
->Gesn
.Mdl
);
1746 Info
->Gesn
.BufferSize
= GESN_BUFFER_SIZE
;
1747 Info
->Gesn
.EventMask
= 0;
1750 // all items are prepared to use GESN (except the event mask, so don't
1751 // optimize this part out!).
1753 // now see if it really works. we have to loop through this because
1754 // many SAMSUNG (and one COMPAQ) drives timeout when requesting
1755 // NOT_READY events, even when the IMMEDIATE bit is set. :(
1757 // using a drive list is cumbersome, so this might fix the problem.
1760 adapterDescriptor
= FdoExtension
->AdapterDescriptor
;
1762 retryImmediately
= TRUE
;
1763 for (i
= 0; i
< 16 && retryImmediately
== TRUE
; i
++) {
1765 irp
= ClasspPrepareMcnIrp(FdoExtension
, Info
, TRUE
);
1767 status
= STATUS_INSUFFICIENT_RESOURCES
;
1771 ASSERT(TEST_FLAG(Info
->MediaChangeSrb
.SrbFlags
, SRB_FLAGS_NO_QUEUE_FREEZE
));
1774 // replace the completion routine with a different one this time...
1777 IoSetCompletionRoutine(irp
,
1778 ClassSignalCompletion
,
1781 KeInitializeEvent(&event
, SynchronizationEvent
, FALSE
);
1783 status
= IoCallDriver(FdoExtension
->CommonExtension
.LowerDeviceObject
, irp
);
1785 if (status
== STATUS_PENDING
) {
1786 status
= KeWaitForSingleObject(&event
,
1791 ASSERT(NT_SUCCESS(status
));
1793 ClassReleaseRemoveLock(FdoExtension
->DeviceObject
, irp
);
1795 if (SRB_STATUS(Info
->MediaChangeSrb
.SrbStatus
) != SRB_STATUS_SUCCESS
) {
1796 ClassInterpretSenseInfo(FdoExtension
->DeviceObject
,
1797 &(Info
->MediaChangeSrb
),
1805 if ((adapterDescriptor
->BusType
== BusTypeAtapi
) &&
1806 (Info
->MediaChangeSrb
.SrbStatus
== SRB_STATUS_BUS_RESET
)
1810 // ATAPI unfortunately returns SRB_STATUS_BUS_RESET instead
1811 // of SRB_STATUS_TIMEOUT, so we cannot differentiate between
1812 // the two. if we get this status four time consecutively,
1813 // stop trying this command. it is too late to change ATAPI
1814 // at this point, so special-case this here. (07/10/2001)
1815 // NOTE: any value more than 4 may cause the device to be
1820 if (atapiResets
>= 4) {
1821 status
= STATUS_IO_DEVICE_ERROR
;
1826 if (status
== STATUS_DATA_OVERRUN
) {
1827 status
= STATUS_SUCCESS
;
1830 if ((status
== STATUS_INVALID_DEVICE_REQUEST
) ||
1831 (status
== STATUS_TIMEOUT
) ||
1832 (status
== STATUS_IO_DEVICE_ERROR
) ||
1833 (status
== STATUS_IO_TIMEOUT
)
1837 // with these error codes, we don't ever want to try this command
1838 // again on this device, since it reacts poorly.
1841 ClassSetDeviceParameter(FdoExtension
,
1842 CLASSP_REG_SUBKEY_NAME
,
1843 CLASSP_REG_MMC_DETECTION_VALUE_NAME
,
1844 ClassDetectionUnsupported
);
1845 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugWarning
,
1846 "Classpnp => GESN test failed %x for fdo %p\n",
1847 status
, FdoExtension
->DeviceObject
));
1853 if (!NT_SUCCESS(status
)) {
1856 // this may be other errors that should not disable GESN
1857 // for all future start_device calls.
1860 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugWarning
,
1861 "Classpnp => GESN test failed %x for fdo %p\n",
1862 status
, FdoExtension
->DeviceObject
));
1869 // the first time, the request was just retrieving a mask of
1870 // available bits. use this to mask future requests.
1873 header
= (PNOTIFICATION_EVENT_STATUS_HEADER
)(Info
->Gesn
.Buffer
);
1875 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
1876 "Classpnp => Fdo %p supports event mask %x\n",
1877 FdoExtension
->DeviceObject
, header
->SupportedEventClasses
));
1880 if (TEST_FLAG(header
->SupportedEventClasses
,
1881 NOTIFICATION_MEDIA_STATUS_CLASS_MASK
)) {
1882 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
1883 "Classpnp => GESN supports MCN\n"));
1885 if (TEST_FLAG(header
->SupportedEventClasses
,
1886 NOTIFICATION_DEVICE_BUSY_CLASS_MASK
)) {
1887 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
1888 "Classpnp => GESN supports DeviceBusy\n"));
1890 Info
->Gesn
.EventMask
= header
->SupportedEventClasses
;
1893 // realistically, we are only considering the following events:
1894 // EXTERNAL REQUEST - this is being tested for play/stop/etc.
1895 // MEDIA STATUS - autorun and ejection requests.
1896 // DEVICE BUSY - to allow us to predict when media will be ready.
1897 // therefore, we should not bother querying for the other,
1898 // unknown events. clear all but the above flags.
1901 Info
->Gesn
.EventMask
&=
1902 NOTIFICATION_EXTERNAL_REQUEST_CLASS_MASK
|
1903 NOTIFICATION_MEDIA_STATUS_CLASS_MASK
|
1904 NOTIFICATION_DEVICE_BUSY_CLASS_MASK
;
1908 // HACKHACK - REF #0001
1909 // Some devices will *never* report an event if we've also requested
1910 // that it report lower-priority events. this is due to a
1911 // misunderstanding in the specification wherein a "No Change" is
1912 // interpreted to be a real event. what should occur is that the
1913 // device should ignore "No Change" events when multiple event types
1914 // are requested unless there are no other events waiting. this
1915 // greatly reduces the number of requests that the host must send
1916 // to determine if an event has occurred. Since we must work on all
1917 // drives, default to enabling the hack until we find evidence of
1921 if (CountOfSetBitsUChar(Info
->Gesn
.EventMask
) == 1) {
1922 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
1923 "Classpnp => GESN hack %s for FDO %p\n",
1924 "not required", FdoExtension
->DeviceObject
));
1926 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
1927 "Classpnp => GESN hack %s for FDO %p\n",
1928 "enabled", FdoExtension
->DeviceObject
));
1929 Info
->Gesn
.HackEventMask
= 1;
1935 // not the first time looping through, so interpret the results.
1938 ClasspInterpretGesnData(FdoExtension
,
1939 (PVOID
)Info
->Gesn
.Buffer
,
1944 } // end loop of GESN requests....
1947 // we can only use this if it can be relied upon for media changes,
1948 // since we are (by definition) no longer going to be polling via
1949 // a TEST_UNIT_READY irp, and drives will not report UNIT ATTENTION
1950 // for this command (although a filter driver, such as one for burning
1951 // cd's, might still fake those errors).
1953 // since we also rely upon NOT_READY events to change the cursor
1954 // into a "wait" cursor, we can't use GESN without NOT_READY support.
1957 if (TEST_FLAG(Info
->Gesn
.EventMask
,
1958 NOTIFICATION_MEDIA_STATUS_CLASS_MASK
) &&
1959 TEST_FLAG(Info
->Gesn
.EventMask
,
1960 NOTIFICATION_DEVICE_BUSY_CLASS_MASK
)
1963 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
1964 "Classpnp => Enabling GESN support for fdo %p\n",
1965 FdoExtension
->DeviceObject
));
1966 Info
->Gesn
.Supported
= TRUE
;
1968 ClassSetDeviceParameter(FdoExtension
,
1969 CLASSP_REG_SUBKEY_NAME
,
1970 CLASSP_REG_MMC_DETECTION_VALUE_NAME
,
1971 ClassDetectionSupported
);
1973 return STATUS_SUCCESS
;
1977 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
1978 "Classpnp => GESN available but not enabled for fdo %p\n",
1979 FdoExtension
->DeviceObject
));
1985 if (Info
->Gesn
.Mdl
) {
1986 IoFreeMdl(Info
->Gesn
.Mdl
);
1987 Info
->Gesn
.Mdl
= NULL
;
1989 if (Info
->Gesn
.Buffer
) {
1990 ExFreePool(Info
->Gesn
.Buffer
);
1991 Info
->Gesn
.Buffer
= NULL
;
1993 Info
->Gesn
.Supported
= 0;
1994 Info
->Gesn
.EventMask
= 0;
1995 Info
->Gesn
.BufferSize
= 0;
1996 return STATUS_NOT_SUPPORTED
;
2000 /*++////////////////////////////////////////////////////////////////////////////
2002 ClassInitializeTestUnitPolling()
2004 Routine Description:
2006 This routine will initialize MCN regardless of the settings stored
2007 in the registry. This should be used with caution, as some devices
2008 react badly to constant io. (i.e. never spin down, continuously cycling
2009 media in changers, ejection of media, etc.) It is highly suggested to
2010 use ClassInitializeMediaChangeDetection() instead.
2014 FdoExtension is the device to poll
2016 AllowDriveToSleep says whether to attempt to allow the drive to sleep
2017 or not. This only affects system-known spin down states, so if a
2018 drive spins itself down, this has no effect until the system spins
2026 ClassInitializeTestUnitPolling(
2027 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
2028 IN BOOLEAN AllowDriveToSleep
2031 return ClasspInitializePolling(FdoExtension
, AllowDriveToSleep
);
2032 } // end ClassInitializeTestUnitPolling()
2034 /*++////////////////////////////////////////////////////////////////////////////
2036 ClassInitializeMediaChangeDetection()
2038 Routine Description:
2040 This routine checks to see if it is safe to initialize MCN (the back end
2041 to autorun) for a given device. It will then check the device-type wide
2042 key "Autorun" in the service key (for legacy reasons), and then look in
2043 the device-specific key to potentially override that setting.
2045 If MCN is to be enabled, all neccessary structures and memory are
2046 allocated and initialized.
2048 This routine MUST be called only from the ClassInit() callback.
2052 FdoExtension - the device to initialize MCN for, if appropriate
2054 EventPrefix - unused, legacy argument. Set to zero.
2061 ClassInitializeMediaChangeDetection(
2062 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
2063 IN PUCHAR EventPrefix
2066 PDEVICE_OBJECT fdo
= FdoExtension
->DeviceObject
;
2069 PCLASS_DRIVER_EXTENSION driverExtension
= ClassGetDriverExtension(
2072 BOOLEAN disabledForBadHardware
;
2074 BOOLEAN instanceOverride
;
2079 // NOTE: This assumes that ClassInitializeMediaChangeDetection is always
2080 // called in the context of the ClassInitDevice callback. If called
2081 // after then this check will have already been made and the
2082 // once a second timer will not have been enabled.
2085 disabledForBadHardware
= ClasspIsMediaChangeDisabledDueToHardwareLimitation(
2087 &(driverExtension
->RegistryPath
)
2090 if (disabledForBadHardware
) {
2091 DebugPrint((ClassDebugMCN
,
2092 "ClassInitializeMCN: Disabled due to hardware"
2093 "limitations for this device"));
2098 // autorun should now be enabled by default for all media types.
2101 disabled
= ClasspIsMediaChangeDisabledForClass(
2103 &(driverExtension
->RegistryPath
)
2106 DebugPrint((ClassDebugMCN
,
2107 "ClassInitializeMCN: Class MCN is %s\n",
2108 (disabled
? "disabled" : "enabled")));
2110 status
= ClasspMediaChangeDeviceInstanceOverride(
2112 &instanceOverride
); // default value
2114 if (!NT_SUCCESS(status
)) {
2115 DebugPrint((ClassDebugMCN
,
2116 "ClassInitializeMCN: Instance using default\n"));
2118 DebugPrint((ClassDebugMCN
,
2119 "ClassInitializeMCN: Instance override: %s MCN\n",
2120 (instanceOverride
? "Enabling" : "Disabling")));
2121 disabled
= !instanceOverride
;
2124 DebugPrint((ClassDebugMCN
,
2125 "ClassInitializeMCN: Instance MCN is %s\n",
2126 (disabled
? "disabled" : "enabled")));
2133 // if the drive is not a CDROM, allow the drive to sleep
2135 if (FdoExtension
->DeviceObject
->DeviceType
== FILE_DEVICE_CD_ROM
) {
2136 ClasspInitializePolling(FdoExtension
, FALSE
);
2138 ClasspInitializePolling(FdoExtension
, TRUE
);
2142 } // end ClassInitializeMediaChangeDetection()
2144 /*++////////////////////////////////////////////////////////////////////////////
2146 ClasspMediaChangeDeviceInstanceOverride()
2148 Routine Description:
2150 The user can override the global setting to enable or disable Autorun on a
2151 specific cdrom device via the control panel. This routine checks and/or
2156 FdoExtension - the device to set/get the value for
2157 Value - the value to use in a set
2158 SetValue - whether to set the value
2162 TRUE - Autorun is disabled
2163 FALSE - Autorun is not disabled (Default)
2168 ClasspMediaChangeDeviceInstanceOverride(
2169 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
2170 OUT PBOOLEAN Enabled
2173 HANDLE deviceParameterHandle
; // cdrom instance key
2174 HANDLE driverParameterHandle
; // cdrom specific key
2175 RTL_QUERY_REGISTRY_TABLE queryTable
[3];
2176 OBJECT_ATTRIBUTES objectAttributes
;
2177 UNICODE_STRING subkeyName
;
2180 ULONG alwaysDisable
;
2186 deviceParameterHandle
= NULL
;
2187 driverParameterHandle
= NULL
;
2188 status
= STATUS_UNSUCCESSFUL
;
2189 alwaysEnable
= FALSE
;
2190 alwaysDisable
= FALSE
;
2194 status
= IoOpenDeviceRegistryKey( FdoExtension
->LowerPdo
,
2195 PLUGPLAY_REGKEY_DEVICE
,
2197 &deviceParameterHandle
2199 if (!NT_SUCCESS(status
)) {
2202 // this can occur when a new device is added to the system
2203 // this is due to cdrom.sys being an 'essential' driver
2205 DebugPrint((ClassDebugMCN
,
2206 "ClassMediaChangeDeviceInstanceDisabled: "
2207 "Could not open device registry key [%lx]\n", status
));
2211 RtlInitUnicodeString(&subkeyName
, MCN_REG_SUBKEY_NAME
);
2212 InitializeObjectAttributes(&objectAttributes
,
2214 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
2215 deviceParameterHandle
,
2216 (PSECURITY_DESCRIPTOR
) NULL
);
2218 status
= ZwCreateKey(&driverParameterHandle
,
2222 (PUNICODE_STRING
) NULL
,
2223 REG_OPTION_NON_VOLATILE
,
2226 if (!NT_SUCCESS(status
)) {
2227 DebugPrint((ClassDebugMCN
,
2228 "ClassMediaChangeDeviceInstanceDisabled: "
2229 "subkey could not be created. %lx\n", status
));
2234 // Default to not changing autorun behavior, based upon setting
2235 // registryValue to zero.
2240 RtlZeroMemory(&queryTable
[0], sizeof(queryTable
));
2242 queryTable
[0].Flags
= RTL_QUERY_REGISTRY_DIRECT
;
2243 queryTable
[0].DefaultType
= REG_DWORD
;
2244 queryTable
[0].DefaultLength
= 0;
2247 queryTable
[0].Name
= MCN_REG_AUTORUN_DISABLE_INSTANCE_NAME
;
2248 queryTable
[0].EntryContext
= &alwaysDisable
;
2249 queryTable
[0].DefaultData
= &alwaysDisable
;
2251 queryTable
[0].Name
= MCN_REG_AUTORUN_ENABLE_INSTANCE_NAME
;
2252 queryTable
[0].EntryContext
= &alwaysEnable
;
2253 queryTable
[0].DefaultData
= &alwaysEnable
;
2257 // don't care if it succeeds, since we set defaults above
2260 RtlQueryRegistryValues(RTL_REGISTRY_HANDLE
,
2261 (PWSTR
)driverParameterHandle
,
2269 if (driverParameterHandle
) ZwClose(driverParameterHandle
);
2270 if (deviceParameterHandle
) ZwClose(deviceParameterHandle
);
2274 if (alwaysEnable
&& alwaysDisable
) {
2276 DebugPrint((ClassDebugMCN
,
2277 "ClassMediaChangeDeviceInstanceDisabled: %s selected\n",
2278 "Both Enable and Disable set -- DISABLE"));
2279 ASSERT(NT_SUCCESS(status
));
2280 status
= STATUS_SUCCESS
;
2283 } else if (alwaysDisable
) {
2285 DebugPrint((ClassDebugMCN
,
2286 "ClassMediaChangeDeviceInstanceDisabled: %s selected\n",
2288 ASSERT(NT_SUCCESS(status
));
2289 status
= STATUS_SUCCESS
;
2292 } else if (alwaysEnable
) {
2294 DebugPrint((ClassDebugMCN
,
2295 "ClassMediaChangeDeviceInstanceDisabled: %s selected\n",
2297 ASSERT(NT_SUCCESS(status
));
2298 status
= STATUS_SUCCESS
;
2303 DebugPrint((ClassDebugMCN
,
2304 "ClassMediaChangeDeviceInstanceDisabled: %s selected\n",
2306 status
= STATUS_UNSUCCESSFUL
;
2312 } // end ClasspMediaChangeDeviceInstanceOverride()
2314 /*++////////////////////////////////////////////////////////////////////////////
2316 ClasspIsMediaChangeDisabledDueToHardwareLimitation()
2318 Routine Description:
2320 The key AutoRunAlwaysDisable contains a MULTI_SZ of hardware IDs for
2321 which to never enable MediaChangeNotification.
2323 The user can override the global setting to enable or disable Autorun on a
2324 specific cdrom device via the control panel.
2329 RegistryPath - pointer to the unicode string inside
2330 ...\CurrentControlSet\Services\Cdrom
2335 FALSE - Autorun may be enabled
2340 ClasspIsMediaChangeDisabledDueToHardwareLimitation(
2341 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
2342 IN PUNICODE_STRING RegistryPath
2345 PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor
= FdoExtension
->DeviceDescriptor
;
2346 OBJECT_ATTRIBUTES objectAttributes
;
2347 HANDLE serviceKey
= NULL
;
2348 RTL_QUERY_REGISTRY_TABLE parameters
[2];
2350 UNICODE_STRING deviceUnicodeString
;
2351 ANSI_STRING deviceString
;
2352 ULONG mediaChangeNotificationDisabled
= FALSE
;
2360 // open the service key.
2363 InitializeObjectAttributes(&objectAttributes
,
2365 OBJ_CASE_INSENSITIVE
,
2369 status
= ZwOpenKey(&serviceKey
,
2373 ASSERT(NT_SUCCESS(status
));
2376 if(!NT_SUCCESS(status
)) {
2379 // always take the safe path. if we can't open the service key,
2389 // Determine if drive is in a list of those requiring
2390 // autorun to be disabled. this is stored in a REG_MULTI_SZ
2391 // named AutoRunAlwaysDisable. this is required as some autochangers
2392 // must load the disc to reply to ChkVerify request, causing them
2393 // to cycle discs continuously.
2403 deviceString
.Buffer
= NULL
;
2404 deviceUnicodeString
.Buffer
= NULL
;
2407 // there may be nothing to check against
2410 if ((deviceDescriptor
->VendorIdOffset
== 0) &&
2411 (deviceDescriptor
->ProductIdOffset
== 0)) {
2417 if (deviceDescriptor
->VendorIdOffset
== 0) {
2420 vendorId
= (PUCHAR
) deviceDescriptor
+ deviceDescriptor
->VendorIdOffset
;
2421 length
= strlen(vendorId
);
2424 if ( deviceDescriptor
->ProductIdOffset
== 0 ) {
2427 productId
= (PUCHAR
) deviceDescriptor
+ deviceDescriptor
->ProductIdOffset
;
2428 length
+= strlen(productId
);
2431 if ( deviceDescriptor
->ProductRevisionOffset
== 0 ) {
2434 revisionId
= (PUCHAR
) deviceDescriptor
+ deviceDescriptor
->ProductRevisionOffset
;
2435 length
+= strlen(revisionId
);
2439 // allocate a buffer for the string
2442 deviceString
.Length
= (USHORT
)( length
);
2443 deviceString
.MaximumLength
= deviceString
.Length
+ 1;
2444 deviceString
.Buffer
= (PUCHAR
)ExAllocatePoolWithTag( NonPagedPool
,
2445 deviceString
.MaximumLength
,
2446 CLASS_TAG_AUTORUN_DISABLE
2448 if (deviceString
.Buffer
== NULL
) {
2449 DebugPrint((ClassDebugMCN
,
2450 "ClassMediaChangeDisabledForHardware: Unable to alloc "
2451 "string buffer\n" ));
2456 // copy strings to the buffer
2460 if (vendorId
!= NULL
) {
2461 RtlCopyMemory(deviceString
.Buffer
+ offset
,
2464 offset
+= strlen(vendorId
);
2467 if ( productId
!= NULL
) {
2468 RtlCopyMemory(deviceString
.Buffer
+ offset
,
2471 offset
+= strlen(productId
);
2473 if ( revisionId
!= NULL
) {
2474 RtlCopyMemory(deviceString
.Buffer
+ offset
,
2476 strlen(revisionId
));
2477 offset
+= strlen(revisionId
);
2480 ASSERT(offset
== deviceString
.Length
);
2482 deviceString
.Buffer
[deviceString
.Length
] = '\0'; // Null-terminated
2485 // convert to unicode as registry deals with unicode strings
2488 status
= RtlAnsiStringToUnicodeString( &deviceUnicodeString
,
2492 if (!NT_SUCCESS(status
)) {
2493 DebugPrint((ClassDebugMCN
,
2494 "ClassMediaChangeDisabledForHardware: cannot convert "
2495 "to unicode %lx\n", status
));
2500 // query the value, setting valueFound to true if found
2503 RtlZeroMemory(parameters
, sizeof(parameters
));
2505 nullMultiSz
= L
"\0";
2506 parameters
[0].QueryRoutine
= ClasspMediaChangeRegistryCallBack
;
2507 parameters
[0].Flags
= RTL_QUERY_REGISTRY_REQUIRED
;
2508 parameters
[0].Name
= L
"AutoRunAlwaysDisable";
2509 parameters
[0].EntryContext
= &mediaChangeNotificationDisabled
;
2510 parameters
[0].DefaultType
= REG_MULTI_SZ
;
2511 parameters
[0].DefaultData
= nullMultiSz
;
2512 parameters
[0].DefaultLength
= 0;
2514 status
= RtlQueryRegistryValues(RTL_REGISTRY_HANDLE
,
2517 &deviceUnicodeString
,
2520 if ( !NT_SUCCESS(status
) ) {
2526 if (deviceString
.Buffer
!= NULL
) {
2527 ExFreePool( deviceString
.Buffer
);
2529 if (deviceUnicodeString
.Buffer
!= NULL
) {
2530 RtlFreeUnicodeString( &deviceUnicodeString
);
2533 ZwClose(serviceKey
);
2536 if (mediaChangeNotificationDisabled
) {
2537 DebugPrint((ClassDebugMCN
, "ClassMediaChangeDisabledForHardware: "
2538 "Device is on disable list\n"));
2543 } // end ClasspIsMediaChangeDisabledDueToHardwareLimitation()
2545 /*++////////////////////////////////////////////////////////////////////////////
2547 ClasspIsMediaChangeDisabledForClass()
2549 Routine Description:
2551 The user must specify that AutoPlay is to run on the platform
2552 by setting the registry value HKEY_LOCAL_MACHINE\System\CurrentControlSet\
2553 Services\<SERVICE>\Autorun:REG_DWORD:1.
2555 The user can override the global setting to enable or disable Autorun on a
2556 specific cdrom device via the control panel.
2561 RegistryPath - pointer to the unicode string inside
2562 ...\CurrentControlSet\Services\Cdrom
2566 TRUE - Autorun is disabled for this class
2567 FALSE - Autorun is enabled for this class
2572 ClasspIsMediaChangeDisabledForClass(
2573 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
2574 IN PUNICODE_STRING RegistryPath
2577 //PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor = FdoExtension->DeviceDescriptor;
2579 OBJECT_ATTRIBUTES objectAttributes
;
2580 HANDLE serviceKey
= NULL
;
2581 HANDLE parametersKey
= NULL
;
2582 RTL_QUERY_REGISTRY_TABLE parameters
[3];
2584 UNICODE_STRING paramStr
;
2585 //UNICODE_STRING deviceUnicodeString;
2586 //ANSI_STRING deviceString;
2589 // Default to ENABLING MediaChangeNotification (!)
2592 ULONG mcnRegistryValue
= 1;
2600 // open the service key.
2603 InitializeObjectAttributes(&objectAttributes
,
2605 OBJ_CASE_INSENSITIVE
,
2609 status
= ZwOpenKey(&serviceKey
,
2613 ASSERT(NT_SUCCESS(status
));
2615 if(!NT_SUCCESS(status
)) {
2618 // return the default value, which is the
2619 // inverse of the registry setting default
2620 // since this routine asks if it's disabled
2623 DebugPrint((ClassDebugMCN
, "ClassCheckServiceMCN: Defaulting to %s\n",
2624 (mcnRegistryValue
? "Enabled" : "Disabled")));
2625 return (BOOLEAN
)(!mcnRegistryValue
);
2629 RtlZeroMemory(parameters
, sizeof(parameters
));
2632 // Open the parameters key (if any) beneath the services key.
2635 RtlInitUnicodeString(¶mStr
, L
"Parameters");
2637 InitializeObjectAttributes(&objectAttributes
,
2639 OBJ_CASE_INSENSITIVE
,
2643 status
= ZwOpenKey(¶metersKey
,
2647 if (!NT_SUCCESS(status
)) {
2648 parametersKey
= NULL
;
2654 // Check for the Autorun value.
2657 parameters
[0].Flags
= RTL_QUERY_REGISTRY_DIRECT
;
2658 parameters
[0].Name
= L
"Autorun";
2659 parameters
[0].EntryContext
= &mcnRegistryValue
;
2660 parameters
[0].DefaultType
= REG_DWORD
;
2661 parameters
[0].DefaultData
= &mcnRegistryValue
;
2662 parameters
[0].DefaultLength
= sizeof(ULONG
);
2664 status
= RtlQueryRegistryValues(RTL_REGISTRY_HANDLE
| RTL_REGISTRY_OPTIONAL
,
2670 DebugPrint((ClassDebugMCN
, "ClassCheckServiceMCN: "
2671 "<Service>/Autorun flag = %d\n", mcnRegistryValue
));
2673 if(parametersKey
!= NULL
) {
2675 status
= RtlQueryRegistryValues(RTL_REGISTRY_HANDLE
| RTL_REGISTRY_OPTIONAL
,
2680 DebugPrint((ClassDebugMCN
, "ClassCheckServiceMCN: "
2681 "<Service>/Parameters/Autorun flag = %d\n",
2683 ZwClose(parametersKey
);
2686 ZwClose(serviceKey
);
2688 DebugPrint((ClassDebugMCN
, "ClassCheckServiceMCN: "
2689 "Autoplay for device %p is %s\n",
2690 FdoExtension
->DeviceObject
,
2691 (mcnRegistryValue
? "on" : "off")
2695 // return if it is _disabled_, which is the
2696 // inverse of the registry setting
2699 return (BOOLEAN
)(!mcnRegistryValue
);
2700 } // end ClasspIsMediaChangeDisabledForClass()
2702 /*++////////////////////////////////////////////////////////////////////////////
2704 ClassEnableMediaChangeDetection() ISSUE-2000/02/20-henrygab - why public?
2705 ClassEnableMediaChangeDetection() ISSUE-2000/02/20-henrygab - not documented
2707 Routine Description:
2721 ClassEnableMediaChangeDetection(
2722 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
2725 PMEDIA_CHANGE_DETECTION_INFO info
= FdoExtension
->MediaChangeDetectionInfo
;
2731 DebugPrint((ClassDebugMCN
,
2732 "ClassEnableMediaChangeDetection: not initialized\n"));
2736 KeWaitForMutexObject(&info
->MediaChangeMutex
,
2742 oldCount
= --info
->MediaChangeDetectionDisableCount
;
2744 ASSERT(oldCount
>= 0);
2746 DebugPrint((ClassDebugMCN
, "ClassEnableMediaChangeDetection: Disable count "
2748 info
->MediaChangeDetectionDisableCount
));
2753 // We don't know what state the media is in anymore.
2756 ClasspInternalSetMediaChangeState(FdoExtension
,
2762 // Reset the MCN timer.
2765 ClassResetMediaChangeTimer(FdoExtension
);
2767 DebugPrint((ClassDebugMCN
, "MCD is enabled\n"));
2771 DebugPrint((ClassDebugMCN
, "MCD still disabled\n"));
2777 // Let something else run.
2780 KeReleaseMutex(&info
->MediaChangeMutex
, FALSE
);
2783 } // end ClassEnableMediaChangeDetection()
2785 /*++////////////////////////////////////////////////////////////////////////////
2787 ClassDisableMediaChangeDetection() ISSUE-2000/02/20-henrygab - why public?
2788 ClassDisableMediaChangeDetection() ISSUE-2000/02/20-henrygab - not documented
2790 Routine Description:
2802 ULONG BreakOnMcnDisable
= FALSE
;
2806 ClassDisableMediaChangeDetection(
2807 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
2810 PMEDIA_CHANGE_DETECTION_INFO info
= FdoExtension
->MediaChangeDetectionInfo
;
2818 KeWaitForMutexObject(&info
->MediaChangeMutex
,
2824 info
->MediaChangeDetectionDisableCount
++;
2826 DebugPrint((ClassDebugMCN
, "ClassDisableMediaChangeDetection: "
2827 "disable count is %d\n",
2828 info
->MediaChangeDetectionDisableCount
));
2830 KeReleaseMutex(&info
->MediaChangeMutex
, FALSE
);
2833 } // end ClassDisableMediaChangeDetection()
2835 /*++////////////////////////////////////////////////////////////////////////////
2837 ClassCleanupMediaChangeDetection() ISSUE-2000/02/20-henrygab - why public?!
2839 Routine Description:
2841 This routine will cleanup any resources allocated for MCN. It is called
2842 by classpnp during remove device, and therefore is not typically required
2843 by external drivers.
2852 ClassCleanupMediaChangeDetection(
2853 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
2856 PMEDIA_CHANGE_DETECTION_INFO info
= FdoExtension
->MediaChangeDetectionInfo
;
2864 FdoExtension
->MediaChangeDetectionInfo
= NULL
;
2866 if (info
->Gesn
.Buffer
) {
2867 ExFreePool(info
->Gesn
.Buffer
);
2869 IoFreeIrp(info
->MediaChangeIrp
);
2870 ExFreePool(info
->SenseBuffer
);
2873 } // end ClassCleanupMediaChangeDetection()
2875 /*++////////////////////////////////////////////////////////////////////////////
2877 ClasspMcnControl() - ISSUE-2000/02/20-henrygab - not documented
2879 Routine Description:
2894 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
2896 IN PSCSI_REQUEST_BLOCK Srb
2899 PCOMMON_DEVICE_EXTENSION commonExtension
=
2900 (PCOMMON_DEVICE_EXTENSION
) FdoExtension
;
2902 PIO_STACK_LOCATION irpStack
= IoGetCurrentIrpStackLocation(Irp
);
2903 PPREVENT_MEDIA_REMOVAL request
= Irp
->AssociatedIrp
.SystemBuffer
;
2905 PFILE_OBJECT fileObject
= irpStack
->FileObject
;
2906 PFILE_OBJECT_EXTENSION fsContext
= NULL
;
2908 NTSTATUS status
= STATUS_SUCCESS
;
2913 // Check to make sure we have a file object extension to keep track of this
2914 // request. If not we'll fail it before synchronizing.
2919 if(fileObject
!= NULL
) {
2920 fsContext
= ClasspGetFsContext(commonExtension
, fileObject
);
2921 }else if(Irp
->RequestorMode
== KernelMode
) { // && fileObject == NULL
2922 fsContext
= &FdoExtension
->KernelModeMcnContext
;
2925 if (fsContext
== NULL
) {
2928 // This handle isn't setup correctly. We can't let the
2932 status
= STATUS_INVALID_PARAMETER
;
2936 if(request
->PreventMediaRemoval
) {
2939 // This is a lock command. Reissue the command in case bus or
2940 // device was reset and the lock was cleared.
2943 ClassDisableMediaChangeDetection(FdoExtension
);
2944 InterlockedIncrement(&(fsContext
->McnDisableCount
));
2948 if(fsContext
->McnDisableCount
== 0) {
2949 status
= STATUS_INVALID_DEVICE_STATE
;
2953 InterlockedDecrement(&(fsContext
->McnDisableCount
));
2954 ClassEnableMediaChangeDetection(FdoExtension
);
2959 Irp
->IoStatus
.Status
= status
;
2965 ClassReleaseRemoveLock(FdoExtension
->DeviceObject
, Irp
);
2966 ClassCompleteRequest(FdoExtension
->DeviceObject
,
2971 } // end ClasspMcnControl(
2973 /*++////////////////////////////////////////////////////////////////////////////
2975 ClasspMediaChangeRegistryCallBack()
2977 Routine Description:
2979 This callback for a registry SZ or MULTI_SZ is called once for each
2980 SZ in the value. It will attempt to match the data with the
2981 UNICODE_STRING passed in as Context, and modify EntryContext if a
2982 match is found. Written for ClasspCheckRegistryForMediaChangeCompletion
2986 ValueName - name of the key that was opened
2987 ValueType - type of data stored in the value (REG_SZ for this routine)
2988 ValueData - data in the registry, in this case a wide string
2989 ValueLength - length of the data including the terminating null
2990 Context - unicode string to compare against ValueData
2991 EntryContext - should be initialized to 0, will be set to 1 if match found
2996 EntryContext will be 1 if found
3001 ClasspMediaChangeRegistryCallBack(
3005 IN ULONG ValueLength
,
3007 IN PVOID EntryContext
3011 PUNICODE_STRING deviceString
;
3015 UNREFERENCED_PARAMETER(ValueName
);
3019 // if we have already set the value to true, exit
3022 valueFound
= EntryContext
;
3023 if ((*valueFound
) != 0) {
3024 DebugPrint((ClassDebugMCN
, "ClasspMcnRegCB: already set to true\n"));
3025 return STATUS_SUCCESS
;
3028 if (ValueLength
== sizeof(WCHAR
)) {
3029 DebugPrint((ClassDebugError
, "ClasspMcnRegCB: NULL string should "
3030 "never be passed to registry call-back!\n"));
3031 return STATUS_SUCCESS
;
3036 // if the data is not a terminated string, exit
3039 if (ValueType
!= REG_SZ
) {
3040 return STATUS_SUCCESS
;
3043 deviceString
= Context
;
3044 keyValue
= ValueData
;
3045 ValueLength
-= sizeof(WCHAR
); // ignore the null character
3048 // do not compare more memory than is in deviceString
3051 if (ValueLength
> deviceString
->Length
) {
3052 ValueLength
= deviceString
->Length
;
3056 // if the strings match, disable autorun
3059 if (RtlCompareMemory(deviceString
->Buffer
, keyValue
, ValueLength
) == ValueLength
) {
3060 DebugPrint((ClassDebugMCN
, "ClasspRegMcnCB: Match found\n"));
3061 DebugPrint((ClassDebugMCN
, "ClasspRegMcnCB: DeviceString at %p\n",
3062 deviceString
->Buffer
));
3063 DebugPrint((ClassDebugMCN
, "ClasspRegMcnCB: KeyValue at %p\n",
3065 (*valueFound
) = TRUE
;
3068 return STATUS_SUCCESS
;
3069 } // end ClasspMediaChangeRegistryCallBack()
3071 /*++////////////////////////////////////////////////////////////////////////////
3073 ClasspTimerTick() - ISSUE-2000/02/20-henrygab - not documented
3075 Routine Description:
3090 PDEVICE_OBJECT DeviceObject
,
3094 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= DeviceObject
->DeviceExtension
;
3095 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
3098 ASSERT(commonExtension
->IsFdo
);
3101 // Do any media change work
3103 isRemoved
= ClassAcquireRemoveLock(DeviceObject
, (PIRP
)ClasspTimerTick
);
3106 // We stop the timer before deleting the device. It's safe to keep going
3107 // if the flag value is REMOVE_PENDING because the removal thread will be
3108 // blocked trying to stop the timer.
3111 ASSERT(isRemoved
!= REMOVE_COMPLETE
);
3114 // This routine is reasonably safe even if the device object has a pending
3119 PFAILURE_PREDICTION_INFO info
= fdoExtension
->FailurePredictionInfo
;
3122 // Do any media change detection work
3125 if (fdoExtension
->MediaChangeDetectionInfo
!= NULL
) {
3127 ClassCheckMediaState(fdoExtension
);
3132 // Do any failure prediction work
3134 if ((info
!= NULL
) && (info
->Method
!= FailurePredictionNone
)) {
3139 if (ClasspCanSendPollingIrp(fdoExtension
)) {
3142 // Synchronization is not required here since the Interlocked
3143 // locked instruction guarantees atomicity. Other code that
3144 // resets CountDown uses InterlockedExchange which is also
3147 countDown
= InterlockedDecrement(&info
->CountDown
);
3148 if (countDown
== 0) {
3150 DebugPrint((4, "ClasspTimerTick: Send FP irp for %p\n",
3153 if(info
->WorkQueueItem
== NULL
) {
3155 info
->WorkQueueItem
=
3156 IoAllocateWorkItem(fdoExtension
->DeviceObject
);
3158 if(info
->WorkQueueItem
== NULL
) {
3161 // Set the countdown to one minute in the future.
3162 // we'll try again then in the hopes there's more
3166 DebugPrint((1, "ClassTimerTick: Couldn't allocate "
3167 "item - try again in one minute\n"));
3168 InterlockedExchange(&info
->CountDown
, 60);
3173 // Grab the remove lock so that removal will block
3174 // until the work item is done.
3177 ClassAcquireRemoveLock(fdoExtension
->DeviceObject
,
3178 info
->WorkQueueItem
);
3180 IoQueueWorkItem(info
->WorkQueueItem
,
3181 ClasspFailurePredict
,
3188 DebugPrint((3, "ClasspTimerTick: Failure "
3189 "Prediction work item is "
3190 "already active for device %p\n",
3194 } // end (countdown == 0)
3198 // If device is sleeping then just rearm polling timer
3199 DebugPrint((4, "ClassTimerTick, SHHHH!!! device is %p is sleeping\n",
3203 } // end failure prediction polling
3206 // Give driver a chance to do its own specific work
3209 if (commonExtension
->DriverExtension
->InitData
.ClassTick
!= NULL
) {
3211 commonExtension
->DriverExtension
->InitData
.ClassTick(DeviceObject
);
3213 } // end device specific tick handler
3214 } // end check for removed
3216 ClassReleaseRemoveLock(DeviceObject
, (PIRP
)ClasspTimerTick
);
3217 } // end ClasspTimerTick()
3219 /*++////////////////////////////////////////////////////////////////////////////
3221 ClasspEnableTimer() - ISSUE-2000/02/20-henrygab - not documented
3223 Routine Description:
3238 PDEVICE_OBJECT DeviceObject
3245 if (DeviceObject
->Timer
== NULL
) {
3247 status
= IoInitializeTimer(DeviceObject
, ClasspTimerTick
, NULL
);
3251 status
= STATUS_SUCCESS
;
3255 if (NT_SUCCESS(status
)) {
3257 IoStartTimer(DeviceObject
);
3258 DebugPrint((1, "ClasspEnableTimer: Once a second timer enabled "
3259 "for device %p\n", DeviceObject
));
3263 DebugPrint((1, "ClasspEnableTimer: Device %p, Status %lx "
3264 "initializing timer\n", DeviceObject
, status
));
3268 } // end ClasspEnableTimer()
3270 /*++////////////////////////////////////////////////////////////////////////////
3272 ClasspDisableTimer() - ISSUE-2000/02/20-henrygab - not documented
3274 Routine Description:
3289 PDEVICE_OBJECT DeviceObject
3292 //PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
3293 //PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
3294 //PMEDIA_CHANGE_DETECTION_INFO mCDInfo = fdoExtension->MediaChangeDetectionInfo;
3295 //PFAILURE_PREDICTION_INFO fPInfo = fdoExtension->FailurePredictionInfo;
3300 if (DeviceObject
->Timer
!= NULL
) {
3303 // we are only going to stop the actual timer in remove device routine.
3304 // it is the responsibility of the code within the timer routine to
3305 // check if the device is removed and not processing io for the final
3307 // this keeps the code clean and prevents lots of bugs.
3311 IoStopTimer(DeviceObject
);
3312 DebugPrint((3, "ClasspDisableTimer: Once a second timer disabled "
3313 "for device %p\n", DeviceObject
));
3317 DebugPrint((1, "ClasspDisableTimer: Timer never enabled\n"));
3321 return STATUS_SUCCESS
;
3322 } // end ClasspDisableTimer()
3324 /*++////////////////////////////////////////////////////////////////////////////
3326 ClasspFailurePredict() - ISSUE-2000/02/20-henrygab - not documented
3328 Routine Description:
3339 Note: this function can be called (via the workitem callback) after the paging device is shut down,
3340 so it must be PAGE LOCKED.
3344 ClasspFailurePredict(
3345 IN PDEVICE_OBJECT DeviceObject
,
3346 IN PFAILURE_PREDICTION_INFO Info
3349 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= DeviceObject
->DeviceExtension
;
3350 PIO_WORKITEM workItem
;
3351 STORAGE_PREDICT_FAILURE checkFailure
;
3352 SCSI_ADDRESS scsiAddress
;
3356 ASSERT(Info
!= NULL
);
3358 DebugPrint((1, "ClasspFailurePredict: Polling for failure\n"));
3361 // Mark the work item as inactive and reset the countdown timer. we
3362 // can't risk freeing the work item until we've released the remove-lock
3363 // though - if we do it might get resused as a tag before we can release
3367 InterlockedExchange(&Info
->CountDown
, Info
->Period
);
3368 workItem
= InterlockedExchangePointer(&(Info
->WorkQueueItem
), NULL
);
3370 if (ClasspCanSendPollingIrp(fdoExtension
)) {
3373 PDEVICE_OBJECT topOfStack
;
3375 IO_STATUS_BLOCK ioStatus
;
3377 KeInitializeEvent(&event
, SynchronizationEvent
, FALSE
);
3379 topOfStack
= IoGetAttachedDeviceReference(DeviceObject
);
3382 // Send down irp to see if drive is predicting failure
3385 irp
= IoBuildDeviceIoControlRequest(
3386 IOCTL_STORAGE_PREDICT_FAILURE
,
3391 sizeof(STORAGE_PREDICT_FAILURE
),
3398 status
= IoCallDriver(topOfStack
, irp
);
3399 if (status
== STATUS_PENDING
) {
3400 KeWaitForSingleObject(&event
, Executive
, KernelMode
, FALSE
, NULL
);
3401 status
= ioStatus
.Status
;
3404 status
= STATUS_INSUFFICIENT_RESOURCES
;
3407 if (NT_SUCCESS(status
) && (checkFailure
.PredictFailure
)) {
3409 checkFailure
.PredictFailure
= 512;
3412 // Send down irp to get scsi address
3414 KeInitializeEvent(&event
, SynchronizationEvent
, FALSE
);
3416 RtlZeroMemory(&scsiAddress
, sizeof(SCSI_ADDRESS
));
3417 irp
= IoBuildDeviceIoControlRequest(
3418 IOCTL_SCSI_GET_ADDRESS
,
3423 sizeof(SCSI_ADDRESS
),
3429 status
= IoCallDriver(topOfStack
, irp
);
3430 if (status
== STATUS_PENDING
) {
3431 KeWaitForSingleObject(&event
, Executive
, KernelMode
, FALSE
, NULL
);
3432 status
= ioStatus
.Status
;
3436 ClassNotifyFailurePredicted(fdoExtension
,
3437 (PUCHAR
)&checkFailure
,
3438 sizeof(checkFailure
),
3439 (BOOLEAN
)(fdoExtension
->FailurePredicted
== FALSE
),
3442 scsiAddress
.TargetId
,
3445 fdoExtension
->FailurePredicted
= TRUE
;
3449 ObDereferenceObject(topOfStack
);
3452 ClassReleaseRemoveLock(DeviceObject
, (PIRP
) workItem
);
3453 IoFreeWorkItem(workItem
);
3455 } // end ClasspFailurePredict()
3457 /*++////////////////////////////////////////////////////////////////////////////
3459 ClassNotifyFailurePredicted() ISSUE-alanwar-2000/02/20 - not documented
3461 Routine Description:
3470 ClassNotifyFailurePredicted(
3471 PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
3475 ULONG UniqueErrorValue
,
3481 PIO_ERROR_LOG_PACKET logEntry
;
3483 DebugPrint((1, "ClasspFailurePredictPollCompletion: Failure predicted for device %p\n", FdoExtension
->DeviceObject
));
3486 // Fire off a WMI event
3488 ClassWmiFireEvent(FdoExtension
->DeviceObject
,
3489 &StoragePredictFailureEventGuid
,
3495 // Log an error into the eventlog
3500 logEntry
= IoAllocateErrorLogEntry(
3501 FdoExtension
->DeviceObject
,
3502 sizeof(IO_ERROR_LOG_PACKET
) + (3 * sizeof(ULONG
)));
3504 if (logEntry
!= NULL
)
3507 logEntry
->FinalStatus
= STATUS_SUCCESS
;
3508 logEntry
->ErrorCode
= IO_WRN_FAILURE_PREDICTED
;
3509 logEntry
->SequenceNumber
= 0;
3510 logEntry
->MajorFunctionCode
= IRP_MJ_DEVICE_CONTROL
;
3511 logEntry
->IoControlCode
= IOCTL_STORAGE_PREDICT_FAILURE
;
3512 logEntry
->RetryCount
= 0;
3513 logEntry
->UniqueErrorValue
= UniqueErrorValue
;
3514 logEntry
->DumpDataSize
= 3;
3516 logEntry
->DumpData
[0] = PathId
;
3517 logEntry
->DumpData
[1] = TargetId
;
3518 logEntry
->DumpData
[2] = Lun
;
3521 // Write the error log packet.
3524 IoWriteErrorLogEntry(logEntry
);
3527 } // end ClassNotifyFailurePredicted()
3529 /*++////////////////////////////////////////////////////////////////////////////
3531 ClassSetFailurePredictionPoll()
3533 Routine Description:
3535 This routine enables polling for failure prediction, setting the timer
3536 to fire every N seconds as specified by the PollingPeriod.
3540 FdoExtension - the device to setup failure prediction for.
3542 FailurePredictionMethod - specific failure prediction method to use
3543 if set to FailurePredictionNone, will disable failure detection
3545 PollingPeriod - if 0 then no change to current polling timer
3554 ClassSetFailurePredictionPoll(
3555 PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
3556 FAILURE_PREDICTION_METHOD FailurePredictionMethod
,
3560 PFAILURE_PREDICTION_INFO info
;
3562 //DEVICE_POWER_STATE powerState;
3566 if (FdoExtension
->FailurePredictionInfo
== NULL
) {
3568 if (FailurePredictionMethod
!= FailurePredictionNone
) {
3570 info
= ExAllocatePoolWithTag(NonPagedPool
,
3571 sizeof(FAILURE_PREDICTION_INFO
),
3572 CLASS_TAG_FAILURE_PREDICT
);
3576 return STATUS_INSUFFICIENT_RESOURCES
;
3580 KeInitializeEvent(&info
->Event
, SynchronizationEvent
, TRUE
);
3582 info
->WorkQueueItem
= NULL
;
3583 info
->Period
= DEFAULT_FAILURE_PREDICTION_PERIOD
;
3588 // FaultPrediction has not been previously initialized, nor
3589 // is it being initialized now. No need to do anything.
3591 return STATUS_SUCCESS
;
3595 FdoExtension
->FailurePredictionInfo
= info
;
3599 info
= FdoExtension
->FailurePredictionInfo
;
3603 KeWaitForSingleObject(&info
->Event
,
3611 // Reset polling period and counter. Setup failure detection type
3614 if (PollingPeriod
!= 0) {
3616 InterlockedExchange(&info
->Period
, PollingPeriod
);
3620 InterlockedExchange(&info
->CountDown
, info
->Period
);
3622 info
->Method
= FailurePredictionMethod
;
3623 if (FailurePredictionMethod
!= FailurePredictionNone
) {
3625 status
= ClasspEnableTimer(FdoExtension
->DeviceObject
);
3627 if (NT_SUCCESS(status
)) {
3628 DebugPrint((3, "ClassEnableFailurePredictPoll: Enabled for "
3629 "device %p\n", FdoExtension
->DeviceObject
));
3634 status
= ClasspDisableTimer(FdoExtension
->DeviceObject
);
3635 DebugPrint((3, "ClassEnableFailurePredictPoll: Disabled for "
3636 "device %p\n", FdoExtension
->DeviceObject
));
3637 status
= STATUS_SUCCESS
;
3641 KeSetEvent(&info
->Event
, IO_NO_INCREMENT
, FALSE
);
3644 } // end ClassSetFailurePredictionPoll()