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
,
106 IO_WORKITEM_ROUTINE ClasspFailurePredict
;
110 ClasspInitializePolling(
111 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
112 IN BOOLEAN AllowDriveToSleep
118 #pragma alloc_text(PAGE, ClassInitializeMediaChangeDetection)
119 #pragma alloc_text(PAGE, ClassEnableMediaChangeDetection)
120 #pragma alloc_text(PAGE, ClassDisableMediaChangeDetection)
121 #pragma alloc_text(PAGE, ClassCleanupMediaChangeDetection)
122 #pragma alloc_text(PAGE, ClasspMediaChangeRegistryCallBack)
123 #pragma alloc_text(PAGE, ClasspInitializePolling)
125 #pragma alloc_text(PAGE, ClasspIsMediaChangeDisabledDueToHardwareLimitation)
126 #pragma alloc_text(PAGE, ClasspMediaChangeDeviceInstanceOverride)
127 #pragma alloc_text(PAGE, ClasspIsMediaChangeDisabledForClass)
129 #pragma alloc_text(PAGE, ClassSetFailurePredictionPoll)
130 #pragma alloc_text(PAGE, ClasspDisableTimer)
131 #pragma alloc_text(PAGE, ClasspEnableTimer)
135 // ISSUE -- make this public?
138 ClassSendEjectionNotification(
139 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
143 // For post-NT5.1 work, need to move EjectSynchronizationEvent
144 // to be a MUTEX so we can attempt to grab it here and benefit
145 // from deadlock detection. This will allow checking if the media
146 // has been locked by programs before broadcasting these events.
147 // (what's the point of broadcasting if the media is not locked?)
149 // This would currently only be a slight optimization. For post-NT5.1,
150 // it would allow us to send a single PERSISTENT_PREVENT to MMC devices,
151 // thereby cleaning up a lot of the ejection code. Then, when the
152 // ejection request occured, we could see if any locks for the media
153 // existed. if locked, broadcast. if not, we send the eject irp.
157 // for now, just always broadcast. make this a public routine,
158 // so class drivers can add special hacks to broadcast this for their
159 // non-MMC-compliant devices also from sense codes.
162 DBGTRACE(ClassDebugTrace
, ("ClassSendEjectionNotification: media EJECT_REQUEST"));
163 ClasspSendNotification(FdoExtension
,
164 &GUID_IO_MEDIA_EJECT_REQUEST
,
172 ClasspSendNotification(
173 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
174 IN
const GUID
* Guid
,
175 IN ULONG ExtraDataSize
,
179 PTARGET_DEVICE_CUSTOM_NOTIFICATION notification
;
183 (sizeof(TARGET_DEVICE_CUSTOM_NOTIFICATION
) - sizeof(UCHAR
)) +
186 if (requiredSize
> 0x0000ffff) {
187 // MAX_USHORT, max total size for these events!
188 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugWarning
,
189 "Error sending event: size too large! (%x)\n",
194 notification
= ExAllocatePoolWithTag(NonPagedPool
,
199 // if none allocated, exit
202 if (notification
== NULL
) {
207 // Prepare and send the request!
210 RtlZeroMemory(notification
, requiredSize
);
211 notification
->Version
= 1;
212 notification
->Size
= (USHORT
)(requiredSize
);
213 notification
->FileObject
= NULL
;
214 notification
->NameBufferOffset
= -1;
215 notification
->Event
= *Guid
;
216 RtlCopyMemory(notification
->CustomDataBuffer
, ExtraData
, ExtraDataSize
);
218 IoReportTargetDeviceChangeAsynchronous(FdoExtension
->LowerPdo
,
222 ExFreePool(notification
);
227 /*++////////////////////////////////////////////////////////////////////////////
229 ClasspInterpretGesnData()
233 This routine will interpret the data returned for a GESN command, and
234 (if appropriate) set the media change event, and broadcast the
235 appropriate events to user mode for applications who care.
239 FdoExtension - the device
241 DataBuffer - the resulting data from a GESN event.
242 requires at least EIGHT valid bytes (header == 4, data == 4)
244 ResendImmediately - whether or not to immediately resend the request.
245 this should be FALSE if there was no event, FALSE if the reported
246 event was of the DEVICE BUSY class, else true.
254 DataBuffer must be at least four bytes of valid data (header == 4 bytes),
255 and have at least eight bytes of allocated memory (all events == 4 bytes).
257 The call to StartNextPacket may occur before this routine is completed.
258 the operational change notifications are informational in nature, and
259 while useful, are not neccessary to ensure proper operation. For example,
260 if the device morphs to no longer supporting WRITE commands, all further
261 write commands will fail. There exists a small timing window wherein
262 IOCTL_IS_DISK_WRITABLE may be called and get an incorrect response. If
263 a device supports software write protect, it is expected that the
264 application can handle such a case.
266 NOTE: perhaps setting the updaterequired byte to one should be done here.
267 if so, it relies upon the setting of a 32-byte value to be an atomic
268 operation. unfortunately, there is no simple way to notify a class driver
269 which wants to know that the device behavior requires updating.
271 Not ready events may be sent every second. For example, if we were
272 to minimize the number of asynchronous notifications, an application may
273 register just after a large busy time was reported. This would then
274 prevent the application from knowing the device was busy until some
275 arbitrarily chosen timeout has occurred. Also, the GESN request would
276 have to still occur, since it checks for non-busy events (such as user
277 keybutton presses and media change events) as well. The specification
278 states that the lower-numered events get reported first, so busy events,
279 while repeating, will only be reported when all other events have been
280 cleared from the device.
285 ClasspInterpretGesnData(
286 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
287 IN PNOTIFICATION_EVENT_STATUS_HEADER Header
,
288 IN PBOOLEAN ResendImmediately
291 PMEDIA_CHANGE_DETECTION_INFO info
;
295 info
= FdoExtension
->MediaChangeDetectionInfo
;
298 // note: don't allocate anything in this routine so that we can
299 // always just 'return'.
302 *ResendImmediately
= FALSE
;
307 if (Header
->NotificationClass
== NOTIFICATION_NO_CLASS_EVENTS
) {
312 // HACKHACK - REF #0001
313 // This loop is only taken initially, due to the inability to reliably
314 // auto-detect drives that report events correctly at boot. When we
315 // detect this behavior during the normal course of running, we will
316 // disable the hack, allowing more efficient use of the system. This
317 // should occur "nearly" instantly, as the drive should have multiple
318 // events queue'd (ie. power, morphing, media).
321 if (info
->Gesn
.HackEventMask
) {
324 // all events use the low four bytes of zero to indicate
325 // that there was no change in status.
328 UCHAR thisEvent
= Header
->ClassEventData
[0] & 0xf;
330 UCHAR thisEventBit
= (1 << Header
->NotificationClass
);
332 ASSERT(TEST_FLAG(info
->Gesn
.EventMask
, thisEventBit
));
336 // some bit magic here... this results in the lowest set bit only
339 lowestSetBit
= info
->Gesn
.EventMask
;
340 lowestSetBit
&= (info
->Gesn
.EventMask
- 1);
341 lowestSetBit
^= (info
->Gesn
.EventMask
);
343 if (thisEventBit
!= lowestSetBit
) {
346 // HACKHACK - REF #0001
347 // the first time we ever see an event set that is not the lowest
348 // set bit in the request (iow, highest priority), we know that the
349 // hack is no longer required, as the device is ignoring "no change"
350 // events when a real event is waiting in the other requested queues.
353 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
354 "Classpnp => GESN::NONE: Compliant drive found, "
355 "removing GESN hack (%x, %x)\n",
356 thisEventBit
, info
->Gesn
.EventMask
));
358 info
->Gesn
.HackEventMask
= FALSE
;
360 } else if (thisEvent
== 0) {
363 // HACKHACK - REF #0001
364 // note: this hack prevents poorly implemented firmware from constantly
365 // returning "No Event". we do this by cycling through the
366 // supported list of events here.
369 SET_FLAG(info
->Gesn
.NoChangeEventMask
, thisEventBit
);
370 CLEAR_FLAG(info
->Gesn
.EventMask
, thisEventBit
);
373 // if we have cycled through all supported event types, then
374 // we need to reset the events we are asking about. else we
375 // want to resend this request immediately in case there was
376 // another event pending.
379 if (info
->Gesn
.EventMask
== 0) {
380 info
->Gesn
.EventMask
= info
->Gesn
.NoChangeEventMask
;
381 info
->Gesn
.NoChangeEventMask
= 0;
383 *ResendImmediately
= TRUE
;
388 } // end if (info->Gesn.HackEventMask)
391 (Header
->EventDataLength
[0] << 8) |
392 (Header
->EventDataLength
[1] & 0xff);
394 requiredLength
= 4; // all events are four bytes
396 if (dataLength
< requiredLength
) {
397 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugWarning
,
398 "Classpnp => GESN returned only %x bytes data for fdo %p\n",
399 dataLength
, FdoExtension
->DeviceObject
));
402 if (dataLength
!= requiredLength
) {
403 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugWarning
,
404 "Classpnp => GESN returned too many (%x) bytes data for fdo %p\n",
405 dataLength
, FdoExtension
->DeviceObject
));
410 ClasspSendNotification(FdoExtension,
411 &GUID_IO_GENERIC_GESN_EVENT,
412 sizeof(NOTIFICATION_EVENT_STATUS_HEADER) + dataLength,
416 switch (Header
->NotificationClass
) {
418 case NOTIFICATION_EXTERNAL_REQUEST_CLASS_EVENTS
: { // 0x3
420 PNOTIFICATION_EXTERNAL_STATUS externalInfo
=
421 (PNOTIFICATION_EXTERNAL_STATUS
)(Header
->ClassEventData
);
422 DEVICE_EVENT_EXTERNAL_REQUEST externalData
;
425 // unfortunately, due to time constraints, we will only notify
426 // about keys being pressed, and not released. this makes keys
427 // single-function, but simplifies the code significantly.
430 if (externalInfo
->ExternalEvent
!=
431 NOTIFICATION_EXTERNAL_EVENT_BUTTON_DOWN
) {
435 *ResendImmediately
= TRUE
;
436 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
437 "Classpnp => GESN::EXTERNAL: Event: %x Status %x Req %x\n",
438 externalInfo
->ExternalEvent
, externalInfo
->ExternalStatus
,
439 (externalInfo
->Request
[0] << 8) | externalInfo
->Request
[1]
442 RtlZeroMemory(&externalData
, sizeof(DEVICE_EVENT_EXTERNAL_REQUEST
));
443 externalData
.Version
= 1;
444 externalData
.DeviceClass
= 0;
445 externalData
.ButtonStatus
= externalInfo
->ExternalEvent
;
446 externalData
.Request
=
447 (externalInfo
->Request
[0] << 8) |
448 (externalInfo
->Request
[1] & 0xff);
449 KeQuerySystemTime(&(externalData
.SystemTime
));
450 externalData
.SystemTime
.QuadPart
*= (LONGLONG
)KeQueryTimeIncrement();
452 DBGTRACE(ClassDebugTrace
, ("ClasspInterpretGesnData: media DEVICE_EXTERNAL_REQUEST"));
453 ClasspSendNotification(FdoExtension
,
454 &GUID_IO_DEVICE_EXTERNAL_REQUEST
,
455 sizeof(DEVICE_EVENT_EXTERNAL_REQUEST
),
460 case NOTIFICATION_MEDIA_STATUS_CLASS_EVENTS
: { // 0x4
462 PNOTIFICATION_MEDIA_STATUS mediaInfo
=
463 (PNOTIFICATION_MEDIA_STATUS
)(Header
->ClassEventData
);
465 if (mediaInfo
->MediaEvent
== NOTIFICATION_MEDIA_EVENT_NO_CHANGE
) {
469 *ResendImmediately
= TRUE
;
470 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
471 "Classpnp => GESN::MEDIA: Event: %x Status %x\n",
472 mediaInfo
->MediaEvent
, mediaInfo
->MediaStatus
));
474 if ((mediaInfo
->MediaEvent
== NOTIFICATION_MEDIA_EVENT_NEW_MEDIA
) ||
475 (mediaInfo
->MediaEvent
== NOTIFICATION_MEDIA_EVENT_MEDIA_CHANGE
)) {
478 if (TEST_FLAG(FdoExtension
->DeviceObject
->Characteristics
,
479 FILE_REMOVABLE_MEDIA
) &&
480 (ClassGetVpb(FdoExtension
->DeviceObject
) != NULL
) &&
481 (ClassGetVpb(FdoExtension
->DeviceObject
)->Flags
& VPB_MOUNTED
)
484 SET_FLAG(FdoExtension
->DeviceObject
->Flags
, DO_VERIFY_VOLUME
);
487 InterlockedIncrement((PLONG
)&FdoExtension
->MediaChangeCount
);
488 ClasspSetMediaChangeStateEx(FdoExtension
,
493 } else if (mediaInfo
->MediaEvent
== NOTIFICATION_MEDIA_EVENT_MEDIA_REMOVAL
) {
495 ClasspSetMediaChangeStateEx(FdoExtension
,
500 } else if (mediaInfo
->MediaEvent
== NOTIFICATION_MEDIA_EVENT_EJECT_REQUEST
) {
502 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugError
,
503 "Classpnp => GESN Ejection request received!\n"));
504 ClassSendEjectionNotification(FdoExtension
);
511 case NOTIFICATION_DEVICE_BUSY_CLASS_EVENTS
: { // lowest priority events...
513 PNOTIFICATION_BUSY_STATUS busyInfo
=
514 (PNOTIFICATION_BUSY_STATUS
)(Header
->ClassEventData
);
515 DEVICE_EVENT_BECOMING_READY busyData
;
518 // NOTE: we never actually need to immediately retry for these
519 // events: if one exists, the device is busy, and if not,
520 // we still don't want to retry.
523 if (busyInfo
->DeviceBusyStatus
== NOTIFICATION_BUSY_STATUS_NO_EVENT
) {
528 // else we want to report the approximated time till it's ready.
531 RtlZeroMemory(&busyData
, sizeof(DEVICE_EVENT_BECOMING_READY
));
532 busyData
.Version
= 1;
533 busyData
.Reason
= busyInfo
->DeviceBusyStatus
;
534 busyData
.Estimated100msToReady
= (busyInfo
->Time
[0] << 8) |
535 (busyInfo
->Time
[1] & 0xff);
537 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
538 "Classpnp => GESN::BUSY: Event: %x Status %x Time %x\n",
539 busyInfo
->DeviceBusyEvent
, busyInfo
->DeviceBusyStatus
,
540 busyData
.Estimated100msToReady
543 DBGTRACE(ClassDebugTrace
, ("ClasspInterpretGesnData: media BECOMING_READY"));
544 ClasspSendNotification(FdoExtension
,
545 &GUID_IO_DEVICE_BECOMING_READY
,
546 sizeof(DEVICE_EVENT_BECOMING_READY
),
557 } // end switch on notification class
561 /*++////////////////////////////////////////////////////////////////////////////
563 ClasspInternalSetMediaChangeState()
567 This routine will (if appropriate) set the media change event for the
568 device. The event will be set if the media state is changed and
569 media change events are enabled. Otherwise the media state will be
570 tracked but the event will not be set.
572 This routine will lock out the other media change routines if possible
573 but if not a media change notification may be lost after the enable has
578 FdoExtension - the device
580 MediaPresent - indicates whether the device has media inserted into it
581 (TRUE) or not (FALSE).
590 ClasspInternalSetMediaChangeState(
591 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
592 IN MEDIA_CHANGE_DETECTION_STATE NewState
,
593 IN BOOLEAN KnownStateChange
// can ignore oldstate == unknown
597 PCSTR states
[] = {"Unknown", "Present", "Not Present"};
599 MEDIA_CHANGE_DETECTION_STATE oldMediaState
;
600 PMEDIA_CHANGE_DETECTION_INFO info
= FdoExtension
->MediaChangeDetectionInfo
;
604 ASSERT((NewState
>= MediaUnknown
) && (NewState
<= MediaNotPresent
));
610 oldMediaState
= InterlockedExchange(
611 (PLONG
)(&info
->MediaChangeDetectionState
),
614 if((oldMediaState
== MediaUnknown
) && (!KnownStateChange
)) {
617 // The media was in an indeterminate state before - don't notify for
621 DebugPrint((ClassDebugMCN
,
622 "ClassSetMediaChangeState: State was unknown - this may "
623 "not be a change\n"));
626 } else if(oldMediaState
== NewState
) {
629 // Media is in the same state it was before.
635 if(info
->MediaChangeDetectionDisableCount
!= 0) {
637 DBGTRACE(ClassDebugMCN
,
638 ("ClassSetMediaChangeState: MCN not enabled, state "
639 "changed from %s to %s\n",
640 states
[oldMediaState
], states
[NewState
]));
645 DBGTRACE(ClassDebugMCN
,
646 ("ClassSetMediaChangeState: State change from %s to %s\n",
647 states
[oldMediaState
], states
[NewState
]));
650 // make the data useful -- it used to always be zero.
652 data
= FdoExtension
->MediaChangeCount
;
654 if (NewState
== MediaPresent
) {
656 DBGTRACE(ClassDebugTrace
, ("ClasspInternalSetMediaChangeState: media ARRIVAL"));
657 ClasspSendNotification(FdoExtension
,
658 &GUID_IO_MEDIA_ARRIVAL
,
663 else if (NewState
== MediaNotPresent
) {
665 DBGTRACE(ClassDebugTrace
, ("ClasspInternalSetMediaChangeState: media REMOVAL"));
666 ClasspSendNotification(FdoExtension
,
667 &GUID_IO_MEDIA_REMOVAL
,
674 // Don't notify of changed going to unknown.
681 } // end ClasspInternalSetMediaChangeState()
683 /*++////////////////////////////////////////////////////////////////////////////
685 ClassSetMediaChangeState()
689 This routine will (if appropriate) set the media change event for the
690 device. The event will be set if the media state is changed and
691 media change events are enabled. Otherwise the media state will be
692 tracked but the event will not be set.
694 This routine will lock out the other media change routines if possible
695 but if not a media change notification may be lost after the enable has
700 FdoExtension - the device
702 MediaPresent - indicates whether the device has media inserted into it
703 (TRUE) or not (FALSE).
705 Wait - indicates whether the function should wait until it can acquire
706 the synchronization lock or not.
715 ClasspSetMediaChangeStateEx(
716 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
717 IN MEDIA_CHANGE_DETECTION_STATE NewState
,
719 IN BOOLEAN KnownStateChange
// can ignore oldstate == unknown
722 PMEDIA_CHANGE_DETECTION_INFO info
= FdoExtension
->MediaChangeDetectionInfo
;
726 DBGTRACE(ClassDebugMCN
, ("> ClasspSetMediaChangeStateEx"));
729 // Reset SMART status on media removal as the old status may not be
730 // valid when there is no media in the device or when new media is
734 if (NewState
== MediaNotPresent
) {
736 FdoExtension
->FailurePredicted
= FALSE
;
737 FdoExtension
->FailureReason
= 0;
748 status
= KeWaitForMutexObject(&info
->MediaChangeMutex
,
752 ((Wait
== TRUE
) ? NULL
: &zero
));
754 if(status
== STATUS_TIMEOUT
) {
757 // Someone else is in the process of setting the media state
760 DBGWARN(("ClasspSetMediaChangeStateEx - timed out waiting for mutex"));
765 // Change the media present state and signal an event, if applicable
768 ClasspInternalSetMediaChangeState(FdoExtension
, NewState
, KnownStateChange
);
770 KeReleaseMutex(&info
->MediaChangeMutex
, FALSE
);
772 DBGTRACE(ClassDebugMCN
, ("< ClasspSetMediaChangeStateEx"));
775 } // end ClassSetMediaChangeStateEx()
779 ClassSetMediaChangeState(
780 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
781 IN MEDIA_CHANGE_DETECTION_STATE NewState
,
785 ClasspSetMediaChangeStateEx(FdoExtension
, NewState
, Wait
, FALSE
);
789 /*++////////////////////////////////////////////////////////////////////////////
791 ClasspMediaChangeDetectionCompletion()
795 This routine handles the completion of the test unit ready irps used to
796 determine if the media has changed. If the media has changed, this code
797 signals the named event to wake up other system services that react to
798 media change (aka AutoPlay).
802 DeviceObject - the object for the completion
803 Irp - the IRP being completed
804 Context - the SRB from the IRP
813 ClasspMediaChangeDetectionCompletion(
814 PDEVICE_OBJECT DeviceObject
,
819 PSCSI_REQUEST_BLOCK srb
= Context
;
820 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
;
821 PCLASS_PRIVATE_FDO_DATA fdoData
;
822 PMEDIA_CHANGE_DETECTION_INFO info
;
823 //PIO_STACK_LOCATION nextIrpStack;
825 BOOLEAN retryImmediately
= FALSE
;
828 // Since the class driver created this request, it's completion routine
829 // will not get a valid device object handed in. Use the one in the
833 DeviceObject
= IoGetCurrentIrpStackLocation(Irp
)->DeviceObject
;
834 fdoExtension
= DeviceObject
->DeviceExtension
;
835 fdoData
= fdoExtension
->PrivateFdoData
;
836 info
= fdoExtension
->MediaChangeDetectionInfo
;
838 ASSERT(info
->MediaChangeIrp
!= NULL
);
839 ASSERT(!TEST_FLAG(srb
->SrbStatus
, SRB_STATUS_QUEUE_FROZEN
));
840 DBGTRACE(ClassDebugMCN
, ("> ClasspMediaChangeDetectionCompletion: Device %p completed MCN irp %p.", DeviceObject
, Irp
));
843 * HACK for IoMega 2GB Jaz drive:
844 * This drive spins down on its own to preserve the media.
845 * When spun down, TUR fails with 2/4/0 (SCSI_SENSE_NOT_READY/SCSI_ADSENSE_LUN_NOT_READY/?).
846 * ClassInterpretSenseInfo would then call ClassSendStartUnit to spin the media up, which defeats the
847 * purpose of the spindown.
848 * So in this case, make this into a successful TUR.
849 * This allows the drive to stay spun down until it is actually accessed again.
850 * (If the media were actually removed, TUR would fail with 2/3a/0 ).
851 * This hack only applies to drives with the CAUSE_NOT_REPORTABLE_HACK bit set; this
852 * is set by disk.sys when HackCauseNotReportableHack is set for the drive in its BadControllers list.
854 if ((SRB_STATUS(srb
->SrbStatus
) != SRB_STATUS_SUCCESS
) &&
855 TEST_FLAG(fdoExtension
->ScanForSpecialFlags
, CLASS_SPECIAL_CAUSE_NOT_REPORTABLE_HACK
) &&
856 (srb
->SenseInfoBufferLength
>= RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA
, AdditionalSenseCode
))){
858 PSENSE_DATA senseData
= srb
->SenseInfoBuffer
;
860 if ((senseData
->SenseKey
== SCSI_SENSE_NOT_READY
) &&
861 (senseData
->AdditionalSenseCode
== SCSI_ADSENSE_LUN_NOT_READY
)){
862 srb
->SrbStatus
= SRB_STATUS_SUCCESS
;
868 // use ClassInterpretSenseInfo() to check for media state, and also
869 // to call ClassError() with correct parameters.
871 status
= STATUS_SUCCESS
;
872 if (SRB_STATUS(srb
->SrbStatus
) != SRB_STATUS_SUCCESS
) {
874 DBGTRACE(ClassDebugMCN
, ("ClasspMediaChangeDetectionCompletion - failed - srb status=%s, sense=%s/%s/%s.", DBGGETSRBSTATUSSTR(srb
), DBGGETSENSECODESTR(srb
), DBGGETADSENSECODESTR(srb
), DBGGETADSENSEQUALIFIERSTR(srb
)));
876 ClassInterpretSenseInfo(DeviceObject
,
887 fdoData
->LoggedTURFailureSinceLastIO
= FALSE
;
889 if (!info
->Gesn
.Supported
) {
891 DBGTRACE(ClassDebugMCN
, ("ClasspMediaChangeDetectionCompletion - succeeded and GESN NOT supported, setting MediaPresent."));
894 // success != media for GESN case
897 ClassSetMediaChangeState(fdoExtension
, MediaPresent
, FALSE
);
901 DBGTRACE(ClassDebugMCN
, ("ClasspMediaChangeDetectionCompletion - succeeded (GESN supported)."));
905 if (info
->Gesn
.Supported
) {
907 if (status
== STATUS_DATA_OVERRUN
) {
908 DBGTRACE(ClassDebugMCN
, ("ClasspMediaChangeDetectionCompletion - Overrun"));
909 status
= STATUS_SUCCESS
;
912 if (!NT_SUCCESS(status
)) {
913 DBGTRACE(ClassDebugMCN
, ("ClasspMediaChangeDetectionCompletion: GESN failed with status %x", status
));
917 // for GESN, need to interpret the results of the data.
918 // this may also require an immediate retry
921 if (Irp
->IoStatus
.Information
== 8 ) {
922 ClasspInterpretGesnData(fdoExtension
,
923 (PVOID
)info
->Gesn
.Buffer
,
927 } // end of NT_SUCCESS(status)
929 } // end of Info->Gesn.Supported
932 // free port-allocated sense buffer, if any.
935 if (PORT_ALLOCATED_SENSE(fdoExtension
, srb
)) {
936 FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension
, srb
);
940 // Remember the IRP and SRB for use the next time.
943 ASSERT(IoGetNextIrpStackLocation(Irp
));
944 IoGetNextIrpStackLocation(Irp
)->Parameters
.Scsi
.Srb
= srb
;
947 // Reset the MCN timer.
950 ClassResetMediaChangeTimer(fdoExtension
);
953 // run a sanity check to make sure we're not recursing continuously
956 if (retryImmediately
) {
958 info
->MediaChangeRetryCount
++;
959 if (info
->MediaChangeRetryCount
> MAXIMUM_IMMEDIATE_MCN_RETRIES
) {
960 ASSERT(!"Recursing too often in MCN?");
961 info
->MediaChangeRetryCount
= 0;
962 retryImmediately
= FALSE
;
967 info
->MediaChangeRetryCount
= 0;
973 // release the remove lock....
978 ClassAcquireRemoveLock(DeviceObject
, (PIRP
)(&uniqueValue
));
979 ClassReleaseRemoveLock(DeviceObject
, Irp
);
983 // set the irp as not in use
986 volatile LONG irpWasInUse
;
987 irpWasInUse
= InterlockedCompareExchange(&info
->MediaChangeIrpInUse
, 0, 1);
988 #if _MSC_FULL_VER != 13009111 // This compiler always takes the wrong path here.
994 // now send it again before we release our last remove lock
997 if (retryImmediately
) {
998 ClasspSendMediaStateIrp(fdoExtension
, info
, 0);
1001 DBGTRACE(ClassDebugMCN
, ("ClasspMediaChangeDetectionCompletion - not retrying immediately"));
1005 // release the temporary remove lock
1008 ClassReleaseRemoveLock(DeviceObject
, (PIRP
)(&uniqueValue
));
1011 DBGTRACE(ClassDebugMCN
, ("< ClasspMediaChangeDetectionCompletion"));
1013 return STATUS_MORE_PROCESSING_REQUIRED
;
1016 /*++////////////////////////////////////////////////////////////////////////////
1018 ClasspSendTestUnitIrp() - ISSUE-2000/02/20-henrygab - not documented
1020 Routine Description:
1035 ClasspPrepareMcnIrp(
1036 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
1037 IN PMEDIA_CHANGE_DETECTION_INFO Info
,
1041 PSCSI_REQUEST_BLOCK srb
;
1042 PIO_STACK_LOCATION irpStack
;
1043 PIO_STACK_LOCATION nextIrpStack
;
1050 // Setup the IRP to perform a test unit ready.
1053 irp
= Info
->MediaChangeIrp
;
1062 // don't keep sending this if the device is being removed.
1065 status
= ClassAcquireRemoveLock(FdoExtension
->DeviceObject
, irp
);
1066 if (status
== REMOVE_COMPLETE
) {
1067 ASSERT(status
!= REMOVE_COMPLETE
);
1070 else if (status
== REMOVE_PENDING
) {
1071 ClassReleaseRemoveLock(FdoExtension
->DeviceObject
, irp
);
1075 ASSERT(status
== NO_REMOVE
);
1078 irp
->IoStatus
.Status
= STATUS_SUCCESS
;
1079 irp
->IoStatus
.Information
= 0;
1081 irp
->UserBuffer
= NULL
;
1084 // If the irp is sent down when the volume needs to be
1085 // verified, CdRomUpdateGeometryCompletion won't complete
1086 // it since it's not associated with a thread. Marking
1087 // it to override the verify causes it always be sent
1088 // to the port driver
1091 irpStack
= IoGetCurrentIrpStackLocation(irp
);
1092 irpStack
->Flags
|= SL_OVERRIDE_VERIFY_VOLUME
;
1094 nextIrpStack
= IoGetNextIrpStackLocation(irp
);
1095 nextIrpStack
->MajorFunction
= IRP_MJ_INTERNAL_DEVICE_CONTROL
;
1096 nextIrpStack
->Parameters
.Scsi
.Srb
= &(Info
->MediaChangeSrb
);
1099 // Prepare the SRB for execution.
1102 srb
= nextIrpStack
->Parameters
.Scsi
.Srb
;
1103 buffer
= srb
->SenseInfoBuffer
;
1104 RtlZeroMemory(srb
, sizeof(SCSI_REQUEST_BLOCK
));
1105 RtlZeroMemory(buffer
, SENSE_BUFFER_SIZE
);
1108 srb
->QueueTag
= SP_UNTAGGED
;
1109 srb
->QueueAction
= SRB_SIMPLE_TAG_REQUEST
;
1110 srb
->Length
= sizeof(SCSI_REQUEST_BLOCK
);
1111 srb
->Function
= SRB_FUNCTION_EXECUTE_SCSI
;
1112 srb
->SenseInfoBuffer
= buffer
;
1114 srb
->ScsiStatus
= 0;
1115 srb
->OriginalRequest
= irp
;
1116 srb
->SenseInfoBufferLength
= SENSE_BUFFER_SIZE
;
1118 srb
->SrbFlags
= FdoExtension
->SrbFlags
;
1119 SET_FLAG(srb
->SrbFlags
, Info
->SrbFlags
);
1121 srb
->TimeOutValue
= FdoExtension
->TimeOutValue
* 2;
1123 if (srb
->TimeOutValue
== 0) {
1125 if (FdoExtension
->TimeOutValue
== 0) {
1127 KdPrintEx((DPFLTR_CLASSPNP_ID
, DPFLTR_ERROR_LEVEL
,
1128 "ClassSendTestUnitIrp: FdoExtension->TimeOutValue "
1129 "is set to zero?! -- resetting to 10\n"));
1130 srb
->TimeOutValue
= 10 * 2; // reasonable default
1134 KdPrintEx((DPFLTR_CLASSPNP_ID
, DPFLTR_ERROR_LEVEL
,
1135 "ClassSendTestUnitIrp: Someone set "
1136 "srb->TimeOutValue to zero?! -- resetting to %x\n",
1137 FdoExtension
->TimeOutValue
* 2));
1138 srb
->TimeOutValue
= FdoExtension
->TimeOutValue
* 2;
1147 srb
->DataTransferLength
= 0;
1148 SET_FLAG(srb
->SrbFlags
, SRB_FLAGS_NO_DATA_TRANSFER
);
1149 nextIrpStack
->Parameters
.DeviceIoControl
.IoControlCode
=
1150 IOCTL_SCSI_EXECUTE_NONE
;
1151 srb
->DataBuffer
= NULL
;
1152 srb
->DataTransferLength
= 0;
1153 irp
->MdlAddress
= NULL
;
1155 cdb
= (PCDB
) &srb
->Cdb
[0];
1156 cdb
->CDB6GENERIC
.OperationCode
= SCSIOP_TEST_UNIT_READY
;
1160 ASSERT(Info
->Gesn
.Buffer
);
1162 srb
->TimeOutValue
= GESN_TIMEOUT_VALUE
; // much shorter timeout for GESN
1164 srb
->CdbLength
= 10;
1165 SET_FLAG(srb
->SrbFlags
, SRB_FLAGS_DATA_IN
);
1166 nextIrpStack
->Parameters
.DeviceIoControl
.IoControlCode
=
1167 IOCTL_SCSI_EXECUTE_IN
;
1168 srb
->DataBuffer
= Info
->Gesn
.Buffer
;
1169 srb
->DataTransferLength
= Info
->Gesn
.BufferSize
;
1170 irp
->MdlAddress
= Info
->Gesn
.Mdl
;
1172 cdb
= (PCDB
) &srb
->Cdb
[0];
1173 cdb
->GET_EVENT_STATUS_NOTIFICATION
.OperationCode
=
1174 SCSIOP_GET_EVENT_STATUS
;
1175 cdb
->GET_EVENT_STATUS_NOTIFICATION
.Immediate
= 1;
1176 cdb
->GET_EVENT_STATUS_NOTIFICATION
.EventListLength
[0] =
1177 (UCHAR
)((Info
->Gesn
.BufferSize
) >> 8);
1178 cdb
->GET_EVENT_STATUS_NOTIFICATION
.EventListLength
[1] =
1179 (UCHAR
)((Info
->Gesn
.BufferSize
) & 0xff);
1180 cdb
->GET_EVENT_STATUS_NOTIFICATION
.NotificationClassRequest
=
1181 Info
->Gesn
.EventMask
;
1185 IoSetCompletionRoutine(irp
,
1186 ClasspMediaChangeDetectionCompletion
,
1196 /*++////////////////////////////////////////////////////////////////////////////
1198 ClasspSendMediaStateIrp() - ISSUE-2000/02/20-henrygab - not documented
1200 Routine Description:
1214 ClasspSendMediaStateIrp(
1215 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
1216 IN PMEDIA_CHANGE_DETECTION_INFO Info
,
1220 BOOLEAN requestPending
= FALSE
;
1222 //LARGE_INTEGER zero;
1225 DBGTRACE(ClassDebugMCN
, ("> ClasspSendMediaStateIrp"));
1227 if (((FdoExtension
->CommonExtension
.CurrentState
!= IRP_MN_START_DEVICE
) ||
1228 (FdoExtension
->DevicePowerState
!= PowerDeviceD0
)
1230 (!Info
->MediaChangeIrpLost
)) {
1233 // the device may be stopped, powered down, or otherwise queueing io,
1234 // so should not timeout the autorun irp (yet) -- set to zero ticks.
1235 // scattered code relies upon this to not prematurely "lose" an
1236 // autoplay irp that was queued.
1239 Info
->MediaChangeIrpTimeInUse
= 0;
1243 // if the irp is not in use, mark it as such.
1246 irpInUse
= InterlockedCompareExchange(&Info
->MediaChangeIrpInUse
, 1, 0);
1252 timeInUse
= InterlockedIncrement(&Info
->MediaChangeIrpTimeInUse
);
1254 DebugPrint((ClassDebugMCN
, "ClasspSendMediaStateIrp: irp in use for "
1255 "%x seconds when synchronizing for MCD\n", timeInUse
));
1257 if (Info
->MediaChangeIrpLost
== FALSE
) {
1259 if (timeInUse
> MEDIA_CHANGE_TIMEOUT_TIME
) {
1262 // currently set to five minutes. hard to imagine a drive
1263 // taking that long to spin up.
1266 DebugPrint((ClassDebugError
,
1267 "CdRom%d: Media Change Notification has lost "
1268 "it's irp and doesn't know where to find it. "
1269 "Leave it alone and it'll come home dragging "
1270 "it's stack behind it.\n",
1271 FdoExtension
->DeviceNumber
));
1272 Info
->MediaChangeIrpLost
= TRUE
;
1276 DBGTRACE(ClassDebugMCN
, ("< ClasspSendMediaStateIrp - irpInUse"));
1283 if (Info
->MediaChangeDetectionDisableCount
!= 0) {
1284 DebugPrint((ClassDebugTrace
, "ClassCheckMediaState: device %p has "
1285 " detection disabled \n", FdoExtension
->DeviceObject
));
1289 if (FdoExtension
->DevicePowerState
!= PowerDeviceD0
) {
1291 if (TEST_FLAG(Info
->SrbFlags
, SRB_FLAGS_NO_KEEP_AWAKE
)) {
1292 DebugPrint((ClassDebugMCN
,
1293 "ClassCheckMediaState: device %p is powered "
1294 "down and flags are set to let it sleep\n",
1295 FdoExtension
->DeviceObject
));
1296 ClassResetMediaChangeTimer(FdoExtension
);
1301 // NOTE: we don't increment the time in use until our power state
1302 // changes above. this way, we won't "lose" the autoplay irp.
1303 // it's up to the lower driver to determine if powering up is a
1307 DebugPrint((ClassDebugMCN
,
1308 "ClassCheckMediaState: device %p needs to powerup "
1309 "to handle this io (may take a few extra seconds).\n",
1310 FdoExtension
->DeviceObject
));
1314 Info
->MediaChangeIrpTimeInUse
= 0;
1315 Info
->MediaChangeIrpLost
= FALSE
;
1317 if (CountDown
== 0) {
1321 DebugPrint((ClassDebugTrace
,
1322 "ClassCheckMediaState: timer expired\n"));
1324 if (Info
->MediaChangeDetectionDisableCount
!= 0) {
1325 DebugPrint((ClassDebugTrace
,
1326 "ClassCheckMediaState: detection disabled\n"));
1331 // Prepare the IRP for the test unit ready
1334 irp
= ClasspPrepareMcnIrp(FdoExtension
,
1336 Info
->Gesn
.Supported
);
1339 // Issue the request.
1342 DebugPrint((ClassDebugTrace
,
1343 "ClasspSendMediaStateIrp: Device %p getting TUR "
1344 " irp %p\n", FdoExtension
->DeviceObject
, irp
));
1352 // note: if we send it to the class dispatch routines, there is
1353 // a timing window here (since they grab the remove lock)
1354 // where we'd be removed. ELIMINATE the window by grabbing
1355 // the lock ourselves above and sending it to the lower
1356 // device object directly or to the device's StartIo
1357 // routine (which doesn't acquire the lock).
1360 requestPending
= TRUE
;
1362 DBGTRACE(ClassDebugMCN
, (" ClasspSendMediaStateIrp - calling IoCallDriver."));
1363 IoCallDriver(FdoExtension
->CommonExtension
.LowerDeviceObject
, irp
);
1368 if(requestPending
== FALSE
) {
1369 irpInUse
= InterlockedCompareExchange(&Info
->MediaChangeIrpInUse
, 0, 1);
1370 #if _MSC_FULL_VER != 13009111 // This compiler always takes the wrong path here.
1377 DBGTRACE(ClassDebugMCN
, ("< ClasspSendMediaStateIrp"));
1380 } // end ClasspSendMediaStateIrp()
1382 /*++////////////////////////////////////////////////////////////////////////////
1384 ClassCheckMediaState()
1386 Routine Description:
1388 This routine is called by the class driver to test for a media change
1389 condition and/or poll for disk failure prediction. It should be called
1390 from the class driver's IO timer routine once per second.
1394 FdoExtension - the device extension
1403 ClassCheckMediaState(
1404 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
1407 PMEDIA_CHANGE_DETECTION_INFO info
= FdoExtension
->MediaChangeDetectionInfo
;
1411 DebugPrint((ClassDebugTrace
,
1412 "ClassCheckMediaState: detection not enabled\n"));
1417 // Media change support is active and the IRP is waiting. Decrement the
1418 // timer. There is no MP protection on the timer counter. This code
1419 // is the only code that will manipulate the timer counter and only one
1420 // instance of it should be running at any given time.
1423 countDown
= InterlockedDecrement(&(info
->MediaChangeCountDown
));
1426 // Try to acquire the media change event. If we can't do it immediately
1427 // then bail out and assume the caller will try again later.
1429 ClasspSendMediaStateIrp(FdoExtension
,
1434 } // end ClassCheckMediaState()
1436 /*++////////////////////////////////////////////////////////////////////////////
1438 ClassResetMediaChangeTimer()
1440 Routine Description:
1442 Resets the media change count down timer to the default number of seconds.
1446 FdoExtension - the device to reset the timer for
1455 ClassResetMediaChangeTimer(
1456 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
1459 PMEDIA_CHANGE_DETECTION_INFO info
= FdoExtension
->MediaChangeDetectionInfo
;
1462 InterlockedExchange(&(info
->MediaChangeCountDown
),
1463 MEDIA_CHANGE_DEFAULT_TIME
);
1466 } // end ClassResetMediaChangeTimer()
1468 /*++////////////////////////////////////////////////////////////////////////////
1470 ClasspInitializePolling() - ISSUE-2000/02/20-henrygab - not documented
1472 Routine Description:
1486 ClasspInitializePolling(
1487 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
1488 IN BOOLEAN AllowDriveToSleep
1491 PDEVICE_OBJECT fdo
= FdoExtension
->DeviceObject
;
1492 //PCLASS_PRIVATE_FDO_DATA fdoData = FdoExtension->PrivateFdoData;
1495 PMEDIA_CHANGE_DETECTION_INFO info
;
1500 if (FdoExtension
->MediaChangeDetectionInfo
!= NULL
) {
1501 return STATUS_SUCCESS
;
1504 info
= ExAllocatePoolWithTag(NonPagedPool
,
1505 sizeof(MEDIA_CHANGE_DETECTION_INFO
),
1506 CLASS_TAG_MEDIA_CHANGE_DETECTION
);
1509 RtlZeroMemory(info
, sizeof(MEDIA_CHANGE_DETECTION_INFO
));
1511 FdoExtension
->KernelModeMcnContext
.FileObject
= (PVOID
)-1;
1512 FdoExtension
->KernelModeMcnContext
.DeviceObject
= (PVOID
)-1;
1513 FdoExtension
->KernelModeMcnContext
.LockCount
= 0;
1514 FdoExtension
->KernelModeMcnContext
.McnDisableCount
= 0;
1517 * Allocate an IRP to carry the Test-Unit-Ready.
1518 * Allocate an extra IRP stack location
1519 * so we can cache our device object in the top location.
1521 irp
= IoAllocateIrp((CCHAR
)(fdo
->StackSize
+1), FALSE
);
1527 buffer
= ExAllocatePoolWithTag(
1528 NonPagedPoolCacheAligned
,
1530 CLASS_TAG_MEDIA_CHANGE_DETECTION
);
1532 if (buffer
!= NULL
) {
1533 PIO_STACK_LOCATION irpStack
;
1534 PSCSI_REQUEST_BLOCK srb
;
1537 srb
= &(info
->MediaChangeSrb
);
1538 info
->MediaChangeIrp
= irp
;
1539 info
->SenseBuffer
= buffer
;
1542 * For the driver that creates an IRP, there is no 'current' stack location.
1543 * Step down one IRP stack location so that the extra top one
1544 * becomes our 'current' one.
1546 IoSetNextIrpStackLocation(irp
);
1549 * Cache our device object in the extra top IRP stack location
1550 * so we have it in our completion routine.
1552 irpStack
= IoGetCurrentIrpStackLocation(irp
);
1553 irpStack
->DeviceObject
= fdo
;
1556 * Now start setting up the next IRP stack location for the call like any driver would.
1558 irpStack
= IoGetNextIrpStackLocation(irp
);
1559 irpStack
->Parameters
.Scsi
.Srb
= srb
;
1560 info
->MediaChangeIrp
= irp
;
1563 // Initialize the SRB
1566 RtlZeroMemory(srb
, sizeof(SCSI_REQUEST_BLOCK
));
1569 // Initialize and set up the sense information buffer
1572 RtlZeroMemory(buffer
, SENSE_BUFFER_SIZE
);
1573 srb
->SenseInfoBuffer
= buffer
;
1574 srb
->SenseInfoBufferLength
= SENSE_BUFFER_SIZE
;
1577 // Set default values for the media change notification
1581 info
->MediaChangeCountDown
= MEDIA_CHANGE_DEFAULT_TIME
;
1582 info
->MediaChangeDetectionDisableCount
= 0;
1585 // Assume that there is initially no media in the device
1586 // only notify upper layers if there is something there
1589 info
->MediaChangeDetectionState
= MediaUnknown
;
1591 info
->MediaChangeIrpTimeInUse
= 0;
1592 info
->MediaChangeIrpLost
= FALSE
;
1595 // setup all extra flags we'll be setting for this irp
1598 if (AllowDriveToSleep
) {
1599 SET_FLAG(info
->SrbFlags
, SRB_FLAGS_NO_KEEP_AWAKE
);
1601 SET_FLAG(info
->SrbFlags
, SRB_CLASS_FLAGS_LOW_PRIORITY
);
1602 SET_FLAG(info
->SrbFlags
, SRB_FLAGS_NO_QUEUE_FREEZE
);
1603 SET_FLAG(info
->SrbFlags
, SRB_FLAGS_DISABLE_SYNCH_TRANSFER
);
1605 KeInitializeMutex(&info
->MediaChangeMutex
, 0x100);
1608 // It is ok to support media change events on this
1612 FdoExtension
->MediaChangeDetectionInfo
= info
;
1615 // NOTE: the DeviceType is FILE_DEVICE_CD_ROM even
1616 // when the device supports DVD (no need to
1617 // check for FILE_DEVICE_DVD, as it's not a
1621 if (FdoExtension
->DeviceObject
->DeviceType
== FILE_DEVICE_CD_ROM
){
1625 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
1626 "ClasspInitializePolling: Testing for GESN\n"));
1627 status
= ClasspInitializeGesn(FdoExtension
, info
);
1628 if (NT_SUCCESS(status
)) {
1629 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
1630 "ClasspInitializePolling: GESN available "
1631 "for %p\n", FdoExtension
->DeviceObject
));
1632 ASSERT(info
->Gesn
.Supported
);
1633 ASSERT(info
->Gesn
.Buffer
!= NULL
);
1634 ASSERT(info
->Gesn
.BufferSize
!= 0);
1635 ASSERT(info
->Gesn
.EventMask
!= 0);
1636 // must return here, for ASSERTs to be valid.
1637 return STATUS_SUCCESS
;
1639 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
1640 "ClasspInitializePolling: GESN *NOT* available "
1641 "for %p\n", FdoExtension
->DeviceObject
));
1644 ASSERT(info
->Gesn
.Supported
== 0);
1645 ASSERT(info
->Gesn
.Buffer
== NULL
);
1646 ASSERT(info
->Gesn
.BufferSize
== 0);
1647 ASSERT(info
->Gesn
.EventMask
== 0);
1648 info
->Gesn
.Supported
= 0; // just in case....
1649 return STATUS_SUCCESS
;
1659 // nothing to free here
1661 return STATUS_INSUFFICIENT_RESOURCES
;
1663 } // end ClasspInitializePolling()
1667 ClasspInitializeGesn(
1668 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
1669 IN PMEDIA_CHANGE_DETECTION_INFO Info
1672 PNOTIFICATION_EVENT_STATUS_HEADER header
;
1673 CLASS_DETECTION_STATE detectionState
= ClassDetectionUnknown
;
1674 PSTORAGE_ADAPTER_DESCRIPTOR adapterDescriptor
;
1675 NTSTATUS status
= STATUS_NOT_SUPPORTED
;
1678 BOOLEAN retryImmediately
;
1684 ASSERT(Info
== FdoExtension
->MediaChangeDetectionInfo
);
1687 // read if we already know the abilities of the device
1690 ClassGetDeviceParameter(FdoExtension
,
1691 CLASSP_REG_SUBKEY_NAME
,
1692 CLASSP_REG_MMC_DETECTION_VALUE_NAME
,
1693 (PULONG
)&detectionState
);
1695 if (detectionState
== ClassDetectionUnsupported
) {
1700 // check if the device has a hack flag saying never to try this.
1703 if (TEST_FLAG(FdoExtension
->PrivateFdoData
->HackFlags
,
1704 FDO_HACK_GESN_IS_BAD
)) {
1706 detectionState
= ClassDetectionUnsupported
;
1707 ClassSetDeviceParameter(FdoExtension
,
1708 CLASSP_REG_SUBKEY_NAME
,
1709 CLASSP_REG_MMC_DETECTION_VALUE_NAME
,
1710 ClassDetectionSupported
);
1717 // else go through the process since we allocate buffers and
1718 // get all sorts of device settings.
1721 if (Info
->Gesn
.Buffer
== NULL
) {
1722 Info
->Gesn
.Buffer
= ExAllocatePoolWithTag(NonPagedPoolCacheAligned
,
1726 if (Info
->Gesn
.Buffer
== NULL
) {
1727 status
= STATUS_INSUFFICIENT_RESOURCES
;
1730 if (Info
->Gesn
.Mdl
!= NULL
) {
1731 IoFreeMdl(Info
->Gesn
.Mdl
);
1733 Info
->Gesn
.Mdl
= IoAllocateMdl(Info
->Gesn
.Buffer
,
1735 FALSE
, FALSE
, NULL
);
1736 if (Info
->Gesn
.Mdl
== NULL
) {
1737 status
= STATUS_INSUFFICIENT_RESOURCES
;
1741 MmBuildMdlForNonPagedPool(Info
->Gesn
.Mdl
);
1742 Info
->Gesn
.BufferSize
= GESN_BUFFER_SIZE
;
1743 Info
->Gesn
.EventMask
= 0;
1746 // all items are prepared to use GESN (except the event mask, so don't
1747 // optimize this part out!).
1749 // now see if it really works. we have to loop through this because
1750 // many SAMSUNG (and one COMPAQ) drives timeout when requesting
1751 // NOT_READY events, even when the IMMEDIATE bit is set. :(
1753 // using a drive list is cumbersome, so this might fix the problem.
1756 adapterDescriptor
= FdoExtension
->AdapterDescriptor
;
1758 retryImmediately
= TRUE
;
1759 for (i
= 0; i
< 16 && retryImmediately
== TRUE
; i
++) {
1761 irp
= ClasspPrepareMcnIrp(FdoExtension
, Info
, TRUE
);
1763 status
= STATUS_INSUFFICIENT_RESOURCES
;
1767 ASSERT(TEST_FLAG(Info
->MediaChangeSrb
.SrbFlags
, SRB_FLAGS_NO_QUEUE_FREEZE
));
1770 // replace the completion routine with a different one this time...
1773 IoSetCompletionRoutine(irp
,
1774 ClassSignalCompletion
,
1777 KeInitializeEvent(&event
, SynchronizationEvent
, FALSE
);
1779 status
= IoCallDriver(FdoExtension
->CommonExtension
.LowerDeviceObject
, irp
);
1781 if (status
== STATUS_PENDING
) {
1782 status
= KeWaitForSingleObject(&event
,
1787 ASSERT(NT_SUCCESS(status
));
1789 ClassReleaseRemoveLock(FdoExtension
->DeviceObject
, irp
);
1791 if (SRB_STATUS(Info
->MediaChangeSrb
.SrbStatus
) != SRB_STATUS_SUCCESS
) {
1792 ClassInterpretSenseInfo(FdoExtension
->DeviceObject
,
1793 &(Info
->MediaChangeSrb
),
1801 if ((adapterDescriptor
->BusType
== BusTypeAtapi
) &&
1802 (Info
->MediaChangeSrb
.SrbStatus
== SRB_STATUS_BUS_RESET
)
1806 // ATAPI unfortunately returns SRB_STATUS_BUS_RESET instead
1807 // of SRB_STATUS_TIMEOUT, so we cannot differentiate between
1808 // the two. if we get this status four time consecutively,
1809 // stop trying this command. it is too late to change ATAPI
1810 // at this point, so special-case this here. (07/10/2001)
1811 // NOTE: any value more than 4 may cause the device to be
1816 if (atapiResets
>= 4) {
1817 status
= STATUS_IO_DEVICE_ERROR
;
1822 if (status
== STATUS_DATA_OVERRUN
) {
1823 status
= STATUS_SUCCESS
;
1826 if ((status
== STATUS_INVALID_DEVICE_REQUEST
) ||
1827 (status
== STATUS_TIMEOUT
) ||
1828 (status
== STATUS_IO_DEVICE_ERROR
) ||
1829 (status
== STATUS_IO_TIMEOUT
)
1833 // with these error codes, we don't ever want to try this command
1834 // again on this device, since it reacts poorly.
1837 ClassSetDeviceParameter(FdoExtension
,
1838 CLASSP_REG_SUBKEY_NAME
,
1839 CLASSP_REG_MMC_DETECTION_VALUE_NAME
,
1840 ClassDetectionUnsupported
);
1841 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugWarning
,
1842 "Classpnp => GESN test failed %x for fdo %p\n",
1843 status
, FdoExtension
->DeviceObject
));
1849 if (!NT_SUCCESS(status
)) {
1852 // this may be other errors that should not disable GESN
1853 // for all future start_device calls.
1856 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugWarning
,
1857 "Classpnp => GESN test failed %x for fdo %p\n",
1858 status
, FdoExtension
->DeviceObject
));
1865 // the first time, the request was just retrieving a mask of
1866 // available bits. use this to mask future requests.
1869 header
= (PNOTIFICATION_EVENT_STATUS_HEADER
)(Info
->Gesn
.Buffer
);
1871 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
1872 "Classpnp => Fdo %p supports event mask %x\n",
1873 FdoExtension
->DeviceObject
, header
->SupportedEventClasses
));
1876 if (TEST_FLAG(header
->SupportedEventClasses
,
1877 NOTIFICATION_MEDIA_STATUS_CLASS_MASK
)) {
1878 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
1879 "Classpnp => GESN supports MCN\n"));
1881 if (TEST_FLAG(header
->SupportedEventClasses
,
1882 NOTIFICATION_DEVICE_BUSY_CLASS_MASK
)) {
1883 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
1884 "Classpnp => GESN supports DeviceBusy\n"));
1886 Info
->Gesn
.EventMask
= header
->SupportedEventClasses
;
1889 // realistically, we are only considering the following events:
1890 // EXTERNAL REQUEST - this is being tested for play/stop/etc.
1891 // MEDIA STATUS - autorun and ejection requests.
1892 // DEVICE BUSY - to allow us to predict when media will be ready.
1893 // therefore, we should not bother querying for the other,
1894 // unknown events. clear all but the above flags.
1897 Info
->Gesn
.EventMask
&=
1898 NOTIFICATION_EXTERNAL_REQUEST_CLASS_MASK
|
1899 NOTIFICATION_MEDIA_STATUS_CLASS_MASK
|
1900 NOTIFICATION_DEVICE_BUSY_CLASS_MASK
;
1904 // HACKHACK - REF #0001
1905 // Some devices will *never* report an event if we've also requested
1906 // that it report lower-priority events. this is due to a
1907 // misunderstanding in the specification wherein a "No Change" is
1908 // interpreted to be a real event. what should occur is that the
1909 // device should ignore "No Change" events when multiple event types
1910 // are requested unless there are no other events waiting. this
1911 // greatly reduces the number of requests that the host must send
1912 // to determine if an event has occurred. Since we must work on all
1913 // drives, default to enabling the hack until we find evidence of
1917 if (CountOfSetBitsUChar(Info
->Gesn
.EventMask
) == 1) {
1918 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
1919 "Classpnp => GESN hack %s for FDO %p\n",
1920 "not required", FdoExtension
->DeviceObject
));
1922 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
1923 "Classpnp => GESN hack %s for FDO %p\n",
1924 "enabled", FdoExtension
->DeviceObject
));
1925 Info
->Gesn
.HackEventMask
= 1;
1931 // not the first time looping through, so interpret the results.
1934 ClasspInterpretGesnData(FdoExtension
,
1935 (PVOID
)Info
->Gesn
.Buffer
,
1940 } // end loop of GESN requests....
1943 // we can only use this if it can be relied upon for media changes,
1944 // since we are (by definition) no longer going to be polling via
1945 // a TEST_UNIT_READY irp, and drives will not report UNIT ATTENTION
1946 // for this command (although a filter driver, such as one for burning
1947 // cd's, might still fake those errors).
1949 // since we also rely upon NOT_READY events to change the cursor
1950 // into a "wait" cursor, we can't use GESN without NOT_READY support.
1953 if (TEST_FLAG(Info
->Gesn
.EventMask
,
1954 NOTIFICATION_MEDIA_STATUS_CLASS_MASK
) &&
1955 TEST_FLAG(Info
->Gesn
.EventMask
,
1956 NOTIFICATION_DEVICE_BUSY_CLASS_MASK
)
1959 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
1960 "Classpnp => Enabling GESN support for fdo %p\n",
1961 FdoExtension
->DeviceObject
));
1962 Info
->Gesn
.Supported
= TRUE
;
1964 ClassSetDeviceParameter(FdoExtension
,
1965 CLASSP_REG_SUBKEY_NAME
,
1966 CLASSP_REG_MMC_DETECTION_VALUE_NAME
,
1967 ClassDetectionSupported
);
1969 return STATUS_SUCCESS
;
1973 KdPrintEx((DPFLTR_CLASSPNP_ID
, ClassDebugMCN
,
1974 "Classpnp => GESN available but not enabled for fdo %p\n",
1975 FdoExtension
->DeviceObject
));
1981 if (Info
->Gesn
.Mdl
) {
1982 IoFreeMdl(Info
->Gesn
.Mdl
);
1983 Info
->Gesn
.Mdl
= NULL
;
1985 if (Info
->Gesn
.Buffer
) {
1986 ExFreePool(Info
->Gesn
.Buffer
);
1987 Info
->Gesn
.Buffer
= NULL
;
1989 Info
->Gesn
.Supported
= 0;
1990 Info
->Gesn
.EventMask
= 0;
1991 Info
->Gesn
.BufferSize
= 0;
1992 return STATUS_NOT_SUPPORTED
;
1996 /*++////////////////////////////////////////////////////////////////////////////
1998 ClassInitializeTestUnitPolling()
2000 Routine Description:
2002 This routine will initialize MCN regardless of the settings stored
2003 in the registry. This should be used with caution, as some devices
2004 react badly to constant io. (i.e. never spin down, continuously cycling
2005 media in changers, ejection of media, etc.) It is highly suggested to
2006 use ClassInitializeMediaChangeDetection() instead.
2010 FdoExtension is the device to poll
2012 AllowDriveToSleep says whether to attempt to allow the drive to sleep
2013 or not. This only affects system-known spin down states, so if a
2014 drive spins itself down, this has no effect until the system spins
2022 ClassInitializeTestUnitPolling(
2023 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
2024 IN BOOLEAN AllowDriveToSleep
2027 return ClasspInitializePolling(FdoExtension
, AllowDriveToSleep
);
2028 } // end ClassInitializeTestUnitPolling()
2030 /*++////////////////////////////////////////////////////////////////////////////
2032 ClassInitializeMediaChangeDetection()
2034 Routine Description:
2036 This routine checks to see if it is safe to initialize MCN (the back end
2037 to autorun) for a given device. It will then check the device-type wide
2038 key "Autorun" in the service key (for legacy reasons), and then look in
2039 the device-specific key to potentially override that setting.
2041 If MCN is to be enabled, all neccessary structures and memory are
2042 allocated and initialized.
2044 This routine MUST be called only from the ClassInit() callback.
2048 FdoExtension - the device to initialize MCN for, if appropriate
2050 EventPrefix - unused, legacy argument. Set to zero.
2057 ClassInitializeMediaChangeDetection(
2058 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
2059 IN PUCHAR EventPrefix
2062 PDEVICE_OBJECT fdo
= FdoExtension
->DeviceObject
;
2065 PCLASS_DRIVER_EXTENSION driverExtension
= ClassGetDriverExtension(
2068 BOOLEAN disabledForBadHardware
;
2070 BOOLEAN instanceOverride
;
2075 // NOTE: This assumes that ClassInitializeMediaChangeDetection is always
2076 // called in the context of the ClassInitDevice callback. If called
2077 // after then this check will have already been made and the
2078 // once a second timer will not have been enabled.
2081 disabledForBadHardware
= ClasspIsMediaChangeDisabledDueToHardwareLimitation(
2083 &(driverExtension
->RegistryPath
)
2086 if (disabledForBadHardware
) {
2087 DebugPrint((ClassDebugMCN
,
2088 "ClassInitializeMCN: Disabled due to hardware"
2089 "limitations for this device"));
2094 // autorun should now be enabled by default for all media types.
2097 disabled
= ClasspIsMediaChangeDisabledForClass(
2099 &(driverExtension
->RegistryPath
)
2102 DebugPrint((ClassDebugMCN
,
2103 "ClassInitializeMCN: Class MCN is %s\n",
2104 (disabled
? "disabled" : "enabled")));
2106 status
= ClasspMediaChangeDeviceInstanceOverride(
2108 &instanceOverride
); // default value
2110 if (!NT_SUCCESS(status
)) {
2111 DebugPrint((ClassDebugMCN
,
2112 "ClassInitializeMCN: Instance using default\n"));
2114 DebugPrint((ClassDebugMCN
,
2115 "ClassInitializeMCN: Instance override: %s MCN\n",
2116 (instanceOverride
? "Enabling" : "Disabling")));
2117 disabled
= !instanceOverride
;
2120 DebugPrint((ClassDebugMCN
,
2121 "ClassInitializeMCN: Instance MCN is %s\n",
2122 (disabled
? "disabled" : "enabled")));
2129 // if the drive is not a CDROM, allow the drive to sleep
2131 if (FdoExtension
->DeviceObject
->DeviceType
== FILE_DEVICE_CD_ROM
) {
2132 ClasspInitializePolling(FdoExtension
, FALSE
);
2134 ClasspInitializePolling(FdoExtension
, TRUE
);
2138 } // end ClassInitializeMediaChangeDetection()
2140 /*++////////////////////////////////////////////////////////////////////////////
2142 ClasspMediaChangeDeviceInstanceOverride()
2144 Routine Description:
2146 The user can override the global setting to enable or disable Autorun on a
2147 specific cdrom device via the control panel. This routine checks and/or
2152 FdoExtension - the device to set/get the value for
2153 Value - the value to use in a set
2154 SetValue - whether to set the value
2158 TRUE - Autorun is disabled
2159 FALSE - Autorun is not disabled (Default)
2164 ClasspMediaChangeDeviceInstanceOverride(
2165 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
2166 OUT PBOOLEAN Enabled
2169 HANDLE deviceParameterHandle
; // cdrom instance key
2170 HANDLE driverParameterHandle
; // cdrom specific key
2171 RTL_QUERY_REGISTRY_TABLE queryTable
[3];
2172 OBJECT_ATTRIBUTES objectAttributes
;
2173 UNICODE_STRING subkeyName
;
2176 ULONG alwaysDisable
;
2182 deviceParameterHandle
= NULL
;
2183 driverParameterHandle
= NULL
;
2184 status
= STATUS_UNSUCCESSFUL
;
2185 alwaysEnable
= FALSE
;
2186 alwaysDisable
= FALSE
;
2190 status
= IoOpenDeviceRegistryKey( FdoExtension
->LowerPdo
,
2191 PLUGPLAY_REGKEY_DEVICE
,
2193 &deviceParameterHandle
2195 if (!NT_SUCCESS(status
)) {
2198 // this can occur when a new device is added to the system
2199 // this is due to cdrom.sys being an 'essential' driver
2201 DebugPrint((ClassDebugMCN
,
2202 "ClassMediaChangeDeviceInstanceDisabled: "
2203 "Could not open device registry key [%lx]\n", status
));
2207 RtlInitUnicodeString(&subkeyName
, MCN_REG_SUBKEY_NAME
);
2208 InitializeObjectAttributes(&objectAttributes
,
2210 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
2211 deviceParameterHandle
,
2212 (PSECURITY_DESCRIPTOR
) NULL
);
2214 status
= ZwCreateKey(&driverParameterHandle
,
2218 (PUNICODE_STRING
) NULL
,
2219 REG_OPTION_NON_VOLATILE
,
2222 if (!NT_SUCCESS(status
)) {
2223 DebugPrint((ClassDebugMCN
,
2224 "ClassMediaChangeDeviceInstanceDisabled: "
2225 "subkey could not be created. %lx\n", status
));
2230 // Default to not changing autorun behavior, based upon setting
2231 // registryValue to zero.
2236 RtlZeroMemory(&queryTable
[0], sizeof(queryTable
));
2238 queryTable
[0].Flags
= RTL_QUERY_REGISTRY_DIRECT
;
2239 queryTable
[0].DefaultType
= REG_DWORD
;
2240 queryTable
[0].DefaultLength
= 0;
2243 queryTable
[0].Name
= MCN_REG_AUTORUN_DISABLE_INSTANCE_NAME
;
2244 queryTable
[0].EntryContext
= &alwaysDisable
;
2245 queryTable
[0].DefaultData
= &alwaysDisable
;
2247 queryTable
[0].Name
= MCN_REG_AUTORUN_ENABLE_INSTANCE_NAME
;
2248 queryTable
[0].EntryContext
= &alwaysEnable
;
2249 queryTable
[0].DefaultData
= &alwaysEnable
;
2253 // don't care if it succeeds, since we set defaults above
2256 RtlQueryRegistryValues(RTL_REGISTRY_HANDLE
,
2257 (PWSTR
)driverParameterHandle
,
2265 if (driverParameterHandle
) ZwClose(driverParameterHandle
);
2266 if (deviceParameterHandle
) ZwClose(deviceParameterHandle
);
2270 if (alwaysEnable
&& alwaysDisable
) {
2272 DebugPrint((ClassDebugMCN
,
2273 "ClassMediaChangeDeviceInstanceDisabled: %s selected\n",
2274 "Both Enable and Disable set -- DISABLE"));
2275 ASSERT(NT_SUCCESS(status
));
2276 status
= STATUS_SUCCESS
;
2279 } else if (alwaysDisable
) {
2281 DebugPrint((ClassDebugMCN
,
2282 "ClassMediaChangeDeviceInstanceDisabled: %s selected\n",
2284 ASSERT(NT_SUCCESS(status
));
2285 status
= STATUS_SUCCESS
;
2288 } else if (alwaysEnable
) {
2290 DebugPrint((ClassDebugMCN
,
2291 "ClassMediaChangeDeviceInstanceDisabled: %s selected\n",
2293 ASSERT(NT_SUCCESS(status
));
2294 status
= STATUS_SUCCESS
;
2299 DebugPrint((ClassDebugMCN
,
2300 "ClassMediaChangeDeviceInstanceDisabled: %s selected\n",
2302 status
= STATUS_UNSUCCESSFUL
;
2308 } // end ClasspMediaChangeDeviceInstanceOverride()
2310 /*++////////////////////////////////////////////////////////////////////////////
2312 ClasspIsMediaChangeDisabledDueToHardwareLimitation()
2314 Routine Description:
2316 The key AutoRunAlwaysDisable contains a MULTI_SZ of hardware IDs for
2317 which to never enable MediaChangeNotification.
2319 The user can override the global setting to enable or disable Autorun on a
2320 specific cdrom device via the control panel.
2325 RegistryPath - pointer to the unicode string inside
2326 ...\CurrentControlSet\Services\Cdrom
2331 FALSE - Autorun may be enabled
2336 ClasspIsMediaChangeDisabledDueToHardwareLimitation(
2337 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
2338 IN PUNICODE_STRING RegistryPath
2341 PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor
= FdoExtension
->DeviceDescriptor
;
2342 OBJECT_ATTRIBUTES objectAttributes
;
2343 HANDLE serviceKey
= NULL
;
2344 RTL_QUERY_REGISTRY_TABLE parameters
[2];
2346 UNICODE_STRING deviceUnicodeString
;
2347 ANSI_STRING deviceString
;
2348 ULONG mediaChangeNotificationDisabled
= FALSE
;
2356 // open the service key.
2359 InitializeObjectAttributes(&objectAttributes
,
2361 OBJ_CASE_INSENSITIVE
,
2365 status
= ZwOpenKey(&serviceKey
,
2369 ASSERT(NT_SUCCESS(status
));
2372 if(!NT_SUCCESS(status
)) {
2375 // always take the safe path. if we can't open the service key,
2385 // Determine if drive is in a list of those requiring
2386 // autorun to be disabled. this is stored in a REG_MULTI_SZ
2387 // named AutoRunAlwaysDisable. this is required as some autochangers
2388 // must load the disc to reply to ChkVerify request, causing them
2389 // to cycle discs continuously.
2399 deviceString
.Buffer
= NULL
;
2400 deviceUnicodeString
.Buffer
= NULL
;
2403 // there may be nothing to check against
2406 if ((deviceDescriptor
->VendorIdOffset
== 0) &&
2407 (deviceDescriptor
->ProductIdOffset
== 0)) {
2413 if (deviceDescriptor
->VendorIdOffset
== 0) {
2416 vendorId
= (PCSTR
) deviceDescriptor
+ deviceDescriptor
->VendorIdOffset
;
2417 length
= strlen(vendorId
);
2420 if ( deviceDescriptor
->ProductIdOffset
== 0 ) {
2423 productId
= (PCSTR
) deviceDescriptor
+ deviceDescriptor
->ProductIdOffset
;
2424 length
+= strlen(productId
);
2427 if ( deviceDescriptor
->ProductRevisionOffset
== 0 ) {
2430 revisionId
= (PCSTR
) deviceDescriptor
+ deviceDescriptor
->ProductRevisionOffset
;
2431 length
+= strlen(revisionId
);
2435 // allocate a buffer for the string
2438 deviceString
.Length
= (USHORT
)( length
);
2439 deviceString
.MaximumLength
= deviceString
.Length
+ 1;
2440 deviceString
.Buffer
= ExAllocatePoolWithTag( NonPagedPool
,
2441 deviceString
.MaximumLength
,
2442 CLASS_TAG_AUTORUN_DISABLE
2444 if (deviceString
.Buffer
== NULL
) {
2445 DebugPrint((ClassDebugMCN
,
2446 "ClassMediaChangeDisabledForHardware: Unable to alloc "
2447 "string buffer\n" ));
2452 // copy strings to the buffer
2456 if (vendorId
!= NULL
) {
2457 RtlCopyMemory(deviceString
.Buffer
+ offset
,
2460 offset
+= strlen(vendorId
);
2463 if ( productId
!= NULL
) {
2464 RtlCopyMemory(deviceString
.Buffer
+ offset
,
2467 offset
+= strlen(productId
);
2469 if ( revisionId
!= NULL
) {
2470 RtlCopyMemory(deviceString
.Buffer
+ offset
,
2472 strlen(revisionId
));
2473 offset
+= strlen(revisionId
);
2476 ASSERT(offset
== deviceString
.Length
);
2478 deviceString
.Buffer
[deviceString
.Length
] = '\0'; // Null-terminated
2481 // convert to unicode as registry deals with unicode strings
2484 status
= RtlAnsiStringToUnicodeString( &deviceUnicodeString
,
2488 if (!NT_SUCCESS(status
)) {
2489 DebugPrint((ClassDebugMCN
,
2490 "ClassMediaChangeDisabledForHardware: cannot convert "
2491 "to unicode %lx\n", status
));
2496 // query the value, setting valueFound to true if found
2499 RtlZeroMemory(parameters
, sizeof(parameters
));
2501 nullMultiSz
= L
"\0";
2502 parameters
[0].QueryRoutine
= ClasspMediaChangeRegistryCallBack
;
2503 parameters
[0].Flags
= RTL_QUERY_REGISTRY_REQUIRED
;
2504 parameters
[0].Name
= L
"AutoRunAlwaysDisable";
2505 parameters
[0].EntryContext
= &mediaChangeNotificationDisabled
;
2506 parameters
[0].DefaultType
= REG_MULTI_SZ
;
2507 parameters
[0].DefaultData
= nullMultiSz
;
2508 parameters
[0].DefaultLength
= 0;
2510 status
= RtlQueryRegistryValues(RTL_REGISTRY_HANDLE
,
2513 &deviceUnicodeString
,
2516 if ( !NT_SUCCESS(status
) ) {
2522 if (deviceString
.Buffer
!= NULL
) {
2523 ExFreePool( deviceString
.Buffer
);
2525 if (deviceUnicodeString
.Buffer
!= NULL
) {
2526 RtlFreeUnicodeString( &deviceUnicodeString
);
2529 ZwClose(serviceKey
);
2532 if (mediaChangeNotificationDisabled
) {
2533 DebugPrint((ClassDebugMCN
, "ClassMediaChangeDisabledForHardware: "
2534 "Device is on disable list\n"));
2539 } // end ClasspIsMediaChangeDisabledDueToHardwareLimitation()
2541 /*++////////////////////////////////////////////////////////////////////////////
2543 ClasspIsMediaChangeDisabledForClass()
2545 Routine Description:
2547 The user must specify that AutoPlay is to run on the platform
2548 by setting the registry value HKEY_LOCAL_MACHINE\System\CurrentControlSet\
2549 Services\<SERVICE>\Autorun:REG_DWORD:1.
2551 The user can override the global setting to enable or disable Autorun on a
2552 specific cdrom device via the control panel.
2557 RegistryPath - pointer to the unicode string inside
2558 ...\CurrentControlSet\Services\Cdrom
2562 TRUE - Autorun is disabled for this class
2563 FALSE - Autorun is enabled for this class
2568 ClasspIsMediaChangeDisabledForClass(
2569 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
2570 IN PUNICODE_STRING RegistryPath
2573 //PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor = FdoExtension->DeviceDescriptor;
2575 OBJECT_ATTRIBUTES objectAttributes
;
2576 HANDLE serviceKey
= NULL
;
2577 HANDLE parametersKey
= NULL
;
2578 RTL_QUERY_REGISTRY_TABLE parameters
[3];
2580 UNICODE_STRING paramStr
;
2581 //UNICODE_STRING deviceUnicodeString;
2582 //ANSI_STRING deviceString;
2585 // Default to ENABLING MediaChangeNotification (!)
2588 ULONG mcnRegistryValue
= 1;
2596 // open the service key.
2599 InitializeObjectAttributes(&objectAttributes
,
2601 OBJ_CASE_INSENSITIVE
,
2605 status
= ZwOpenKey(&serviceKey
,
2609 ASSERT(NT_SUCCESS(status
));
2611 if(!NT_SUCCESS(status
)) {
2614 // return the default value, which is the
2615 // inverse of the registry setting default
2616 // since this routine asks if it's disabled
2619 DebugPrint((ClassDebugMCN
, "ClassCheckServiceMCN: Defaulting to %s\n",
2620 (mcnRegistryValue
? "Enabled" : "Disabled")));
2621 return (BOOLEAN
)(!mcnRegistryValue
);
2625 RtlZeroMemory(parameters
, sizeof(parameters
));
2628 // Open the parameters key (if any) beneath the services key.
2631 RtlInitUnicodeString(¶mStr
, L
"Parameters");
2633 InitializeObjectAttributes(&objectAttributes
,
2635 OBJ_CASE_INSENSITIVE
,
2639 status
= ZwOpenKey(¶metersKey
,
2643 if (!NT_SUCCESS(status
)) {
2644 parametersKey
= NULL
;
2650 // Check for the Autorun value.
2653 parameters
[0].Flags
= RTL_QUERY_REGISTRY_DIRECT
;
2654 parameters
[0].Name
= L
"Autorun";
2655 parameters
[0].EntryContext
= &mcnRegistryValue
;
2656 parameters
[0].DefaultType
= REG_DWORD
;
2657 parameters
[0].DefaultData
= &mcnRegistryValue
;
2658 parameters
[0].DefaultLength
= sizeof(ULONG
);
2660 status
= RtlQueryRegistryValues(RTL_REGISTRY_HANDLE
| RTL_REGISTRY_OPTIONAL
,
2666 DebugPrint((ClassDebugMCN
, "ClassCheckServiceMCN: "
2667 "<Service>/Autorun flag = %d\n", mcnRegistryValue
));
2669 if(parametersKey
!= NULL
) {
2671 status
= RtlQueryRegistryValues(RTL_REGISTRY_HANDLE
| RTL_REGISTRY_OPTIONAL
,
2676 DebugPrint((ClassDebugMCN
, "ClassCheckServiceMCN: "
2677 "<Service>/Parameters/Autorun flag = %d\n",
2679 ZwClose(parametersKey
);
2682 ZwClose(serviceKey
);
2684 DebugPrint((ClassDebugMCN
, "ClassCheckServiceMCN: "
2685 "Autoplay for device %p is %s\n",
2686 FdoExtension
->DeviceObject
,
2687 (mcnRegistryValue
? "on" : "off")
2691 // return if it is _disabled_, which is the
2692 // inverse of the registry setting
2695 return (BOOLEAN
)(!mcnRegistryValue
);
2696 } // end ClasspIsMediaChangeDisabledForClass()
2698 /*++////////////////////////////////////////////////////////////////////////////
2700 ClassEnableMediaChangeDetection() ISSUE-2000/02/20-henrygab - why public?
2701 ClassEnableMediaChangeDetection() ISSUE-2000/02/20-henrygab - not documented
2703 Routine Description:
2717 ClassEnableMediaChangeDetection(
2718 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
2721 PMEDIA_CHANGE_DETECTION_INFO info
= FdoExtension
->MediaChangeDetectionInfo
;
2727 DebugPrint((ClassDebugMCN
,
2728 "ClassEnableMediaChangeDetection: not initialized\n"));
2732 KeWaitForMutexObject(&info
->MediaChangeMutex
,
2738 oldCount
= --info
->MediaChangeDetectionDisableCount
;
2740 ASSERT(oldCount
>= 0);
2742 DebugPrint((ClassDebugMCN
, "ClassEnableMediaChangeDetection: Disable count "
2744 info
->MediaChangeDetectionDisableCount
));
2749 // We don't know what state the media is in anymore.
2752 ClasspInternalSetMediaChangeState(FdoExtension
,
2758 // Reset the MCN timer.
2761 ClassResetMediaChangeTimer(FdoExtension
);
2763 DebugPrint((ClassDebugMCN
, "MCD is enabled\n"));
2767 DebugPrint((ClassDebugMCN
, "MCD still disabled\n"));
2773 // Let something else run.
2776 KeReleaseMutex(&info
->MediaChangeMutex
, FALSE
);
2779 } // end ClassEnableMediaChangeDetection()
2781 /*++////////////////////////////////////////////////////////////////////////////
2783 ClassDisableMediaChangeDetection() ISSUE-2000/02/20-henrygab - why public?
2784 ClassDisableMediaChangeDetection() ISSUE-2000/02/20-henrygab - not documented
2786 Routine Description:
2798 ULONG BreakOnMcnDisable
= FALSE
;
2802 ClassDisableMediaChangeDetection(
2803 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
2806 PMEDIA_CHANGE_DETECTION_INFO info
= FdoExtension
->MediaChangeDetectionInfo
;
2814 KeWaitForMutexObject(&info
->MediaChangeMutex
,
2820 info
->MediaChangeDetectionDisableCount
++;
2822 DebugPrint((ClassDebugMCN
, "ClassDisableMediaChangeDetection: "
2823 "disable count is %d\n",
2824 info
->MediaChangeDetectionDisableCount
));
2826 KeReleaseMutex(&info
->MediaChangeMutex
, FALSE
);
2829 } // end ClassDisableMediaChangeDetection()
2831 /*++////////////////////////////////////////////////////////////////////////////
2833 ClassCleanupMediaChangeDetection() ISSUE-2000/02/20-henrygab - why public?!
2835 Routine Description:
2837 This routine will cleanup any resources allocated for MCN. It is called
2838 by classpnp during remove device, and therefore is not typically required
2839 by external drivers.
2848 ClassCleanupMediaChangeDetection(
2849 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
2852 PMEDIA_CHANGE_DETECTION_INFO info
= FdoExtension
->MediaChangeDetectionInfo
;
2860 FdoExtension
->MediaChangeDetectionInfo
= NULL
;
2862 if (info
->Gesn
.Buffer
) {
2863 ExFreePool(info
->Gesn
.Buffer
);
2865 IoFreeIrp(info
->MediaChangeIrp
);
2866 ExFreePool(info
->SenseBuffer
);
2869 } // end ClassCleanupMediaChangeDetection()
2871 /*++////////////////////////////////////////////////////////////////////////////
2873 ClasspMcnControl() - ISSUE-2000/02/20-henrygab - not documented
2875 Routine Description:
2890 IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
2892 IN PSCSI_REQUEST_BLOCK Srb
2895 PCOMMON_DEVICE_EXTENSION commonExtension
=
2896 (PCOMMON_DEVICE_EXTENSION
) FdoExtension
;
2898 PIO_STACK_LOCATION irpStack
= IoGetCurrentIrpStackLocation(Irp
);
2899 PPREVENT_MEDIA_REMOVAL request
= Irp
->AssociatedIrp
.SystemBuffer
;
2901 PFILE_OBJECT fileObject
= irpStack
->FileObject
;
2902 PFILE_OBJECT_EXTENSION fsContext
= NULL
;
2904 NTSTATUS status
= STATUS_SUCCESS
;
2909 // Check to make sure we have a file object extension to keep track of this
2910 // request. If not we'll fail it before synchronizing.
2915 if(fileObject
!= NULL
) {
2916 fsContext
= ClasspGetFsContext(commonExtension
, fileObject
);
2917 }else if(Irp
->RequestorMode
== KernelMode
) { // && fileObject == NULL
2918 fsContext
= &FdoExtension
->KernelModeMcnContext
;
2921 if (fsContext
== NULL
) {
2924 // This handle isn't setup correctly. We can't let the
2928 status
= STATUS_INVALID_PARAMETER
;
2932 if(request
->PreventMediaRemoval
) {
2935 // This is a lock command. Reissue the command in case bus or
2936 // device was reset and the lock was cleared.
2939 ClassDisableMediaChangeDetection(FdoExtension
);
2940 InterlockedIncrement((PLONG
)&fsContext
->McnDisableCount
);
2944 if(fsContext
->McnDisableCount
== 0) {
2945 status
= STATUS_INVALID_DEVICE_STATE
;
2949 InterlockedDecrement((PLONG
)&fsContext
->McnDisableCount
);
2950 ClassEnableMediaChangeDetection(FdoExtension
);
2955 Irp
->IoStatus
.Status
= status
;
2961 ClassReleaseRemoveLock(FdoExtension
->DeviceObject
, Irp
);
2962 ClassCompleteRequest(FdoExtension
->DeviceObject
,
2967 } // end ClasspMcnControl(
2969 /*++////////////////////////////////////////////////////////////////////////////
2971 ClasspMediaChangeRegistryCallBack()
2973 Routine Description:
2975 This callback for a registry SZ or MULTI_SZ is called once for each
2976 SZ in the value. It will attempt to match the data with the
2977 UNICODE_STRING passed in as Context, and modify EntryContext if a
2978 match is found. Written for ClasspCheckRegistryForMediaChangeCompletion
2982 ValueName - name of the key that was opened
2983 ValueType - type of data stored in the value (REG_SZ for this routine)
2984 ValueData - data in the registry, in this case a wide string
2985 ValueLength - length of the data including the terminating null
2986 Context - unicode string to compare against ValueData
2987 EntryContext - should be initialized to 0, will be set to 1 if match found
2992 EntryContext will be 1 if found
2997 ClasspMediaChangeRegistryCallBack(
3001 IN ULONG ValueLength
,
3003 IN PVOID EntryContext
3007 PUNICODE_STRING deviceString
;
3011 UNREFERENCED_PARAMETER(ValueName
);
3015 // if we have already set the value to true, exit
3018 valueFound
= EntryContext
;
3019 if ((*valueFound
) != 0) {
3020 DebugPrint((ClassDebugMCN
, "ClasspMcnRegCB: already set to true\n"));
3021 return STATUS_SUCCESS
;
3024 if (ValueLength
== sizeof(WCHAR
)) {
3025 DebugPrint((ClassDebugError
, "ClasspMcnRegCB: NULL string should "
3026 "never be passed to registry call-back!\n"));
3027 return STATUS_SUCCESS
;
3032 // if the data is not a terminated string, exit
3035 if (ValueType
!= REG_SZ
) {
3036 return STATUS_SUCCESS
;
3039 deviceString
= Context
;
3040 keyValue
= ValueData
;
3041 ValueLength
-= sizeof(WCHAR
); // ignore the null character
3044 // do not compare more memory than is in deviceString
3047 if (ValueLength
> deviceString
->Length
) {
3048 ValueLength
= deviceString
->Length
;
3052 // if the strings match, disable autorun
3055 if (RtlCompareMemory(deviceString
->Buffer
, keyValue
, ValueLength
) == ValueLength
) {
3056 DebugPrint((ClassDebugMCN
, "ClasspRegMcnCB: Match found\n"));
3057 DebugPrint((ClassDebugMCN
, "ClasspRegMcnCB: DeviceString at %p\n",
3058 deviceString
->Buffer
));
3059 DebugPrint((ClassDebugMCN
, "ClasspRegMcnCB: KeyValue at %p\n",
3061 (*valueFound
) = TRUE
;
3064 return STATUS_SUCCESS
;
3065 } // end ClasspMediaChangeRegistryCallBack()
3067 /*++////////////////////////////////////////////////////////////////////////////
3069 ClasspTimerTick() - ISSUE-2000/02/20-henrygab - not documented
3071 Routine Description:
3086 PDEVICE_OBJECT DeviceObject
,
3090 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= DeviceObject
->DeviceExtension
;
3091 PCOMMON_DEVICE_EXTENSION commonExtension
= DeviceObject
->DeviceExtension
;
3094 ASSERT(commonExtension
->IsFdo
);
3097 // Do any media change work
3099 isRemoved
= ClassAcquireRemoveLock(DeviceObject
, (PIRP
)ClasspTimerTick
);
3102 // We stop the timer before deleting the device. It's safe to keep going
3103 // if the flag value is REMOVE_PENDING because the removal thread will be
3104 // blocked trying to stop the timer.
3107 ASSERT(isRemoved
!= REMOVE_COMPLETE
);
3110 // This routine is reasonably safe even if the device object has a pending
3115 PFAILURE_PREDICTION_INFO info
= fdoExtension
->FailurePredictionInfo
;
3118 // Do any media change detection work
3121 if (fdoExtension
->MediaChangeDetectionInfo
!= NULL
) {
3123 ClassCheckMediaState(fdoExtension
);
3128 // Do any failure prediction work
3130 if ((info
!= NULL
) && (info
->Method
!= FailurePredictionNone
)) {
3135 if (ClasspCanSendPollingIrp(fdoExtension
)) {
3138 // Synchronization is not required here since the Interlocked
3139 // locked instruction guarantees atomicity. Other code that
3140 // resets CountDown uses InterlockedExchange which is also
3143 countDown
= InterlockedDecrement((PLONG
)&info
->CountDown
);
3144 if (countDown
== 0) {
3146 DebugPrint((4, "ClasspTimerTick: Send FP irp for %p\n",
3149 if(info
->WorkQueueItem
== NULL
) {
3151 info
->WorkQueueItem
=
3152 IoAllocateWorkItem(fdoExtension
->DeviceObject
);
3154 if(info
->WorkQueueItem
== NULL
) {
3157 // Set the countdown to one minute in the future.
3158 // we'll try again then in the hopes there's more
3162 DebugPrint((1, "ClassTimerTick: Couldn't allocate "
3163 "item - try again in one minute\n"));
3164 InterlockedExchange((PLONG
)&info
->CountDown
, 60);
3169 // Grab the remove lock so that removal will block
3170 // until the work item is done.
3173 ClassAcquireRemoveLock(fdoExtension
->DeviceObject
,
3174 info
->WorkQueueItem
);
3176 IoQueueWorkItem(info
->WorkQueueItem
,
3177 ClasspFailurePredict
,
3184 DebugPrint((3, "ClasspTimerTick: Failure "
3185 "Prediction work item is "
3186 "already active for device %p\n",
3190 } // end (countdown == 0)
3194 // If device is sleeping then just rearm polling timer
3195 DebugPrint((4, "ClassTimerTick, SHHHH!!! device is %p is sleeping\n",
3199 } // end failure prediction polling
3202 // Give driver a chance to do its own specific work
3205 if (commonExtension
->DriverExtension
->InitData
.ClassTick
!= NULL
) {
3207 commonExtension
->DriverExtension
->InitData
.ClassTick(DeviceObject
);
3209 } // end device specific tick handler
3210 } // end check for removed
3212 ClassReleaseRemoveLock(DeviceObject
, (PIRP
)ClasspTimerTick
);
3213 } // end ClasspTimerTick()
3215 /*++////////////////////////////////////////////////////////////////////////////
3217 ClasspEnableTimer() - ISSUE-2000/02/20-henrygab - not documented
3219 Routine Description:
3234 PDEVICE_OBJECT DeviceObject
3241 if (DeviceObject
->Timer
== NULL
) {
3243 status
= IoInitializeTimer(DeviceObject
, ClasspTimerTick
, NULL
);
3247 status
= STATUS_SUCCESS
;
3251 if (NT_SUCCESS(status
)) {
3253 IoStartTimer(DeviceObject
);
3254 DebugPrint((1, "ClasspEnableTimer: Once a second timer enabled "
3255 "for device %p\n", DeviceObject
));
3259 DebugPrint((1, "ClasspEnableTimer: Device %p, Status %lx "
3260 "initializing timer\n", DeviceObject
, status
));
3264 } // end ClasspEnableTimer()
3266 /*++////////////////////////////////////////////////////////////////////////////
3268 ClasspDisableTimer() - ISSUE-2000/02/20-henrygab - not documented
3270 Routine Description:
3285 PDEVICE_OBJECT DeviceObject
3288 //PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
3289 //PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
3290 //PMEDIA_CHANGE_DETECTION_INFO mCDInfo = fdoExtension->MediaChangeDetectionInfo;
3291 //PFAILURE_PREDICTION_INFO fPInfo = fdoExtension->FailurePredictionInfo;
3296 if (DeviceObject
->Timer
!= NULL
) {
3299 // we are only going to stop the actual timer in remove device routine.
3300 // it is the responsibility of the code within the timer routine to
3301 // check if the device is removed and not processing io for the final
3303 // this keeps the code clean and prevents lots of bugs.
3307 IoStopTimer(DeviceObject
);
3308 DebugPrint((3, "ClasspDisableTimer: Once a second timer disabled "
3309 "for device %p\n", DeviceObject
));
3313 DebugPrint((1, "ClasspDisableTimer: Timer never enabled\n"));
3317 return STATUS_SUCCESS
;
3318 } // end ClasspDisableTimer()
3320 /*++////////////////////////////////////////////////////////////////////////////
3322 ClasspFailurePredict() - ISSUE-2000/02/20-henrygab - not documented
3324 Routine Description:
3335 Note: this function can be called (via the workitem callback) after the paging device is shut down,
3336 so it must be PAGE LOCKED.
3340 ClasspFailurePredict(
3341 IN PDEVICE_OBJECT DeviceObject
,
3345 PFAILURE_PREDICTION_INFO info
= Context
;
3346 PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
= DeviceObject
->DeviceExtension
;
3347 PIO_WORKITEM workItem
;
3348 STORAGE_PREDICT_FAILURE checkFailure
;
3349 SCSI_ADDRESS scsiAddress
;
3353 ASSERT(info
!= NULL
);
3355 DebugPrint((1, "ClasspFailurePredict: Polling for failure\n"));
3358 // Mark the work item as inactive and reset the countdown timer. we
3359 // can't risk freeing the work item until we've released the remove-lock
3360 // though - if we do it might get resused as a tag before we can release
3364 InterlockedExchange((PLONG
)&info
->CountDown
, info
->Period
);
3365 workItem
= InterlockedExchangePointer(&info
->WorkQueueItem
, NULL
);
3367 if (ClasspCanSendPollingIrp(fdoExtension
)) {
3370 PDEVICE_OBJECT topOfStack
;
3372 IO_STATUS_BLOCK ioStatus
;
3374 KeInitializeEvent(&event
, SynchronizationEvent
, FALSE
);
3376 topOfStack
= IoGetAttachedDeviceReference(DeviceObject
);
3379 // Send down irp to see if drive is predicting failure
3382 irp
= IoBuildDeviceIoControlRequest(
3383 IOCTL_STORAGE_PREDICT_FAILURE
,
3388 sizeof(STORAGE_PREDICT_FAILURE
),
3395 status
= IoCallDriver(topOfStack
, irp
);
3396 if (status
== STATUS_PENDING
) {
3397 KeWaitForSingleObject(&event
, Executive
, KernelMode
, FALSE
, NULL
);
3398 status
= ioStatus
.Status
;
3401 status
= STATUS_INSUFFICIENT_RESOURCES
;
3404 if (NT_SUCCESS(status
) && (checkFailure
.PredictFailure
)) {
3406 checkFailure
.PredictFailure
= 512;
3409 // Send down irp to get scsi address
3411 KeInitializeEvent(&event
, SynchronizationEvent
, FALSE
);
3413 RtlZeroMemory(&scsiAddress
, sizeof(SCSI_ADDRESS
));
3414 irp
= IoBuildDeviceIoControlRequest(
3415 IOCTL_SCSI_GET_ADDRESS
,
3420 sizeof(SCSI_ADDRESS
),
3426 status
= IoCallDriver(topOfStack
, irp
);
3427 if (status
== STATUS_PENDING
) {
3428 KeWaitForSingleObject(&event
, Executive
, KernelMode
, FALSE
, NULL
);
3429 status
= ioStatus
.Status
;
3433 ClassNotifyFailurePredicted(fdoExtension
,
3434 (PUCHAR
)&checkFailure
,
3435 sizeof(checkFailure
),
3436 (BOOLEAN
)(fdoExtension
->FailurePredicted
== FALSE
),
3439 scsiAddress
.TargetId
,
3442 fdoExtension
->FailurePredicted
= TRUE
;
3446 ObDereferenceObject(topOfStack
);
3449 ClassReleaseRemoveLock(DeviceObject
, (PIRP
) workItem
);
3450 IoFreeWorkItem(workItem
);
3452 } // end ClasspFailurePredict()
3454 /*++////////////////////////////////////////////////////////////////////////////
3456 ClassNotifyFailurePredicted() ISSUE-alanwar-2000/02/20 - not documented
3458 Routine Description:
3467 ClassNotifyFailurePredicted(
3468 PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
3472 ULONG UniqueErrorValue
,
3478 PIO_ERROR_LOG_PACKET logEntry
;
3480 DebugPrint((1, "ClasspFailurePredictPollCompletion: Failure predicted for device %p\n", FdoExtension
->DeviceObject
));
3483 // Fire off a WMI event
3485 ClassWmiFireEvent(FdoExtension
->DeviceObject
,
3486 &StoragePredictFailureEventGuid
,
3492 // Log an error into the eventlog
3497 logEntry
= IoAllocateErrorLogEntry(
3498 FdoExtension
->DeviceObject
,
3499 sizeof(IO_ERROR_LOG_PACKET
) + (3 * sizeof(ULONG
)));
3501 if (logEntry
!= NULL
)
3504 logEntry
->FinalStatus
= STATUS_SUCCESS
;
3505 logEntry
->ErrorCode
= IO_WRN_FAILURE_PREDICTED
;
3506 logEntry
->SequenceNumber
= 0;
3507 logEntry
->MajorFunctionCode
= IRP_MJ_DEVICE_CONTROL
;
3508 logEntry
->IoControlCode
= IOCTL_STORAGE_PREDICT_FAILURE
;
3509 logEntry
->RetryCount
= 0;
3510 logEntry
->UniqueErrorValue
= UniqueErrorValue
;
3511 logEntry
->DumpDataSize
= 3;
3513 logEntry
->DumpData
[0] = PathId
;
3514 logEntry
->DumpData
[1] = TargetId
;
3515 logEntry
->DumpData
[2] = Lun
;
3518 // Write the error log packet.
3521 IoWriteErrorLogEntry(logEntry
);
3524 } // end ClassNotifyFailurePredicted()
3526 /*++////////////////////////////////////////////////////////////////////////////
3528 ClassSetFailurePredictionPoll()
3530 Routine Description:
3532 This routine enables polling for failure prediction, setting the timer
3533 to fire every N seconds as specified by the PollingPeriod.
3537 FdoExtension - the device to setup failure prediction for.
3539 FailurePredictionMethod - specific failure prediction method to use
3540 if set to FailurePredictionNone, will disable failure detection
3542 PollingPeriod - if 0 then no change to current polling timer
3551 ClassSetFailurePredictionPoll(
3552 PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
,
3553 FAILURE_PREDICTION_METHOD FailurePredictionMethod
,
3557 PFAILURE_PREDICTION_INFO info
;
3559 //DEVICE_POWER_STATE powerState;
3563 if (FdoExtension
->FailurePredictionInfo
== NULL
) {
3565 if (FailurePredictionMethod
!= FailurePredictionNone
) {
3567 info
= ExAllocatePoolWithTag(NonPagedPool
,
3568 sizeof(FAILURE_PREDICTION_INFO
),
3569 CLASS_TAG_FAILURE_PREDICT
);
3573 return STATUS_INSUFFICIENT_RESOURCES
;
3577 KeInitializeEvent(&info
->Event
, SynchronizationEvent
, TRUE
);
3579 info
->WorkQueueItem
= NULL
;
3580 info
->Period
= DEFAULT_FAILURE_PREDICTION_PERIOD
;
3585 // FaultPrediction has not been previously initialized, nor
3586 // is it being initialized now. No need to do anything.
3588 return STATUS_SUCCESS
;
3592 FdoExtension
->FailurePredictionInfo
= info
;
3596 info
= FdoExtension
->FailurePredictionInfo
;
3600 KeWaitForSingleObject(&info
->Event
,
3608 // Reset polling period and counter. Setup failure detection type
3611 if (PollingPeriod
!= 0) {
3613 InterlockedExchange((PLONG
)&info
->Period
, PollingPeriod
);
3617 InterlockedExchange((PLONG
)&info
->CountDown
, info
->Period
);
3619 info
->Method
= FailurePredictionMethod
;
3620 if (FailurePredictionMethod
!= FailurePredictionNone
) {
3622 status
= ClasspEnableTimer(FdoExtension
->DeviceObject
);
3624 if (NT_SUCCESS(status
)) {
3625 DebugPrint((3, "ClassEnableFailurePredictPoll: Enabled for "
3626 "device %p\n", FdoExtension
->DeviceObject
));
3631 status
= ClasspDisableTimer(FdoExtension
->DeviceObject
);
3632 DebugPrint((3, "ClassEnableFailurePredictPoll: Disabled for "
3633 "device %p\n", FdoExtension
->DeviceObject
));
3634 status
= STATUS_SUCCESS
;
3638 KeSetEvent(&info
->Event
, IO_NO_INCREMENT
, FALSE
);
3641 } // end ClassSetFailurePredictionPoll()