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