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