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