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