diff options
Diffstat (limited to 'storage/class/cdrom/src/autorun.c')
| -rw-r--r-- | storage/class/cdrom/src/autorun.c | 3072 |
1 files changed, 0 insertions, 3072 deletions
diff --git a/storage/class/cdrom/src/autorun.c b/storage/class/cdrom/src/autorun.c deleted file mode 100644 index 57bacec5..00000000 --- a/storage/class/cdrom/src/autorun.c +++ /dev/null @@ -1,3072 +0,0 @@ -/*++ - -Copyright (C) Microsoft Corporation. All rights reserved. - -Module Name: - - autorun.c - -Abstract: - - Code for support of media change detection in the cd/dvd driver - -Environment: - - kernel mode only - -Notes: - - -Revision History: - ---*/ - -#include "stddef.h" -#include "string.h" - -#include "ntddk.h" -#include "ntddstor.h" -#include "cdrom.h" -#include "mmc.h" -#include "ioctl.h" - -#include "ntstrsafe.h" - -#ifdef DEBUG_USE_WPP -#include "autorun.tmh" -#endif - -#define GESN_TIMEOUT_VALUE (0x4) -#define GESN_BUFFER_SIZE (0x8) -#define GESN_DEVICE_BUSY_LOWER_THRESHOLD_100_MS (2) - -#define MAXIMUM_IMMEDIATE_MCN_RETRIES (0x20) -#define MCN_REG_SUBKEY_NAME (L"MediaChangeNotification") -#define MCN_REG_AUTORUN_DISABLE_INSTANCE_NAME (L"AlwaysDisableMCN") -#define MCN_REG_AUTORUN_ENABLE_INSTANCE_NAME (L"AlwaysEnableMCN") - -// -// Only send polling irp when device is fully powered up and a -// power down irp is not in progress. -// -// NOTE: This helps close a window in time where a polling irp could cause -// a drive to spin up right after it has powered down. The problem is -// that SCSIPORT, ATAPI and SBP2 will be in the process of powering -// down (which may take a few seconds), but won't know that. It would -// then get a polling irp which will be put into its queue since it -// the disk isn't powered down yet. Once the disk is powered down it -// will find the polling irp in the queue and then power up the -// device to do the poll. They do not want to check if the polling -// irp has the SRB_NO_KEEP_AWAKE flag here since it is in a critical -// path and would slow down all I/Os. A better way to fix this -// would be to serialize the polling and power down irps so that -// only one of them is sent to the device at a time. -// - -_IRQL_requires_max_(PASSIVE_LEVEL) -BOOLEAN -DeviceIsMediaChangeDisabledDueToHardwareLimitation( - _In_ PCDROM_DEVICE_EXTENSION DeviceExtension - ); - -_IRQL_requires_max_(PASSIVE_LEVEL) -BOOLEAN -DeviceIsMediaChangeDisabledForClass( - _In_ PCDROM_DEVICE_EXTENSION DeviceExtension - ); - -_IRQL_requires_max_(PASSIVE_LEVEL) -NTSTATUS -DeviceMediaChangeDeviceInstanceOverride( - _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, - _Out_ PBOOLEAN Enabled - ); - -_IRQL_requires_max_(PASSIVE_LEVEL) -NTSTATUS -DeviceInitializeMcn( - _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, - _In_ BOOLEAN AllowDriveToSleep - ); - -_IRQL_requires_max_(PASSIVE_LEVEL) -NTSTATUS -DeviceInitializeGesn( - _In_ PCDROM_DEVICE_EXTENSION DeviceExtension - ); - -_IRQL_requires_max_(APC_LEVEL) -NTSTATUS -GesnDataInterpret( - _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, - _In_ PNOTIFICATION_EVENT_STATUS_HEADER Header, - _Out_ PBOOLEAN ResendImmediately - ); - -RTL_QUERY_REGISTRY_ROUTINE DeviceMediaChangeRegistryCallBack; - -EVT_WDF_WORKITEM DeviceDisableGesn; - -#if ALLOC_PRAGMA - -#pragma alloc_text(PAGE, DeviceInitializeMediaChangeDetection) -#pragma alloc_text(PAGE, DeviceEnableMediaChangeDetection) -#pragma alloc_text(PAGE, DeviceDisableMediaChangeDetection) -#pragma alloc_text(PAGE, DeviceSendDelayedMediaChangeNotifications) -#pragma alloc_text(PAGE, DeviceReleaseMcnResources) -#pragma alloc_text(PAGE, DeviceMediaChangeRegistryCallBack) -#pragma alloc_text(PAGE, DeviceInitializeMcn) -#pragma alloc_text(PAGE, DeviceDisableGesn) - -#pragma alloc_text(PAGE, DeviceIsMediaChangeDisabledDueToHardwareLimitation) -#pragma alloc_text(PAGE, DeviceMediaChangeDeviceInstanceOverride) -#pragma alloc_text(PAGE, DeviceIsMediaChangeDisabledForClass) - -#pragma alloc_text(PAGE, DeviceDisableMainTimer) - -#pragma alloc_text(PAGE, GesnDataInterpret) - -#pragma alloc_text(PAGE, RequestSetupMcnRequest) -#pragma alloc_text(PAGE, RequestPostWorkMcnRequest) -#pragma alloc_text(PAGE, RequestSendMcnRequest) - -// -// DeviceEnableMainTimer is called by EvtDeviceD0Entry which can't be made pageable -// so neither is DeviceEnableMainTimer -// -//#pragma alloc_text(PAGE, DeviceEnableMainTimer) - -#pragma alloc_text(PAGE, DeviceInitializeGesn) - -#pragma alloc_text(PAGE, RequestHandleMcnControl) - -#endif - -#pragma warning(push) -#pragma warning(disable:4152) // nonstandard extension, function/data pointer conversion in expression - - -_IRQL_requires_max_(APC_LEVEL) -NTSTATUS -GesnDataInterpret( - _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, - _In_ PNOTIFICATION_EVENT_STATUS_HEADER Header, - _Out_ PBOOLEAN ResendImmediately - ) -/*++ - -Routine Description: - - This routine will interpret the data returned for a GESN command, and - (if appropriate) set the media change event, and broadcast the - appropriate events to user mode for applications who care. - -Arguments: - - DeviceExtension - the device extension - - Header - the resulting data from a GESN event. - requires at least EIGHT valid bytes (header == 4, data == 4) - - ResendImmediately - whether or not to immediately resend the request. - this should be FALSE if there was no event, FALSE if the reported - event was of the DEVICE BUSY class, else true. - -Return Value: - - STATUS_SUCCESS if successful, an error code otherwise - -Notes: - - DataBuffer must be at least four bytes of valid data (header == 4 bytes), - and have at least eight bytes of allocated memory (all events == 4 bytes). - - The call to StartNextPacket may occur before this routine is completed. - the operational change notifications are informational in nature, and - while useful, are not neccessary to ensure proper operation. For example, - if the device morphs to no longer supporting WRITE commands, all further - write commands will fail. There exists a small timing window wherein - IOCTL_IS_DISK_WRITABLE may be called and get an incorrect response. If - a device supports software write protect, it is expected that the - application can handle such a case. - - NOTE: perhaps setting the updaterequired byte to one should be done here. - if so, it relies upon the setting of a 32-byte value to be an atomic - operation. unfortunately, there is no simple way to notify a class driver - which wants to know that the device behavior requires updating. - - Not ready events may be sent every second. For example, if we were - to minimize the number of asynchronous notifications, an application may - register just after a large busy time was reported. This would then - prevent the application from knowing the device was busy until some - arbitrarily chosen timeout has occurred. Also, the GESN request would - have to still occur, since it checks for non-busy events (such as user - keybutton presses and media change events) as well. The specification - states that the lower-numered events get reported first, so busy events, - while repeating, will only be reported when all other events have been - cleared from the device. - ---*/ -{ - NTSTATUS status = STATUS_SUCCESS; - PMEDIA_CHANGE_DETECTION_INFO info = DeviceExtension->MediaChangeDetectionInfo; - LONG dataLength = 0; - LONG requiredLength = 0; - BOOLEAN inHomePosition = FALSE; - - PAGED_CODE(); - - // note: don't allocate anything in this routine so that we can - // always just 'return'. - *ResendImmediately = FALSE; - - if (Header->NEA) - { - return status; - } - if (Header->NotificationClass == NOTIFICATION_NO_CLASS_EVENTS) - { - return status; - } - - // HACKHACK - REF #0001 - // This loop is only taken initially, due to the inability to reliably - // auto-detect drives that report events correctly at boot. When we - // detect this behavior during the normal course of running, we will - // disable the hack, allowing more efficient use of the system. This - // should occur "nearly" instantly, as the drive should have multiple - // events queue'd (ie. power, morphing, media). - if (info->Gesn.HackEventMask) - { - // all events use the low four bytes of zero to indicate - // that there was no change in status. - UCHAR thisEvent = Header->ClassEventData[0] & 0xf; - UCHAR lowestSetBit; - UCHAR thisEventBit = (1 << Header->NotificationClass); - - if (!TEST_FLAG(info->Gesn.EventMask, thisEventBit)) - { - // The drive is reporting an event that wasn't requested - return STATUS_DEVICE_PROTOCOL_ERROR; - } - - // some bit magic here... this results in the lowest set bit only - lowestSetBit = info->Gesn.EventMask; - lowestSetBit &= (info->Gesn.EventMask - 1); - lowestSetBit ^= (info->Gesn.EventMask); - - if (thisEventBit != lowestSetBit) - { - // HACKHACK - REF #0001 - // the first time we ever see an event set that is not the lowest - // set bit in the request (iow, highest priority), we know that the - // hack is no longer required, as the device is ignoring "no change" - // events when a real event is waiting in the other requested queues. - TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, - "GESN::NONE: Compliant drive found, " - "removing GESN hack (%x, %x)\n", - thisEventBit, info->Gesn.EventMask)); - - info->Gesn.HackEventMask = FALSE; - } - else if (thisEvent == 0) // NOTIFICATION_*_EVENT_NO_CHANGE - { - // HACKHACK - REF #0001 - // note: this hack prevents poorly implemented firmware from constantly - // returning "No Event". we do this by cycling through the - // supported list of events here. - SET_FLAG(info->Gesn.NoChangeEventMask, thisEventBit); - CLEAR_FLAG(info->Gesn.EventMask, thisEventBit); - - // if we have cycled through all supported event types, then - // we need to reset the events we are asking about. else we - // want to resend this request immediately in case there was - // another event pending. - if (info->Gesn.EventMask == 0) - { - info->Gesn.EventMask = info->Gesn.NoChangeEventMask; - info->Gesn.NoChangeEventMask = 0; - } - else - { - *ResendImmediately = TRUE; - } - return status; - } - - } // end if (info->Gesn.HackEventMask) - - dataLength = (Header->EventDataLength[0] << 8) | - (Header->EventDataLength[1] & 0xff); - dataLength -= 2; - requiredLength = 4; // all events are four bytes - - if (dataLength < requiredLength) - { - TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN, - "error - GESN returned only %x bytes data for fdo %p\n", - dataLength, DeviceExtension->DeviceObject)); - - return STATUS_DEVICE_PROTOCOL_ERROR; - } - - if (dataLength > requiredLength) - { - TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN, - "error - GESN returned too many (%x) bytes data for fdo %p\n", - dataLength, DeviceExtension->DeviceObject)); - } - - if ((Header->ClassEventData[0] & 0xf) == 0) - { - // a zero event is a "no change event, so do not retry - return status; - } - - // because a event other than "no change" occurred, - // we should immediately resend this request. - *ResendImmediately = TRUE; - - switch (Header->NotificationClass) - { - - case NOTIFICATION_OPERATIONAL_CHANGE_CLASS_EVENTS: // 0x01 - { - PNOTIFICATION_OPERATIONAL_STATUS opChangeInfo = - (PNOTIFICATION_OPERATIONAL_STATUS)(Header->ClassEventData); - ULONG event; - - if (opChangeInfo->OperationalEvent == NOTIFICATION_OPERATIONAL_EVENT_CHANGE_REQUESTED) - { - break; - } - - event = (opChangeInfo->Operation[0] << 8) | - (opChangeInfo->Operation[1] ) ; - - // Workaround some hardware that is buggy but prevalent in the market - // This hardware has the property that it will report OpChange events repeatedly, - // causing us to retry immediately so quickly that we will eventually disable - // GESN to prevent an infinite loop. - // (only one valid OpChange event type now, only two ever defined) - if (info->MediaChangeRetryCount >= 4) - { - // - // HACKHACK - REF #0002 - // Some drives incorrectly report OpChange/Change (001b/0001h) events - // continuously when the tray has been ejected. This causes this routine - // to set ResendImmediately to "TRUE", and that results in our cycling - // 32 times immediately resending. At that point, we give up detecting - // the infinite retry loop, and disable GESN on these drives. This - // prevents Media Eject Request (from eject button) from being reported. - // Thus, instead we should attempt to workaround this issue by detecting - // this behavior. - // - - static UCHAR const OpChangeMask = 0x02; - - // At least one device reports "temporarily busy" (which is useless) on eject - // At least one device reports "OpChange" repeatedly when re-inserting media - // All seem to work well using this workaround - - TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_MCN, - "GESN OpChange events are broken. Working around this problem in software (for WDFDEVICE %p)\n", - DeviceExtension->Device)); - - // OpChange is not the only bit set -- Media class is required.... - NT_ASSERT(CountOfSetBitsUChar(info->Gesn.EventMask) != 1); - - // Force the use of the hackhack (ref #0001) to workaround the - // issue noted this hackhack (ref #0002). - SET_FLAG(info->Gesn.NoChangeEventMask, OpChangeMask); - CLEAR_FLAG(info->Gesn.EventMask, OpChangeMask); - info->Gesn.HackEventMask = TRUE; - - // don't request the opChange event again. use the method - // defined by hackhack (ref #0001) as the workaround. - if (info->Gesn.EventMask == 0) - { - info->Gesn.EventMask = info->Gesn.NoChangeEventMask; - info->Gesn.NoChangeEventMask = 0; - *ResendImmediately = FALSE; - } - else - { - *ResendImmediately = TRUE; - } - - break; - } - - - if ((event == NOTIFICATION_OPERATIONAL_OPCODE_FEATURE_ADDED) | - (event == NOTIFICATION_OPERATIONAL_OPCODE_FEATURE_CHANGE)) - { - TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, - "GESN says features added/changed for WDFDEVICE %p\n", - DeviceExtension->Device)); - - // don't notify that new media arrived, just set the - // DO_VERIFY to force a FS reload. - - if (IsVolumeMounted(DeviceExtension->DeviceObject)) - { - SET_FLAG(DeviceExtension->DeviceObject->Flags, DO_VERIFY_VOLUME); - } - - // Call error handler with - // a "fake" media change error in case it needs to update - // internal structures as though a media change occurred. - { - SCSI_REQUEST_BLOCK srb = {0}; - SENSE_DATA sense = {0}; - NTSTATUS tempStatus; - BOOLEAN retry; - - tempStatus = STATUS_MEDIA_CHANGED; - retry = FALSE; - - srb.CdbLength = 6; - srb.Length = sizeof(SCSI_REQUEST_BLOCK); - srb.SrbStatus = SRB_STATUS_AUTOSENSE_VALID | SRB_STATUS_ERROR; - srb.SenseInfoBuffer = &sense; - srb.SenseInfoBufferLength = sizeof(SENSE_DATA); - - sense.AdditionalSenseLength = sizeof(SENSE_DATA) - - RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseLength); - - sense.SenseKey = SCSI_SENSE_UNIT_ATTENTION; - sense.AdditionalSenseCode = SCSI_ADSENSE_MEDIUM_CHANGED; - - if (DeviceExtension->DeviceAdditionalData.ErrorHandler) - { - DeviceExtension->DeviceAdditionalData.ErrorHandler(DeviceExtension, - &srb, - &tempStatus, - &retry); - } - } // end error handler - - } - break; - } - - case NOTIFICATION_EXTERNAL_REQUEST_CLASS_EVENTS: // 0x3 - { - PNOTIFICATION_EXTERNAL_STATUS externalInfo = - (PNOTIFICATION_EXTERNAL_STATUS)(Header->ClassEventData); - DEVICE_EVENT_EXTERNAL_REQUEST externalData = {0}; - - // unfortunately, due to time constraints, we will only notify - // about keys being pressed, and not released. this makes keys - // single-function, but simplifies the code significantly. - if (externalInfo->ExternalEvent != NOTIFICATION_EXTERNAL_EVENT_BUTTON_DOWN) - { - break; - } - - TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, - "GESN::EXTERNAL: Event: %x Status %x Req %x\n", - externalInfo->ExternalEvent, externalInfo->ExternalStatus, - (externalInfo->Request[0] << 8) | externalInfo->Request[1] - )); - - externalData.Version = 1; - externalData.DeviceClass = 0; - externalData.ButtonStatus = externalInfo->ExternalEvent; - externalData.Request = (externalInfo->Request[0] << 8) | - (externalInfo->Request[1] & 0xff); - KeQuerySystemTime(&(externalData.SystemTime)); - externalData.SystemTime.QuadPart *= (LONGLONG)KeQueryTimeIncrement(); - - DeviceSendNotification(DeviceExtension, - &GUID_IO_DEVICE_EXTERNAL_REQUEST, - sizeof(DEVICE_EVENT_EXTERNAL_REQUEST), - &externalData); - - return status; - } - - case NOTIFICATION_MEDIA_STATUS_CLASS_EVENTS: // 0x4 - { - PNOTIFICATION_MEDIA_STATUS mediaInfo = - (PNOTIFICATION_MEDIA_STATUS)(Header->ClassEventData); - - if ((mediaInfo->MediaEvent == NOTIFICATION_MEDIA_EVENT_NEW_MEDIA) || - (mediaInfo->MediaEvent == NOTIFICATION_MEDIA_EVENT_MEDIA_CHANGE)) - { - TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, - "GESN::MEDIA ARRIVAL, Status %x\n", - mediaInfo->MediaStatus)); - - if (IsVolumeMounted(DeviceExtension->DeviceObject)) - { - SET_FLAG(DeviceExtension->DeviceObject->Flags, DO_VERIFY_VOLUME); - } - DeviceSetMediaChangeStateEx(DeviceExtension, - MediaPresent, - NULL); - - // If media is inserted into slot loading type, mark the device active - // to not power off. - if ((DeviceExtension->ZeroPowerODDInfo != NULL) && - (DeviceExtension->ZeroPowerODDInfo->LoadingMechanism == LOADING_MECHANISM_CADDY) && - (DeviceExtension->ZeroPowerODDInfo->Load == 0)) // Slot - { - TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, - "GesnDataInterpret: MediaArrival event detected, device marked as active\n")); - - DeviceMarkActive(DeviceExtension, TRUE, FALSE); - } - } - else if (mediaInfo->MediaEvent == NOTIFICATION_MEDIA_EVENT_MEDIA_REMOVAL) - { - TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, - "GESN::MEDIA REMOVAL, Status %x\n", - mediaInfo->MediaStatus)); - - DeviceSetMediaChangeStateEx(DeviceExtension, - MediaNotPresent, - NULL); - - // If media is removed from slot loading type, start powering off the device - // if it is ZPODD capable. - if ((DeviceExtension->ZeroPowerODDInfo != NULL) && - (DeviceExtension->ZeroPowerODDInfo->LoadingMechanism == LOADING_MECHANISM_CADDY) && - (DeviceExtension->ZeroPowerODDInfo->Load == 0)) // Slot - { - TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, - "GesnDataInterpret: MediaRemoval event detected, device marked as idle\n")); - - DeviceMarkActive(DeviceExtension, FALSE, FALSE); - } - } - else if (mediaInfo->MediaEvent == NOTIFICATION_MEDIA_EVENT_EJECT_REQUEST) - { - TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, - "GESN::MEDIA EJECTION, Status %x\n", - mediaInfo->MediaStatus)); - - DeviceSendNotification(DeviceExtension, - &GUID_IO_MEDIA_EJECT_REQUEST, - 0, - NULL); - } - - break; - } - - case NOTIFICATION_DEVICE_BUSY_CLASS_EVENTS: // lowest priority events... - { - PNOTIFICATION_BUSY_STATUS busyInfo = - (PNOTIFICATION_BUSY_STATUS)(Header->ClassEventData); - DEVICE_EVENT_BECOMING_READY busyData = {0}; - - // else we want to report the approximated time till it's ready. - busyData.Version = 1; - busyData.Reason = busyInfo->DeviceBusyStatus; - busyData.Estimated100msToReady = (busyInfo->Time[0] << 8) | - (busyInfo->Time[1] & 0xff); - - TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, - "GESN::BUSY: Event: %x Status %x Time %x\n", - busyInfo->DeviceBusyEvent, busyInfo->DeviceBusyStatus, - busyData.Estimated100msToReady - )); - - // Ignore the notification if the time is small - if (busyData.Estimated100msToReady >= GESN_DEVICE_BUSY_LOWER_THRESHOLD_100_MS) - { - TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, - "GesnDataInterpret: media BECOMING_READY\n")); - - DeviceSendNotification(DeviceExtension, - &GUID_IO_DEVICE_BECOMING_READY, - sizeof(DEVICE_EVENT_BECOMING_READY), - &busyData); - } - - // If manual loading operation is observed for slot loading type, start powering off the device - // if it is ZPODD capable. - if ((DeviceExtension->ZeroPowerODDInfo != NULL) && - (DeviceExtension->ZeroPowerODDInfo->LoadingMechanism == LOADING_MECHANISM_TRAY) && - (DeviceExtension->ZeroPowerODDInfo->Load == 0) && // Drawer - (busyInfo->DeviceBusyEvent == NOTIFICATION_BUSY_EVENT_LO_CHANGE) && - (busyInfo->DeviceBusyStatus == NOTIFICATION_BUSY_STATUS_NO_EVENT)) - { - inHomePosition = DeviceZPODDIsInHomePosition(DeviceExtension); - - if (inHomePosition == FALSE) - { - TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, - "GesnDataInterpret: LoChange event detected, device marked as active\n")); - - DeviceMarkActive(DeviceExtension, TRUE, FALSE); - } - else - { - TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, - "GesnDataInterpret: LoChange event detected, device marked as idle\n")); - - DeviceMarkActive(DeviceExtension, FALSE, FALSE); - } - } - - break; - } - - default: - { - break; - } - - } // end switch on notification class - - return status; -} - - -VOID -DeviceInternalSetMediaChangeState( - _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, - _In_ MEDIA_CHANGE_DETECTION_STATE NewState, - _Inout_opt_ PMEDIA_CHANGE_DETECTION_STATE OldState - ) -/*++ - -Routine Description: - - This routine will (if appropriate) set the media change event for the - device. The event will be set if the media state is changed and - media change events are enabled. Otherwise the media state will be - tracked but the event will not be set. - - This routine will lock out the other media change routines if possible - but if not a media change notification may be lost after the enable has - been completed. - -Arguments: - - DeviceExtension - the device extension - - NewState - new state for setting - - OldState - optional storage for the old state - -Return Value: - - none - ---*/ -{ -#if DBG - LPCSTR states[] = {"Unknown", "Present", "Not Present", "Unavailable"}; -#endif - MEDIA_CHANGE_DETECTION_STATE oldMediaState; - PMEDIA_CHANGE_DETECTION_INFO info = DeviceExtension->MediaChangeDetectionInfo; - CLASS_MEDIA_CHANGE_CONTEXT mcnContext; - - if (!((NewState >= MediaUnknown) && (NewState <= MediaUnavailable))) - { - return; - } - - if (info == NULL) - { - return; - } - - oldMediaState = info->LastKnownMediaDetectionState; - if (OldState) - { - *OldState = oldMediaState; - } - - info->LastKnownMediaDetectionState = NewState; - - // Increment MediaChangeCount on transition to MediaPresent - if (NewState == MediaPresent && oldMediaState != NewState) - { - InterlockedIncrement((PLONG)&DeviceExtension->MediaChangeCount); - } - - if (info->MediaChangeDetectionDisableCount != 0) - { -#if DBG - TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, - "DeviceInternalSetMediaChangeState: MCN not enabled, state " - "changed from %s to %s\n", - states[oldMediaState], states[NewState])); -#endif - return; - } -#if DBG - TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, - "DeviceInternalSetMediaChangeState: State change from %s to %s\n", - states[oldMediaState], states[NewState])); -#endif - - if (info->LastReportedMediaDetectionState == info->LastKnownMediaDetectionState) - { - // Media is in the same state as we reported last time, no need to report again. - return; - } - - // make the data useful -- it used to always be zero. - mcnContext.MediaChangeCount = DeviceExtension->MediaChangeCount; - mcnContext.NewState = NewState; - - if (NewState == MediaPresent) - { - TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, - "DeviceInternalSetMediaChangeState: Reporting media ARRIVAL\n")); - - DeviceSendNotification(DeviceExtension, - &GUID_IO_MEDIA_ARRIVAL, - sizeof(CLASS_MEDIA_CHANGE_CONTEXT), - &mcnContext); - } - else if ((NewState == MediaNotPresent) || (NewState == MediaUnavailable)) - { - TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, - "DeviceInternalSetMediaChangeState: Reporting media REMOVAL\n")); - DeviceSendNotification(DeviceExtension, - &GUID_IO_MEDIA_REMOVAL, - sizeof(CLASS_MEDIA_CHANGE_CONTEXT), - &mcnContext); - } - else - { - // Don't notify of changed going to unknown. - return; - } - - info->LastReportedMediaDetectionState = info->LastKnownMediaDetectionState; - - return; -} // end DeviceInternalSetMediaChangeState() - - -VOID -DeviceSetMediaChangeStateEx( - _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, - _In_ MEDIA_CHANGE_DETECTION_STATE NewState, - _Inout_opt_ PMEDIA_CHANGE_DETECTION_STATE OldState - ) -/*++ - -Routine Description: - - This routine will (if appropriate) set the media change event for the - device. The event will be set if the media state is changed and - media change events are enabled. Otherwise the media state will be - tracked but the event will not be set. - -Arguments: - - DeviceExtension - the device extension - - NewState - new state for setting - - OldState - optional storage for the old state - -Return Value: - - none - ---*/ -{ - PMEDIA_CHANGE_DETECTION_INFO info = DeviceExtension->MediaChangeDetectionInfo; - LARGE_INTEGER zero; - NTSTATUS status; - - // timeout value must be 0, as this function can be called at DISPATCH_LEVEL. - zero.QuadPart = 0; - - if (info == NULL) - { - return; - } - - status = KeWaitForMutexObject(&info->MediaChangeMutex, - Executive, - KernelMode, - FALSE, - &zero); - - if (status == STATUS_TIMEOUT) - { - // Someone else is in the process of setting the media state. - return; - } - - // Change the media present state and signal an event, if applicable - DeviceInternalSetMediaChangeState(DeviceExtension, NewState, OldState); - - KeReleaseMutex(&info->MediaChangeMutex, FALSE); - - return; -} // end DeviceSetMediaChangeStateEx() - - -_IRQL_requires_max_(APC_LEVEL) -VOID -DeviceSendDelayedMediaChangeNotifications( - _In_ PCDROM_DEVICE_EXTENSION DeviceExtension - ) -/*++ - -Routine Description: - - This routine sends the so-called delayed media change notifications. - These notifications get accumulated while the MCN mechanism is disabled - and need to be sent to the application on MCN enabling, if MCN enabling - happens as a part of exclusive access unlock and the application has not - requested us explicitly to not send the delayed notifications. - -Arguments: - - DeviceExtension - the device extension - -Return Value: - - none - ---*/ -{ - PMEDIA_CHANGE_DETECTION_INFO info = DeviceExtension->MediaChangeDetectionInfo; - LARGE_INTEGER zero; - NTSTATUS status; - - PAGED_CODE(); - - zero.QuadPart = 0; - - if (info == NULL) - { - return; - } - - status = KeWaitForMutexObject(&info->MediaChangeMutex, - Executive, - KernelMode, - FALSE, - &zero); - - if (status == STATUS_TIMEOUT) - { - // Someone else is in the process of setting the media state. - // That's totally okay, we'll send delayed notifications later. - return; - } - - // If the last reported state and the last known state are different and - // MCN is enabled, generate a notification based on the last known state. - if ((info->LastKnownMediaDetectionState != info->LastReportedMediaDetectionState) && - (info->MediaChangeDetectionDisableCount == 0)) - { - DeviceInternalSetMediaChangeState(DeviceExtension, info->LastKnownMediaDetectionState, NULL); - } - - KeReleaseMutex(&info->MediaChangeMutex, FALSE); - - return; -} - - -_IRQL_requires_max_(APC_LEVEL) -NTSTATUS -RequestSetupMcnRequest( - _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, - _In_ BOOLEAN UseGesn -) -/*++ - -Routine Description: - - This routine sets up the fields of the request for MCN - -Arguments: - DeviceExtension - device context - - UseGesn - If TRUE, the device supports GESN and it's currently the mechanism for MCN - -Return Value: - NTSTATUS - ---*/ -{ - NTSTATUS status = STATUS_SUCCESS; - PSCSI_REQUEST_BLOCK srb; - PIRP irp; - PIO_STACK_LOCATION nextIrpStack; - PCDB cdb; - PVOID buffer; - WDF_REQUEST_REUSE_PARAMS params; - PMEDIA_CHANGE_DETECTION_INFO info = DeviceExtension->MediaChangeDetectionInfo; - - PAGED_CODE(); - - irp = WdfRequestWdmGetIrp(info->MediaChangeRequest); - NT_ASSERT(irp != NULL); - - // deassign the MdlAddress, this is the value we assign explicitly. - // this is to prevent WdfRequestReuse to release the Mdl unexpectly. - if (irp->MdlAddress) - { - irp->MdlAddress = NULL; - } - - if (NT_SUCCESS(status)) - { - // Setup the IRP to perform a test unit ready. - WDF_REQUEST_REUSE_PARAMS_INIT(¶ms, - WDF_REQUEST_REUSE_NO_FLAGS, - STATUS_NOT_SUPPORTED); - - status = WdfRequestReuse(info->MediaChangeRequest, ¶ms); - } - - if (NT_SUCCESS(status)) - { - // Format the request. - status = WdfIoTargetFormatRequestForInternalIoctlOthers(DeviceExtension->IoTarget, - info->MediaChangeRequest, - IOCTL_SCSI_EXECUTE_IN, - NULL, NULL, - NULL, NULL, - NULL, NULL); - - if (!NT_SUCCESS(status)) - { - TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, - "RequestSetupMcnRequest: WdfIoTargetFormatRequestForInternalIoctlOthers failed, %!STATUS!\n", - status)); - } - } - - if (NT_SUCCESS(status)) - { - RequestClearSendTime(info->MediaChangeRequest); - - nextIrpStack = IoGetNextIrpStackLocation(irp); - - nextIrpStack->Flags = SL_OVERRIDE_VERIFY_VOLUME; - nextIrpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; - nextIrpStack->Parameters.Scsi.Srb = &(info->MediaChangeSrb); - - // Prepare the SRB for execution. - srb = nextIrpStack->Parameters.Scsi.Srb; - buffer = info->SenseBuffer; - RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK)); - RtlZeroMemory(buffer, SENSE_BUFFER_SIZE); - - srb->QueueTag = SP_UNTAGGED; - srb->QueueAction = SRB_SIMPLE_TAG_REQUEST; - srb->Length = sizeof(SCSI_REQUEST_BLOCK); - srb->Function = SRB_FUNCTION_EXECUTE_SCSI; - srb->SenseInfoBuffer = buffer; - srb->SrbStatus = 0; - srb->ScsiStatus = 0; - srb->OriginalRequest = irp; - srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE; - - srb->SrbFlags = DeviceExtension->SrbFlags; - SET_FLAG(srb->SrbFlags, info->SrbFlags); - - if (!UseGesn) - { - srb->TimeOutValue = CDROM_TEST_UNIT_READY_TIMEOUT; - srb->CdbLength = 6; - srb->DataTransferLength = 0; - SET_FLAG(srb->SrbFlags, SRB_FLAGS_NO_DATA_TRANSFER); - nextIrpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_NONE; - srb->DataBuffer = NULL; - srb->DataTransferLength = 0; - irp->MdlAddress = NULL; - - cdb = (PCDB) &srb->Cdb[0]; - cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY; - } - else - { - NT_ASSERT(info->Gesn.Buffer); - - srb->TimeOutValue = GESN_TIMEOUT_VALUE; // much shorter timeout for GESN - - srb->CdbLength = 10; - SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_IN); - nextIrpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_IN; - srb->DataBuffer = info->Gesn.Buffer; - srb->DataTransferLength = info->Gesn.BufferSize; - irp->MdlAddress = info->Gesn.Mdl; - - cdb = (PCDB) &srb->Cdb[0]; - cdb->GET_EVENT_STATUS_NOTIFICATION.OperationCode = SCSIOP_GET_EVENT_STATUS; - cdb->GET_EVENT_STATUS_NOTIFICATION.Immediate = 1; - cdb->GET_EVENT_STATUS_NOTIFICATION.EventListLength[0] = (UCHAR)((info->Gesn.BufferSize) >> 8); - cdb->GET_EVENT_STATUS_NOTIFICATION.EventListLength[1] = (UCHAR)((info->Gesn.BufferSize) & 0xff); - cdb->GET_EVENT_STATUS_NOTIFICATION.NotificationClassRequest = info->Gesn.EventMask; - } - } - - return status; -} - - -VOID -DeviceDisableGesn( - _In_ WDFWORKITEM WorkItem - ) -/*++ - -Routine Description: - - Work item routine to set the hack flag in the registry to disable GESN - This routine is invoked when the device reports TOO many events that affects system - -Arguments: - WorkItem - the work item be perfromed. - -Return Value: - None - ---*/ -{ - WDFDEVICE device = WdfWorkItemGetParentObject(WorkItem); - PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(device); - - PAGED_CODE(); - - // - // Set the hack flag in the registry - // - DeviceSetParameter(deviceExtension, - CLASSP_REG_SUBKEY_NAME, - CLASSP_REG_MMC_DETECTION_VALUE_NAME, - CdromDetectionUnsupported); - - WdfObjectDelete(WorkItem); - - return; -} - -_IRQL_requires_max_(APC_LEVEL) -BOOLEAN -RequestPostWorkMcnRequest( - _In_ PCDROM_DEVICE_EXTENSION DeviceExtension - ) -/*++ - -Routine Description: - - This routine handles the completion of the test unit ready irps used to - determine if the media has changed. If the media has changed, this code - signals the named event to wake up other system services that react to - media change (aka AutoPlay). - -Arguments: - - DeviceExtension - the device context - -Return Value: - - BOOLEAN - TRUE (needs retry); FALSE (shoule not retry) - ---*/ -{ - NTSTATUS status = STATUS_SUCCESS; - PMEDIA_CHANGE_DETECTION_INFO info = DeviceExtension->MediaChangeDetectionInfo; - PIRP irp; - BOOLEAN retryImmediately = FALSE; - - PAGED_CODE(); - - NT_ASSERT(info->MediaChangeRequest != NULL); - irp = WdfRequestWdmGetIrp(info->MediaChangeRequest); - - NT_ASSERT(!TEST_FLAG(info->MediaChangeSrb.SrbStatus, SRB_STATUS_QUEUE_FROZEN)); - - // use InterpretSenseInfo routine to check for media state, and also - // to call ClassError() with correct parameters. - if (SRB_STATUS(info->MediaChangeSrb.SrbStatus) != SRB_STATUS_SUCCESS) - { - TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN, "MCN request - failed - srb status=%x, sense=%x/%x/%x.\n", - info->MediaChangeSrb.SrbStatus, - ((PSENSE_DATA)(info->MediaChangeSrb.SenseInfoBuffer))->SenseKey, - ((PSENSE_DATA)(info->MediaChangeSrb.SenseInfoBuffer))->AdditionalSenseCode, - ((PSENSE_DATA)(info->MediaChangeSrb.SenseInfoBuffer))->AdditionalSenseCodeQualifier)); - - if (SRB_STATUS(info->MediaChangeSrb.SrbStatus) != SRB_STATUS_NOT_POWERED) - { - // Release the queue if it is frozen. - if (info->MediaChangeSrb.SrbStatus & SRB_STATUS_QUEUE_FROZEN) - { - DeviceReleaseQueue(DeviceExtension->Device); - } - - RequestSenseInfoInterpret(DeviceExtension, - info->MediaChangeRequest, - &info->MediaChangeSrb, - 0, - &status, - NULL); - } - } - else - { - DeviceExtension->PrivateFdoData->LoggedTURFailureSinceLastIO = FALSE; - - if (!info->Gesn.Supported) - { - TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, - "MCN request - succeeded (GESN NOT supported, setting MediaPresent).\n")); - - // success != media for GESN case - DeviceSetMediaChangeStateEx(DeviceExtension, - MediaPresent, - NULL); - } - else - { - TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN, - "MCN request - succeeded (GESN supported).\n")); - } - } - - if (info->Gesn.Supported) - { - if (status == STATUS_DATA_OVERRUN) - { - TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "MCN Request - Data Overrun\n")); - status = STATUS_SUCCESS; - } - - if (!NT_SUCCESS(status)) - { - TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "MCN Request: GESN failed with status %x\n", status)); - } - else - { - // for GESN, need to interpret the results of the data. - // this may also require an immediate retry - if (irp->IoStatus.Information == 8 ) - { - GesnDataInterpret(DeviceExtension, - (PVOID)info->Gesn.Buffer, - &retryImmediately); - } - - } // end of NT_SUCCESS(status) - - } // end of Info->Gesn.Supported - - // free port-allocated sense buffer, if any. - if (PORT_ALLOCATED_SENSE(DeviceExtension, &info->MediaChangeSrb)) - { - FREE_PORT_ALLOCATED_SENSE_BUFFER(DeviceExtension, &info->MediaChangeSrb); - } - - // Remember the IRP and SRB for use the next time. - NT_ASSERT(IoGetNextIrpStackLocation(irp)); - IoGetNextIrpStackLocation(irp)->Parameters.Scsi.Srb = &info->MediaChangeSrb; - - // run a sanity check to make sure we're not recursing continuously - if (retryImmediately) - { - info->MediaChangeRetryCount++; - - if (info->MediaChangeRetryCount > MAXIMUM_IMMEDIATE_MCN_RETRIES) - { - // Disable GESN on this device. - // Create a work item to set the value in the registry - WDF_OBJECT_ATTRIBUTES attributes; - WDF_WORKITEM_CONFIG workitemConfig; - WDFWORKITEM workItem; - - WDF_OBJECT_ATTRIBUTES_INIT(&attributes); - attributes.ParentObject = DeviceExtension->Device; - - WDF_WORKITEM_CONFIG_INIT(&workitemConfig, DeviceDisableGesn); - workitemConfig.AutomaticSerialization = FALSE; - - status = WdfWorkItemCreate(&workitemConfig, - &attributes, - &workItem); - - TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "MCN Request: Disabling GESN for WDFDEVICE %p\n", DeviceExtension->Device)); - - if (NT_SUCCESS(status)) - { - WdfWorkItemEnqueue(workItem); - } - - info->Gesn.Supported = FALSE; - info->Gesn.EventMask = 0; - info->Gesn.BufferSize = 0; - info->MediaChangeRetryCount = 0; - retryImmediately = FALSE; - // should we log an error in event log? - } - } - else - { - info->MediaChangeRetryCount = 0; - } - - return retryImmediately; -} - - -_IRQL_requires_max_(APC_LEVEL) -BOOLEAN -RequestSendMcnRequest( - _In_ PCDROM_DEVICE_EXTENSION DeviceExtension - ) -/*++ - -Routine Description: - - This routine sends the formatted MCN request sychronizely to lower driver. - -Arguments: - - DeviceExtension - the device context - -Return Value: - BOOLEAN - TRUE (requst successfully sent); FALSE (request failed to send) - ---*/ -{ - BOOLEAN requestSent = FALSE; - PMEDIA_CHANGE_DETECTION_INFO info = DeviceExtension->MediaChangeDetectionInfo; - - PAGED_CODE(); - - RequestSend(DeviceExtension, - info->MediaChangeRequest, - DeviceExtension->IoTarget, - WDF_REQUEST_SEND_OPTION_SYNCHRONOUS, - &requestSent); - - return requestSent; -} // end RequestSendMcnRequest() - - - -_IRQL_requires_max_(PASSIVE_LEVEL) -NTSTATUS -DeviceInitializeMcn( - _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, - _In_ BOOLEAN AllowDriveToSleep - ) -/*++ - -Routine Description: - - This routine initialize the contents of MCN structure. - -Arguments: - - DeviceExtension - the device extension - - AllowDriveToSleep - for CDROM, this parameter should be always FALSE - -Return Value: - NTSTATUS - ---*/ -{ - NTSTATUS status = STATUS_SUCCESS; - PMEDIA_CHANGE_DETECTION_INFO mediaChangeInfo = NULL; - PIRP irp = NULL; - PVOID senseBuffer = NULL; - WDF_OBJECT_ATTRIBUTES attributes; - - PAGED_CODE(); - - if (DeviceExtension->MediaChangeDetectionInfo != NULL) - { - //Already initialized. - return STATUS_SUCCESS; - } - - DeviceExtension->KernelModeMcnContext.FileObject = (PVOID)-1; - DeviceExtension->KernelModeMcnContext.DeviceObject = (PVOID)-1; - DeviceExtension->KernelModeMcnContext.LockCount = 0; - DeviceExtension->KernelModeMcnContext.McnDisableCount = 0; - - mediaChangeInfo = ExAllocatePool2(POOL_FLAG_NON_PAGED, - sizeof(MEDIA_CHANGE_DETECTION_INFO), - CDROM_TAG_MEDIA_CHANGE_DETECTION); - - if (mediaChangeInfo == NULL) - { - status = STATUS_INSUFFICIENT_RESOURCES; - } - - if (NT_SUCCESS(status)) - { - if ((DeviceExtension->PowerDescriptor != NULL) && - (DeviceExtension->PowerDescriptor->AsynchronousNotificationSupported != FALSE) && - (!TEST_FLAG(DeviceExtension->PrivateFdoData->HackFlags, FDO_HACK_NO_ASYNCHRONOUS_NOTIFICATION))) - { - mediaChangeInfo->AsynchronousNotificationSupported = TRUE; - } - } - - // Allocate an IRP to carry the IOCTL_MCN_SYNC_FAKE_IOCTL. - if (NT_SUCCESS(status)) - { - irp = IoAllocateIrp(DeviceExtension->DeviceObject->StackSize, FALSE); - - if (irp == NULL) - { - status = STATUS_INSUFFICIENT_RESOURCES; - } - } - - if (NT_SUCCESS(status)) - { - WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, - CDROM_REQUEST_CONTEXT); - attributes.ParentObject = DeviceExtension->Device; - status = WdfRequestCreate(&attributes, - DeviceExtension->IoTarget, - &mediaChangeInfo->MediaChangeRequest); - } - - if (NT_SUCCESS(status)) - { - // Preformat the media change request. With this being done, we never need to worry about - // WdfIoTargetFormatRequestForInternalIoctlOthers ever failing later. - status = WdfIoTargetFormatRequestForInternalIoctlOthers(DeviceExtension->IoTarget, - mediaChangeInfo->MediaChangeRequest, - IOCTL_SCSI_EXECUTE_IN, - NULL, NULL, - NULL, NULL, - NULL, NULL); - } - - if (NT_SUCCESS(status)) - { - senseBuffer = ExAllocatePool2(POOL_FLAG_NON_PAGED | POOL_FLAG_CACHE_ALIGNED, - SENSE_BUFFER_SIZE, - CDROM_TAG_MEDIA_CHANGE_DETECTION); - if (senseBuffer == NULL) - { - status = STATUS_INSUFFICIENT_RESOURCES; - } - } - - if (NT_SUCCESS(status)) - { - mediaChangeInfo->MediaChangeSyncIrp = irp; - mediaChangeInfo->SenseBuffer = senseBuffer; - - // Set default values for the media change notification - // configuration. - mediaChangeInfo->MediaChangeDetectionDisableCount = 0; - - // Assume that there is initially no media in the device - // only notify upper layers if there is something there - mediaChangeInfo->LastKnownMediaDetectionState = MediaUnknown; - mediaChangeInfo->LastReportedMediaDetectionState = MediaUnknown; - - // setup all extra flags we'll be setting for this irp - mediaChangeInfo->SrbFlags = 0; - - SET_FLAG(mediaChangeInfo->SrbFlags, SRB_CLASS_FLAGS_LOW_PRIORITY); - SET_FLAG(mediaChangeInfo->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE); - SET_FLAG(mediaChangeInfo->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER); - - if (AllowDriveToSleep) //FALSE for CD/DVD devices - { - SET_FLAG(mediaChangeInfo->SrbFlags, SRB_FLAGS_NO_KEEP_AWAKE); - } - - KeInitializeMutex(&mediaChangeInfo->MediaChangeMutex, 0x100); - - // It is ok to support media change events on this device. - DeviceExtension->MediaChangeDetectionInfo = mediaChangeInfo; - - // check the device supports GESN or not, initialize GESN structure if it supports. - { - // This is only valid for type5 devices. - NTSTATUS tempStatus = STATUS_SUCCESS; - - TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, - "DeviceInitializeMcn: Testing for GESN\n")); - tempStatus = DeviceInitializeGesn(DeviceExtension); - - if (NT_SUCCESS(tempStatus)) - { - TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, - "DeviceInitializeMcn: GESN available for %p\n", - DeviceExtension->DeviceObject)); - NT_ASSERT(mediaChangeInfo->Gesn.Supported ); - NT_ASSERT(mediaChangeInfo->Gesn.Buffer != NULL); - NT_ASSERT(mediaChangeInfo->Gesn.BufferSize != 0); - NT_ASSERT(mediaChangeInfo->Gesn.EventMask != 0); - } - else - { - TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, - "DeviceInitializeMcn: GESN *NOT* available for %p\n", - DeviceExtension->DeviceObject)); - NT_ASSERT(!mediaChangeInfo->Gesn.Supported); - NT_ASSERT(mediaChangeInfo->Gesn.Buffer == NULL); - NT_ASSERT(mediaChangeInfo->Gesn.BufferSize == 0); - NT_ASSERT(mediaChangeInfo->Gesn.EventMask == 0); - mediaChangeInfo->Gesn.Supported = FALSE; // just in case.... - } - } - } - - if (NT_SUCCESS(status)) - { - // Register for display state change on AOAC capable systems so we can put the - // device to low power state when not required. - if (mediaChangeInfo->DisplayStateCallbackHandle == NULL) - { - POWER_PLATFORM_INFORMATION PlatformInfo = {0}; - - status = ZwPowerInformation(PlatformInformation, - NULL, - 0, - &PlatformInfo, - sizeof(PlatformInfo)); - - if (NT_SUCCESS(status) && PlatformInfo.AoAc) - { - PoRegisterPowerSettingCallback(DeviceExtension->DeviceObject, - &GUID_CONSOLE_DISPLAY_STATE, - &DevicePowerSettingCallback, - DeviceExtension, - &mediaChangeInfo->DisplayStateCallbackHandle); - } - - // Ignore any failures above. - status = STATUS_SUCCESS; - } - } - - if (!NT_SUCCESS(status)) - { - if (irp != NULL) - { - IoFreeIrp(irp); - } - FREE_POOL(senseBuffer); - FREE_POOL(mediaChangeInfo); - } - - return status; - -} // end DeviceInitializeMcn() - - -_IRQL_requires_max_(PASSIVE_LEVEL) -NTSTATUS -DeviceInitializeGesn( - _In_ PCDROM_DEVICE_EXTENSION DeviceExtension - ) -/*++ - -Routine Description: - - This routine initialize the contents of GESN structure. - -Arguments: - - DeviceExtension - the device extension - -Return Value: - NTSTATUS - ---*/ -{ - NTSTATUS status = STATUS_SUCCESS; - PNOTIFICATION_EVENT_STATUS_HEADER header = NULL; - CDROM_DETECTION_STATE detectionState = CdromDetectionUnknown; - PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor = DeviceExtension->DeviceDescriptor; - BOOLEAN retryImmediately = TRUE; - ULONG i = 0; - ULONG atapiResets = 0; - PMEDIA_CHANGE_DETECTION_INFO info = DeviceExtension->MediaChangeDetectionInfo; - - PAGED_CODE(); - - NT_ASSERT(info != NULL); - - // read if we already know the abilities of the device - DeviceGetParameter(DeviceExtension, - CLASSP_REG_SUBKEY_NAME, - CLASSP_REG_MMC_DETECTION_VALUE_NAME, - (PULONG)&detectionState); - - if (detectionState == CdromDetectionUnsupported) - { - status = STATUS_NOT_SUPPORTED; - } - - // check if the device has a hack flag saying never to try this. - if (NT_SUCCESS(status) && - (TEST_FLAG(DeviceExtension->PrivateFdoData->HackFlags, FDO_HACK_GESN_IS_BAD)) ) - { - DeviceSetParameter(DeviceExtension, - CLASSP_REG_SUBKEY_NAME, - CLASSP_REG_MMC_DETECTION_VALUE_NAME, - CdromDetectionUnsupported); - status = STATUS_NOT_SUPPORTED; - } - - // else go through the process since we allocate buffers and - // get all sorts of device settings. - if (NT_SUCCESS(status)) - { - if (info->Gesn.Buffer == NULL) - { - info->Gesn.Buffer = ExAllocatePool2(POOL_FLAG_NON_PAGED | POOL_FLAG_CACHE_ALIGNED, - GESN_BUFFER_SIZE, - CDROM_TAG_GESN); - } - - if (info->Gesn.Buffer == NULL) - { - status = STATUS_INSUFFICIENT_RESOURCES; - } - } - - if (NT_SUCCESS(status)) - { - if (info->Gesn.Mdl != NULL) - { - IoFreeMdl(info->Gesn.Mdl); - } - - info->Gesn.Mdl = IoAllocateMdl(info->Gesn.Buffer, - GESN_BUFFER_SIZE, - FALSE, - FALSE, - NULL); - if (info->Gesn.Mdl == NULL) - { - status = STATUS_INSUFFICIENT_RESOURCES; - } - } - - if (NT_SUCCESS(status)) - { - MmBuildMdlForNonPagedPool(info->Gesn.Mdl); - info->Gesn.BufferSize = GESN_BUFFER_SIZE; - info->Gesn.EventMask = 0; - - // all items are prepared to use GESN (except the event mask, so don't - // optimize this part out!). - // - // now see if it really works. we have to loop through this because - // many SAMSUNG (and one COMPAQ) drives timeout when requesting - // NOT_READY events, even when the IMMEDIATE bit is set. :( - // - // using a drive list is cumbersome, so this might fix the problem. - for (i = 0; (i < 16) && retryImmediately; i++) - { - status = RequestSetupMcnRequest(DeviceExtension, TRUE); - - if (!NT_SUCCESS(status)) - { - TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN, - "Setup Mcn request failed %x for WDFDEVICE %p\n", - status, DeviceExtension->Device)); - break; - } - - NT_ASSERT(TEST_FLAG(info->MediaChangeSrb.SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE)); - - status = DeviceSendRequestSynchronously(DeviceExtension->Device, info->MediaChangeRequest, TRUE); - - if (SRB_STATUS(info->MediaChangeSrb.SrbStatus) != SRB_STATUS_SUCCESS) - { - // Release the queue if it is frozen. - if (info->MediaChangeSrb.SrbStatus & SRB_STATUS_QUEUE_FROZEN) - { - DeviceReleaseQueue(DeviceExtension->Device); - } - - RequestSenseInfoInterpret(DeviceExtension, - info->MediaChangeRequest, - &(info->MediaChangeSrb), - 0, - &status, - NULL); - } - - if ((deviceDescriptor->BusType == BusTypeAtapi) && - (info->MediaChangeSrb.SrbStatus == SRB_STATUS_BUS_RESET)) - { - // - // ATAPI unfortunately returns SRB_STATUS_BUS_RESET instead - // of SRB_STATUS_TIMEOUT, so we cannot differentiate between - // the two. if we get this status four time consecutively, - // stop trying this command. it is too late to change ATAPI - // at this point, so special-case this here. (07/10/2001) - // NOTE: any value more than 4 may cause the device to be - // marked missing. - // - atapiResets++; - if (atapiResets >= 4) - { - status = STATUS_IO_DEVICE_ERROR; - break; - } - } - - if (status == STATUS_DATA_OVERRUN) - { - status = STATUS_SUCCESS; - } - - if ((status == STATUS_INVALID_DEVICE_REQUEST) || - (status == STATUS_TIMEOUT) || - (status == STATUS_IO_DEVICE_ERROR) || - (status == STATUS_IO_TIMEOUT)) - { - // with these error codes, we don't ever want to try this command - // again on this device, since it reacts poorly. - DeviceSetParameter( DeviceExtension, - CLASSP_REG_SUBKEY_NAME, - CLASSP_REG_MMC_DETECTION_VALUE_NAME, - CdromDetectionUnsupported); - TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN, - "GESN test failed %x for WDFDEVICE %p\n", - status, DeviceExtension->Device)); - break; - } - - if (!NT_SUCCESS(status)) - { - // this may be other errors that should not disable GESN - // for all future start_device calls. - TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN, - "GESN test failed %x for WDFDEVICE %p\n", - status, DeviceExtension->Device)); - break; - } - else if (i == 0) - { - // the first time, the request was just retrieving a mask of - // available bits. use this to mask future requests. - header = (PNOTIFICATION_EVENT_STATUS_HEADER)(info->Gesn.Buffer); - - TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, - "WDFDEVICE %p supports event mask %x\n", - DeviceExtension->Device, header->SupportedEventClasses)); - - if (TEST_FLAG(header->SupportedEventClasses, - NOTIFICATION_MEDIA_STATUS_CLASS_MASK)) - { - TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, - "GESN supports MCN\n")); - } - if (TEST_FLAG(header->SupportedEventClasses, - NOTIFICATION_DEVICE_BUSY_CLASS_MASK)) - { - TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, - "GESN supports DeviceBusy\n")); - } - if (TEST_FLAG(header->SupportedEventClasses, - NOTIFICATION_OPERATIONAL_CHANGE_CLASS_MASK)) - { - if (TEST_FLAG(DeviceExtension->PrivateFdoData->HackFlags, - FDO_HACK_GESN_IGNORE_OPCHANGE)) - { - TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, - "GESN supports OpChange, but must ignore these events for compatibility\n")); - CLEAR_FLAG(header->SupportedEventClasses, - NOTIFICATION_OPERATIONAL_CHANGE_CLASS_MASK); - } - else - { - TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, - "GESN supports OpChange\n")); - } - } - info->Gesn.EventMask = header->SupportedEventClasses; - - // - // realistically, we are only considering the following events: - // EXTERNAL REQUEST - this is being tested for play/stop/etc. - // MEDIA STATUS - autorun and ejection requests. - // DEVICE BUSY - to allow us to predict when media will be ready. - // therefore, we should not bother querying for the other, - // unknown events. clear all but the above flags. - // - info->Gesn.EventMask &= NOTIFICATION_OPERATIONAL_CHANGE_CLASS_MASK | - NOTIFICATION_EXTERNAL_REQUEST_CLASS_MASK | - NOTIFICATION_MEDIA_STATUS_CLASS_MASK | - NOTIFICATION_DEVICE_BUSY_CLASS_MASK ; - - - // - // HACKHACK - REF #0001 - // Some devices will *never* report an event if we've also requested - // that it report lower-priority events. this is due to a - // misunderstanding in the specification wherein a "No Change" is - // interpreted to be a real event. what should occur is that the - // device should ignore "No Change" events when multiple event types - // are requested unless there are no other events waiting. this - // greatly reduces the number of requests that the host must send - // to determine if an event has occurred. Since we must work on all - // drives, default to enabling the hack until we find evidence of - // proper firmware. - // - if (info->Gesn.EventMask == 0) - { - TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, - "GESN supported, but not mask we care about (%x) for FDO %p\n", - header->SupportedEventClasses, - DeviceExtension->DeviceObject)); - // NOTE: the status is still status_sucess. - break; - } - else if (CountOfSetBitsUChar(info->Gesn.EventMask) == 1) - { - TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, - "GESN hack not required for FDO %p\n", - DeviceExtension->DeviceObject)); - } - else - { - TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, - "GESN hack enabled for FDO %p\n", - DeviceExtension->DeviceObject)); - info->Gesn.HackEventMask = 1; - } - } - else - { - // i > 0; not the first time looping through, so interpret the results. - status = GesnDataInterpret(DeviceExtension, - (PVOID)info->Gesn.Buffer, - &retryImmediately); - - if (!NT_SUCCESS(status)) - { - // This drive does not support GESN correctly - DeviceSetParameter( DeviceExtension, - CLASSP_REG_SUBKEY_NAME, - CLASSP_REG_MMC_DETECTION_VALUE_NAME, - CdromDetectionUnsupported); - break; - } - } - } // end 'for' loop of GESN requests.... - } - - if (NT_SUCCESS(status)) - { - // - // we can only use this if it can be relied upon for media changes, - // since we are (by definition) no longer going to be polling via - // a TEST_UNIT_READY irp, and drives will not report UNIT ATTENTION - // for this command (although a filter driver, such as one for burning - // cd's, might still fake those errors). - // - // since we also rely upon NOT_READY events to change the cursor - // into a "wait" cursor; GESN is still more reliable than other - // methods, and includes eject button requests, so we'll use it - // without DEVICE_BUSY in Windows Vista. - // - - if (TEST_FLAG(info->Gesn.EventMask, NOTIFICATION_MEDIA_STATUS_CLASS_MASK)) - { - TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, - "Enabling GESN support for WDFDEVICE %p\n", - DeviceExtension->Device)); - info->Gesn.Supported = TRUE; - - DeviceSetParameter( DeviceExtension, - CLASSP_REG_SUBKEY_NAME, - CLASSP_REG_MMC_DETECTION_VALUE_NAME, - CdromDetectionSupported); - } - else - { - TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, - "GESN available but not enabled for WDFDEVICE %p\n", - DeviceExtension->Device)); - status = STATUS_NOT_SUPPORTED; - } - } - - if (!NT_SUCCESS(status)) - { - TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN, - "GESN support detection failed for WDFDEVICE %p with status %08x\n", - DeviceExtension->Device, status)); - - if (info->Gesn.Mdl) - { - PIRP irp = WdfRequestWdmGetIrp(info->MediaChangeRequest); - - IoFreeMdl(info->Gesn.Mdl); - info->Gesn.Mdl = NULL; - irp->MdlAddress = NULL; - } - - FREE_POOL(info->Gesn.Buffer); - info->Gesn.Supported = FALSE; - info->Gesn.EventMask = 0; - info->Gesn.BufferSize = 0; - } - - return status; -} - - -_IRQL_requires_max_(PASSIVE_LEVEL) -NTSTATUS -DeviceInitializeMediaChangeDetection( - _In_ PCDROM_DEVICE_EXTENSION DeviceExtension - ) -/*++ - -Routine Description: - - This routine checks to see if it is safe to initialize MCN (the back end - to autorun) for a given device. It will then check the device-type wide - key "Autorun" in the service key (for legacy reasons), and then look in - the device-specific key to potentially override that setting. - - If MCN is to be enabled, all neccessary structures and memory are - allocated and initialized. - - This routine MUST be called only from the DeviceInit...() . - -Arguments: - - DeviceExtension - the device to initialize MCN for, if appropriate - -Return Value: - - NTSTATUS - ---*/ -{ - NTSTATUS status = STATUS_SUCCESS; - - BOOLEAN disabled = FALSE; - BOOLEAN instanceOverride; - - PAGED_CODE(); - - // NOTE: This assumes that DeviceInitializeMediaChangeDetection is always - // called in the context of the DeviceInitDevicePhase2. If called - // after then this check will have already been made and the - // once a second timer will not have been enabled. - - disabled = DeviceIsMediaChangeDisabledDueToHardwareLimitation(DeviceExtension); - - if (disabled) - { - TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, - "DeviceInitializeMediaChangeDetection: Disabled due to hardware" - "limitations for this device\n")); - } - else - { - // autorun should now be enabled by default for all media types. - disabled = DeviceIsMediaChangeDisabledForClass(DeviceExtension); - - TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, - "DeviceInitializeMediaChangeDetection: MCN is %s\n", - (disabled ? "disabled" : "enabled"))); - - status = DeviceMediaChangeDeviceInstanceOverride(DeviceExtension, - &instanceOverride); // default value - - if (!NT_SUCCESS(status)) - { - TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, - "DeviceInitializeMediaChangeDetection: Instance using default\n")); - } - else - { - TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, - "DeviceInitializeMediaChangeDetection: Instance override: %s MCN\n", - (instanceOverride ? "Enabling" : "Disabling"))); - disabled = !instanceOverride; - } - - TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, - "DeviceInitializeMediaChangeDetection: Instance MCN is %s\n", - (disabled ? "disabled" : "enabled"))); - } - - if (!disabled) - { - // if the drive is not a CDROM, allow the drive to sleep - status = DeviceInitializeMcn(DeviceExtension, FALSE); - } - - return status; -} // end DeviceInitializeMediaChangeDetection() - - -_IRQL_requires_max_(PASSIVE_LEVEL) -NTSTATUS -DeviceMediaChangeDeviceInstanceOverride( - _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, - _Out_ PBOOLEAN Enabled - ) -/*++ - -Routine Description: - - The user can override the global setting to enable or disable Autorun on a - specific cdrom device via the control panel. This routine checks and/or - sets this value. - -Arguments: - - DeviceExtension - the device to set/get the value for - - Enabled - TRUE (Autorun is enabled) - FALSE (Autorun is disabled) -Return Value: - NTSTATUS - ---*/ -{ - NTSTATUS status = STATUS_UNSUCCESSFUL; - WDFKEY deviceKey = NULL; - WDFKEY subKey = NULL; - - UNICODE_STRING subkeyName; - UNICODE_STRING enableMcnValueName; - UNICODE_STRING disableMcnValueName; - ULONG alwaysEnable = 0; - ULONG alwaysDisable = 0; - - PAGED_CODE(); - - status = WdfDeviceOpenRegistryKey(DeviceExtension->Device, - PLUGPLAY_REGKEY_DEVICE, - KEY_ALL_ACCESS, - WDF_NO_OBJECT_ATTRIBUTES, - &deviceKey); - if (!NT_SUCCESS(status)) - { - // this can occur when a new device is added to the system - // this is due to cdrom.sys being an 'essential' driver - TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, - "DeviceMediaChangeDeviceInstanceOverride: " - "Could not open device registry key [%lx]\n", status)); - } - else - { - RtlInitUnicodeString(&subkeyName, MCN_REG_SUBKEY_NAME); - - status = WdfRegistryOpenKey(deviceKey, - &subkeyName, - KEY_READ, - WDF_NO_OBJECT_ATTRIBUTES, - &subKey); - } - - if (!NT_SUCCESS(status)) - { - TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, - "DeviceMediaChangeDeviceInstanceOverride: " - "subkey could not be created. %lx\n", status)); - } - else - { - // Default to not changing autorun behavior, based upon setting - // registryValue to zero. - RtlInitUnicodeString(&enableMcnValueName, MCN_REG_AUTORUN_ENABLE_INSTANCE_NAME); - RtlInitUnicodeString(&disableMcnValueName, MCN_REG_AUTORUN_DISABLE_INSTANCE_NAME); - - // Ignore failures on reading of subkeys - (VOID) WdfRegistryQueryULong(subKey, - &enableMcnValueName, - &alwaysEnable); - (VOID) WdfRegistryQueryULong(subKey, - &disableMcnValueName, - &alwaysDisable); - } - - // set return value and cleanup - - if (subKey != NULL) - { - WdfRegistryClose(subKey); - } - - if (deviceKey != NULL) - { - WdfRegistryClose(deviceKey); - } - - if (alwaysEnable && alwaysDisable) - { - TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, - "DeviceMediaChangeDeviceInstanceOverride: %s selected\n", - "Both Enable and Disable set -- DISABLE")); - NT_ASSERT(NT_SUCCESS(status)); - status = STATUS_SUCCESS; - *Enabled = FALSE; - } - else if (alwaysDisable) - { - TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, - "DeviceMediaChangeDeviceInstanceOverride: %s selected\n", - "DISABLE")); - NT_ASSERT(NT_SUCCESS(status)); - status = STATUS_SUCCESS; - *Enabled = FALSE; - } - else if (alwaysEnable) - { - TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, - "DeviceMediaChangeDeviceInstanceOverride: %s selected\n", - "ENABLE")); - NT_ASSERT(NT_SUCCESS(status)); - status = STATUS_SUCCESS; - *Enabled = TRUE; - } - else - { - TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, - "DeviceMediaChangeDeviceInstanceOverride: %s selected\n", - "DEFAULT")); - status = STATUS_UNSUCCESSFUL; - } - - return status; -} // end DeviceMediaChangeDeviceInstanceOverride() - - -NTSTATUS -DeviceMediaChangeRegistryCallBack( - _In_z_ PWSTR ValueName, - _In_ ULONG ValueType, - _In_reads_bytes_opt_(ValueLength) PVOID ValueData, - _In_ ULONG ValueLength, - _In_opt_ PVOID Context, - _In_opt_ PVOID EntryContext - ) -/*++ - -Routine Description: - - This callback for a registry SZ or MULTI_SZ is called once for each - SZ in the value. It will attempt to match the data with the - UNICODE_STRING passed in as Context, and modify EntryContext if a - match is found. Written for ClasspCheckRegistryForMediaChangeCompletion - -Arguments: - - ValueName - name of the key that was opened - ValueType - type of data stored in the value (REG_SZ for this routine) - ValueData - data in the registry, in this case a wide string - ValueLength - length of the data including the terminating null - Context - unicode string to compare against ValueData - EntryContext - should be initialized to 0, will be set to 1 if match found - -Return Value: - - STATUS_SUCCESS - EntryContext will be 1 if found - ---*/ -{ - PULONG valueFound; - PUNICODE_STRING deviceString; - PWSTR keyValue; - - PAGED_CODE(); - - UNREFERENCED_PARAMETER(ValueName); - - if ((Context == NULL) || (EntryContext == NULL)) - { - TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN, - "DeviceMediaChangeRegistryCallBack: NULL context should never be passed to registry call-back!\n")); - - return STATUS_SUCCESS; - } - - // if we have already set the value to true, exit - valueFound = EntryContext; - if ((*valueFound) != 0) - { - TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, - "DeviceMediaChangeRegistryCallBack: already set to true\n")); - return STATUS_SUCCESS; - } - - if (ValueLength == sizeof(WCHAR)) - { - TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN, - "DeviceMediaChangeRegistryCallBack: NULL string should never be passed to registry call-back!\n")); - return STATUS_SUCCESS; - } - - // if the data is not a terminated string, exit - if (ValueType != REG_SZ) - { - return STATUS_SUCCESS; - } - - deviceString = Context; - keyValue = ValueData; - ValueLength -= sizeof(WCHAR); // ignore the null character - - // do not compare more memory than is in deviceString - if (ValueLength > deviceString->Length) - { - ValueLength = deviceString->Length; - } - - if (keyValue == NULL) - { - return STATUS_SUCCESS; - } - - // if the strings match, disable autorun - if (RtlCompareMemory(deviceString->Buffer, keyValue, ValueLength) == ValueLength) - { - TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN, "DeviceMediaChangeRegistryCallBack: Match found\n")); - TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN, "DeviceMediaChangeRegistryCallBack: DeviceString at %p\n", - deviceString->Buffer)); - TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN, - "DeviceMediaChangeRegistryCallBack: KeyValue at %p\n", - keyValue)); - (*valueFound) = TRUE; - } - - return STATUS_SUCCESS; -} // end DeviceMediaChangeRegistryCallBack() - - -_IRQL_requires_max_(PASSIVE_LEVEL) -BOOLEAN -DeviceIsMediaChangeDisabledDueToHardwareLimitation( - _In_ PCDROM_DEVICE_EXTENSION DeviceExtension - ) -/*++ - -Routine Description: - - The key AutoRunAlwaysDisable contains a MULTI_SZ of hardware IDs for - which to never enable MediaChangeNotification. - - The user can override the global setting to enable or disable Autorun on a - specific cdrom device via the control panel. - - NOTE: It's intended that not use WdfRegistryQueryMultiString in this funciton, - as it's much more complicated.(needs WDFCOLLECTION, WDFSTRING other than - UNICODE_STRING) - -Arguments: - - FdoExtension - - RegistryPath - pointer to the unicode string inside - ...\CurrentControlSet\Services\Cdrom - -Return Value: - - TRUE - no autorun. - FALSE - Autorun may be enabled - ---*/ -{ - NTSTATUS status; - - PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor = DeviceExtension->DeviceDescriptor; - WDFKEY wdfKey; - HANDLE serviceKey = NULL; - RTL_QUERY_REGISTRY_TABLE parameters[2] = {0}; - - UNICODE_STRING deviceUnicodeString = {0}; - ANSI_STRING deviceString = {0}; - ULONG mediaChangeNotificationDisabled = 0; - - PAGED_CODE(); - - // open the service key. - status = WdfDriverOpenParametersRegistryKey(WdfGetDriver(), - KEY_ALL_ACCESS, - WDF_NO_OBJECT_ATTRIBUTES, - &wdfKey); - - if(!NT_SUCCESS(status)) - { - NT_ASSERT(FALSE); - - // always take the safe path. if we can't open the service key, disable autorun - return TRUE; - } - - if(NT_SUCCESS(status)) - { - // Determine if drive is in a list of those requiring - // autorun to be disabled. this is stored in a REG_MULTI_SZ - // named AutoRunAlwaysDisable. this is required as some autochangers - // must load the disc to reply to ChkVerify request, causing them - // to cycle discs continuously. - - PWSTR nullMultiSz; - PUCHAR vendorId = NULL; - PUCHAR productId = NULL; - PUCHAR revisionId = NULL; - size_t length; - size_t offset; - - deviceString.Buffer = NULL; - deviceUnicodeString.Buffer = NULL; - - serviceKey = WdfRegistryWdmGetHandle(wdfKey); - - // there may be nothing to check against - if ((deviceDescriptor->VendorIdOffset == 0) && - (deviceDescriptor->ProductIdOffset == 0)) - { - // no valid data in device extension. - status = STATUS_INTERNAL_ERROR; - } - - // build deviceString using VendorId, Model and Revision. - // this string will be used to checked if it's one of devices in registry disable list. - if (NT_SUCCESS(status)) - { - length = 0; - - if (deviceDescriptor->VendorIdOffset == 0) - { - vendorId = NULL; - } - else - { - vendorId = (PUCHAR) deviceDescriptor + deviceDescriptor->VendorIdOffset; - length = strlen((LPCSTR)vendorId); - } - - if ( deviceDescriptor->ProductIdOffset == 0 ) - { - productId = NULL; - } - else - { - productId = (PUCHAR) deviceDescriptor + deviceDescriptor->ProductIdOffset; - length += strlen((LPCSTR)productId); - } - - if ( deviceDescriptor->ProductRevisionOffset == 0 ) - { - revisionId = NULL; - } - else - { - revisionId = (PUCHAR) deviceDescriptor + deviceDescriptor->ProductRevisionOffset; - length += strlen((LPCSTR)revisionId); - } - - // allocate a buffer for the string - deviceString.Length = (USHORT)( length ); - deviceString.MaximumLength = deviceString.Length + 1; - deviceString.Buffer = (PCHAR)ExAllocatePool2(POOL_FLAG_NON_PAGED, - deviceString.MaximumLength, - CDROM_TAG_AUTORUN_DISABLE - ); - if (deviceString.Buffer == NULL) - { - TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, - "DeviceIsMediaChangeDisabledDueToHardwareLimitation: Unable to alloc string buffer\n" )); - status = STATUS_INTERNAL_ERROR; - } - } - - if (NT_SUCCESS(status)) - { - // copy strings to the buffer - offset = 0; - - if (vendorId != NULL) - { - RtlCopyMemory(deviceString.Buffer + offset, - vendorId, - strlen((LPCSTR)vendorId)); - offset += strlen((LPCSTR)vendorId); - } - - if ( productId != NULL ) - { - RtlCopyMemory(deviceString.Buffer + offset, - productId, - strlen((LPCSTR)productId)); - offset += strlen((LPCSTR)productId); - } - if ( revisionId != NULL ) - { - RtlCopyMemory(deviceString.Buffer + offset, - revisionId, - strlen((LPCSTR)revisionId)); - offset += strlen((LPCSTR)revisionId); - } - - NT_ASSERT(offset == deviceString.Length); - - #pragma warning(suppress:6386) // Not an issue as deviceString.Buffer is of size deviceString.MaximumLength, which is equal to (deviceString.Length + 1) - deviceString.Buffer[deviceString.Length] = '\0'; // Null-terminated - - // convert to unicode as registry deals with unicode strings - status = RtlAnsiStringToUnicodeString( &deviceUnicodeString, - &deviceString, - TRUE - ); - if (!NT_SUCCESS(status)) - { - TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, - "DeviceIsMediaChangeDisabledDueToHardwareLimitation: cannot convert " - "to unicode %lx\n", status)); - } - } - - if (NT_SUCCESS(status)) - { - // query the value, setting valueFound to true if found - nullMultiSz = L"\0"; - parameters[0].QueryRoutine = DeviceMediaChangeRegistryCallBack; - parameters[0].Flags = RTL_QUERY_REGISTRY_REQUIRED; - parameters[0].Name = L"AutoRunAlwaysDisable"; - parameters[0].EntryContext = &mediaChangeNotificationDisabled; - parameters[0].DefaultType = REG_MULTI_SZ; - parameters[0].DefaultData = nullMultiSz; - parameters[0].DefaultLength = 0; - - status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE, - serviceKey, - parameters, - &deviceUnicodeString, - NULL); - UNREFERENCED_PARAMETER(status); //defensive coding, avoid PREFAST warning. - } - } - - // Cleanup - { - - FREE_POOL( deviceString.Buffer ); - if (deviceUnicodeString.Buffer != NULL) - { - RtlFreeUnicodeString( &deviceUnicodeString ); - } - - // handle serviceKey will be closed by framework while it closes registry key. - WdfRegistryClose(wdfKey); - } - - if (mediaChangeNotificationDisabled > 0) - { - TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, - "DeviceIsMediaChangeDisabledDueToHardwareLimitation: Device is on MCN disable list\n")); - } - - return (mediaChangeNotificationDisabled > 0); - -} // end DeviceIsMediaChangeDisabledDueToHardwareLimitation() - - -_IRQL_requires_max_(PASSIVE_LEVEL) -BOOLEAN -DeviceIsMediaChangeDisabledForClass( - _In_ PCDROM_DEVICE_EXTENSION DeviceExtension - ) -/*++ - -Routine Description: - - The user must specify that AutoPlay is to run on the platform - by setting the registry value HKEY_LOCAL_MACHINE\System\CurrentControlSet\ - Services\<SERVICE>\Autorun:REG_DWORD:1. - - The user can override the global setting to enable or disable Autorun on a - specific cdrom device via the control panel. - -Arguments: - - DeviceExtension - device extension - -Return Value: - - TRUE - Autorun is disabled for this class - FALSE - Autorun is enabled for this class - ---*/ -{ - NTSTATUS status; - WDFKEY serviceKey = NULL; - WDFKEY parametersKey = NULL; - - UNICODE_STRING parameterKeyName; - UNICODE_STRING valueName; - - // Default to ENABLING MediaChangeNotification (!) - ULONG mcnRegistryValue = 1; - - PAGED_CODE(); - - // open the service key. - status = WdfDriverOpenParametersRegistryKey(WdfGetDriver(), - KEY_ALL_ACCESS, - WDF_NO_OBJECT_ATTRIBUTES, - &serviceKey); - if(!NT_SUCCESS(status)) - { - // return the default value, which is the inverse of the registry setting default - // since this routine asks if it's disabled - TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, - "DeviceIsMediaChangeDisabledForClass: Defaulting to %s\n", - (mcnRegistryValue ? "Enabled" : "Disabled"))); - return (BOOLEAN)(mcnRegistryValue == 0); - } - else - { - // Open the parameters key (if any) beneath the services key. - RtlInitUnicodeString(¶meterKeyName, L"Parameters"); - - status = WdfRegistryOpenKey(serviceKey, - ¶meterKeyName, - KEY_READ, - WDF_NO_OBJECT_ATTRIBUTES, - ¶metersKey); - } - - if (!NT_SUCCESS(status)) - { - parametersKey = NULL; - } - - RtlInitUnicodeString(&valueName, L"Autorun"); - // ignore failures - status = WdfRegistryQueryULong(serviceKey, - &valueName, - &mcnRegistryValue); - - if (NT_SUCCESS(status)) - { - TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN, - "DeviceIsMediaChangeDisabledForClass: <Service>/Autorun flag = %d\n", - mcnRegistryValue)); - } - - if (parametersKey != NULL) - { - status = WdfRegistryQueryULong(parametersKey, - &valueName, - &mcnRegistryValue); - - if (NT_SUCCESS(status)) - { - TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN, - "DeviceIsMediaChangeDisabledForClass: <Service>/Parameters/Autorun flag = %d\n", - mcnRegistryValue)); - } - - WdfRegistryClose(parametersKey); - } - - WdfRegistryClose(serviceKey); - - TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, - "DeviceIsMediaChangeDisabledForClass: Autoplay for device %p is %s\n", - DeviceExtension->DeviceObject, - (mcnRegistryValue ? "on" : "off") - )); - - // return if it is _disabled_, which is the - // inverse of the registry setting - - return (BOOLEAN)(!mcnRegistryValue); -} // end DeviceIsMediaChangeDisabledForClass() - - -_IRQL_requires_max_(APC_LEVEL) -VOID -DeviceEnableMediaChangeDetection( - _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, - _Inout_ PFILE_OBJECT_CONTEXT FileObjectContext, - _In_ BOOLEAN IgnorePreviousMediaChanges - ) -/*++ - -Routine Description: - - When the disable count decrease to 0, enable the MCN - -Arguments: - - DeviceExtension - the device context - - FileObjectContext - the file object context - - IgnorePreviousMediaChanges - ignore all previous media changes - -Return Value: - None. - ---*/ -{ - PMEDIA_CHANGE_DETECTION_INFO info = DeviceExtension->MediaChangeDetectionInfo; - LONG oldCount; - - PAGED_CODE(); - - if (FileObjectContext) - { - InterlockedDecrement((PLONG)&(FileObjectContext->McnDisableCount)); - } - - if (info == NULL) - { - TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, - "DeviceEnableMediaChangeDetection: not initialized\n")); - return; - } - - (VOID) KeWaitForMutexObject(&info->MediaChangeMutex, - UserRequest, - KernelMode, - FALSE, - NULL); - - oldCount = --info->MediaChangeDetectionDisableCount; - - NT_ASSERT(oldCount >= 0); - - TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN, - "DeviceEnableMediaChangeDetection: Disable count reduced to %d - \n", - info->MediaChangeDetectionDisableCount)); - - if (oldCount == 0) - { - if (IgnorePreviousMediaChanges) - { - info->LastReportedMediaDetectionState = info->LastKnownMediaDetectionState; - } - - TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "MCN is enabled\n")); - } - else - { - TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN, "MCD still disabled\n")); - } - - // Let something else run. - KeReleaseMutex(&info->MediaChangeMutex, FALSE); - - return; -} // end DeviceEnableMediaChangeDetection() - - -_IRQL_requires_max_(APC_LEVEL) -VOID -DeviceDisableMediaChangeDetection( - _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, - _Inout_ PFILE_OBJECT_CONTEXT FileObjectContext - ) -/*++ - -Routine Description: - - Increase the disable count. - -Arguments: - - DeviceExtension - the device context - - FileObjectContext - the file object context - -Return Value: - None. - ---*/ -{ - PMEDIA_CHANGE_DETECTION_INFO info = DeviceExtension->MediaChangeDetectionInfo; - - PAGED_CODE(); - - if (FileObjectContext) - { - InterlockedIncrement((PLONG)&(FileObjectContext->McnDisableCount)); - } - - if (info == NULL) - { - return; - } - - (VOID) KeWaitForMutexObject(&info->MediaChangeMutex, - UserRequest, - KernelMode, - FALSE, - NULL); - - info->MediaChangeDetectionDisableCount++; - - TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN, - "DisableMediaChangeDetection: disable count is %d\n", - info->MediaChangeDetectionDisableCount)); - - KeReleaseMutex(&info->MediaChangeMutex, FALSE); - - return; -} // end DeviceDisableMediaChangeDetection() - - -_IRQL_requires_max_(APC_LEVEL) -VOID -DeviceReleaseMcnResources( - _In_ PCDROM_DEVICE_EXTENSION DeviceExtension - ) -/*++ - -Routine Description: - - This routine will cleanup any resources allocated for MCN. It is called - by classpnp during remove device, and therefore is not typically required - by external drivers. - -Arguments: - - DeviceExtension - the device context - -Return Value: - None. - ---*/ -{ - PMEDIA_CHANGE_DETECTION_INFO info = DeviceExtension->MediaChangeDetectionInfo; - - PAGED_CODE() - - if(info == NULL) - { - return; - } - - if (info->Gesn.Mdl) - { - PIRP irp = WdfRequestWdmGetIrp(info->MediaChangeRequest); - IoFreeMdl(info->Gesn.Mdl); - irp->MdlAddress = NULL; - } - IoFreeIrp(info->MediaChangeSyncIrp); - FREE_POOL(info->Gesn.Buffer); - FREE_POOL(info->SenseBuffer); - - if (info->DisplayStateCallbackHandle) - { - PoUnregisterPowerSettingCallback(info->DisplayStateCallbackHandle); - info->DisplayStateCallbackHandle = NULL; - } - - FREE_POOL(info); - - DeviceExtension->MediaChangeDetectionInfo = NULL; - - return; -} // end DeviceReleaseMcnResources() - - -IO_COMPLETION_ROUTINE RequestMcnSyncIrpCompletion; - -NTSTATUS -RequestMcnSyncIrpCompletion( - _In_ PDEVICE_OBJECT DeviceObject, - _In_ PIRP Irp, - _In_reads_opt_(_Inexpressible_("varies")) PVOID Context - ) -/*++ - -Routine Description: - - The MCN work finishes, reset the fields to allow another MCN request - be scheduled. - -Arguments: - - DeviceObject - device that the completion routine fires on. - - Irp - The irp to be completed. - - Context - IRP context - -Return Value: - NTSTATUS - ---*/ -{ - PCDROM_DEVICE_EXTENSION DeviceExtension = NULL; - PMEDIA_CHANGE_DETECTION_INFO info = NULL; - - if (Context == NULL) - { - // this will never happen, but code must be there to prevent OACR warnings. - return STATUS_MORE_PROCESSING_REQUIRED; - } - - DeviceExtension = (PCDROM_DEVICE_EXTENSION) Context; - info = DeviceExtension->MediaChangeDetectionInfo; - -#ifndef DEBUG - UNREFERENCED_PARAMETER(Irp); -#endif - UNREFERENCED_PARAMETER(DeviceObject); - - NT_ASSERT(Irp == info->MediaChangeSyncIrp); - - IoReuseIrp(info->MediaChangeSyncIrp, STATUS_NOT_SUPPORTED); - - // reset the value to let timer routine be able to send the next request. - InterlockedCompareExchange((PLONG)&(info->MediaChangeRequestInUse), 0, 1); - - return STATUS_MORE_PROCESSING_REQUIRED; -} - - -VOID -RequestSetupMcnSyncIrp( - _In_ PCDROM_DEVICE_EXTENSION DeviceExtension - ) -/*++ - -Routine Description: - - setup the MCN synchronization irp. - -Arguments: - - DeviceExtension - the device context - -Return Value: - None - ---*/ -{ - PIRP irp = NULL; - PIO_STACK_LOCATION irpStack = NULL; - PIO_STACK_LOCATION nextIrpStack = NULL; - - irp = DeviceExtension->MediaChangeDetectionInfo->MediaChangeSyncIrp; - NT_ASSERT(irp != NULL); - - // - // For the driver that creates an IRP, there is no 'current' stack location. - // Step down one IRP stack location so that the extra top one - // becomes our 'current' one. - // - IoSetNextIrpStackLocation(irp); - - /* - * Cache our device object in the extra top IRP stack location - * so we have it in our completion routine. - */ - irpStack = IoGetCurrentIrpStackLocation(irp); - irpStack->DeviceObject = DeviceExtension->DeviceObject; - - // - // If the irp is sent down when the volume needs to be - // verified, CdRomUpdateGeometryCompletion won't complete - // it since it's not associated with a thread. Marking - // it to override the verify causes it always be sent - // to the port driver - // - nextIrpStack = IoGetNextIrpStackLocation(irp); - - SET_FLAG(nextIrpStack->Flags, SL_OVERRIDE_VERIFY_VOLUME); - - nextIrpStack->MajorFunction = IRP_MJ_DEVICE_CONTROL; - // pick up this IOCTL code as it's not normaly seen for CD/DVD drive and does not require input. - // set other fields to make this IOCTL recognizable by CDROM.SYS - nextIrpStack->Parameters.Others.Argument1 = RequestSetupMcnSyncIrp; - nextIrpStack->Parameters.Others.Argument2 = RequestSetupMcnSyncIrp; - nextIrpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_MCN_SYNC_FAKE_IOCTL; //Argument3. - nextIrpStack->Parameters.Others.Argument4 = RequestSetupMcnSyncIrp; - - IoSetCompletionRoutine(irp, - RequestMcnSyncIrpCompletion, - DeviceExtension, - TRUE, - TRUE, - TRUE); - - return; -} - - -VOID -DeviceMainTimerTickHandler( - _In_ WDFTIMER Timer - ) -/*++ - -Routine Description: - - This routine setup a sync irp and send it to the serial queue. - Serial queue will process MCN when receive this sync irp. - -Arguments: - - Timer - the timer object that fires. - -Return Value: - None - ---*/ -{ - PCDROM_DEVICE_EXTENSION deviceExtension = NULL; - size_t dataLength = 0; - - deviceExtension = WdfObjectGetTypedContext(WdfTimerGetParentObject(Timer), CDROM_DEVICE_EXTENSION); - - (void) RequestHandleEventNotification(deviceExtension, NULL, NULL, &dataLength); - - return; -} // end DeviceMainTimerTickHandler() - - -_IRQL_requires_max_(APC_LEVEL) -NTSTATUS -DeviceEnableMainTimer( - _In_ PCDROM_DEVICE_EXTENSION DeviceExtension - ) -/*++ - -Routine Description: - - This routine will allocate timer related resources on the first time call. - Start the timer. - -Arguments: - - DeviceExtension - the device context - -Return Value: - NTSTATUS - ---*/ -{ - NTSTATUS status = STATUS_SUCCESS; - - if ((DeviceExtension->MediaChangeDetectionInfo == NULL) || - (DeviceExtension->MediaChangeDetectionInfo->AsynchronousNotificationSupported != FALSE)) - { - // Asynchronous Notification is enabled, timer not needed. - return status; - } - - if (DeviceExtension->MainTimer == NULL) - { - //create main timer object. - WDF_TIMER_CONFIG timerConfig; - WDF_OBJECT_ATTRIBUTES timerAttributes; - - WDF_TIMER_CONFIG_INIT(&timerConfig, DeviceMainTimerTickHandler); - - // Polling frequently on virtual optical devices created by Hyper-V will - // cause a significant perf / power hit. These devices need to be polled - // less frequently for device state changes. - if (TEST_FLAG(DeviceExtension->DeviceAdditionalData.HackFlags, CDROM_HACK_MSFT_VIRTUAL_ODD)) - { - timerConfig.Period = 2000; // 2 seconds, in milliseconds. - } - else - { - timerConfig.Period = 1000; // 1 second, in milliseconds. - } - - timerConfig.TolerableDelay = 500; // 0.5 seconds, in milliseconds - - //Set the autoSerialization to FALSE, as the parent device's - //execute level is WdfExecutionLevelPassive. - timerConfig.AutomaticSerialization = FALSE; - - WDF_OBJECT_ATTRIBUTES_INIT(&timerAttributes); - timerAttributes.ParentObject = DeviceExtension->Device; - timerAttributes.ExecutionLevel = WdfExecutionLevelInheritFromParent; - - status = WdfTimerCreate(&timerConfig, - &timerAttributes, - &DeviceExtension->MainTimer); - } - - if (NT_SUCCESS(status)) - { - WdfTimerStart(DeviceExtension->MainTimer,WDF_REL_TIMEOUT_IN_MS(100)); - - TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, - "DeviceEnableMainTimer: Once a second timer enabled for WDFDEVICE %p\n", - DeviceExtension->Device)); - } - else - { - TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, - "DeviceEnableMainTimer: WDFDEVICE %p, Status %lx initializing timer\n", - DeviceExtension->Device, status)); - } - - return status; -} // end DeviceEnableMainTimer() - - -_IRQL_requires_max_(PASSIVE_LEVEL) -VOID -DeviceDisableMainTimer( - _In_ PCDROM_DEVICE_EXTENSION DeviceExtension - ) -/*++ - -Routine Description: - - stop the timer. - -Arguments: - - DeviceExtension - device context - -Return Value: - None - ---*/ -{ - PAGED_CODE(); - - if ((DeviceExtension->MediaChangeDetectionInfo == NULL) || - (DeviceExtension->MediaChangeDetectionInfo->AsynchronousNotificationSupported != FALSE)) - { - // Asynchronous Notification is enabled, timer not needed. - return; - } - - if (DeviceExtension->MainTimer != NULL) - { - // - // we are only going to stop the actual timer in remove device routine. - // it is the responsibility of the code within the timer routine to - // check if the device is removed and not processing io for the final - // call. - // this keeps the code clean and prevents lots of bugs. - // - WdfTimerStop(DeviceExtension->MainTimer,TRUE); - - TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, - "DeviceDisableMainTimer: Once a second timer disabled for device %p\n", - DeviceExtension->Device)); - } - else - { - TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN, - "DeviceDisableMainTimer: Timer never enabled\n")); - } - - return; -} // end DeviceDisableMainTimer() - - -_IRQL_requires_max_(APC_LEVEL) -NTSTATUS -RequestHandleMcnControl( - _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, - _In_ WDFREQUEST Request, - _Out_ size_t * DataLength - ) -/*++ - -Routine Description: - - This routine handles the process of IOCTL_STORAGE_MCN_CONTROL - -Arguments: - - DeviceExtension - device context - - Request - request object - - RequestParameters - request parameters - - DataLength - data transferred - -Return Value: - NTSTATUS - ---*/ -{ - NTSTATUS status = STATUS_SUCCESS; - WDFFILEOBJECT fileObject = NULL; - PFILE_OBJECT_CONTEXT fileObjectContext = NULL; - PPREVENT_MEDIA_REMOVAL mediaRemoval = NULL; - - PAGED_CODE(); - - *DataLength = 0; - - status = WdfRequestRetrieveInputBuffer(Request, - sizeof(PREVENT_MEDIA_REMOVAL), - &mediaRemoval, - NULL); - - if (NT_SUCCESS(status)) - { - fileObject = WdfRequestGetFileObject(Request); - - // Check to make sure we have a file object extension to keep track of this - // request. If not we'll fail it before synchronizing. - if (fileObject != NULL) - { - fileObjectContext = FileObjectGetContext(fileObject); - } - - if ((fileObjectContext == NULL) && - (WdfRequestGetRequestorMode(Request) == KernelMode)) - { - fileObjectContext = &DeviceExtension->KernelModeMcnContext; - } - - if (fileObjectContext == NULL) - { - // This handle isn't setup correctly. We can't let the - // operation go. - status = STATUS_INVALID_PARAMETER; - } - } - - if (NT_SUCCESS(status)) - { - if (mediaRemoval->PreventMediaRemoval) - { - // This is a lock command. Reissue the command in case bus or - // device was reset and the lock was cleared. - DeviceDisableMediaChangeDetection(DeviceExtension, fileObjectContext); - } - else - { - if (fileObjectContext->McnDisableCount == 0) - { - status = STATUS_INVALID_DEVICE_STATE; - } - else - { - DeviceEnableMediaChangeDetection(DeviceExtension, fileObjectContext, TRUE); - } - } - } - - return status; -} // end RequestHandleMcnControl() - -#pragma warning(pop) // un-sets any local warning changes - |
