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