📄 autorun.c
字号:
/*++
Copyright (C) Microsoft Corporation, 1991 - 1999
Module Name:
autorun.c
Abstract:
Code for support of media change detection in the class driver
Environment:
kernel mode only
Notes:
Revision History:
--*/
#include "classp.h"
#include "debug.h"
#define GESN_TIMEOUT_VALUE (0x4)
#define GESN_BUFFER_SIZE (0x8)
#define GESN_DEVICE_BUSY_LOWER_THRESHOLD_MS (200)
#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")
GUID StoragePredictFailureEventGuid = WMI_STORAGE_PREDICT_FAILURE_EVENT_GUID;
//
// 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.
//
#define ClasspCanSendPollingIrp(fdoExtension) \
((fdoExtension->DevicePowerState == PowerDeviceD0) && \
(! fdoExtension->PowerDownInProgress) )
BOOLEAN
ClasspIsMediaChangeDisabledDueToHardwareLimitation(
IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
IN PUNICODE_STRING RegistryPath
);
NTSTATUS
ClasspMediaChangeDeviceInstanceOverride(
IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
OUT PBOOLEAN Enabled
);
BOOLEAN
ClasspIsMediaChangeDisabledForClass(
IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
IN PUNICODE_STRING RegistryPath
);
VOID
ClasspSetMediaChangeStateEx(
IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
IN MEDIA_CHANGE_DETECTION_STATE NewState,
IN BOOLEAN Wait,
IN BOOLEAN KnownStateChange // can ignore oldstate == unknown
);
NTSTATUS
ClasspMediaChangeRegistryCallBack(
IN PWSTR ValueName,
IN ULONG ValueType,
IN PVOID ValueData,
IN ULONG ValueLength,
IN PVOID Context,
IN PVOID EntryContext
);
VOID
ClasspSendMediaStateIrp(
IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
IN PMEDIA_CHANGE_DETECTION_INFO Info,
IN ULONG CountDown
);
VOID
ClasspFailurePredict(
IN PDEVICE_OBJECT DeviceObject,
IN PFAILURE_PREDICTION_INFO Info
);
NTSTATUS
ClasspInitializePolling(
IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
IN BOOLEAN AllowDriveToSleep
);
VOID
ClasspDisableGesn(
IN PDEVICE_OBJECT Fdo,
IN PIO_WORKITEM WorkItem
);
#if ALLOC_PRAGMA
#pragma alloc_text(PAGE, ClassInitializeMediaChangeDetection)
#pragma alloc_text(PAGE, ClassEnableMediaChangeDetection)
#pragma alloc_text(PAGE, ClassDisableMediaChangeDetection)
#pragma alloc_text(PAGE, ClassCleanupMediaChangeDetection)
#pragma alloc_text(PAGE, ClasspMediaChangeRegistryCallBack)
#pragma alloc_text(PAGE, ClasspInitializePolling)
#pragma alloc_text(PAGE, ClasspDisableGesn)
#pragma alloc_text(PAGE, ClasspIsMediaChangeDisabledDueToHardwareLimitation)
#pragma alloc_text(PAGE, ClasspMediaChangeDeviceInstanceOverride)
#pragma alloc_text(PAGE, ClasspIsMediaChangeDisabledForClass)
#pragma alloc_text(PAGE, ClassSetFailurePredictionPoll)
#pragma alloc_text(PAGE, ClasspDisableTimer)
#pragma alloc_text(PAGE, ClasspEnableTimer)
#endif
// ISSUE -- make this public?
VOID
ClassSendEjectionNotification(
IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
)
{
//
// For post-NT5.1 work, need to move EjectSynchronizationEvent
// to be a MUTEX so we can attempt to grab it here and benefit
// from deadlock detection. This will allow checking if the media
// has been locked by programs before broadcasting these events.
// (what's the point of broadcasting if the media is not locked?)
//
// This would currently only be a slight optimization. For post-NT5.1,
// it would allow us to send a single PERSISTENT_PREVENT to MMC devices,
// thereby cleaning up a lot of the ejection code. Then, when the
// ejection request occured, we could see if any locks for the media
// existed. if locked, broadcast. if not, we send the eject irp.
//
//
// for now, just always broadcast. make this a public routine,
// so class drivers can add special hacks to broadcast this for their
// non-MMC-compliant devices also from sense codes.
//
DBGTRACE(ClassDebugTrace, ("ClassSendEjectionNotification: media EJECT_REQUEST"));
ClasspSendNotification(FdoExtension,
&GUID_IO_MEDIA_EJECT_REQUEST,
0,
NULL);
return;
}
VOID
ClasspSendNotification(
IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
IN const GUID * Guid,
IN ULONG ExtraDataSize,
IN PVOID ExtraData
)
{
PTARGET_DEVICE_CUSTOM_NOTIFICATION notification;
ULONG requiredSize;
requiredSize =
(sizeof(TARGET_DEVICE_CUSTOM_NOTIFICATION) - sizeof(UCHAR)) +
ExtraDataSize;
if (requiredSize > 0x0000ffff) {
// MAX_USHORT, max total size for these events!
KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugWarning,
"Error sending event: size too large! (%x)\n",
requiredSize));
return;
}
notification = ExAllocatePoolWithTag(NonPagedPool,
requiredSize,
'oNcS');
//
// if none allocated, exit
//
if (notification == NULL) {
return;
}
//
// Prepare and send the request!
//
RtlZeroMemory(notification, requiredSize);
notification->Version = 1;
notification->Size = (USHORT)(requiredSize);
notification->FileObject = NULL;
notification->NameBufferOffset = -1;
notification->Event = *Guid;
RtlCopyMemory(notification->CustomDataBuffer, ExtraData, ExtraDataSize);
IoReportTargetDeviceChangeAsynchronous(FdoExtension->LowerPdo,
notification,
NULL, NULL);
ExFreePool(notification);
notification = NULL;
return;
}
NTSTATUS
ClasspInterpretGesnData(
IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
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:
FdoExtension - the device
DataBuffer - 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.
--*/
{
PMEDIA_CHANGE_DETECTION_INFO info;
LONG dataLength;
LONG requiredLength;
NTSTATUS status = STATUS_SUCCESS;
info = FdoExtension->MediaChangeDetectionInfo;
//
// 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.
//
KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN,
"Classpnp => 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;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -