ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

autorun.c
Go to the documentation of this file.
00001 /*++
00002 
00003 Copyright (C) Microsoft Corporation, 1991 - 1999
00004 
00005 Module Name:
00006 
00007     autorun.c
00008 
00009 Abstract:
00010 
00011     Code for support of media change detection in the class driver
00012 
00013 Environment:
00014 
00015     kernel mode only
00016 
00017 Notes:
00018 
00019 
00020 Revision History:
00021 
00022 --*/
00023 
00024 #include "classp.h"
00025 #include "debug.h"
00026 
00027 #define GESN_TIMEOUT_VALUE (0x4)
00028 #define GESN_BUFFER_SIZE (0x8)
00029 #define MAXIMUM_IMMEDIATE_MCN_RETRIES (0x20)
00030 #define MCN_REG_SUBKEY_NAME                   (L"MediaChangeNotification")
00031 #define MCN_REG_AUTORUN_DISABLE_INSTANCE_NAME (L"AlwaysDisableMCN")
00032 #define MCN_REG_AUTORUN_ENABLE_INSTANCE_NAME  (L"AlwaysEnableMCN")
00033 
00034 GUID StoragePredictFailureEventGuid = WMI_STORAGE_PREDICT_FAILURE_EVENT_GUID;
00035 
00036 //
00037 // Only send polling irp when device is fully powered up and a
00038 // power down irp is not in progress.
00039 //
00040 // NOTE:   This helps close a window in time where a polling irp could cause
00041 //         a drive to spin up right after it has powered down. The problem is
00042 //         that SCSIPORT, ATAPI and SBP2 will be in the process of powering
00043 //         down (which may take a few seconds), but won't know that. It would
00044 //         then get a polling irp which will be put into its queue since it
00045 //         the disk isn't powered down yet. Once the disk is powered down it
00046 //         will find the polling irp in the queue and then power up the
00047 //         device to do the poll. They do not want to check if the polling
00048 //         irp has the SRB_NO_KEEP_AWAKE flag here since it is in a critical
00049 //         path and would slow down all I/Os. A better way to fix this
00050 //         would be to serialize the polling and power down irps so that
00051 //         only one of them is sent to the device at a time.
00052 //
00053 #define ClasspCanSendPollingIrp(fdoExtension)                           \
00054                ((fdoExtension->DevicePowerState == PowerDeviceD0) &&  \
00055                 (! fdoExtension->PowerDownInProgress) )
00056 
00057 BOOLEAN
00058 ClasspIsMediaChangeDisabledDueToHardwareLimitation(
00059     IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
00060     IN PUNICODE_STRING RegistryPath
00061     );
00062 
00063 NTSTATUS
00064 ClasspMediaChangeDeviceInstanceOverride(
00065     IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
00066     OUT PBOOLEAN Enabled
00067     );
00068 
00069 BOOLEAN
00070 ClasspIsMediaChangeDisabledForClass(
00071     IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
00072     IN PUNICODE_STRING RegistryPath
00073     );
00074 
00075 VOID
00076 ClasspSetMediaChangeStateEx(
00077     IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
00078     IN MEDIA_CHANGE_DETECTION_STATE NewState,
00079     IN BOOLEAN Wait,
00080     IN BOOLEAN KnownStateChange // can ignore oldstate == unknown
00081     );
00082 
00083 NTSTATUS
00084 ClasspMediaChangeRegistryCallBack(
00085     IN PWSTR ValueName,
00086     IN ULONG ValueType,
00087     IN PVOID ValueData,
00088     IN ULONG ValueLength,
00089     IN PVOID Context,
00090     IN PVOID EntryContext
00091     );
00092 
00093 VOID
00094 ClasspSendMediaStateIrp(
00095     IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
00096     IN PMEDIA_CHANGE_DETECTION_INFO Info,
00097     IN ULONG CountDown
00098     );
00099 
00100 VOID
00101 ClasspFailurePredict(
00102     IN PDEVICE_OBJECT DeviceObject,
00103     IN PFAILURE_PREDICTION_INFO Info
00104     );
00105 
00106 NTSTATUS
00107 ClasspInitializePolling(
00108     IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
00109     IN BOOLEAN AllowDriveToSleep
00110     );
00111 
00112 
00113 #if ALLOC_PRAGMA
00114 
00115 #pragma alloc_text(PAGE, ClassInitializeMediaChangeDetection)
00116 #pragma alloc_text(PAGE, ClassEnableMediaChangeDetection)
00117 #pragma alloc_text(PAGE, ClassDisableMediaChangeDetection)
00118 #pragma alloc_text(PAGE, ClassCleanupMediaChangeDetection)
00119 #pragma alloc_text(PAGE, ClasspMediaChangeRegistryCallBack)
00120 #pragma alloc_text(PAGE, ClasspInitializePolling)
00121 
00122 #pragma alloc_text(PAGE, ClasspIsMediaChangeDisabledDueToHardwareLimitation)
00123 #pragma alloc_text(PAGE, ClasspMediaChangeDeviceInstanceOverride)
00124 #pragma alloc_text(PAGE, ClasspIsMediaChangeDisabledForClass)
00125 
00126 #pragma alloc_text(PAGE, ClassSetFailurePredictionPoll)
00127 #pragma alloc_text(PAGE, ClasspDisableTimer)
00128 #pragma alloc_text(PAGE, ClasspEnableTimer)
00129 
00130 #endif
00131 
00132 // ISSUE -- make this public?
00133 VOID
00134 ClassSendEjectionNotification(
00135     IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
00136     )
00137 {
00138     //
00139     // For post-NT5.1 work, need to move EjectSynchronizationEvent
00140     // to be a MUTEX so we can attempt to grab it here and benefit
00141     // from deadlock detection.  This will allow checking if the media
00142     // has been locked by programs before broadcasting these events.
00143     // (what's the point of broadcasting if the media is not locked?)
00144     //
00145     // This would currently only be a slight optimization.  For post-NT5.1,
00146     // it would allow us to send a single PERSISTENT_PREVENT to MMC devices,
00147     // thereby cleaning up a lot of the ejection code.  Then, when the
00148     // ejection request occured, we could see if any locks for the media
00149     // existed.  if locked, broadcast.  if not, we send the eject irp.
00150     //
00151     
00152     //
00153     // for now, just always broadcast.  make this a public routine,
00154     // so class drivers can add special hacks to broadcast this for their
00155     // non-MMC-compliant devices also from sense codes.
00156     //
00157 
00158     DBGTRACE(ClassDebugTrace, ("ClassSendEjectionNotification: media EJECT_REQUEST"));    
00159     ClasspSendNotification(FdoExtension,
00160                            &GUID_IO_MEDIA_EJECT_REQUEST,
00161                            0,
00162                            NULL);
00163     return;
00164 }
00165 
00166 
00167 VOID
00168 ClasspSendNotification(
00169     IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
00170     IN const GUID * Guid,
00171     IN ULONG  ExtraDataSize,
00172     IN PVOID  ExtraData
00173     )
00174 {
00175     PTARGET_DEVICE_CUSTOM_NOTIFICATION notification;
00176     ULONG requiredSize;
00177         
00178     requiredSize =
00179         (sizeof(TARGET_DEVICE_CUSTOM_NOTIFICATION) - sizeof(UCHAR)) +
00180         ExtraDataSize;
00181 
00182     if (requiredSize > 0x0000ffff) {
00183         // MAX_USHORT, max total size for these events!
00184         KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugWarning,
00185                    "Error sending event: size too large! (%x)\n",
00186                    requiredSize));
00187         return;
00188     }
00189     
00190     notification = ExAllocatePoolWithTag(NonPagedPool,
00191                                          requiredSize,
00192                                          'oNcS');
00193     
00194     //
00195     // if none allocated, exit
00196     //
00197 
00198     if (notification == NULL) {
00199         return;
00200     }
00201 
00202     //
00203     // Prepare and send the request!
00204     //
00205     
00206     RtlZeroMemory(notification, requiredSize);
00207     notification->Version = 1;
00208     notification->Size = (USHORT)(requiredSize);
00209     notification->FileObject = NULL;
00210     notification->NameBufferOffset = -1;
00211     notification->Event = *Guid;
00212     RtlCopyMemory(notification->CustomDataBuffer, ExtraData, ExtraDataSize);
00213 
00214     IoReportTargetDeviceChangeAsynchronous(FdoExtension->LowerPdo,
00215                                            notification,
00216                                            NULL, NULL);
00217     
00218     ExFreePool(notification);
00219     notification = NULL;
00220     return;
00221 }
00222 
00223 
00224 
00225 
00226 /*++////////////////////////////////////////////////////////////////////////////
00227 
00228 ClasspInterpretGesnData()
00229 
00230 Routine Description:
00231 
00232     This routine will interpret the data returned for a GESN command, and
00233     (if appropriate) set the media change event, and broadcast the
00234     appropriate events to user mode for applications who care.
00235 
00236 Arguments:
00237 
00238     FdoExtension - the device
00239     
00240     DataBuffer - the resulting data from a GESN event.
00241         requires at least EIGHT valid bytes (header == 4, data == 4)
00242 
00243     ResendImmediately - whether or not to immediately resend the request.
00244         this should be FALSE if there was no event, FALSE if the reported
00245         event was of the DEVICE BUSY class, else true.
00246 
00247 Return Value:
00248     
00249     None
00250     
00251 Notes:
00252 
00253     DataBuffer must be at least four bytes of valid data (header == 4 bytes),
00254     and have at least eight bytes of allocated memory (all events == 4 bytes).
00255     
00256     The call to StartNextPacket may occur before this routine is completed.
00257     the operational change notifications are informational in nature, and
00258     while useful, are not neccessary to ensure proper operation.  For example,
00259     if the device morphs to no longer supporting WRITE commands, all further
00260     write commands will fail.  There exists a small timing window wherein
00261     IOCTL_IS_DISK_WRITABLE may be called and get an incorrect response.  If
00262     a device supports software write protect, it is expected that the
00263     application can handle such a case.
00264     
00265     NOTE: perhaps setting the updaterequired byte to one should be done here.
00266     if so, it relies upon the setting of a 32-byte value to be an atomic
00267     operation.  unfortunately, there is no simple way to notify a class driver
00268     which wants to know that the device behavior requires updating.
00269     
00270     Not ready events may be sent every second.  For example, if we were
00271     to minimize the number of asynchronous notifications, an application may
00272     register just after a large busy time was reported.  This would then
00273     prevent the application from knowing the device was busy until some
00274     arbitrarily chosen timeout has occurred.  Also, the GESN request would
00275     have to still occur, since it checks for non-busy events (such as user
00276     keybutton presses and media change events) as well.  The specification
00277     states that the lower-numered events get reported first, so busy events,
00278     while repeating, will only be reported when all other events have been
00279     cleared from the device.
00280 
00281 --*/
00282 VOID
00283 ClasspInterpretGesnData(
00284     IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
00285     IN PNOTIFICATION_EVENT_STATUS_HEADER Header,
00286     IN PBOOLEAN ResendImmediately
00287     )
00288 {
00289     PMEDIA_CHANGE_DETECTION_INFO info;
00290     LONG dataLength;
00291     LONG requiredLength;
00292 
00293     info = FdoExtension->MediaChangeDetectionInfo;
00294 
00295     //
00296     // note: don't allocate anything in this routine so that we can
00297     //       always just 'return'.
00298     //
00299 
00300     *ResendImmediately = FALSE;
00301 
00302     if (Header->NEA) {
00303         return;
00304     }
00305     if (Header->NotificationClass == NOTIFICATION_NO_CLASS_EVENTS) {
00306         return;
00307     }
00308 
00309     //
00310     // HACKHACK - REF #0001
00311     // This loop is only taken initially, due to the inability to reliably
00312     // auto-detect drives that report events correctly at boot.  When we
00313     // detect this behavior during the normal course of running, we will
00314     // disable the hack, allowing more efficient use of the system.  This
00315     // should occur "nearly" instantly, as the drive should have multiple
00316     // events queue'd (ie. power, morphing, media).
00317     //
00318 
00319     if (info->Gesn.HackEventMask) {
00320 
00321         //
00322         // all events use the low four bytes of zero to indicate
00323         // that there was no change in status.
00324         //
00325 
00326         UCHAR thisEvent = Header->ClassEventData[0] & 0xf;
00327         UCHAR lowestSetBit;
00328         UCHAR thisEventBit = (1 << Header->NotificationClass);
00329 
00330         ASSERT(TEST_FLAG(info->Gesn.EventMask, thisEventBit));
00331 
00332 
00333         //
00334         // some bit magic here... this results in the lowest set bit only
00335         //
00336 
00337         lowestSetBit = info->Gesn.EventMask;
00338         lowestSetBit &= (info->Gesn.EventMask - 1);
00339         lowestSetBit ^= (info->Gesn.EventMask);
00340 
00341         if (thisEventBit != lowestSetBit) {
00342             
00343             //
00344             // HACKHACK - REF #0001
00345             // the first time we ever see an event set that is not the lowest
00346             // set bit in the request (iow, highest priority), we know that the
00347             // hack is no longer required, as the device is ignoring "no change"
00348             // events when a real event is waiting in the other requested queues.
00349             //
00350 
00351             KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN,
00352                        "Classpnp => GESN::NONE: Compliant drive found, "
00353                        "removing GESN hack (%x, %x)\n",
00354                        thisEventBit, info->Gesn.EventMask));
00355             
00356             info->Gesn.HackEventMask = FALSE;
00357 
00358         } else if (thisEvent == 0) {
00359             
00360             //
00361             // HACKHACK - REF #0001
00362             // note: this hack prevents poorly implemented firmware from constantly
00363             //       returning "No Event".  we do this by cycling through the
00364             //       supported list of events here.
00365             //
00366 
00367             SET_FLAG(info->Gesn.NoChangeEventMask, thisEventBit);
00368             CLEAR_FLAG(info->Gesn.EventMask, thisEventBit);
00369 
00370             //
00371             // if we have cycled through all supported event types, then
00372             // we need to reset the events we are asking about. else we
00373             // want to resend this request immediately in case there was
00374             // another event pending.
00375             //
00376 
00377             if (info->Gesn.EventMask == 0) {
00378                 info->Gesn.EventMask         = info->Gesn.NoChangeEventMask;
00379                 info->Gesn.NoChangeEventMask = 0;
00380             } else {
00381                 *ResendImmediately = TRUE;
00382             }
00383             return;
00384         }
00385 
00386     } // end if (info->Gesn.HackEventMask)
00387 
00388     dataLength =
00389         (Header->EventDataLength[0] << 8) |
00390         (Header->EventDataLength[1] & 0xff);
00391     dataLength -= 2;
00392     requiredLength = 4; // all events are four bytes
00393 
00394     if (dataLength < requiredLength) {
00395         KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugWarning,
00396                    "Classpnp => GESN returned only %x bytes data for fdo %p\n",
00397                    dataLength, FdoExtension->DeviceObject));
00398         return;
00399     }
00400     if (dataLength != requiredLength) {
00401         KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugWarning,
00402                    "Classpnp => GESN returned too many (%x) bytes data for fdo %p\n",
00403                    dataLength, FdoExtension->DeviceObject));
00404         dataLength = 4;
00405     }
00406 
00407 /*
00408     ClasspSendNotification(FdoExtension,
00409                            &GUID_IO_GENERIC_GESN_EVENT,
00410                            sizeof(NOTIFICATION_EVENT_STATUS_HEADER) + dataLength,
00411                            Header)
00412 */                           
00413 
00414     switch (Header->NotificationClass) {
00415 
00416     case NOTIFICATION_EXTERNAL_REQUEST_CLASS_EVENTS: { // 0x3
00417         
00418         PNOTIFICATION_EXTERNAL_STATUS externalInfo = 
00419             (PNOTIFICATION_EXTERNAL_STATUS)(Header->ClassEventData);
00420         DEVICE_EVENT_EXTERNAL_REQUEST externalData;
00421 
00422         //
00423         // unfortunately, due to time constraints, we will only notify
00424         // about keys being pressed, and not released.  this makes keys
00425         // single-function, but simplifies the code significantly.
00426         //
00427         
00428         if (externalInfo->ExternalEvent !=
00429             NOTIFICATION_EXTERNAL_EVENT_BUTTON_DOWN) {
00430             break;
00431         }
00432         
00433         *ResendImmediately = TRUE;
00434         KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN,
00435                    "Classpnp => GESN::EXTERNAL: Event: %x Status %x Req %x\n",
00436                    externalInfo->ExternalEvent, externalInfo->ExternalStatus,
00437                    (externalInfo->Request[0] >> 8) | externalInfo->Request[1]
00438                    ));
00439 
00440         RtlZeroMemory(&externalData, sizeof(DEVICE_EVENT_EXTERNAL_REQUEST));
00441         externalData.Version = 1;
00442         externalData.DeviceClass = 0;
00443         externalData.ButtonStatus = externalInfo->ExternalEvent;
00444         externalData.Request =
00445             (externalInfo->Request[0] << 8) |
00446             (externalInfo->Request[1] & 0xff);
00447         KeQuerySystemTime(&(externalData.SystemTime));
00448         externalData.SystemTime.QuadPart *= (LONGLONG)KeQueryTimeIncrement();
00449 
00450         DBGTRACE(ClassDebugTrace, ("ClasspInterpretGesnData: media DEVICE_EXTERNAL_REQUEST"));        
00451         ClasspSendNotification(FdoExtension,
00452                                &GUID_IO_DEVICE_EXTERNAL_REQUEST,
00453                                sizeof(DEVICE_EVENT_EXTERNAL_REQUEST),
00454                                &externalData);
00455         return;
00456     }
00457     
00458     case NOTIFICATION_MEDIA_STATUS_CLASS_EVENTS: { // 0x4
00459         
00460         PNOTIFICATION_MEDIA_STATUS mediaInfo =
00461             (PNOTIFICATION_MEDIA_STATUS)(Header->ClassEventData);
00462         
00463         if (mediaInfo->MediaEvent == NOTIFICATION_MEDIA_EVENT_NO_CHANGE) {
00464             break;
00465         }
00466         
00467         *ResendImmediately = TRUE;
00468         KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN,
00469                    "Classpnp => GESN::MEDIA: Event: %x Status %x\n",
00470                    mediaInfo->MediaEvent, mediaInfo->MediaStatus));
00471         
00472         if ((mediaInfo->MediaEvent == NOTIFICATION_MEDIA_EVENT_NEW_MEDIA) ||
00473             (mediaInfo->MediaEvent == NOTIFICATION_MEDIA_EVENT_MEDIA_CHANGE)) {
00474 
00475 
00476             if (TEST_FLAG(FdoExtension->DeviceObject->Characteristics,
00477                           FILE_REMOVABLE_MEDIA) &&
00478                 (ClassGetVpb(FdoExtension->DeviceObject) != NULL) &&
00479                 (ClassGetVpb(FdoExtension->DeviceObject)->Flags & VPB_MOUNTED)
00480                 ) {
00481 
00482                 SET_FLAG(FdoExtension->DeviceObject->Flags, DO_VERIFY_VOLUME);
00483 
00484             }
00485             InterlockedIncrement(&FdoExtension->MediaChangeCount);
00486             ClasspSetMediaChangeStateEx(FdoExtension,
00487                                         MediaPresent,
00488                                         FALSE,
00489                                         TRUE);
00490 
00491         } else if (mediaInfo->MediaEvent == NOTIFICATION_MEDIA_EVENT_MEDIA_REMOVAL) {
00492             
00493             ClasspSetMediaChangeStateEx(FdoExtension,
00494                                         MediaNotPresent,
00495                                         FALSE,
00496                                         TRUE);
00497         
00498         } else if (mediaInfo->MediaEvent == NOTIFICATION_MEDIA_EVENT_EJECT_REQUEST) {
00499 
00500             KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugError,
00501                        "Classpnp => GESN Ejection request received!\n"));
00502             ClassSendEjectionNotification(FdoExtension);
00503         
00504         }
00505         break;
00506 
00507     }
00508     
00509     case NOTIFICATION_DEVICE_BUSY_CLASS_EVENTS: { // lowest priority events...
00510         
00511         PNOTIFICATION_BUSY_STATUS busyInfo =
00512             (PNOTIFICATION_BUSY_STATUS)(Header->ClassEventData);
00513         DEVICE_EVENT_BECOMING_READY busyData;
00514         
00515         //
00516         // NOTE: we never actually need to immediately retry for these
00517         //       events: if one exists, the device is busy, and if not,
00518         //       we still don't want to retry.
00519         //
00520 
00521         if (busyInfo->DeviceBusyStatus == NOTIFICATION_BUSY_STATUS_NO_EVENT) {
00522             break;
00523         }
00524         
00525         //
00526         // else we want to report the approximated time till it's ready.
00527         //
00528 
00529         RtlZeroMemory(&busyData, sizeof(DEVICE_EVENT_BECOMING_READY));
00530         busyData.Version = 1;
00531         busyData.Reason = busyInfo->DeviceBusyStatus;
00532         busyData.Estimated100msToReady = (busyInfo->Time[0] << 8) |
00533                                          (busyInfo->Time[1] & 0xff);
00534         
00535         KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN,
00536                    "Classpnp => GESN::BUSY: Event: %x Status %x Time %x\n",
00537                    busyInfo->DeviceBusyEvent, busyInfo->DeviceBusyStatus,
00538                    busyData.Estimated100msToReady
00539                    ));
00540 
00541         DBGTRACE(ClassDebugTrace, ("ClasspInterpretGesnData: media BECOMING_READY"));                
00542         ClasspSendNotification(FdoExtension,
00543                                &GUID_IO_DEVICE_BECOMING_READY,
00544                                sizeof(DEVICE_EVENT_BECOMING_READY),
00545                                &busyData);
00546         break;
00547     }
00548     
00549     default: {
00550         
00551         break;
00552 
00553     }
00554     
00555     } // end switch on notification class    
00556     return;
00557 }
00558 
00559 /*++////////////////////////////////////////////////////////////////////////////
00560 
00561 ClasspInternalSetMediaChangeState()
00562 
00563 Routine Description:
00564 
00565     This routine will (if appropriate) set the media change event for the
00566     device.  The event will be set if the media state is changed and
00567     media change events are enabled.  Otherwise the media state will be
00568     tracked but the event will not be set.
00569 
00570     This routine will lock out the other media change routines if possible
00571     but if not a media change notification may be lost after the enable has
00572     been completed.
00573 
00574 Arguments:
00575 
00576     FdoExtension - the device
00577 
00578     MediaPresent - indicates whether the device has media inserted into it
00579                    (TRUE) or not (FALSE).
00580  
00581 Return Value:
00582 
00583     none
00584 
00585 --*/
00586 VOID
00587 ClasspInternalSetMediaChangeState(
00588     IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
00589     IN MEDIA_CHANGE_DETECTION_STATE NewState,
00590     IN BOOLEAN KnownStateChange // can ignore oldstate == unknown
00591     )
00592 {
00593 #if DBG
00594     PUCHAR states[] = {"Unknown", "Present", "Not Present"};
00595 #endif
00596     MEDIA_CHANGE_DETECTION_STATE oldMediaState;
00597     PMEDIA_CHANGE_DETECTION_INFO info = FdoExtension->MediaChangeDetectionInfo;
00598     ULONG data;
00599     NTSTATUS status;
00600 
00601     ASSERT((NewState >= MediaUnknown) && (NewState <= MediaNotPresent));
00602 
00603     if(info == NULL) {
00604         return;
00605     }
00606 
00607     oldMediaState = InterlockedExchange(
00608         (PLONG)(&info->MediaChangeDetectionState),
00609         (LONG)NewState);
00610 
00611     if((oldMediaState == MediaUnknown) && (!KnownStateChange)) {
00612 
00613         //
00614         // The media was in an indeterminate state before - don't notify for
00615         // this change.
00616         //
00617 
00618         DebugPrint((ClassDebugMCN,
00619                     "ClassSetMediaChangeState: State was unknown - this may "
00620                     "not be a change\n"));
00621         return;
00622 
00623     } else if(oldMediaState == NewState) {
00624 
00625         //
00626         // Media is in the same state it was before.
00627         //
00628 
00629         return;
00630     }
00631 
00632     if(info->MediaChangeDetectionDisableCount != 0) {
00633 
00634         DBGTRACE(ClassDebugMCN,
00635                     ("ClassSetMediaChangeState: MCN not enabled, state "
00636                     "changed from %s to %s\n",
00637                     states[oldMediaState], states[NewState]));
00638         return;
00639 
00640     }
00641 
00642     DBGTRACE(ClassDebugMCN,
00643                 ("ClassSetMediaChangeState: State change from %s to %s\n",
00644                 states[oldMediaState], states[NewState]));
00645 
00646     //
00647     // make the data useful -- it used to always be zero.
00648     //
00649     data = FdoExtension->MediaChangeCount;
00650 
00651     if (NewState == MediaPresent) {
00652 
00653         DBGTRACE(ClassDebugTrace, ("ClasspInternalSetMediaChangeState: media ARRIVAL"));
00654         ClasspSendNotification(FdoExtension,
00655                                &GUID_IO_MEDIA_ARRIVAL,
00656                                sizeof(ULONG),
00657                                &data);
00658 
00659     } 
00660     else if (NewState == MediaNotPresent) {
00661 
00662         DBGTRACE(ClassDebugTrace, ("ClasspInternalSetMediaChangeState: media REMOVAL"));
00663         ClasspSendNotification(FdoExtension,
00664                                &GUID_IO_MEDIA_REMOVAL,
00665                                sizeof(ULONG),
00666                                &data);
00667 
00668     } else {
00669 
00670         //
00671         // Don't notify of changed going to unknown.
00672         //
00673 
00674         return;
00675     }
00676     
00677     return;
00678 } // end ClasspInternalSetMediaChangeState()
00679 
00680 /*++////////////////////////////////////////////////////////////////////////////
00681 
00682 ClassSetMediaChangeState()
00683 
00684 Routine Description:
00685 
00686     This routine will (if appropriate) set the media change event for the
00687     device.  The event will be set if the media state is changed and
00688     media change events are enabled.  Otherwise the media state will be
00689     tracked but the event will not be set.
00690 
00691     This routine will lock out the other media change routines if possible
00692     but if not a media change notification may be lost after the enable has
00693     been completed.
00694 
00695 Arguments:
00696 
00697     FdoExtension - the device
00698 
00699     MediaPresent - indicates whether the device has media inserted into it
00700                    (TRUE) or not (FALSE).
00701 
00702     Wait - indicates whether the function should wait until it can acquire
00703            the synchronization lock or not.
00704 
00705 Return Value:
00706 
00707     none
00708 
00709 --*/
00710 VOID
00711 ClasspSetMediaChangeStateEx(
00712     IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
00713     IN MEDIA_CHANGE_DETECTION_STATE NewState,
00714     IN BOOLEAN Wait,
00715     IN BOOLEAN KnownStateChange // can ignore oldstate == unknown
00716     )
00717 {
00718     PMEDIA_CHANGE_DETECTION_INFO info = FdoExtension->MediaChangeDetectionInfo;
00719     LARGE_INTEGER zero;
00720     NTSTATUS status;
00721 
00722     DBGTRACE(ClassDebugMCN, ("> ClasspSetMediaChangeStateEx"));
00723 
00724     //
00725     // Reset SMART status on media removal as the old status may not be
00726     // valid when there is no media in the device or when new media is
00727     // inserted.
00728     //
00729 
00730     if (NewState == MediaNotPresent) {
00731 
00732         FdoExtension->FailurePredicted = FALSE;
00733         FdoExtension->FailureReason = 0;
00734 
00735     }
00736 
00737 
00738     zero.QuadPart = 0;
00739 
00740     if(info == NULL) {
00741         return;
00742     }
00743 
00744     status = KeWaitForMutexObject(&info->MediaChangeMutex,
00745                                   Executive,
00746                                   KernelMode,
00747                                   FALSE,
00748                                   ((Wait == TRUE) ? NULL : &zero));
00749 
00750     if(status == STATUS_TIMEOUT) {
00751 
00752         //
00753         // Someone else is in the process of setting the media state
00754         //
00755 
00756         DBGWARN(("ClasspSetMediaChangeStateEx - timed out waiting for mutex"));
00757         return;
00758     }
00759 
00760     //
00761     // Change the media present state and signal an event, if applicable
00762     //
00763 
00764     ClasspInternalSetMediaChangeState(FdoExtension, NewState, KnownStateChange);
00765 
00766     KeReleaseMutex(&info->MediaChangeMutex, FALSE);
00767 
00768     DBGTRACE(ClassDebugMCN, ("< ClasspSetMediaChangeStateEx"));
00769 
00770     return;
00771 } // end ClassSetMediaChangeStateEx()
00772 VOID
00773 ClassSetMediaChangeState(
00774     IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
00775     IN MEDIA_CHANGE_DETECTION_STATE NewState,
00776     IN BOOLEAN Wait
00777     )
00778 {
00779     ClasspSetMediaChangeStateEx(FdoExtension, NewState, Wait, FALSE);
00780     return;
00781 }
00782 
00783 /*++////////////////////////////////////////////////////////////////////////////
00784 
00785 ClasspMediaChangeDetectionCompletion()
00786 
00787 Routine Description:
00788 
00789     This routine handles the completion of the test unit ready irps used to
00790     determine if the media has changed.  If the media has changed, this code
00791     signals the named event to wake up other system services that react to
00792     media change (aka AutoPlay).
00793 
00794 Arguments:
00795 
00796     DeviceObject - the object for the completion
00797     Irp - the IRP being completed
00798     Context - the SRB from the IRP
00799 
00800 Return Value:
00801 
00802     NTSTATUS
00803 
00804 --*/
00805 NTSTATUS
00806 ClasspMediaChangeDetectionCompletion(
00807     PDEVICE_OBJECT DeviceObject,
00808     PIRP Irp,
00809     PSCSI_REQUEST_BLOCK Srb
00810     )
00811 {
00812     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
00813     PCLASS_PRIVATE_FDO_DATA fdoData;
00814     PMEDIA_CHANGE_DETECTION_INFO info;
00815     PIO_STACK_LOCATION  nextIrpStack;
00816     NTSTATUS status;
00817     BOOLEAN retryImmediately = FALSE;
00818 
00819     //
00820     // Since the class driver created this request, it's completion routine
00821     // will not get a valid device object handed in.  Use the one in the
00822     // irp stack instead
00823     //
00824 
00825     DeviceObject = IoGetCurrentIrpStackLocation(Irp)->DeviceObject;
00826     fdoExtension = DeviceObject->DeviceExtension;
00827     fdoData = fdoExtension->PrivateFdoData;
00828     info         = fdoExtension->MediaChangeDetectionInfo;
00829 
00830     ASSERT(info->MediaChangeIrp != NULL);
00831     ASSERT(!TEST_FLAG(Srb->SrbStatus, SRB_STATUS_QUEUE_FROZEN));
00832     DBGTRACE(ClassDebugMCN, ("> ClasspMediaChangeDetectionCompletion: Device %p completed MCN irp %p.", DeviceObject, Irp));
00833 
00834     /*
00835      *  HACK for IoMega 2GB Jaz drive:
00836      *  This drive spins down on its own to preserve the media.  
00837      *  When spun down, TUR fails with 2/4/0 (SCSI_SENSE_NOT_READY/SCSI_ADSENSE_LUN_NOT_READY/?).
00838      *  ClassInterpretSenseInfo would then call ClassSendStartUnit to spin the media up, which defeats the
00839      *  purpose of the spindown.
00840      *  So in this case, make this into a successful TUR.  
00841      *  This allows the drive to stay spun down until it is actually accessed again.
00842      *  (If the media were actually removed, TUR would fail with 2/3a/0 ).
00843      *  This hack only applies to drives with the CAUSE_NOT_REPORTABLE_HACK bit set; this
00844      *  is set by disk.sys when HackCauseNotReportableHack is set for the drive in its BadControllers list.
00845      */
00846     if ((SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_SUCCESS) &&
00847         TEST_FLAG(fdoExtension->ScanForSpecialFlags, CLASS_SPECIAL_CAUSE_NOT_REPORTABLE_HACK) &&
00848         (Srb->SenseInfoBufferLength >= RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseCode))){
00849         
00850         PSENSE_DATA senseData = Srb->SenseInfoBuffer;
00851         
00852         if ((senseData->SenseKey == SCSI_SENSE_NOT_READY) && 
00853             (senseData->AdditionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY)){
00854             Srb->SrbStatus = SRB_STATUS_SUCCESS;
00855         }
00856     }       
00857         
00858 
00859     //
00860     // use ClassInterpretSenseInfo() to check for media state, and also
00861     // to call ClassError() with correct parameters.
00862     //
00863     status = STATUS_SUCCESS;
00864     if (SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_SUCCESS) {
00865 
00866         DBGTRACE(ClassDebugMCN, ("ClasspMediaChangeDetectionCompletion - failed - srb status=%s, sense=%s/%s/%s.", DBGGETSRBSTATUSSTR(Srb), DBGGETSENSECODESTR(Srb), DBGGETADSENSECODESTR(Srb), DBGGETADSENSEQUALIFIERSTR(Srb)));
00867 
00868         ClassInterpretSenseInfo(DeviceObject,
00869                                 Srb,
00870                                 IRP_MJ_SCSI,
00871                                 0,
00872                                 0,
00873                                 &status,
00874                                 NULL);
00875 
00876     } 
00877     else {
00878         
00879         fdoData->LoggedTURFailureSinceLastIO = FALSE;
00880         
00881         if (!info->Gesn.Supported) {
00882 
00883             DBGTRACE(ClassDebugMCN, ("ClasspMediaChangeDetectionCompletion - succeeded and GESN NOT supported, setting MediaPresent."));
00884         
00885             //
00886             // success != media for GESN case
00887             //
00888 
00889             ClassSetMediaChangeState(fdoExtension, MediaPresent, FALSE);
00890 
00891         }
00892         else {
00893             DBGTRACE(ClassDebugMCN, ("ClasspMediaChangeDetectionCompletion - succeeded (GESN supported)."));
00894         }
00895     }
00896     
00897     if (info->Gesn.Supported) {
00898 
00899         if (status == STATUS_DATA_OVERRUN) {
00900             DBGTRACE(ClassDebugMCN, ("ClasspMediaChangeDetectionCompletion - Overrun"));
00901             status = STATUS_SUCCESS;
00902         }
00903 
00904         if (!NT_SUCCESS(status)) {            
00905             DBGTRACE(ClassDebugMCN, ("ClasspMediaChangeDetectionCompletion: GESN failed with status %x", status));
00906         } else {
00907 
00908             //
00909             // for GESN, need to interpret the results of the data.
00910             // this may also require an immediate retry
00911             //
00912             
00913             if (Irp->IoStatus.Information == 8 ) {
00914                 ClasspInterpretGesnData(fdoExtension,
00915                                         (PVOID)info->Gesn.Buffer,
00916                                         &retryImmediately);
00917             }
00918 
00919         } // end of NT_SUCCESS(status)
00920 
00921     } // end of Info->Gesn.Supported
00922 
00923     //
00924     // free port-allocated sense buffer, if any.
00925     //
00926 
00927     if (PORT_ALLOCATED_SENSE(fdoExtension, Srb)) {
00928         FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, Srb);
00929     }
00930 
00931     //
00932     // Remember the IRP and SRB for use the next time.
00933     //
00934 
00935     ASSERT(IoGetNextIrpStackLocation(Irp));
00936     IoGetNextIrpStackLocation(Irp)->Parameters.Scsi.Srb = Srb;
00937 
00938     //
00939     // Reset the MCN timer.
00940     //
00941 
00942     ClassResetMediaChangeTimer(fdoExtension);
00943 
00944     //
00945     // run a sanity check to make sure we're not recursing continuously
00946     //
00947 
00948     if (retryImmediately) {
00949         
00950         info->MediaChangeRetryCount++;
00951         if (info->MediaChangeRetryCount > MAXIMUM_IMMEDIATE_MCN_RETRIES) {
00952             ASSERT(!"Recursing too often in MCN?");
00953             info->MediaChangeRetryCount = 0;
00954             retryImmediately = FALSE;
00955         }
00956 
00957     } else {
00958         
00959         info->MediaChangeRetryCount = 0;
00960 
00961     }
00962 
00963 
00964     //
00965     // release the remove lock....
00966     //
00967 
00968     {
00969         UCHAR uniqueValue;
00970         ClassAcquireRemoveLock(DeviceObject, (PIRP)(&uniqueValue));
00971         ClassReleaseRemoveLock(DeviceObject, Irp);
00972 
00973         
00974         //
00975         // set the irp as not in use
00976         //
00977         {
00978             volatile LONG irpWasInUse;
00979             irpWasInUse = InterlockedCompareExchange(&info->MediaChangeIrpInUse, 0, 1);
00980             #if _MSC_FULL_VER != 13009111        // This compiler always takes the wrong path here.
00981                 ASSERT(irpWasInUse);
00982             #endif
00983         }
00984 
00985         //
00986         // now send it again before we release our last remove lock
00987         //
00988 
00989         if (retryImmediately) {
00990             ClasspSendMediaStateIrp(fdoExtension, info, 0);
00991         }
00992         else {
00993             DBGTRACE(ClassDebugMCN, ("ClasspMediaChangeDetectionCompletion - not retrying immediately"));
00994         }
00995         
00996         //
00997         // release the temporary remove lock
00998         //
00999         
01000         ClassReleaseRemoveLock(DeviceObject, (PIRP)(&uniqueValue));
01001     }
01002 
01003     DBGTRACE(ClassDebugMCN, ("< ClasspMediaChangeDetectionCompletion"));
01004 
01005     return STATUS_MORE_PROCESSING_REQUIRED;
01006 } 
01007 
01008 /*++////////////////////////////////////////////////////////////////////////////
01009 
01010 ClasspSendTestUnitIrp() - ISSUE-2000/02/20-henrygab - not documented
01011 
01012 Routine Description:
01013 
01014     This routine
01015 
01016 Arguments:
01017 
01018     DeviceObject - 
01019     Irp - 
01020 
01021 Return Value:
01022 
01023 
01024 --*/
01025 PIRP
01026 ClasspPrepareMcnIrp(
01027     IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
01028     IN PMEDIA_CHANGE_DETECTION_INFO Info,
01029     IN BOOLEAN UseGesn
01030 )
01031 {
01032     PSCSI_REQUEST_BLOCK srb;
01033     PIO_STACK_LOCATION irpStack;
01034     PIO_STACK_LOCATION nextIrpStack;
01035     NTSTATUS status;
01036     PCDB cdb;
01037     PIRP irp;
01038     PVOID buffer;
01039 
01040     //
01041     // Setup the IRP to perform a test unit ready.
01042     //
01043 
01044     irp = Info->MediaChangeIrp;
01045 
01046     ASSERT(irp);
01047 
01048     if (irp == NULL) {
01049         return NULL;
01050     }
01051 
01052     //
01053     // don't keep sending this if the device is being removed.
01054     //
01055 
01056     status = ClassAcquireRemoveLock(FdoExtension->DeviceObject, irp);
01057     if (status == REMOVE_COMPLETE) {
01058         ASSERT(status != REMOVE_COMPLETE);
01059         return NULL;
01060     } 
01061     else if (status == REMOVE_PENDING) {
01062         ClassReleaseRemoveLock(FdoExtension->DeviceObject, irp);
01063         return NULL;
01064     }
01065     else {
01066         ASSERT(status == NO_REMOVE);
01067     }
01068 
01069     irp->IoStatus.Status = STATUS_SUCCESS;
01070     irp->IoStatus.Information = 0;
01071     irp->Flags = 0;
01072     irp->UserBuffer = NULL;
01073 
01074     //
01075     // If the irp is sent down when the volume needs to be
01076     // verified, CdRomUpdateGeometryCompletion won't complete
01077     // it since it's not associated with a thread.  Marking
01078     // it to override the verify causes it always be sent
01079     // to the port driver
01080     //
01081 
01082     irpStack = IoGetCurrentIrpStackLocation(irp);
01083     irpStack->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
01084 
01085     nextIrpStack = IoGetNextIrpStackLocation(irp);
01086     nextIrpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
01087     nextIrpStack->Parameters.Scsi.Srb = &(Info->MediaChangeSrb);
01088 
01089     //
01090     // Prepare the SRB for execution.
01091     //
01092 
01093     srb = nextIrpStack->Parameters.Scsi.Srb;
01094     buffer = srb->SenseInfoBuffer;
01095     RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
01096     RtlZeroMemory(buffer, SENSE_BUFFER_SIZE);
01097 
01098 
01099     srb->QueueTag = SP_UNTAGGED;
01100     srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
01101     srb->Length = sizeof(SCSI_REQUEST_BLOCK);
01102     srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
01103     srb->SenseInfoBuffer = buffer;
01104     srb->SrbStatus = 0;
01105     srb->ScsiStatus = 0;
01106     srb->OriginalRequest = irp;
01107     srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
01108     
01109     srb->SrbFlags = FdoExtension->SrbFlags;
01110     SET_FLAG(srb->SrbFlags, Info->SrbFlags);
01111 
01112     srb->TimeOutValue = FdoExtension->TimeOutValue * 2;
01113     
01114     if (srb->TimeOutValue == 0) {
01115 
01116         if (FdoExtension->TimeOutValue == 0) {
01117 
01118             KdPrintEx((DPFLTR_CLASSPNP_ID, DPFLTR_ERROR_LEVEL,
01119                        "ClassSendTestUnitIrp: FdoExtension->TimeOutValue "
01120                        "is set to zero?! -- resetting to 10\n"));
01121             srb->TimeOutValue = 10 * 2;  // reasonable default
01122         
01123         } else {
01124             
01125             KdPrintEx((DPFLTR_CLASSPNP_ID, DPFLTR_ERROR_LEVEL,
01126                        "ClassSendTestUnitIrp: Someone set "
01127                        "srb->TimeOutValue to zero?! -- resetting to %x\n",
01128                        FdoExtension->TimeOutValue * 2));
01129             srb->TimeOutValue = FdoExtension->TimeOutValue * 2;
01130 
01131         }
01132 
01133     }
01134     
01135     if (!UseGesn) {
01136         
01137         srb->CdbLength = 6;
01138         srb->DataTransferLength = 0;
01139         SET_FLAG(srb->SrbFlags, SRB_FLAGS_NO_DATA_TRANSFER);
01140         nextIrpStack->Parameters.DeviceIoControl.IoControlCode =
01141                 IOCTL_SCSI_EXECUTE_NONE;
01142         srb->DataBuffer = NULL;
01143         srb->DataTransferLength = 0;
01144         irp->MdlAddress = NULL;
01145         
01146         cdb = (PCDB) &srb->Cdb[0];
01147         cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY;
01148 
01149     } else {
01150         
01151         ASSERT(Info->Gesn.Buffer);
01152 
01153         srb->TimeOutValue = GESN_TIMEOUT_VALUE; // much shorter timeout for GESN
01154         
01155         srb->CdbLength = 10;
01156         SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_IN);
01157         nextIrpStack->Parameters.DeviceIoControl.IoControlCode =
01158                 IOCTL_SCSI_EXECUTE_IN;
01159         srb->DataBuffer = Info->Gesn.Buffer;
01160         srb->DataTransferLength = Info->Gesn.BufferSize;
01161         irp->MdlAddress = Info->Gesn.Mdl;
01162 
01163         cdb = (PCDB) &srb->Cdb[0];
01164         cdb->GET_EVENT_STATUS_NOTIFICATION.OperationCode =
01165             SCSIOP_GET_EVENT_STATUS;
01166         cdb->GET_EVENT_STATUS_NOTIFICATION.Immediate = 1;
01167         cdb->GET_EVENT_STATUS_NOTIFICATION.EventListLength[0] =
01168             (UCHAR)((Info->Gesn.BufferSize) >> 8);
01169         cdb->GET_EVENT_STATUS_NOTIFICATION.EventListLength[1] =
01170             (UCHAR)((Info->Gesn.BufferSize) & 0xff);
01171         cdb->GET_EVENT_STATUS_NOTIFICATION.NotificationClassRequest =
01172             Info->Gesn.EventMask;
01173 
01174     }
01175 
01176     IoSetCompletionRoutine(irp,
01177                            ClasspMediaChangeDetectionCompletion,
01178                            srb,
01179                            TRUE,
01180                            TRUE,
01181                            TRUE);
01182 
01183     return irp;
01184 
01185 }
01186 
01187 /*++////////////////////////////////////////////////////////////////////////////
01188 
01189 ClasspSendMediaStateIrp() - ISSUE-2000/02/20-henrygab - not documented
01190 
01191 Routine Description:
01192 
01193     This routine
01194 
01195 Arguments:
01196 
01197     DeviceObject - 
01198     Irp - 
01199 
01200 Return Value:
01201 
01202 --*/
01203 VOID
01204 ClasspSendMediaStateIrp(
01205     IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
01206     IN PMEDIA_CHANGE_DETECTION_INFO Info,
01207     IN ULONG CountDown
01208     )
01209 {
01210     BOOLEAN requestPending = FALSE;
01211     LONG irpInUse;
01212     LARGE_INTEGER zero;
01213     NTSTATUS status;
01214 
01215     DBGTRACE(ClassDebugMCN, ("> ClasspSendMediaStateIrp"));
01216 
01217     if (((FdoExtension->CommonExtension.CurrentState != IRP_MN_START_DEVICE) ||
01218          (FdoExtension->DevicePowerState != PowerDeviceD0)
01219          ) &&
01220         (!Info->MediaChangeIrpLost)) {
01221 
01222         //
01223         // the device may be stopped, powered down, or otherwise queueing io,
01224         // so should not timeout the autorun irp (yet) -- set to zero ticks.
01225         // scattered code relies upon this to not prematurely "lose" an
01226         // autoplay irp that was queued.
01227         //
01228 
01229         Info->MediaChangeIrpTimeInUse = 0;
01230     }
01231 
01232     //
01233     // if the irp is not in use, mark it as such.
01234     //
01235 
01236     irpInUse = InterlockedCompareExchange(&Info->MediaChangeIrpInUse, 1, 0);
01237 
01238     if (irpInUse) {
01239 
01240         LONG timeInUse;
01241 
01242         timeInUse = InterlockedIncrement(&Info->MediaChangeIrpTimeInUse);
01243 
01244         DebugPrint((ClassDebugMCN, "ClasspSendMediaStateIrp: irp in use for "
01245                     "%x seconds when synchronizing for MCD\n", timeInUse));
01246 
01247         if (Info->MediaChangeIrpLost == FALSE) {
01248 
01249             if (timeInUse > MEDIA_CHANGE_TIMEOUT_TIME) {
01250 
01251                 //
01252                 // currently set to five minutes.  hard to imagine a drive
01253                 // taking that long to spin up.
01254                 //
01255 
01256                 DebugPrint((ClassDebugError,
01257                             "CdRom%d: Media Change Notification has lost "
01258                             "it's irp and doesn't know where to find it.  "
01259                             "Leave it alone and it'll come home dragging "
01260                             "it's stack behind it.\n",
01261                             FdoExtension->DeviceNumber));
01262                 Info->MediaChangeIrpLost = TRUE;
01263             }
01264         }
01265 
01266         DBGTRACE(ClassDebugMCN, ("< ClasspSendMediaStateIrp - irpInUse"));
01267         return;
01268 
01269     }
01270 
01271     TRY {
01272 
01273         if (Info->MediaChangeDetectionDisableCount != 0) {
01274             DebugPrint((ClassDebugTrace, "ClassCheckMediaState: device %p has "
01275                         " detection disabled \n", FdoExtension->DeviceObject));
01276             LEAVE;
01277         }
01278 
01279         if (FdoExtension->DevicePowerState != PowerDeviceD0) {
01280 
01281             if (TEST_FLAG(Info->SrbFlags, SRB_FLAGS_NO_KEEP_AWAKE)) {
01282                 DebugPrint((ClassDebugMCN,
01283                             "ClassCheckMediaState: device %p is powered "
01284                             "down and flags are set to let it sleep\n",
01285                             FdoExtension->DeviceObject));
01286                 ClassResetMediaChangeTimer(FdoExtension);
01287                 LEAVE;
01288             }
01289 
01290             //
01291             // NOTE: we don't increment the time in use until our power state
01292             // changes above.  this way, we won't "lose" the autoplay irp.
01293             // it's up to the lower driver to determine if powering up is a
01294             // good idea.
01295             //
01296 
01297             DebugPrint((ClassDebugMCN,
01298                         "ClassCheckMediaState: device %p needs to powerup "
01299                         "to handle this io (may take a few extra seconds).\n",
01300                         FdoExtension->DeviceObject));
01301 
01302         }
01303 
01304         Info->MediaChangeIrpTimeInUse = 0;
01305         Info->MediaChangeIrpLost = FALSE;
01306 
01307         if (CountDown == 0) {
01308 
01309             PIRP irp;
01310 
01311             DebugPrint((ClassDebugTrace,
01312                         "ClassCheckMediaState: timer expired\n"));
01313 
01314             if (Info->MediaChangeDetectionDisableCount != 0) {
01315                 DebugPrint((ClassDebugTrace,
01316                             "ClassCheckMediaState: detection disabled\n"));
01317                 LEAVE;
01318             }
01319 
01320             //
01321             // Prepare the IRP for the test unit ready
01322             //
01323 
01324             irp = ClasspPrepareMcnIrp(FdoExtension,
01325                                       Info,
01326                                       Info->Gesn.Supported);
01327 
01328             //
01329             // Issue the request.
01330             //
01331 
01332             DebugPrint((ClassDebugTrace,
01333                         "ClasspSendMediaStateIrp: Device %p getting TUR "
01334                         " irp %p\n", FdoExtension->DeviceObject, irp));
01335 
01336             if (irp == NULL) {
01337                 LEAVE;
01338             }
01339 
01340 
01341             //
01342             // note: if we send it to the class dispatch routines, there is
01343             //       a timing window here (since they grab the remove lock)
01344             //       where we'd be removed. ELIMINATE the window by grabbing
01345             //       the lock ourselves above and sending it to the lower
01346             //       device object directly or to the device's StartIo
01347             //       routine (which doesn't acquire the lock).
01348             //
01349             
01350             requestPending = TRUE;
01351 
01352             DBGTRACE(ClassDebugMCN, ("  ClasspSendMediaStateIrp - calling IoCallDriver."));
01353             IoCallDriver(FdoExtension->CommonExtension.LowerDeviceObject, irp);
01354         }
01355 
01356     } FINALLY {
01357 
01358         if(requestPending == FALSE) {
01359             irpInUse = InterlockedCompareExchange(&Info->MediaChangeIrpInUse, 0, 1);
01360             #if _MSC_FULL_VER != 13009111        // This compiler always takes the wrong path here.
01361                 ASSERT(irpInUse);
01362             #endif
01363         }
01364 
01365     }
01366 
01367     DBGTRACE(ClassDebugMCN, ("< ClasspSendMediaStateIrp"));
01368     
01369     return;
01370 } // end ClasspSendMediaStateIrp()
01371 
01372 /*++////////////////////////////////////////////////////////////////////////////
01373 
01374 ClassCheckMediaState()
01375 
01376 Routine Description:
01377 
01378     This routine is called by the class driver to test for a media change
01379     condition and/or poll for disk failure prediction.  It should be called
01380     from the class driver's IO timer routine once per second.
01381 
01382 Arguments:
01383 
01384     FdoExtension - the device extension
01385 
01386 Return Value:
01387 
01388     none
01389 
01390 --*/
01391 VOID
01392 ClassCheckMediaState(
01393     IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
01394     )
01395 {
01396     PMEDIA_CHANGE_DETECTION_INFO info = FdoExtension->MediaChangeDetectionInfo;
01397     LONG countDown;
01398 
01399     if(info == NULL) {
01400         DebugPrint((ClassDebugTrace,
01401                     "ClassCheckMediaState: detection not enabled\n"));
01402         return;
01403     }
01404 
01405     //
01406     // Media change support is active and the IRP is waiting. Decrement the
01407     // timer.  There is no MP protection on the timer counter.  This code
01408     // is the only code that will manipulate the timer counter and only one
01409     // instance of it should be running at any given time.
01410     //
01411 
01412     countDown = InterlockedDecrement(&(info->MediaChangeCountDown));
01413 
01414     //
01415     // Try to acquire the media change event.  If we can't do it immediately
01416     // then bail out and assume the caller will try again later.
01417     //
01418     ClasspSendMediaStateIrp(FdoExtension,
01419                             info,
01420                             countDown);
01421 
01422     return;
01423 } // end ClassCheckMediaState()
01424 
01425 /*++////////////////////////////////////////////////////////////////////////////
01426 
01427 ClassResetMediaChangeTimer()
01428 
01429 Routine Description:
01430 
01431     Resets the media change count down timer to the default number of seconds.
01432     
01433 Arguments:
01434 
01435     FdoExtension - the device to reset the timer for
01436 
01437 Return Value:
01438 
01439     None
01440 
01441 --*/
01442 VOID
01443 ClassResetMediaChangeTimer(
01444     IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
01445     )
01446 {
01447     PMEDIA_CHANGE_DETECTION_INFO info = FdoExtension->MediaChangeDetectionInfo;
01448 
01449     if(info != NULL) {
01450         InterlockedExchange(&(info->MediaChangeCountDown),
01451                             MEDIA_CHANGE_DEFAULT_TIME);
01452     }
01453     return;
01454 } // end ClassResetMediaChangeTimer()
01455 
01456 /*++////////////////////////////////////////////////////////////////////////////
01457 
01458 ClasspInitializePolling() - ISSUE-2000/02/20-henrygab - not documented
01459 
01460 Routine Description:
01461 
01462     This routine
01463 
01464 Arguments:
01465 
01466     DeviceObject - 
01467     Irp - 
01468 
01469 Return Value:
01470 
01471 --*/
01472 NTSTATUS
01473 ClasspInitializePolling(
01474     IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
01475     IN BOOLEAN AllowDriveToSleep
01476     )
01477 {
01478     PDEVICE_OBJECT fdo = FdoExtension->DeviceObject;
01479     PCLASS_PRIVATE_FDO_DATA fdoData = FdoExtension->PrivateFdoData;
01480 
01481     ULONG size;
01482     PMEDIA_CHANGE_DETECTION_INFO info;
01483     PIRP irp;
01484 
01485     PAGED_CODE();
01486 
01487     if (FdoExtension->MediaChangeDetectionInfo != NULL) {
01488         return STATUS_SUCCESS;
01489     }
01490 
01491     info = ExAllocatePoolWithTag(NonPagedPool,
01492                                  sizeof(MEDIA_CHANGE_DETECTION_INFO),
01493                                  CLASS_TAG_MEDIA_CHANGE_DETECTION);
01494 
01495     if(info != NULL) {
01496         RtlZeroMemory(info, sizeof(MEDIA_CHANGE_DETECTION_INFO));
01497 
01498         FdoExtension->KernelModeMcnContext.FileObject      = (PVOID)-1;
01499         FdoExtension->KernelModeMcnContext.DeviceObject    = (PVOID)-1;
01500         FdoExtension->KernelModeMcnContext.LockCount       = 0;
01501         FdoExtension->KernelModeMcnContext.McnDisableCount = 0;
01502 
01503         /*
01504          *  Allocate an IRP to carry the Test-Unit-Ready.
01505          *  Allocate an extra IRP stack location 
01506          *  so we can cache our device object in the top location.
01507          */
01508         irp = IoAllocateIrp((CCHAR)(fdo->StackSize+1), FALSE);
01509         
01510         if (irp != NULL) {
01511 
01512             PVOID buffer;
01513 
01514             buffer = ExAllocatePoolWithTag(
01515                         NonPagedPoolCacheAligned,
01516                         SENSE_BUFFER_SIZE,
01517                         CLASS_TAG_MEDIA_CHANGE_DETECTION);
01518 
01519             if (buffer != NULL) {
01520                 PIO_STACK_LOCATION irpStack;
01521                 PSCSI_REQUEST_BLOCK srb;
01522                 PCDB cdb;
01523 
01524                 srb = &(info->MediaChangeSrb);
01525                 info->MediaChangeIrp = irp;
01526                 info->SenseBuffer = buffer;
01527 
01528                 /*
01529                  *  For the driver that creates an IRP, there is no 'current' stack location.
01530                  *  Step down one IRP stack location so that the extra top one
01531                  *  becomes our 'current' one.
01532                  */
01533                 IoSetNextIrpStackLocation(irp);
01534 
01535                 /*
01536                  *  Cache our device object in the extra top IRP stack location
01537                  *  so we have it in our completion routine.
01538                  */
01539                 irpStack = IoGetCurrentIrpStackLocation(irp);
01540                 irpStack->DeviceObject = fdo;
01541 
01542                 /*
01543                  *  Now start setting up the next IRP stack location for the call like any driver would.
01544                  */
01545                 irpStack = IoGetNextIrpStackLocation(irp);
01546                 irpStack->Parameters.Scsi.Srb = srb;
01547                 info->MediaChangeIrp = irp;
01548 
01549                 //
01550                 // Initialize the SRB
01551                 //
01552 
01553                 RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
01554 
01555                 //
01556                 // Initialize and set up the sense information buffer
01557                 //
01558 
01559                 RtlZeroMemory(buffer, SENSE_BUFFER_SIZE);
01560                 srb->SenseInfoBuffer = buffer;
01561                 srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
01562 
01563                 //
01564                 // Set default values for the media change notification
01565                 // configuration.
01566                 //
01567 
01568                 info->MediaChangeCountDown = MEDIA_CHANGE_DEFAULT_TIME;
01569                 info->MediaChangeDetectionDisableCount = 0;
01570 
01571                 //
01572                 // Assume that there is initially no media in the device
01573                 // only notify upper layers if there is something there
01574                 //
01575 
01576                 info->MediaChangeDetectionState = MediaUnknown;
01577 
01578                 info->MediaChangeIrpTimeInUse = 0;
01579                 info->MediaChangeIrpLost = FALSE;
01580 
01581                 //
01582                 // setup all extra flags we'll be setting for this irp
01583                 //
01584                 info->SrbFlags = 0;
01585                 if (AllowDriveToSleep) {
01586                     SET_FLAG(info->SrbFlags, SRB_FLAGS_NO_KEEP_AWAKE);
01587                 }
01588                 SET_FLAG(info->SrbFlags, SRB_CLASS_FLAGS_LOW_PRIORITY);
01589                 SET_FLAG(info->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
01590                 SET_FLAG(info->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
01591 
01592                 KeInitializeMutex(&info->MediaChangeMutex, 0x100);
01593 
01594                 //
01595                 // It is ok to support media change events on this
01596                 // device.
01597                 //
01598 
01599                 FdoExtension->MediaChangeDetectionInfo = info;
01600 
01601                 //
01602                 // NOTE: the DeviceType is FILE_DEVICE_CD_ROM even
01603                 //       when the device supports DVD (no need to
01604                 //       check for FILE_DEVICE_DVD, as it's not a
01605                 //       valid check).
01606                 //
01607 
01608                 if (FdoExtension->DeviceObject->DeviceType == FILE_DEVICE_CD_ROM){
01609 
01610                     NTSTATUS status;
01611 
01612                     KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN,
01613                                "ClasspInitializePolling: Testing for GESN\n"));
01614                     status = ClasspInitializeGesn(FdoExtension, info);
01615                     if (NT_SUCCESS(status)) {
01616                         KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN,
01617                                    "ClasspInitializePolling: GESN available "
01618                                    "for %p\n", FdoExtension->DeviceObject));
01619                         ASSERT(info->Gesn.Supported );
01620                         ASSERT(info->Gesn.Buffer     != NULL);
01621                         ASSERT(info->Gesn.BufferSize != 0);
01622                         ASSERT(info->Gesn.EventMask  != 0);
01623                         // must return here, for ASSERTs to be valid.
01624                         return STATUS_SUCCESS;
01625                     }
01626                     KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN,
01627                                "ClasspInitializePolling: GESN *NOT* available "
01628                                "for %p\n", FdoExtension->DeviceObject));
01629                 }
01630                 
01631                 ASSERT(info->Gesn.Supported == 0);
01632                 ASSERT(info->Gesn.Buffer == NULL);
01633                 ASSERT(info->Gesn.BufferSize == 0);
01634                 ASSERT(info->Gesn.EventMask  == 0);
01635                 info->Gesn.Supported = 0; // just in case....
01636                 return STATUS_SUCCESS;
01637             }
01638 
01639             IoFreeIrp(irp);
01640         }
01641 
01642         ExFreePool(info);
01643     }
01644 
01645     //
01646     // nothing to free here
01647     //
01648     return STATUS_INSUFFICIENT_RESOURCES;
01649 
01650 } // end ClasspInitializePolling()
01651 
01652 NTSTATUS
01653 ClasspInitializeGesn(
01654     IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
01655     IN PMEDIA_CHANGE_DETECTION_INFO Info
01656     )
01657 {    
01658     PNOTIFICATION_EVENT_STATUS_HEADER header;
01659     CLASS_DETECTION_STATE detectionState = ClassDetectionUnknown;
01660     PSTORAGE_ADAPTER_DESCRIPTOR adapterDescriptor;
01661     NTSTATUS status = STATUS_NOT_SUPPORTED;
01662     PIRP irp;
01663     KEVENT event;
01664     BOOLEAN retryImmediately;
01665     ULONG i;
01666     ULONG atapiResets;
01667 
01668     
01669     PAGED_CODE();
01670     ASSERT(Info == FdoExtension->MediaChangeDetectionInfo);
01671 
01672     //
01673     // read if we already know the abilities of the device
01674     //
01675 
01676     ClassGetDeviceParameter(FdoExtension,
01677                             CLASSP_REG_SUBKEY_NAME,
01678                             CLASSP_REG_MMC_DETECTION_VALUE_NAME,
01679                             (PULONG)&detectionState);
01680     
01681     if (detectionState == ClassDetectionUnsupported) {
01682         goto ExitWithError;
01683     }
01684 
01685     //
01686     // check if the device has a hack flag saying never to try this.
01687     //
01688 
01689     if (TEST_FLAG(FdoExtension->PrivateFdoData->HackFlags,
01690                   FDO_HACK_GESN_IS_BAD)) {
01691         
01692         detectionState = ClassDetectionUnsupported;
01693         ClassSetDeviceParameter(FdoExtension,
01694                                 CLASSP_REG_SUBKEY_NAME,
01695                                 CLASSP_REG_MMC_DETECTION_VALUE_NAME,
01696                                 ClassDetectionSupported);
01697         goto ExitWithError;
01698 
01699     }
01700 
01701 
01702     //
01703     // else go through the process since we allocate buffers and 
01704     // get all sorts of device settings.
01705     //
01706 
01707     if (Info->Gesn.Buffer == NULL) {
01708         Info->Gesn.Buffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
01709                                                   GESN_BUFFER_SIZE,
01710                                                   '??cS');
01711     }
01712     if (Info->Gesn.Buffer == NULL) {
01713         status = STATUS_INSUFFICIENT_RESOURCES;
01714         goto ExitWithError;
01715     }
01716     if (Info->Gesn.Mdl != NULL) {
01717         IoFreeMdl(Info->Gesn.Mdl);
01718     }
01719     Info->Gesn.Mdl = IoAllocateMdl(Info->Gesn.Buffer,
01720                                    GESN_BUFFER_SIZE,
01721                                    FALSE, FALSE, NULL);
01722     if (Info->Gesn.Mdl == NULL) {
01723         status = STATUS_INSUFFICIENT_RESOURCES;
01724         goto ExitWithError;
01725     }
01726 
01727     MmBuildMdlForNonPagedPool(Info->Gesn.Mdl);
01728     Info->Gesn.BufferSize = GESN_BUFFER_SIZE;
01729     Info->Gesn.EventMask = 0;
01730 
01731     //
01732     // all items are prepared to use GESN (except the event mask, so don't
01733     // optimize this part out!).
01734     //
01735     // now see if it really works. we have to loop through this because
01736     // many SAMSUNG (and one COMPAQ) drives timeout when requesting
01737     // NOT_READY events, even when the IMMEDIATE bit is set. :(
01738     //
01739     // using a drive list is cumbersome, so this might fix the problem.
01740     //
01741 
01742     adapterDescriptor = FdoExtension->AdapterDescriptor;
01743     atapiResets = 0;
01744     retryImmediately = TRUE;
01745     for (i = 0; i < 16 && retryImmediately == TRUE; i++) {
01746     
01747         irp = ClasspPrepareMcnIrp(FdoExtension, Info, TRUE);
01748         if (irp == NULL) {
01749             status = STATUS_INSUFFICIENT_RESOURCES;
01750             goto ExitWithError;
01751         }
01752 
01753         ASSERT(TEST_FLAG(Info->MediaChangeSrb.SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE));
01754     
01755         //
01756         // replace the completion routine with a different one this time...
01757         //
01758     
01759         IoSetCompletionRoutine(irp,
01760                                ClassSignalCompletion,
01761                                &event,
01762                                TRUE, TRUE, TRUE);
01763         KeInitializeEvent(&event, SynchronizationEvent, FALSE);
01764     
01765         status = IoCallDriver(FdoExtension->CommonExtension.LowerDeviceObject, irp);
01766     
01767         if (status == STATUS_PENDING) {
01768             status = KeWaitForSingleObject(&event,
01769                                            Executive,
01770                                            KernelMode,
01771                                            FALSE,
01772                                            NULL);
01773             ASSERT(NT_SUCCESS(status));
01774         }
01775         ClassReleaseRemoveLock(FdoExtension->DeviceObject, irp);
01776     
01777         if (SRB_STATUS(Info->MediaChangeSrb.SrbStatus) != SRB_STATUS_SUCCESS) {
01778             ClassInterpretSenseInfo(FdoExtension->DeviceObject,
01779                                     &(Info->MediaChangeSrb),
01780                                     IRP_MJ_SCSI,
01781                                     0,
01782                                     0,
01783                                     &status,
01784                                     NULL);
01785         }
01786 
01787         if ((adapterDescriptor->BusType == BusTypeAtapi) &&
01788             (Info->MediaChangeSrb.SrbStatus == SRB_STATUS_BUS_RESET)
01789             ) {
01790 
01791             //
01792             // ATAPI unfortunately returns SRB_STATUS_BUS_RESET instead
01793             // of SRB_STATUS_TIMEOUT, so we cannot differentiate between
01794             // the two.  if we get this status four time consecutively,
01795             // stop trying this command.  it is too late to change ATAPI
01796             // at this point, so special-case this here. (07/10/2001)
01797             // NOTE: any value more than 4 may cause the device to be
01798             //       marked missing.
01799             //
01800 
01801             atapiResets++;
01802             if (atapiResets >= 4) {
01803                 status = STATUS_IO_DEVICE_ERROR;
01804                 goto ExitWithError;
01805             }
01806         }
01807 
01808         if (status == STATUS_DATA_OVERRUN) {
01809             status = STATUS_SUCCESS;
01810         }
01811     
01812         if ((status == STATUS_INVALID_DEVICE_REQUEST) ||
01813             (status == STATUS_TIMEOUT) ||
01814             (status == STATUS_IO_DEVICE_ERROR) ||
01815             (status == STATUS_IO_TIMEOUT)
01816             ) {
01817     
01818             //
01819             // with these error codes, we don't ever want to try this command
01820             // again on this device, since it reacts poorly.
01821             //
01822     
01823             ClassSetDeviceParameter(FdoExtension,
01824                                     CLASSP_REG_SUBKEY_NAME,
01825                                     CLASSP_REG_MMC_DETECTION_VALUE_NAME,
01826                                     ClassDetectionUnsupported);
01827             KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugWarning,
01828                        "Classpnp => GESN test failed %x for fdo %p\n",
01829                        status, FdoExtension->DeviceObject));
01830             goto ExitWithError;
01831 
01832     
01833         }
01834     
01835         if (!NT_SUCCESS(status)) {
01836 
01837             //
01838             // this may be other errors that should not disable GESN
01839             // for all future start_device calls.
01840             //
01841 
01842             KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugWarning,
01843                        "Classpnp => GESN test failed %x for fdo %p\n",
01844                        status, FdoExtension->DeviceObject));
01845             goto ExitWithError;
01846         }
01847     
01848         if (i == 0) {
01849 
01850             //
01851             // the first time, the request was just retrieving a mask of
01852             // available bits.  use this to mask future requests.
01853             //
01854         
01855             header = (PNOTIFICATION_EVENT_STATUS_HEADER)(Info->Gesn.Buffer);
01856             
01857             KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN,
01858                        "Classpnp => Fdo %p supports event mask %x\n",
01859                        FdoExtension->DeviceObject, header->SupportedEventClasses));
01860             
01861         
01862             if (TEST_FLAG(header->SupportedEventClasses,
01863                           NOTIFICATION_MEDIA_STATUS_CLASS_MASK)) {
01864                 KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN,
01865                            "Classpnp => GESN supports MCN\n"));
01866             }
01867             if (TEST_FLAG(header->SupportedEventClasses,
01868                           NOTIFICATION_DEVICE_BUSY_CLASS_MASK)) {
01869                 KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN,
01870                            "Classpnp => GESN supports DeviceBusy\n"));
01871             }
01872             Info->Gesn.EventMask = header->SupportedEventClasses;
01873 
01874             //
01875             // realistically, we are only considering the following events:
01876             //    EXTERNAL REQUEST - this is being tested for play/stop/etc.
01877             //    MEDIA STATUS - autorun and ejection requests.
01878             //    DEVICE BUSY - to allow us to predict when media will be ready.
01879             // therefore, we should not bother querying for the other,
01880             // unknown events. clear all but the above flags.
01881             //
01882         
01883             Info->Gesn.EventMask &=
01884                 NOTIFICATION_EXTERNAL_REQUEST_CLASS_MASK |
01885                 NOTIFICATION_MEDIA_STATUS_CLASS_MASK     |
01886                 NOTIFICATION_DEVICE_BUSY_CLASS_MASK      ;
01887         
01888         
01889             //
01890             // HACKHACK - REF #0001
01891             // Some devices will *never* report an event if we've also requested
01892             // that it report lower-priority events.  this is due to a
01893             // misunderstanding in the specification wherein a "No Change" is
01894             // interpreted to be a real event.  what should occur is that the
01895             // device should ignore "No Change" events when multiple event types
01896             // are requested unless there are no other events waiting.  this
01897             // greatly reduces the number of requests that the host must send
01898             // to determine if an event has occurred. Since we must work on all
01899             // drives, default to enabling the hack until we find evidence of
01900             // proper firmware.
01901             //
01902         
01903             if (CountOfSetBitsUChar(Info->Gesn.EventMask) == 1) {
01904                 KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN,
01905                            "Classpnp => GESN hack %s for FDO %p\n",
01906                            "not required", FdoExtension->DeviceObject));
01907             } else {
01908                 KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN,
01909                            "Classpnp => GESN hack %s for FDO %p\n",
01910                            "enabled", FdoExtension->DeviceObject));
01911                 Info->Gesn.HackEventMask = 1;
01912             }
01913 
01914         } else {
01915             
01916             //
01917             // not the first time looping through, so interpret the results.
01918             //
01919 
01920             ClasspInterpretGesnData(FdoExtension,
01921                                     (PVOID)Info->Gesn.Buffer,
01922                                     &retryImmediately);
01923 
01924         }
01925 
01926     } // end loop of GESN requests....
01927 
01928     //
01929     // we can only use this if it can be relied upon for media changes,
01930     // since we are (by definition) no longer going to be polling via
01931     // a TEST_UNIT_READY irp, and drives will not report UNIT ATTENTION
01932     // for this command (although a filter driver, such as one for burning
01933     // cd's, might still fake those errors).
01934     //
01935     // since we also rely upon NOT_READY events to change the cursor
01936     // into a "wait" cursor, we can't use GESN without NOT_READY support.
01937     //
01938     
01939     if (TEST_FLAG(Info->Gesn.EventMask,
01940                   NOTIFICATION_MEDIA_STATUS_CLASS_MASK) &&
01941         TEST_FLAG(Info->Gesn.EventMask,
01942                   NOTIFICATION_DEVICE_BUSY_CLASS_MASK)
01943         ) {
01944         
01945         KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN,
01946                    "Classpnp => Enabling GESN support for fdo %p\n",
01947                    FdoExtension->DeviceObject));
01948         Info->Gesn.Supported = TRUE;
01949 
01950         ClassSetDeviceParameter(FdoExtension,
01951                                 CLASSP_REG_SUBKEY_NAME,
01952                                 CLASSP_REG_MMC_DETECTION_VALUE_NAME,
01953                                 ClassDetectionSupported);
01954 
01955         return STATUS_SUCCESS;
01956 
01957     }
01958     
01959     KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN,
01960                "Classpnp => GESN available but not enabled for fdo %p\n",
01961                FdoExtension->DeviceObject));
01962     goto ExitWithError;
01963 
01964     // fall through...
01965 
01966 ExitWithError:
01967     if (Info->Gesn.Mdl) {
01968         IoFreeMdl(Info->Gesn.Mdl);
01969         Info->Gesn.Mdl = NULL;
01970     }
01971     if (Info->Gesn.Buffer) {
01972         ExFreePool(Info->Gesn.Buffer);
01973         Info->Gesn.Buffer = NULL;
01974     }
01975     Info->Gesn.Supported  = 0;
01976     Info->Gesn.EventMask  = 0;
01977     Info->Gesn.BufferSize = 0;
01978     return STATUS_NOT_SUPPORTED;
01979 
01980 }
01981 
01982 /*++////////////////////////////////////////////////////////////////////////////
01983 
01984 ClassInitializeTestUnitPolling()
01985 
01986 Routine Description:
01987 
01988     This routine will initialize MCN regardless of the settings stored
01989     in the registry.  This should be used with caution, as some devices
01990     react badly to constant io. (i.e. never spin down, continuously cycling
01991     media in changers, ejection of media, etc.)  It is highly suggested to
01992     use ClassInitializeMediaChangeDetection() instead.
01993 
01994 Arguments:
01995 
01996     FdoExtension is the device to poll
01997     
01998     AllowDriveToSleep says whether to attempt to allow the drive to sleep
01999         or not.  This only affects system-known spin down states, so if a 
02000         drive spins itself down, this has no effect until the system spins
02001         it down.
02002 
02003 Return Value:
02004 
02005 --*/
02006 NTSTATUS
02007 ClassInitializeTestUnitPolling(
02008     IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
02009     IN BOOLEAN AllowDriveToSleep
02010     )
02011 {
02012     return ClasspInitializePolling(FdoExtension, AllowDriveToSleep);
02013 } // end ClassInitializeTestUnitPolling()
02014 
02015 /*++////////////////////////////////////////////////////////////////////////////
02016 
02017 ClassInitializeMediaChangeDetection()
02018 
02019 Routine Description:
02020 
02021     This routine checks to see if it is safe to initialize MCN (the back end
02022     to autorun) for a given device.  It will then check the device-type wide
02023     key "Autorun" in the service key (for legacy reasons), and then look in
02024     the device-specific key to potentially override that setting.
02025     
02026     If MCN is to be enabled, all neccessary structures and memory are
02027     allocated and initialized.
02028     
02029     This routine MUST be called only from the ClassInit() callback.
02030 
02031 Arguments:
02032 
02033     FdoExtension - the device to initialize MCN for, if appropriate
02034     
02035     EventPrefix - unused, legacy argument.  Set to zero.
02036 
02037 Return Value:
02038 
02039 --*/
02040 VOID
02041 ClassInitializeMediaChangeDetection(
02042     IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
02043     IN PUCHAR EventPrefix
02044     )
02045 {
02046     PDEVICE_OBJECT fdo = FdoExtension->DeviceObject;
02047     NTSTATUS status;
02048 
02049     PCLASS_DRIVER_EXTENSION driverExtension = ClassGetDriverExtension(
02050                                                 fdo->DriverObject);
02051 
02052     BOOLEAN disabledForBadHardware;
02053     BOOLEAN disabled;
02054     BOOLEAN instanceOverride;
02055 
02056     PAGED_CODE();
02057 
02058     //
02059     // NOTE: This assumes that ClassInitializeMediaChangeDetection is always
02060     //       called in the context of the ClassInitDevice callback. If called
02061     //       after then this check will have already been made and the
02062     //       once a second timer will not have been enabled.
02063     //
02064 
02065     disabledForBadHardware = ClasspIsMediaChangeDisabledDueToHardwareLimitation(
02066                                 FdoExtension,
02067                                 &(driverExtension->RegistryPath)
02068                                 );
02069 
02070     if (disabledForBadHardware) {
02071         DebugPrint((ClassDebugMCN,
02072                     "ClassInitializeMCN: Disabled due to hardware"
02073                     "limitations for this device"));
02074         return;
02075     }
02076 
02077     //
02078     // autorun should now be enabled by default for all media types.
02079     //
02080 
02081     disabled = ClasspIsMediaChangeDisabledForClass(
02082                     FdoExtension,
02083                     &(driverExtension->RegistryPath)
02084                     );
02085 
02086     DebugPrint((ClassDebugMCN,
02087                 "ClassInitializeMCN: Class    MCN is %s\n",
02088                 (disabled ? "disabled" : "enabled")));
02089 
02090     status = ClasspMediaChangeDeviceInstanceOverride(
02091                 FdoExtension,
02092                 &instanceOverride);  // default value
02093 
02094     if (!NT_SUCCESS(status)) {
02095         DebugPrint((ClassDebugMCN,
02096                     "ClassInitializeMCN: Instance using default\n"));
02097     } else {
02098         DebugPrint((ClassDebugMCN,
02099                     "ClassInitializeMCN: Instance override: %s MCN\n",
02100                     (instanceOverride ? "Enabling" : "Disabling")));
02101         disabled = !instanceOverride;
02102     }
02103 
02104     DebugPrint((ClassDebugMCN,
02105                 "ClassInitializeMCN: Instance MCN is %s\n",
02106                 (disabled ? "disabled" : "enabled")));
02107 
02108     if (disabled) {
02109         return;
02110     }
02111     
02112     //
02113     // if the drive is not a CDROM, allow the drive to sleep
02114     //
02115     if (FdoExtension->DeviceObject->DeviceType == FILE_DEVICE_CD_ROM) {
02116         ClasspInitializePolling(FdoExtension, FALSE);
02117     } else {
02118         ClasspInitializePolling(FdoExtension, TRUE);
02119     }
02120 
02121     return;
02122 } // end ClassInitializeMediaChangeDetection()
02123 
02124 /*++////////////////////////////////////////////////////////////////////////////
02125 
02126 ClasspMediaChangeDeviceInstanceOverride()
02127 
02128 Routine Description:
02129 
02130     The user can override the global setting to enable or disable Autorun on a
02131     specific cdrom device via the control panel.  This routine checks and/or
02132     sets this value.
02133 
02134 Arguments:
02135 
02136     FdoExtension - the device to set/get the value for
02137     Value        - the value to use in a set
02138     SetValue     - whether to set the value
02139 
02140 Return Value:
02141 
02142     TRUE - Autorun is disabled
02143     FALSE - Autorun is not disabled (Default)
02144 
02145 --*/
02146 NTSTATUS
02147 ClasspMediaChangeDeviceInstanceOverride(
02148     IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
02149     OUT PBOOLEAN Enabled
02150     )
02151 {
02152     HANDLE                   deviceParameterHandle;  // cdrom instance key
02153     HANDLE                   driverParameterHandle;  // cdrom specific key
02154     RTL_QUERY_REGISTRY_TABLE queryTable[3];
02155     OBJECT_ATTRIBUTES        objectAttributes;
02156     UNICODE_STRING           subkeyName;
02157     NTSTATUS                 status;
02158     ULONG                    alwaysEnable;
02159     ULONG                    alwaysDisable;
02160     ULONG                    i;
02161 
02162 
02163     PAGED_CODE();
02164 
02165     deviceParameterHandle = NULL;
02166     driverParameterHandle = NULL;
02167     status = STATUS_UNSUCCESSFUL;
02168     alwaysEnable = FALSE;
02169     alwaysDisable = FALSE;
02170 
02171     TRY {
02172 
02173         status = IoOpenDeviceRegistryKey( FdoExtension->LowerPdo,
02174                                           PLUGPLAY_REGKEY_DEVICE,
02175                                           KEY_ALL_ACCESS,
02176                                           &deviceParameterHandle
02177                                           );
02178         if (!NT_SUCCESS(status)) {
02179 
02180             //
02181             // this can occur when a new device is added to the system
02182             // this is due to cdrom.sys being an 'essential' driver
02183             //
02184             DebugPrint((ClassDebugMCN,
02185                         "ClassMediaChangeDeviceInstanceDisabled: "
02186                         "Could not open device registry key [%lx]\n", status));
02187             LEAVE;
02188         }
02189 
02190         RtlInitUnicodeString(&subkeyName, MCN_REG_SUBKEY_NAME);
02191         InitializeObjectAttributes(&objectAttributes,
02192                                    &subkeyName,
02193                                    OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
02194                                    deviceParameterHandle,
02195                                    (PSECURITY_DESCRIPTOR) NULL);
02196 
02197         status = ZwCreateKey(&driverParameterHandle,
02198                              KEY_READ,
02199                              &objectAttributes,
02200                              0,
02201                              (PUNICODE_STRING) NULL,
02202                              REG_OPTION_NON_VOLATILE,
02203                              NULL);
02204 
02205         if (!NT_SUCCESS(status)) {
02206             DebugPrint((ClassDebugMCN,
02207                         "ClassMediaChangeDeviceInstanceDisabled: "
02208                         "subkey could not be created. %lx\n", status));
02209             LEAVE;
02210         }
02211 
02212         //
02213         // Default to not changing autorun behavior, based upon setting
02214         // registryValue to zero.
02215         //
02216 
02217         for (i=0;i<2;i++) {
02218 
02219             RtlZeroMemory(&queryTable[0], sizeof(queryTable));
02220 
02221             queryTable[0].Flags         = RTL_QUERY_REGISTRY_DIRECT;
02222             queryTable[0].DefaultType   = REG_DWORD;
02223             queryTable[0].DefaultLength = 0;
02224 
02225             if (i==0) {
02226                 queryTable[0].Name          = MCN_REG_AUTORUN_DISABLE_INSTANCE_NAME;
02227                 queryTable[0].EntryContext  = &alwaysDisable;
02228                 queryTable[0].DefaultData   = &alwaysDisable;
02229             } else {
02230                 queryTable[0].Name          = MCN_REG_AUTORUN_ENABLE_INSTANCE_NAME;
02231                 queryTable[0].EntryContext  = &alwaysEnable;
02232                 queryTable[0].DefaultData   = &alwaysEnable;
02233             }
02234 
02235             //
02236             // don't care if it succeeds, since we set defaults above
02237             //
02238 
02239             RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
02240                                    (PWSTR)driverParameterHandle,
02241                                    queryTable,
02242                                    NULL,
02243                                    NULL);
02244         }
02245 
02246     } FINALLY {
02247 
02248         if (driverParameterHandle) ZwClose(driverParameterHandle);
02249         if (deviceParameterHandle) ZwClose(deviceParameterHandle);
02250 
02251     }
02252 
02253     if (alwaysEnable && alwaysDisable) {
02254 
02255         DebugPrint((ClassDebugMCN,
02256                     "ClassMediaChangeDeviceInstanceDisabled: %s selected\n",
02257                     "Both Enable and Disable set -- DISABLE"));
02258         ASSERT(NT_SUCCESS(status));
02259         status = STATUS_SUCCESS;
02260         *Enabled = FALSE;
02261 
02262     } else if (alwaysDisable) {
02263 
02264         DebugPrint((ClassDebugMCN,
02265                     "ClassMediaChangeDeviceInstanceDisabled: %s selected\n",
02266                     "DISABLE"));
02267         ASSERT(NT_SUCCESS(status));
02268         status = STATUS_SUCCESS;
02269         *Enabled = FALSE;
02270 
02271     } else if (alwaysEnable) {
02272 
02273         DebugPrint((ClassDebugMCN,
02274                     "ClassMediaChangeDeviceInstanceDisabled: %s selected\n",
02275                     "ENABLE"));
02276         ASSERT(NT_SUCCESS(status));
02277         status = STATUS_SUCCESS;
02278         *Enabled = TRUE;
02279 
02280     } else {
02281 
02282         DebugPrint((ClassDebugMCN,
02283                     "ClassMediaChangeDeviceInstanceDisabled: %s selected\n",
02284                     "DEFAULT"));
02285         status = STATUS_UNSUCCESSFUL;
02286 
02287     }
02288 
02289     return status;
02290 
02291 } // end ClasspMediaChangeDeviceInstanceOverride()
02292 
02293 /*++////////////////////////////////////////////////////////////////////////////
02294 
02295 ClasspIsMediaChangeDisabledDueToHardwareLimitation()
02296 
02297 Routine Description:
02298 
02299     The key AutoRunAlwaysDisable contains a MULTI_SZ of hardware IDs for
02300     which to never enable MediaChangeNotification.
02301 
02302     The user can override the global setting to enable or disable Autorun on a
02303     specific cdrom device via the control panel.
02304 
02305 Arguments:
02306 
02307     FdoExtension -
02308     RegistryPath - pointer to the unicode string inside
02309                    ...\CurrentControlSet\Services\Cdrom
02310 
02311 Return Value:
02312 
02313     TRUE - no autorun.
02314     FALSE - Autorun may be enabled
02315 
02316 --*/
02317 BOOLEAN
02318 ClasspIsMediaChangeDisabledDueToHardwareLimitation(
02319     IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
02320     IN PUNICODE_STRING RegistryPath
02321     )
02322 {
02323     PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor = FdoExtension->DeviceDescriptor;
02324     OBJECT_ATTRIBUTES objectAttributes;
02325     HANDLE serviceKey = NULL;
02326     RTL_QUERY_REGISTRY_TABLE parameters[2];
02327 
02328     UNICODE_STRING deviceUnicodeString;
02329     ANSI_STRING deviceString;
02330     ULONG mediaChangeNotificationDisabled = FALSE;
02331 
02332     NTSTATUS status;
02333 
02334 
02335     PAGED_CODE();
02336 
02337     //
02338     // open the service key.
02339     //
02340 
02341     InitializeObjectAttributes(&objectAttributes,
02342                                RegistryPath,
02343                                OBJ_CASE_INSENSITIVE,
02344                                NULL,
02345                                NULL);
02346 
02347     status = ZwOpenKey(&serviceKey,
02348                        KEY_READ,
02349                        &objectAttributes);
02350 
02351     ASSERT(NT_SUCCESS(status));
02352 
02353 
02354     if(!NT_SUCCESS(status)) {
02355 
02356         //
02357         // always take the safe path.  if we can't open the service key,
02358         // disable autorun
02359         //
02360 
02361         return TRUE;
02362 
02363     }
02364 
02365     TRY {
02366         //
02367         // Determine if drive is in a list of those requiring
02368         // autorun to be disabled.  this is stored in a REG_MULTI_SZ
02369         // named AutoRunAlwaysDisable.  this is required as some autochangers
02370         // must load the disc to reply to ChkVerify request, causing them
02371         // to cycle discs continuously.
02372         //
02373 
02374         PWSTR nullMultiSz;
02375         PUCHAR vendorId;
02376         PUCHAR productId;
02377         PUCHAR revisionId;
02378         ULONG  length;
02379         ULONG  offset;
02380 
02381         deviceString.Buffer        = NULL;
02382         deviceUnicodeString.Buffer = NULL;
02383 
02384         //
02385         // there may be nothing to check against
02386         //
02387 
02388         if ((deviceDescriptor->VendorIdOffset == 0) &&
02389             (deviceDescriptor->ProductIdOffset == 0)) {
02390             LEAVE;
02391         }
02392 
02393         length = 0;
02394 
02395         if (deviceDescriptor->VendorIdOffset == 0) {
02396             vendorId = NULL;
02397         } else {
02398             vendorId = (PUCHAR) deviceDescriptor + deviceDescriptor->VendorIdOffset;
02399             length = strlen(vendorId);
02400         }
02401 
02402         if ( deviceDescriptor->ProductIdOffset == 0 ) {
02403             productId = NULL;
02404         } else {
02405             productId = (PUCHAR) deviceDescriptor + deviceDescriptor->ProductIdOffset;
02406             length += strlen(productId);
02407         }
02408 
02409         if ( deviceDescriptor->ProductRevisionOffset == 0 ) {
02410             revisionId = NULL;
02411         } else {
02412             revisionId = (PUCHAR) deviceDescriptor + deviceDescriptor->ProductRevisionOffset;
02413             length += strlen(revisionId);
02414         }
02415 
02416         //
02417         // allocate a buffer for the string
02418         //
02419 
02420         deviceString.Length = (USHORT)( length );
02421         deviceString.MaximumLength = deviceString.Length + 1;
02422         deviceString.Buffer = (PUCHAR)ExAllocatePoolWithTag( NonPagedPool,
02423                                                              deviceString.MaximumLength,
02424                                                              CLASS_TAG_AUTORUN_DISABLE
02425                                                              );
02426         if (deviceString.Buffer == NULL) {
02427             DebugPrint((ClassDebugMCN,
02428                         "ClassMediaChangeDisabledForHardware: Unable to alloc "
02429                         "string buffer\n" ));
02430             LEAVE;
02431         }
02432 
02433         //
02434         // copy strings to the buffer
02435         //
02436         offset = 0;
02437 
02438         if (vendorId != NULL) {
02439             RtlCopyMemory(deviceString.Buffer + offset,
02440                           vendorId,
02441                           strlen(vendorId));
02442             offset += strlen(vendorId);
02443         }
02444 
02445         if ( productId != NULL ) {
02446             RtlCopyMemory(deviceString.Buffer + offset,
02447                           productId,
02448                           strlen(productId));
02449             offset += strlen(productId);
02450         }
02451         if ( revisionId != NULL ) {
02452             RtlCopyMemory(deviceString.Buffer + offset,
02453                           revisionId,
02454                           strlen(revisionId));
02455             offset += strlen(revisionId);
02456         }
02457 
02458         ASSERT(offset == deviceString.Length);
02459 
02460         deviceString.Buffer[deviceString.Length] = '\0';  // Null-terminated
02461 
02462         //
02463         // convert to unicode as registry deals with unicode strings
02464         //
02465 
02466         status = RtlAnsiStringToUnicodeString( &deviceUnicodeString,
02467                                                &deviceString,
02468                                                TRUE
02469                                                );
02470         if (!NT_SUCCESS(status)) {
02471             DebugPrint((ClassDebugMCN,
02472                         "ClassMediaChangeDisabledForHardware: cannot convert "
02473                         "to unicode %lx\n", status));
02474             LEAVE;
02475         }
02476 
02477         //
02478         // query the value, setting valueFound to true if found
02479         //
02480 
02481         RtlZeroMemory(parameters, sizeof(parameters));
02482 
02483         nullMultiSz = L"\0";
02484         parameters[0].QueryRoutine  = ClasspMediaChangeRegistryCallBack;
02485         parameters[0].Flags         = RTL_QUERY_REGISTRY_REQUIRED;
02486         parameters[0].Name          = L"AutoRunAlwaysDisable";
02487         parameters[0].EntryContext  = &mediaChangeNotificationDisabled;
02488         parameters[0].DefaultType   = REG_MULTI_SZ;
02489         parameters[0].DefaultData   = nullMultiSz;
02490         parameters[0].DefaultLength = 0;
02491 
02492         status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
02493                                         serviceKey,
02494                                         parameters,
02495                                         &deviceUnicodeString,
02496                                         NULL);
02497 
02498         if ( !NT_SUCCESS(status) ) {
02499             LEAVE;
02500         }
02501 
02502     } FINALLY {
02503 
02504         if (deviceString.Buffer != NULL) {
02505             ExFreePool( deviceString.Buffer );
02506         }
02507         if (deviceUnicodeString.Buffer != NULL) {
02508             RtlFreeUnicodeString( &deviceUnicodeString );
02509         }
02510 
02511         ZwClose(serviceKey);
02512     }
02513 
02514     if (mediaChangeNotificationDisabled) {
02515         DebugPrint((ClassDebugMCN, "ClassMediaChangeDisabledForHardware: "
02516                     "Device is on disable list\n"));
02517         return TRUE;
02518     }
02519     return FALSE;
02520 
02521 } // end ClasspIsMediaChangeDisabledDueToHardwareLimitation()
02522 
02523 /*++////////////////////////////////////////////////////////////////////////////
02524 
02525 ClasspIsMediaChangeDisabledForClass()
02526 
02527 Routine Description:
02528 
02529     The user must specify that AutoPlay is to run on the platform
02530     by setting the registry value HKEY_LOCAL_MACHINE\System\CurrentControlSet\
02531     Services<SERVICE>\Autorun:REG_DWORD:1.
02532 
02533     The user can override the global setting to enable or disable Autorun on a
02534     specific cdrom device via the control panel.
02535 
02536 Arguments:
02537 
02538     FdoExtension -
02539     RegistryPath - pointer to the unicode string inside
02540                    ...\CurrentControlSet\Services\Cdrom
02541 
02542 Return Value:
02543 
02544     TRUE - Autorun is disabled for this class
02545     FALSE - Autorun is enabled for this class
02546 
02547 --*/
02548 BOOLEAN
02549 ClasspIsMediaChangeDisabledForClass(
02550     IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
02551     IN PUNICODE_STRING RegistryPath
02552     )
02553 {
02554     PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor = FdoExtension->DeviceDescriptor;
02555 
02556     OBJECT_ATTRIBUTES objectAttributes;
02557     HANDLE serviceKey = NULL;
02558     HANDLE parametersKey = NULL;
02559     RTL_QUERY_REGISTRY_TABLE parameters[3];
02560 
02561     UNICODE_STRING paramStr;
02562     UNICODE_STRING deviceUnicodeString;
02563     ANSI_STRING deviceString;
02564 
02565     //
02566     //  Default to ENABLING MediaChangeNotification (!)
02567     //
02568 
02569     ULONG mcnRegistryValue = 1;
02570 
02571     NTSTATUS status;
02572 
02573 
02574     PAGED_CODE();
02575 
02576     //
02577     // open the service key.
02578     //
02579 
02580     InitializeObjectAttributes(&objectAttributes,
02581                                RegistryPath,
02582                                OBJ_CASE_INSENSITIVE,
02583                                NULL,
02584                                NULL);
02585 
02586     status = ZwOpenKey(&serviceKey,
02587                        KEY_READ,
02588                        &objectAttributes);
02589 
02590     ASSERT(NT_SUCCESS(status));
02591 
02592     if(!NT_SUCCESS(status)) {
02593 
02594         //
02595         // return the default value, which is the
02596         // inverse of the registry setting default
02597         // since this routine asks if it's disabled
02598         //
02599 
02600         DebugPrint((ClassDebugMCN, "ClassCheckServiceMCN: Defaulting to %s\n",
02601                     (mcnRegistryValue ? "Enabled" : "Disabled")));
02602         return (BOOLEAN)(!mcnRegistryValue);
02603 
02604     }
02605 
02606     RtlZeroMemory(parameters, sizeof(parameters));
02607 
02608     //
02609     // Open the parameters key (if any) beneath the services key.
02610     //
02611 
02612     RtlInitUnicodeString(&paramStr, L"Parameters");
02613 
02614     InitializeObjectAttributes(&objectAttributes,
02615                                &paramStr,
02616                                OBJ_CASE_INSENSITIVE,
02617                                serviceKey,
02618                                NULL);
02619 
02620     status = ZwOpenKey(&parametersKey,
02621                        KEY_READ,
02622                        &objectAttributes);
02623 
02624     if (!NT_SUCCESS(status)) {
02625         parametersKey = NULL;
02626     }
02627 
02628 
02629 
02630     //
02631     // Check for the Autorun value.
02632     //
02633 
02634     parameters[0].Flags         = RTL_QUERY_REGISTRY_DIRECT;
02635     parameters[0].Name          = L"Autorun";
02636     parameters[0].EntryContext  = &mcnRegistryValue;
02637     parameters[0].DefaultType   = REG_DWORD;
02638     parameters[0].DefaultData   = &mcnRegistryValue;
02639     parameters[0].DefaultLength = sizeof(ULONG);
02640 
02641     status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE | RTL_REGISTRY_OPTIONAL,
02642                                     serviceKey,
02643                                     parameters,
02644                                     NULL,
02645                                     NULL);
02646 
02647     DebugPrint((ClassDebugMCN, "ClassCheckServiceMCN: "
02648                 "<Service>/Autorun flag = %d\n", mcnRegistryValue));
02649 
02650     if(parametersKey != NULL) {
02651 
02652         status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE | RTL_REGISTRY_OPTIONAL,
02653                                         parametersKey,
02654                                         parameters,
02655                                         NULL,
02656                                         NULL);
02657         DebugPrint((ClassDebugMCN, "ClassCheckServiceMCN: "
02658                     "<Service>/Parameters/Autorun flag = %d\n",
02659                     mcnRegistryValue));
02660         ZwClose(parametersKey);
02661 
02662     }
02663     ZwClose(serviceKey);
02664     
02665     DebugPrint((ClassDebugMCN, "ClassCheckServiceMCN: "
02666                 "Autoplay for device %p is %s\n",
02667                 FdoExtension->DeviceObject,
02668                 (mcnRegistryValue ? "on" : "off")
02669                 ));
02670 
02671     //
02672     // return if it is _disabled_, which is the
02673     // inverse of the registry setting
02674     //
02675 
02676     return (BOOLEAN)(!mcnRegistryValue);
02677 } // end ClasspIsMediaChangeDisabledForClass()
02678 
02679 /*++////////////////////////////////////////////////////////////////////////////
02680 
02681 ClassEnableMediaChangeDetection() ISSUE-2000/02/20-henrygab - why public?
02682 ClassEnableMediaChangeDetection() ISSUE-2000/02/20-henrygab - not documented
02683 
02684 Routine Description:
02685 
02686     This routine
02687 
02688 Arguments:
02689 
02690     DeviceObject - 
02691     Irp - 
02692 
02693 Return Value:
02694 
02695 --*/
02696 VOID
02697 ClassEnableMediaChangeDetection(
02698     IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
02699     )
02700 {
02701     PMEDIA_CHANGE_DETECTION_INFO info = FdoExtension->MediaChangeDetectionInfo;
02702     LONG oldCount;
02703 
02704     PAGED_CODE();
02705 
02706     if(info == NULL) {
02707         DebugPrint((ClassDebugMCN,
02708                     "ClassEnableMediaChangeDetection: not initialized\n"));
02709         return;
02710     }
02711 
02712     KeWaitForMutexObject(&info->MediaChangeMutex,
02713                           UserRequest,
02714                           KernelMode,
02715                           FALSE,
02716                           NULL);
02717 
02718     oldCount = --info->MediaChangeDetectionDisableCount;
02719 
02720     ASSERT(oldCount >= 0);
02721 
02722     DebugPrint((ClassDebugMCN, "ClassEnableMediaChangeDetection: Disable count "
02723                 "reduced to %d - ",
02724                 info->MediaChangeDetectionDisableCount));
02725 
02726     if(oldCount == 0) {
02727 
02728         //
02729         // We don't know what state the media is in anymore.
02730         //
02731 
02732         ClasspInternalSetMediaChangeState(FdoExtension,
02733                                           MediaUnknown,
02734                                           FALSE
02735                                           );
02736 
02737         //
02738         // Reset the MCN timer.
02739         //
02740 
02741         ClassResetMediaChangeTimer(FdoExtension);
02742 
02743         DebugPrint((ClassDebugMCN, "MCD is enabled\n"));
02744 
02745     } else {
02746 
02747         DebugPrint((ClassDebugMCN, "MCD still disabled\n"));
02748 
02749     }
02750 
02751 
02752     //
02753     // Let something else run.
02754     //
02755 
02756     KeReleaseMutex(&info->MediaChangeMutex, FALSE);
02757 
02758     return;
02759 } // end ClassEnableMediaChangeDetection()
02760 
02761 /*++////////////////////////////////////////////////////////////////////////////
02762 
02763 ClassDisableMediaChangeDetection() ISSUE-2000/02/20-henrygab - why public?
02764 ClassDisableMediaChangeDetection() ISSUE-2000/02/20-henrygab - not documented
02765 
02766 Routine Description:
02767 
02768     This routine
02769 
02770 Arguments:
02771 
02772     DeviceObject - 
02773     Irp - 
02774 
02775 Return Value:
02776 
02777 --*/
02778 ULONG BreakOnMcnDisable = FALSE;
02779 
02780 VOID
02781 ClassDisableMediaChangeDetection(
02782     IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
02783     )
02784 {
02785     PMEDIA_CHANGE_DETECTION_INFO info = FdoExtension->MediaChangeDetectionInfo;
02786 
02787     PAGED_CODE();
02788 
02789     if(info == NULL) {
02790         return;
02791     }
02792 
02793     KeWaitForMutexObject(&info->MediaChangeMutex,
02794                          UserRequest,
02795                          KernelMode,
02796                          FALSE,
02797                          NULL);
02798 
02799     info->MediaChangeDetectionDisableCount++;
02800 
02801     DebugPrint((ClassDebugMCN, "ClassDisableMediaChangeDetection: "
02802                 "disable count is %d\n",
02803                 info->MediaChangeDetectionDisableCount));
02804 
02805     KeReleaseMutex(&info->MediaChangeMutex, FALSE);
02806 
02807     return;
02808 } // end ClassDisableMediaChangeDetection()
02809 
02810 /*++////////////////////////////////////////////////////////////////////////////
02811 
02812 ClassCleanupMediaChangeDetection() ISSUE-2000/02/20-henrygab - why public?!
02813 
02814 Routine Description:
02815 
02816     This routine will cleanup any resources allocated for MCN.  It is called
02817     by classpnp during remove device, and therefore is not typically required
02818     by external drivers.
02819 
02820 Arguments:
02821 
02822 Return Value:
02823 
02824 --*/
02825 VOID
02826 ClassCleanupMediaChangeDetection(
02827     IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
02828     )
02829 {
02830     PMEDIA_CHANGE_DETECTION_INFO info = FdoExtension->MediaChangeDetectionInfo;
02831 
02832     PAGED_CODE()
02833 
02834     if(info == NULL) {
02835         return;
02836     }
02837 
02838     FdoExtension->MediaChangeDetectionInfo = NULL;
02839     
02840     if (info->Gesn.Buffer) {
02841         ExFreePool(info->Gesn.Buffer);
02842     }
02843     IoFreeIrp(info->MediaChangeIrp);
02844     ExFreePool(info->SenseBuffer);
02845     ExFreePool(info);
02846     return;
02847 } // end ClassCleanupMediaChangeDetection()
02848 
02849 /*++////////////////////////////////////////////////////////////////////////////
02850 
02851 ClasspMcnControl() - ISSUE-2000/02/20-henrygab - not documented
02852 
02853 Routine Description:
02854 
02855     This routine
02856 
02857 Arguments:
02858 
02859     DeviceObject - 
02860     Irp - 
02861 
02862 Return Value:
02863 
02864 --*/
02865 NTSTATUS
02866 ClasspMcnControl(
02867     IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
02868     IN PIRP Irp,
02869     IN PSCSI_REQUEST_BLOCK Srb
02870     )
02871 {
02872     PCOMMON_DEVICE_EXTENSION commonExtension =
02873         (PCOMMON_DEVICE_EXTENSION) FdoExtension;
02874 
02875     PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
02876     PPREVENT_MEDIA_REMOVAL request = Irp->AssociatedIrp.SystemBuffer;
02877 
02878     PFILE_OBJECT fileObject = irpStack->FileObject;
02879     PFILE_OBJECT_EXTENSION fsContext = NULL;
02880 
02881     NTSTATUS status = STATUS_SUCCESS;
02882 
02883     PAGED_CODE();
02884 
02885     //
02886     // Check to make sure we have a file object extension to keep track of this
02887     // request.  If not we'll fail it before synchronizing.
02888     //
02889 
02890     TRY {
02891 
02892         if(fileObject != NULL) {
02893             fsContext = ClasspGetFsContext(commonExtension, fileObject);
02894         }else if(Irp->RequestorMode == KernelMode) { // && fileObject == NULL
02895             fsContext = &FdoExtension->KernelModeMcnContext;
02896         }
02897 
02898         if (fsContext == NULL) {
02899 
02900             //
02901             // This handle isn't setup correctly.  We can't let the
02902             // operation go.
02903             //
02904 
02905             status = STATUS_INVALID_PARAMETER;
02906             LEAVE;
02907         }
02908 
02909         if(request->PreventMediaRemoval) {
02910 
02911             //
02912             // This is a lock command.  Reissue the command in case bus or
02913             // device was reset and the lock was cleared.
02914             //
02915 
02916             ClassDisableMediaChangeDetection(FdoExtension);
02917             InterlockedIncrement(&(fsContext->McnDisableCount));
02918 
02919         } else {
02920 
02921             if(fsContext->McnDisableCount == 0) {
02922                 status = STATUS_INVALID_DEVICE_STATE;
02923                 LEAVE;
02924             }
02925 
02926             InterlockedDecrement(&(fsContext->McnDisableCount));
02927             ClassEnableMediaChangeDetection(FdoExtension);
02928         }
02929 
02930     } FINALLY {
02931 
02932         Irp->IoStatus.Status = status;
02933 
02934         if(Srb) {
02935             ExFreePool(Srb);
02936         }
02937 
02938         ClassReleaseRemoveLock(FdoExtension->DeviceObject, Irp);
02939         ClassCompleteRequest(FdoExtension->DeviceObject,
02940                              Irp,
02941                              IO_NO_INCREMENT);
02942     }
02943     return status;
02944 } // end ClasspMcnControl(
02945 
02946 /*++////////////////////////////////////////////////////////////////////////////
02947 
02948 ClasspMediaChangeRegistryCallBack()
02949 
02950 Routine Description:
02951 
02952     This callback for a registry SZ or MULTI_SZ is called once for each
02953     SZ in the value.  It will attempt to match the data with the
02954     UNICODE_STRING passed in as Context, and modify EntryContext if a
02955     match is found.  Written for ClasspCheckRegistryForMediaChangeCompletion
02956 
02957 Arguments:
02958 
02959     ValueName     - name of the key that was opened
02960     ValueType     - type of data stored in the value (REG_SZ for this routine)
02961     ValueData     - data in the registry, in this case a wide string
02962     ValueLength   - length of the data including the terminating null
02963     Context       - unicode string to compare against ValueData
02964     EntryContext  - should be initialized to 0, will be set to 1 if match found
02965 
02966 Return Value:
02967 
02968     STATUS_SUCCESS
02969     EntryContext will be 1 if found
02970 
02971 --*/
02972 NTSTATUS
02973 ClasspMediaChangeRegistryCallBack(
02974     IN PWSTR ValueName,
02975     IN ULONG ValueType,
02976     IN PVOID ValueData,
02977     IN ULONG ValueLength,
02978     IN PVOID Context,
02979     IN PVOID EntryContext
02980     )
02981 {
02982     PULONG valueFound;
02983     PUNICODE_STRING deviceString;
02984     PWSTR keyValue;
02985 
02986     PAGED_CODE();
02987     UNREFERENCED_PARAMETER(ValueName);
02988 
02989 
02990     //
02991     // if we have already set the value to true, exit
02992     //
02993 
02994     valueFound = EntryContext;
02995     if ((*valueFound) != 0) {
02996         DebugPrint((ClassDebugMCN, "ClasspMcnRegCB: already set to true\n"));
02997         return STATUS_SUCCESS;
02998     }
02999 
03000     if (ValueLength == sizeof(WCHAR)) {
03001         DebugPrint((ClassDebugError, "ClasspMcnRegCB: NULL string should "
03002                     "never be passed to registry call-back!\n"));
03003         return STATUS_SUCCESS;
03004     }
03005 
03006 
03007     //
03008     // if the data is not a terminated string, exit
03009     //
03010 
03011     if (ValueType != REG_SZ) {
03012         return STATUS_SUCCESS;
03013     }
03014 
03015     deviceString = Context;
03016     keyValue = ValueData;
03017     ValueLength -= sizeof(WCHAR); // ignore the null character
03018 
03019     //
03020     // do not compare more memory than is in deviceString
03021     //
03022 
03023     if (ValueLength > deviceString->Length) {
03024         ValueLength = deviceString->Length;
03025     }
03026 
03027     //
03028     // if the strings match, disable autorun
03029     //
03030 
03031     if (RtlCompareMemory(deviceString->Buffer, keyValue, ValueLength) == ValueLength) {
03032         DebugPrint((ClassDebugMCN, "ClasspRegMcnCB: Match found\n"));
03033         DebugPrint((ClassDebugMCN, "ClasspRegMcnCB: DeviceString at %p\n",
03034                     deviceString->Buffer));
03035         DebugPrint((ClassDebugMCN, "ClasspRegMcnCB: KeyValue at %p\n",
03036                     keyValue));
03037         (*valueFound) = TRUE;
03038     }
03039 
03040     return STATUS_SUCCESS;
03041 } // end ClasspMediaChangeRegistryCallBack()
03042 
03043 /*++////////////////////////////////////////////////////////////////////////////
03044 
03045 ClasspTimerTick() - ISSUE-2000/02/20-henrygab - not documented
03046 
03047 Routine Description:
03048 
03049     This routine
03050 
03051 Arguments:
03052 
03053     DeviceObject - 
03054     Irp - 
03055 
03056 Return Value:
03057 
03058 --*/
03059 VOID
03060 ClasspTimerTick(
03061     PDEVICE_OBJECT DeviceObject,
03062     PVOID Context
03063     )
03064 {
03065     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
03066     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
03067     ULONG isRemoved;
03068 
03069     ASSERT(commonExtension->IsFdo);
03070 
03071     //
03072     // Do any media change work
03073     //
03074     isRemoved = ClassAcquireRemoveLock(DeviceObject, (PIRP)ClasspTimerTick);
03075 
03076     //
03077     // We stop the timer before deleting the device.  It's safe to keep going
03078     // if the flag value is REMOVE_PENDING because the removal thread will be
03079     // blocked trying to stop the timer.
03080     //
03081 
03082     ASSERT(isRemoved != REMOVE_COMPLETE);
03083 
03084     //
03085     // This routine is reasonably safe even if the device object has a pending
03086     // remove
03087 
03088     if(!isRemoved) {
03089 
03090         PFAILURE_PREDICTION_INFO info = fdoExtension->FailurePredictionInfo;
03091 
03092         //
03093         // Do any media change detection work
03094         //
03095 
03096         if (fdoExtension->MediaChangeDetectionInfo != NULL) {
03097 
03098             ClassCheckMediaState(fdoExtension);
03099 
03100         }
03101 
03102         //
03103         // Do any failure prediction work
03104         //
03105         if ((info != NULL) && (info->Method != FailurePredictionNone)) {
03106 
03107             ULONG countDown;
03108             ULONG active;
03109 
03110             if (ClasspCanSendPollingIrp(fdoExtension)) {
03111 
03112                 //
03113                 // Synchronization is not required here since the Interlocked
03114                 // locked instruction guarantees atomicity. Other code that
03115                 // resets CountDown uses InterlockedExchange which is also
03116                 // atomic.
03117                 //
03118                 countDown = InterlockedDecrement(&info->CountDown);
03119                 if (countDown == 0) {
03120 
03121                     DebugPrint((4, "ClasspTimerTick: Send FP irp for %p\n",
03122                                    DeviceObject));
03123 
03124                     if(info->WorkQueueItem == NULL) {
03125 
03126                         info->WorkQueueItem =
03127                             IoAllocateWorkItem(fdoExtension->DeviceObject);
03128 
03129                         if(info->WorkQueueItem == NULL) {
03130 
03131                             //
03132                             // Set the countdown to one minute in the future.
03133                             // we'll try again then in the hopes there's more
03134                             // free memory.
03135                             //
03136 
03137                             DebugPrint((1, "ClassTimerTick: Couldn't allocate "
03138                                            "item - try again in one minute\n"));
03139                             InterlockedExchange(&info->CountDown, 60);
03140 
03141                         } else {
03142 
03143                             //
03144                             // Grab the remove lock so that removal will block
03145                             // until the work item is done.
03146                             //
03147 
03148                             ClassAcquireRemoveLock(fdoExtension->DeviceObject,
03149                                                    info->WorkQueueItem);
03150 
03151                             IoQueueWorkItem(info->WorkQueueItem,
03152                                             ClasspFailurePredict,
03153                                             DelayedWorkQueue,
03154                                             info);
03155                         }
03156 
03157                     } else {
03158 
03159                         DebugPrint((3, "ClasspTimerTick: Failure "
03160                                        "Prediction work item is "
03161                                        "already active for device %p\n",
03162                                     DeviceObject));
03163 
03164                     }
03165                 } // end (countdown == 0)
03166 
03167             } else {
03168                 //
03169                 // If device is sleeping then just rearm polling timer
03170                 DebugPrint((4, "ClassTimerTick, SHHHH!!! device is %p is sleeping\n",
03171                             DeviceObject));
03172             }
03173 
03174         } // end failure prediction polling
03175 
03176         //
03177         // Give driver a chance to do its own specific work
03178         //
03179 
03180         if (commonExtension->DriverExtension->InitData.ClassTick != NULL) {
03181 
03182             commonExtension->DriverExtension->InitData.ClassTick(DeviceObject);
03183 
03184         } // end device specific tick handler
03185     } // end check for removed
03186 
03187     ClassReleaseRemoveLock(DeviceObject, (PIRP)ClasspTimerTick);
03188 } // end ClasspTimerTick()
03189 
03190 /*++////////////////////////////////////////////////////////////////////////////
03191 
03192 ClasspEnableTimer() - ISSUE-2000/02/20-henrygab - not documented
03193 
03194 Routine Description:
03195 
03196     This routine
03197 
03198 Arguments:
03199 
03200     DeviceObject - 
03201     Irp - 
03202 
03203 Return Value:
03204 
03205 --*/
03206 NTSTATUS
03207 ClasspEnableTimer(
03208     PDEVICE_OBJECT DeviceObject
03209     )
03210 {
03211     NTSTATUS status;
03212 
03213     PAGED_CODE();
03214 
03215     if (DeviceObject->Timer == NULL) {
03216 
03217         status = IoInitializeTimer(DeviceObject, ClasspTimerTick, NULL);
03218 
03219     } else {
03220 
03221         status = STATUS_SUCCESS;
03222 
03223     }
03224 
03225     if (NT_SUCCESS(status)) {
03226 
03227         IoStartTimer(DeviceObject);
03228         DebugPrint((1, "ClasspEnableTimer: Once a second timer enabled "
03229                     "for device %p\n", DeviceObject));
03230 
03231     }
03232 
03233     DebugPrint((1, "ClasspEnableTimer: Device %p, Status %lx "
03234                 "initializing timer\n", DeviceObject, status));
03235 
03236     return status;
03237 
03238 } // end ClasspEnableTimer()
03239 
03240 /*++////////////////////////////////////////////////////////////////////////////
03241 
03242 ClasspDisableTimer() - ISSUE-2000/02/20-henrygab - not documented
03243 
03244 Routine Description:
03245 
03246     This routine
03247 
03248 Arguments:
03249 
03250     DeviceObject - 
03251     Irp - 
03252 
03253 Return Value:
03254 
03255 --*/
03256 NTSTATUS
03257 ClasspDisableTimer(
03258     PDEVICE_OBJECT DeviceObject
03259     )
03260 {
03261     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
03262     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
03263     PMEDIA_CHANGE_DETECTION_INFO mCDInfo = fdoExtension->MediaChangeDetectionInfo;
03264     PFAILURE_PREDICTION_INFO fPInfo = fdoExtension->FailurePredictionInfo;
03265     NTSTATUS status;
03266 
03267     PAGED_CODE();
03268 
03269     if (DeviceObject->Timer != NULL) {
03270 
03271         //
03272         // we are only going to stop the actual timer in remove device routine.
03273         // it is the responsibility of the code within the timer routine to
03274         // check if the device is removed and not processing io for the final
03275         // call.
03276         // this keeps the code clean and prevents lots of bugs.
03277         //
03278 
03279 
03280         IoStopTimer(DeviceObject);
03281         DebugPrint((3, "ClasspDisableTimer: Once a second timer disabled "
03282                     "for device %p\n", DeviceObject));
03283 
03284     } else {
03285 
03286         DebugPrint((1, "ClasspDisableTimer: Timer never enabled\n"));
03287 
03288     }
03289 
03290     return STATUS_SUCCESS;
03291 } // end ClasspDisableTimer()
03292 
03293 /*++////////////////////////////////////////////////////////////////////////////
03294 
03295 ClasspFailurePredict() - ISSUE-2000/02/20-henrygab - not documented
03296 
03297 Routine Description:
03298 
03299     This routine
03300 
03301 Arguments:
03302 
03303     DeviceObject - 
03304     Irp - 
03305 
03306 Return Value:
03307 
03308 Note:  this function can be called (via the workitem callback) after the paging device is shut down,
03309          so it must be PAGE LOCKED.
03310 --*/
03311 VOID
03312 ClasspFailurePredict(
03313     IN PDEVICE_OBJECT DeviceObject,
03314     IN PFAILURE_PREDICTION_INFO Info
03315     )
03316 {
03317     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
03318     PIO_WORKITEM workItem;
03319     STORAGE_PREDICT_FAILURE checkFailure;
03320     SCSI_ADDRESS scsiAddress;
03321 
03322     NTSTATUS status;
03323 
03324     ASSERT(Info != NULL);
03325 
03326     DebugPrint((1, "ClasspFailurePredict: Polling for failure\n"));
03327 
03328     //
03329     // Mark the work item as inactive and reset the countdown timer.  we
03330     // can't risk freeing the work item until we've released the remove-lock
03331     // though - if we do it might get resused as a tag before we can release
03332     // the lock.
03333     //
03334 
03335     InterlockedExchange(&Info->CountDown, Info->Period);
03336     workItem = InterlockedExchangePointer(&(Info->WorkQueueItem), NULL);
03337 
03338     if (ClasspCanSendPollingIrp(fdoExtension)) {
03339 
03340         KEVENT event;
03341         PDEVICE_OBJECT topOfStack;
03342         PIRP irp = NULL;
03343         IO_STATUS_BLOCK ioStatus;
03344 
03345         KeInitializeEvent(&event, SynchronizationEvent, FALSE);
03346 
03347         topOfStack = IoGetAttachedDeviceReference(DeviceObject);
03348 
03349         //
03350         // Send down irp to see if drive is predicting failure
03351         //
03352 
03353         irp = IoBuildDeviceIoControlRequest(
03354                         IOCTL_STORAGE_PREDICT_FAILURE,
03355                         topOfStack,
03356                         NULL,
03357                         0,
03358                         &checkFailure,
03359                         sizeof(STORAGE_PREDICT_FAILURE),
03360                         FALSE,
03361                         &event,
03362                         &ioStatus);
03363 
03364 
03365         if (irp != NULL) {
03366             status = IoCallDriver(topOfStack, irp);
03367             if (status == STATUS_PENDING) {
03368                 KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
03369                 status = ioStatus.Status;
03370             }
03371         } else {
03372             status = STATUS_INSUFFICIENT_RESOURCES;
03373         }
03374 
03375         if (NT_SUCCESS(status) && (checkFailure.PredictFailure)) {
03376 
03377             checkFailure.PredictFailure = 512;
03378 
03379             //
03380             // Send down irp to get scsi address
03381             //
03382             KeInitializeEvent(&event, SynchronizationEvent, FALSE);
03383 
03384             RtlZeroMemory(&scsiAddress, sizeof(SCSI_ADDRESS));
03385             irp = IoBuildDeviceIoControlRequest(
03386                 IOCTL_SCSI_GET_ADDRESS,
03387                 topOfStack,
03388                 NULL,
03389                 0,
03390                 &scsiAddress,
03391                 sizeof(SCSI_ADDRESS),
03392                 FALSE,
03393                 &event,
03394                 &ioStatus);
03395 
03396             if (irp != NULL) {
03397                 status = IoCallDriver(topOfStack, irp);
03398                 if (status == STATUS_PENDING) {
03399                     KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
03400                     status = ioStatus.Status;
03401                 }
03402             }
03403 
03404             ClassNotifyFailurePredicted(fdoExtension,
03405                                     (PUCHAR)&checkFailure,
03406                                     sizeof(checkFailure),
03407                                     (BOOLEAN)(fdoExtension->FailurePredicted == FALSE),
03408                                     2,
03409                                     scsiAddress.PathId,
03410                                     scsiAddress.TargetId,
03411                                     scsiAddress.Lun);
03412 
03413             fdoExtension->FailurePredicted = TRUE;
03414 
03415         }
03416 
03417         ObDereferenceObject(topOfStack);
03418     }
03419 
03420     ClassReleaseRemoveLock(DeviceObject, (PIRP) workItem);
03421     IoFreeWorkItem(workItem);
03422     return;
03423 } // end ClasspFailurePredict()
03424 
03425 /*++////////////////////////////////////////////////////////////////////////////
03426 
03427 ClassNotifyFailurePredicted() ISSUE-alanwar-2000/02/20 - not documented
03428 
03429 Routine Description:
03430 
03431 Arguments:
03432 
03433 Return Value:
03434 
03435 --*/
03436 VOID
03437 ClassNotifyFailurePredicted(
03438     PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
03439     PUCHAR Buffer,
03440     ULONG BufferSize,
03441     BOOLEAN LogError,
03442     ULONG UniqueErrorValue,
03443     UCHAR PathId,
03444     UCHAR TargetId,
03445     UCHAR Lun
03446     )
03447 {
03448     PIO_ERROR_LOG_PACKET logEntry;
03449 
03450     DebugPrint((1, "ClasspFailurePredictPollCompletion: Failure predicted for device %p\n", FdoExtension->DeviceObject));
03451 
03452     //
03453     // Fire off a WMI event
03454     //
03455     ClassWmiFireEvent(FdoExtension->DeviceObject,
03456                                    &StoragePredictFailureEventGuid,
03457                                    0,
03458                                    BufferSize,
03459                                    Buffer);
03460 
03461     //
03462     // Log an error into the eventlog
03463     //
03464 
03465     if (LogError)
03466     {
03467         logEntry = IoAllocateErrorLogEntry(
03468                             FdoExtension->DeviceObject,
03469                            sizeof(IO_ERROR_LOG_PACKET) + (3 * sizeof(ULONG)));
03470 
03471         if (logEntry != NULL)
03472         {
03473 
03474             logEntry->FinalStatus     = STATUS_SUCCESS;
03475             logEntry->ErrorCode       = IO_WRN_FAILURE_PREDICTED;
03476             logEntry->SequenceNumber  = 0;
03477             logEntry->MajorFunctionCode = IRP_MJ_DEVICE_CONTROL;
03478             logEntry->IoControlCode   = IOCTL_STORAGE_PREDICT_FAILURE;
03479             logEntry->RetryCount      = 0;
03480             logEntry->UniqueErrorValue = UniqueErrorValue;
03481             logEntry->DumpDataSize    = 3;
03482 
03483             logEntry->DumpData[0] = PathId;
03484             logEntry->DumpData[1] = TargetId;
03485             logEntry->DumpData[2] = Lun;
03486 
03487             //
03488             // Write the error log packet.
03489             //
03490 
03491             IoWriteErrorLogEntry(logEntry);
03492         }
03493     }
03494 } // end ClassNotifyFailurePredicted()
03495 
03496 /*++////////////////////////////////////////////////////////////////////////////
03497 
03498 ClassSetFailurePredictionPoll()
03499 
03500 Routine Description:
03501 
03502     This routine enables polling for failure prediction, setting the timer
03503     to fire every N seconds as specified by the PollingPeriod.
03504 
03505 Arguments:
03506 
03507     FdoExtension - the device to setup failure prediction for.
03508 
03509     FailurePredictionMethod - specific failure prediction method to use
03510         if set to FailurePredictionNone, will disable failure detection
03511 
03512     PollingPeriod - if 0 then no change to current polling timer
03513 
03514 Return Value:
03515 
03516     NT Status
03517 
03518 --*/
03519 NTSTATUS
03520 ClassSetFailurePredictionPoll(
03521     PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
03522     FAILURE_PREDICTION_METHOD FailurePredictionMethod,
03523     ULONG PollingPeriod
03524     )
03525 {
03526     PFAILURE_PREDICTION_INFO info;
03527     NTSTATUS status;
03528     DEVICE_POWER_STATE powerState;
03529 
03530     PAGED_CODE();
03531 
03532     if (FdoExtension->FailurePredictionInfo == NULL) {
03533 
03534         if (FailurePredictionMethod != FailurePredictionNone) {
03535 
03536             info = ExAllocatePoolWithTag(NonPagedPool,
03537                                          sizeof(FAILURE_PREDICTION_INFO),
03538                                          CLASS_TAG_FAILURE_PREDICT);
03539 
03540             if (info == NULL) {
03541 
03542                 return STATUS_INSUFFICIENT_RESOURCES;
03543 
03544             }
03545 
03546             KeInitializeEvent(&info->Event, SynchronizationEvent, TRUE);
03547 
03548             info->WorkQueueItem = NULL;
03549             info->Period = DEFAULT_FAILURE_PREDICTION_PERIOD;
03550 
03551         } else {
03552 
03553             //
03554             // FaultPrediction has not been previously initialized, nor
03555             // is it being initialized now. No need to do anything.
03556             //
03557             return STATUS_SUCCESS;
03558 
03559         }
03560 
03561         FdoExtension->FailurePredictionInfo = info;
03562 
03563     } else {
03564 
03565         info = FdoExtension->FailurePredictionInfo;
03566 
03567     }
03568 
03569     KeWaitForSingleObject(&info->Event,
03570                           UserRequest,
03571                           UserMode,
03572                           FALSE,
03573                           NULL);
03574 
03575 
03576     //
03577     // Reset polling period and counter. Setup failure detection type
03578     //
03579 
03580     if (PollingPeriod != 0) {
03581 
03582         InterlockedExchange(&info->Period, PollingPeriod);
03583 
03584     }
03585 
03586     InterlockedExchange(&info->CountDown, info->Period);
03587 
03588     info->Method = FailurePredictionMethod;
03589     if (FailurePredictionMethod != FailurePredictionNone) {
03590 
03591         status = ClasspEnableTimer(FdoExtension->DeviceObject);
03592 
03593         if (NT_SUCCESS(status)) {
03594             DebugPrint((3, "ClassEnableFailurePredictPoll: Enabled for "
03595                         "device %p\n", FdoExtension->DeviceObject));
03596         }
03597 
03598     } else {
03599 
03600         status = ClasspDisableTimer(FdoExtension->DeviceObject);
03601         DebugPrint((3, "ClassEnableFailurePredictPoll: Disabled for "
03602                     "device %p\n", FdoExtension->DeviceObject));
03603         status = STATUS_SUCCESS;
03604 
03605     }
03606 
03607     KeSetEvent(&info->Event, IO_NO_INCREMENT, FALSE);
03608 
03609     return status;
03610 } // end ClassSetFailurePredictionPoll()
03611 

Generated on Sun May 27 2012 04:28:16 for ReactOS by doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.