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