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) )
58 ClasspIsMediaChangeDisabledDueToHardwareLimitation(
59 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
60 IN PUNICODE_STRING RegistryPath
64 ClasspMediaChangeDeviceInstanceOverride(
65 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
70 ClasspIsMediaChangeDisabledForClass(
71 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
72 IN PUNICODE_STRING RegistryPath
76 ClasspSetMediaChangeStateEx(
77 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
78 IN MEDIA_CHANGE_DETECTION_STATE NewState
,
80 IN BOOLEAN KnownStateChange
// can ignore oldstate == unknown
84 ClasspMediaChangeRegistryCallBack(
94 ClasspSendMediaStateIrp(
95 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
96 IN PMEDIA_CHANGE_DETECTION_INFO Info
,
101 ClasspFailurePredict(
102 IN PDEVICE_OBJECT DeviceObject
,
103 IN PFAILURE_PREDICTION_INFO Info
107 ClasspInitializePolling(
108 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
109 IN BOOLEAN AllowDriveToSleep
115 #pragma alloc_text(PAGE, ClassInitializeMediaChangeDetection)
116 #pragma alloc_text(PAGE, ClassEnableMediaChangeDetection)
117 #pragma alloc_text(PAGE, ClassDisableMediaChangeDetection)
118 #pragma alloc_text(PAGE, ClassCleanupMediaChangeDetection)
119 #pragma alloc_text(PAGE, ClasspMediaChangeRegistryCallBack)
120 #pragma alloc_text(PAGE, ClasspInitializePolling)
122 #pragma alloc_text(PAGE, ClasspIsMediaChangeDisabledDueToHardwareLimitation)
123 #pragma alloc_text(PAGE, ClasspMediaChangeDeviceInstanceOverride)
124 #pragma alloc_text(PAGE, ClasspIsMediaChangeDisabledForClass)
126 #pragma alloc_text(PAGE, ClassSetFailurePredictionPoll)
127 #pragma alloc_text(PAGE, ClasspDisableTimer)
128 #pragma alloc_text(PAGE, ClasspEnableTimer)
132 // ISSUE -- make this public?
134 ClassSendEjectionNotification(
135 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
139 // For post-NT5.1 work, need to move EjectSynchronizationEvent
140 // to be a MUTEX so we can attempt to grab it here and benefit
141 // from deadlock detection. This will allow checking if the media
142 // has been locked by programs before broadcasting these events.
143 // (what's the point of broadcasting if the media is not locked?)
145 // This would currently only be a slight optimization. For post-NT5.1,
146 // it would allow us to send a single PERSISTENT_PREVENT to MMC devices,
147 // thereby cleaning up a lot of the ejection code. Then, when the
148 // ejection request occured, we could see if any locks for the media
149 // existed. if locked, broadcast. if not, we send the eject irp.
153 // for now, just always broadcast. make this a public routine,
154 // so class drivers can add special hacks to broadcast this for their
155 // non-MMC-compliant devices also from sense codes.
158 DBGTRACE(ClassDebugTrace
, ("ClassSendEjectionNotification: media EJECT_REQUEST"));
159 ClasspSendNotification(FdoExtension
,
160 &GUID_IO_MEDIA_EJECT_REQUEST
,
168 ClasspSendNotification(
169 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
170 IN
const GUID
* Guid
,
171 IN ULONG ExtraDataSize
,
175 PTARGET_DEVICE_CUSTOM_NOTIFICATION notification
;
179 (sizeof(TARGET_DEVICE_CUSTOM_NOTIFICATION
) - sizeof(UCHAR
)) +
182 if (requiredSize
> 0x0000ffff) {
183 // MAX_USHORT, max total size for these events!
184 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugWarning
,
185 "Error sending event: size too large! (%x)\n",
190 notification
= ExAllocatePoolWithTag(NonPagedPool
,
195 // if none allocated, exit
198 if (notification
== NULL
) {
203 // Prepare and send the request!
206 RtlZeroMemory(notification
, requiredSize
);
207 notification
->Version
= 1;
208 notification
->Size
= (USHORT
)(requiredSize
);
209 notification
->FileObject
= NULL
;
210 notification
->NameBufferOffset
= -1;
211 notification
->Event
= *Guid
;
212 RtlCopyMemory(notification
->CustomDataBuffer
, ExtraData
, ExtraDataSize
);
214 IoReportTargetDeviceChangeAsynchronous(FdoExtension
->LowerPdo
,
218 ExFreePool(notification
);
226 /*++////////////////////////////////////////////////////////////////////////////
228 ClasspInterpretGesnData()
232 This routine will interpret the data returned for a GESN command, and
233 (if appropriate) set the media change event, and broadcast the
234 appropriate events to user mode for applications who care.
238 FdoExtension - the device
240 DataBuffer - the resulting data from a GESN event.
241 requires at least EIGHT valid bytes (header == 4, data == 4)
243 ResendImmediately - whether or not to immediately resend the request.
244 this should be FALSE if there was no event, FALSE if the reported
245 event was of the DEVICE BUSY class, else true.
253 DataBuffer must be at least four bytes of valid data (header == 4 bytes),
254 and have at least eight bytes of allocated memory (all events == 4 bytes).
256 The call to StartNextPacket may occur before this routine is completed.
257 the operational change notifications are informational in nature, and
258 while useful, are not neccessary to ensure proper operation. For example,
259 if the device morphs to no longer supporting WRITE commands, all further
260 write commands will fail. There exists a small timing window wherein
261 IOCTL_IS_DISK_WRITABLE may be called and get an incorrect response. If
262 a device supports software write protect, it is expected that the
263 application can handle such a case.
265 NOTE: perhaps setting the updaterequired byte to one should be done here.
266 if so, it relies upon the setting of a 32-byte value to be an atomic
267 operation. unfortunately, there is no simple way to notify a class driver
268 which wants to know that the device behavior requires updating.
270 Not ready events may be sent every second. For example, if we were
271 to minimize the number of asynchronous notifications, an application may
272 register just after a large busy time was reported. This would then
273 prevent the application from knowing the device was busy until some
274 arbitrarily chosen timeout has occurred. Also, the GESN request would
275 have to still occur, since it checks for non-busy events (such as user
276 keybutton presses and media change events) as well. The specification
277 states that the lower-numered events get reported first, so busy events,
278 while repeating, will only be reported when all other events have been
279 cleared from the device.
283 ClasspInterpretGesnData(
284 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
285 IN PNOTIFICATION_EVENT_STATUS_HEADER Header
,
286 IN PBOOLEAN ResendImmediately
289 PMEDIA_CHANGE_DETECTION_INFO info
;
293 info
= FdoExtension
->MediaChangeDetectionInfo
;
296 // note: don't allocate anything in this routine so that we can
297 // always just 'return'.
300 *ResendImmediately
= FALSE
;
305 if (Header
->NotificationClass
== NOTIFICATION_NO_CLASS_EVENTS
) {
310 // HACKHACK - REF #0001
311 // This loop is only taken initially, due to the inability to reliably
312 // auto-detect drives that report events correctly at boot. When we
313 // detect this behavior during the normal course of running, we will
314 // disable the hack, allowing more efficient use of the system. This
315 // should occur "nearly" instantly, as the drive should have multiple
316 // events queue'd (ie. power, morphing, media).
319 if (info
->Gesn
.HackEventMask
) {
322 // all events use the low four bytes of zero to indicate
323 // that there was no change in status.
326 UCHAR thisEvent
= Header
->ClassEventData
[0] & 0xf;
328 UCHAR thisEventBit
= (1 << Header
->NotificationClass
);
330 ASSERT(TEST_FLAG(info
->Gesn
.EventMask
, thisEventBit
));
334 // some bit magic here... this results in the lowest set bit only
337 lowestSetBit
= info
->Gesn
.EventMask
;
338 lowestSetBit
&= (info
->Gesn
.EventMask
- 1);
339 lowestSetBit
^= (info
->Gesn
.EventMask
);
341 if (thisEventBit
!= lowestSetBit
) {
344 // HACKHACK - REF #0001
345 // the first time we ever see an event set that is not the lowest
346 // set bit in the request (iow, highest priority), we know that the
347 // hack is no longer required, as the device is ignoring "no change"
348 // events when a real event is waiting in the other requested queues.
351 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
352 "Classpnp => GESN::NONE: Compliant drive found, "
353 "removing GESN hack (%x, %x)\n",
354 thisEventBit
, info
->Gesn
.EventMask
));
356 info
->Gesn
.HackEventMask
= FALSE
;
358 } else if (thisEvent
== 0) {
361 // HACKHACK - REF #0001
362 // note: this hack prevents poorly implemented firmware from constantly
363 // returning "No Event". we do this by cycling through the
364 // supported list of events here.
367 SET_FLAG(info
->Gesn
.NoChangeEventMask
, thisEventBit
);
368 CLEAR_FLAG(info
->Gesn
.EventMask
, thisEventBit
);
371 // if we have cycled through all supported event types, then
372 // we need to reset the events we are asking about. else we
373 // want to resend this request immediately in case there was
374 // another event pending.
377 if (info
->Gesn
.EventMask
== 0) {
378 info
->Gesn
.EventMask
= info
->Gesn
.NoChangeEventMask
;
379 info
->Gesn
.NoChangeEventMask
= 0;
381 *ResendImmediately
= TRUE
;
386 } // end if (info->Gesn.HackEventMask)
389 (Header
->EventDataLength
[0] << 8) |
390 (Header
->EventDataLength
[1] & 0xff);
392 requiredLength
= 4; // all events are four bytes
394 if (dataLength
< requiredLength
) {
395 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugWarning
,
396 "Classpnp => GESN returned only %x bytes data for fdo %p\n",
397 dataLength
, FdoExtension
->DeviceObject
));
400 if (dataLength
!= requiredLength
) {
401 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugWarning
,
402 "Classpnp => GESN returned too many (%x) bytes data for fdo %p\n",
403 dataLength
, FdoExtension
->DeviceObject
));
408 ClasspSendNotification(FdoExtension,
409 &GUID_IO_GENERIC_GESN_EVENT,
410 sizeof(NOTIFICATION_EVENT_STATUS_HEADER) + dataLength,
414 switch (Header
->NotificationClass
) {
416 case NOTIFICATION_EXTERNAL_REQUEST_CLASS_EVENTS
: { // 0x3
418 PNOTIFICATION_EXTERNAL_STATUS externalInfo
=
419 (PNOTIFICATION_EXTERNAL_STATUS
)(Header
->ClassEventData
);
420 DEVICE_EVENT_EXTERNAL_REQUEST externalData
;
423 // unfortunately, due to time constraints, we will only notify
424 // about keys being pressed, and not released. this makes keys
425 // single-function, but simplifies the code significantly.
428 if (externalInfo
->ExternalEvent
!=
429 NOTIFICATION_EXTERNAL_EVENT_BUTTON_DOWN
) {
433 *ResendImmediately
= TRUE
;
434 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
435 "Classpnp => GESN::EXTERNAL: Event: %x Status %x Req %x\n",
436 externalInfo
->ExternalEvent
, externalInfo
->ExternalStatus
,
437 (externalInfo
->Request
[0] >> 8) | externalInfo
->Request
[1]
440 RtlZeroMemory(&externalData
, sizeof(DEVICE_EVENT_EXTERNAL_REQUEST
));
441 externalData
.Version
= 1;
442 externalData
.DeviceClass
= 0;
443 externalData
.ButtonStatus
= externalInfo
->ExternalEvent
;
444 externalData
.Request
=
445 (externalInfo
->Request
[0] << 8) |
446 (externalInfo
->Request
[1] & 0xff);
447 KeQuerySystemTime(&(externalData
.SystemTime
));
448 externalData
.SystemTime
.QuadPart
*= (LONGLONG
)KeQueryTimeIncrement();
450 DBGTRACE(ClassDebugTrace
, ("ClasspInterpretGesnData: media DEVICE_EXTERNAL_REQUEST"));
451 ClasspSendNotification(FdoExtension
,
452 &GUID_IO_DEVICE_EXTERNAL_REQUEST
,
453 sizeof(DEVICE_EVENT_EXTERNAL_REQUEST
),
458 case NOTIFICATION_MEDIA_STATUS_CLASS_EVENTS
: { // 0x4
460 PNOTIFICATION_MEDIA_STATUS mediaInfo
=
461 (PNOTIFICATION_MEDIA_STATUS
)(Header
->ClassEventData
);
463 if (mediaInfo
->MediaEvent
== NOTIFICATION_MEDIA_EVENT_NO_CHANGE
) {
467 *ResendImmediately
= TRUE
;
468 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
469 "Classpnp => GESN::MEDIA: Event: %x Status %x\n",
470 mediaInfo
->MediaEvent
, mediaInfo
->MediaStatus
));
472 if ((mediaInfo
->MediaEvent
== NOTIFICATION_MEDIA_EVENT_NEW_MEDIA
) ||
473 (mediaInfo
->MediaEvent
== NOTIFICATION_MEDIA_EVENT_MEDIA_CHANGE
)) {
476 if (TEST_FLAG(FdoExtension
->DeviceObject
->Characteristics
,
477 FILE_REMOVABLE_MEDIA
) &&
478 (ClassGetVpb(FdoExtension
->DeviceObject
) != NULL
) &&
479 (ClassGetVpb(FdoExtension
->DeviceObject
)->Flags
& VPB_MOUNTED
)
482 SET_FLAG(FdoExtension
->DeviceObject
->Flags
, DO_VERIFY_VOLUME
);
485 InterlockedIncrement(&FdoExtension
->MediaChangeCount
);
486 ClasspSetMediaChangeStateEx(FdoExtension
,
491 } else if (mediaInfo
->MediaEvent
== NOTIFICATION_MEDIA_EVENT_MEDIA_REMOVAL
) {
493 ClasspSetMediaChangeStateEx(FdoExtension
,
498 } else if (mediaInfo
->MediaEvent
== NOTIFICATION_MEDIA_EVENT_EJECT_REQUEST
) {
500 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugError
,
501 "Classpnp => GESN Ejection request received!\n"));
502 ClassSendEjectionNotification(FdoExtension
);
509 case NOTIFICATION_DEVICE_BUSY_CLASS_EVENTS
: { // lowest priority events...
511 PNOTIFICATION_BUSY_STATUS busyInfo
=
512 (PNOTIFICATION_BUSY_STATUS
)(Header
->ClassEventData
);
513 DEVICE_EVENT_BECOMING_READY busyData
;
516 // NOTE: we never actually need to immediately retry for these
517 // events: if one exists, the device is busy, and if not,
518 // we still don't want to retry.
521 if (busyInfo
->DeviceBusyStatus
== NOTIFICATION_BUSY_STATUS_NO_EVENT
) {
526 // else we want to report the approximated time till it's ready.
529 RtlZeroMemory(&busyData
, sizeof(DEVICE_EVENT_BECOMING_READY
));
530 busyData
.Version
= 1;
531 busyData
.Reason
= busyInfo
->DeviceBusyStatus
;
532 busyData
.Estimated100msToReady
= (busyInfo
->Time
[0] << 8) |
533 (busyInfo
->Time
[1] & 0xff);
535 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
536 "Classpnp => GESN::BUSY: Event: %x Status %x Time %x\n",
537 busyInfo
->DeviceBusyEvent
, busyInfo
->DeviceBusyStatus
,
538 busyData
.Estimated100msToReady
541 DBGTRACE(ClassDebugTrace
, ("ClasspInterpretGesnData: media BECOMING_READY"));
542 ClasspSendNotification(FdoExtension
,
543 &GUID_IO_DEVICE_BECOMING_READY
,
544 sizeof(DEVICE_EVENT_BECOMING_READY
),
555 } // end switch on notification class
559 /*++////////////////////////////////////////////////////////////////////////////
561 ClasspInternalSetMediaChangeState()
565 This routine will (if appropriate) set the media change event for the
566 device. The event will be set if the media state is changed and
567 media change events are enabled. Otherwise the media state will be
568 tracked but the event will not be set.
570 This routine will lock out the other media change routines if possible
571 but if not a media change notification may be lost after the enable has
576 FdoExtension - the device
578 MediaPresent - indicates whether the device has media inserted into it
579 (TRUE) or not (FALSE).
587 ClasspInternalSetMediaChangeState(
588 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
589 IN MEDIA_CHANGE_DETECTION_STATE NewState
,
590 IN BOOLEAN KnownStateChange
// can ignore oldstate == unknown
594 PUCHAR states
[] = {"Unknown", "Present", "Not Present"};
596 MEDIA_CHANGE_DETECTION_STATE oldMediaState
;
597 PMEDIA_CHANGE_DETECTION_INFO info
= FdoExtension
->MediaChangeDetectionInfo
;
601 ASSERT((NewState
>= MediaUnknown
) && (NewState
<= MediaNotPresent
));
607 oldMediaState
= InterlockedExchange(
608 (PLONG
)(&info
->MediaChangeDetectionState
),
611 if((oldMediaState
== MediaUnknown
) && (!KnownStateChange
)) {
614 // The media was in an indeterminate state before - don't notify for
618 DebugPrint((ClassDebugMCN
,
619 "ClassSetMediaChangeState: State was unknown - this may "
620 "not be a change\n"));
623 } else if(oldMediaState
== NewState
) {
626 // Media is in the same state it was before.
632 if(info
->MediaChangeDetectionDisableCount
!= 0) {
634 DBGTRACE(ClassDebugMCN
,
635 ("ClassSetMediaChangeState: MCN not enabled, state "
636 "changed from %s to %s\n",
637 states
[oldMediaState
], states
[NewState
]));
642 DBGTRACE(ClassDebugMCN
,
643 ("ClassSetMediaChangeState: State change from %s to %s\n",
644 states
[oldMediaState
], states
[NewState
]));
647 // make the data useful -- it used to always be zero.
649 data
= FdoExtension
->MediaChangeCount
;
651 if (NewState
== MediaPresent
) {
653 DBGTRACE(ClassDebugTrace
, ("ClasspInternalSetMediaChangeState: media ARRIVAL"));
654 ClasspSendNotification(FdoExtension
,
655 &GUID_IO_MEDIA_ARRIVAL
,
660 else if (NewState
== MediaNotPresent
) {
662 DBGTRACE(ClassDebugTrace
, ("ClasspInternalSetMediaChangeState: media REMOVAL"));
663 ClasspSendNotification(FdoExtension
,
664 &GUID_IO_MEDIA_REMOVAL
,
671 // Don't notify of changed going to unknown.
678 } // end ClasspInternalSetMediaChangeState()
680 /*++////////////////////////////////////////////////////////////////////////////
682 ClassSetMediaChangeState()
686 This routine will (if appropriate) set the media change event for the
687 device. The event will be set if the media state is changed and
688 media change events are enabled. Otherwise the media state will be
689 tracked but the event will not be set.
691 This routine will lock out the other media change routines if possible
692 but if not a media change notification may be lost after the enable has
697 FdoExtension - the device
699 MediaPresent - indicates whether the device has media inserted into it
700 (TRUE) or not (FALSE).
702 Wait - indicates whether the function should wait until it can acquire
703 the synchronization lock or not.
711 ClasspSetMediaChangeStateEx(
712 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
713 IN MEDIA_CHANGE_DETECTION_STATE NewState
,
715 IN BOOLEAN KnownStateChange
// can ignore oldstate == unknown
718 PMEDIA_CHANGE_DETECTION_INFO info
= FdoExtension
->MediaChangeDetectionInfo
;
722 DBGTRACE(ClassDebugMCN
, ("> ClasspSetMediaChangeStateEx"));
725 // Reset SMART status on media removal as the old status may not be
726 // valid when there is no media in the device or when new media is
730 if (NewState
== MediaNotPresent
) {
732 FdoExtension
->FailurePredicted
= FALSE
;
733 FdoExtension
->FailureReason
= 0;
744 status
= KeWaitForMutexObject(&info
->MediaChangeMutex
,
748 ((Wait
== TRUE
) ? NULL
: &zero
));
750 if(status
== STATUS_TIMEOUT
) {
753 // Someone else is in the process of setting the media state
756 DBGWARN(("ClasspSetMediaChangeStateEx - timed out waiting for mutex"));
761 // Change the media present state and signal an event, if applicable
764 ClasspInternalSetMediaChangeState(FdoExtension
, NewState
, KnownStateChange
);
766 KeReleaseMutex(&info
->MediaChangeMutex
, FALSE
);
768 DBGTRACE(ClassDebugMCN
, ("< ClasspSetMediaChangeStateEx"));
771 } // end ClassSetMediaChangeStateEx()
773 ClassSetMediaChangeState(
774 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
775 IN MEDIA_CHANGE_DETECTION_STATE NewState
,
779 ClasspSetMediaChangeStateEx(FdoExtension
, NewState
, Wait
, FALSE
);
783 /*++////////////////////////////////////////////////////////////////////////////
785 ClasspMediaChangeDetectionCompletion()
789 This routine handles the completion of the test unit ready irps used to
790 determine if the media has changed. If the media has changed, this code
791 signals the named event to wake up other system services that react to
792 media change (aka AutoPlay).
796 DeviceObject - the object for the completion
797 Irp - the IRP being completed
798 Context - the SRB from the IRP
806 ClasspMediaChangeDetectionCompletion(
807 PDEVICE_OBJECT DeviceObject
,
809 PSCSI_REQUEST_BLOCK Srb
812 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
;
813 PCLASS_PRIVATE_FDO_DATA fdoData
;
814 PMEDIA_CHANGE_DETECTION_INFO info
;
815 PIO_STACK_LOCATION nextIrpStack
;
817 BOOLEAN retryImmediately
= FALSE
;
820 // Since the class driver created this request, it's completion routine
821 // will not get a valid device object handed in. Use the one in the
825 DeviceObject
= IoGetCurrentIrpStackLocation(Irp
)->DeviceObject
;
826 fdoExtension
= DeviceObject
->DeviceExtension
;
827 fdoData
= fdoExtension
->PrivateFdoData
;
828 info
= fdoExtension
->MediaChangeDetectionInfo
;
830 ASSERT(info
->MediaChangeIrp
!= NULL
);
831 ASSERT(!TEST_FLAG(Srb
->SrbStatus
, SRB_STATUS_QUEUE_FROZEN
));
832 DBGTRACE(ClassDebugMCN
, ("> ClasspMediaChangeDetectionCompletion: Device %p completed MCN irp %p.", DeviceObject
, Irp
));
835 * HACK for IoMega 2GB Jaz drive:
836 * This drive spins down on its own to preserve the media.
837 * When spun down, TUR fails with 2/4/0 (SCSI_SENSE_NOT_READY/SCSI_ADSENSE_LUN_NOT_READY/?).
838 * ClassInterpretSenseInfo would then call ClassSendStartUnit to spin the media up, which defeats the
839 * purpose of the spindown.
840 * So in this case, make this into a successful TUR.
841 * This allows the drive to stay spun down until it is actually accessed again.
842 * (If the media were actually removed, TUR would fail with 2/3a/0 ).
843 * This hack only applies to drives with the CAUSE_NOT_REPORTABLE_HACK bit set; this
844 * is set by disk.sys when HackCauseNotReportableHack is set for the drive in its BadControllers list.
846 if ((SRB_STATUS(Srb
->SrbStatus
) != SRB_STATUS_SUCCESS
) &&
847 TEST_FLAG(fdoExtension
->ScanForSpecialFlags
, CLASS_SPECIAL_CAUSE_NOT_REPORTABLE_HACK
) &&
848 (Srb
->SenseInfoBufferLength
>= RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA
, AdditionalSenseCode
))){
850 PSENSE_DATA senseData
= Srb
->SenseInfoBuffer
;
852 if ((senseData
->SenseKey
== SCSI_SENSE_NOT_READY
) &&
853 (senseData
->AdditionalSenseCode
== SCSI_ADSENSE_LUN_NOT_READY
)){
854 Srb
->SrbStatus
= SRB_STATUS_SUCCESS
;
860 // use ClassInterpretSenseInfo() to check for media state, and also
861 // to call ClassError() with correct parameters.
863 status
= STATUS_SUCCESS
;
864 if (SRB_STATUS(Srb
->SrbStatus
) != SRB_STATUS_SUCCESS
) {
866 DBGTRACE(ClassDebugMCN
, ("ClasspMediaChangeDetectionCompletion - failed - srb status=%s, sense=%s/%s/%s.", DBGGETSRBSTATUSSTR(Srb
), DBGGETSENSECODESTR(Srb
), DBGGETADSENSECODESTR(Srb
), DBGGETADSENSEQUALIFIERSTR(Srb
)));
868 ClassInterpretSenseInfo(DeviceObject
,
879 fdoData
->LoggedTURFailureSinceLastIO
= FALSE
;
881 if (!info
->Gesn
.Supported
) {
883 DBGTRACE(ClassDebugMCN
, ("ClasspMediaChangeDetectionCompletion - succeeded and GESN NOT supported, setting MediaPresent."));
886 // success != media for GESN case
889 ClassSetMediaChangeState(fdoExtension
, MediaPresent
, FALSE
);
893 DBGTRACE(ClassDebugMCN
, ("ClasspMediaChangeDetectionCompletion - succeeded (GESN supported)."));
897 if (info
->Gesn
.Supported
) {
899 if (status
== STATUS_DATA_OVERRUN
) {
900 DBGTRACE(ClassDebugMCN
, ("ClasspMediaChangeDetectionCompletion - Overrun"));
901 status
= STATUS_SUCCESS
;
904 if (!NT_SUCCESS(status
)) {
905 DBGTRACE(ClassDebugMCN
, ("ClasspMediaChangeDetectionCompletion: GESN failed with status %x", status
));
909 // for GESN, need to interpret the results of the data.
910 // this may also require an immediate retry
913 if (Irp
->IoStatus
.Information
== 8 ) {
914 ClasspInterpretGesnData(fdoExtension
,
915 (PVOID
)info
->Gesn
.Buffer
,
919 } // end of NT_SUCCESS(status)
921 } // end of Info->Gesn.Supported
924 // free port-allocated sense buffer, if any.
927 if (PORT_ALLOCATED_SENSE(fdoExtension
, Srb
)) {
928 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension
, Srb
);
932 // Remember the IRP and SRB for use the next time.
935 ASSERT(IoGetNextIrpStackLocation(Irp
));
936 IoGetNextIrpStackLocation(Irp
)->Parameters
.Scsi
.Srb
= Srb
;
939 // Reset the MCN timer.
942 ClassResetMediaChangeTimer(fdoExtension
);
945 // run a sanity check to make sure we're not recursing continuously
948 if (retryImmediately
) {
950 info
->MediaChangeRetryCount
++;
951 if (info
->MediaChangeRetryCount
> MAXIMUM_IMMEDIATE_MCN_RETRIES
) {
952 ASSERT(!"Recursing too often in MCN?");
953 info
->MediaChangeRetryCount
= 0;
954 retryImmediately
= FALSE
;
959 info
->MediaChangeRetryCount
= 0;
965 // release the remove lock....
970 ClassAcquireRemoveLock(DeviceObject
, (PIRP
)(&uniqueValue
));
971 ClassReleaseRemoveLock(DeviceObject
, Irp
);
975 // set the irp as not in use
978 volatile LONG irpWasInUse
;
979 irpWasInUse
= InterlockedCompareExchange(&info
->MediaChangeIrpInUse
, 0, 1);
980 #if _MSC_FULL_VER != 13009111 // This compiler always takes the wrong path here.
986 // now send it again before we release our last remove lock
989 if (retryImmediately
) {
990 ClasspSendMediaStateIrp(fdoExtension
, info
, 0);
993 DBGTRACE(ClassDebugMCN
, ("ClasspMediaChangeDetectionCompletion - not retrying immediately"));
997 // release the temporary remove lock
1000 ClassReleaseRemoveLock(DeviceObject
, (PIRP
)(&uniqueValue
));
1003 DBGTRACE(ClassDebugMCN
, ("< ClasspMediaChangeDetectionCompletion"));
1005 return STATUS_MORE_PROCESSING_REQUIRED
;
1008 /*++////////////////////////////////////////////////////////////////////////////
1010 ClasspSendTestUnitIrp() - ISSUE-2000/02/20-henrygab - not documented
1012 Routine Description:
1026 ClasspPrepareMcnIrp(
1027 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
1028 IN PMEDIA_CHANGE_DETECTION_INFO Info
,
1032 PSCSI_REQUEST_BLOCK srb
;
1033 PIO_STACK_LOCATION irpStack
;
1034 PIO_STACK_LOCATION nextIrpStack
;
1041 // Setup the IRP to perform a test unit ready.
1044 irp
= Info
->MediaChangeIrp
;
1053 // don't keep sending this if the device is being removed.
1056 status
= ClassAcquireRemoveLock(FdoExtension
->DeviceObject
, irp
);
1057 if (status
== REMOVE_COMPLETE
) {
1058 ASSERT(status
!= REMOVE_COMPLETE
);
1061 else if (status
== REMOVE_PENDING
) {
1062 ClassReleaseRemoveLock(FdoExtension
->DeviceObject
, irp
);
1066 ASSERT(status
== NO_REMOVE
);
1069 irp
->IoStatus
.Status
= STATUS_SUCCESS
;
1070 irp
->IoStatus
.Information
= 0;
1072 irp
->UserBuffer
= NULL
;
1075 // If the irp is sent down when the volume needs to be
1076 // verified, CdRomUpdateGeometryCompletion won't complete
1077 // it since it's not associated with a thread. Marking
1078 // it to override the verify causes it always be sent
1079 // to the port driver
1082 irpStack
= IoGetCurrentIrpStackLocation(irp
);
1083 irpStack
->Flags
|= SL_OVERRIDE_VERIFY_VOLUME
;
1085 nextIrpStack
= IoGetNextIrpStackLocation(irp
);
1086 nextIrpStack
->MajorFunction
= IRP_MJ_INTERNAL_DEVICE_CONTROL
;
1087 nextIrpStack
->Parameters
.Scsi
.Srb
= &(Info
->MediaChangeSrb
);
1090 // Prepare the SRB for execution.
1093 srb
= nextIrpStack
->Parameters
.Scsi
.Srb
;
1094 buffer
= srb
->SenseInfoBuffer
;
1095 RtlZeroMemory(srb
, sizeof(SCSI_REQUEST_BLOCK
));
1096 RtlZeroMemory(buffer
, SENSE_BUFFER_SIZE
);
1099 srb
->QueueTag
= SP_UNTAGGED
;
1100 srb
->QueueAction
= SRB_SIMPLE_TAG_REQUEST
;
1101 srb
->Length
= sizeof(SCSI_REQUEST_BLOCK
);
1102 srb
->Function
= SRB_FUNCTION_EXECUTE_SCSI
;
1103 srb
->SenseInfoBuffer
= buffer
;
1105 srb
->ScsiStatus
= 0;
1106 srb
->OriginalRequest
= irp
;
1107 srb
->SenseInfoBufferLength
= SENSE_BUFFER_SIZE
;
1109 srb
->SrbFlags
= FdoExtension
->SrbFlags
;
1110 SET_FLAG(srb
->SrbFlags
, Info
->SrbFlags
);
1112 srb
->TimeOutValue
= FdoExtension
->TimeOutValue
* 2;
1114 if (srb
->TimeOutValue
== 0) {
1116 if (FdoExtension
->TimeOutValue
== 0) {
1118 KdPrintEx((DPFLTR_CLASSPNP_ID
, DPFLTR_ERROR_LEVEL
,
1119 "ClassSendTestUnitIrp: FdoExtension->TimeOutValue "
1120 "is set to zero?! -- resetting to 10\n"));
1121 srb
->TimeOutValue
= 10 * 2; // reasonable default
1125 KdPrintEx((DPFLTR_CLASSPNP_ID
, DPFLTR_ERROR_LEVEL
,
1126 "ClassSendTestUnitIrp: Someone set "
1127 "srb->TimeOutValue to zero?! -- resetting to %x\n",
1128 FdoExtension
->TimeOutValue
* 2));
1129 srb
->TimeOutValue
= FdoExtension
->TimeOutValue
* 2;
1138 srb
->DataTransferLength
= 0;
1139 SET_FLAG(srb
->SrbFlags
, SRB_FLAGS_NO_DATA_TRANSFER
);
1140 nextIrpStack
->Parameters
.DeviceIoControl
.IoControlCode
=
1141 IOCTL_SCSI_EXECUTE_NONE
;
1142 srb
->DataBuffer
= NULL
;
1143 srb
->DataTransferLength
= 0;
1144 irp
->MdlAddress
= NULL
;
1146 cdb
= (PCDB
) &srb
->Cdb
[0];
1147 cdb
->CDB6GENERIC
.OperationCode
= SCSIOP_TEST_UNIT_READY
;
1151 ASSERT(Info
->Gesn
.Buffer
);
1153 srb
->TimeOutValue
= GESN_TIMEOUT_VALUE
; // much shorter timeout for GESN
1155 srb
->CdbLength
= 10;
1156 SET_FLAG(srb
->SrbFlags
, SRB_FLAGS_DATA_IN
);
1157 nextIrpStack
->Parameters
.DeviceIoControl
.IoControlCode
=
1158 IOCTL_SCSI_EXECUTE_IN
;
1159 srb
->DataBuffer
= Info
->Gesn
.Buffer
;
1160 srb
->DataTransferLength
= Info
->Gesn
.BufferSize
;
1161 irp
->MdlAddress
= Info
->Gesn
.Mdl
;
1163 cdb
= (PCDB
) &srb
->Cdb
[0];
1164 cdb
->GET_EVENT_STATUS_NOTIFICATION
.OperationCode
=
1165 SCSIOP_GET_EVENT_STATUS
;
1166 cdb
->GET_EVENT_STATUS_NOTIFICATION
.Immediate
= 1;
1167 cdb
->GET_EVENT_STATUS_NOTIFICATION
.EventListLength
[0] =
1168 (UCHAR
)((Info
->Gesn
.BufferSize
) >> 8);
1169 cdb
->GET_EVENT_STATUS_NOTIFICATION
.EventListLength
[1] =
1170 (UCHAR
)((Info
->Gesn
.BufferSize
) & 0xff);
1171 cdb
->GET_EVENT_STATUS_NOTIFICATION
.NotificationClassRequest
=
1172 Info
->Gesn
.EventMask
;
1176 IoSetCompletionRoutine(irp
,
1177 ClasspMediaChangeDetectionCompletion
,
1187 /*++////////////////////////////////////////////////////////////////////////////
1189 ClasspSendMediaStateIrp() - ISSUE-2000/02/20-henrygab - not documented
1191 Routine Description:
1204 ClasspSendMediaStateIrp(
1205 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
1206 IN PMEDIA_CHANGE_DETECTION_INFO Info
,
1210 BOOLEAN requestPending
= FALSE
;
1215 DBGTRACE(ClassDebugMCN
, ("> ClasspSendMediaStateIrp"));
1217 if (((FdoExtension
->CommonExtension
.CurrentState
!= IRP_MN_START_DEVICE
) ||
1218 (FdoExtension
->DevicePowerState
!= PowerDeviceD0
)
1220 (!Info
->MediaChangeIrpLost
)) {
1223 // the device may be stopped, powered down, or otherwise queueing io,
1224 // so should not timeout the autorun irp (yet) -- set to zero ticks.
1225 // scattered code relies upon this to not prematurely "lose" an
1226 // autoplay irp that was queued.
1229 Info
->MediaChangeIrpTimeInUse
= 0;
1233 // if the irp is not in use, mark it as such.
1236 irpInUse
= InterlockedCompareExchange(&Info
->MediaChangeIrpInUse
, 1, 0);
1242 timeInUse
= InterlockedIncrement(&Info
->MediaChangeIrpTimeInUse
);
1244 DebugPrint((ClassDebugMCN
, "ClasspSendMediaStateIrp: irp in use for "
1245 "%x seconds when synchronizing for MCD\n", timeInUse
));
1247 if (Info
->MediaChangeIrpLost
== FALSE
) {
1249 if (timeInUse
> MEDIA_CHANGE_TIMEOUT_TIME
) {
1252 // currently set to five minutes. hard to imagine a drive
1253 // taking that long to spin up.
1256 DebugPrint((ClassDebugError
,
1257 "CdRom%d: Media Change Notification has lost "
1258 "it's irp and doesn't know where to find it. "
1259 "Leave it alone and it'll come home dragging "
1260 "it's stack behind it.\n",
1261 FdoExtension
->DeviceNumber
));
1262 Info
->MediaChangeIrpLost
= TRUE
;
1266 DBGTRACE(ClassDebugMCN
, ("< ClasspSendMediaStateIrp - irpInUse"));
1273 if (Info
->MediaChangeDetectionDisableCount
!= 0) {
1274 DebugPrint((ClassDebugTrace
, "ClassCheckMediaState: device %p has "
1275 " detection disabled \n", FdoExtension
->DeviceObject
));
1279 if (FdoExtension
->DevicePowerState
!= PowerDeviceD0
) {
1281 if (TEST_FLAG(Info
->SrbFlags
, SRB_FLAGS_NO_KEEP_AWAKE
)) {
1282 DebugPrint((ClassDebugMCN
,
1283 "ClassCheckMediaState: device %p is powered "
1284 "down and flags are set to let it sleep\n",
1285 FdoExtension
->DeviceObject
));
1286 ClassResetMediaChangeTimer(FdoExtension
);
1291 // NOTE: we don't increment the time in use until our power state
1292 // changes above. this way, we won't "lose" the autoplay irp.
1293 // it's up to the lower driver to determine if powering up is a
1297 DebugPrint((ClassDebugMCN
,
1298 "ClassCheckMediaState: device %p needs to powerup "
1299 "to handle this io (may take a few extra seconds).\n",
1300 FdoExtension
->DeviceObject
));
1304 Info
->MediaChangeIrpTimeInUse
= 0;
1305 Info
->MediaChangeIrpLost
= FALSE
;
1307 if (CountDown
== 0) {
1311 DebugPrint((ClassDebugTrace
,
1312 "ClassCheckMediaState: timer expired\n"));
1314 if (Info
->MediaChangeDetectionDisableCount
!= 0) {
1315 DebugPrint((ClassDebugTrace
,
1316 "ClassCheckMediaState: detection disabled\n"));
1321 // Prepare the IRP for the test unit ready
1324 irp
= ClasspPrepareMcnIrp(FdoExtension
,
1326 Info
->Gesn
.Supported
);
1329 // Issue the request.
1332 DebugPrint((ClassDebugTrace
,
1333 "ClasspSendMediaStateIrp: Device %p getting TUR "
1334 " irp %p\n", FdoExtension
->DeviceObject
, irp
));
1342 // note: if we send it to the class dispatch routines, there is
1343 // a timing window here (since they grab the remove lock)
1344 // where we'd be removed. ELIMINATE the window by grabbing
1345 // the lock ourselves above and sending it to the lower
1346 // device object directly or to the device's StartIo
1347 // routine (which doesn't acquire the lock).
1350 requestPending
= TRUE
;
1352 DBGTRACE(ClassDebugMCN
, (" ClasspSendMediaStateIrp - calling IoCallDriver."));
1353 IoCallDriver(FdoExtension
->CommonExtension
.LowerDeviceObject
, irp
);
1358 if(requestPending
== FALSE
) {
1359 irpInUse
= InterlockedCompareExchange(&Info
->MediaChangeIrpInUse
, 0, 1);
1360 #if _MSC_FULL_VER != 13009111 // This compiler always takes the wrong path here.
1367 DBGTRACE(ClassDebugMCN
, ("< ClasspSendMediaStateIrp"));
1370 } // end ClasspSendMediaStateIrp()
1372 /*++////////////////////////////////////////////////////////////////////////////
1374 ClassCheckMediaState()
1376 Routine Description:
1378 This routine is called by the class driver to test for a media change
1379 condition and/or poll for disk failure prediction. It should be called
1380 from the class driver's IO timer routine once per second.
1384 FdoExtension - the device extension
1392 ClassCheckMediaState(
1393 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
1396 PMEDIA_CHANGE_DETECTION_INFO info
= FdoExtension
->MediaChangeDetectionInfo
;
1400 DebugPrint((ClassDebugTrace
,
1401 "ClassCheckMediaState: detection not enabled\n"));
1406 // Media change support is active and the IRP is waiting. Decrement the
1407 // timer. There is no MP protection on the timer counter. This code
1408 // is the only code that will manipulate the timer counter and only one
1409 // instance of it should be running at any given time.
1412 countDown
= InterlockedDecrement(&(info
->MediaChangeCountDown
));
1415 // Try to acquire the media change event. If we can't do it immediately
1416 // then bail out and assume the caller will try again later.
1418 ClasspSendMediaStateIrp(FdoExtension
,
1423 } // end ClassCheckMediaState()
1425 /*++////////////////////////////////////////////////////////////////////////////
1427 ClassResetMediaChangeTimer()
1429 Routine Description:
1431 Resets the media change count down timer to the default number of seconds.
1435 FdoExtension - the device to reset the timer for
1443 ClassResetMediaChangeTimer(
1444 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
1447 PMEDIA_CHANGE_DETECTION_INFO info
= FdoExtension
->MediaChangeDetectionInfo
;
1450 InterlockedExchange(&(info
->MediaChangeCountDown
),
1451 MEDIA_CHANGE_DEFAULT_TIME
);
1454 } // end ClassResetMediaChangeTimer()
1456 /*++////////////////////////////////////////////////////////////////////////////
1458 ClasspInitializePolling() - ISSUE-2000/02/20-henrygab - not documented
1460 Routine Description:
1473 ClasspInitializePolling(
1474 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
1475 IN BOOLEAN AllowDriveToSleep
1478 PDEVICE_OBJECT fdo
= FdoExtension
->DeviceObject
;
1479 PCLASS_PRIVATE_FDO_DATA fdoData
= FdoExtension
->PrivateFdoData
;
1482 PMEDIA_CHANGE_DETECTION_INFO info
;
1487 if (FdoExtension
->MediaChangeDetectionInfo
!= NULL
) {
1488 return STATUS_SUCCESS
;
1491 info
= ExAllocatePoolWithTag(NonPagedPool
,
1492 sizeof(MEDIA_CHANGE_DETECTION_INFO
),
1493 CLASS_TAG_MEDIA_CHANGE_DETECTION
);
1496 RtlZeroMemory(info
, sizeof(MEDIA_CHANGE_DETECTION_INFO
));
1498 FdoExtension
->KernelModeMcnContext
.FileObject
= (PVOID
)-1;
1499 FdoExtension
->KernelModeMcnContext
.DeviceObject
= (PVOID
)-1;
1500 FdoExtension
->KernelModeMcnContext
.LockCount
= 0;
1501 FdoExtension
->KernelModeMcnContext
.McnDisableCount
= 0;
1504 * Allocate an IRP to carry the Test-Unit-Ready.
1505 * Allocate an extra IRP stack location
1506 * so we can cache our device object in the top location.
1508 irp
= IoAllocateIrp((CCHAR
)(fdo
->StackSize
+1), FALSE
);
1514 buffer
= ExAllocatePoolWithTag(
1515 NonPagedPoolCacheAligned
,
1517 CLASS_TAG_MEDIA_CHANGE_DETECTION
);
1519 if (buffer
!= NULL
) {
1520 PIO_STACK_LOCATION irpStack
;
1521 PSCSI_REQUEST_BLOCK srb
;
1524 srb
= &(info
->MediaChangeSrb
);
1525 info
->MediaChangeIrp
= irp
;
1526 info
->SenseBuffer
= buffer
;
1529 * For the driver that creates an IRP, there is no 'current' stack location.
1530 * Step down one IRP stack location so that the extra top one
1531 * becomes our 'current' one.
1533 IoSetNextIrpStackLocation(irp
);
1536 * Cache our device object in the extra top IRP stack location
1537 * so we have it in our completion routine.
1539 irpStack
= IoGetCurrentIrpStackLocation(irp
);
1540 irpStack
->DeviceObject
= fdo
;
1543 * Now start setting up the next IRP stack location for the call like any driver would.
1545 irpStack
= IoGetNextIrpStackLocation(irp
);
1546 irpStack
->Parameters
.Scsi
.Srb
= srb
;
1547 info
->MediaChangeIrp
= irp
;
1550 // Initialize the SRB
1553 RtlZeroMemory(srb
, sizeof(SCSI_REQUEST_BLOCK
));
1556 // Initialize and set up the sense information buffer
1559 RtlZeroMemory(buffer
, SENSE_BUFFER_SIZE
);
1560 srb
->SenseInfoBuffer
= buffer
;
1561 srb
->SenseInfoBufferLength
= SENSE_BUFFER_SIZE
;
1564 // Set default values for the media change notification
1568 info
->MediaChangeCountDown
= MEDIA_CHANGE_DEFAULT_TIME
;
1569 info
->MediaChangeDetectionDisableCount
= 0;
1572 // Assume that there is initially no media in the device
1573 // only notify upper layers if there is something there
1576 info
->MediaChangeDetectionState
= MediaUnknown
;
1578 info
->MediaChangeIrpTimeInUse
= 0;
1579 info
->MediaChangeIrpLost
= FALSE
;
1582 // setup all extra flags we'll be setting for this irp
1585 if (AllowDriveToSleep
) {
1586 SET_FLAG(info
->SrbFlags
, SRB_FLAGS_NO_KEEP_AWAKE
);
1588 SET_FLAG(info
->SrbFlags
, SRB_CLASS_FLAGS_LOW_PRIORITY
);
1589 SET_FLAG(info
->SrbFlags
, SRB_FLAGS_NO_QUEUE_FREEZE
);
1590 SET_FLAG(info
->SrbFlags
, SRB_FLAGS_DISABLE_SYNCH_TRANSFER
);
1592 KeInitializeMutex(&info
->MediaChangeMutex
, 0x100);
1595 // It is ok to support media change events on this
1599 FdoExtension
->MediaChangeDetectionInfo
= info
;
1602 // NOTE: the DeviceType is FILE_DEVICE_CD_ROM even
1603 // when the device supports DVD (no need to
1604 // check for FILE_DEVICE_DVD, as it's not a
1608 if (FdoExtension
->DeviceObject
->DeviceType
== FILE_DEVICE_CD_ROM
){
1612 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
1613 "ClasspInitializePolling: Testing for GESN\n"));
1614 status
= ClasspInitializeGesn(FdoExtension
, info
);
1615 if (NT_SUCCESS(status
)) {
1616 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
1617 "ClasspInitializePolling: GESN available "
1618 "for %p\n", FdoExtension
->DeviceObject
));
1619 ASSERT(info
->Gesn
.Supported
);
1620 ASSERT(info
->Gesn
.Buffer
!= NULL
);
1621 ASSERT(info
->Gesn
.BufferSize
!= 0);
1622 ASSERT(info
->Gesn
.EventMask
!= 0);
1623 // must return here, for ASSERTs to be valid.
1624 return STATUS_SUCCESS
;
1626 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
1627 "ClasspInitializePolling: GESN *NOT* available "
1628 "for %p\n", FdoExtension
->DeviceObject
));
1631 ASSERT(info
->Gesn
.Supported
== 0);
1632 ASSERT(info
->Gesn
.Buffer
== NULL
);
1633 ASSERT(info
->Gesn
.BufferSize
== 0);
1634 ASSERT(info
->Gesn
.EventMask
== 0);
1635 info
->Gesn
.Supported
= 0; // just in case....
1636 return STATUS_SUCCESS
;
1646 // nothing to free here
1648 return STATUS_INSUFFICIENT_RESOURCES
;
1650 } // end ClasspInitializePolling()
1653 ClasspInitializeGesn(
1654 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
1655 IN PMEDIA_CHANGE_DETECTION_INFO Info
1658 PNOTIFICATION_EVENT_STATUS_HEADER header
;
1659 CLASS_DETECTION_STATE detectionState
= ClassDetectionUnknown
;
1660 PSTORAGE_ADAPTER_DESCRIPTOR adapterDescriptor
;
1661 NTSTATUS status
= STATUS_NOT_SUPPORTED
;
1664 BOOLEAN retryImmediately
;
1670 ASSERT(Info
== FdoExtension
->MediaChangeDetectionInfo
);
1673 // read if we already know the abilities of the device
1676 ClassGetDeviceParameter(FdoExtension
,
1677 CLASSP_REG_SUBKEY_NAME
,
1678 CLASSP_REG_MMC_DETECTION_VALUE_NAME
,
1679 (PULONG
)&detectionState
);
1681 if (detectionState
== ClassDetectionUnsupported
) {
1686 // check if the device has a hack flag saying never to try this.
1689 if (TEST_FLAG(FdoExtension
->PrivateFdoData
->HackFlags
,
1690 FDO_HACK_GESN_IS_BAD
)) {
1692 detectionState
= ClassDetectionUnsupported
;
1693 ClassSetDeviceParameter(FdoExtension
,
1694 CLASSP_REG_SUBKEY_NAME
,
1695 CLASSP_REG_MMC_DETECTION_VALUE_NAME
,
1696 ClassDetectionSupported
);
1703 // else go through the process since we allocate buffers and
1704 // get all sorts of device settings.
1707 if (Info
->Gesn
.Buffer
== NULL
) {
1708 Info
->Gesn
.Buffer
= ExAllocatePoolWithTag(NonPagedPoolCacheAligned
,
1712 if (Info
->Gesn
.Buffer
== NULL
) {
1713 status
= STATUS_INSUFFICIENT_RESOURCES
;
1716 if (Info
->Gesn
.Mdl
!= NULL
) {
1717 IoFreeMdl(Info
->Gesn
.Mdl
);
1719 Info
->Gesn
.Mdl
= IoAllocateMdl(Info
->Gesn
.Buffer
,
1721 FALSE
, FALSE
, NULL
);
1722 if (Info
->Gesn
.Mdl
== NULL
) {
1723 status
= STATUS_INSUFFICIENT_RESOURCES
;
1727 MmBuildMdlForNonPagedPool(Info
->Gesn
.Mdl
);
1728 Info
->Gesn
.BufferSize
= GESN_BUFFER_SIZE
;
1729 Info
->Gesn
.EventMask
= 0;
1732 // all items are prepared to use GESN (except the event mask, so don't
1733 // optimize this part out!).
1735 // now see if it really works. we have to loop through this because
1736 // many SAMSUNG (and one COMPAQ) drives timeout when requesting
1737 // NOT_READY events, even when the IMMEDIATE bit is set. :(
1739 // using a drive list is cumbersome, so this might fix the problem.
1742 adapterDescriptor
= FdoExtension
->AdapterDescriptor
;
1744 retryImmediately
= TRUE
;
1745 for (i
= 0; i
< 16 && retryImmediately
== TRUE
; i
++) {
1747 irp
= ClasspPrepareMcnIrp(FdoExtension
, Info
, TRUE
);
1749 status
= STATUS_INSUFFICIENT_RESOURCES
;
1753 ASSERT(TEST_FLAG(Info
->MediaChangeSrb
.SrbFlags
, SRB_FLAGS_NO_QUEUE_FREEZE
));
1756 // replace the completion routine with a different one this time...
1759 IoSetCompletionRoutine(irp
,
1760 ClassSignalCompletion
,
1763 KeInitializeEvent(&event
, SynchronizationEvent
, FALSE
);
1765 status
= IoCallDriver(FdoExtension
->CommonExtension
.LowerDeviceObject
, irp
);
1767 if (status
== STATUS_PENDING
) {
1768 status
= KeWaitForSingleObject(&event
,
1773 ASSERT(NT_SUCCESS(status
));
1775 ClassReleaseRemoveLock(FdoExtension
->DeviceObject
, irp
);
1777 if (SRB_STATUS(Info
->MediaChangeSrb
.SrbStatus
) != SRB_STATUS_SUCCESS
) {
1778 ClassInterpretSenseInfo(FdoExtension
->DeviceObject
,
1779 &(Info
->MediaChangeSrb
),
1787 if ((adapterDescriptor
->BusType
== BusTypeAtapi
) &&
1788 (Info
->MediaChangeSrb
.SrbStatus
== SRB_STATUS_BUS_RESET
)
1792 // ATAPI unfortunately returns SRB_STATUS_BUS_RESET instead
1793 // of SRB_STATUS_TIMEOUT, so we cannot differentiate between
1794 // the two. if we get this status four time consecutively,
1795 // stop trying this command. it is too late to change ATAPI
1796 // at this point, so special-case this here. (07/10/2001)
1797 // NOTE: any value more than 4 may cause the device to be
1802 if (atapiResets
>= 4) {
1803 status
= STATUS_IO_DEVICE_ERROR
;
1808 if (status
== STATUS_DATA_OVERRUN
) {
1809 status
= STATUS_SUCCESS
;
1812 if ((status
== STATUS_INVALID_DEVICE_REQUEST
) ||
1813 (status
== STATUS_TIMEOUT
) ||
1814 (status
== STATUS_IO_DEVICE_ERROR
) ||
1815 (status
== STATUS_IO_TIMEOUT
)
1819 // with these error codes, we don't ever want to try this command
1820 // again on this device, since it reacts poorly.
1823 ClassSetDeviceParameter(FdoExtension
,
1824 CLASSP_REG_SUBKEY_NAME
,
1825 CLASSP_REG_MMC_DETECTION_VALUE_NAME
,
1826 ClassDetectionUnsupported
);
1827 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugWarning
,
1828 "Classpnp => GESN test failed %x for fdo %p\n",
1829 status
, FdoExtension
->DeviceObject
));
1835 if (!NT_SUCCESS(status
)) {
1838 // this may be other errors that should not disable GESN
1839 // for all future start_device calls.
1842 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugWarning
,
1843 "Classpnp => GESN test failed %x for fdo %p\n",
1844 status
, FdoExtension
->DeviceObject
));
1851 // the first time, the request was just retrieving a mask of
1852 // available bits. use this to mask future requests.
1855 header
= (PNOTIFICATION_EVENT_STATUS_HEADER
)(Info
->Gesn
.Buffer
);
1857 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
1858 "Classpnp => Fdo %p supports event mask %x\n",
1859 FdoExtension
->DeviceObject
, header
->SupportedEventClasses
));
1862 if (TEST_FLAG(header
->SupportedEventClasses
,
1863 NOTIFICATION_MEDIA_STATUS_CLASS_MASK
)) {
1864 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
1865 "Classpnp => GESN supports MCN\n"));
1867 if (TEST_FLAG(header
->SupportedEventClasses
,
1868 NOTIFICATION_DEVICE_BUSY_CLASS_MASK
)) {
1869 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
1870 "Classpnp => GESN supports DeviceBusy\n"));
1872 Info
->Gesn
.EventMask
= header
->SupportedEventClasses
;
1875 // realistically, we are only considering the following events:
1876 // EXTERNAL REQUEST - this is being tested for play/stop/etc.
1877 // MEDIA STATUS - autorun and ejection requests.
1878 // DEVICE BUSY - to allow us to predict when media will be ready.
1879 // therefore, we should not bother querying for the other,
1880 // unknown events. clear all but the above flags.
1883 Info
->Gesn
.EventMask
&=
1884 NOTIFICATION_EXTERNAL_REQUEST_CLASS_MASK
|
1885 NOTIFICATION_MEDIA_STATUS_CLASS_MASK
|
1886 NOTIFICATION_DEVICE_BUSY_CLASS_MASK
;
1890 // HACKHACK - REF #0001
1891 // Some devices will *never* report an event if we've also requested
1892 // that it report lower-priority events. this is due to a
1893 // misunderstanding in the specification wherein a "No Change" is
1894 // interpreted to be a real event. what should occur is that the
1895 // device should ignore "No Change" events when multiple event types
1896 // are requested unless there are no other events waiting. this
1897 // greatly reduces the number of requests that the host must send
1898 // to determine if an event has occurred. Since we must work on all
1899 // drives, default to enabling the hack until we find evidence of
1903 if (CountOfSetBitsUChar(Info
->Gesn
.EventMask
) == 1) {
1904 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
1905 "Classpnp => GESN hack %s for FDO %p\n",
1906 "not required", FdoExtension
->DeviceObject
));
1908 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
1909 "Classpnp => GESN hack %s for FDO %p\n",
1910 "enabled", FdoExtension
->DeviceObject
));
1911 Info
->Gesn
.HackEventMask
= 1;
1917 // not the first time looping through, so interpret the results.
1920 ClasspInterpretGesnData(FdoExtension
,
1921 (PVOID
)Info
->Gesn
.Buffer
,
1926 } // end loop of GESN requests....
1929 // we can only use this if it can be relied upon for media changes,
1930 // since we are (by definition) no longer going to be polling via
1931 // a TEST_UNIT_READY irp, and drives will not report UNIT ATTENTION
1932 // for this command (although a filter driver, such as one for burning
1933 // cd's, might still fake those errors).
1935 // since we also rely upon NOT_READY events to change the cursor
1936 // into a "wait" cursor, we can't use GESN without NOT_READY support.
1939 if (TEST_FLAG(Info
->Gesn
.EventMask
,
1940 NOTIFICATION_MEDIA_STATUS_CLASS_MASK
) &&
1941 TEST_FLAG(Info
->Gesn
.EventMask
,
1942 NOTIFICATION_DEVICE_BUSY_CLASS_MASK
)
1945 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
1946 "Classpnp => Enabling GESN support for fdo %p\n",
1947 FdoExtension
->DeviceObject
));
1948 Info
->Gesn
.Supported
= TRUE
;
1950 ClassSetDeviceParameter(FdoExtension
,
1951 CLASSP_REG_SUBKEY_NAME
,
1952 CLASSP_REG_MMC_DETECTION_VALUE_NAME
,
1953 ClassDetectionSupported
);
1955 return STATUS_SUCCESS
;
1959 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
1960 "Classpnp => GESN available but not enabled for fdo %p\n",
1961 FdoExtension
->DeviceObject
));
1967 if (Info
->Gesn
.Mdl
) {
1968 IoFreeMdl(Info
->Gesn
.Mdl
);
1969 Info
->Gesn
.Mdl
= NULL
;
1971 if (Info
->Gesn
.Buffer
) {
1972 ExFreePool(Info
->Gesn
.Buffer
);
1973 Info
->Gesn
.Buffer
= NULL
;
1975 Info
->Gesn
.Supported
= 0;
1976 Info
->Gesn
.EventMask
= 0;
1977 Info
->Gesn
.BufferSize
= 0;
1978 return STATUS_NOT_SUPPORTED
;
1982 /*++////////////////////////////////////////////////////////////////////////////
1984 ClassInitializeTestUnitPolling()
1986 Routine Description:
1988 This routine will initialize MCN regardless of the settings stored
1989 in the registry. This should be used with caution, as some devices
1990 react badly to constant io. (i.e. never spin down, continuously cycling
1991 media in changers, ejection of media, etc.) It is highly suggested to
1992 use ClassInitializeMediaChangeDetection() instead.
1996 FdoExtension is the device to poll
1998 AllowDriveToSleep says whether to attempt to allow the drive to sleep
1999 or not. This only affects system-known spin down states, so if a
2000 drive spins itself down, this has no effect until the system spins
2007 ClassInitializeTestUnitPolling(
2008 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
2009 IN BOOLEAN AllowDriveToSleep
2012 return ClasspInitializePolling(FdoExtension
, AllowDriveToSleep
);
2013 } // end ClassInitializeTestUnitPolling()
2015 /*++////////////////////////////////////////////////////////////////////////////
2017 ClassInitializeMediaChangeDetection()
2019 Routine Description:
2021 This routine checks to see if it is safe to initialize MCN (the back end
2022 to autorun) for a given device. It will then check the device-type wide
2023 key "Autorun" in the service key (for legacy reasons), and then look in
2024 the device-specific key to potentially override that setting.
2026 If MCN is to be enabled, all neccessary structures and memory are
2027 allocated and initialized.
2029 This routine MUST be called only from the ClassInit() callback.
2033 FdoExtension - the device to initialize MCN for, if appropriate
2035 EventPrefix - unused, legacy argument. Set to zero.
2041 ClassInitializeMediaChangeDetection(
2042 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
2043 IN PUCHAR EventPrefix
2046 PDEVICE_OBJECT fdo
= FdoExtension
->DeviceObject
;
2049 PCLASS_DRIVER_EXTENSION driverExtension
= ClassGetDriverExtension(
2052 BOOLEAN disabledForBadHardware
;
2054 BOOLEAN instanceOverride
;
2059 // NOTE: This assumes that ClassInitializeMediaChangeDetection is always
2060 // called in the context of the ClassInitDevice callback. If called
2061 // after then this check will have already been made and the
2062 // once a second timer will not have been enabled.
2065 disabledForBadHardware
= ClasspIsMediaChangeDisabledDueToHardwareLimitation(
2067 &(driverExtension
->RegistryPath
)
2070 if (disabledForBadHardware
) {
2071 DebugPrint((ClassDebugMCN
,
2072 "ClassInitializeMCN: Disabled due to hardware"
2073 "limitations for this device"));
2078 // autorun should now be enabled by default for all media types.
2081 disabled
= ClasspIsMediaChangeDisabledForClass(
2083 &(driverExtension
->RegistryPath
)
2086 DebugPrint((ClassDebugMCN
,
2087 "ClassInitializeMCN: Class MCN is %s\n",
2088 (disabled
? "disabled" : "enabled")));
2090 status
= ClasspMediaChangeDeviceInstanceOverride(
2092 &instanceOverride
); // default value
2094 if (!NT_SUCCESS(status
)) {
2095 DebugPrint((ClassDebugMCN
,
2096 "ClassInitializeMCN: Instance using default\n"));
2098 DebugPrint((ClassDebugMCN
,
2099 "ClassInitializeMCN: Instance override: %s MCN\n",
2100 (instanceOverride
? "Enabling" : "Disabling")));
2101 disabled
= !instanceOverride
;
2104 DebugPrint((ClassDebugMCN
,
2105 "ClassInitializeMCN: Instance MCN is %s\n",
2106 (disabled
? "disabled" : "enabled")));
2113 // if the drive is not a CDROM, allow the drive to sleep
2115 if (FdoExtension
->DeviceObject
->DeviceType
== FILE_DEVICE_CD_ROM
) {
2116 ClasspInitializePolling(FdoExtension
, FALSE
);
2118 ClasspInitializePolling(FdoExtension
, TRUE
);
2122 } // end ClassInitializeMediaChangeDetection()
2124 /*++////////////////////////////////////////////////////////////////////////////
2126 ClasspMediaChangeDeviceInstanceOverride()
2128 Routine Description:
2130 The user can override the global setting to enable or disable Autorun on a
2131 specific cdrom device via the control panel. This routine checks and/or
2136 FdoExtension - the device to set/get the value for
2137 Value - the value to use in a set
2138 SetValue - whether to set the value
2142 TRUE - Autorun is disabled
2143 FALSE - Autorun is not disabled (Default)
2147 ClasspMediaChangeDeviceInstanceOverride(
2148 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
2149 OUT PBOOLEAN Enabled
2152 HANDLE deviceParameterHandle
; // cdrom instance key
2153 HANDLE driverParameterHandle
; // cdrom specific key
2154 RTL_QUERY_REGISTRY_TABLE queryTable
[3];
2155 OBJECT_ATTRIBUTES objectAttributes
;
2156 UNICODE_STRING subkeyName
;
2159 ULONG alwaysDisable
;
2165 deviceParameterHandle
= NULL
;
2166 driverParameterHandle
= NULL
;
2167 status
= STATUS_UNSUCCESSFUL
;
2168 alwaysEnable
= FALSE
;
2169 alwaysDisable
= FALSE
;
2173 status
= IoOpenDeviceRegistryKey( FdoExtension
->LowerPdo
,
2174 PLUGPLAY_REGKEY_DEVICE
,
2176 &deviceParameterHandle
2178 if (!NT_SUCCESS(status
)) {
2181 // this can occur when a new device is added to the system
2182 // this is due to cdrom.sys being an 'essential' driver
2184 DebugPrint((ClassDebugMCN
,
2185 "ClassMediaChangeDeviceInstanceDisabled: "
2186 "Could not open device registry key [%lx]\n", status
));
2190 RtlInitUnicodeString(&subkeyName
, MCN_REG_SUBKEY_NAME
);
2191 InitializeObjectAttributes(&objectAttributes
,
2193 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
2194 deviceParameterHandle
,
2195 (PSECURITY_DESCRIPTOR
) NULL
);
2197 status
= ZwCreateKey(&driverParameterHandle
,
2201 (PUNICODE_STRING
) NULL
,
2202 REG_OPTION_NON_VOLATILE
,
2205 if (!NT_SUCCESS(status
)) {
2206 DebugPrint((ClassDebugMCN
,
2207 "ClassMediaChangeDeviceInstanceDisabled: "
2208 "subkey could not be created. %lx\n", status
));
2213 // Default to not changing autorun behavior, based upon setting
2214 // registryValue to zero.
2219 RtlZeroMemory(&queryTable
[0], sizeof(queryTable
));
2221 queryTable
[0].Flags
= RTL_QUERY_REGISTRY_DIRECT
;
2222 queryTable
[0].DefaultType
= REG_DWORD
;
2223 queryTable
[0].DefaultLength
= 0;
2226 queryTable
[0].Name
= MCN_REG_AUTORUN_DISABLE_INSTANCE_NAME
;
2227 queryTable
[0].EntryContext
= &alwaysDisable
;
2228 queryTable
[0].DefaultData
= &alwaysDisable
;
2230 queryTable
[0].Name
= MCN_REG_AUTORUN_ENABLE_INSTANCE_NAME
;
2231 queryTable
[0].EntryContext
= &alwaysEnable
;
2232 queryTable
[0].DefaultData
= &alwaysEnable
;
2236 // don't care if it succeeds, since we set defaults above
2239 RtlQueryRegistryValues(RTL_REGISTRY_HANDLE
,
2240 (PWSTR
)driverParameterHandle
,
2248 if (driverParameterHandle
) ZwClose(driverParameterHandle
);
2249 if (deviceParameterHandle
) ZwClose(deviceParameterHandle
);
2253 if (alwaysEnable
&& alwaysDisable
) {
2255 DebugPrint((ClassDebugMCN
,
2256 "ClassMediaChangeDeviceInstanceDisabled: %s selected\n",
2257 "Both Enable and Disable set -- DISABLE"));
2258 ASSERT(NT_SUCCESS(status
));
2259 status
= STATUS_SUCCESS
;
2262 } else if (alwaysDisable
) {
2264 DebugPrint((ClassDebugMCN
,
2265 "ClassMediaChangeDeviceInstanceDisabled: %s selected\n",
2267 ASSERT(NT_SUCCESS(status
));
2268 status
= STATUS_SUCCESS
;
2271 } else if (alwaysEnable
) {
2273 DebugPrint((ClassDebugMCN
,
2274 "ClassMediaChangeDeviceInstanceDisabled: %s selected\n",
2276 ASSERT(NT_SUCCESS(status
));
2277 status
= STATUS_SUCCESS
;
2282 DebugPrint((ClassDebugMCN
,
2283 "ClassMediaChangeDeviceInstanceDisabled: %s selected\n",
2285 status
= STATUS_UNSUCCESSFUL
;
2291 } // end ClasspMediaChangeDeviceInstanceOverride()
2293 /*++////////////////////////////////////////////////////////////////////////////
2295 ClasspIsMediaChangeDisabledDueToHardwareLimitation()
2297 Routine Description:
2299 The key AutoRunAlwaysDisable contains a MULTI_SZ of hardware IDs for
2300 which to never enable MediaChangeNotification.
2302 The user can override the global setting to enable or disable Autorun on a
2303 specific cdrom device via the control panel.
2308 RegistryPath - pointer to the unicode string inside
2309 ...\CurrentControlSet\Services\Cdrom
2314 FALSE - Autorun may be enabled
2318 ClasspIsMediaChangeDisabledDueToHardwareLimitation(
2319 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
2320 IN PUNICODE_STRING RegistryPath
2323 PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor
= FdoExtension
->DeviceDescriptor
;
2324 OBJECT_ATTRIBUTES objectAttributes
;
2325 HANDLE serviceKey
= NULL
;
2326 RTL_QUERY_REGISTRY_TABLE parameters
[2];
2328 UNICODE_STRING deviceUnicodeString
;
2329 ANSI_STRING deviceString
;
2330 ULONG mediaChangeNotificationDisabled
= FALSE
;
2338 // open the service key.
2341 InitializeObjectAttributes(&objectAttributes
,
2343 OBJ_CASE_INSENSITIVE
,
2347 status
= ZwOpenKey(&serviceKey
,
2351 ASSERT(NT_SUCCESS(status
));
2354 if(!NT_SUCCESS(status
)) {
2357 // always take the safe path. if we can't open the service key,
2367 // Determine if drive is in a list of those requiring
2368 // autorun to be disabled. this is stored in a REG_MULTI_SZ
2369 // named AutoRunAlwaysDisable. this is required as some autochangers
2370 // must load the disc to reply to ChkVerify request, causing them
2371 // to cycle discs continuously.
2381 deviceString
.Buffer
= NULL
;
2382 deviceUnicodeString
.Buffer
= NULL
;
2385 // there may be nothing to check against
2388 if ((deviceDescriptor
->VendorIdOffset
== 0) &&
2389 (deviceDescriptor
->ProductIdOffset
== 0)) {
2395 if (deviceDescriptor
->VendorIdOffset
== 0) {
2398 vendorId
= (PUCHAR
) deviceDescriptor
+ deviceDescriptor
->VendorIdOffset
;
2399 length
= strlen(vendorId
);
2402 if ( deviceDescriptor
->ProductIdOffset
== 0 ) {
2405 productId
= (PUCHAR
) deviceDescriptor
+ deviceDescriptor
->ProductIdOffset
;
2406 length
+= strlen(productId
);
2409 if ( deviceDescriptor
->ProductRevisionOffset
== 0 ) {
2412 revisionId
= (PUCHAR
) deviceDescriptor
+ deviceDescriptor
->ProductRevisionOffset
;
2413 length
+= strlen(revisionId
);
2417 // allocate a buffer for the string
2420 deviceString
.Length
= (USHORT
)( length
);
2421 deviceString
.MaximumLength
= deviceString
.Length
+ 1;
2422 deviceString
.Buffer
= (PUCHAR
)ExAllocatePoolWithTag( NonPagedPool
,
2423 deviceString
.MaximumLength
,
2424 CLASS_TAG_AUTORUN_DISABLE
2426 if (deviceString
.Buffer
== NULL
) {
2427 DebugPrint((ClassDebugMCN
,
2428 "ClassMediaChangeDisabledForHardware: Unable to alloc "
2429 "string buffer\n" ));
2434 // copy strings to the buffer
2438 if (vendorId
!= NULL
) {
2439 RtlCopyMemory(deviceString
.Buffer
+ offset
,
2442 offset
+= strlen(vendorId
);
2445 if ( productId
!= NULL
) {
2446 RtlCopyMemory(deviceString
.Buffer
+ offset
,
2449 offset
+= strlen(productId
);
2451 if ( revisionId
!= NULL
) {
2452 RtlCopyMemory(deviceString
.Buffer
+ offset
,
2454 strlen(revisionId
));
2455 offset
+= strlen(revisionId
);
2458 ASSERT(offset
== deviceString
.Length
);
2460 deviceString
.Buffer
[deviceString
.Length
] = '\0'; // Null-terminated
2463 // convert to unicode as registry deals with unicode strings
2466 status
= RtlAnsiStringToUnicodeString( &deviceUnicodeString
,
2470 if (!NT_SUCCESS(status
)) {
2471 DebugPrint((ClassDebugMCN
,
2472 "ClassMediaChangeDisabledForHardware: cannot convert "
2473 "to unicode %lx\n", status
));
2478 // query the value, setting valueFound to true if found
2481 RtlZeroMemory(parameters
, sizeof(parameters
));
2483 nullMultiSz
= L
"\0";
2484 parameters
[0].QueryRoutine
= ClasspMediaChangeRegistryCallBack
;
2485 parameters
[0].Flags
= RTL_QUERY_REGISTRY_REQUIRED
;
2486 parameters
[0].Name
= L
"AutoRunAlwaysDisable";
2487 parameters
[0].EntryContext
= &mediaChangeNotificationDisabled
;
2488 parameters
[0].DefaultType
= REG_MULTI_SZ
;
2489 parameters
[0].DefaultData
= nullMultiSz
;
2490 parameters
[0].DefaultLength
= 0;
2492 status
= RtlQueryRegistryValues(RTL_REGISTRY_HANDLE
,
2495 &deviceUnicodeString
,
2498 if ( !NT_SUCCESS(status
) ) {
2504 if (deviceString
.Buffer
!= NULL
) {
2505 ExFreePool( deviceString
.Buffer
);
2507 if (deviceUnicodeString
.Buffer
!= NULL
) {
2508 RtlFreeUnicodeString( &deviceUnicodeString
);
2511 ZwClose(serviceKey
);
2514 if (mediaChangeNotificationDisabled
) {
2515 DebugPrint((ClassDebugMCN
, "ClassMediaChangeDisabledForHardware: "
2516 "Device is on disable list\n"));
2521 } // end ClasspIsMediaChangeDisabledDueToHardwareLimitation()
2523 /*++////////////////////////////////////////////////////////////////////////////
2525 ClasspIsMediaChangeDisabledForClass()
2527 Routine Description:
2529 The user must specify that AutoPlay is to run on the platform
2530 by setting the registry value HKEY_LOCAL_MACHINE\System\CurrentControlSet\
2531 Services\<SERVICE>\Autorun:REG_DWORD:1.
2533 The user can override the global setting to enable or disable Autorun on a
2534 specific cdrom device via the control panel.
2539 RegistryPath - pointer to the unicode string inside
2540 ...\CurrentControlSet\Services\Cdrom
2544 TRUE - Autorun is disabled for this class
2545 FALSE - Autorun is enabled for this class
2549 ClasspIsMediaChangeDisabledForClass(
2550 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
2551 IN PUNICODE_STRING RegistryPath
2554 PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor
= FdoExtension
->DeviceDescriptor
;
2556 OBJECT_ATTRIBUTES objectAttributes
;
2557 HANDLE serviceKey
= NULL
;
2558 HANDLE parametersKey
= NULL
;
2559 RTL_QUERY_REGISTRY_TABLE parameters
[3];
2561 UNICODE_STRING paramStr
;
2562 UNICODE_STRING deviceUnicodeString
;
2563 ANSI_STRING deviceString
;
2566 // Default to ENABLING MediaChangeNotification (!)
2569 ULONG mcnRegistryValue
= 1;
2577 // open the service key.
2580 InitializeObjectAttributes(&objectAttributes
,
2582 OBJ_CASE_INSENSITIVE
,
2586 status
= ZwOpenKey(&serviceKey
,
2590 ASSERT(NT_SUCCESS(status
));
2592 if(!NT_SUCCESS(status
)) {
2595 // return the default value, which is the
2596 // inverse of the registry setting default
2597 // since this routine asks if it's disabled
2600 DebugPrint((ClassDebugMCN
, "ClassCheckServiceMCN: Defaulting to %s\n",
2601 (mcnRegistryValue
? "Enabled" : "Disabled")));
2602 return (BOOLEAN
)(!mcnRegistryValue
);
2606 RtlZeroMemory(parameters
, sizeof(parameters
));
2609 // Open the parameters key (if any) beneath the services key.
2612 RtlInitUnicodeString(¶mStr
, L
"Parameters");
2614 InitializeObjectAttributes(&objectAttributes
,
2616 OBJ_CASE_INSENSITIVE
,
2620 status
= ZwOpenKey(¶metersKey
,
2624 if (!NT_SUCCESS(status
)) {
2625 parametersKey
= NULL
;
2631 // Check for the Autorun value.
2634 parameters
[0].Flags
= RTL_QUERY_REGISTRY_DIRECT
;
2635 parameters
[0].Name
= L
"Autorun";
2636 parameters
[0].EntryContext
= &mcnRegistryValue
;
2637 parameters
[0].DefaultType
= REG_DWORD
;
2638 parameters
[0].DefaultData
= &mcnRegistryValue
;
2639 parameters
[0].DefaultLength
= sizeof(ULONG
);
2641 status
= RtlQueryRegistryValues(RTL_REGISTRY_HANDLE
| RTL_REGISTRY_OPTIONAL
,
2647 DebugPrint((ClassDebugMCN
, "ClassCheckServiceMCN: "
2648 "<Service>/Autorun flag = %d\n", mcnRegistryValue
));
2650 if(parametersKey
!= NULL
) {
2652 status
= RtlQueryRegistryValues(RTL_REGISTRY_HANDLE
| RTL_REGISTRY_OPTIONAL
,
2657 DebugPrint((ClassDebugMCN
, "ClassCheckServiceMCN: "
2658 "<Service>/Parameters/Autorun flag = %d\n",
2660 ZwClose(parametersKey
);
2663 ZwClose(serviceKey
);
2665 DebugPrint((ClassDebugMCN
, "ClassCheckServiceMCN: "
2666 "Autoplay for device %p is %s\n",
2667 FdoExtension
->DeviceObject
,
2668 (mcnRegistryValue
? "on" : "off")
2672 // return if it is _disabled_, which is the
2673 // inverse of the registry setting
2676 return (BOOLEAN
)(!mcnRegistryValue
);
2677 } // end ClasspIsMediaChangeDisabledForClass()
2679 /*++////////////////////////////////////////////////////////////////////////////
2681 ClassEnableMediaChangeDetection() ISSUE-2000/02/20-henrygab - why public?
2682 ClassEnableMediaChangeDetection() ISSUE-2000/02/20-henrygab - not documented
2684 Routine Description:
2697 ClassEnableMediaChangeDetection(
2698 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
2701 PMEDIA_CHANGE_DETECTION_INFO info
= FdoExtension
->MediaChangeDetectionInfo
;
2707 DebugPrint((ClassDebugMCN
,
2708 "ClassEnableMediaChangeDetection: not initialized\n"));
2712 KeWaitForMutexObject(&info
->MediaChangeMutex
,
2718 oldCount
= --info
->MediaChangeDetectionDisableCount
;
2720 ASSERT(oldCount
>= 0);
2722 DebugPrint((ClassDebugMCN
, "ClassEnableMediaChangeDetection: Disable count "
2724 info
->MediaChangeDetectionDisableCount
));
2729 // We don't know what state the media is in anymore.
2732 ClasspInternalSetMediaChangeState(FdoExtension
,
2738 // Reset the MCN timer.
2741 ClassResetMediaChangeTimer(FdoExtension
);
2743 DebugPrint((ClassDebugMCN
, "MCD is enabled\n"));
2747 DebugPrint((ClassDebugMCN
, "MCD still disabled\n"));
2753 // Let something else run.
2756 KeReleaseMutex(&info
->MediaChangeMutex
, FALSE
);
2759 } // end ClassEnableMediaChangeDetection()
2761 /*++////////////////////////////////////////////////////////////////////////////
2763 ClassDisableMediaChangeDetection() ISSUE-2000/02/20-henrygab - why public?
2764 ClassDisableMediaChangeDetection() ISSUE-2000/02/20-henrygab - not documented
2766 Routine Description:
2778 ULONG BreakOnMcnDisable
= FALSE
;
2781 ClassDisableMediaChangeDetection(
2782 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
2785 PMEDIA_CHANGE_DETECTION_INFO info
= FdoExtension
->MediaChangeDetectionInfo
;
2793 KeWaitForMutexObject(&info
->MediaChangeMutex
,
2799 info
->MediaChangeDetectionDisableCount
++;
2801 DebugPrint((ClassDebugMCN
, "ClassDisableMediaChangeDetection: "
2802 "disable count is %d\n",
2803 info
->MediaChangeDetectionDisableCount
));
2805 KeReleaseMutex(&info
->MediaChangeMutex
, FALSE
);
2808 } // end ClassDisableMediaChangeDetection()
2810 /*++////////////////////////////////////////////////////////////////////////////
2812 ClassCleanupMediaChangeDetection() ISSUE-2000/02/20-henrygab - why public?!
2814 Routine Description:
2816 This routine will cleanup any resources allocated for MCN. It is called
2817 by classpnp during remove device, and therefore is not typically required
2818 by external drivers.
2826 ClassCleanupMediaChangeDetection(
2827 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
2830 PMEDIA_CHANGE_DETECTION_INFO info
= FdoExtension
->MediaChangeDetectionInfo
;
2838 FdoExtension
->MediaChangeDetectionInfo
= NULL
;
2840 if (info
->Gesn
.Buffer
) {
2841 ExFreePool(info
->Gesn
.Buffer
);
2843 IoFreeIrp(info
->MediaChangeIrp
);
2844 ExFreePool(info
->SenseBuffer
);
2847 } // end ClassCleanupMediaChangeDetection()
2849 /*++////////////////////////////////////////////////////////////////////////////
2851 ClasspMcnControl() - ISSUE-2000/02/20-henrygab - not documented
2853 Routine Description:
2867 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
2869 IN PSCSI_REQUEST_BLOCK Srb
2872 PCOMMON_DEVICE_EXTENSION commonExtension
=
2873 (PCOMMON_DEVICE_EXTENSION
) FdoExtension
;
2875 PIO_STACK_LOCATION irpStack
= IoGetCurrentIrpStackLocation(Irp
);
2876 PPREVENT_MEDIA_REMOVAL request
= Irp
->AssociatedIrp
.SystemBuffer
;
2878 PFILE_OBJECT fileObject
= irpStack
->FileObject
;
2879 PFILE_OBJECT_EXTENSION fsContext
= NULL
;
2881 NTSTATUS status
= STATUS_SUCCESS
;
2886 // Check to make sure we have a file object extension to keep track of this
2887 // request. If not we'll fail it before synchronizing.
2892 if(fileObject
!= NULL
) {
2893 fsContext
= ClasspGetFsContext(commonExtension
, fileObject
);
2894 }else if(Irp
->RequestorMode
== KernelMode
) { // && fileObject == NULL
2895 fsContext
= &FdoExtension
->KernelModeMcnContext
;
2898 if (fsContext
== NULL
) {
2901 // This handle isn't setup correctly. We can't let the
2905 status
= STATUS_INVALID_PARAMETER
;
2909 if(request
->PreventMediaRemoval
) {
2912 // This is a lock command. Reissue the command in case bus or
2913 // device was reset and the lock was cleared.
2916 ClassDisableMediaChangeDetection(FdoExtension
);
2917 InterlockedIncrement(&(fsContext
->McnDisableCount
));
2921 if(fsContext
->McnDisableCount
== 0) {
2922 status
= STATUS_INVALID_DEVICE_STATE
;
2926 InterlockedDecrement(&(fsContext
->McnDisableCount
));
2927 ClassEnableMediaChangeDetection(FdoExtension
);
2932 Irp
->IoStatus
.Status
= status
;
2938 ClassReleaseRemoveLock(FdoExtension
->DeviceObject
, Irp
);
2939 ClassCompleteRequest(FdoExtension
->DeviceObject
,
2944 } // end ClasspMcnControl(
2946 /*++////////////////////////////////////////////////////////////////////////////
2948 ClasspMediaChangeRegistryCallBack()
2950 Routine Description:
2952 This callback for a registry SZ or MULTI_SZ is called once for each
2953 SZ in the value. It will attempt to match the data with the
2954 UNICODE_STRING passed in as Context, and modify EntryContext if a
2955 match is found. Written for ClasspCheckRegistryForMediaChangeCompletion
2959 ValueName - name of the key that was opened
2960 ValueType - type of data stored in the value (REG_SZ for this routine)
2961 ValueData - data in the registry, in this case a wide string
2962 ValueLength - length of the data including the terminating null
2963 Context - unicode string to compare against ValueData
2964 EntryContext - should be initialized to 0, will be set to 1 if match found
2969 EntryContext will be 1 if found
2973 ClasspMediaChangeRegistryCallBack(
2977 IN ULONG ValueLength
,
2979 IN PVOID EntryContext
2983 PUNICODE_STRING deviceString
;
2987 UNREFERENCED_PARAMETER(ValueName
);
2991 // if we have already set the value to true, exit
2994 valueFound
= EntryContext
;
2995 if ((*valueFound
) != 0) {
2996 DebugPrint((ClassDebugMCN
, "ClasspMcnRegCB: already set to true\n"));
2997 return STATUS_SUCCESS
;
3000 if (ValueLength
== sizeof(WCHAR
)) {
3001 DebugPrint((ClassDebugError
, "ClasspMcnRegCB: NULL string should "
3002 "never be passed to registry call-back!\n"));
3003 return STATUS_SUCCESS
;
3008 // if the data is not a terminated string, exit
3011 if (ValueType
!= REG_SZ
) {
3012 return STATUS_SUCCESS
;
3015 deviceString
= Context
;
3016 keyValue
= ValueData
;
3017 ValueLength
-= sizeof(WCHAR
); // ignore the null character
3020 // do not compare more memory than is in deviceString
3023 if (ValueLength
> deviceString
->Length
) {
3024 ValueLength
= deviceString
->Length
;
3028 // if the strings match, disable autorun
3031 if (RtlCompareMemory(deviceString
->Buffer
, keyValue
, ValueLength
) == ValueLength
) {
3032 DebugPrint((ClassDebugMCN
, "ClasspRegMcnCB: Match found\n"));
3033 DebugPrint((ClassDebugMCN
, "ClasspRegMcnCB: DeviceString at %p\n",
3034 deviceString
->Buffer
));
3035 DebugPrint((ClassDebugMCN
, "ClasspRegMcnCB: KeyValue at %p\n",
3037 (*valueFound
) = TRUE
;
3040 return STATUS_SUCCESS
;
3041 } // end ClasspMediaChangeRegistryCallBack()
3043 /*++////////////////////////////////////////////////////////////////////////////
3045 ClasspTimerTick() - ISSUE-2000/02/20-henrygab - not documented
3047 Routine Description:
3061 PDEVICE_OBJECT DeviceObject
,
3065 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= DeviceObject
->DeviceExtension
;
3066 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
3069 ASSERT(commonExtension
->IsFdo
);
3072 // Do any media change work
3074 isRemoved
= ClassAcquireRemoveLock(DeviceObject
, (PIRP
)ClasspTimerTick
);
3077 // We stop the timer before deleting the device. It's safe to keep going
3078 // if the flag value is REMOVE_PENDING because the removal thread will be
3079 // blocked trying to stop the timer.
3082 ASSERT(isRemoved
!= REMOVE_COMPLETE
);
3085 // This routine is reasonably safe even if the device object has a pending
3090 PFAILURE_PREDICTION_INFO info
= fdoExtension
->FailurePredictionInfo
;
3093 // Do any media change detection work
3096 if (fdoExtension
->MediaChangeDetectionInfo
!= NULL
) {
3098 ClassCheckMediaState(fdoExtension
);
3103 // Do any failure prediction work
3105 if ((info
!= NULL
) && (info
->Method
!= FailurePredictionNone
)) {
3110 if (ClasspCanSendPollingIrp(fdoExtension
)) {
3113 // Synchronization is not required here since the Interlocked
3114 // locked instruction guarantees atomicity. Other code that
3115 // resets CountDown uses InterlockedExchange which is also
3118 countDown
= InterlockedDecrement(&info
->CountDown
);
3119 if (countDown
== 0) {
3121 DebugPrint((4, "ClasspTimerTick: Send FP irp for %p\n",
3124 if(info
->WorkQueueItem
== NULL
) {
3126 info
->WorkQueueItem
=
3127 IoAllocateWorkItem(fdoExtension
->DeviceObject
);
3129 if(info
->WorkQueueItem
== NULL
) {
3132 // Set the countdown to one minute in the future.
3133 // we'll try again then in the hopes there's more
3137 DebugPrint((1, "ClassTimerTick: Couldn't allocate "
3138 "item - try again in one minute\n"));
3139 InterlockedExchange(&info
->CountDown
, 60);
3144 // Grab the remove lock so that removal will block
3145 // until the work item is done.
3148 ClassAcquireRemoveLock(fdoExtension
->DeviceObject
,
3149 info
->WorkQueueItem
);
3151 IoQueueWorkItem(info
->WorkQueueItem
,
3152 ClasspFailurePredict
,
3159 DebugPrint((3, "ClasspTimerTick: Failure "
3160 "Prediction work item is "
3161 "already active for device %p\n",
3165 } // end (countdown == 0)
3169 // If device is sleeping then just rearm polling timer
3170 DebugPrint((4, "ClassTimerTick, SHHHH!!! device is %p is sleeping\n",
3174 } // end failure prediction polling
3177 // Give driver a chance to do its own specific work
3180 if (commonExtension
->DriverExtension
->InitData
.ClassTick
!= NULL
) {
3182 commonExtension
->DriverExtension
->InitData
.ClassTick(DeviceObject
);
3184 } // end device specific tick handler
3185 } // end check for removed
3187 ClassReleaseRemoveLock(DeviceObject
, (PIRP
)ClasspTimerTick
);
3188 } // end ClasspTimerTick()
3190 /*++////////////////////////////////////////////////////////////////////////////
3192 ClasspEnableTimer() - ISSUE-2000/02/20-henrygab - not documented
3194 Routine Description:
3208 PDEVICE_OBJECT DeviceObject
3215 if (DeviceObject
->Timer
== NULL
) {
3217 status
= IoInitializeTimer(DeviceObject
, ClasspTimerTick
, NULL
);
3221 status
= STATUS_SUCCESS
;
3225 if (NT_SUCCESS(status
)) {
3227 IoStartTimer(DeviceObject
);
3228 DebugPrint((1, "ClasspEnableTimer: Once a second timer enabled "
3229 "for device %p\n", DeviceObject
));
3233 DebugPrint((1, "ClasspEnableTimer: Device %p, Status %lx "
3234 "initializing timer\n", DeviceObject
, status
));
3238 } // end ClasspEnableTimer()
3240 /*++////////////////////////////////////////////////////////////////////////////
3242 ClasspDisableTimer() - ISSUE-2000/02/20-henrygab - not documented
3244 Routine Description:
3258 PDEVICE_OBJECT DeviceObject
3261 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= DeviceObject
->DeviceExtension
;
3262 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
3263 PMEDIA_CHANGE_DETECTION_INFO mCDInfo
= fdoExtension
->MediaChangeDetectionInfo
;
3264 PFAILURE_PREDICTION_INFO fPInfo
= fdoExtension
->FailurePredictionInfo
;
3269 if (DeviceObject
->Timer
!= NULL
) {
3272 // we are only going to stop the actual timer in remove device routine.
3273 // it is the responsibility of the code within the timer routine to
3274 // check if the device is removed and not processing io for the final
3276 // this keeps the code clean and prevents lots of bugs.
3280 IoStopTimer(DeviceObject
);
3281 DebugPrint((3, "ClasspDisableTimer: Once a second timer disabled "
3282 "for device %p\n", DeviceObject
));
3286 DebugPrint((1, "ClasspDisableTimer: Timer never enabled\n"));
3290 return STATUS_SUCCESS
;
3291 } // end ClasspDisableTimer()
3293 /*++////////////////////////////////////////////////////////////////////////////
3295 ClasspFailurePredict() - ISSUE-2000/02/20-henrygab - not documented
3297 Routine Description:
3308 Note: this function can be called (via the workitem callback) after the paging device is shut down,
3309 so it must be PAGE LOCKED.
3312 ClasspFailurePredict(
3313 IN PDEVICE_OBJECT DeviceObject
,
3314 IN PFAILURE_PREDICTION_INFO Info
3317 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= DeviceObject
->DeviceExtension
;
3318 PIO_WORKITEM workItem
;
3319 STORAGE_PREDICT_FAILURE checkFailure
;
3320 SCSI_ADDRESS scsiAddress
;
3324 ASSERT(Info
!= NULL
);
3326 DebugPrint((1, "ClasspFailurePredict: Polling for failure\n"));
3329 // Mark the work item as inactive and reset the countdown timer. we
3330 // can't risk freeing the work item until we've released the remove-lock
3331 // though - if we do it might get resused as a tag before we can release
3335 InterlockedExchange(&Info
->CountDown
, Info
->Period
);
3336 workItem
= InterlockedExchangePointer(&(Info
->WorkQueueItem
), NULL
);
3338 if (ClasspCanSendPollingIrp(fdoExtension
)) {
3341 PDEVICE_OBJECT topOfStack
;
3343 IO_STATUS_BLOCK ioStatus
;
3345 KeInitializeEvent(&event
, SynchronizationEvent
, FALSE
);
3347 topOfStack
= IoGetAttachedDeviceReference(DeviceObject
);
3350 // Send down irp to see if drive is predicting failure
3353 irp
= IoBuildDeviceIoControlRequest(
3354 IOCTL_STORAGE_PREDICT_FAILURE
,
3359 sizeof(STORAGE_PREDICT_FAILURE
),
3366 status
= IoCallDriver(topOfStack
, irp
);
3367 if (status
== STATUS_PENDING
) {
3368 KeWaitForSingleObject(&event
, Executive
, KernelMode
, FALSE
, NULL
);
3369 status
= ioStatus
.Status
;
3372 status
= STATUS_INSUFFICIENT_RESOURCES
;
3375 if (NT_SUCCESS(status
) && (checkFailure
.PredictFailure
)) {
3377 checkFailure
.PredictFailure
= 512;
3380 // Send down irp to get scsi address
3382 KeInitializeEvent(&event
, SynchronizationEvent
, FALSE
);
3384 RtlZeroMemory(&scsiAddress
, sizeof(SCSI_ADDRESS
));
3385 irp
= IoBuildDeviceIoControlRequest(
3386 IOCTL_SCSI_GET_ADDRESS
,
3391 sizeof(SCSI_ADDRESS
),
3397 status
= IoCallDriver(topOfStack
, irp
);
3398 if (status
== STATUS_PENDING
) {
3399 KeWaitForSingleObject(&event
, Executive
, KernelMode
, FALSE
, NULL
);
3400 status
= ioStatus
.Status
;
3404 ClassNotifyFailurePredicted(fdoExtension
,
3405 (PUCHAR
)&checkFailure
,
3406 sizeof(checkFailure
),
3407 (BOOLEAN
)(fdoExtension
->FailurePredicted
== FALSE
),
3410 scsiAddress
.TargetId
,
3413 fdoExtension
->FailurePredicted
= TRUE
;
3417 ObDereferenceObject(topOfStack
);
3420 ClassReleaseRemoveLock(DeviceObject
, (PIRP
) workItem
);
3421 IoFreeWorkItem(workItem
);
3423 } // end ClasspFailurePredict()
3425 /*++////////////////////////////////////////////////////////////////////////////
3427 ClassNotifyFailurePredicted() ISSUE-alanwar-2000/02/20 - not documented
3429 Routine Description:
3437 ClassNotifyFailurePredicted(
3438 PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
3442 ULONG UniqueErrorValue
,
3448 PIO_ERROR_LOG_PACKET logEntry
;
3450 DebugPrint((1, "ClasspFailurePredictPollCompletion: Failure predicted for device %p\n", FdoExtension
->DeviceObject
));
3453 // Fire off a WMI event
3455 ClassWmiFireEvent(FdoExtension
->DeviceObject
,
3456 &StoragePredictFailureEventGuid
,
3462 // Log an error into the eventlog
3467 logEntry
= IoAllocateErrorLogEntry(
3468 FdoExtension
->DeviceObject
,
3469 sizeof(IO_ERROR_LOG_PACKET
) + (3 * sizeof(ULONG
)));
3471 if (logEntry
!= NULL
)
3474 logEntry
->FinalStatus
= STATUS_SUCCESS
;
3475 logEntry
->ErrorCode
= IO_WRN_FAILURE_PREDICTED
;
3476 logEntry
->SequenceNumber
= 0;
3477 logEntry
->MajorFunctionCode
= IRP_MJ_DEVICE_CONTROL
;
3478 logEntry
->IoControlCode
= IOCTL_STORAGE_PREDICT_FAILURE
;
3479 logEntry
->RetryCount
= 0;
3480 logEntry
->UniqueErrorValue
= UniqueErrorValue
;
3481 logEntry
->DumpDataSize
= 3;
3483 logEntry
->DumpData
[0] = PathId
;
3484 logEntry
->DumpData
[1] = TargetId
;
3485 logEntry
->DumpData
[2] = Lun
;
3488 // Write the error log packet.
3491 IoWriteErrorLogEntry(logEntry
);
3494 } // end ClassNotifyFailurePredicted()
3496 /*++////////////////////////////////////////////////////////////////////////////
3498 ClassSetFailurePredictionPoll()
3500 Routine Description:
3502 This routine enables polling for failure prediction, setting the timer
3503 to fire every N seconds as specified by the PollingPeriod.
3507 FdoExtension - the device to setup failure prediction for.
3509 FailurePredictionMethod - specific failure prediction method to use
3510 if set to FailurePredictionNone, will disable failure detection
3512 PollingPeriod - if 0 then no change to current polling timer
3520 ClassSetFailurePredictionPoll(
3521 PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
3522 FAILURE_PREDICTION_METHOD FailurePredictionMethod
,
3526 PFAILURE_PREDICTION_INFO info
;
3528 DEVICE_POWER_STATE powerState
;
3532 if (FdoExtension
->FailurePredictionInfo
== NULL
) {
3534 if (FailurePredictionMethod
!= FailurePredictionNone
) {
3536 info
= ExAllocatePoolWithTag(NonPagedPool
,
3537 sizeof(FAILURE_PREDICTION_INFO
),
3538 CLASS_TAG_FAILURE_PREDICT
);
3542 return STATUS_INSUFFICIENT_RESOURCES
;
3546 KeInitializeEvent(&info
->Event
, SynchronizationEvent
, TRUE
);
3548 info
->WorkQueueItem
= NULL
;
3549 info
->Period
= DEFAULT_FAILURE_PREDICTION_PERIOD
;
3554 // FaultPrediction has not been previously initialized, nor
3555 // is it being initialized now. No need to do anything.
3557 return STATUS_SUCCESS
;
3561 FdoExtension
->FailurePredictionInfo
= info
;
3565 info
= FdoExtension
->FailurePredictionInfo
;
3569 KeWaitForSingleObject(&info
->Event
,
3577 // Reset polling period and counter. Setup failure detection type
3580 if (PollingPeriod
!= 0) {
3582 InterlockedExchange(&info
->Period
, PollingPeriod
);
3586 InterlockedExchange(&info
->CountDown
, info
->Period
);
3588 info
->Method
= FailurePredictionMethod
;
3589 if (FailurePredictionMethod
!= FailurePredictionNone
) {
3591 status
= ClasspEnableTimer(FdoExtension
->DeviceObject
);
3593 if (NT_SUCCESS(status
)) {
3594 DebugPrint((3, "ClassEnableFailurePredictPoll: Enabled for "
3595 "device %p\n", FdoExtension
->DeviceObject
));
3600 status
= ClasspDisableTimer(FdoExtension
->DeviceObject
);
3601 DebugPrint((3, "ClassEnableFailurePredictPoll: Disabled for "
3602 "device %p\n", FdoExtension
->DeviceObject
));
3603 status
= STATUS_SUCCESS
;
3607 KeSetEvent(&info
->Event
, IO_NO_INCREMENT
, FALSE
);
3610 } // end ClassSetFailurePredictionPoll()