summaryrefslogtreecommitdiff
path: root/storage/class
diff options
context:
space:
mode:
authorDave Wilson <[email protected]>2015-03-17 19:50:07 -0700
committerDave Wilson <[email protected]>2015-03-17 19:50:07 -0700
commit97cf5197cf5b882b2c689d8dc2b555f2edf8f418 (patch)
tree46f3701832d70b420eb0fc0eb93261f9da45db3f /storage/class
parentef1905bf1e8825bb31120dfb27e0daf3154d859a (diff)
Initial publish
Diffstat (limited to 'storage/class')
-rw-r--r--storage/class/classpnp/ReadMe.md15
-rw-r--r--storage/class/classpnp/classpnp.sln28
-rw-r--r--storage/class/classpnp/src/autorun.c4388
-rw-r--r--storage/class/classpnp/src/class.c16415
-rw-r--r--storage/class/classpnp/src/class.def119
-rw-r--r--storage/class/classpnp/src/class.rc22
-rw-r--r--storage/class/classpnp/src/classlog.mof175
-rw-r--r--storage/class/classpnp/src/classp.h2619
-rw-r--r--storage/class/classpnp/src/classpnp.htm273
-rw-r--r--storage/class/classpnp/src/classpnp.vcxproj322
-rw-r--r--storage/class/classpnp/src/classpnp.vcxproj.Filters87
-rw-r--r--storage/class/classpnp/src/classwmi.c1224
-rw-r--r--storage/class/classpnp/src/clntirp.c783
-rw-r--r--storage/class/classpnp/src/create.c1018
-rw-r--r--storage/class/classpnp/src/data.c222
-rw-r--r--storage/class/classpnp/src/debug.c966
-rw-r--r--storage/class/classpnp/src/debug.h132
-rw-r--r--storage/class/classpnp/src/dictlib.c218
-rw-r--r--storage/class/classpnp/src/dispatch.c131
-rw-r--r--storage/class/classpnp/src/history.c146
-rw-r--r--storage/class/classpnp/src/lock.c550
-rw-r--r--storage/class/classpnp/src/obsolete.c1125
-rw-r--r--storage/class/classpnp/src/power.c2650
-rw-r--r--storage/class/classpnp/src/retry.c758
-rw-r--r--storage/class/classpnp/src/srblib.c374
-rw-r--r--storage/class/classpnp/src/utils.c8906
-rw-r--r--storage/class/classpnp/src/xferpkt.c2047
27 files changed, 45713 insertions, 0 deletions
diff --git a/storage/class/classpnp/ReadMe.md b/storage/class/classpnp/ReadMe.md
new file mode 100644
index 00000000..136dbe9d
--- /dev/null
+++ b/storage/class/classpnp/ReadMe.md
@@ -0,0 +1,15 @@
+ClassPnP Storage Class Driver Library
+=====================================
+
+This library is the library for all storage drivers. It simplifies writing a storage class driver by implementing 90 percent of the code that you need to support Plug and Play (PnP), power management, and so on. This library is used by disk, CDROM, and the tape class drivers.
+
+## Universal Compliant
+This sample builds a Windows Universal driver. It uses only APIs and DDIs that are included in Windows Core.
+
+Installation and Operation
+--------------------------
+
+The storage class drivers are used to interact with mass storage devices along with appropriate port driver. The class drivers are layered above the port drivers and manage mass storage devices of a specific class, regardless of their bus type. The classpnp sample contains the common routines that are required for all storage class drivers such as PnP and power management. It also provides I/O and error handling support.
+
+For more information, see [Introduction to Storage Class Drivers](http://msdn.microsoft.com/en-us/library/windows/hardware/ff559215) in the storage technologies design guide.
+
diff --git a/storage/class/classpnp/classpnp.sln b/storage/class/classpnp/classpnp.sln
new file mode 100644
index 00000000..34e3421f
--- /dev/null
+++ b/storage/class/classpnp/classpnp.sln
@@ -0,0 +1,28 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2013
+VisualStudioVersion = 12.0
+MinimumVisualStudioVersion = 12.0
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "classpnp", "src\classpnp.vcxproj", "{6AEA931E-5110-494C-A1DB-9FDE40CE7133}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Release|Win32 = Release|Win32
+ Debug|x64 = Debug|x64
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {6AEA931E-5110-494C-A1DB-9FDE40CE7133}.Debug|Win32.ActiveCfg = Debug|Win32
+ {6AEA931E-5110-494C-A1DB-9FDE40CE7133}.Debug|Win32.Build.0 = Debug|Win32
+ {6AEA931E-5110-494C-A1DB-9FDE40CE7133}.Release|Win32.ActiveCfg = Release|Win32
+ {6AEA931E-5110-494C-A1DB-9FDE40CE7133}.Release|Win32.Build.0 = Release|Win32
+ {6AEA931E-5110-494C-A1DB-9FDE40CE7133}.Debug|x64.ActiveCfg = Debug|x64
+ {6AEA931E-5110-494C-A1DB-9FDE40CE7133}.Debug|x64.Build.0 = Debug|x64
+ {6AEA931E-5110-494C-A1DB-9FDE40CE7133}.Release|x64.ActiveCfg = Release|x64
+ {6AEA931E-5110-494C-A1DB-9FDE40CE7133}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/storage/class/classpnp/src/autorun.c b/storage/class/classpnp/src/autorun.c
new file mode 100644
index 00000000..4dba537b
--- /dev/null
+++ b/storage/class/classpnp/src/autorun.c
@@ -0,0 +1,4388 @@
+/*++
+
+Copyright (C) Microsoft Corporation, 1991 - 2010
+
+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"
+
+#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")
+
+const GUID StoragePredictFailureEventGuid = WMI_STORAGE_PREDICT_FAILURE_EVENT_GUID;
+
+//
+// Only send polling irp when device is fully powered up, a
+// power down irp is not in progress, and the screen is on.
+//
+// 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.
+//
+__inline
+BOOLEAN
+ClasspCanSendPollingIrp(
+ _In_ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension
+ )
+{
+ return ((fdoExtension->DevicePowerState == PowerDeviceD0) &&
+ (fdoExtension->PowerDownInProgress == FALSE) &&
+ (ClasspScreenOff == FALSE));
+}
+
+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
+ );
+
+RTL_QUERY_REGISTRY_ROUTINE ClasspMediaChangeRegistryCallBack;
+
+VOID
+ClasspSendMediaStateIrp(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN PMEDIA_CHANGE_DETECTION_INFO Info,
+ IN ULONG CountDown
+ );
+
+IO_WORKITEM_ROUTINE ClasspFailurePredict;
+
+NTSTATUS
+ClasspInitializePolling(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN BOOLEAN AllowDriveToSleep
+ );
+
+
+IO_WORKITEM_ROUTINE ClasspDisableGesn;
+
+IO_COMPLETION_ROUTINE ClasspMediaChangeDetectionCompletion;
+
+KDEFERRED_ROUTINE ClasspTimerTick;
+
+#if (NTDDI_VERSION >= NTDDI_WINBLUE)
+EXT_CALLBACK ClasspTimerTickEx;
+#endif
+
+BOOLEAN ClasspScreenOff = FALSE;
+
+//
+// Tick timer related defines.
+//
+#define TICK_TIMER_PERIOD_IN_MSEC 1000
+#define TICK_TIMER_DELAY_IN_MSEC 1000
+
+#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, ClasspInitializeGesn)
+#pragma alloc_text(PAGE, ClasspMcnControl)
+
+#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.
+ //
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClassSendEjectionNotification: media EJECT_REQUEST"));
+ ClassSendNotification(FdoExtension,
+ &GUID_IO_MEDIA_EJECT_REQUEST,
+ 0,
+ NULL);
+ return;
+}
+
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+VOID
+ClassSendNotification(
+ _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ _In_ const GUID * Guid,
+ _In_ ULONG ExtraDataSize,
+ _In_reads_bytes_opt_(ExtraDataSize) PVOID ExtraData
+ )
+{
+ PTARGET_DEVICE_CUSTOM_NOTIFICATION notification;
+ ULONG requiredSize;
+ NTSTATUS status;
+
+ status = RtlULongAdd((sizeof(TARGET_DEVICE_CUSTOM_NOTIFICATION) - sizeof(UCHAR)),
+ ExtraDataSize,
+ &requiredSize);
+
+ if (!(NT_SUCCESS(status)) || (requiredSize > 0x0000ffff)) {
+ // MAX_USHORT, max total size for these events!
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN,
+ "Error sending event: size too large! (%x)\n",
+ requiredSize));
+ return;
+ }
+
+ notification = ExAllocatePoolWithTag(NonPagedPoolNx,
+ 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;
+
+ if (ExtraData != NULL && ExtraDataSize != 0) {
+ RtlCopyMemory(notification->CustomDataBuffer, ExtraData, ExtraDataSize);
+ }
+
+ IoReportTargetDeviceChangeAsynchronous(FdoExtension->LowerPdo,
+ notification,
+ NULL, NULL);
+
+ FREE_POOL(notification);
+ 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.
+ //
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+ "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;
+ }
+
+ } // 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,
+ "Classpnp => GESN returned only %x bytes data for fdo %p\n",
+ dataLength, FdoExtension->DeviceObject));
+
+ return STATUS_DEVICE_PROTOCOL_ERROR;
+ }
+ if (dataLength != requiredLength) {
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN,
+ "Classpnp => GESN returned too many (%x) bytes data for fdo %p\n",
+ dataLength, FdoExtension->DeviceObject));
+ // dataLength = 4;
+ }
+
+ NT_ASSERT(dataLength == 4);
+
+ 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;
+
+
+/*
+ ClassSendNotification(FdoExtension,
+ &GUID_IO_GENERIC_GESN_EVENT,
+ sizeof(NOTIFICATION_EVENT_STATUS_HEADER) + dataLength,
+ Header)
+*/
+
+
+
+ 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,
+ "Classpnp => GESN OpChange events are broken. Working around this "
+ "problem in software (for fdo %p)\n",
+ FdoExtension->DeviceObject));
+
+
+ // 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,
+ "Classpnp => GESN says features added/changedfor fdo %p\n",
+ FdoExtension->DeviceObject));
+
+ // don't notify that new media arrived, just set the
+ // DO_VERIFY to force a FS reload.
+
+ if (TEST_FLAG(FdoExtension->DeviceObject->Characteristics,
+ FILE_REMOVABLE_MEDIA) &&
+ (ClassGetVpb(FdoExtension->DeviceObject) != NULL) &&
+ (ClassGetVpb(FdoExtension->DeviceObject)->Flags & VPB_MOUNTED)
+ ) {
+
+ SET_FLAG(FdoExtension->DeviceObject->Flags, DO_VERIFY_VOLUME);
+ }
+
+ //
+ // If there is a class specific error handler, call it with
+ // a "fake" media change error in case it needs to update
+ // internal structures as though a media change occurred.
+ //
+
+ if (FdoExtension->CommonExtension.DevInfo->ClassError != NULL) {
+
+ SCSI_REQUEST_BLOCK srb = {0};
+ UCHAR srbExBuffer[CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE] = {0};
+ PSTORAGE_REQUEST_BLOCK srbEx = (PSTORAGE_REQUEST_BLOCK)srbExBuffer;
+ PSCSI_REQUEST_BLOCK srbPtr;
+
+ SENSE_DATA sense = {0};
+ NTSTATUS tempStatus;
+ BOOLEAN retry;
+
+ tempStatus = STATUS_MEDIA_CHANGED;
+ retry = FALSE;
+
+ sense.ErrorCode = SCSI_SENSE_ERRORCODE_FIXED_CURRENT;
+
+ sense.AdditionalSenseLength = sizeof(SENSE_DATA) -
+ RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseLength);
+
+ sense.SenseKey = SCSI_SENSE_UNIT_ATTENTION;
+ sense.AdditionalSenseCode = SCSI_ADSENSE_MEDIUM_CHANGED;
+
+ //
+ // Send the right type of SRB to the class driver
+ //
+ if ((FdoExtension->CommonExtension.DriverExtension->SrbSupport &
+ CLASS_SRB_STORAGE_REQUEST_BLOCK) != 0) {
+ #pragma prefast(suppress:26015, "InitializeStorageRequestBlock ensures buffer access is bounded")
+ status = InitializeStorageRequestBlock(srbEx,
+ STORAGE_ADDRESS_TYPE_BTL8,
+ CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE,
+ 1,
+ SrbExDataTypeScsiCdb16);
+ if (NT_SUCCESS(status)) {
+ SrbSetCdbLength(srbEx, 6);
+ srbEx->SrbStatus = SRB_STATUS_AUTOSENSE_VALID | SRB_STATUS_ERROR;
+ SrbSetSenseInfoBuffer(srbEx, &sense);
+ SrbSetSenseInfoBufferLength(srbEx, sizeof(sense));
+ srbPtr = (PSCSI_REQUEST_BLOCK)srbEx;
+ } else {
+ // should not happen. Revert to legacy SRB.
+ NT_ASSERT(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);
+ srbPtr = &srb;
+ }
+ } else {
+ 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);
+ srbPtr = &srb;
+ }
+
+ FdoExtension->CommonExtension.DevInfo->ClassError(FdoExtension->DeviceObject,
+ srbPtr,
+ &tempStatus,
+ &retry);
+
+ } // end class 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,
+ "Classpnp => 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();
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClasspInterpretGesnData: media DEVICE_EXTERNAL_REQUEST"));
+ ClassSendNotification(FdoExtension,
+ &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);
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+ "Classpnp => GESN::MEDIA: Event: %x Status %x\n",
+ mediaInfo->MediaEvent, mediaInfo->MediaStatus));
+
+ if ((mediaInfo->MediaEvent == NOTIFICATION_MEDIA_EVENT_NEW_MEDIA) ||
+ (mediaInfo->MediaEvent == NOTIFICATION_MEDIA_EVENT_MEDIA_CHANGE)) {
+
+
+ if (TEST_FLAG(FdoExtension->DeviceObject->Characteristics,
+ FILE_REMOVABLE_MEDIA) &&
+ (ClassGetVpb(FdoExtension->DeviceObject) != NULL) &&
+ (ClassGetVpb(FdoExtension->DeviceObject)->Flags & VPB_MOUNTED)
+ ) {
+
+ SET_FLAG(FdoExtension->DeviceObject->Flags, DO_VERIFY_VOLUME);
+
+ }
+ InterlockedIncrement((volatile LONG *)&FdoExtension->MediaChangeCount);
+ ClasspSetMediaChangeStateEx(FdoExtension,
+ MediaPresent,
+ FALSE,
+ TRUE);
+
+ } else if (mediaInfo->MediaEvent == NOTIFICATION_MEDIA_EVENT_MEDIA_REMOVAL) {
+
+ ClasspSetMediaChangeStateEx(FdoExtension,
+ MediaNotPresent,
+ FALSE,
+ TRUE);
+
+ } else if (mediaInfo->MediaEvent == NOTIFICATION_MEDIA_EVENT_EJECT_REQUEST) {
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+ "Classpnp => GESN Ejection request received!\n"));
+ ClassSendEjectionNotification(FdoExtension);
+
+ }
+ 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};
+
+ //
+ // NOTE: we never actually need to immediately retry for these
+ // events: if one exists, the device is busy, and if not,
+ // we still don't want to retry.
+ //
+
+ *ResendImmediately = FALSE;
+
+ //
+ // 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,
+ "Classpnp => 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) {
+ break;
+ }
+
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClasspInterpretGesnData: media BECOMING_READY"));
+ ClassSendNotification(FdoExtension,
+ &GUID_IO_DEVICE_BECOMING_READY,
+ sizeof(DEVICE_EVENT_BECOMING_READY),
+ &busyData);
+ break;
+ }
+
+ default: {
+
+ break;
+
+ }
+
+ } // end switch on notification class
+ return status;
+}
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClasspInternalSetMediaChangeState()
+
+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:
+
+ FdoExtension - the device
+
+ MediaPresent - indicates whether the device has media inserted into it
+ (TRUE) or not (FALSE).
+
+Return Value:
+
+ none
+
+--*/
+VOID
+ClasspInternalSetMediaChangeState(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN MEDIA_CHANGE_DETECTION_STATE NewState,
+ IN BOOLEAN KnownStateChange // can ignore oldstate == unknown
+ )
+{
+#if DBG
+ PCSZ states[] = {"Unknown", "Present", "Not Present", "Unavailable"};
+#endif
+ MEDIA_CHANGE_DETECTION_STATE oldMediaState;
+ PMEDIA_CHANGE_DETECTION_INFO info = FdoExtension->MediaChangeDetectionInfo;
+ CLASS_MEDIA_CHANGE_CONTEXT mcnContext;
+
+ if (!((NewState >= MediaUnknown) && (NewState <= MediaUnavailable))) {
+ return;
+ }
+
+ if(info == NULL) {
+ return;
+ }
+
+ oldMediaState = InterlockedExchange(
+ (PLONG)(&info->MediaChangeDetectionState),
+ (LONG)NewState);
+
+ if((oldMediaState == MediaUnknown) && (!KnownStateChange)) {
+
+ //
+ // The media was in an indeterminate state before - don't notify for
+ // this change.
+ //
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+ "ClassSetMediaChangeState: State was unknown - this may "
+ "not be a change\n"));
+ return;
+
+ } else if(oldMediaState == NewState) {
+
+ //
+ // Media is in the same state it was before.
+ //
+
+ return;
+ }
+
+ if(info->MediaChangeDetectionDisableCount != 0) {
+#if DBG
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+ "ClassSetMediaChangeState: 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,
+ "ClassSetMediaChangeState: State change from %s to %s\n",
+ states[oldMediaState], states[NewState]));
+#endif
+
+ //
+ // make the data useful -- it used to always be zero.
+ //
+ mcnContext.MediaChangeCount = FdoExtension->MediaChangeCount;
+ mcnContext.NewState = NewState;
+
+ if (NewState == MediaPresent) {
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClasspInternalSetMediaChangeState: media ARRIVAL"));
+ ClassSendNotification(FdoExtension,
+ &GUID_IO_MEDIA_ARRIVAL,
+ sizeof(CLASS_MEDIA_CHANGE_CONTEXT),
+ &mcnContext);
+
+ }
+ else if ((NewState == MediaNotPresent) || (NewState == MediaUnavailable)) {
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClasspInternalSetMediaChangeState: media REMOVAL"));
+ ClassSendNotification(FdoExtension,
+ &GUID_IO_MEDIA_REMOVAL,
+ sizeof(CLASS_MEDIA_CHANGE_CONTEXT),
+ &mcnContext);
+
+ } else {
+
+ //
+ // Don't notify of changed going to unknown.
+ //
+
+ return;
+ }
+
+ return;
+} // end ClasspInternalSetMediaChangeState()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassSetMediaChangeState()
+
+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:
+
+ FdoExtension - the device
+
+ MediaPresent - indicates whether the device has media inserted into it
+ (TRUE) or not (FALSE).
+
+ Wait - indicates whether the function should wait until it can acquire
+ the synchronization lock or not.
+
+Return Value:
+
+ none
+
+--*/
+
+VOID
+#pragma prefast(suppress:26165, "The mutex won't be acquired in the case of a timeout.")
+ClasspSetMediaChangeStateEx(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN MEDIA_CHANGE_DETECTION_STATE NewState,
+ IN BOOLEAN Wait,
+ IN BOOLEAN KnownStateChange // can ignore oldstate == unknown
+ )
+{
+ PMEDIA_CHANGE_DETECTION_INFO info = FdoExtension->MediaChangeDetectionInfo;
+ LARGE_INTEGER zero;
+ NTSTATUS status;
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "> ClasspSetMediaChangeStateEx"));
+
+ //
+ // Reset SMART status on media removal as the old status may not be
+ // valid when there is no media in the device or when new media is
+ // inserted.
+ //
+
+ if (NewState == MediaNotPresent) {
+
+ FdoExtension->FailurePredicted = FALSE;
+ FdoExtension->FailureReason = 0;
+
+ }
+
+
+ zero.QuadPart = 0;
+
+ if(info == NULL) {
+ return;
+ }
+
+ status = KeWaitForMutexObject(&info->MediaChangeMutex,
+ Executive,
+ KernelMode,
+ FALSE,
+ ((Wait == TRUE) ? NULL : &zero));
+
+ if(status == STATUS_TIMEOUT) {
+
+ //
+ // Someone else is in the process of setting the media state
+ //
+
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN, "ClasspSetMediaChangeStateEx - timed out waiting for mutex"));
+ return;
+ }
+
+ //
+ // Change the media present state and signal an event, if applicable
+ //
+
+ ClasspInternalSetMediaChangeState(FdoExtension, NewState, KnownStateChange);
+
+ KeReleaseMutex(&info->MediaChangeMutex, FALSE);
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "< ClasspSetMediaChangeStateEx"));
+
+ return;
+} // end ClassSetMediaChangeStateEx()
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+ClassSetMediaChangeState(
+ _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ _In_ MEDIA_CHANGE_DETECTION_STATE NewState,
+ _In_ BOOLEAN Wait
+ )
+{
+ ClasspSetMediaChangeStateEx(FdoExtension, NewState, Wait, FALSE);
+ return;
+}
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClasspMediaChangeDetectionCompletion()
+
+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:
+
+ DeviceObject - the object for the completion
+ Irp - the IRP being completed
+ Context - the SRB from the IRP
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+NTSTATUS
+ClasspMediaChangeDetectionCompletion(
+ PDEVICE_OBJECT DeviceObject,
+ PIRP Irp,
+ PVOID Context
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData;
+ PMEDIA_CHANGE_DETECTION_INFO info;
+ NTSTATUS status;
+ BOOLEAN retryImmediately = FALSE;
+ PSTORAGE_REQUEST_BLOCK_HEADER Srb = (PSTORAGE_REQUEST_BLOCK_HEADER) Context;
+
+ _Analysis_assume_(Srb != NULL);
+
+ //
+ // Since the class driver created this request, it's completion routine
+ // will not get a valid device object handed in. Use the one in the
+ // irp stack instead
+ //
+
+ DeviceObject = IoGetCurrentIrpStackLocation(Irp)->DeviceObject;
+ fdoExtension = DeviceObject->DeviceExtension;
+ fdoData = fdoExtension->PrivateFdoData;
+ info = fdoExtension->MediaChangeDetectionInfo;
+
+ NT_ASSERT(info->MediaChangeIrp != NULL);
+ NT_ASSERT(!TEST_FLAG(Srb->SrbStatus, SRB_STATUS_QUEUE_FROZEN));
+ TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN, "> ClasspMediaChangeDetectionCompletion: Device %p completed MCN irp %p.", DeviceObject, Irp));
+
+ /*
+ * HACK for IoMega 2GB Jaz drive:
+ * This drive spins down on its own to preserve the media.
+ * When spun down, TUR fails with 2/4/0 (SCSI_SENSE_NOT_READY/SCSI_ADSENSE_LUN_NOT_READY/?).
+ * InterpretSenseInfo routine would then call ClassSendStartUnit to spin the media up, which defeats the
+ * purpose of the spindown.
+ * So in this case, make this into a successful TUR.
+ * This allows the drive to stay spun down until it is actually accessed again.
+ * (If the media were actually removed, TUR would fail with 2/3a/0 ).
+ * This hack only applies to drives with the CAUSE_NOT_REPORTABLE_HACK bit set; this
+ * is set by disk.sys when HackCauseNotReportableHack is set for the drive in its BadControllers list.
+ */
+
+ if ((SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_SUCCESS) &&
+ TEST_FLAG(fdoExtension->ScanForSpecialFlags, CLASS_SPECIAL_CAUSE_NOT_REPORTABLE_HACK)) {
+
+ PVOID senseData = SrbGetSenseInfoBuffer(Srb);
+
+ if (senseData) {
+
+ BOOLEAN validSense = TRUE;
+ UCHAR senseInfoBufferLength = SrbGetSenseInfoBufferLength(Srb);
+ UCHAR senseKey = 0;
+ UCHAR additionalSenseCode = 0;
+
+ validSense = ScsiGetSenseKeyAndCodes(senseData,
+ senseInfoBufferLength,
+ SCSI_SENSE_OPTIONS_FIXED_FORMAT_IF_UNKNOWN_FORMAT_INDICATED,
+ &senseKey,
+ &additionalSenseCode,
+ NULL);
+
+ if (validSense &&
+ senseKey == SCSI_SENSE_NOT_READY &&
+ additionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY) {
+ Srb->SrbStatus = SRB_STATUS_SUCCESS;
+ }
+ }
+ }
+
+ //
+ // use InterpretSenseInfo routine to check for media state, and also
+ // to call ClassError() with correct parameters.
+ //
+ status = STATUS_SUCCESS;
+ if (SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_SUCCESS) {
+
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN, "ClasspMediaChangeDetectionCompletion - failed - srb status=%s, sense=%s/%s/%s.",
+ DBGGETSRBSTATUSSTR(Srb), DBGGETSENSECODESTR(Srb), DBGGETADSENSECODESTR(Srb), DBGGETADSENSEQUALIFIERSTR(Srb)));
+
+ InterpretSenseInfoWithoutHistory(DeviceObject,
+ Irp,
+ (PSCSI_REQUEST_BLOCK)Srb,
+ IRP_MJ_SCSI,
+ 0,
+ 0,
+ &status,
+ NULL);
+ }
+ else {
+
+ fdoData->LoggedTURFailureSinceLastIO = FALSE;
+
+ if (!info->Gesn.Supported) {
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClasspMediaChangeDetectionCompletion - succeeded and GESN NOT supported, setting MediaPresent."));
+
+ //
+ // success != media for GESN case
+ //
+
+ ClassSetMediaChangeState(fdoExtension, MediaPresent, FALSE);
+
+ }
+ else {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClasspMediaChangeDetectionCompletion - succeeded (GESN supported)."));
+ }
+ }
+
+ if (info->Gesn.Supported) {
+
+ if (status == STATUS_DATA_OVERRUN) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClasspMediaChangeDetectionCompletion - Overrun"));
+ status = STATUS_SUCCESS;
+ }
+
+ if (!NT_SUCCESS(status)) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClasspMediaChangeDetectionCompletion: GESN failed with status %x", status));
+ } else {
+
+ //
+ // for GESN, need to interpret the results of the data.
+ // this may also require an immediate retry
+ //
+
+ if (Irp->IoStatus.Information == 8 ) {
+ ClasspInterpretGesnData(fdoExtension,
+ (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_EX(fdoExtension, Srb)) {
+ FREE_PORT_ALLOCATED_SENSE_BUFFER_EX(fdoExtension, Srb);
+ }
+
+ //
+ // Remember the IRP and SRB for use the next time.
+ //
+
+ NT_ASSERT(IoGetNextIrpStackLocation(Irp));
+ IoGetNextIrpStackLocation(Irp)->Parameters.Scsi.Srb = (PSCSI_REQUEST_BLOCK)Srb;
+
+ //
+ // Reset the MCN timer.
+ //
+
+ ClassResetMediaChangeTimer(fdoExtension);
+
+ //
+ // 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
+ //
+
+ PIO_WORKITEM workItem;
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClasspMediaChangeDetectionCompletion: Disabling GESN for device %p", DeviceObject));
+
+ workItem = IoAllocateWorkItem(DeviceObject);
+
+ if (workItem) {
+ IoQueueWorkItem(workItem, ClasspDisableGesn, DelayedWorkQueue, workItem);
+ }
+
+ info->Gesn.Supported = 0;
+ info->Gesn.EventMask = 0;
+ info->Gesn.BufferSize = 0;
+ info->MediaChangeRetryCount = 0;
+ retryImmediately = FALSE;
+ }
+
+ } else {
+
+ info->MediaChangeRetryCount = 0;
+
+ }
+
+
+ //
+ // release the remove lock....
+ //
+
+ {
+ UCHAR uniqueValue = 0;
+ ClassAcquireRemoveLock(DeviceObject, (PVOID)(&uniqueValue));
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+
+
+ //
+ // set the irp as not in use
+ //
+ {
+#if DBG
+ volatile LONG irpWasInUse;
+ irpWasInUse = InterlockedCompareExchange(&info->MediaChangeIrpInUse, 0, 1);
+ #if _MSC_FULL_VER != 13009111 // This compiler always takes the wrong path here.
+ NT_ASSERT(irpWasInUse);
+ #endif
+#else
+ InterlockedCompareExchange(&info->MediaChangeIrpInUse, 0, 1);
+#endif
+ }
+
+ //
+ // now send it again before we release our last remove lock
+ //
+
+ if (retryImmediately) {
+ ClasspSendMediaStateIrp(fdoExtension, info, 0);
+ }
+ else {
+ TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN, "ClasspMediaChangeDetectionCompletion - not retrying immediately"));
+ }
+
+ //
+ // release the temporary remove lock
+ //
+
+ ClassReleaseRemoveLock(DeviceObject, (PVOID)(&uniqueValue));
+ }
+
+ TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN, "< ClasspMediaChangeDetectionCompletion"));
+
+ return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClasspSendTestUnitIrp() - ISSUE-2000/02/20-henrygab - not documented
+
+Routine Description:
+
+ This routine
+
+Arguments:
+
+ DeviceObject -
+ Irp -
+
+Return Value:
+
+
+--*/
+PIRP
+ClasspPrepareMcnIrp(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN PMEDIA_CHANGE_DETECTION_INFO Info,
+ IN BOOLEAN UseGesn
+)
+{
+ PSCSI_REQUEST_BLOCK srb;
+ PSTORAGE_REQUEST_BLOCK srbEx;
+ PIO_STACK_LOCATION irpStack;
+ PIO_STACK_LOCATION nextIrpStack;
+ NTSTATUS status;
+ PCDB cdb;
+ PIRP irp;
+ PVOID buffer;
+ UCHAR bufferLength;
+ ULONG srbFlags;
+ ULONG timeOutValue;
+ UCHAR cdbLength;
+ PVOID dataBuffer;
+ ULONG dataTransferLength;
+
+ //
+ // Setup the IRP to perform a test unit ready.
+ //
+
+ irp = Info->MediaChangeIrp;
+
+ if (irp == NULL) {
+ NT_ASSERT(irp);
+ return NULL;
+ }
+
+ //
+ // don't keep sending this if the device is being removed.
+ //
+
+ status = ClassAcquireRemoveLock(FdoExtension->DeviceObject, irp);
+ if (status == REMOVE_COMPLETE) {
+ NT_ASSERT(status != REMOVE_COMPLETE);
+ return NULL;
+ }
+ else if (status == REMOVE_PENDING) {
+ ClassReleaseRemoveLock(FdoExtension->DeviceObject, irp);
+ return NULL;
+ }
+ else {
+ NT_ASSERT(status == NO_REMOVE);
+ }
+
+ IoReuseIrp(irp, STATUS_NOT_SUPPORTED);
+
+ /*
+ * 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 = FdoExtension->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
+ //
+
+ irpStack->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
+
+ nextIrpStack = IoGetNextIrpStackLocation(irp);
+ nextIrpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
+ nextIrpStack->Parameters.Scsi.Srb = &(Info->MediaChangeSrb.Srb);
+
+ //
+ // Prepare the SRB for execution.
+ //
+
+ buffer = Info->SenseBuffer;
+ bufferLength = Info->SenseBufferLength;
+
+ NT_ASSERT(bufferLength > 0);
+ RtlZeroMemory(buffer, bufferLength);
+
+ srbFlags = FdoExtension->SrbFlags;
+ SET_FLAG(srbFlags, Info->SrbFlags);
+
+ timeOutValue = FdoExtension->TimeOutValue * 2;
+ if (timeOutValue == 0) {
+
+ if (FdoExtension->TimeOutValue == 0) {
+
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN,
+ "ClassSendTestUnitIrp: FdoExtension->TimeOutValue "
+ "is set to zero?! -- resetting to 10\n"));
+ timeOutValue = 10 * 2; // reasonable default
+
+ } else {
+
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN,
+ "ClassSendTestUnitIrp: Someone set "
+ "srb->TimeOutValue to zero?! -- resetting to %x\n",
+ FdoExtension->TimeOutValue * 2));
+ timeOutValue = FdoExtension->TimeOutValue * 2;
+
+ }
+
+ }
+
+ if (!UseGesn) {
+ nextIrpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_NONE;
+ irp->MdlAddress = NULL;
+
+ SET_FLAG(srbFlags, SRB_FLAGS_NO_DATA_TRANSFER);
+
+ //
+ // Set SRB_FLAGS_NO_KEEP_AWAKE for non-cdrom devices if these requests should
+ // not prevent devices from going to sleep.
+ //
+ if ((FdoExtension->DeviceObject->DeviceType != FILE_DEVICE_CD_ROM) &&
+ (ClasspScreenOff == TRUE)) {
+ SET_FLAG(srbFlags, SRB_FLAGS_NO_KEEP_AWAKE);
+ }
+
+ cdbLength = 6;
+ dataBuffer = NULL;
+ dataTransferLength = 0;
+
+ } else {
+ NT_ASSERT(Info->Gesn.Buffer);
+
+ nextIrpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_IN;
+ irp->MdlAddress = Info->Gesn.Mdl;
+
+ SET_FLAG(srbFlags, SRB_FLAGS_DATA_IN);
+ cdbLength = 10;
+ dataBuffer = Info->Gesn.Buffer;
+ dataTransferLength = Info->Gesn.BufferSize;
+ timeOutValue = GESN_TIMEOUT_VALUE; // much shorter timeout for GESN
+
+ }
+
+ //
+ // SRB used here is the MediaChangeSrb in _MEDIA_CHANGE_DETECTION_INFO.
+ //
+ srb = nextIrpStack->Parameters.Scsi.Srb;
+ if (FdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ srbEx = (PSTORAGE_REQUEST_BLOCK)nextIrpStack->Parameters.Scsi.Srb;
+
+ status = InitializeStorageRequestBlock(srbEx,
+ STORAGE_ADDRESS_TYPE_BTL8,
+ CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE,
+ 1,
+ SrbExDataTypeScsiCdb16);
+ if (!NT_SUCCESS(status)) {
+ // should not happen
+ NT_ASSERT(FALSE);
+ return NULL;
+ }
+
+ srbEx->RequestTag = SP_UNTAGGED;
+ srbEx->RequestAttribute = SRB_SIMPLE_TAG_REQUEST;
+ srbEx->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
+ srbEx->SrbStatus = 0;
+ srbEx->OriginalRequest = irp;
+ srbEx->SrbFlags = srbFlags;
+ srbEx->TimeOutValue = timeOutValue;
+ srbEx->DataBuffer = dataBuffer;
+ srbEx->DataTransferLength = dataTransferLength;
+
+ SrbSetScsiStatus(srbEx, 0);
+ SrbSetSenseInfoBuffer(srbEx, buffer);
+ SrbSetSenseInfoBufferLength(srbEx, bufferLength);
+ SrbSetCdbLength(srbEx, cdbLength);
+
+ cdb = SrbGetCdb(srbEx);
+
+ } else {
+ RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
+
+ 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->SenseInfoBufferLength = bufferLength;
+ srb->SrbStatus = 0;
+ srb->ScsiStatus = 0;
+ srb->OriginalRequest = irp;
+
+ srb->SrbFlags = srbFlags;
+ srb->TimeOutValue = timeOutValue;
+ srb->CdbLength = cdbLength;
+ srb->DataBuffer = dataBuffer;
+ srb->DataTransferLength = dataTransferLength;
+
+ cdb = (PCDB) &srb->Cdb[0];
+
+ }
+
+ if (cdb) {
+ if (!UseGesn) {
+ cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY;
+ } else {
+ 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;
+ }
+ }
+
+ IoSetCompletionRoutine(irp,
+ ClasspMediaChangeDetectionCompletion,
+ srb,
+ TRUE,
+ TRUE,
+ TRUE);
+
+ return irp;
+
+}
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClasspSendMediaStateIrp() - ISSUE-2000/02/20-henrygab - not documented
+
+Routine Description:
+
+ This routine
+
+Arguments:
+
+ DeviceObject -
+ Irp -
+
+Return Value:
+
+--*/
+VOID
+ClasspSendMediaStateIrp(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN PMEDIA_CHANGE_DETECTION_INFO Info,
+ IN ULONG CountDown
+ )
+{
+ BOOLEAN requestPending = FALSE;
+ LONG irpInUse;
+
+ TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN, "> ClasspSendMediaStateIrp"));
+
+ if (((FdoExtension->CommonExtension.CurrentState != IRP_MN_START_DEVICE) ||
+ (FdoExtension->DevicePowerState != PowerDeviceD0)
+ ) &&
+ (!Info->MediaChangeIrpLost)) {
+
+ //
+ // the device may be stopped, powered down, or otherwise queueing io,
+ // so should not timeout the autorun irp (yet) -- set to zero ticks.
+ // scattered code relies upon this to not prematurely "lose" an
+ // autoplay irp that was queued.
+ //
+
+ Info->MediaChangeIrpTimeInUse = 0;
+ }
+
+ //
+ // if the irp is not in use, mark it as such.
+ //
+
+ irpInUse = InterlockedCompareExchange(&Info->MediaChangeIrpInUse, 1, 0);
+
+ if (irpInUse) {
+
+ LONG timeInUse;
+
+ timeInUse = InterlockedIncrement(&Info->MediaChangeIrpTimeInUse);
+
+ TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN, "ClasspSendMediaStateIrp: irp in use for "
+ "%x seconds when synchronizing for MCD\n", timeInUse));
+
+ if (Info->MediaChangeIrpLost == FALSE) {
+
+ if (timeInUse > MEDIA_CHANGE_TIMEOUT_TIME) {
+
+ //
+ // currently set to five minutes. hard to imagine a drive
+ // taking that long to spin up.
+ //
+
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN,
+ "CdRom%d: Media Change Notification has lost "
+ "it's irp and doesn't know where to find it. "
+ "Leave it alone and it'll come home dragging "
+ "it's stack behind it.\n",
+ FdoExtension->DeviceNumber));
+ Info->MediaChangeIrpLost = TRUE;
+ }
+ }
+
+ TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN, "< ClasspSendMediaStateIrp - irpInUse"));
+ return;
+
+ }
+
+ TRY {
+
+ if (Info->MediaChangeDetectionDisableCount != 0) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClassCheckMediaState: device %p has "
+ " detection disabled \n", FdoExtension->DeviceObject));
+ LEAVE;
+ }
+
+ if (FdoExtension->DevicePowerState != PowerDeviceD0) {
+
+ //
+ // It's possible that the device went to D3 while the screen was
+ // off so we need to make sure that we send the IRP regardless
+ // of the device's power state in order to wake the device back
+ // up when the screen comes back on.
+ // When the screen is off we set the SRB_FLAG_NO_KEEP_AWAKE flag
+ // so that the lower driver does not power-up the device for this
+ // request. When the screen comes back on, however, we want to
+ // resume checking for media presence so we no longer set the flag.
+ // When the device is in D3 we also stop the polling timer as well.
+ //
+
+ //
+ // NOTE: we don't increment the time in use until our power state
+ // changes above. this way, we won't "lose" the autoplay irp.
+ // it's up to the lower driver to determine if powering up is a
+ // good idea.
+ //
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+ "ClassCheckMediaState: device %p needs to powerup "
+ "to handle this io (may take a few extra seconds).\n",
+ FdoExtension->DeviceObject));
+ }
+
+ Info->MediaChangeIrpTimeInUse = 0;
+ Info->MediaChangeIrpLost = FALSE;
+
+ if (CountDown == 0) {
+
+ PIRP irp;
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+ "ClassCheckMediaState: timer expired\n"));
+
+ if (Info->MediaChangeDetectionDisableCount != 0) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+ "ClassCheckMediaState: detection disabled\n"));
+ LEAVE;
+ }
+
+ //
+ // Prepare the IRP for the test unit ready
+ //
+
+ irp = ClasspPrepareMcnIrp(FdoExtension,
+ Info,
+ Info->Gesn.Supported);
+
+ //
+ // Issue the request.
+ //
+
+ TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN,
+ "ClasspSendMediaStateIrp: Device %p getting TUR "
+ " irp %p\n", FdoExtension->DeviceObject, irp));
+
+ if (irp == NULL) {
+ LEAVE;
+ }
+
+
+ //
+ // note: if we send it to the class dispatch routines, there is
+ // a timing window here (since they grab the remove lock)
+ // where we'd be removed. ELIMINATE the window by grabbing
+ // the lock ourselves above and sending it to the lower
+ // device object directly or to the device's StartIo
+ // routine (which doesn't acquire the lock).
+ //
+
+ requestPending = TRUE;
+
+ TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN, " ClasspSendMediaStateIrp - calling IoCallDriver."));
+ IoCallDriver(FdoExtension->CommonExtension.LowerDeviceObject, irp);
+ }
+
+ } FINALLY {
+
+ if(requestPending == FALSE) {
+#if DBG
+ irpInUse = InterlockedCompareExchange(&Info->MediaChangeIrpInUse, 0, 1);
+ #if _MSC_FULL_VER != 13009111 // This compiler always takes the wrong path here.
+ NT_ASSERT(irpInUse);
+ #endif
+#else
+ InterlockedCompareExchange(&Info->MediaChangeIrpInUse, 0, 1);
+#endif
+ }
+
+ }
+
+ TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_MCN, "< ClasspSendMediaStateIrp"));
+
+ return;
+} // end ClasspSendMediaStateIrp()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassCheckMediaState()
+
+Routine Description:
+
+ This routine is called by the class driver to test for a media change
+ condition and/or poll for disk failure prediction. It should be called
+ from the class driver's IO timer routine once per second.
+
+Arguments:
+
+ FdoExtension - the device extension
+
+Return Value:
+
+ none
+
+--*/
+VOID
+ClassCheckMediaState(
+ _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ )
+{
+ PMEDIA_CHANGE_DETECTION_INFO info = FdoExtension->MediaChangeDetectionInfo;
+ LONG countDown;
+
+ if(info == NULL) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+ "ClassCheckMediaState: detection not enabled\n"));
+ return;
+ }
+
+ //
+ // Media change support is active and the IRP is waiting. Decrement the
+ // timer. There is no MP protection on the timer counter. This code
+ // is the only code that will manipulate the timer counter and only one
+ // instance of it should be running at any given time.
+ //
+
+ countDown = InterlockedDecrement(&(info->MediaChangeCountDown));
+
+ //
+ // Try to acquire the media change event. If we can't do it immediately
+ // then bail out and assume the caller will try again later.
+ //
+ ClasspSendMediaStateIrp(FdoExtension,
+ info,
+ countDown);
+
+ return;
+} // end ClassCheckMediaState()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassResetMediaChangeTimer()
+
+Routine Description:
+
+ Resets the media change count down timer to the default number of seconds.
+
+Arguments:
+
+ FdoExtension - the device to reset the timer for
+
+Return Value:
+
+ None
+
+--*/
+VOID
+ClassResetMediaChangeTimer(
+ _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ )
+{
+ PMEDIA_CHANGE_DETECTION_INFO info = FdoExtension->MediaChangeDetectionInfo;
+
+ if(info != NULL) {
+ InterlockedExchange(&(info->MediaChangeCountDown),
+ MEDIA_CHANGE_DEFAULT_TIME);
+ }
+ return;
+} // end ClassResetMediaChangeTimer()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClasspInitializePolling() - ISSUE-2000/02/20-henrygab - not documented
+
+Routine Description:
+
+ This routine
+
+Arguments:
+
+ DeviceObject -
+ Irp -
+
+Return Value:
+
+--*/
+NTSTATUS
+ClasspInitializePolling(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN BOOLEAN AllowDriveToSleep
+ )
+{
+ PDEVICE_OBJECT fdo = FdoExtension->DeviceObject;
+
+ PMEDIA_CHANGE_DETECTION_INFO info;
+ PIRP irp;
+
+ PAGED_CODE();
+
+ if (FdoExtension->MediaChangeDetectionInfo != NULL) {
+ return STATUS_SUCCESS;
+ }
+
+ info = ExAllocatePoolWithTag(NonPagedPoolNx,
+ sizeof(MEDIA_CHANGE_DETECTION_INFO),
+ CLASS_TAG_MEDIA_CHANGE_DETECTION);
+
+ if (info != NULL) {
+ RtlZeroMemory(info, sizeof(MEDIA_CHANGE_DETECTION_INFO));
+
+ FdoExtension->KernelModeMcnContext.FileObject = (PVOID)-1;
+ FdoExtension->KernelModeMcnContext.DeviceObject = (PVOID)-1;
+ FdoExtension->KernelModeMcnContext.LockCount = 0;
+ FdoExtension->KernelModeMcnContext.McnDisableCount = 0;
+
+ /*
+ * Allocate an IRP to carry the Test-Unit-Ready.
+ * Allocate an extra IRP stack location
+ * so we can cache our device object in the top location.
+ */
+ irp = IoAllocateIrp((CCHAR)(fdo->StackSize+1), FALSE);
+
+ if (irp != NULL) {
+
+ PVOID buffer;
+ BOOLEAN GesnSupported = FALSE;
+
+ buffer = ExAllocatePoolWithTag(
+ NonPagedPoolNxCacheAligned,
+ SENSE_BUFFER_SIZE_EX,
+ CLASS_TAG_MEDIA_CHANGE_DETECTION);
+
+ if (buffer != NULL) {
+
+ info->MediaChangeIrp = irp;
+ info->SenseBuffer = buffer;
+ info->SenseBufferLength = SENSE_BUFFER_SIZE_EX;
+
+ //
+ // Set default values for the media change notification
+ // configuration.
+ //
+
+ info->MediaChangeCountDown = MEDIA_CHANGE_DEFAULT_TIME;
+ info->MediaChangeDetectionDisableCount = 0;
+
+ //
+ // Assume that there is initially no media in the device
+ // only notify upper layers if there is something there
+ //
+
+ info->MediaChangeDetectionState = MediaUnknown;
+
+ info->MediaChangeIrpTimeInUse = 0;
+ info->MediaChangeIrpLost = FALSE;
+
+ //
+ // setup all extra flags we'll be setting for this irp
+ //
+ info->SrbFlags = 0;
+ if (AllowDriveToSleep) {
+ SET_FLAG(info->SrbFlags, SRB_FLAGS_NO_KEEP_AWAKE);
+ }
+ SET_FLAG(info->SrbFlags, SRB_CLASS_FLAGS_LOW_PRIORITY);
+ SET_FLAG(info->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
+ SET_FLAG(info->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
+
+ KeInitializeMutex(&info->MediaChangeMutex, 0x100);
+
+ //
+ // It is ok to support media change events on this
+ // device.
+ //
+
+ FdoExtension->MediaChangeDetectionInfo = info;
+
+ //
+ // NOTE: the DeviceType is FILE_DEVICE_CD_ROM even
+ // when the device supports DVD (no need to
+ // check for FILE_DEVICE_DVD, as it's not a
+ // valid check).
+ //
+
+ if (FdoExtension->DeviceObject->DeviceType == FILE_DEVICE_CD_ROM) {
+
+ NTSTATUS status;
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+ "ClasspInitializePolling: Testing for GESN\n"));
+ status = ClasspInitializeGesn(FdoExtension, info);
+ if (NT_SUCCESS(status)) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+ "ClasspInitializePolling: GESN available "
+ "for %p\n", FdoExtension->DeviceObject));
+ NT_ASSERT(info->Gesn.Supported );
+ NT_ASSERT(info->Gesn.Buffer != NULL);
+ NT_ASSERT(info->Gesn.BufferSize != 0);
+ NT_ASSERT(info->Gesn.EventMask != 0);
+ GesnSupported = TRUE;
+ } else {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+ "ClasspInitializePolling: GESN *NOT* available "
+ "for %p\n", FdoExtension->DeviceObject));
+ }
+ }
+
+ if (GesnSupported == FALSE) {
+ NT_ASSERT(info->Gesn.Supported == 0);
+ NT_ASSERT(info->Gesn.Buffer == NULL);
+ NT_ASSERT(info->Gesn.BufferSize == 0);
+ NT_ASSERT(info->Gesn.EventMask == 0);
+ info->Gesn.Supported = 0; // just in case....
+ }
+
+ //
+ // Register for screen state notification. Will use this to
+ // determine user presence.
+ //
+ if (ScreenStateNotificationHandle == NULL) {
+ PoRegisterPowerSettingCallback(fdo,
+ &GUID_CONSOLE_DISPLAY_STATE,
+ &ClasspPowerSettingCallback,
+ NULL,
+ &ScreenStateNotificationHandle);
+ }
+
+ return STATUS_SUCCESS;
+ }
+
+ IoFreeIrp(irp);
+ }
+
+ FREE_POOL(info);
+ }
+
+ //
+ // nothing to free here
+ //
+ return STATUS_INSUFFICIENT_RESOURCES;
+
+} // end ClasspInitializePolling()
+
+NTSTATUS
+ClasspInitializeGesn(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN PMEDIA_CHANGE_DETECTION_INFO Info
+ )
+{
+ PNOTIFICATION_EVENT_STATUS_HEADER header;
+ CLASS_DETECTION_STATE detectionState = ClassDetectionUnknown;
+ PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor;
+ NTSTATUS status = STATUS_NOT_SUPPORTED;
+ PIRP irp;
+ KEVENT event;
+ BOOLEAN retryImmediately;
+ ULONG i;
+ ULONG atapiResets;
+ ULONG srbFlags;
+
+ PAGED_CODE();
+ NT_ASSERT(Info == FdoExtension->MediaChangeDetectionInfo);
+
+ //
+ // read if we already know the abilities of the device
+ //
+
+ ClassGetDeviceParameter(FdoExtension,
+ CLASSP_REG_SUBKEY_NAME,
+ CLASSP_REG_MMC_DETECTION_VALUE_NAME,
+ (PULONG)&detectionState);
+
+ if (detectionState == ClassDetectionUnsupported) {
+ goto ExitWithError;
+ }
+
+ //
+ // check if the device has a hack flag saying never to try this.
+ //
+
+ if (TEST_FLAG(FdoExtension->PrivateFdoData->HackFlags,
+ FDO_HACK_GESN_IS_BAD)) {
+
+ ClassSetDeviceParameter(FdoExtension,
+ CLASSP_REG_SUBKEY_NAME,
+ CLASSP_REG_MMC_DETECTION_VALUE_NAME,
+ ClassDetectionUnsupported);
+ goto ExitWithError;
+
+ }
+
+
+ //
+ // else go through the process since we allocate buffers and
+ // get all sorts of device settings.
+ //
+
+ if (Info->Gesn.Buffer == NULL) {
+ Info->Gesn.Buffer = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
+ GESN_BUFFER_SIZE,
+ '??cS');
+ }
+ if (Info->Gesn.Buffer == NULL) {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ goto ExitWithError;
+ }
+ 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;
+ goto ExitWithError;
+ }
+
+ 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.
+ //
+
+ deviceDescriptor = FdoExtension->DeviceDescriptor;
+ atapiResets = 0;
+ retryImmediately = TRUE;
+ for (i = 0; i < 16 && retryImmediately == TRUE; i++) {
+
+ irp = ClasspPrepareMcnIrp(FdoExtension, Info, TRUE);
+ if (irp == NULL) {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ goto ExitWithError;
+ }
+
+ if (Info->MediaChangeSrb.Srb.Function == SRB_FUNCTION_STORAGE_REQUEST_BLOCK) {
+ srbFlags = Info->MediaChangeSrb.SrbEx.SrbFlags;
+ } else {
+ srbFlags = Info->MediaChangeSrb.Srb.SrbFlags;
+ }
+ NT_ASSERT(TEST_FLAG(srbFlags, SRB_FLAGS_NO_QUEUE_FREEZE));
+
+ //
+ // replace the completion routine with a different one this time...
+ //
+
+ KeInitializeEvent(&event, SynchronizationEvent, FALSE);
+ IoSetCompletionRoutine(irp,
+ ClassSignalCompletion,
+ &event,
+ TRUE, TRUE, TRUE);
+
+ status = IoCallDriver(FdoExtension->CommonExtension.LowerDeviceObject, irp);
+
+ if (status == STATUS_PENDING) {
+ status = KeWaitForSingleObject(&event,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL);
+ NT_ASSERT(NT_SUCCESS(status));
+ }
+ ClassReleaseRemoveLock(FdoExtension->DeviceObject, irp);
+
+ if (SRB_STATUS(Info->MediaChangeSrb.Srb.SrbStatus) != SRB_STATUS_SUCCESS) {
+
+ InterpretSenseInfoWithoutHistory(FdoExtension->DeviceObject,
+ irp,
+ &(Info->MediaChangeSrb.Srb),
+ IRP_MJ_SCSI,
+ 0,
+ 0,
+ &status,
+ NULL);
+ }
+
+ if ((deviceDescriptor->BusType == BusTypeAtapi) &&
+ (Info->MediaChangeSrb.Srb.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;
+ goto ExitWithError;
+ }
+ }
+
+ 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.
+ //
+
+ ClassSetDeviceParameter(FdoExtension,
+ CLASSP_REG_SUBKEY_NAME,
+ CLASSP_REG_MMC_DETECTION_VALUE_NAME,
+ ClassDetectionUnsupported);
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN,
+ "Classpnp => GESN test failed %x for fdo %p\n",
+ status, FdoExtension->DeviceObject));
+ goto ExitWithError;
+
+
+ }
+
+ 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,
+ "Classpnp => GESN test failed %x for fdo %p\n",
+ status, FdoExtension->DeviceObject));
+ goto ExitWithError;
+ }
+
+ 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,
+ "Classpnp => Fdo %p supports event mask %x\n",
+ FdoExtension->DeviceObject, header->SupportedEventClasses));
+
+
+ if (TEST_FLAG(header->SupportedEventClasses,
+ NOTIFICATION_MEDIA_STATUS_CLASS_MASK)) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+ "Classpnp => GESN supports MCN\n"));
+ }
+ if (TEST_FLAG(header->SupportedEventClasses,
+ NOTIFICATION_DEVICE_BUSY_CLASS_MASK)) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+ "Classpnp => GESN supports DeviceBusy\n"));
+ }
+ if (TEST_FLAG(header->SupportedEventClasses,
+ NOTIFICATION_OPERATIONAL_CHANGE_CLASS_MASK)) {
+
+ if (TEST_FLAG(FdoExtension->PrivateFdoData->HackFlags,
+ FDO_HACK_GESN_IGNORE_OPCHANGE)) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+ "Classpnp => 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,
+ "Classpnp => 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,
+ "Classpnp => GESN supported, but not mask we care "
+ "about (%x) for FDO %p\n",
+ header->SupportedEventClasses,
+ FdoExtension->DeviceObject));
+ goto ExitWithError;
+
+ } else if (CountOfSetBitsUChar(Info->Gesn.EventMask) == 1) {
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+ "Classpnp => GESN hack not required for FDO %p\n",
+ FdoExtension->DeviceObject));
+
+ } else {
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+ "Classpnp => GESN hack enabled for FDO %p\n",
+ FdoExtension->DeviceObject));
+ Info->Gesn.HackEventMask = 1;
+
+ }
+
+ } else {
+
+ //
+ // not the first time looping through, so interpret the results.
+ //
+
+ status = ClasspInterpretGesnData(FdoExtension,
+ (PVOID)Info->Gesn.Buffer,
+ &retryImmediately);
+
+ if (!NT_SUCCESS(status)) {
+
+ //
+ // This drive does not support GESN correctly
+ //
+
+ ClassSetDeviceParameter(FdoExtension,
+ CLASSP_REG_SUBKEY_NAME,
+ CLASSP_REG_MMC_DETECTION_VALUE_NAME,
+ ClassDetectionUnsupported);
+ goto ExitWithError;
+ }
+ }
+
+ } // end loop of GESN requests....
+
+ //
+ // 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,
+ "Classpnp => Enabling GESN support for fdo %p\n",
+ FdoExtension->DeviceObject));
+ Info->Gesn.Supported = TRUE;
+
+ ClassSetDeviceParameter(FdoExtension,
+ CLASSP_REG_SUBKEY_NAME,
+ CLASSP_REG_MMC_DETECTION_VALUE_NAME,
+ ClassDetectionSupported);
+
+ return STATUS_SUCCESS;
+
+ }
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+ "Classpnp => GESN available but not enabled for fdo %p\n",
+ FdoExtension->DeviceObject));
+ goto ExitWithError;
+
+ // fall through...
+
+ExitWithError:
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN,
+ "Classpnp => GESN support detection failed for fdo %p with status %08x\n",
+ FdoExtension->DeviceObject, status));
+
+
+ if (Info->Gesn.Mdl) {
+ IoFreeMdl(Info->Gesn.Mdl);
+ Info->Gesn.Mdl = NULL;
+ }
+ FREE_POOL(Info->Gesn.Buffer);
+ Info->Gesn.Supported = 0;
+ Info->Gesn.EventMask = 0;
+ Info->Gesn.BufferSize = 0;
+ return STATUS_NOT_SUPPORTED;
+
+}
+
+
+//
+// Work item to set the hack flag in the registry to disable GESN
+// on devices that sends too many events
+//
+
+VOID
+ClasspDisableGesn(
+ IN PDEVICE_OBJECT Fdo,
+ IN PVOID Context
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+ PIO_WORKITEM WorkItem = (PIO_WORKITEM)Context;
+
+ PAGED_CODE();
+
+ //
+ // Set the hack flag in the registry
+ //
+ ClassSetDeviceParameter(fdoExtension,
+ CLASSP_REG_SUBKEY_NAME,
+ CLASSP_REG_MMC_DETECTION_VALUE_NAME,
+ ClassDetectionUnsupported);
+ _Analysis_assume_(WorkItem != NULL);
+ IoFreeWorkItem(WorkItem);
+}
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassInitializeTestUnitPolling()
+
+Routine Description:
+
+ This routine will initialize MCN regardless of the settings stored
+ in the registry. This should be used with caution, as some devices
+ react badly to constant io. (i.e. never spin down, continuously cycling
+ media in changers, ejection of media, etc.) It is highly suggested to
+ use ClassInitializeMediaChangeDetection() instead.
+
+Arguments:
+
+ FdoExtension is the device to poll
+
+ AllowDriveToSleep says whether to attempt to allow the drive to sleep
+ or not. This only affects system-known spin down states, so if a
+ drive spins itself down, this has no effect until the system spins
+ it down.
+
+Return Value:
+
+--*/
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+ClassInitializeTestUnitPolling(
+ _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ _In_ BOOLEAN AllowDriveToSleep
+ )
+{
+ return ClasspInitializePolling(FdoExtension, AllowDriveToSleep);
+} // end ClassInitializeTestUnitPolling()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassInitializeMediaChangeDetection()
+
+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 ClassInit() callback.
+
+Arguments:
+
+ FdoExtension - the device to initialize MCN for, if appropriate
+
+ EventPrefix - unused, legacy argument. Set to zero.
+
+Return Value:
+
+--*/
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+ClassInitializeMediaChangeDetection(
+ _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ _In_ PUCHAR EventPrefix
+ )
+{
+ PDEVICE_OBJECT fdo = FdoExtension->DeviceObject;
+ NTSTATUS status;
+
+ PCLASS_DRIVER_EXTENSION driverExtension = ClassGetDriverExtension(
+ fdo->DriverObject);
+
+ BOOLEAN disabledForBadHardware;
+ BOOLEAN disabled;
+ BOOLEAN instanceOverride;
+
+ UNREFERENCED_PARAMETER(EventPrefix);
+
+ PAGED_CODE();
+
+ //
+ // NOTE: This assumes that ClassInitializeMediaChangeDetection is always
+ // called in the context of the ClassInitDevice callback. If called
+ // after then this check will have already been made and the
+ // once a second timer will not have been enabled.
+ //
+
+ disabledForBadHardware = ClasspIsMediaChangeDisabledDueToHardwareLimitation(
+ FdoExtension,
+ &(driverExtension->RegistryPath)
+ );
+
+ if (disabledForBadHardware) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+ "ClassInitializeMCN: Disabled due to hardware"
+ "limitations for this device"));
+ return;
+ }
+
+ //
+ // autorun should now be enabled by default for all media types.
+ //
+
+ disabled = ClasspIsMediaChangeDisabledForClass(
+ FdoExtension,
+ &(driverExtension->RegistryPath)
+ );
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+ "ClassInitializeMCN: Class MCN is %s\n",
+ (disabled ? "disabled" : "enabled")));
+
+ status = ClasspMediaChangeDeviceInstanceOverride(
+ FdoExtension,
+ &instanceOverride); // default value
+
+ if (!NT_SUCCESS(status)) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+ "ClassInitializeMCN: Instance using default\n"));
+ } else {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+ "ClassInitializeMCN: Instance override: %s MCN\n",
+ (instanceOverride ? "Enabling" : "Disabling")));
+ disabled = !instanceOverride;
+ }
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+ "ClassInitializeMCN: Instance MCN is %s\n",
+ (disabled ? "disabled" : "enabled")));
+
+ if (disabled) {
+ return;
+ }
+
+ //
+ // Do not allow drive to sleep for all types of devices initially.
+ // For non-cdrom devices, allow devices to go to sleep if it's
+ // unlikely a media change will occur (e.g. user not present).
+ //
+ ClasspInitializePolling(FdoExtension, FALSE);
+
+ return;
+} // end ClassInitializeMediaChangeDetection()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClasspMediaChangeDeviceInstanceOverride()
+
+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:
+
+ FdoExtension - the device to set/get the value for
+ Value - the value to use in a set
+ SetValue - whether to set the value
+
+Return Value:
+
+ TRUE - Autorun is disabled
+ FALSE - Autorun is not disabled (Default)
+
+--*/
+NTSTATUS
+ClasspMediaChangeDeviceInstanceOverride(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ OUT PBOOLEAN Enabled
+ )
+{
+ HANDLE deviceParameterHandle = NULL; // cdrom instance key
+ HANDLE driverParameterHandle = NULL; // cdrom specific key
+ RTL_QUERY_REGISTRY_TABLE queryTable[3];
+ OBJECT_ATTRIBUTES objectAttributes;
+ UNICODE_STRING subkeyName;
+ NTSTATUS status = STATUS_UNSUCCESSFUL;
+ ULONG alwaysEnable = FALSE;
+ ULONG alwaysDisable = FALSE;
+ ULONG i;
+
+ PAGED_CODE();
+
+ TRY {
+
+ status = IoOpenDeviceRegistryKey( FdoExtension->LowerPdo,
+ PLUGPLAY_REGKEY_DEVICE,
+ KEY_ALL_ACCESS,
+ &deviceParameterHandle
+ );
+ 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,
+ "ClassMediaChangeDeviceInstanceDisabled: "
+ "Could not open device registry key [%lx]\n", status));
+ LEAVE;
+ }
+
+ RtlInitUnicodeString(&subkeyName, MCN_REG_SUBKEY_NAME);
+ InitializeObjectAttributes(&objectAttributes,
+ &subkeyName,
+ OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+ deviceParameterHandle,
+ (PSECURITY_DESCRIPTOR) NULL);
+
+ status = ZwCreateKey(&driverParameterHandle,
+ KEY_READ,
+ &objectAttributes,
+ 0,
+ (PUNICODE_STRING) NULL,
+ REG_OPTION_NON_VOLATILE,
+ NULL);
+
+ if (!NT_SUCCESS(status)) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+ "ClassMediaChangeDeviceInstanceDisabled: "
+ "subkey could not be created. %lx\n", status));
+ LEAVE;
+ }
+
+ //
+ // Default to not changing autorun behavior, based upon setting
+ // registryValue to zero.
+ //
+
+ for (i=0;i<2;i++) {
+
+ RtlZeroMemory(&queryTable[0], sizeof(queryTable));
+
+ queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
+ queryTable[0].DefaultType = REG_DWORD;
+ queryTable[0].DefaultLength = 0;
+
+ if (i==0) {
+ queryTable[0].Name = MCN_REG_AUTORUN_DISABLE_INSTANCE_NAME;
+ queryTable[0].EntryContext = &alwaysDisable;
+ queryTable[0].DefaultData = &alwaysDisable;
+ } else {
+ queryTable[0].Name = MCN_REG_AUTORUN_ENABLE_INSTANCE_NAME;
+ queryTable[0].EntryContext = &alwaysEnable;
+ queryTable[0].DefaultData = &alwaysEnable;
+ }
+
+ //
+ // don't care if it succeeds, since we set defaults above
+ //
+
+ RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
+ (PWSTR)driverParameterHandle,
+ queryTable,
+ NULL,
+ NULL);
+ }
+
+ } FINALLY {
+
+ if (driverParameterHandle) ZwClose(driverParameterHandle);
+ if (deviceParameterHandle) ZwClose(deviceParameterHandle);
+
+ }
+
+ if (alwaysEnable && alwaysDisable) {
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+ "ClassMediaChangeDeviceInstanceDisabled: %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,
+ "ClassMediaChangeDeviceInstanceDisabled: %s selected\n",
+ "DISABLE"));
+ NT_ASSERT(NT_SUCCESS(status));
+ status = STATUS_SUCCESS;
+ *Enabled = FALSE;
+
+ } else if (alwaysEnable) {
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+ "ClassMediaChangeDeviceInstanceDisabled: %s selected\n",
+ "ENABLE"));
+ NT_ASSERT(NT_SUCCESS(status));
+ status = STATUS_SUCCESS;
+ *Enabled = TRUE;
+
+ } else {
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+ "ClassMediaChangeDeviceInstanceDisabled: %s selected\n",
+ "DEFAULT"));
+ status = STATUS_UNSUCCESSFUL;
+
+ }
+
+ return status;
+
+} // end ClasspMediaChangeDeviceInstanceOverride()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClasspIsMediaChangeDisabledDueToHardwareLimitation()
+
+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.
+
+Arguments:
+
+ FdoExtension -
+ RegistryPath - pointer to the unicode string inside
+ ...\CurrentControlSet\Services\Cdrom
+
+Return Value:
+
+ TRUE - no autorun.
+ FALSE - Autorun may be enabled
+
+--*/
+BOOLEAN
+ClasspIsMediaChangeDisabledDueToHardwareLimitation(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN PUNICODE_STRING RegistryPath
+ )
+{
+ PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor = FdoExtension->DeviceDescriptor;
+ OBJECT_ATTRIBUTES objectAttributes = {0};
+ HANDLE serviceKey = NULL;
+ RTL_QUERY_REGISTRY_TABLE parameters[2] = {0};
+
+ UNICODE_STRING deviceUnicodeString;
+ ANSI_STRING deviceString;
+ ULONG mediaChangeNotificationDisabled = FALSE;
+
+ NTSTATUS status;
+
+
+ PAGED_CODE();
+
+ //
+ // open the service key.
+ //
+
+ InitializeObjectAttributes(&objectAttributes,
+ RegistryPath,
+ OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+ NULL,
+ NULL);
+
+ status = ZwOpenKey(&serviceKey,
+ KEY_READ,
+ &objectAttributes);
+
+ NT_ASSERT(NT_SUCCESS(status));
+
+
+ if(!NT_SUCCESS(status)) {
+
+ //
+ // always take the safe path. if we can't open the service key,
+ // disable autorun
+ //
+
+ return TRUE;
+
+ }
+
+ TRY {
+ //
+ // 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;
+ PUCHAR productId;
+ PUCHAR revisionId;
+ ULONG length;
+ ULONG offset;
+
+ deviceString.Buffer = NULL;
+ deviceUnicodeString.Buffer = NULL;
+
+ //
+ // there may be nothing to check against
+ //
+
+ if ((deviceDescriptor->VendorIdOffset == 0) &&
+ (deviceDescriptor->ProductIdOffset == 0)) {
+ LEAVE;
+ }
+
+ length = 0;
+
+ if (deviceDescriptor->VendorIdOffset == 0) {
+ vendorId = NULL;
+ } else {
+ vendorId = (PUCHAR) deviceDescriptor + deviceDescriptor->VendorIdOffset;
+ length = (ULONG)strlen((PCSZ)vendorId);
+ }
+
+ if ( deviceDescriptor->ProductIdOffset == 0 ) {
+ productId = NULL;
+ } else {
+ productId = (PUCHAR)deviceDescriptor + deviceDescriptor->ProductIdOffset;
+ length += (ULONG)strlen((PCSZ)productId);
+ }
+
+ if ( deviceDescriptor->ProductRevisionOffset == 0 ) {
+ revisionId = NULL;
+ } else {
+ revisionId = (PUCHAR) deviceDescriptor + deviceDescriptor->ProductRevisionOffset;
+ length += (ULONG)strlen((PCSZ)revisionId);
+ }
+
+ //
+ // allocate a buffer for the string
+ //
+
+ deviceString.Length = (USHORT)( length );
+ deviceString.MaximumLength = deviceString.Length + 1;
+ deviceString.Buffer = (PCHAR)ExAllocatePoolWithTag( NonPagedPoolNx,
+ deviceString.MaximumLength,
+ CLASS_TAG_AUTORUN_DISABLE
+ );
+ if (deviceString.Buffer == NULL) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+ "ClassMediaChangeDisabledForHardware: Unable to alloc "
+ "string buffer\n" ));
+ LEAVE;
+ }
+
+ //
+ // copy strings to the buffer
+ //
+ offset = 0;
+
+ if (vendorId != NULL) {
+ RtlCopyMemory(deviceString.Buffer + offset,
+ vendorId,
+ strlen((PCSZ)vendorId));
+ offset += (ULONG)strlen((PCSZ)vendorId);
+ }
+
+ if ( productId != NULL ) {
+ RtlCopyMemory(deviceString.Buffer + offset,
+ productId,
+ strlen((PCSZ)productId));
+ offset += (ULONG)strlen((PCSZ)productId);
+ }
+ if ( revisionId != NULL ) {
+ RtlCopyMemory(deviceString.Buffer + offset,
+ revisionId,
+ strlen((PCSZ)revisionId));
+ offset += (ULONG)strlen((PCSZ)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,
+ "ClassMediaChangeDisabledForHardware: cannot convert "
+ "to unicode %lx\n", status));
+ LEAVE;
+ }
+
+ //
+ // query the value, setting valueFound to true if found
+ //
+ nullMultiSz = L"\0";
+ parameters[0].QueryRoutine = ClasspMediaChangeRegistryCallBack;
+ 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);
+
+ if ( !NT_SUCCESS(status) ) {
+ LEAVE;
+ }
+
+ } FINALLY {
+
+ FREE_POOL( deviceString.Buffer );
+ if (deviceUnicodeString.Buffer != NULL) {
+ RtlFreeUnicodeString( &deviceUnicodeString );
+ }
+
+ ZwClose(serviceKey);
+ }
+
+ if (mediaChangeNotificationDisabled) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClassMediaChangeDisabledForHardware: "
+ "Device is on disable list\n"));
+ return TRUE;
+ }
+ return FALSE;
+
+} // end ClasspIsMediaChangeDisabledDueToHardwareLimitation()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClasspIsMediaChangeDisabledForClass()
+
+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:
+
+ FdoExtension -
+ RegistryPath - pointer to the unicode string inside
+ ...\CurrentControlSet\Services\Cdrom
+
+Return Value:
+
+ TRUE - Autorun is disabled for this class
+ FALSE - Autorun is enabled for this class
+
+--*/
+BOOLEAN
+ClasspIsMediaChangeDisabledForClass(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN PUNICODE_STRING RegistryPath
+ )
+{
+ OBJECT_ATTRIBUTES objectAttributes = {0};
+ HANDLE serviceKey = NULL;
+ HANDLE parametersKey = NULL;
+ RTL_QUERY_REGISTRY_TABLE parameters[3] = {0};
+
+ UNICODE_STRING paramStr;
+
+ //
+ // Default to ENABLING MediaChangeNotification (!)
+ //
+
+ ULONG mcnRegistryValue = 1;
+
+ NTSTATUS status;
+
+
+ PAGED_CODE();
+
+ //
+ // open the service key.
+ //
+
+ InitializeObjectAttributes(&objectAttributes,
+ RegistryPath,
+ OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+ NULL,
+ NULL);
+
+ status = ZwOpenKey(&serviceKey,
+ KEY_READ,
+ &objectAttributes);
+
+ NT_ASSERT(NT_SUCCESS(status));
+
+ 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, "ClassCheckServiceMCN: Defaulting to %s\n",
+ (mcnRegistryValue ? "Enabled" : "Disabled")));
+ return (BOOLEAN)(!mcnRegistryValue);
+
+ }
+
+ //
+ // Open the parameters key (if any) beneath the services key.
+ //
+
+ RtlInitUnicodeString(&paramStr, L"Parameters");
+
+ InitializeObjectAttributes(&objectAttributes,
+ &paramStr,
+ OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+ serviceKey,
+ NULL);
+
+ status = ZwOpenKey(&parametersKey,
+ KEY_READ,
+ &objectAttributes);
+
+ if (!NT_SUCCESS(status)) {
+ parametersKey = NULL;
+ }
+
+
+
+ //
+ // Check for the Autorun value.
+ //
+
+ parameters[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
+ parameters[0].Name = L"Autorun";
+ parameters[0].EntryContext = &mcnRegistryValue;
+ parameters[0].DefaultType = REG_DWORD;
+ parameters[0].DefaultData = &mcnRegistryValue;
+ parameters[0].DefaultLength = sizeof(ULONG);
+
+ // ignore failures
+ RtlQueryRegistryValues(RTL_REGISTRY_HANDLE | RTL_REGISTRY_OPTIONAL,
+ serviceKey,
+ parameters,
+ NULL,
+ NULL);
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClassCheckServiceMCN: "
+ "<Service>/Autorun flag = %d\n", mcnRegistryValue));
+
+ if(parametersKey != NULL) {
+
+ // ignore failures
+ RtlQueryRegistryValues(RTL_REGISTRY_HANDLE | RTL_REGISTRY_OPTIONAL,
+ parametersKey,
+ parameters,
+ NULL,
+ NULL);
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClassCheckServiceMCN: "
+ "<Service>/Parameters/Autorun flag = %d\n",
+ mcnRegistryValue));
+ ZwClose(parametersKey);
+
+ }
+ ZwClose(serviceKey);
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClassCheckServiceMCN: "
+ "Autoplay for device %p is %s\n",
+ FdoExtension->DeviceObject,
+ (mcnRegistryValue ? "on" : "off")
+ ));
+
+ //
+ // return if it is _disabled_, which is the
+ // inverse of the registry setting
+ //
+
+ return (BOOLEAN)(!mcnRegistryValue);
+} // end ClasspIsMediaChangeDisabledForClass()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassEnableMediaChangeDetection() ISSUE-2000/02/20-henrygab - why public?
+ClassEnableMediaChangeDetection() ISSUE-2000/02/20-henrygab - not documented
+
+Routine Description:
+
+ This routine
+
+Arguments:
+
+ DeviceObject -
+ Irp -
+
+Return Value:
+
+--*/
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+ClassEnableMediaChangeDetection(
+ _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ )
+{
+ PMEDIA_CHANGE_DETECTION_INFO info = FdoExtension->MediaChangeDetectionInfo;
+ LONG oldCount;
+
+ PAGED_CODE();
+
+ if(info == NULL) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+ "ClassEnableMediaChangeDetection: not initialized\n"));
+ return;
+ }
+
+ (VOID)KeWaitForMutexObject(&info->MediaChangeMutex,
+ UserRequest,
+ KernelMode,
+ FALSE,
+ NULL);
+
+ oldCount = --info->MediaChangeDetectionDisableCount;
+
+ NT_ASSERT(oldCount >= 0);
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClassEnableMediaChangeDetection: Disable count "
+ "reduced to %d - ",
+ info->MediaChangeDetectionDisableCount));
+
+ if(oldCount == 0) {
+
+ //
+ // We don't know what state the media is in anymore.
+ //
+
+ ClasspInternalSetMediaChangeState(FdoExtension,
+ MediaUnknown,
+ FALSE
+ );
+
+ //
+ // Reset the MCN timer.
+ //
+
+ ClassResetMediaChangeTimer(FdoExtension);
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "MCD is enabled\n"));
+
+ } else {
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "MCD still disabled\n"));
+
+ }
+
+
+ //
+ // Let something else run.
+ //
+
+ KeReleaseMutex(&info->MediaChangeMutex, FALSE);
+
+ return;
+} // end ClassEnableMediaChangeDetection()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassDisableMediaChangeDetection() ISSUE-2000/02/20-henrygab - why public?
+ClassDisableMediaChangeDetection() ISSUE-2000/02/20-henrygab - not documented
+
+Routine Description:
+
+ This routine
+
+Arguments:
+
+ DeviceObject -
+ Irp -
+
+Return Value:
+
+--*/
+ULONG BreakOnMcnDisable = FALSE;
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+ClassDisableMediaChangeDetection(
+ _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ )
+{
+ PMEDIA_CHANGE_DETECTION_INFO info = FdoExtension->MediaChangeDetectionInfo;
+
+ PAGED_CODE();
+
+ if(info == NULL) {
+ return;
+ }
+
+ (VOID)KeWaitForMutexObject(&info->MediaChangeMutex,
+ UserRequest,
+ KernelMode,
+ FALSE,
+ NULL);
+
+ info->MediaChangeDetectionDisableCount++;
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClassDisableMediaChangeDetection: "
+ "disable count is %d\n",
+ info->MediaChangeDetectionDisableCount));
+
+ KeReleaseMutex(&info->MediaChangeMutex, FALSE);
+
+ return;
+} // end ClassDisableMediaChangeDetection()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassCleanupMediaChangeDetection() ISSUE-2000/02/20-henrygab - why public?!
+
+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:
+
+Return Value:
+
+--*/
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+ClassCleanupMediaChangeDetection(
+ _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ )
+{
+ PMEDIA_CHANGE_DETECTION_INFO info = FdoExtension->MediaChangeDetectionInfo;
+
+ PAGED_CODE()
+
+ if(info == NULL) {
+ return;
+ }
+
+ FdoExtension->MediaChangeDetectionInfo = NULL;
+
+ if (info->Gesn.Mdl) {
+ IoFreeMdl(info->Gesn.Mdl);
+ }
+ FREE_POOL(info->Gesn.Buffer);
+ IoFreeIrp(info->MediaChangeIrp);
+ FREE_POOL(info->SenseBuffer);
+ FREE_POOL(info);
+ return;
+} // end ClassCleanupMediaChangeDetection()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClasspMcnControl() - ISSUE-2000/02/20-henrygab - not documented
+
+Routine Description:
+
+ This routine
+
+Arguments:
+
+ DeviceObject -
+ Irp -
+
+Return Value:
+
+--*/
+NTSTATUS
+ClasspMcnControl(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN PIRP Irp,
+ IN PSCSI_REQUEST_BLOCK Srb
+ )
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension =
+ (PCOMMON_DEVICE_EXTENSION) FdoExtension;
+
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
+ PPREVENT_MEDIA_REMOVAL request = Irp->AssociatedIrp.SystemBuffer;
+
+ PFILE_OBJECT fileObject = irpStack->FileObject;
+ PFILE_OBJECT_EXTENSION fsContext = NULL;
+
+ NTSTATUS status = STATUS_SUCCESS;
+
+ PAGED_CODE();
+
+ //
+ // Check to make sure we have a file object extension to keep track of this
+ // request. If not we'll fail it before synchronizing.
+ //
+
+ TRY {
+
+ if(fileObject != NULL) {
+ fsContext = ClassGetFsContext(commonExtension, fileObject);
+ }else if(Irp->RequestorMode == KernelMode) { // && fileObject == NULL
+ fsContext = &FdoExtension->KernelModeMcnContext;
+ }
+
+ if (fsContext == NULL) {
+
+ //
+ // This handle isn't setup correctly. We can't let the
+ // operation go.
+ //
+
+ status = STATUS_INVALID_PARAMETER;
+ LEAVE;
+ }
+
+ if(request->PreventMediaRemoval) {
+
+ //
+ // This is a lock command. Reissue the command in case bus or
+ // device was reset and the lock was cleared.
+ //
+
+ ClassDisableMediaChangeDetection(FdoExtension);
+ InterlockedIncrement((volatile LONG *)&(fsContext->McnDisableCount));
+
+ } else {
+
+ if(fsContext->McnDisableCount == 0) {
+ status = STATUS_INVALID_DEVICE_STATE;
+ LEAVE;
+ }
+
+ InterlockedDecrement((volatile LONG *)&(fsContext->McnDisableCount));
+ ClassEnableMediaChangeDetection(FdoExtension);
+ }
+
+ } FINALLY {
+
+ Irp->IoStatus.Status = status;
+
+ FREE_POOL(Srb);
+
+ ClassReleaseRemoveLock(FdoExtension->DeviceObject, Irp);
+ ClassCompleteRequest(FdoExtension->DeviceObject,
+ Irp,
+ IO_NO_INCREMENT);
+ }
+ return status;
+} // end ClasspMcnControl(
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClasspMediaChangeRegistryCallBack()
+
+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
+
+--*/
+_Function_class_(RTL_QUERY_REGISTRY_ROUTINE)
+_IRQL_requires_max_(PASSIVE_LEVEL)
+_IRQL_requires_same_
+NTSTATUS
+ClasspMediaChangeRegistryCallBack(
+ _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
+ )
+{
+ PULONG valueFound;
+ PUNICODE_STRING deviceString;
+ PWSTR keyValue;
+
+ PAGED_CODE();
+ UNREFERENCED_PARAMETER(ValueName);
+
+ if (ValueData == NULL ||
+ Context == NULL ||
+ EntryContext == NULL) {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // if we have already set the value to true, exit
+ //
+
+ valueFound = EntryContext;
+ if ((*valueFound) != 0) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClasspMcnRegCB: already set to true\n"));
+ return STATUS_SUCCESS;
+ }
+
+ if (ValueLength == sizeof(WCHAR)) {
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN, "ClasspMcnRegCB: 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 the strings match, disable autorun
+ //
+
+ if (RtlCompareMemory(deviceString->Buffer, keyValue, ValueLength) == ValueLength) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClasspRegMcnCB: Match found\n"));
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClasspRegMcnCB: DeviceString at %p\n",
+ deviceString->Buffer));
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClasspRegMcnCB: KeyValue at %p\n",
+ keyValue));
+ (*valueFound) = TRUE;
+ }
+
+ return STATUS_SUCCESS;
+} // end ClasspMediaChangeRegistryCallBack()
+
+#if (NTDDI_VERSION >= NTDDI_WINBLUE)
+VOID
+ClasspTimerTickEx(
+ _In_ PEX_TIMER Timer,
+ _In_opt_ PVOID Context
+)
+{
+ KDPC dummyDpc = { 0 };
+
+ UNREFERENCED_PARAMETER(Timer);
+ //
+ // This is just a wrapper around ClasspTimerTick that allows us to make
+ // the TickTimer a no-wake EX_TIMER.
+ // We pass in a dummy DPC b/c ClasspTimerTick expects a non-NULL parameter
+ // for the DPC. However, ClasspTimerTick does not actually reference it.
+ //
+ ClasspTimerTick(&dummyDpc, Context, NULL, NULL);
+}
+#endif
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClasspTimerTick() - ISSUE-2000/02/20-henrygab - not documented
+
+Routine Description:
+
+ This routine
+
+Arguments:
+
+ DeviceObject -
+ Irp -
+
+Return Value:
+
+--*/
+_Function_class_(KDEFERRED_ROUTINE)
+_IRQL_requires_max_(DISPATCH_LEVEL)
+_IRQL_requires_min_(DISPATCH_LEVEL)
+_IRQL_requires_(DISPATCH_LEVEL)
+_IRQL_requires_same_
+VOID
+ClasspTimerTick(
+ _In_ PKDPC Dpc,
+ _In_opt_ PVOID DeferredContext,
+ _In_opt_ PVOID SystemArgument1,
+ _In_opt_ PVOID SystemArgument2
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeferredContext;
+ PCOMMON_DEVICE_EXTENSION commonExtension;
+ PDEVICE_OBJECT DeviceObject;
+ ULONG isRemoved;
+
+ UNREFERENCED_PARAMETER(Dpc);
+ UNREFERENCED_PARAMETER(SystemArgument1);
+ UNREFERENCED_PARAMETER(SystemArgument2);
+
+ NT_ASSERT(fdoExtension != NULL);
+ _Analysis_assume_(fdoExtension != NULL);
+
+ commonExtension = &fdoExtension->CommonExtension;
+ DeviceObject = fdoExtension->DeviceObject;
+ NT_ASSERT(commonExtension->IsFdo);
+
+ //
+ // Do any media change work
+ //
+#pragma warning(suppress:4054) // okay to type cast function pointer to PIRP for this use case
+ isRemoved = ClassAcquireRemoveLock(DeviceObject, (PIRP)ClasspTimerTick);
+
+ //
+ // We stop the timer before deleting the device. It's safe to keep going
+ // if the flag value is REMOVE_PENDING because the removal thread will be
+ // blocked trying to stop the timer.
+ //
+
+ NT_ASSERT(isRemoved != REMOVE_COMPLETE);
+
+ //
+ // This routine is reasonably safe even if the device object has a pending
+ // remove
+
+ if (!isRemoved) {
+
+ PFAILURE_PREDICTION_INFO info = fdoExtension->FailurePredictionInfo;
+
+ //
+ // Do any media change detection work
+ //
+
+ if ((fdoExtension->MediaChangeDetectionInfo != NULL) &&
+ (fdoExtension->FunctionSupportInfo->AsynchronousNotificationSupported == FALSE)) {
+
+ ClassCheckMediaState(fdoExtension);
+
+ }
+
+ //
+ // Do any failure prediction work
+ //
+ if ((info != NULL) && (info->Method != FailurePredictionNone)) {
+
+ ULONG countDown;
+
+ if (ClasspCanSendPollingIrp(fdoExtension)) {
+
+ //
+ // Synchronization is not required here since the Interlocked
+ // locked instruction guarantees atomicity. Other code that
+ // resets CountDown uses InterlockedExchange which is also
+ // atomic.
+ //
+ countDown = InterlockedDecrement((volatile LONG *)&info->CountDown);
+ if (countDown == 0) {
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClasspTimerTick: Send FP irp for %p\n",
+ DeviceObject));
+
+ if(info->WorkQueueItem == NULL) {
+
+ info->WorkQueueItem =
+ IoAllocateWorkItem(fdoExtension->DeviceObject);
+
+ if(info->WorkQueueItem == NULL) {
+
+ //
+ // Set the countdown to one minute in the future.
+ // we'll try again then in the hopes there's more
+ // free memory.
+ //
+
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN, "ClassTimerTick: Couldn't allocate "
+ "item - try again in one minute\n"));
+ InterlockedExchange((volatile LONG *)&info->CountDown, 60);
+
+ } else {
+
+ //
+ // Grab the remove lock so that removal will block
+ // until the work item is done.
+ //
+
+ ClassAcquireRemoveLock(fdoExtension->DeviceObject,
+ info->WorkQueueItem);
+
+ IoQueueWorkItem(info->WorkQueueItem,
+ ClasspFailurePredict,
+ DelayedWorkQueue,
+ info);
+ }
+
+ } else {
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClasspTimerTick: Failure "
+ "Prediction work item is "
+ "already active for device %p\n",
+ DeviceObject));
+
+ }
+ } // end (countdown == 0)
+
+ } else {
+ //
+ // If device is sleeping then just rearm polling timer
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClassTimerTick, SHHHH!!! device is %p is sleeping\n",
+ DeviceObject));
+ }
+
+ } // end failure prediction polling
+
+ //
+ // Give driver a chance to do its own specific work
+ //
+
+ if (commonExtension->DriverExtension->InitData.ClassTick != NULL) {
+
+ commonExtension->DriverExtension->InitData.ClassTick(DeviceObject);
+
+ } // end device specific tick handler
+ } // end check for removed
+
+#pragma warning(suppress:4054) // okay to type cast function pointer to PIRP for this use case
+ ClassReleaseRemoveLock(DeviceObject, (PIRP)ClasspTimerTick);
+} // end ClasspTimerTick()
+
+#if (NTDDI_VERSION >= NTDDI_WINBLUE)
+BOOLEAN
+ClasspUpdateTimerNoWakeTolerance(
+ _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+)
+/*
+Routine Description:
+
+ Updates the no-wake timer's tolerance based on system state.
+
+ If the timer is not allocated, initialized, or enabled then this function
+ does nothing.
+
+ If the timer is enabled but the no-wake tolerance has *not* changed from
+ its previous value then this function does nothing.
+
+ If the timer is enabled and the no-wake tolerance has changed from its
+ previous value then this function *will* set/reset the tick timer.
+
+Arguments:
+
+ FdoExtension for the device that has the timer whose tolerance needs updating.
+
+Returns:
+
+ TRUE if the timer was set/reset.
+ FALSE if the timer was not set/reset.
+
+*/
+{
+ PCLASS_PRIVATE_FDO_DATA fdoData = NULL;
+
+ if (FdoExtension->CommonExtension.IsFdo) {
+ fdoData = FdoExtension->PrivateFdoData;
+ }
+
+ if (fdoData != NULL &&
+ fdoData->TickTimer != NULL &&
+ fdoData->TimerInitialized &&
+ fdoData->TickTimerEnabled) {
+
+ LONGLONG noWakeTolerance = TICK_TIMER_DELAY_IN_MSEC * (10 * 1000);
+
+ //
+ // Set the no-wake tolerance to "unlimited" if the conditions below
+ // are met. An "unlimited" no-wake tolerance means that the timer
+ // will *never* wake the processor if the processor is in a
+ // low-power state.
+ // 1. The screen is off.
+ // 2. The class driver is *not* a consumer of the tick timer (ClassTick is NULL).
+ // 3. This is a disk device.
+ // Otherwise the tolerance is set to the normal, default tolerable delay.
+ //
+ if (ClasspScreenOff &&
+ FdoExtension->CommonExtension.DriverExtension->InitData.ClassTick == NULL &&
+ FdoExtension->DeviceObject->DeviceType == FILE_DEVICE_DISK) {
+ noWakeTolerance = EX_TIMER_UNLIMITED_TOLERANCE;
+ }
+
+ //
+ // The new tolerance is different from the current tolerance so we need
+ // to set/reset the timer with the new tolerance value.
+ //
+ if (fdoData->CurrentNoWakeTolerance != noWakeTolerance) {
+ EXT_SET_PARAMETERS parameters;
+ LONGLONG period = TICK_TIMER_PERIOD_IN_MSEC * (10 * 1000); // Convert to units of 100ns.
+ LONGLONG dueTime = period * (-1); // Negative sign indicates dueTime is relative.
+
+ ExInitializeSetTimerParameters(&parameters);
+ parameters.NoWakeTolerance = noWakeTolerance;
+ fdoData->CurrentNoWakeTolerance = noWakeTolerance;
+
+ ExSetTimer(fdoData->TickTimer,
+ dueTime,
+ period,
+ &parameters);
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+#endif
+
+NTSTATUS
+ClasspInitializeTimer(
+ _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+)
+/*
+Routine Description:
+
+ This routine will attempt to initialize the tick timer.
+ The caller should call ClasspEnableTmer() to actually start the timer.
+
+ If the caller just needs to check if the timer is initialized, the caller
+ should simply check FdoExtension->PrivateFdoData->TimerInitialized rather
+ than call this function.
+
+ The caller should subsequently call ClasspDeleteTimer() when they are done
+ with the timer.
+
+Arguments:
+
+ FdoExtension
+
+Return Value:
+
+ STATUS_SUCCESS if the timer is initialized (the timer may already have been
+ initialized by a previous call).
+ A non-success status if the timer is not initialized.
+
+*/
+{
+ PCLASS_PRIVATE_FDO_DATA fdoData = NULL;
+
+ if (FdoExtension->CommonExtension.IsFdo) {
+ fdoData = FdoExtension->PrivateFdoData;
+ }
+
+ if (fdoData == NULL) {
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ if (fdoData->TimerInitialized == FALSE) {
+#if (NTDDI_VERSION >= NTDDI_WINBLUE)
+ NT_ASSERT(fdoData->TickTimer == NULL);
+ //
+ // The tick timer is a no-wake timer, which means it will not wake
+ // the processor while the processor is in a low power state until
+ // the timer's no-wake tolerance is reached.
+ //
+ fdoData->TickTimer = ExAllocateTimer(ClasspTimerTickEx, FdoExtension, EX_TIMER_NO_WAKE);
+ if (fdoData->TickTimer == NULL) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+#else
+ KeInitializeDpc(&fdoData->TickTimerDpc, ClasspTimerTick, FdoExtension);
+ KeInitializeTimer(&fdoData->TickTimer);
+#endif
+ fdoData->TimerInitialized = TRUE;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+VOID
+ClasspDeleteTimer(
+ _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+)
+/*
+Routine Description:
+
+ This routine will attempt to de-initialize and free the tick timer.
+ This routine should only be called after a successful call to
+ ClasspInitializeTimer().
+
+Arguments:
+
+ FdoExtension
+
+Return Value:
+
+ None.
+
+*/
+{
+ PCLASS_PRIVATE_FDO_DATA fdoData = NULL;
+
+ if (FdoExtension->CommonExtension.IsFdo) {
+ fdoData = FdoExtension->PrivateFdoData;
+ if (fdoData != NULL) {
+#if (NTDDI_VERSION >= NTDDI_WINBLUE)
+ if (fdoData->TickTimer != NULL) {
+ EXT_DELETE_PARAMETERS parameters;
+ ExInitializeDeleteTimerParameters(&parameters);
+ ExDeleteTimer(fdoData->TickTimer, TRUE, FALSE, &parameters);
+ fdoData->TickTimer = NULL;
+ }
+#endif
+ fdoData->TimerInitialized = FALSE;
+ fdoData->TickTimerEnabled = FALSE;
+ }
+ }
+}
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClasspEnableTimer() - ISSUE-2000/02/20-henrygab - not documented
+
+Routine Description:
+
+ This routine will enable the tick timer. ClasspInitializeTimer() should
+ first be called to initialize the timer. Use ClasspDisableTimer() to
+ disable the timer and then call this function to re-enable it.
+
+Arguments:
+
+ FdoExtension
+
+Return Value:
+
+ None.
+
+--*/
+VOID
+ClasspEnableTimer(
+ _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ )
+{
+ PCLASS_PRIVATE_FDO_DATA fdoData = NULL;
+
+ if (FdoExtension->CommonExtension.IsFdo) {
+ fdoData = FdoExtension->PrivateFdoData;
+ }
+
+ if (fdoData != NULL) {
+ //
+ // The timer should have already been initialized, but if that's not
+ // the case it's not the end of the world. We can attempt to
+ // initialize it now.
+ //
+ NT_ASSERT(fdoData->TimerInitialized);
+ if (fdoData->TimerInitialized == FALSE) {
+ NTSTATUS status;
+ status = ClasspInitializeTimer(FdoExtension);
+ if (NT_SUCCESS(status) == FALSE) {
+ return;
+ }
+ }
+
+#if (NTDDI_VERSION >= NTDDI_WINBLUE)
+ if (fdoData->TickTimer != NULL) {
+ EXT_SET_PARAMETERS parameters;
+ LONGLONG period = TICK_TIMER_PERIOD_IN_MSEC * (10 * 1000); // Convert to units of 100ns.
+ LONGLONG dueTime = period * (-1); // Negative sign indicates dueTime is relative.
+
+ ExInitializeSetTimerParameters(&parameters);
+
+ //
+ // Set the no-wake tolerance to "unlimited" if the conditions below
+ // are met. An "unlimited" no-wake tolerance means that the timer
+ // will *never* wake the processor if the processor is in a
+ // low-power state.
+ // 1. The screen is off.
+ // 2. The class driver is *not* a consumer of the tick timer (ClassTick is NULL).
+ // 3. This is a disk device.
+ // Otherwise the tolerance is set to the normal tolerable delay.
+ //
+ if (ClasspScreenOff &&
+ FdoExtension->CommonExtension.DriverExtension->InitData.ClassTick == NULL &&
+ FdoExtension->DeviceObject->DeviceType == FILE_DEVICE_DISK) {
+ parameters.NoWakeTolerance = EX_TIMER_UNLIMITED_TOLERANCE;
+ } else {
+ parameters.NoWakeTolerance = TICK_TIMER_DELAY_IN_MSEC * (10 * 1000);
+ }
+
+ fdoData->CurrentNoWakeTolerance = parameters.NoWakeTolerance;
+
+ ExSetTimer(fdoData->TickTimer,
+ dueTime,
+ period,
+ &parameters);
+
+ fdoData->TickTimerEnabled = TRUE;
+ } else {
+ NT_ASSERT(fdoData->TickTimer != NULL);
+ }
+#else
+ //
+ // Start the periodic tick timer using a coalescable timer with some delay
+ //
+ {
+ LARGE_INTEGER timeout;
+ timeout.QuadPart = TICK_TIMER_PERIOD_IN_MSEC * (10 * 1000) * (-1);
+ KeSetCoalescableTimer(&fdoData->TickTimer,
+ timeout, TICK_TIMER_PERIOD_IN_MSEC, TICK_TIMER_DELAY_IN_MSEC,
+ &fdoData->TickTimerDpc);
+ fdoData->TickTimerEnabled = TRUE;
+ }
+#endif
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClasspEnableTimer: Periodic tick timer enabled "
+ "for device %p\n", FdoExtension->DeviceObject));
+
+ }
+
+} // end ClasspEnableTimer()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClasspDisableTimer() - ISSUE-2000/02/20-henrygab - not documented
+
+Routine Description:
+
+ This routine
+
+Arguments:
+
+ FdoExtension
+
+Return Value:
+
+--*/
+VOID
+ClasspDisableTimer(
+ _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ )
+{
+ PCLASS_PRIVATE_FDO_DATA fdoData = NULL;
+
+ if (FdoExtension->CommonExtension.IsFdo) {
+ fdoData = FdoExtension->PrivateFdoData;
+ }
+
+ if (fdoData && fdoData->TimerInitialized == TRUE) {
+
+ //
+ // we are only going to stop the actual timer in remove device routine
+ // or when done transitioning to D3 (timer will be started again when
+ // done transitioning to D0).
+ //
+ // 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.
+ //
+#if (NTDDI_VERSION >= NTDDI_WINBLUE)
+ NT_ASSERT(fdoData->TickTimer != NULL);
+ ExCancelTimer(fdoData->TickTimer, NULL);
+#else
+ KeCancelTimer(&fdoData->TickTimer);
+#endif
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClasspDisableTimer: Periodic tick timer disabled "
+ "for device %p\n", FdoExtension->DeviceObject));
+ fdoData->TickTimerEnabled = FALSE;
+
+ } else {
+
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN, "ClasspDisableTimer: Timer never initialized\n"));
+
+ }
+
+ return;
+} // end ClasspDisableTimer()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClasspFailurePredict() - ISSUE-2000/02/20-henrygab - not documented
+
+Routine Description:
+
+ This routine
+
+Arguments:
+
+ DeviceObject - Device object
+ Context - Context (PFAILURE_PREDICTION_INFO)
+
+Return Value:
+
+Note: this function can be called (via the workitem callback) after the paging device is shut down,
+ so it must be PAGE LOCKED.
+--*/
+VOID
+ClasspFailurePredict(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PVOID Context
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+ PIO_WORKITEM workItem;
+ STORAGE_PREDICT_FAILURE checkFailure = {0};
+ SCSI_ADDRESS scsiAddress = {0};
+ PFAILURE_PREDICTION_INFO Info = (PFAILURE_PREDICTION_INFO)Context;
+
+ NTSTATUS status;
+
+ if (Info == NULL) {
+ NT_ASSERT(Info != NULL);
+ return;
+ }
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "ClasspFailurePredict: Polling for failure\n"));
+
+ //
+ // Mark the work item as inactive and reset the countdown timer. we
+ // can't risk freeing the work item until we've released the remove-lock
+ // though - if we do it might get reused as a tag before we can release
+ // the lock.
+ //
+
+ InterlockedExchange((volatile LONG *)&Info->CountDown, Info->Period);
+ workItem = InterlockedExchangePointer(&(Info->WorkQueueItem), NULL);
+
+ if (ClasspCanSendPollingIrp(fdoExtension)) {
+
+ KEVENT event;
+ PDEVICE_OBJECT topOfStack;
+ PIRP irp = NULL;
+ IO_STATUS_BLOCK ioStatus;
+ NTSTATUS activateStatus = STATUS_UNSUCCESSFUL;
+
+ //
+ // Take an active reference on the device to ensure it is powered up
+ // while we do the failure prediction query.
+ //
+ if (fdoExtension->FunctionSupportInfo->IdlePower.IdlePowerEnabled) {
+ activateStatus = ClasspPowerActivateDevice(DeviceObject);
+ }
+
+ KeInitializeEvent(&event, SynchronizationEvent, FALSE);
+
+ topOfStack = IoGetAttachedDeviceReference(DeviceObject);
+
+ //
+ // Send down irp to see if drive is predicting failure
+ //
+
+ irp = IoBuildDeviceIoControlRequest(
+ IOCTL_STORAGE_PREDICT_FAILURE,
+ topOfStack,
+ NULL,
+ 0,
+ &checkFailure,
+ sizeof(STORAGE_PREDICT_FAILURE),
+ FALSE,
+ &event,
+ &ioStatus);
+
+
+ if (irp != NULL) {
+
+
+ status = IoCallDriver(topOfStack, irp);
+ if (status == STATUS_PENDING) {
+ (VOID)KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
+ status = ioStatus.Status;
+ }
+
+
+ } else {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ if (NT_SUCCESS(status) && (checkFailure.PredictFailure)) {
+
+ checkFailure.PredictFailure = 512;
+
+ //
+ // Send down irp to get scsi address
+ //
+ KeInitializeEvent(&event, SynchronizationEvent, FALSE);
+
+ RtlZeroMemory(&scsiAddress, sizeof(SCSI_ADDRESS));
+ irp = IoBuildDeviceIoControlRequest(
+ IOCTL_SCSI_GET_ADDRESS,
+ topOfStack,
+ NULL,
+ 0,
+ &scsiAddress,
+ sizeof(SCSI_ADDRESS),
+ FALSE,
+ &event,
+ &ioStatus);
+
+ if (irp != NULL) {
+
+
+ status = IoCallDriver(topOfStack, irp);
+ if (status == STATUS_PENDING) {
+ (VOID)KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
+ }
+
+ }
+
+ ClassNotifyFailurePredicted(fdoExtension,
+ (PUCHAR)&checkFailure,
+ sizeof(checkFailure),
+ (BOOLEAN)(fdoExtension->FailurePredicted == FALSE),
+ 2,
+ scsiAddress.PathId,
+ scsiAddress.TargetId,
+ scsiAddress.Lun);
+
+ fdoExtension->FailurePredicted = TRUE;
+
+ }
+
+ ObDereferenceObject(topOfStack);
+
+ //
+ // Update the failure prediction query time and release the active
+ // reference.
+ //
+
+ KeQuerySystemTime(&(Info->LastFailurePredictionQueryTime));
+
+ if (NT_SUCCESS(activateStatus)) {
+ ClasspPowerIdleDevice(DeviceObject);
+ }
+ }
+
+ ClassReleaseRemoveLock(DeviceObject, (PIRP) workItem);
+ IoFreeWorkItem(workItem);
+ return;
+} // end ClasspFailurePredict()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassNotifyFailurePredicted() ISSUE-alanwar-2000/02/20 - not documented
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+_IRQL_requires_max_(DISPATCH_LEVEL)
+VOID
+ClassNotifyFailurePredicted(
+ _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ _In_reads_bytes_(BufferSize) PUCHAR Buffer,
+ _In_ ULONG BufferSize,
+ _In_ BOOLEAN LogError,
+ _In_ ULONG UniqueErrorValue,
+ _In_ UCHAR PathId,
+ _In_ UCHAR TargetId,
+ _In_ UCHAR Lun
+ )
+{
+ PIO_ERROR_LOG_PACKET logEntry;
+ EVENT_DESCRIPTOR eventDescriptor;
+ PCLASS_DRIVER_EXTENSION driverExtension;
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "ClasspFailurePredictPollCompletion: Failure predicted for device %p\n", FdoExtension->DeviceObject));
+
+ //
+ // Fire off a WMI event
+ //
+ ClassWmiFireEvent(FdoExtension->DeviceObject,
+ (LPGUID)&StoragePredictFailureEventGuid,
+ 0,
+ BufferSize,
+ Buffer);
+ //
+ // Log an error into the eventlog
+ //
+
+ if (LogError)
+ {
+ logEntry = IoAllocateErrorLogEntry(
+ FdoExtension->DeviceObject,
+ sizeof(IO_ERROR_LOG_PACKET) + (3 * sizeof(ULONG)));
+
+ if (logEntry != NULL)
+ {
+
+ logEntry->FinalStatus = STATUS_SUCCESS;
+ logEntry->ErrorCode = IO_WRN_FAILURE_PREDICTED;
+ logEntry->SequenceNumber = 0;
+ logEntry->MajorFunctionCode = IRP_MJ_DEVICE_CONTROL;
+ logEntry->IoControlCode = IOCTL_STORAGE_PREDICT_FAILURE;
+ logEntry->RetryCount = 0;
+ logEntry->UniqueErrorValue = UniqueErrorValue;
+ logEntry->DumpDataSize = 3;
+
+ logEntry->DumpData[0] = PathId;
+ logEntry->DumpData[1] = TargetId;
+ logEntry->DumpData[2] = Lun;
+
+ //
+ // Write the error log packet.
+ //
+
+ IoWriteErrorLogEntry(logEntry);
+ }
+ }
+
+ //
+ // Send ETW event if LogError is TRUE. ClassInterpretSenseInfo sets this
+ // to FALSE. So if failure is predicted for the first time and UniqueErrorValue
+ // is 4 (used by ClassInterpretSenseInfo) then send ETW event.
+ //
+
+ if ((LogError == TRUE) ||
+ ((FdoExtension->FailurePredicted == FALSE) && (UniqueErrorValue == 4))) {
+
+#pragma warning(suppress:4054) // okay to type cast function pointer as data pointer for this use case
+ driverExtension = IoGetDriverObjectExtension(FdoExtension->DeviceObject->DriverObject, CLASS_DRIVER_EXTENSION_KEY);
+
+ if ((driverExtension != NULL) && (driverExtension->EtwHandle != 0)) {
+ EventDescCreate(&eventDescriptor,
+ 1, // Id
+ 0, // Version
+ 0, // Channel
+ 0, // Level
+ 0, // Task
+ 0, // OpCode
+ 0); // Keyword
+
+ EtwWrite(driverExtension->EtwHandle,
+ &eventDescriptor,
+ NULL,
+ 0,
+ NULL);
+ }
+ }
+
+} // end ClassNotifyFailurePredicted()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassSetFailurePredictionPoll()
+
+Routine Description:
+
+ This routine enables polling for failure prediction, setting the timer
+ to fire every N seconds as specified by the PollingPeriod.
+
+Arguments:
+
+ FdoExtension - the device to setup failure prediction for.
+
+ FailurePredictionMethod - specific failure prediction method to use
+ if set to FailurePredictionNone, will disable failure detection
+
+ PollingPeriod - if 0 then no change to current polling timer
+
+Return Value:
+
+ NT Status
+
+--*/
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+ClassSetFailurePredictionPoll(
+ _Inout_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ _In_ FAILURE_PREDICTION_METHOD FailurePredictionMethod,
+ _In_ ULONG PollingPeriod
+ )
+{
+ PFAILURE_PREDICTION_INFO info;
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ if (FdoExtension->FailurePredictionInfo == NULL) {
+
+ if (FailurePredictionMethod != FailurePredictionNone) {
+
+ info = ExAllocatePoolWithTag(NonPagedPoolNx,
+ sizeof(FAILURE_PREDICTION_INFO),
+ CLASS_TAG_FAILURE_PREDICT);
+
+ if (info == NULL) {
+
+ return STATUS_INSUFFICIENT_RESOURCES;
+
+ }
+
+ KeInitializeEvent(&info->Event, SynchronizationEvent, TRUE);
+
+ info->WorkQueueItem = NULL;
+ info->Period = DEFAULT_FAILURE_PREDICTION_PERIOD;
+
+ KeQuerySystemTime(&(info->LastFailurePredictionQueryTime));
+
+ } else {
+
+ //
+ // FaultPrediction has not been previously initialized, nor
+ // is it being initialized now. No need to do anything.
+ //
+ return STATUS_SUCCESS;
+
+ }
+
+ FdoExtension->FailurePredictionInfo = info;
+
+ } else {
+
+ info = FdoExtension->FailurePredictionInfo;
+
+ }
+
+ /*
+ * Make sure the user-mode thread is not suspended while we hold the synchronization event.
+ */
+ KeEnterCriticalRegion();
+
+ (VOID)KeWaitForSingleObject(&info->Event,
+ UserRequest,
+ KernelMode,
+ FALSE,
+ NULL);
+
+
+ //
+ // Reset polling period and counter. Setup failure detection type
+ //
+
+ if (PollingPeriod != 0) {
+
+ InterlockedExchange((volatile LONG *)&info->Period, PollingPeriod);
+ }
+
+ InterlockedExchange((volatile LONG *)&info->CountDown, info->Period);
+
+ info->Method = FailurePredictionMethod;
+ if (FailurePredictionMethod != FailurePredictionNone) {
+
+ ClasspEnableTimer(FdoExtension);
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "ClassEnableFailurePredictPoll: Enabled for "
+ "device %p\n", FdoExtension->DeviceObject));
+
+ } else {
+
+ ClasspDisableTimer(FdoExtension);
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "ClassEnableFailurePredictPoll: Disabled for "
+ "device %p\n", FdoExtension->DeviceObject));
+ }
+ status = STATUS_SUCCESS;
+
+
+ KeSetEvent(&info->Event, IO_NO_INCREMENT, FALSE);
+
+ KeLeaveCriticalRegion();
+
+ return status;
+} // end ClassSetFailurePredictionPoll()
+
+BOOLEAN
+ClasspFailurePredictionPeriodMissed(
+ _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ )
+/*
+Routine Description:
+ This routine can be used to determine if a failure prediction polling
+ period has been missed. That is, the time since the last failure
+ prediction IOCTL has been sent is greater than the failure prediction
+ polling period. This can happen if failure prediction polling was
+ disabled, such as when the device is in D3 or when the screen is off.
+
+Parameters:
+ FdoExtension - FDO extension. The caller should make sure the FDO
+ extension is valid. The FailurePredictionInfo structure should also
+ be valid and the failure prediction method should not be "none".
+
+Returns:
+ TRUE if there was one or more failure prediction polling periods that was
+ missed.
+ FALSE otherwise.
+*/
+{
+ LARGE_INTEGER currentTime;
+ LARGE_INTEGER timeDifference;
+ BOOLEAN missedPeriod = FALSE;
+
+ NT_ASSERT(FdoExtension);
+ NT_ASSERT(FdoExtension->FailurePredictionInfo);
+ NT_ASSERT(FdoExtension->FailurePredictionInfo->Method != FailurePredictionNone);
+
+ //
+ // Find the difference between the last failure prediction
+ // query and the current time and convert it to seconds.
+ //
+ KeQuerySystemTime(&currentTime);
+ timeDifference.QuadPart = currentTime.QuadPart - FdoExtension->FailurePredictionInfo->LastFailurePredictionQueryTime.QuadPart;
+ timeDifference.QuadPart /= (10LL * 1000LL * 1000LL);
+
+ if (timeDifference.QuadPart >= FdoExtension->FailurePredictionInfo->Period) {
+ missedPeriod = TRUE;
+ }
+
+ return missedPeriod;
+}
+
+
diff --git a/storage/class/classpnp/src/class.c b/storage/class/classpnp/src/class.c
new file mode 100644
index 00000000..2f92e453
--- /dev/null
+++ b/storage/class/classpnp/src/class.c
@@ -0,0 +1,16415 @@
+/*++
+
+Copyright (C) Microsoft Corporation, 1991 - 2010
+
+Module Name:
+
+ class.c
+
+Abstract:
+
+ SCSI class driver routines
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+
+Revision History:
+
+--*/
+
+#define CLASS_INIT_GUID 1
+#define DEBUG_MAIN_SOURCE 1
+
+#include "classp.h"
+#include "debug.h"
+#include <process.h>
+#include <devpkey.h>
+#include <ntiologc.h>
+
+
+#ifdef DEBUG_USE_WPP
+#include "class.tmh"
+#endif
+
+#ifdef ALLOC_PRAGMA
+ #pragma alloc_text(INIT, DriverEntry)
+ #pragma alloc_text(PAGE, ClassAddDevice)
+ #pragma alloc_text(PAGE, ClassClaimDevice)
+ #pragma alloc_text(PAGE, ClassCreateDeviceObject)
+ #pragma alloc_text(PAGE, ClassDispatchPnp)
+ #pragma alloc_text(PAGE, ClassGetDescriptor)
+ #pragma alloc_text(PAGE, ClassGetPdoId)
+ #pragma alloc_text(PAGE, ClassInitialize)
+ #pragma alloc_text(PAGE, ClassInitializeEx)
+ #pragma alloc_text(PAGE, ClassInvalidateBusRelations)
+ #pragma alloc_text(PAGE, ClassMarkChildMissing)
+ #pragma alloc_text(PAGE, ClassMarkChildrenMissing)
+ #pragma alloc_text(PAGE, ClassModeSense)
+ #pragma alloc_text(PAGE, ClassPnpQueryFdoRelations)
+ #pragma alloc_text(PAGE, ClassPnpStartDevice)
+ #pragma alloc_text(PAGE, ClassQueryPnpCapabilities)
+ #pragma alloc_text(PAGE, ClassQueryTimeOutRegistryValue)
+ #pragma alloc_text(PAGE, ClassRemoveDevice)
+ #pragma alloc_text(PAGE, ClassRetrieveDeviceRelations)
+ #pragma alloc_text(PAGE, ClassUpdateInformationInRegistry)
+ #pragma alloc_text(PAGE, ClassSendDeviceIoControlSynchronous)
+ #pragma alloc_text(PAGE, ClassUnload)
+ #pragma alloc_text(PAGE, ClasspAllocateReleaseRequest)
+ #pragma alloc_text(PAGE, ClasspFreeReleaseRequest)
+ #pragma alloc_text(PAGE, ClasspInitializeHotplugInfo)
+ #pragma alloc_text(PAGE, ClasspRegisterMountedDeviceInterface)
+ #pragma alloc_text(PAGE, ClasspScanForClassHacks)
+ #pragma alloc_text(PAGE, ClasspScanForSpecialInRegistry)
+ #pragma alloc_text(PAGE, ClasspModeSense)
+ #pragma alloc_text(PAGE, ClasspIsPortable)
+ #pragma alloc_text(PAGE, ClassAcquireChildLock)
+ #pragma alloc_text(PAGE, ClassDetermineTokenOperationCommandSupport)
+ #pragma alloc_text(PAGE, ClassDeviceProcessOffloadRead)
+ #pragma alloc_text(PAGE, ClassDeviceProcessOffloadWrite)
+ #pragma alloc_text(PAGE, ClasspServicePopulateTokenTransferRequest)
+ #pragma alloc_text(PAGE, ClasspServiceWriteUsingTokenTransferRequest)
+#if (NTDDI_VERSION >= NTDDI_WINBLUE)
+ #pragma alloc_text(PAGE, ClassModeSenseEx)
+#endif
+#endif
+
+#pragma prefast(disable:28159, "There are certain cases when we have to bugcheck...")
+
+IO_COMPLETION_ROUTINE ClassCheckVerifyComplete;
+
+
+ULONG ClassPnpAllowUnload = TRUE;
+ULONG ClassMaxInterleavePerCriticalIo = CLASS_MAX_INTERLEAVE_PER_CRITICAL_IO;
+CONST LARGE_INTEGER Magic10000 = {0xe219652c, 0xd1b71758};
+GUID StoragePredictFailureDPSGuid = WDI_STORAGE_PREDICT_FAILURE_DPS_GUID;
+
+#define FirstDriveLetter 'C'
+#define LastDriveLetter 'Z'
+
+BOOLEAN UseQPCTime = FALSE;
+
+//
+// Keep track of whether security cookie is initialized or not. This is
+// required by SDL.
+//
+
+BOOLEAN InitSecurityCookie = FALSE;
+
+//
+// List Identifier for offload data transfer operations
+//
+ULONG MaxTokenOperationListIdentifier = MAX_TOKEN_LIST_IDENTIFIERS;
+volatile ULONG TokenOperationListIdentifier = (ULONG)-1;
+
+//
+// List of FDOs that have enabled idle power management.
+//
+LIST_ENTRY IdlePowerFDOList = {0};
+KGUARDED_MUTEX IdlePowerFDOListMutex;
+
+//
+// Handle used to register for power setting notifications.
+//
+PVOID PowerSettingNotificationHandle;
+
+//
+// Handle used to register for screen state setting notifications.
+//
+PVOID ScreenStateNotificationHandle;
+
+//
+// Disk idle timeout in milliseconds.
+// We default this to 0xFFFFFFFF as this is what the power manager considers
+// "never" and ensures we do not set a disk idle timeout until the power
+// manager calls us back with a different value.
+//
+ULONG DiskIdleTimeoutInMS = 0xFFFFFFFF;
+
+
+NTSTATUS DllUnload(VOID)
+{
+ DbgPrintEx(DPFLTR_CLASSPNP_ID, DPFLTR_INFO_LEVEL, "classpnp.sys is now unloading\n");
+
+ if (PowerSettingNotificationHandle) {
+ PoUnregisterPowerSettingCallback(PowerSettingNotificationHandle);
+ PowerSettingNotificationHandle = NULL;
+ }
+
+ if (ScreenStateNotificationHandle) {
+ PoUnregisterPowerSettingCallback(ScreenStateNotificationHandle);
+ ScreenStateNotificationHandle = NULL;
+ }
+
+
+ return STATUS_SUCCESS;
+}
+
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+DriverEntry()
+
+Routine Description:
+
+ Temporary entry point needed to initialize the class system dll.
+ It doesn't do anything.
+
+Arguments:
+
+ DriverObject - Pointer to the driver object created by the system.
+
+Return Value:
+
+ STATUS_SUCCESS
+
+--*/
+NTSTATUS
+DriverEntry(
+ IN PDRIVER_OBJECT DriverObject,
+ IN PUNICODE_STRING RegistryPath
+ )
+{
+ UNREFERENCED_PARAMETER(DriverObject);
+ UNREFERENCED_PARAMETER(RegistryPath);
+
+ return STATUS_SUCCESS;
+}
+
+
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassInitialize()
+
+Routine Description:
+
+ This routine is called by a class driver during its
+ DriverEntry routine to initialize the driver.
+
+Arguments:
+
+ Argument1 - Driver Object.
+ Argument2 - Registry Path.
+ InitializationData - Device-specific driver's initialization data.
+
+Return Value:
+
+ A valid return code for a DriverEntry routine.
+
+--*/
+_IRQL_requires_max_(PASSIVE_LEVEL)
+_Must_inspect_result_
+ULONG
+ClassInitialize(
+ _In_ PVOID Argument1,
+ _In_ PVOID Argument2,
+ _In_ PCLASS_INIT_DATA InitializationData
+ )
+{
+ PDRIVER_OBJECT DriverObject = Argument1;
+ PUNICODE_STRING RegistryPath = Argument2;
+
+ PCLASS_DRIVER_EXTENSION driverExtension;
+
+ NTSTATUS status;
+
+
+
+ PAGED_CODE();
+
+ //
+ // Initialize the security cookie if needed.
+ //
+ if (InitSecurityCookie == FALSE) {
+ __security_init_cookie();
+ InitSecurityCookie = TRUE;
+ }
+
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT, "\n\nSCSI Class Driver\n"));
+
+ ClasspInitializeDebugGlobals();
+
+ //
+ // Validate the length of this structure. This is effectively a
+ // version check.
+ //
+
+ if (InitializationData->InitializationDataSize != sizeof(CLASS_INIT_DATA)) {
+
+ //
+ // This DebugPrint is to help third-party driver writers
+ //
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT, "ClassInitialize: Class driver wrong version\n"));
+ return (ULONG) STATUS_REVISION_MISMATCH;
+ }
+
+ //
+ // Check that each required entry is not NULL. Note that Shutdown, Flush and Error
+ // are not required entry points.
+ //
+
+ if ((!InitializationData->FdoData.ClassDeviceControl) ||
+ (!((InitializationData->FdoData.ClassReadWriteVerification) ||
+ (InitializationData->ClassStartIo))) ||
+ (!InitializationData->ClassAddDevice) ||
+ (!InitializationData->FdoData.ClassStartDevice)) {
+
+ //
+ // This DebugPrint is to help third-party driver writers
+ //
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT,
+ "ClassInitialize: Class device-specific driver missing required "
+ "FDO entry\n"));
+
+ return (ULONG) STATUS_REVISION_MISMATCH;
+ }
+
+ if ((InitializationData->ClassEnumerateDevice) &&
+ ((!InitializationData->PdoData.ClassDeviceControl) ||
+ (!InitializationData->PdoData.ClassStartDevice) ||
+ (!((InitializationData->PdoData.ClassReadWriteVerification) ||
+ (InitializationData->ClassStartIo))))) {
+
+ //
+ // This DebugPrint is to help third-party driver writers
+ //
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT, "ClassInitialize: Class device-specific missing "
+ "required PDO entry\n"));
+
+ return (ULONG) STATUS_REVISION_MISMATCH;
+ }
+
+ if((InitializationData->FdoData.ClassStopDevice == NULL) ||
+ ((InitializationData->ClassEnumerateDevice != NULL) &&
+ (InitializationData->PdoData.ClassStopDevice == NULL))) {
+
+ //
+ // This DebugPrint is to help third-party driver writers
+ //
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT, "ClassInitialize: Class device-specific missing "
+ "required PDO entry\n"));
+ NT_ASSERT(FALSE);
+ return (ULONG) STATUS_REVISION_MISMATCH;
+ }
+
+ //
+ // Setup the default power handlers if the class driver didn't provide
+ // any.
+ //
+
+ if(InitializationData->FdoData.ClassPowerDevice == NULL) {
+ InitializationData->FdoData.ClassPowerDevice = ClassMinimalPowerHandler;
+ }
+
+ if((InitializationData->ClassEnumerateDevice != NULL) &&
+ (InitializationData->PdoData.ClassPowerDevice == NULL)) {
+ InitializationData->PdoData.ClassPowerDevice = ClassMinimalPowerHandler;
+ }
+
+ //
+ // warn that unload is not supported
+ //
+ // ISSUE-2000/02/03-peterwie
+ // We should think about making this a fatal error.
+ //
+
+ if(InitializationData->ClassUnload == NULL) {
+
+ //
+ // This DebugPrint is to help third-party driver writers
+ //
+
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT, "ClassInitialize: driver does not support unload %wZ\n",
+ RegistryPath));
+ }
+
+ //
+ // Create an extension for the driver object
+ //
+#pragma warning(suppress:4054) // okay to type cast function pointer as data pointer for this use case
+ status = IoAllocateDriverObjectExtension(DriverObject, CLASS_DRIVER_EXTENSION_KEY, sizeof(CLASS_DRIVER_EXTENSION), &driverExtension);
+
+ if(NT_SUCCESS(status)) {
+
+ //
+ // Copy the registry path into the driver extension so we can use it later
+ //
+
+ driverExtension->RegistryPath.Length = RegistryPath->Length;
+ driverExtension->RegistryPath.MaximumLength = RegistryPath->MaximumLength;
+
+ driverExtension->RegistryPath.Buffer =
+ ExAllocatePoolWithTag(PagedPool,
+ RegistryPath->MaximumLength,
+ '1CcS');
+
+ if(driverExtension->RegistryPath.Buffer == NULL) {
+
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ return status;
+ }
+
+ RtlCopyUnicodeString(
+ &(driverExtension->RegistryPath),
+ RegistryPath);
+
+ //
+ // Copy the initialization data into the driver extension so we can reuse
+ // it during our add device routine
+ //
+
+ RtlCopyMemory(
+ &(driverExtension->InitData),
+ InitializationData,
+ sizeof(CLASS_INIT_DATA));
+
+ driverExtension->DeviceCount = 0;
+
+ ClassInitializeDispatchTables(driverExtension);
+
+ } else if (status == STATUS_OBJECT_NAME_COLLISION) {
+
+ //
+ // The extension already exists - get a pointer to it
+ //
+
+#pragma warning(suppress:4054) // okay to type cast function pointer as data pointer for this use case
+ driverExtension = IoGetDriverObjectExtension(DriverObject, CLASS_DRIVER_EXTENSION_KEY);
+
+ NT_ASSERT(driverExtension != NULL);
+
+ } else {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT, "ClassInitialize: Class driver extension could not be "
+ "allocated %lx\n", status));
+ return status;
+ }
+
+
+ //
+ // Update driver object with entry points.
+ //
+
+#pragma prefast(push)
+#pragma prefast(disable:28175, "Accessing DRIVER_OBJECT fileds is OK here since this function " \
+ "is supposed to be invoked from DriverEntry only")
+ DriverObject->MajorFunction[IRP_MJ_CREATE] = ClassGlobalDispatch;
+ DriverObject->MajorFunction[IRP_MJ_CLOSE] = ClassGlobalDispatch;
+ DriverObject->MajorFunction[IRP_MJ_READ] = ClassGlobalDispatch;
+ DriverObject->MajorFunction[IRP_MJ_WRITE] = ClassGlobalDispatch;
+ DriverObject->MajorFunction[IRP_MJ_SCSI] = ClassGlobalDispatch;
+ DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ClassGlobalDispatch;
+ DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = ClassGlobalDispatch;
+ DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS] = ClassGlobalDispatch;
+ DriverObject->MajorFunction[IRP_MJ_PNP] = ClassGlobalDispatch;
+ DriverObject->MajorFunction[IRP_MJ_POWER] = ClassGlobalDispatch;
+ DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = ClassGlobalDispatch;
+
+ if (InitializationData->ClassStartIo) {
+ DriverObject->DriverStartIo = ClasspStartIo;
+ }
+
+ if ((InitializationData->ClassUnload) && (ClassPnpAllowUnload == TRUE)) {
+ DriverObject->DriverUnload = ClassUnload;
+ } else {
+ DriverObject->DriverUnload = NULL;
+ }
+
+ DriverObject->DriverExtension->AddDevice = ClassAddDevice;
+#pragma prefast(pop)
+
+
+ //
+ // Register for event tracing
+ //
+ if (driverExtension->EtwHandle == 0) {
+ status = EtwRegister(&StoragePredictFailureDPSGuid,
+ NULL,
+ NULL,
+ &driverExtension->EtwHandle);
+ if (!NT_SUCCESS(status)) {
+ driverExtension->EtwHandle = 0;
+ }
+ WPP_INIT_TRACING(DriverObject, RegistryPath);
+ }
+
+
+ //
+ // Ensure these are only initialized once.
+ //
+ if (IdlePowerFDOList.Flink == NULL) {
+ InitializeListHead(&IdlePowerFDOList);
+ KeInitializeGuardedMutex(&IdlePowerFDOListMutex);
+ }
+
+ status = STATUS_SUCCESS;
+ return status;
+} // end ClassInitialize()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassInitializeEx()
+
+Routine Description:
+
+ This routine is allows the caller to do any extra initialization or
+ setup that is not done in ClassInitialize. The operation is
+ controlled by the GUID that is passed and the contents of the Data
+ parameter is dependent upon the GUID.
+
+ This is the list of supported operations:
+
+ GUID_CLASSPNP_QUERY_REGINFOEX == CLASS_QUERY_WMI_REGINFO_EX_LIST
+
+ Initialized classpnp to callback a PCLASS_QUERY_WMI_REGINFO_EX
+ callback instead of a PCLASS_QUERY_WMI_REGINFO callback. The
+ former callback allows the driver to specify the name of the
+ mof resource.
+
+ GUID_CLASSPNP_SENSEINFO2 == CLASS_INTERPRET_SENSE_INFO2
+
+ Initialize classpnp to callback into class drive for interpretation
+ of all sense info, and to indicate the count of "history" to keep
+ for each packet.
+
+ GUID_CLASSPNP_WORKING_SET == CLASS_WORKING_SET
+
+ Allow class driver to override the min and max working set transfer
+ packet value used in classpnp.
+
+ GUID_CLASSPNP_SRB_SUPPORT == ULONG
+
+ Allow class driver to provide supported SRB types.
+
+Arguments:
+
+ DriverObject
+ Guid
+ Data
+
+Return Value:
+
+ Status Code
+
+--*/
+_IRQL_requires_max_(PASSIVE_LEVEL)
+_Must_inspect_result_
+ULONG
+ClassInitializeEx(
+ _In_ PDRIVER_OBJECT DriverObject,
+ _In_ LPGUID Guid,
+ _In_ PVOID Data
+ )
+{
+ PCLASS_DRIVER_EXTENSION driverExtension;
+
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+#pragma warning(suppress:4054) // okay to type cast function pointer as data pointer for this use case
+ driverExtension = IoGetDriverObjectExtension( DriverObject, CLASS_DRIVER_EXTENSION_KEY );
+
+ if (driverExtension == NULL)
+ {
+ NT_ASSERT(FALSE);
+ return (ULONG)STATUS_UNSUCCESSFUL;
+ }
+
+ if (IsEqualGUID(Guid, &ClassGuidQueryRegInfoEx))
+ {
+ PCLASS_QUERY_WMI_REGINFO_EX_LIST List;
+
+ //
+ // Indicate the device supports PCLASS_QUERY_REGINFO_EX
+ // callback instead of PCLASS_QUERY_REGINFO callback.
+ //
+ List = (PCLASS_QUERY_WMI_REGINFO_EX_LIST)Data;
+
+ if (List->Size == sizeof(CLASS_QUERY_WMI_REGINFO_EX_LIST))
+ {
+ driverExtension->ClassFdoQueryWmiRegInfoEx = List->ClassFdoQueryWmiRegInfoEx;
+ driverExtension->ClassPdoQueryWmiRegInfoEx = List->ClassPdoQueryWmiRegInfoEx;
+ status = STATUS_SUCCESS;
+ } else {
+ status = STATUS_INVALID_PARAMETER;
+ }
+ }
+ else if (IsEqualGUID(Guid, &ClassGuidWorkingSet))
+ {
+ PCLASS_WORKING_SET infoOriginal = (PCLASS_WORKING_SET)Data;
+ PCLASS_WORKING_SET info = NULL;
+
+ // only try to allocate memory for cached copy if size is correct
+ if (infoOriginal->Size != sizeof(CLASS_WORKING_SET))
+ {
+ // incorrect size -- client programming error
+ status = STATUS_INVALID_PARAMETER;
+ }
+ else
+ {
+ info = ExAllocatePoolWithTag(NonPagedPoolNx,
+ sizeof(CLASS_WORKING_SET),
+ CLASS_TAG_WORKING_SET
+ );
+ if (info == NULL)
+ {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+ else
+ {
+ // cache the structure internally
+ RtlCopyMemory(info, infoOriginal, sizeof(CLASS_WORKING_SET));
+ status = STATUS_SUCCESS;
+ }
+ }
+ // if we successfully cached a copy, validate all the data within
+ if (NT_SUCCESS(status))
+ {
+ if (info->Size != sizeof(CLASS_WORKING_SET))
+ {
+ // incorrect size -- client programming error
+ status = STATUS_INVALID_PARAMETER;
+ }
+ else if (info->XferPacketsWorkingSetMaximum > CLASS_WORKING_SET_MAXIMUM)
+ {
+ // too many requested in the working set
+ status = STATUS_INVALID_PARAMETER;
+ }
+ else if (info->XferPacketsWorkingSetMinimum > CLASS_WORKING_SET_MAXIMUM)
+ {
+ // too many requested in the working set
+ status = STATUS_INVALID_PARAMETER;
+ }
+ else if (driverExtension->InitData.FdoData.DeviceType != FILE_DEVICE_CD_ROM)
+ {
+ // classpnp developer wants to restrict this code path
+ // for now to CDROM devices only.
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ }
+ else if (driverExtension->WorkingSet != NULL)
+ {
+ // not allowed to change it once it is set for a driver
+ status = STATUS_INVALID_PARAMETER;
+ NT_ASSERT(FALSE);
+ }
+ }
+ // save results or cleanup
+ if (NT_SUCCESS(status))
+ {
+ driverExtension->WorkingSet = info; info = NULL;
+ }
+ else
+ {
+ FREE_POOL( info );
+ }
+ }
+ else if (IsEqualGUID(Guid, &ClassGuidSenseInfo2))
+ {
+ PCLASS_INTERPRET_SENSE_INFO2 infoOriginal = (PCLASS_INTERPRET_SENSE_INFO2)Data;
+ PCLASS_INTERPRET_SENSE_INFO2 info = NULL;
+
+ // only try to allocate memory for cached copy if size is correct
+ if (infoOriginal->Size != sizeof(CLASS_INTERPRET_SENSE_INFO2))
+ {
+ // incorrect size -- client programming error
+ status = STATUS_INVALID_PARAMETER;
+ }
+ else
+ {
+ info = ExAllocatePoolWithTag(NonPagedPoolNx,
+ sizeof(CLASS_INTERPRET_SENSE_INFO2),
+ CLASS_TAG_SENSE2
+ );
+ if (info == NULL)
+ {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+ else
+ {
+ // cache the structure internally
+ RtlCopyMemory(info, infoOriginal, sizeof(CLASS_INTERPRET_SENSE_INFO2));
+ status = STATUS_SUCCESS;
+ }
+ }
+
+ // if we successfully cached a copy, validate all the data within
+ if (NT_SUCCESS(status))
+ {
+ if (info->Size != sizeof(CLASS_INTERPRET_SENSE_INFO2))
+ {
+ // incorrect size -- client programming error
+ status = STATUS_INVALID_PARAMETER;
+ }
+ else if (info->HistoryCount > CLASS_INTERPRET_SENSE_INFO2_MAXIMUM_HISTORY_COUNT)
+ {
+ // incorrect count -- client programming error
+ status = STATUS_INVALID_PARAMETER;
+ }
+ else if (info->Compress == NULL)
+ {
+ // Compression of the history is required to be supported
+ status = STATUS_INVALID_PARAMETER;
+ }
+ else if (info->HistoryCount == 0)
+ {
+ // History count cannot be zero
+ status = STATUS_INVALID_PARAMETER;
+ }
+ else if (info->Interpret == NULL)
+ {
+ // Updated interpret sense info function is required
+ status = STATUS_INVALID_PARAMETER;
+ }
+ else if (driverExtension->InitData.FdoData.DeviceType != FILE_DEVICE_CD_ROM)
+ {
+ // classpnp developer wants to restrict this code path
+ // for now to CDROM devices only.
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ }
+ else if (driverExtension->InterpretSenseInfo != NULL)
+ {
+ // not allowed to change it once it is set for a driver
+ status = STATUS_INVALID_PARAMETER;
+ NT_ASSERT(FALSE);
+ }
+ }
+
+ // save results or cleanup
+ if (NT_SUCCESS(status))
+ {
+ driverExtension->InterpretSenseInfo = info; info = NULL;
+ }
+ else
+ {
+ FREE_POOL( info );
+ }
+ }
+ else if (IsEqualGUID(Guid, &ClassGuidSrbSupport))
+ {
+ ULONG srbSupport = *((PULONG)Data);
+
+ //
+ // Validate that at least one of the supported bit flags is set. Assume
+ // all class drivers support SCSI_REQUEST_BLOCK as a class driver that
+ // supports only extended SRB is not feasible.
+ //
+ if ((srbSupport &
+ (CLASS_SRB_SCSI_REQUEST_BLOCK | CLASS_SRB_STORAGE_REQUEST_BLOCK)) != 0) {
+ driverExtension->SrbSupport = srbSupport;
+ status = STATUS_SUCCESS;
+
+ //
+ // Catch cases of a class driver reporting only extended SRB support
+ //
+ if ((driverExtension->SrbSupport & CLASS_SRB_SCSI_REQUEST_BLOCK) == 0) {
+ NT_ASSERT(FALSE);
+ }
+ } else {
+ status = STATUS_INVALID_PARAMETER;
+ }
+ }
+ else
+ {
+ status = STATUS_NOT_SUPPORTED;
+ }
+
+ return status;
+
+} // end ClassInitializeEx()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassUnload()
+
+Routine Description:
+
+ called when there are no more references to the driver. this allows
+ drivers to be updated without rebooting.
+
+Arguments:
+
+ DriverObject - a pointer to the driver object that is being unloaded
+
+Status:
+
+--*/
+VOID
+ClassUnload(
+ IN PDRIVER_OBJECT DriverObject
+ )
+{
+ PCLASS_DRIVER_EXTENSION driverExtension;
+
+ PAGED_CODE();
+
+ NT_ASSERT( DriverObject->DeviceObject == NULL );
+
+#pragma warning(suppress:4054) // okay to type cast function pointer as data pointer for this use case
+ driverExtension = IoGetDriverObjectExtension( DriverObject, CLASS_DRIVER_EXTENSION_KEY );
+
+
+ if (driverExtension == NULL)
+ {
+ NT_ASSERT(FALSE);
+ return;
+ }
+
+ NT_ASSERT(driverExtension->RegistryPath.Buffer != NULL);
+ NT_ASSERT(driverExtension->InitData.ClassUnload != NULL);
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT, "ClassUnload: driver unloading %wZ\n",
+ &driverExtension->RegistryPath));
+
+ //
+ // attempt to process the driver's unload routine first.
+ //
+
+ driverExtension->InitData.ClassUnload(DriverObject);
+
+ //
+ // free own allocated resources and return
+ //
+
+ FREE_POOL( driverExtension->WorkingSet );
+ FREE_POOL( driverExtension->InterpretSenseInfo );
+ FREE_POOL( driverExtension->RegistryPath.Buffer );
+ driverExtension->RegistryPath.Length = 0;
+ driverExtension->RegistryPath.MaximumLength = 0;
+
+
+ //
+ // Unregister ETW
+ //
+ if (driverExtension->EtwHandle != 0) {
+ EtwUnregister(driverExtension->EtwHandle);
+ driverExtension->EtwHandle = 0;
+
+ WPP_CLEANUP(DriverObject);
+ }
+
+
+ return;
+} // end ClassUnload()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassAddDevice()
+
+Routine Description:
+
+ SCSI class driver add device routine. This is called by pnp when a new
+ physical device come into being.
+
+ This routine will call out to the class driver to verify that it should
+ own this device then will create and attach a device object and then hand
+ it to the driver to initialize and create symbolic links
+
+Arguments:
+
+ DriverObject - a pointer to the driver object that this is being created for
+ PhysicalDeviceObject - a pointer to the physical device object
+
+Status: STATUS_NO_SUCH_DEVICE if the class driver did not want this device
+ STATUS_SUCCESS if the creation and attachment was successful
+ status of device creation and initialization
+
+--*/
+NTSTATUS
+#pragma prefast(suppress:28152, "We expect the class driver to clear the DO_DEVICE_INITIALIZING flag in its AddDevice routine.")
+ClassAddDevice(
+ IN PDRIVER_OBJECT DriverObject,
+ IN PDEVICE_OBJECT PhysicalDeviceObject
+ )
+{
+#pragma warning(suppress:4054) // okay to type cast function pointer as data pointer for this use case
+ PCLASS_DRIVER_EXTENSION driverExtension = IoGetDriverObjectExtension(DriverObject, CLASS_DRIVER_EXTENSION_KEY);
+
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+
+ status = driverExtension->InitData.ClassAddDevice(DriverObject,
+ PhysicalDeviceObject);
+
+ return status;
+} // end ClassAddDevice()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassDispatchPnp()
+
+Routine Description:
+
+ Storage class driver pnp routine. This is called by the io system when
+ a PNP request is sent to the device.
+
+Arguments:
+
+ DeviceObject - pointer to the device object
+
+ Irp - pointer to the io request packet
+
+Return Value:
+
+ status
+
+--*/
+NTSTATUS
+ClassDispatchPnp(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ BOOLEAN isFdo = commonExtension->IsFdo;
+
+ PCLASS_DRIVER_EXTENSION driverExtension;
+ PCLASS_INIT_DATA initData;
+ PCLASS_DEV_INFO devInfo;
+
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
+
+ NTSTATUS status = Irp->IoStatus.Status;
+ BOOLEAN completeRequest = TRUE;
+ BOOLEAN lockReleased = FALSE;
+
+
+ PAGED_CODE();
+
+ //
+ // Extract all the useful information out of the driver object
+ // extension
+ //
+
+#pragma warning(suppress:4054) // okay to type cast function pointer as data pointer for this use case
+ driverExtension = IoGetDriverObjectExtension(DeviceObject->DriverObject, CLASS_DRIVER_EXTENSION_KEY);
+
+ if (driverExtension){
+
+ initData = &(driverExtension->InitData);
+
+ if(isFdo) {
+ devInfo = &(initData->FdoData);
+ } else {
+ devInfo = &(initData->PdoData);
+ }
+
+ ClassAcquireRemoveLock(DeviceObject, Irp);
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "ClassDispatchPnp (%p,%p): minor code %#x for %s %p\n",
+ DeviceObject, Irp,
+ irpStack->MinorFunction,
+ isFdo ? "fdo" : "pdo",
+ DeviceObject));
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "ClassDispatchPnp (%p,%p): previous %#x, current %#x\n",
+ DeviceObject, Irp,
+ commonExtension->PreviousState,
+ commonExtension->CurrentState));
+
+
+ switch(irpStack->MinorFunction) {
+
+ case IRP_MN_START_DEVICE: {
+
+ //
+ // if this is sent to the FDO we should forward it down the
+ // attachment chain before we start the FDO.
+ //
+
+ if (isFdo) {
+ status = ClassForwardIrpSynchronous(commonExtension, Irp);
+ }
+ else {
+ status = STATUS_SUCCESS;
+ }
+
+ if (NT_SUCCESS(status)){
+ status = Irp->IoStatus.Status = ClassPnpStartDevice(DeviceObject);
+ }
+
+ break;
+ }
+
+
+ case IRP_MN_QUERY_DEVICE_RELATIONS: {
+
+ DEVICE_RELATION_TYPE type =
+ irpStack->Parameters.QueryDeviceRelations.Type;
+
+ PDEVICE_RELATIONS deviceRelations = NULL;
+
+
+ if(!isFdo) {
+
+ if(type == TargetDeviceRelation) {
+
+ //
+ // Device relations has one entry built in to it's size.
+ //
+
+ status = STATUS_INSUFFICIENT_RESOURCES;
+
+ deviceRelations = ExAllocatePoolWithTag(PagedPool,
+ sizeof(DEVICE_RELATIONS),
+ '2CcS');
+
+ if(deviceRelations != NULL) {
+
+ RtlZeroMemory(deviceRelations,
+ sizeof(DEVICE_RELATIONS));
+
+ Irp->IoStatus.Information = (ULONG_PTR) deviceRelations;
+
+ deviceRelations->Count = 1;
+ deviceRelations->Objects[0] = DeviceObject;
+ ObReferenceObject(deviceRelations->Objects[0]);
+
+ status = STATUS_SUCCESS;
+ }
+
+ } else {
+ //
+ // PDO's just complete enumeration requests without altering
+ // the status.
+ //
+
+ status = Irp->IoStatus.Status;
+ }
+
+ break;
+
+ } else if (type == BusRelations) {
+
+ NT_ASSERT(commonExtension->IsInitialized);
+
+ //
+ // Make sure we support enumeration
+ //
+
+ if(initData->ClassEnumerateDevice == NULL) {
+
+ //
+ // Just send the request down to the lower driver. Perhaps
+ // It can enumerate children.
+ //
+
+ } else {
+
+ //
+ // Re-enumerate the device
+ //
+
+ status = ClassPnpQueryFdoRelations(DeviceObject, Irp);
+
+ if(!NT_SUCCESS(status)) {
+ completeRequest = TRUE;
+ break;
+ }
+ }
+ }
+
+
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+ completeRequest = FALSE;
+
+ break;
+ }
+
+ case IRP_MN_QUERY_ID: {
+
+ BUS_QUERY_ID_TYPE idType = irpStack->Parameters.QueryId.IdType;
+ UNICODE_STRING unicodeString;
+
+
+ if(isFdo) {
+
+
+ //
+ // FDO's should just forward the query down to the lower
+ // device objects
+ //
+
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+
+ status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+ completeRequest = FALSE;
+ break;
+ }
+
+ //
+ // PDO's need to give an answer - this is easy for now
+ //
+
+ RtlInitUnicodeString(&unicodeString, NULL);
+
+ status = ClassGetPdoId(DeviceObject,
+ idType,
+ &unicodeString);
+
+ if(status == STATUS_NOT_IMPLEMENTED) {
+ //
+ // The driver doesn't implement this ID (whatever it is).
+ // Use the status out of the IRP so that we don't mangle a
+ // response from someone else.
+ //
+
+ status = Irp->IoStatus.Status;
+ } else if(NT_SUCCESS(status)) {
+ Irp->IoStatus.Information = (ULONG_PTR) unicodeString.Buffer;
+ } else {
+ Irp->IoStatus.Information = (ULONG_PTR) NULL;
+ }
+
+ break;
+ }
+
+ case IRP_MN_QUERY_STOP_DEVICE:
+ case IRP_MN_QUERY_REMOVE_DEVICE: {
+
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "ClassDispatchPnp (%p,%p): Processing QUERY_%s irp\n",
+ DeviceObject, Irp,
+ ((irpStack->MinorFunction == IRP_MN_QUERY_STOP_DEVICE) ?
+ "STOP" : "REMOVE")));
+
+ //
+ // If this device is in use for some reason (paging, etc...)
+ // then we need to fail the request.
+ //
+
+ if(commonExtension->PagingPathCount != 0) {
+
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_PNP, "ClassDispatchPnp (%p,%p): device is in paging "
+ "path and cannot be removed\n",
+ DeviceObject, Irp));
+ status = STATUS_DEVICE_BUSY;
+ break;
+ }
+
+
+ //
+ // Check with the class driver to see if the query operation
+ // can succeed.
+ //
+
+ if(irpStack->MinorFunction == IRP_MN_QUERY_STOP_DEVICE) {
+ status = devInfo->ClassStopDevice(DeviceObject,
+ irpStack->MinorFunction);
+ } else {
+ status = devInfo->ClassRemoveDevice(DeviceObject,
+ irpStack->MinorFunction);
+ }
+
+ if(NT_SUCCESS(status)) {
+
+ //
+ // ASSERT that we never get two queries in a row, as
+ // this will severly mess up the state machine
+ //
+ NT_ASSERT(commonExtension->CurrentState != irpStack->MinorFunction);
+ commonExtension->PreviousState = commonExtension->CurrentState;
+ commonExtension->CurrentState = irpStack->MinorFunction;
+
+ if(isFdo) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "ClassDispatchPnp (%p,%p): Forwarding QUERY_"
+ "%s irp\n", DeviceObject, Irp,
+ ((irpStack->MinorFunction == IRP_MN_QUERY_STOP_DEVICE) ?
+ "STOP" : "REMOVE")));
+ status = ClassForwardIrpSynchronous(commonExtension, Irp);
+ }
+ }
+
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "ClassDispatchPnp (%p,%p): Final status == %x\n",
+ DeviceObject, Irp, status));
+
+ break;
+ }
+
+ case IRP_MN_CANCEL_STOP_DEVICE:
+ case IRP_MN_CANCEL_REMOVE_DEVICE: {
+
+
+ //
+ // Check with the class driver to see if the query or cancel
+ // operation can succeed.
+ //
+
+ if(irpStack->MinorFunction == IRP_MN_CANCEL_STOP_DEVICE) {
+ status = devInfo->ClassStopDevice(DeviceObject,
+ irpStack->MinorFunction);
+ NT_ASSERTMSG("ClassDispatchPnp !! CANCEL_STOP_DEVICE should never be failed\n", NT_SUCCESS(status));
+ } else {
+ status = devInfo->ClassRemoveDevice(DeviceObject,
+ irpStack->MinorFunction);
+ NT_ASSERTMSG("ClassDispatchPnp !! CANCEL_REMOVE_DEVICE should never be failed\n", NT_SUCCESS(status));
+ }
+
+ Irp->IoStatus.Status = status;
+
+ //
+ // We got a CANCEL - roll back to the previous state only
+ // if the current state is the respective QUERY state.
+ //
+
+ if(((irpStack->MinorFunction == IRP_MN_CANCEL_STOP_DEVICE) &&
+ (commonExtension->CurrentState == IRP_MN_QUERY_STOP_DEVICE)
+ ) ||
+ ((irpStack->MinorFunction == IRP_MN_CANCEL_REMOVE_DEVICE) &&
+ (commonExtension->CurrentState == IRP_MN_QUERY_REMOVE_DEVICE)
+ )
+ ) {
+
+ commonExtension->CurrentState =
+ commonExtension->PreviousState;
+ commonExtension->PreviousState = 0xff;
+
+ }
+
+
+ if(isFdo) {
+
+
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+ completeRequest = FALSE;
+ } else {
+ status = STATUS_SUCCESS;
+ }
+
+ break;
+ }
+
+ case IRP_MN_STOP_DEVICE: {
+
+
+ //
+ // These all mean nothing to the class driver currently. The
+ // port driver will handle all queueing when necessary.
+ //
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "ClassDispatchPnp (%p,%p): got stop request for %s\n",
+ DeviceObject, Irp,
+ (isFdo ? "fdo" : "pdo")
+ ));
+
+ NT_ASSERT(commonExtension->PagingPathCount == 0);
+
+ //
+ // ISSUE-2000/02/03-peterwie
+ // if we stop the timer here then it means no class driver can
+ // do i/o in its ClassStopDevice routine. This is because the
+ // retry (among other things) is tied into the tick handler
+ // and disabling retries could cause the class driver to deadlock.
+ // Currently no class driver we're aware of issues i/o in its
+ // Stop routine but this is a case we may want to defend ourself
+ // against.
+ //
+
+ ClasspDisableTimer((PFUNCTIONAL_DEVICE_EXTENSION)commonExtension);
+
+
+ status = devInfo->ClassStopDevice(DeviceObject, IRP_MN_STOP_DEVICE);
+
+ NT_ASSERTMSG("ClassDispatchPnp !! STOP_DEVICE should never be failed\n", NT_SUCCESS(status));
+
+ if(isFdo) {
+ status = ClassForwardIrpSynchronous(commonExtension, Irp);
+ }
+
+ if(NT_SUCCESS(status)) {
+ commonExtension->CurrentState = irpStack->MinorFunction;
+ commonExtension->PreviousState = 0xff;
+ }
+
+
+ break;
+ }
+
+ case IRP_MN_REMOVE_DEVICE:
+ case IRP_MN_SURPRISE_REMOVAL: {
+ UCHAR removeType = irpStack->MinorFunction;
+
+ //
+ // Log a sytem event when non-removable disks are surprise-removed.
+ //
+ if (isFdo &&
+ (removeType == IRP_MN_SURPRISE_REMOVAL)) {
+
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
+ BOOLEAN logSurpriseRemove = TRUE;
+ STORAGE_BUS_TYPE busType = fdoExtension->DeviceDescriptor->BusType;
+
+ //
+ // Don't log an event for VHDs
+ //
+ if (busType == BusTypeFileBackedVirtual) {
+ logSurpriseRemove = FALSE;
+
+ } else if (fdoData->HotplugInfo.MediaRemovable) {
+ logSurpriseRemove = FALSE;
+
+ } else if (fdoData->HotplugInfo.DeviceHotplug && ( busType == BusTypeUsb || busType == BusType1394)) {
+
+ /*
+ This device is reported as DeviceHotplug but since the busType is Usb or 1394, don't log an event
+ Note that some storage arrays may report DeviceHotplug and we would like to log an event in those cases
+ */
+
+ logSurpriseRemove = FALSE;
+ }
+
+ if (logSurpriseRemove) {
+
+ ClasspLogSystemEventWithDeviceNumber(DeviceObject, IO_WARNING_DISK_SURPRISE_REMOVED);
+ }
+
+ }
+
+ if (commonExtension->PagingPathCount != 0) {
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_PNP, "ClassDispatchPnp (%p,%p): paging device is getting removed!", DeviceObject, Irp));
+ }
+
+ //
+ // Release the lock for this IRP before calling in.
+ //
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ lockReleased = TRUE;
+
+ /*
+ * Set IsRemoved before propagating the REMOVE down the stack.
+ * This keeps class-initiated I/O (e.g. the MCN irp) from getting sent
+ * after we propagate the remove.
+ */
+ commonExtension->IsRemoved = REMOVE_PENDING;
+
+ /*
+ * If a timer was started on the device, stop it.
+ */
+ ClasspDisableTimer((PFUNCTIONAL_DEVICE_EXTENSION)commonExtension);
+
+ /*
+ * "Fire-and-forget" the remove irp to the lower stack.
+ * Don't touch the irp (or the irp stack!) after this.
+ */
+ if (isFdo) {
+
+
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+ status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+ NT_ASSERT(NT_SUCCESS(status));
+ completeRequest = FALSE;
+ }
+ else {
+ status = STATUS_SUCCESS;
+ }
+
+ /*
+ * Do our own cleanup and call the class driver's remove
+ * cleanup routine.
+ * For IRP_MN_REMOVE_DEVICE, this also deletes our device object,
+ * so don't touch the extension after this.
+ */
+ commonExtension->PreviousState = commonExtension->CurrentState;
+ commonExtension->CurrentState = removeType;
+ ClassRemoveDevice(DeviceObject, removeType);
+
+ break;
+ }
+
+ case IRP_MN_DEVICE_USAGE_NOTIFICATION: {
+
+ DEVICE_USAGE_NOTIFICATION_TYPE type = irpStack->Parameters.UsageNotification.Type;
+ BOOLEAN setPagable;
+
+
+ switch(type) {
+
+ case DeviceUsageTypePaging: {
+
+ if ((irpStack->Parameters.UsageNotification.InPath) &&
+ (commonExtension->CurrentState != IRP_MN_START_DEVICE)) {
+
+ //
+ // Device isn't started. Don't allow adding a
+ // paging file, but allow a removal of one.
+ //
+
+ status = STATUS_DEVICE_NOT_READY;
+ break;
+ }
+
+ NT_ASSERT(commonExtension->IsInitialized);
+
+ /*
+ * Ensure that this user thread is not suspended while we are holding the PathCountEvent.
+ */
+ KeEnterCriticalRegion();
+
+ (VOID)KeWaitForSingleObject(&commonExtension->PathCountEvent,
+ Executive, KernelMode,
+ FALSE, NULL);
+ status = STATUS_SUCCESS;
+
+ //
+ // If the volume is removable we should try to lock it in
+ // place or unlock it once per paging path count
+ //
+
+ if (commonExtension->IsFdo){
+ status = ClasspEjectionControl(
+ DeviceObject,
+ Irp,
+ InternalMediaLock,
+ (BOOLEAN)irpStack->Parameters.UsageNotification.InPath);
+ }
+
+ if (!NT_SUCCESS(status)){
+ KeSetEvent(&commonExtension->PathCountEvent, IO_NO_INCREMENT, FALSE);
+ KeLeaveCriticalRegion();
+ break;
+ }
+
+ //
+ // if removing last paging device, need to set DO_POWER_PAGABLE
+ // bit here, and possible re-set it below on failure.
+ //
+
+ setPagable = FALSE;
+
+ if ((!irpStack->Parameters.UsageNotification.InPath) &&
+ (commonExtension->PagingPathCount == 1)) {
+
+ //
+ // removing last paging file
+ // must have DO_POWER_PAGABLE bits set, but only
+ // if none set the DO_POWER_INRUSH bit and no other special files
+ //
+
+ if (TEST_FLAG(DeviceObject->Flags, DO_POWER_INRUSH)) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "ClassDispatchPnp (%p,%p): Last "
+ "paging file removed, but "
+ "DO_POWER_INRUSH was set, so NOT "
+ "setting DO_POWER_PAGABLE\n",
+ DeviceObject, Irp));
+ } else if ((commonExtension->HibernationPathCount == 0) &&
+ (commonExtension->DumpPathCount == 0)) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "ClassDispatchPnp (%p,%p): Last "
+ "paging file removed, "
+ "setting DO_POWER_PAGABLE\n",
+ DeviceObject, Irp));
+ SET_FLAG(DeviceObject->Flags, DO_POWER_PAGABLE);
+ setPagable = TRUE;
+ }
+
+ }
+
+ //
+ // forward the irp before finishing handling the
+ // special cases
+ //
+
+ status = ClassForwardIrpSynchronous(commonExtension, Irp);
+
+ //
+ // now deal with the failure and success cases.
+ // note that we are not allowed to fail the irp
+ // once it is sent to the lower drivers.
+ //
+
+ if (NT_SUCCESS(status)) {
+
+ IoAdjustPagingPathCount(
+ (volatile LONG *)&commonExtension->PagingPathCount,
+ irpStack->Parameters.UsageNotification.InPath);
+
+ if (irpStack->Parameters.UsageNotification.InPath) {
+ if (commonExtension->PagingPathCount == 1) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "ClassDispatchPnp (%p,%p): "
+ "Clearing PAGABLE bit\n",
+ DeviceObject, Irp));
+ CLEAR_FLAG(DeviceObject->Flags, DO_POWER_PAGABLE);
+
+
+ }
+
+ }
+
+ } else {
+
+ //
+ // cleanup the changes done above
+ //
+
+ if (setPagable == TRUE) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "ClassDispatchPnp (%p,%p): Unsetting "
+ "PAGABLE bit due to irp failure\n",
+ DeviceObject, Irp));
+ CLEAR_FLAG(DeviceObject->Flags, DO_POWER_PAGABLE);
+ setPagable = FALSE;
+ }
+
+ //
+ // relock or unlock the media if needed.
+ //
+
+ if (commonExtension->IsFdo) {
+
+ ClasspEjectionControl(
+ DeviceObject,
+ Irp,
+ InternalMediaLock,
+ (BOOLEAN)!irpStack->Parameters.UsageNotification.InPath);
+ }
+ }
+
+ //
+ // set the event so the next one can occur.
+ //
+
+ KeSetEvent(&commonExtension->PathCountEvent,
+ IO_NO_INCREMENT, FALSE);
+ KeLeaveCriticalRegion();
+ break;
+ }
+
+ case DeviceUsageTypeHibernation: {
+
+ //
+ // if removing last hiber device, need to set DO_POWER_PAGABLE
+ // bit here, and possible re-set it below on failure.
+ //
+
+ setPagable = FALSE;
+
+ if ((!irpStack->Parameters.UsageNotification.InPath) &&
+ (commonExtension->HibernationPathCount == 1)) {
+
+ //
+ // removing last hiber file
+ // must have DO_POWER_PAGABLE bits set, but only
+ // if none set the DO_POWER_INRUSH bit and no other special files
+ //
+
+ if (TEST_FLAG(DeviceObject->Flags, DO_POWER_INRUSH)) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "ClassDispatchPnp (%p,%p): Last "
+ "hiber file removed, but "
+ "DO_POWER_INRUSH was set, so NOT "
+ "setting DO_POWER_PAGABLE\n",
+ DeviceObject, Irp));
+ } else if ((commonExtension->PagingPathCount == 0) &&
+ (commonExtension->DumpPathCount == 0)) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "ClassDispatchPnp (%p,%p): Last "
+ "hiber file removed, "
+ "setting DO_POWER_PAGABLE\n",
+ DeviceObject, Irp));
+ SET_FLAG(DeviceObject->Flags, DO_POWER_PAGABLE);
+ setPagable = TRUE;
+ }
+
+ }
+
+ status = ClassForwardIrpSynchronous(commonExtension, Irp);
+ if (!NT_SUCCESS(status)) {
+
+ if (setPagable == TRUE) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "ClassDispatchPnp (%p,%p): Unsetting "
+ "PAGABLE bit due to irp failure\n",
+ DeviceObject, Irp));
+ CLEAR_FLAG(DeviceObject->Flags, DO_POWER_PAGABLE);
+ setPagable = FALSE;
+ }
+
+ } else {
+
+ IoAdjustPagingPathCount(
+ (volatile LONG *)&commonExtension->HibernationPathCount,
+ irpStack->Parameters.UsageNotification.InPath
+ );
+
+ if ((irpStack->Parameters.UsageNotification.InPath) &&
+ (commonExtension->HibernationPathCount == 1)) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "ClassDispatchPnp (%p,%p): "
+ "Clearing PAGABLE bit\n",
+ DeviceObject, Irp));
+ CLEAR_FLAG(DeviceObject->Flags, DO_POWER_PAGABLE);
+ }
+ }
+
+ break;
+ }
+
+ case DeviceUsageTypeDumpFile: {
+
+ //
+ // if removing last dump device, need to set DO_POWER_PAGABLE
+ // bit here, and possible re-set it below on failure.
+ //
+
+ setPagable = FALSE;
+
+ if ((!irpStack->Parameters.UsageNotification.InPath) &&
+ (commonExtension->DumpPathCount == 1)) {
+
+ //
+ // removing last dump file
+ // must have DO_POWER_PAGABLE bits set, but only
+ // if none set the DO_POWER_INRUSH bit and no other special files
+ //
+
+ if (TEST_FLAG(DeviceObject->Flags, DO_POWER_INRUSH)) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "ClassDispatchPnp (%p,%p): Last "
+ "dump file removed, but "
+ "DO_POWER_INRUSH was set, so NOT "
+ "setting DO_POWER_PAGABLE\n",
+ DeviceObject, Irp));
+ } else if ((commonExtension->PagingPathCount == 0) &&
+ (commonExtension->HibernationPathCount == 0)) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "ClassDispatchPnp (%p,%p): Last "
+ "dump file removed, "
+ "setting DO_POWER_PAGABLE\n",
+ DeviceObject, Irp));
+ SET_FLAG(DeviceObject->Flags, DO_POWER_PAGABLE);
+ setPagable = TRUE;
+ }
+
+ }
+
+ status = ClassForwardIrpSynchronous(commonExtension, Irp);
+ if (!NT_SUCCESS(status)) {
+
+ if (setPagable == TRUE) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "ClassDispatchPnp (%p,%p): Unsetting "
+ "PAGABLE bit due to irp failure\n",
+ DeviceObject, Irp));
+ CLEAR_FLAG(DeviceObject->Flags, DO_POWER_PAGABLE);
+ setPagable = FALSE;
+ }
+
+ } else {
+
+ IoAdjustPagingPathCount(
+ (volatile LONG *)&commonExtension->DumpPathCount,
+ irpStack->Parameters.UsageNotification.InPath
+ );
+
+ if ((irpStack->Parameters.UsageNotification.InPath) &&
+ (commonExtension->DumpPathCount == 1)) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "ClassDispatchPnp (%p,%p): "
+ "Clearing PAGABLE bit\n",
+ DeviceObject, Irp));
+ CLEAR_FLAG(DeviceObject->Flags, DO_POWER_PAGABLE);
+ }
+ }
+
+ break;
+ }
+
+ case DeviceUsageTypeBoot: {
+
+ if (isFdo) {
+ PCLASS_PRIVATE_FDO_DATA fdoData;
+
+ fdoData = ((PFUNCTIONAL_DEVICE_EXTENSION)(DeviceObject->DeviceExtension))->PrivateFdoData;
+
+
+ //
+ // If boot disk has removal policy as RemovalPolicyExpectSurpriseRemoval (e.g. disk is hotplug-able),
+ // change the removal policy to RemovalPolicyExpectOrderlyRemoval.
+ // This will cause the write cache of disk to be enabled on subsequent start Fdo (next boot).
+ //
+ if ((fdoData != NULL) &&
+ fdoData->HotplugInfo.DeviceHotplug) {
+
+ fdoData->HotplugInfo.DeviceHotplug = FALSE;
+ fdoData->HotplugInfo.MediaRemovable = FALSE;
+
+ ClassSetDeviceParameter((PFUNCTIONAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension,
+ CLASSP_REG_SUBKEY_NAME,
+ CLASSP_REG_REMOVAL_POLICY_VALUE_NAME,
+ RemovalPolicyExpectOrderlyRemoval);
+ }
+ }
+
+ status = ClassForwardIrpSynchronous(commonExtension, Irp);
+ break;
+ }
+
+ default: {
+ status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+ }
+ break;
+ }
+
+ case IRP_MN_QUERY_CAPABILITIES: {
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "ClassDispatchPnp (%p,%p): QueryCapabilities\n",
+ DeviceObject, Irp));
+
+ if(!isFdo) {
+
+ status = ClassQueryPnpCapabilities(
+ DeviceObject,
+ irpStack->Parameters.DeviceCapabilities.Capabilities
+ );
+
+ break;
+
+ } else {
+
+ PDEVICE_CAPABILITIES deviceCapabilities;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData;
+
+ fdoExtension = DeviceObject->DeviceExtension;
+ fdoData = fdoExtension->PrivateFdoData;
+ deviceCapabilities =
+ irpStack->Parameters.DeviceCapabilities.Capabilities;
+
+ //
+ // forward the irp before handling the special cases
+ //
+
+ status = ClassForwardIrpSynchronous(commonExtension, Irp);
+ if (!NT_SUCCESS(status)) {
+ break;
+ }
+
+ //
+ // we generally want to remove the device from the hotplug
+ // applet, which requires the SR-OK bit to be set.
+ // only when the user specifies that they are capable of
+ // safely removing things do we want to clear this bit
+ // (saved in WriteCacheEnableOverride)
+ //
+ // setting of this bit is done either above, or by the
+ // lower driver.
+ //
+ // note: may not be started, so check we have FDO data first.
+ //
+
+ if (fdoData &&
+ fdoData->HotplugInfo.WriteCacheEnableOverride) {
+ if (deviceCapabilities->SurpriseRemovalOK) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "Classpnp: Clearing SR-OK bit in "
+ "device capabilities due to hotplug "
+ "device or media\n"));
+ }
+ deviceCapabilities->SurpriseRemovalOK = FALSE;
+ }
+ break;
+
+ } // end QUERY_CAPABILITIES for FDOs
+
+ break;
+
+
+ } // end QUERY_CAPABILITIES
+
+ default: {
+
+ if (isFdo){
+
+
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+
+ completeRequest = FALSE;
+ }
+
+ break;
+ }
+ }
+ }
+ else {
+ NT_ASSERT(driverExtension);
+ status = STATUS_INTERNAL_ERROR;
+ }
+
+ if (completeRequest){
+ Irp->IoStatus.Status = status;
+
+ if (!lockReleased){
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ }
+
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "ClassDispatchPnp (%p,%p): leaving with previous %#x, current %#x.", DeviceObject, Irp, commonExtension->PreviousState, commonExtension->CurrentState));
+ }
+ else {
+ /*
+ * The irp is already completed so don't touch it.
+ * This may be a remove so don't touch the device extension.
+ */
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "ClassDispatchPnp (%p,%p): leaving.", DeviceObject, Irp));
+ }
+
+ return status;
+} // end ClassDispatchPnp()
+
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassPnpStartDevice()
+
+Routine Description:
+
+ Storage class driver routine for IRP_MN_START_DEVICE requests.
+ This routine kicks off any device specific initialization
+
+Arguments:
+
+ DeviceObject - a pointer to the device object
+
+ Irp - a pointer to the io request packet
+
+Return Value:
+
+ none
+
+--*/
+NTSTATUS ClassPnpStartDevice(IN PDEVICE_OBJECT DeviceObject)
+{
+ PCLASS_DRIVER_EXTENSION driverExtension;
+ PCLASS_INIT_DATA initData;
+
+ PCLASS_DEV_INFO devInfo;
+
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+ BOOLEAN isFdo = commonExtension->IsFdo;
+
+ BOOLEAN isMountedDevice = TRUE;
+ BOOLEAN isPortable = FALSE;
+
+ NTSTATUS status = STATUS_SUCCESS;
+ PDEVICE_POWER_DESCRIPTOR powerDescriptor = NULL;
+
+
+ PAGED_CODE();
+
+#pragma warning(suppress:4054) // okay to type cast function pointer as data pointer for this use case
+ driverExtension = IoGetDriverObjectExtension(DeviceObject->DriverObject, CLASS_DRIVER_EXTENSION_KEY);
+
+ initData = &(driverExtension->InitData);
+ if(isFdo) {
+ devInfo = &(initData->FdoData);
+ } else {
+ devInfo = &(initData->PdoData);
+ }
+
+ NT_ASSERT(devInfo->ClassInitDevice != NULL);
+ NT_ASSERT(devInfo->ClassStartDevice != NULL);
+
+ if (!commonExtension->IsInitialized){
+
+ //
+ // perform FDO/PDO specific initialization
+ //
+
+ if (isFdo){
+ STORAGE_PROPERTY_ID propertyId;
+
+ //
+ // allocate a private extension for class data
+ //
+
+ if (fdoExtension->PrivateFdoData == NULL) {
+ fdoExtension->PrivateFdoData = ExAllocatePoolWithTag(NonPagedPoolNx,
+ sizeof(CLASS_PRIVATE_FDO_DATA),
+ CLASS_TAG_PRIVATE_DATA
+ );
+ }
+
+ if (fdoExtension->PrivateFdoData == NULL) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP, "ClassPnpStartDevice: Cannot allocate for private fdo data\n"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlZeroMemory(fdoExtension->PrivateFdoData, sizeof(CLASS_PRIVATE_FDO_DATA));
+
+
+#if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD)
+ //
+ // Allocate a structure to hold more data than what we can put in FUNCTIONAL_DEVICE_EXTENSION.
+ // This structure's memory is managed by classpnp, so it is more extensible.
+ //
+ if (fdoExtension->AdditionalFdoData == NULL) {
+ fdoExtension->AdditionalFdoData = ExAllocatePoolWithTag(NonPagedPoolNx,
+ sizeof(ADDITIONAL_FDO_DATA),
+ CLASSPNP_POOL_TAG_ADDITIONAL_DATA
+ );
+ }
+
+ if (fdoExtension->AdditionalFdoData == NULL) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP, "ClassPnpStartDevice: Cannot allocate memory for the additional data structure.\n"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlZeroMemory(fdoExtension->AdditionalFdoData, sizeof(ADDITIONAL_FDO_DATA));
+#endif
+
+ status = ClasspInitializeTimer(fdoExtension);
+ if (NT_SUCCESS(status) == FALSE) {
+ FREE_POOL(fdoExtension->PrivateFdoData);
+#if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD)
+ FREE_POOL(fdoExtension->AdditionalFdoData);
+#endif
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP, "ClassPnpStartDevice: Failed to initialize tick timer\n"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ //
+ // allocate LowerLayerSupport for class data
+ //
+
+ if (fdoExtension->FunctionSupportInfo == NULL) {
+ fdoExtension->FunctionSupportInfo = (PCLASS_FUNCTION_SUPPORT_INFO)ExAllocatePoolWithTag(NonPagedPoolNx,
+ sizeof(CLASS_FUNCTION_SUPPORT_INFO),
+ '3BcS'
+ );
+ }
+
+ if (fdoExtension->FunctionSupportInfo == NULL) {
+ ClasspDeleteTimer(fdoExtension);
+ FREE_POOL(fdoExtension->PrivateFdoData);
+#if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD)
+ FREE_POOL(fdoExtension->AdditionalFdoData);
+#endif
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP, "ClassPnpStartDevice: Cannot allocate for FunctionSupportInfo\n"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ //
+ // initialize the struct's various fields.
+ //
+ RtlZeroMemory(fdoExtension->FunctionSupportInfo, sizeof(CLASS_FUNCTION_SUPPORT_INFO));
+ KeInitializeSpinLock(&fdoExtension->FunctionSupportInfo->SyncLock);
+
+ //
+ // intialize the CommandStatus to -1 indicates that no effort made yet to retrieve the info.
+ // Possible values of CommandStatus (data type: NTSTATUS):
+ // -1: It's not attempted yet to retrieve the information.
+ // success: Command sent and succeeded, information cached in FdoExtension.
+ // failed/warning: Command is either not supported or failed by device or lower level driver.
+ // The command should not be attempted again.
+ //
+ fdoExtension->FunctionSupportInfo->BlockLimitsData.CommandStatus = -1;
+ fdoExtension->FunctionSupportInfo->DeviceCharacteristicsData.CommandStatus = -1;
+ fdoExtension->FunctionSupportInfo->LBProvisioningData.CommandStatus = -1;
+ fdoExtension->FunctionSupportInfo->ReadCapacity16Data.CommandStatus = -1;
+ fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.CommandStatus = -1;
+
+
+ KeInitializeTimer(&fdoExtension->PrivateFdoData->Retry.Timer);
+ KeInitializeDpc(&fdoExtension->PrivateFdoData->Retry.Dpc,
+ ClasspRetryRequestDpc,
+ DeviceObject);
+ KeInitializeSpinLock(&fdoExtension->PrivateFdoData->Retry.Lock);
+ fdoExtension->PrivateFdoData->Retry.Granularity = KeQueryTimeIncrement();
+ commonExtension->Reserved4 = (ULONG_PTR)(' GPH'); // debug aid
+ InitializeListHead(&fdoExtension->PrivateFdoData->DeferredClientIrpList);
+
+ KeInitializeSpinLock(&fdoExtension->PrivateFdoData->SpinLock);
+
+ //
+ // keep a pointer to the senseinfo2 stuff locally also (used in every read/write).
+ //
+ fdoExtension->PrivateFdoData->InterpretSenseInfo = driverExtension->InterpretSenseInfo;
+
+ fdoExtension->PrivateFdoData->MaxNumberOfIoRetries = NUM_IO_RETRIES;
+
+ //
+ // Initialize release queue extended SRB
+ //
+ status = InitializeStorageRequestBlock(&(fdoExtension->PrivateFdoData->ReleaseQueueSrb.SrbEx),
+ STORAGE_ADDRESS_TYPE_BTL8,
+ sizeof(fdoExtension->PrivateFdoData->ReleaseQueueSrb.ReleaseQueueSrbBuffer),
+ 0);
+ if (!NT_SUCCESS(status)) {
+ NT_ASSERT(FALSE);
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP,
+ "ClassPnpStartDevice: fail to initialize release queue extended SRB 0x%x\n", status));
+ return status;
+ }
+
+
+ // Initialize performance counter frequency
+ KeQueryPerformanceCounter(&(fdoExtension->PrivateFdoData->PerfCounterFrequency));
+
+ if (fdoExtension->PrivateFdoData->PerfCounterFrequency.QuadPart == 0) {
+ fdoExtension->PrivateFdoData->PerfCounterFrequency.QuadPart = 1;
+ }
+
+ /*
+ * Anchor the FDO in our static list.
+ * Pnp is synchronized, so we shouldn't need any synchronization here.
+ */
+ InsertTailList(&AllFdosList, &fdoExtension->PrivateFdoData->AllFdosListEntry);
+
+ //
+ // NOTE: the old interface allowed the class driver to allocate
+ // this. this was unsafe for low-memory conditions. allocate one
+ // unconditionally now, and modify our internal functions to use
+ // our own exclusively as it is the only safe way to do this.
+ //
+
+ status = ClasspAllocateReleaseQueueIrp(fdoExtension);
+ if (!NT_SUCCESS(status)) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP, "ClassPnpStartDevice: Cannot allocate the private release queue irp\n"));
+ return status;
+ }
+
+ status = ClasspAllocatePowerProcessIrp(fdoExtension);
+ if (!NT_SUCCESS(status)) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP, "ClassPnpStartDevice: Cannot allocate the power process irp\n"));
+ return status;
+ }
+
+ //
+ // Call port driver to get miniport properties for disk devices
+ // It's ok for this call to fail
+ //
+
+ if ((DeviceObject->DeviceType == FILE_DEVICE_DISK) &&
+ (!TEST_FLAG(DeviceObject->Characteristics, FILE_FLOPPY_DISKETTE))) {
+
+ propertyId = StorageMiniportProperty;
+
+ status = ClassGetDescriptor(fdoExtension->CommonExtension.LowerDeviceObject,
+ &propertyId,
+ &fdoExtension->MiniportDescriptor);
+
+ //
+ // function ClassGetDescriptor returns succeed with buffer "fdoExtension->MiniportDescriptor" allocated.
+ //
+ if ( NT_SUCCESS(status) &&
+ (fdoExtension->MiniportDescriptor->Portdriver != StoragePortCodeSetStorport &&
+ fdoExtension->MiniportDescriptor->Portdriver != StoragePortCodeSetUSBport) ) {
+ //
+ // field "IoTimeoutValue" supported for either Storport or USBStor
+ //
+ fdoExtension->MiniportDescriptor->IoTimeoutValue = 0;
+ }
+
+
+
+ }
+
+ //
+ // Call port driver to get adapter capabilities.
+ //
+
+ propertyId = StorageAdapterProperty;
+
+ status = ClassGetDescriptor(
+ commonExtension->LowerDeviceObject,
+ &propertyId,
+ &fdoExtension->AdapterDescriptor);
+ if (!NT_SUCCESS(status)) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP, "ClassPnpStartDevice: ClassGetDescriptor [ADAPTER] failed %lx\n", status));
+ return status;
+ }
+
+ //
+ // Call port driver to get device descriptor.
+ //
+
+ propertyId = StorageDeviceProperty;
+
+ status = ClassGetDescriptor(
+ commonExtension->LowerDeviceObject,
+ &propertyId,
+ &fdoExtension->DeviceDescriptor);
+ if (NT_SUCCESS(status)){
+
+ ClasspScanForSpecialInRegistry(fdoExtension);
+ ClassScanForSpecial(fdoExtension, ClassBadItems, ClasspScanForClassHacks);
+
+ //
+ // allow perf to be re-enabled after a given number of failed IOs
+ // require this number to be at least CLASS_PERF_RESTORE_MINIMUM
+ //
+
+ {
+ ULONG t = CLASS_PERF_RESTORE_MINIMUM;
+
+ ClassGetDeviceParameter(fdoExtension,
+ CLASSP_REG_SUBKEY_NAME,
+ CLASSP_REG_PERF_RESTORE_VALUE_NAME,
+ &t);
+ if (t >= CLASS_PERF_RESTORE_MINIMUM) {
+ fdoExtension->PrivateFdoData->Perf.ReEnableThreshhold = t;
+ }
+ }
+
+ //
+ // compatibility comes first. writable cd media will not
+ // get a SYNCH_CACHE on power down.
+ //
+ if (fdoExtension->DeviceObject->DeviceType != FILE_DEVICE_DISK) {
+ SET_FLAG(fdoExtension->PrivateFdoData->HackFlags, FDO_HACK_NO_SYNC_CACHE);
+ }
+
+
+ //
+ // Test if the device is portable and updated the characteristics if so
+ //
+ status = ClasspIsPortable(fdoExtension,
+ &isPortable);
+
+ if (NT_SUCCESS(status) && (isPortable == TRUE)) {
+ DeviceObject->Characteristics |= FILE_PORTABLE_DEVICE;
+ }
+
+ //
+ // initialize the hotplug information only after the ScanForSpecial
+ // routines, as it relies upon the hack flags.
+ //
+ status = ClasspInitializeHotplugInfo(fdoExtension);
+ if (NT_SUCCESS(status)){
+ /*
+ * Allocate/initialize TRANSFER_PACKETs and related resources.
+ */
+ status = InitializeTransferPackets(DeviceObject);
+ }
+ else {
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_PNP, "ClassPnpStartDevice: Could not initialize hotplug information %lx\n", status));
+ }
+ }
+ else {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP, "ClassPnpStartDevice: ClassGetDescriptor [DEVICE] failed %lx\n", status));
+ return status;
+ }
+
+
+ if (NT_SUCCESS(status)) {
+
+ //
+ // Retrieve info on whether async notification is supported by port drivers
+ //
+ propertyId = StorageDevicePowerProperty;
+
+ status = ClassGetDescriptor(fdoExtension->CommonExtension.LowerDeviceObject,
+ &propertyId,
+ &powerDescriptor);
+ if (NT_SUCCESS(status) && (powerDescriptor != NULL)) {
+ fdoExtension->FunctionSupportInfo->AsynchronousNotificationSupported = powerDescriptor->AsynchronousNotificationSupported;
+ fdoExtension->FunctionSupportInfo->IdlePower.D3ColdSupported = powerDescriptor->D3ColdSupported;
+ fdoExtension->FunctionSupportInfo->IdlePower.NoVerifyDuringIdlePower = powerDescriptor->NoVerifyDuringIdlePower;
+ FREE_POOL(powerDescriptor);
+ } else {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP, "ClassPnpStartDevice: ClassGetDescriptor [DevicePower] failed %lx\n", status));
+
+ //
+ // Ignore error as device power property is optional
+ //
+ status = STATUS_SUCCESS;
+ }
+ }
+ }
+
+ //
+ // ISSUE - drivers need to disable write caching on the media
+ // if hotplug and !useroverride. perhaps we should
+ // allow registration of a callback to enable/disable
+ // write cache instead.
+ //
+
+ if (NT_SUCCESS(status)){
+ status = devInfo->ClassInitDevice(DeviceObject);
+ }
+
+ if (commonExtension->IsFdo) {
+ fdoExtension->PrivateFdoData->Perf.OriginalSrbFlags = fdoExtension->SrbFlags;
+
+ //
+ // initialization for disk device
+ //
+ if ((DeviceObject->DeviceType == FILE_DEVICE_DISK) &&
+ (!TEST_FLAG(DeviceObject->Characteristics, FILE_FLOPPY_DISKETTE))) {
+
+ ULONG accessAlignmentNotSupported = 0;
+ ULONG qerrOverrideMode = QERR_SET_ZERO_ODX_OR_TP_ONLY;
+ ULONG legacyErrorHandling = FALSE;
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP,
+ "ClassPnpStartDevice: Enabling idle timer for %p\n", DeviceObject));
+ // Initialize idle timer for disk devices
+ ClasspInitializeIdleTimer(fdoExtension);
+
+ if (ClasspIsObsoletePortDriver(fdoExtension) == FALSE) {
+ // get INQUIRY VPD support information. It's safe to send command as everything is ready in ClassInitDevice().
+ ClasspGetInquiryVpdSupportInfo(fdoExtension);
+
+ // Query and cache away Logical Block Provisioning info in the FDO extension.
+ // The cached information will be used in responding to some IOCTLs
+ ClasspGetLBProvisioningInfo(fdoExtension);
+
+ //
+ // Query and cache away Block Device ROD Limits info in the FDO extension.
+ //
+ if (fdoExtension->FunctionSupportInfo->ValidInquiryPages.BlockDeviceRODLimits) {
+ ClassDetermineTokenOperationCommandSupport(DeviceObject);
+ }
+
+ //
+ // See if the user has specified a particular QERR override
+ // mode. "Override" meaning setting QERR = 0 via Mode Select.
+ // 0 = Only when ODX or Thin Provisioning are supported (default)
+ // 1 = Always
+ // 2 = Never (or any value >= 2)
+ //
+ ClassGetDeviceParameter(fdoExtension,
+ CLASSP_REG_SUBKEY_NAME,
+ CLASSP_REG_QERR_OVERRIDE_MODE,
+ &qerrOverrideMode);
+
+ //
+ // If this device is thinly provisioned or supports ODX, we
+ // may need to force QERR to zero. The user may have also
+ // specified that we should always or never do this.
+ //
+ if (qerrOverrideMode == QERR_SET_ZERO_ALWAYS ||
+ (qerrOverrideMode == QERR_SET_ZERO_ODX_OR_TP_ONLY &&
+ (ClasspIsThinProvisioned(fdoExtension->FunctionSupportInfo) ||
+ NT_SUCCESS(ClasspValidateOffloadSupported(DeviceObject, NULL))))) {
+
+ ClasspZeroQERR(DeviceObject);
+ }
+
+ } else {
+
+ //
+ // Since this device has been exposed by a legacy miniport (e.g. SCSIPort miniport)
+ // set its LB Provisioning command status to an error status that will be surfaced
+ // up to the caller of a TRIM/Unmap command.
+ //
+ fdoExtension->FunctionSupportInfo->LBProvisioningData.CommandStatus = STATUS_UNSUCCESSFUL;
+ fdoExtension->FunctionSupportInfo->BlockLimitsData.CommandStatus = STATUS_UNSUCCESSFUL;
+ fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.CommandStatus = STATUS_UNSUCCESSFUL;
+ }
+
+ // Get registry setting of failing the IOCTL for AccessAlignment Property.
+ ClassGetDeviceParameter(fdoExtension,
+ CLASSP_REG_SUBKEY_NAME,
+ CLASSP_REG_ACCESS_ALIGNMENT_NOT_SUPPORTED,
+ &accessAlignmentNotSupported);
+
+ if (accessAlignmentNotSupported > 0) {
+ fdoExtension->FunctionSupportInfo->RegAccessAlignmentQueryNotSupported = TRUE;
+ }
+
+#if (NTDDI_VERSION >= NTDDI_WINBLUE)
+
+
+ //
+ // See if the user has specified legacy error handling.
+ //
+ ClassGetDeviceParameter(fdoExtension,
+ CLASSP_REG_SUBKEY_NAME,
+ CLASSP_REG_LEGACY_ERROR_HANDLING,
+ &legacyErrorHandling);
+
+ if (legacyErrorHandling) {
+ //
+ // Legacy error handling means that the maximum number of
+ // retries allowd for an IO request is 8 instead of 4.
+ //
+ fdoExtension->PrivateFdoData->MaxNumberOfIoRetries = LEGACY_NUM_IO_RETRIES;
+ fdoExtension->PrivateFdoData->LegacyErrorHandling = TRUE;
+ }
+#else
+ UNREFERENCED_PARAMETER(legacyErrorHandling);
+#endif
+
+ }
+
+ }
+ }
+
+ if (!NT_SUCCESS(status)){
+
+ //
+ // Just bail out - the remove that comes down will clean up the
+ // initialized scraps.
+ //
+
+ return status;
+ } else {
+ commonExtension->IsInitialized = TRUE;
+ }
+
+ //
+ // If device requests autorun functionality or a once a second callback
+ // then enable the once per second timer. Exception is if media change
+ // detection is desired but device supports async notification.
+ //
+ // NOTE: This assumes that ClassInitializeMediaChangeDetection is always
+ // called in the context of the ClassInitDevice callback. If called
+ // after then this check will have already been made and the
+ // once a second timer will not have been enabled.
+ //
+ if ((isFdo) &&
+ ((initData->ClassTick != NULL) ||
+ ((fdoExtension->MediaChangeDetectionInfo != NULL) &&
+ (fdoExtension->FunctionSupportInfo != NULL) &&
+ (fdoExtension->FunctionSupportInfo->AsynchronousNotificationSupported == FALSE)) ||
+ ((fdoExtension->FailurePredictionInfo != NULL) &&
+ (fdoExtension->FailurePredictionInfo->Method != FailurePredictionNone))))
+ {
+ ClasspEnableTimer(fdoExtension);
+
+ //
+ // In addition, we may change our polling behavior when the screen is
+ // off so register for screen state notification if we haven't already
+ // done so.
+ //
+ if (ScreenStateNotificationHandle == NULL) {
+ PoRegisterPowerSettingCallback(DeviceObject,
+ &GUID_CONSOLE_DISPLAY_STATE,
+ &ClasspPowerSettingCallback,
+ NULL,
+ &ScreenStateNotificationHandle);
+ }
+ }
+
+ //
+ // NOTE: the timer looks at commonExtension->CurrentState now
+ // to prevent Media Change Notification code from running
+ // until the device is started, but allows the device
+ // specific tick handler to run. therefore it is imperative
+ // that commonExtension->CurrentState not be updated until
+ // the device specific startdevice handler has finished.
+ //
+
+ status = devInfo->ClassStartDevice(DeviceObject);
+
+ if (NT_SUCCESS(status)){
+ commonExtension->CurrentState = IRP_MN_START_DEVICE;
+
+ if((isFdo) && (initData->ClassEnumerateDevice != NULL)) {
+ isMountedDevice = FALSE;
+ }
+
+ if (DeviceObject->DeviceType != FILE_DEVICE_CD_ROM) {
+
+ isMountedDevice = FALSE;
+ }
+
+ //
+ // Register for mounted device interface if this is a
+ // sfloppy device.
+ //
+ if ((DeviceObject->DeviceType == FILE_DEVICE_DISK) &&
+ (TEST_FLAG(DeviceObject->Characteristics, FILE_FLOPPY_DISKETTE))) {
+
+ isMountedDevice = TRUE;
+ }
+
+ if(isMountedDevice) {
+ ClasspRegisterMountedDeviceInterface(DeviceObject);
+ }
+
+ if(commonExtension->IsFdo) {
+ IoWMIRegistrationControl(DeviceObject, WMIREG_ACTION_REGISTER);
+
+ //
+ // Tell Storport (Usbstor or SD) to enable idle power management for this
+ // device, assuming the user hasn't turned it off in the registry.
+ //
+ if (fdoExtension->FunctionSupportInfo != NULL &&
+ fdoExtension->FunctionSupportInfo->IdlePower.IdlePowerEnabled == FALSE &&
+ fdoExtension->MiniportDescriptor != NULL &&
+ (fdoExtension->MiniportDescriptor->Portdriver == StoragePortCodeSetStorport ||
+ fdoExtension->MiniportDescriptor->Portdriver == StoragePortCodeSetSDport ||
+ fdoExtension->MiniportDescriptor->Portdriver == StoragePortCodeSetUSBport)) {
+ ULONG disableIdlePower= 0;
+ ClassGetDeviceParameter(fdoExtension,
+ CLASSP_REG_SUBKEY_NAME,
+ CLASSP_REG_DISBALE_IDLE_POWER_NAME,
+ &disableIdlePower);
+
+ if (!disableIdlePower) {
+ ClasspEnableIdlePower(DeviceObject);
+ }
+ }
+ }
+ }
+ else {
+ ClasspDisableTimer(fdoExtension);
+ }
+
+
+ return status;
+}
+
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassReadWrite()
+
+Routine Description:
+
+ This is the system entry point for read and write requests. The
+ device-specific handler is invoked to perform any validation necessary.
+
+ If the device object is a PDO (partition object) then the request will
+ simply be adjusted for Partition0 and issued to the lower device driver.
+
+ IF the device object is an FDO (paritition 0 object), the number of bytes
+ in the request are checked against the maximum byte counts that the adapter
+ supports and requests are broken up into
+ smaller sizes if necessary.
+
+Arguments:
+
+ DeviceObject - a pointer to the device object for this request
+
+ Irp - IO request
+
+Return Value:
+
+ NT Status
+
+--*/
+NTSTATUS ClassReadWrite(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PDEVICE_OBJECT lowerDeviceObject = commonExtension->LowerDeviceObject;
+ PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
+ ULONG transferByteCount = currentIrpStack->Parameters.Read.Length;
+ ULONG isRemoved;
+ NTSTATUS status;
+
+ /*
+ * Grab the remove lock. If we can't acquire it, bail out.
+ */
+ isRemoved = ClassAcquireRemoveLock(DeviceObject, Irp);
+ if (isRemoved) {
+ Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ status = STATUS_DEVICE_DOES_NOT_EXIST;
+ }
+ else if (TEST_FLAG(DeviceObject->Flags, DO_VERIFY_VOLUME) &&
+ (currentIrpStack->MinorFunction != CLASSP_VOLUME_VERIFY_CHECKED) &&
+ !TEST_FLAG(currentIrpStack->Flags, SL_OVERRIDE_VERIFY_VOLUME)){
+
+ /*
+ * DO_VERIFY_VOLUME is set for the device object,
+ * but this request is not itself a verify request.
+ * So fail this request.
+ */
+ if (Irp->Tail.Overlay.Thread != NULL) {
+ IoSetHardErrorOrVerifyDevice(Irp, DeviceObject);
+ }
+ Irp->IoStatus.Status = STATUS_VERIFY_REQUIRED;
+ Irp->IoStatus.Information = 0;
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, 0);
+ status = STATUS_VERIFY_REQUIRED;
+ }
+ else {
+
+ /*
+ * Since we've bypassed the verify-required tests we don't need to repeat
+ * them with this IRP - in particular we don't want to worry about
+ * hitting them at the partition 0 level if the request has come through
+ * a non-zero partition.
+ */
+ currentIrpStack->MinorFunction = CLASSP_VOLUME_VERIFY_CHECKED;
+
+ /*
+ * Call the miniport driver's pre-pass filter to check if we
+ * should continue with this transfer.
+ */
+ NT_ASSERT(commonExtension->DevInfo->ClassReadWriteVerification);
+ status = commonExtension->DevInfo->ClassReadWriteVerification(DeviceObject, Irp);
+ // Code Analysis cannot analyze the code paths specific to clients.
+ _Analysis_assume_(status != STATUS_PENDING);
+ if (!NT_SUCCESS(status)){
+ NT_ASSERT(Irp->IoStatus.Status == status);
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest (DeviceObject, Irp, IO_NO_INCREMENT);
+ }
+ else if (status == STATUS_PENDING){
+ /*
+ * ClassReadWriteVerification queued this request.
+ * So don't touch the irp anymore.
+ */
+ }
+ else {
+
+ if (transferByteCount == 0) {
+ /*
+ * Several parts of the code turn 0 into 0xffffffff,
+ * so don't process a zero-length request any further.
+ */
+ Irp->IoStatus.Status = STATUS_SUCCESS;
+ Irp->IoStatus.Information = 0;
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ status = STATUS_SUCCESS;
+ }
+ else {
+ /*
+ * If the driver has its own StartIo routine, call it.
+ */
+ if (commonExtension->DriverExtension->InitData.ClassStartIo) {
+ IoMarkIrpPending(Irp);
+ IoStartPacket(DeviceObject, Irp, NULL, NULL);
+ status = STATUS_PENDING;
+ }
+ else {
+ /*
+ * The driver does not have its own StartIo routine.
+ * So process this request ourselves.
+ */
+
+ /*
+ * Add partition byte offset to make starting byte relative to
+ * beginning of disk.
+ */
+ currentIrpStack->Parameters.Read.ByteOffset.QuadPart +=
+ commonExtension->StartingOffset.QuadPart;
+
+ if (commonExtension->IsFdo){
+
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
+
+ /*
+ * Add in any skew for the disk manager software.
+ */
+ currentIrpStack->Parameters.Read.ByteOffset.QuadPart +=
+ commonExtension->PartitionZeroExtension->DMByteSkew;
+
+ //
+ // In case DEV_USE_16BYTE_CDB flag is not set, R/W request will be translated into READ/WRITE 10 SCSI command.
+ // These SCSI commands have 4 bytes in "Starting LBA" field.
+ // Requests cannot be represented in these SCSI commands should be failed.
+ //
+ if (!TEST_FLAG(fdoExtension->DeviceFlags, DEV_USE_16BYTE_CDB)) {
+ LARGE_INTEGER startingLba;
+
+ startingLba.QuadPart = currentIrpStack->Parameters.Read.ByteOffset.QuadPart >> fdoExtension->SectorShift;
+
+ if (startingLba.QuadPart > MAXULONG) {
+ Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
+ Irp->IoStatus.Information = 0;
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ status = STATUS_INVALID_PARAMETER;
+ goto Exit;
+ }
+ }
+
+#if DBG
+ //
+ // Record the caller if:
+ // 1. the disk is currently off
+ // 2. the operation is a read, (likely resulting in disk spinnage)
+ // 3. the operation is marked WT (likely resulting in disk spinnage)
+ //
+ if((fdoExtension->DevicePowerState == PowerDeviceD3) && // disk is off
+ ((currentIrpStack->MajorFunction == IRP_MJ_READ) || // It's a read.
+ (TEST_FLAG(currentIrpStack->Flags, SL_WRITE_THROUGH))) ) { // they *really* want it to go to disk.
+
+ SnapDiskStartup();
+ }
+#endif
+
+ /*
+ * Perform the actual transfer(s) on the hardware
+ * to service this request.
+ */
+ if (ClasspIsIdleRequestSupported(fdoData, Irp)) {
+ ClasspMarkIrpAsIdle(Irp, TRUE);
+ status = ClasspEnqueueIdleRequest(DeviceObject, Irp);
+ } else {
+ UCHAR uniqueAddr = 0;
+
+ //
+ // Since we're touching fdoData after servicing the transfer packet, this opens us up to
+ // a potential window, where the device may be removed between the time that
+ // ServiceTransferPacket completes and we've had a chance to access fdoData. In order
+ // to guard against this, we acquire the removelock an additional time here. This
+ // acquire is guaranteed to succeed otherwise we wouldn't be here (because of the
+ // outer acquire).
+ // The sequence of events we're guarding against with this remLock acquire is:
+ // 1. This UL IRP acquired the lock.
+ // 2. Device gets surprised removed, then gets IRP_MN_REMOVE_DEVICE; ClassRemoveDevice
+ // waits for the above RemoveLock.
+ // 3. ServiceTransferRequest breaks the UL IRP into DL IRPs.
+ // 4. DL IRPs complete with STATUS_NO_SUCH_DEVICE and TransferPktComplete completes the UL
+ // IRP with STATUS_NO_SUCH_DEVICE; releases the RemoveLock.
+ // 5. ClassRemoveDevice is now unblocked, continues running and frees resources (including
+ // fdoData).
+ // 6. Finally ClassReadWrite gets to run again and accesses a freed fdoData when trying to
+ // check/update idle-related fields.
+ //
+ ClassAcquireRemoveLock(DeviceObject, (PVOID)&uniqueAddr);
+
+ ClasspMarkIrpAsIdle(Irp, FALSE);
+ status = ServiceTransferRequest(DeviceObject, Irp, FALSE);
+ if (fdoData->IdlePrioritySupported == TRUE) {
+ fdoData->LastIoTime = ClasspGetCurrentTime(NULL);
+ fdoData->IdleTicks = 0;
+ }
+
+ ClassReleaseRemoveLock(DeviceObject, (PVOID)&uniqueAddr);
+ }
+ }
+ else {
+ /*
+ * This is a child PDO enumerated for our FDO by e.g. disk.sys
+ * and owned by e.g. partmgr. Send it down to the next device
+ * and the same irp will come back to us for the FDO.
+ */
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ status = IoCallDriver(lowerDeviceObject, Irp);
+ }
+ }
+ }
+ }
+ }
+
+Exit:
+ return status;
+}
+
+
+VOID InterpretCapacityData(PDEVICE_OBJECT Fdo, PREAD_CAPACITY_DATA_EX ReadCapacityData)
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
+ ULONG cylinderSize;
+ ULONG bytesPerSector;
+ LARGE_INTEGER lastSector;
+ LARGE_INTEGER largeInt;
+
+ bytesPerSector = ClasspCalculateLogicalSectorSize(Fdo, ReadCapacityData->BytesPerBlock);
+
+ fdoExt->DiskGeometry.BytesPerSector = bytesPerSector;
+ WHICH_BIT(fdoExt->DiskGeometry.BytesPerSector, fdoExt->SectorShift);
+
+ /*
+ * LogicalBlockAddress is the last sector of the logical drive, in big-endian.
+ * It tells us the size of the drive (#sectors is lastSector+1).
+ */
+
+ largeInt = ReadCapacityData->LogicalBlockAddress;
+ REVERSE_BYTES_QUAD(&lastSector, &largeInt);
+
+ if (fdoExt->DMActive){
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT, "ClassReadDriveCapacity: reducing number of sectors by %d\n", fdoExt->DMSkew));
+ lastSector.QuadPart -= fdoExt->DMSkew;
+ }
+
+ /*
+ * Check to see if we have a geometry we should be using already.
+ * If not, we set part of the disk geometry to garbage values that will be filled in by the caller (e.g. disk.sys).
+ *
+ * So the first call to ClassReadDriveCapacity always sets a meaningless geometry.
+ * TracksPerCylinder and SectorsPerTrack are kind of meaningless anyway wrt I/O,
+ * because I/O is always targeted to a logical sector number.
+ * All that really matters is BytesPerSector and the number of sectors.
+ */
+ cylinderSize = fdoExt->DiskGeometry.TracksPerCylinder * fdoExt->DiskGeometry.SectorsPerTrack;
+ if (cylinderSize == 0){
+ fdoExt->DiskGeometry.TracksPerCylinder = 0xff;
+ fdoExt->DiskGeometry.SectorsPerTrack = 0x3f;
+ cylinderSize = fdoExt->DiskGeometry.TracksPerCylinder * fdoExt->DiskGeometry.SectorsPerTrack;
+ }
+
+ /*
+ * Calculate number of cylinders.
+ * If there are zero cylinders, then the device lied AND it's
+ * smaller than 0xff*0x3f (about 16k sectors, usually 8 meg)
+ * this can fit into a single LONGLONG, so create another usable
+ * geometry, even if it's unusual looking.
+ * This allows small, non-standard devices, such as Sony's Memory Stick, to show up as having a partition.
+ */
+ fdoExt->DiskGeometry.Cylinders.QuadPart = (LONGLONG)((lastSector.QuadPart + 1)/cylinderSize);
+ if (fdoExt->DiskGeometry.Cylinders.QuadPart == (LONGLONG)0) {
+ fdoExt->DiskGeometry.SectorsPerTrack = 1;
+ fdoExt->DiskGeometry.TracksPerCylinder = 1;
+ fdoExt->DiskGeometry.Cylinders.QuadPart = lastSector.QuadPart + 1;
+ }
+
+ /*
+ * Calculate media capacity in bytes.
+ * For this purpose we treat the entire LUN as is if it is one partition. Disk will deal with actual partitioning.
+ */
+ fdoExt->CommonExtension.PartitionLength.QuadPart =
+ ((LONGLONG)(lastSector.QuadPart + 1)) << fdoExt->SectorShift;
+
+ /*
+ * Is this removable or fixed media
+ */
+ if (TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)){
+ fdoExt->DiskGeometry.MediaType = RemovableMedia;
+ }
+ else {
+ fdoExt->DiskGeometry.MediaType = FixedMedia;
+ }
+
+}
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassReadDriveCapacity()
+
+Routine Description:
+
+ This routine sends a READ CAPACITY to the requested device, updates
+ the geometry information in the device object and returns
+ when it is complete. This routine is synchronous.
+
+ This routine must be called with the remove lock held or some other
+ assurance that the Fdo will not be removed while processing.
+
+Arguments:
+
+ DeviceObject - Supplies a pointer to the device object that represents
+ the device whose capacity is to be read.
+
+Return Value:
+
+ Status is returned.
+
+--*/
+_Must_inspect_result_
+NTSTATUS ClassReadDriveCapacity(_In_ PDEVICE_OBJECT Fdo)
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
+ PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
+ READ_CAPACITY_DATA_EX PTRALIGN readCapacityData = {0};
+ PTRANSFER_PACKET pkt;
+ NTSTATUS status;
+ PMDL driveCapMdl = NULL;
+ KEVENT event;
+ IRP pseudoIrp = {0};
+ ULONG readCapacityDataSize;
+ BOOLEAN use16ByteCdb;
+ BOOLEAN match = TRUE;
+
+ use16ByteCdb = TEST_FLAG(fdoExt->DeviceFlags, DEV_USE_16BYTE_CDB);
+
+RetryRequest:
+
+ if (use16ByteCdb) {
+ readCapacityDataSize = sizeof(READ_CAPACITY_DATA_EX);
+ } else {
+ readCapacityDataSize = sizeof(READ_CAPACITY_DATA);
+ }
+
+ if (driveCapMdl != NULL) {
+ FreeDeviceInputMdl(driveCapMdl);
+ driveCapMdl = NULL;
+ }
+
+ //
+ // Allocate the MDL based on the Read Capacity command.
+ //
+ driveCapMdl = BuildDeviceInputMdl(&readCapacityData, readCapacityDataSize);
+ if (driveCapMdl == NULL) {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ goto SafeExit;
+ }
+
+ pkt = DequeueFreeTransferPacket(Fdo, TRUE);
+ if (pkt == NULL) {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ goto SafeExit;
+ }
+
+ //
+ // Our engine needs an "original irp" to write the status back to
+ // and to count down packets (one in this case).
+ // Just use a pretend irp for this.
+ //
+
+ pseudoIrp.Tail.Overlay.DriverContext[0] = LongToPtr(1);
+ pseudoIrp.IoStatus.Status = STATUS_SUCCESS;
+ pseudoIrp.IoStatus.Information = 0;
+ pseudoIrp.MdlAddress = driveCapMdl;
+
+ //
+ // Set this up as a SYNCHRONOUS transfer, submit it,
+ // and wait for the packet to complete. The result
+ // status will be written to the original irp.
+ //
+
+ KeInitializeEvent(&event, SynchronizationEvent, FALSE);
+ SetupDriveCapacityTransferPacket(pkt,
+ &readCapacityData,
+ readCapacityDataSize,
+ &event,
+ &pseudoIrp,
+ use16ByteCdb);
+ SubmitTransferPacket(pkt);
+ (VOID)KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
+
+ status = pseudoIrp.IoStatus.Status;
+
+ //
+ // If we got an UNDERRUN, retry exactly once.
+ // (The transfer_packet engine didn't retry because the result
+ // status was success).
+ //
+
+ if (NT_SUCCESS(status) &&
+ (pseudoIrp.IoStatus.Information < readCapacityDataSize)) {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT, "ClassReadDriveCapacity: read len (%xh) < %xh, retrying ...",
+ (ULONG)pseudoIrp.IoStatus.Information, readCapacityDataSize));
+
+ pkt = DequeueFreeTransferPacket(Fdo, TRUE);
+ if (pkt) {
+ pseudoIrp.Tail.Overlay.DriverContext[0] = LongToPtr(1);
+ pseudoIrp.IoStatus.Status = STATUS_SUCCESS;
+ pseudoIrp.IoStatus.Information = 0;
+ KeInitializeEvent(&event, SynchronizationEvent, FALSE);
+ SetupDriveCapacityTransferPacket(pkt,
+ &readCapacityData,
+ readCapacityDataSize,
+ &event,
+ &pseudoIrp,
+ use16ByteCdb);
+ SubmitTransferPacket(pkt);
+ (VOID)KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
+ status = pseudoIrp.IoStatus.Status;
+ if (pseudoIrp.IoStatus.Information < readCapacityDataSize){
+ status = STATUS_DEVICE_BUSY;
+ }
+ } else {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+ }
+
+ if (NT_SUCCESS(status)) {
+ //
+ // The request succeeded. Check for 8 byte LBA support.
+ //
+
+ if (use16ByteCdb == FALSE) {
+
+ PREAD_CAPACITY_DATA readCapacity;
+
+ //
+ // Check whether the device supports 8 byte LBA. If the device supports
+ // it then retry the request using 16 byte CDB.
+ //
+
+ readCapacity = (PREAD_CAPACITY_DATA) &readCapacityData;
+
+ if (readCapacity->LogicalBlockAddress == 0xFFFFFFFF) {
+ //
+ // Device returned max size for last LBA. Need to send
+ // 16 byte request to get the size.
+ //
+ use16ByteCdb = TRUE;
+ goto RetryRequest;
+
+ } else {
+ //
+ // Convert the 4 byte LBA (READ_CAPACITY_DATA) to 8 byte LBA (READ_CAPACITY_DATA_EX)
+ // format for ease of use. This is the only format stored in the device extension.
+ //
+
+ RtlMoveMemory((PUCHAR)(&readCapacityData) + sizeof(ULONG), readCapacity, sizeof(READ_CAPACITY_DATA));
+ RtlZeroMemory((PUCHAR)(&readCapacityData), sizeof(ULONG));
+
+ }
+ } else {
+ //
+ // Device completed 16 byte command successfully, it supports 8-byte LBA.
+ //
+
+ SET_FLAG(fdoExt->DeviceFlags, DEV_USE_16BYTE_CDB);
+ }
+
+ //
+ // Read out and store the drive information.
+ //
+
+ InterpretCapacityData(Fdo, &readCapacityData);
+
+ //
+ // Before caching the new drive capacity, compare it with the
+ // cached capacity for any change.
+ //
+
+ if (fdoData->IsCachedDriveCapDataValid == TRUE) {
+
+ match = (BOOLEAN) RtlEqualMemory(&fdoData->LastKnownDriveCapacityData,
+ &readCapacityData, sizeof(READ_CAPACITY_DATA_EX));
+ }
+
+ //
+ // Store the readCapacityData in private FDO data.
+ // This is so that runtime memory failures don't cause disk.sys to put
+ // the paging disk in an error state. Also this is used in
+ // IOCTL_STORAGE_READ_CAPACITY.
+ //
+ fdoData->LastKnownDriveCapacityData = readCapacityData;
+ fdoData->IsCachedDriveCapDataValid = TRUE;
+
+ if (match == FALSE) {
+ if (commonExtension->CurrentState != IRP_MN_START_DEVICE)
+ {
+ //
+ // This can happen if a disk reports Parameters Changed / Capacity Data Changed sense data.
+ // NT_ASSERT(!"Drive capacity has changed while the device wasn't started!");
+ //
+ } else {
+ //
+ // state of (commonExtension->CurrentState == IRP_MN_START_DEVICE) indicates that the device has been started.
+ // UpdateDiskPropertiesWorkItemActive is used as a flag to ensure we only have one work item updating the disk
+ // properties at a time.
+ //
+ if (InterlockedCompareExchange((volatile LONG *)&fdoData->UpdateDiskPropertiesWorkItemActive, 1, 0) == 0)
+ {
+ PIO_WORKITEM workItem;
+
+ workItem = IoAllocateWorkItem(Fdo);
+
+ if (workItem) {
+ //
+ // The disk capacity has changed, send notification to the disk driver.
+ // Start a work item to notify the disk class driver asynchronously.
+ //
+ IoQueueWorkItem(workItem, ClasspUpdateDiskProperties, DelayedWorkQueue, workItem);
+ } else {
+ InterlockedExchange((volatile LONG *)&fdoData->UpdateDiskPropertiesWorkItemActive, 0);
+ }
+ }
+ }
+ }
+
+ } else {
+ //
+ // The request failed.
+ //
+
+ //
+ // ISSUE - 2000/02/04 - henrygab - non-512-byte sector sizes and failed geometry update
+ // what happens when the disk's sector size is bigger than
+ // 512 bytes and we hit this code path? this is untested.
+ //
+ // If the read capacity fails, set the geometry to reasonable parameter
+ // so things don't fail at unexpected places. Zero the geometry
+ // except for the bytes per sector and sector shift.
+ //
+
+ //
+ // This request can sometimes fail legitimately
+ // (e.g. when a SCSI device is attached but turned off)
+ // so this is not necessarily a device/driver bug.
+ //
+
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT, "ClassReadDriveCapacity on Fdo %p failed with status %ul.", Fdo, status));
+
+ //
+ // Write in a default disk geometry which we HOPE is right (??).
+ //
+
+ RtlZeroMemory(&fdoExt->DiskGeometry, sizeof(DISK_GEOMETRY));
+ fdoExt->DiskGeometry.BytesPerSector = 512;
+ fdoExt->SectorShift = 9;
+ fdoExt->CommonExtension.PartitionLength.QuadPart = (LONGLONG) 0;
+
+ //
+ // Is this removable or fixed media
+ //
+
+ if (TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)){
+ fdoExt->DiskGeometry.MediaType = RemovableMedia;
+ } else {
+ fdoExt->DiskGeometry.MediaType = FixedMedia;
+ }
+ }
+
+SafeExit:
+
+
+ //
+ // In case DEV_USE_16BYTE_CDB flag is not set, classpnp translates R/W request into READ/WRITE 10 SCSI command.
+ // These SCSI commands have 2 bytes in "Transfer Blocks" field.
+ // Make sure this max length (0xFFFF * sector size) is respected during request split.
+ //
+ if (!TEST_FLAG(fdoExt->DeviceFlags, DEV_USE_16BYTE_CDB)) {
+ ULONG cdb10MaxBlocks = ((ULONG)USHORT_MAX) << fdoExt->SectorShift;
+
+ fdoData->HwMaxXferLen = min(cdb10MaxBlocks, fdoData->HwMaxXferLen);
+ }
+
+ if (driveCapMdl != NULL) {
+ FreeDeviceInputMdl(driveCapMdl);
+ }
+
+ //
+ // If the request failed for some reason then invalidate the cached
+ // capacity data for removable devices. So that we won't return
+ // wrong capacity in IOCTL_STORAGE_READ_CAPACITY
+ //
+
+ if (!NT_SUCCESS(status) && (fdoExt->DiskGeometry.MediaType == RemovableMedia)) {
+ fdoData->IsCachedDriveCapDataValid = FALSE;
+ }
+
+ //
+ // Don't let memory failures (either here or in the port driver) in the ReadDriveCapacity call
+ // put the paging disk in an error state such that paging fails.
+ // Return the last known drive capacity (which may possibly be slightly out of date, even on
+ // fixed media, e.g. for storage cabinets that can grow a logical disk).
+ //
+ if ((status == STATUS_INSUFFICIENT_RESOURCES) &&
+ (fdoData->IsCachedDriveCapDataValid) &&
+ (fdoExt->DiskGeometry.MediaType == FixedMedia)) {
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT, "ClassReadDriveCapacity: defaulting to cached DriveCapacity data"));
+ InterpretCapacityData(Fdo, &fdoData->LastKnownDriveCapacityData);
+ status = STATUS_SUCCESS;
+ }
+
+ return status;
+}
+
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassSendStartUnit()
+
+Routine Description:
+
+ Send command to SCSI unit to start or power up.
+ Because this command is issued asynchronounsly, that is, without
+ waiting on it to complete, the IMMEDIATE flag is not set. This
+ means that the CDB will not return until the drive has powered up.
+ This should keep subsequent requests from being submitted to the
+ device before it has completely spun up.
+
+ This routine is called from the InterpretSense routine, when a
+ request sense returns data indicating that a drive must be
+ powered up.
+
+ This routine may also be called from a class driver's error handler,
+ or anytime a non-critical start device should be sent to the device.
+
+Arguments:
+
+ Fdo - The functional device object for the stopped device.
+
+Return Value:
+
+ None.
+
+--*/
+VOID
+ClassSendStartUnit(
+ _In_ PDEVICE_OBJECT Fdo
+ )
+{
+ PIO_STACK_LOCATION irpStack;
+ PIRP irp;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+ PSCSI_REQUEST_BLOCK srb;
+ PCOMPLETION_CONTEXT context;
+ PCDB cdb;
+ NTSTATUS status;
+ PSTORAGE_REQUEST_BLOCK srbEx;
+
+ //
+ // Allocate Srb from nonpaged pool.
+ //
+
+ context = ExAllocatePoolWithTag(NonPagedPoolNx,
+ sizeof(COMPLETION_CONTEXT),
+ '6CcS');
+
+ if (context == NULL) {
+
+ //
+ // ISSUE-2000/02/03-peterwie
+ // This code path was inheritted from the NT 4.0 class2.sys driver.
+ // It needs to be changed to survive low-memory conditions.
+ //
+
+ KeBugCheck(SCSI_DISK_DRIVER_INTERNAL);
+ }
+
+ //
+ // Save the device object in the context for use by the completion
+ // routine.
+ //
+
+ context->DeviceObject = Fdo;
+
+ srb = &context->Srb.Srb;
+ if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ srbEx = &context->Srb.SrbEx;
+ status = InitializeStorageRequestBlock(srbEx,
+ STORAGE_ADDRESS_TYPE_BTL8,
+ sizeof(context->Srb.SrbExBuffer),
+ 1,
+ SrbExDataTypeScsiCdb16);
+ if (!NT_SUCCESS(status)) {
+ FREE_POOL(context);
+ NT_ASSERT(FALSE);
+ return;
+ }
+
+ srbEx->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
+
+ } else {
+
+ //
+ // Zero out srb.
+ //
+
+ RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
+
+ //
+ // Write length to SRB.
+ //
+
+ srb->Length = sizeof(SCSI_REQUEST_BLOCK);
+
+ srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
+ }
+
+ //
+ // Set timeout value large enough for drive to spin up.
+ //
+
+ SrbSetTimeOutValue(srb, START_UNIT_TIMEOUT);
+
+ //
+ // Set the transfer length.
+ //
+
+ SrbAssignSrbFlags(srb,
+ (SRB_FLAGS_NO_DATA_TRANSFER |
+ SRB_FLAGS_DISABLE_AUTOSENSE |
+ SRB_FLAGS_DISABLE_SYNCH_TRANSFER));
+
+ //
+ // Build the start unit CDB.
+ //
+
+ SrbSetCdbLength(srb, 6);
+ cdb = SrbGetCdb(srb);
+
+ cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;
+ cdb->START_STOP.Start = 1;
+ cdb->START_STOP.Immediate = 0;
+ cdb->START_STOP.LogicalUnitNumber = srb->Lun;
+
+ //
+ // Build the asynchronous request to be sent to the port driver.
+ // Since this routine is called from a DPC the IRP should always be
+ // available.
+ //
+
+ irp = IoAllocateIrp(Fdo->StackSize, FALSE);
+
+ if (irp == NULL) {
+
+ //
+ // ISSUE-2000/02/03-peterwie
+ // This code path was inheritted from the NT 4.0 class2.sys driver.
+ // It needs to be changed to survive low-memory conditions.
+ //
+
+ KeBugCheck(SCSI_DISK_DRIVER_INTERNAL);
+
+ }
+
+ ClassAcquireRemoveLock(Fdo, irp);
+
+ IoSetCompletionRoutine(irp,
+ (PIO_COMPLETION_ROUTINE)ClassAsynchronousCompletion,
+ context,
+ TRUE,
+ TRUE,
+ TRUE);
+
+ irpStack = IoGetNextIrpStackLocation(irp);
+ irpStack->MajorFunction = IRP_MJ_SCSI;
+ SrbSetOriginalRequest(srb, irp);
+
+ //
+ // Store the SRB address in next stack for port driver.
+ //
+
+ irpStack->Parameters.Scsi.Srb = srb;
+
+ //
+ // Call the port driver with the IRP.
+ //
+
+ IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp);
+
+ return;
+
+} // end StartUnit()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassAsynchronousCompletion() ISSUE-2000/02/18-henrygab - why public?!
+
+Routine Description:
+
+ This routine is called when an asynchronous I/O request
+ which was issused by the class driver completes. Examples of such requests
+ are release queue or START UNIT. This routine releases the queue if
+ necessary. It then frees the context and the IRP.
+
+Arguments:
+
+ DeviceObject - The device object for the logical unit; however since this
+ is the top stack location the value is NULL.
+
+ Irp - Supplies a pointer to the Irp to be processed.
+
+ Context - Supplies the context to be used to process this request.
+
+Return Value:
+
+ None.
+
+--*/
+NTSTATUS
+ClassAsynchronousCompletion(
+ PDEVICE_OBJECT DeviceObject,
+ PIRP Irp,
+ PVOID Context
+ )
+{
+ PCOMPLETION_CONTEXT context = Context;
+ PSCSI_REQUEST_BLOCK srb;
+ ULONG srbFunction;
+ ULONG srbFlags;
+
+ if (context == NULL) {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ if (DeviceObject == NULL) {
+
+ DeviceObject = context->DeviceObject;
+ }
+
+ srb = &context->Srb.Srb;
+
+ if (srb->Function == SRB_FUNCTION_STORAGE_REQUEST_BLOCK) {
+ srbFunction = ((PSTORAGE_REQUEST_BLOCK)srb)->SrbFunction;
+ srbFlags = ((PSTORAGE_REQUEST_BLOCK)srb)->SrbFlags;
+ } else {
+ srbFunction = srb->Function;
+ srbFlags = srb->SrbFlags;
+ }
+
+ //
+ // If this is an execute srb, then check the return status and make sure.
+ // the queue is not frozen.
+ //
+
+ if (srbFunction == SRB_FUNCTION_EXECUTE_SCSI) {
+
+ //
+ // Check for a frozen queue.
+ //
+
+ if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) {
+
+ //
+ // Unfreeze the queue getting the device object from the context.
+ //
+
+ ClassReleaseQueue(context->DeviceObject);
+ }
+ }
+
+ { // free port-allocated sense buffer if we can detect
+
+ if (((PCOMMON_DEVICE_EXTENSION)(DeviceObject->DeviceExtension))->IsFdo) {
+
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+ if (PORT_ALLOCATED_SENSE(fdoExtension, srb)) {
+ FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, srb);
+ }
+
+ } else {
+
+ NT_ASSERT(!TEST_FLAG(srbFlags, SRB_FLAGS_FREE_SENSE_BUFFER));
+
+ }
+ }
+
+
+ //
+ // Free the context and the Irp.
+ //
+
+ if (Irp->MdlAddress != NULL) {
+ MmUnlockPages(Irp->MdlAddress);
+ IoFreeMdl(Irp->MdlAddress);
+
+ Irp->MdlAddress = NULL;
+ }
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+
+ FREE_POOL(context);
+
+ IoFreeIrp(Irp);
+
+ //
+ // Indicate the I/O system should stop processing the Irp completion.
+ //
+
+ return STATUS_MORE_PROCESSING_REQUIRED;
+
+} // end ClassAsynchronousCompletion()
+
+
+NTSTATUS
+ServiceTransferRequest(
+ PDEVICE_OBJECT Fdo,
+ PIRP Irp,
+ BOOLEAN PostToDpc
+ )
+
+/*++
+
+Routine description:
+
+ This routine processes Io requests, splitting them if they
+ are larger than what the hardware can handle at a time. If
+ there isn't enough memory available, the request is placed
+ in a queue, to be processed at a later time
+
+ If this is a high priority paging request, all regular Io
+ are throttled to provide Mm with better thoroughput
+
+Arguments:
+
+ Fdo - The functional device object processing the request
+ Irp - The Io request to be processed
+ PostToDpc - Flag that indicates that this IRP must be posted to a DPC
+
+Return Value:
+
+ STATUS_SUCCESS if successful, an error code otherwise
+
+--*/
+
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
+ PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension;
+ PSTORAGE_ADAPTER_DESCRIPTOR adapterDesc = commonExtension->PartitionZeroExtension->AdapterDescriptor;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
+ IO_PAGING_PRIORITY priority = (TEST_FLAG(Irp->Flags, IRP_PAGING_IO)) ? IoGetPagingIoPriority(Irp) : IoPagingPriorityInvalid;
+ BOOLEAN deferClientIrp = FALSE;
+ BOOLEAN driverUsesStartIO = (commonExtension->DriverExtension->InitData.ClassStartIo != NULL);
+ KIRQL oldIrql;
+ NTSTATUS status = STATUS_UNSUCCESSFUL;
+ PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
+
+ /*
+ * Initialize IRP status for the master IRP to
+ * - STATUS_FT_READ_FROM_COPY if it's a copy-specific read
+ * - STATUS_SUCCESS otherwise.
+ *
+ * This is required. When Classpnp determines the status for the master IRP
+ * when completing child IRPs, the call to IoSetMasterIrpStatus
+ * will be functioning properly (See TransferPktComplete function)
+ *
+ * Note:
+ * If the IRP is a copy-specific read, File System already initialized the IRP status
+ * to be STATUS_FT_READ_FROM_COPY. However, this can be changed when the IRP arrives
+ * at Classpnp. It's possible that other drivers in the stack may initialize the
+ * IRP status field to other values before forwarding the IRP down the stack.
+ * To be defensive, we initialize the IRP status to either STATUS_FT_READ_FROM_COPY
+ * if it's a copy-specific read, or STATUS_SUCCESS otherwise.
+ */
+ if (currentIrpStack->MajorFunction == IRP_MJ_READ &&
+ TEST_FLAG(currentIrpStack->Flags, SL_KEY_SPECIFIED) &&
+ IsKeyReadCopyNumber(currentIrpStack->Parameters.Read.Key)) {
+ Irp->IoStatus.Status = STATUS_FT_READ_FROM_COPY;
+ } else {
+ Irp->IoStatus.Status = STATUS_SUCCESS;
+ }
+
+ //
+ // If this is a high priority request, hold off all other Io requests
+ //
+
+ if (priority == IoPagingPriorityHigh)
+ {
+ KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
+
+ if (fdoData->NumHighPriorityPagingIo == 0)
+ {
+ //
+ // Entering throttle mode
+ //
+
+ KeQuerySystemTime(&fdoData->ThrottleStartTime);
+ }
+
+ fdoData->NumHighPriorityPagingIo++;
+ fdoData->MaxInterleavedNormalIo += ClassMaxInterleavePerCriticalIo;
+
+ KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
+ }
+ else
+ {
+ if (fdoData->NumHighPriorityPagingIo != 0)
+ {
+ //
+ // This request wasn't flagged as critical and atleast one critical request
+ // is currently outstanding. Queue this request until all of those are done
+ // but only if the interleave threshold has been reached
+ //
+
+ KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
+
+ if (fdoData->NumHighPriorityPagingIo != 0)
+ {
+ if (fdoData->MaxInterleavedNormalIo == 0)
+ {
+ deferClientIrp = TRUE;
+ }
+ else
+ {
+ fdoData->MaxInterleavedNormalIo--;
+ }
+ }
+
+ KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
+ }
+ }
+
+ if (!deferClientIrp)
+ {
+ PIO_STACK_LOCATION currentSp = IoGetCurrentIrpStackLocation(Irp);
+ ULONG entireXferLen = currentSp->Parameters.Read.Length;
+ PUCHAR bufPtr = MmGetMdlVirtualAddress(Irp->MdlAddress);
+ LARGE_INTEGER targetLocation = currentSp->Parameters.Read.ByteOffset;
+ PTRANSFER_PACKET pkt;
+ SINGLE_LIST_ENTRY pktList;
+ PSINGLE_LIST_ENTRY slistEntry;
+ ULONG hwMaxXferLen;
+ ULONG numPackets;
+ ULONG i;
+
+
+ /*
+ * We precomputed fdoData->HwMaxXferLen using (MaximumPhysicalPages-1).
+ * If the buffer is page-aligned, that's one less page crossing so we can add the page back in.
+ * Note: adapters that return MaximumPhysicalPages=0x10 depend on this to
+ * transfer aligned 64K requests in one piece.
+ * Also note: make sure adding PAGE_SIZE back in doesn't wrap to zero.
+ */
+ if (((ULONG_PTR)bufPtr & (PAGE_SIZE-1)) || (fdoData->HwMaxXferLen > 0xffffffff-PAGE_SIZE)){
+ hwMaxXferLen = fdoData->HwMaxXferLen;
+ }
+ else {
+ NT_ASSERT((PAGE_SIZE%fdoExt->DiskGeometry.BytesPerSector) == 0);
+ hwMaxXferLen = min(fdoData->HwMaxXferLen+PAGE_SIZE, adapterDesc->MaximumTransferLength);
+ }
+
+ /*
+ * Compute the number of hw xfers we'll have to do.
+ * Calculate this without allowing for an overflow condition.
+ */
+ NT_ASSERT(hwMaxXferLen >= PAGE_SIZE);
+ numPackets = entireXferLen/hwMaxXferLen;
+ if (entireXferLen % hwMaxXferLen){
+ numPackets++;
+ }
+
+ /*
+ * Use our 'simple' slist functions since we don't need interlocked.
+ */
+ SimpleInitSlistHdr(&pktList);
+
+ if (driverUsesStartIO) {
+ /*
+ * special case: StartIO-based writing must stay serialized, so just
+ * re-use one packet.
+ */
+ pkt = DequeueFreeTransferPacket(Fdo, TRUE);
+ if (pkt) {
+ SimplePushSlist(&pktList, (PSINGLE_LIST_ENTRY)&pkt->SlistEntry);
+ i = 1;
+ } else {
+ i = 0;
+ }
+ } else {
+ /*
+ * First get all the TRANSFER_PACKETs that we'll need at once.
+ */
+ for (i = 0; i < numPackets; i++){
+ pkt = DequeueFreeTransferPacket(Fdo, TRUE);
+ if (pkt){
+ SimplePushSlist(&pktList, (PSINGLE_LIST_ENTRY)&pkt->SlistEntry);
+ }
+ else {
+ break;
+ }
+ }
+ }
+
+
+ if ((i == numPackets) &&
+ (!driverUsesStartIO)) {
+ NTSTATUS pktStat;
+
+ /*
+ * The IoStatus.Information field will be incremented to the
+ * transfer length as the pieces complete.
+ */
+ Irp->IoStatus.Information = 0;
+
+ /*
+ * Store the number of transfer pieces inside the original IRP.
+ * It will be used to count down the pieces as they complete.
+ */
+ Irp->Tail.Overlay.DriverContext[0] = LongToPtr(numPackets);
+
+ /*
+ * For the common 1-packet case, we want to allow for an optimization by BlkCache
+ * (and also potentially synchronous storage drivers) which may complete the
+ * downward request synchronously.
+ * In that synchronous completion case, we want to _not_ mark the original irp pending
+ * and thereby save on the top-level APC.
+ * It's critical to coordinate this with the completion routine so that we mark the original irp
+ * pending if-and-only-if we return STATUS_PENDING for it.
+ */
+ if (numPackets > 1){
+ IoMarkIrpPending(Irp);
+ status = STATUS_PENDING;
+ }
+ else {
+ status = STATUS_SUCCESS;
+ }
+
+ /*
+ * Transmit the pieces of the transfer.
+ */
+ while (entireXferLen > 0){
+ ULONG thisPieceLen = MIN(hwMaxXferLen, entireXferLen);
+
+ /*
+ * Set up a TRANSFER_PACKET for this piece and send it.
+ */
+ slistEntry = SimplePopSlist(&pktList);
+ NT_ASSERT(slistEntry);
+ pkt = CONTAINING_RECORD(slistEntry, TRANSFER_PACKET, SlistEntry);
+ SetupReadWriteTransferPacket( pkt,
+ bufPtr,
+ thisPieceLen,
+ targetLocation,
+ Irp);
+
+ //
+ // If the IRP needs to be split, then we need to use a partial MDL.
+ // This prevents problems if the same MDL is mapped multiple times.
+ //
+ if (numPackets > 1) {
+ pkt->UsePartialMdl = TRUE;
+ }
+
+ /*
+ * When an IRP is completed, the completion routine checks to see if there
+ * is a deferred IRP ready to sent down (assuming that there are no non-idle
+ * requests waiting to be serviced). If such a deferred IRP is available, it
+ * is sent down using this routine. However, if the lower driver completes
+ * the request inline, there is a potential for multiple deferred IRPs being
+ * sent down in the context of the same completion thread, thus exhausting
+ * the call stack.
+ * In order to prevent this from happening, we need to ensure that deferred
+ * IRPs that are dequeued in the context of a request's completion routine
+ * get posted to a DPC.
+ */
+ if (PostToDpc) {
+
+ pkt->RetryIn100nsUnits = 0;
+ TransferPacketQueueRetryDpc(pkt);
+ status = STATUS_PENDING;
+
+ } else {
+
+ pktStat = SubmitTransferPacket(pkt);
+
+ /*
+ * If any of the packets completes with pending, we MUST return pending.
+ * Also, if a packet completes with an error, return pending; this is because
+ * in the completion routine we mark the original irp pending if the packet failed
+ * (since we may retry, thereby switching threads).
+ */
+ if (pktStat != STATUS_SUCCESS){
+ status = STATUS_PENDING;
+ }
+ }
+
+ entireXferLen -= thisPieceLen;
+ bufPtr += thisPieceLen;
+ targetLocation.QuadPart += thisPieceLen;
+ }
+ NT_ASSERT(SimpleIsSlistEmpty(&pktList));
+ }
+ else if (i >= 1){
+ /*
+ * We were unable to get all the TRANSFER_PACKETs we need,
+ * but we did get at least one.
+ * That means that we are in extreme low-memory stress.
+ * We'll try doing this transfer using a single packet.
+ * The port driver is certainly also in stress, so use one-page
+ * transfers.
+ */
+
+ /*
+ * Free all but one of the TRANSFER_PACKETs.
+ */
+ while (i-- > 1){
+ slistEntry = SimplePopSlist(&pktList);
+ NT_ASSERT(slistEntry);
+ pkt = CONTAINING_RECORD(slistEntry, TRANSFER_PACKET, SlistEntry);
+ EnqueueFreeTransferPacket(Fdo, pkt);
+ }
+
+ /*
+ * Get the single TRANSFER_PACKET that we'll be using.
+ */
+ slistEntry = SimplePopSlist(&pktList);
+ NT_ASSERT(slistEntry);
+ NT_ASSERT(SimpleIsSlistEmpty(&pktList));
+ pkt = CONTAINING_RECORD(slistEntry, TRANSFER_PACKET, SlistEntry);
+
+ if (!driverUsesStartIO) {
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_RW, "Insufficient packets available in ServiceTransferRequest - entering lowMemRetry with pkt=%p.", pkt));
+ }
+
+ /*
+ * Set the number of transfer packets (one)
+ * inside the original irp.
+ */
+ Irp->IoStatus.Information = 0;
+ Irp->Tail.Overlay.DriverContext[0] = LongToPtr(1);
+ IoMarkIrpPending(Irp);
+
+ /*
+ * Set up the TRANSFER_PACKET for a lowMem transfer and launch.
+ */
+ SetupReadWriteTransferPacket( pkt,
+ bufPtr,
+ entireXferLen,
+ targetLocation,
+ Irp);
+
+ InitLowMemRetry(pkt, bufPtr, entireXferLen, targetLocation);
+ StepLowMemRetry(pkt);
+ status = STATUS_PENDING;
+ }
+ else {
+ /*
+ * We were unable to get ANY TRANSFER_PACKETs.
+ * Defer this client irp until some TRANSFER_PACKETs free up.
+ */
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_RW, "No packets available in ServiceTransferRequest - deferring transfer (Irp=%p)...", Irp));
+
+ if (priority == IoPagingPriorityHigh)
+ {
+ KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
+
+ if (fdoData->MaxInterleavedNormalIo < ClassMaxInterleavePerCriticalIo)
+ {
+ fdoData->MaxInterleavedNormalIo = 0;
+ }
+ else
+ {
+ fdoData->MaxInterleavedNormalIo -= ClassMaxInterleavePerCriticalIo;
+ }
+
+ fdoData->NumHighPriorityPagingIo--;
+
+ if (fdoData->NumHighPriorityPagingIo == 0)
+ {
+ LARGE_INTEGER period;
+
+ //
+ // Exiting throttle mode
+ //
+
+ KeQuerySystemTime(&fdoData->ThrottleStopTime);
+
+ period.QuadPart = fdoData->ThrottleStopTime.QuadPart - fdoData->ThrottleStartTime.QuadPart;
+ fdoData->LongestThrottlePeriod.QuadPart = max(fdoData->LongestThrottlePeriod.QuadPart, period.QuadPart);
+ }
+
+ KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
+ }
+
+ deferClientIrp = TRUE;
+ }
+ }
+ _Analysis_assume_(deferClientIrp);
+ if (deferClientIrp)
+ {
+ IoMarkIrpPending(Irp);
+ EnqueueDeferredClientIrp(Fdo, Irp);
+ status = STATUS_PENDING;
+ }
+
+ NT_ASSERT(status != STATUS_UNSUCCESSFUL);
+
+ return status;
+}
+
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassIoComplete()
+
+Routine Description:
+
+ This routine executes when the port driver has completed a request.
+ It looks at the SRB status in the completing SRB and if not success
+ it checks for valid request sense buffer information. If valid, the
+ info is used to update status with more precise message of type of
+ error. This routine deallocates the SRB.
+
+ This routine should only be placed on the stack location for a class
+ driver FDO.
+
+Arguments:
+
+ Fdo - Supplies the device object which represents the logical
+ unit.
+
+ Irp - Supplies the Irp which has completed.
+
+ Context - Supplies a pointer to the SRB.
+
+Return Value:
+
+ NT status
+
+--*/
+NTSTATUS
+ClassIoComplete(
+ IN PDEVICE_OBJECT Fdo,
+ IN PIRP Irp,
+ IN PVOID Context
+ )
+{
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
+ PSCSI_REQUEST_BLOCK srb = Context;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
+ NTSTATUS status;
+ BOOLEAN retry;
+ BOOLEAN callStartNextPacket;
+ ULONG srbFlags;
+ ULONG srbFunction;
+
+ NT_ASSERT(fdoExtension->CommonExtension.IsFdo);
+
+ if (srb->Function == SRB_FUNCTION_STORAGE_REQUEST_BLOCK) {
+ srbFlags = ((PSTORAGE_REQUEST_BLOCK)srb)->SrbFlags;
+ srbFunction = ((PSTORAGE_REQUEST_BLOCK)srb)->SrbFunction;
+ } else {
+ srbFlags = srb->SrbFlags;
+ srbFunction = srb->Function;
+ }
+
+ #if DBG
+ if (srbFunction == SRB_FUNCTION_FLUSH) {
+ DBGLOGFLUSHINFO(fdoData, FALSE, FALSE, TRUE);
+ }
+ #endif
+
+ //
+ // Check SRB status for success of completing request.
+ //
+
+ if (SRB_STATUS(srb->SrbStatus) != SRB_STATUS_SUCCESS) {
+ LONGLONG retryInterval;
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassIoComplete: IRP %p, SRB %p\n", Irp, srb));
+
+ //
+ // Release the queue if it is frozen.
+ //
+
+ if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) {
+ ClassReleaseQueue(Fdo);
+ }
+ retry = InterpretSenseInfoWithoutHistory(
+ Fdo,
+ Irp,
+ srb,
+ irpStack->MajorFunction,
+ ((irpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL) ?
+ irpStack->Parameters.DeviceIoControl.IoControlCode :
+ 0),
+ MAXIMUM_RETRIES -
+ ((ULONG)(ULONG_PTR)irpStack->Parameters.Others.Argument4),
+ &status,
+ &retryInterval);
+
+ //
+ // For Persistent Reserve requests, make sure user gets back partial data.
+ //
+
+ if (irpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL &&
+ (irpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_STORAGE_PERSISTENT_RESERVE_IN ||
+ irpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_STORAGE_PERSISTENT_RESERVE_OUT) &&
+ status == STATUS_DATA_OVERRUN) {
+
+ status = STATUS_SUCCESS;
+ retry = FALSE;
+ }
+
+ //
+ // If the status is verified required and the this request
+ // should bypass verify required then retry the request.
+ //
+
+ if (TEST_FLAG(irpStack->Flags, SL_OVERRIDE_VERIFY_VOLUME) &&
+ status == STATUS_VERIFY_REQUIRED) {
+
+ status = STATUS_IO_DEVICE_ERROR;
+ retry = TRUE;
+ }
+
+#pragma warning(suppress:4213) // okay to cast Arg4 as a ulong for this use case
+ if (retry && ((ULONG)(ULONG_PTR)irpStack->Parameters.Others.Argument4)--) {
+
+ //
+ // Retry request.
+ //
+
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "Retry request %p\n", Irp));
+
+ if (PORT_ALLOCATED_SENSE(fdoExtension, srb)) {
+ FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, srb);
+ }
+
+ RetryRequest(Fdo, Irp, srb, FALSE, retryInterval);
+ return STATUS_MORE_PROCESSING_REQUIRED;
+ }
+
+ } else {
+
+ //
+ // Set status for successful request
+ //
+ fdoData->LoggedTURFailureSinceLastIO = FALSE;
+ ClasspPerfIncrementSuccessfulIo(fdoExtension);
+ status = STATUS_SUCCESS;
+ } // end if (SRB_STATUS(srb->SrbStatus) == SRB_STATUS_SUCCESS)
+
+
+ //
+ // ensure we have returned some info, and it matches what the
+ // original request wanted for PAGING operations only
+ //
+
+ if ((NT_SUCCESS(status)) && TEST_FLAG(Irp->Flags, IRP_PAGING_IO)) {
+ NT_ASSERT(Irp->IoStatus.Information != 0);
+ NT_ASSERT(irpStack->Parameters.Read.Length == Irp->IoStatus.Information);
+ }
+
+ //
+ // remember if the caller wanted to skip calling IoStartNextPacket.
+ // for legacy reasons, we cannot call IoStartNextPacket for IoDeviceControl
+ // calls. this setting only affects device objects with StartIo routines.
+ //
+
+ callStartNextPacket = !TEST_FLAG(srbFlags, SRB_FLAGS_DONT_START_NEXT_PACKET);
+ if (irpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL) {
+ callStartNextPacket = FALSE;
+ }
+
+ //
+ // Free MDL if allocated.
+ //
+
+ if (TEST_FLAG(srbFlags, SRB_CLASS_FLAGS_FREE_MDL)) {
+ SrbClearSrbFlags(srb, SRB_CLASS_FLAGS_FREE_MDL);
+ IoFreeMdl(Irp->MdlAddress);
+ Irp->MdlAddress = NULL;
+ }
+
+
+ //
+ // Free the srb
+ //
+
+ if (!TEST_FLAG(srbFlags, SRB_CLASS_FLAGS_PERSISTANT)) {
+
+ if (PORT_ALLOCATED_SENSE(fdoExtension, srb)) {
+ FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, srb);
+ }
+
+ if (fdoExtension->CommonExtension.IsSrbLookasideListInitialized){
+ ClassFreeOrReuseSrb(fdoExtension, srb);
+ }
+ else {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassIoComplete is freeing an SRB (possibly) on behalf of another driver."));
+ FREE_POOL(srb);
+ }
+
+ } else {
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassIoComplete: Not Freeing srb @ %p because "
+ "SRB_CLASS_FLAGS_PERSISTANT set\n", srb));
+ if (PORT_ALLOCATED_SENSE(fdoExtension, srb)) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassIoComplete: Not Freeing sensebuffer @ %p "
+ " because SRB_CLASS_FLAGS_PERSISTANT set\n",
+ srb->SenseInfoBuffer));
+ }
+
+ }
+
+ //
+ // Set status in completing IRP.
+ //
+
+ Irp->IoStatus.Status = status;
+
+ //
+ // Set the hard error if necessary.
+ //
+
+ if (!NT_SUCCESS(status) &&
+ IoIsErrorUserInduced(status) &&
+ (Irp->Tail.Overlay.Thread != NULL)
+ ) {
+
+ //
+ // Store DeviceObject for filesystem, and clear
+ // in IoStatus.Information field.
+ //
+
+ IoSetHardErrorOrVerifyDevice(Irp, Fdo);
+ Irp->IoStatus.Information = 0;
+ }
+
+ //
+ // If pending has be returned for this irp then mark the current stack as
+ // pending.
+ //
+
+ if (Irp->PendingReturned) {
+ IoMarkIrpPending(Irp);
+ }
+
+ if (fdoExtension->CommonExtension.DriverExtension->InitData.ClassStartIo) {
+ if (callStartNextPacket) {
+ KIRQL oldIrql;
+ KeRaiseIrql(DISPATCH_LEVEL, &oldIrql);
+ IoStartNextPacket(Fdo, TRUE); // Yes, some IO must now be cancellable.
+ KeLowerIrql(oldIrql);
+ }
+ }
+
+ ClassReleaseRemoveLock(Fdo, Irp);
+
+ return status;
+
+} // end ClassIoComplete()
+
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassSendSrbSynchronous()
+
+Routine Description:
+
+ This routine is called by SCSI device controls to complete an
+ SRB and send it to the port driver synchronously (ie wait for
+ completion). The CDB is already completed along with the SRB CDB
+ size and request timeout value.
+
+Arguments:
+
+ Fdo - Supplies the functional device object which represents the target.
+
+ Srb - Supplies a partially initialized SRB. The SRB cannot come from zone.
+
+ BufferAddress - Supplies the address of the buffer.
+
+ BufferLength - Supplies the length in bytes of the buffer.
+
+ WriteToDevice - Indicates the data should be transfer to the device.
+
+Return Value:
+
+ NTSTATUS indicating the final results of the operation.
+
+ If NT_SUCCESS(), then the amount of usable data is contained in the field
+ Srb->DataTransferLength
+
+--*/
+NTSTATUS
+ClassSendSrbSynchronous(
+ _In_ PDEVICE_OBJECT Fdo,
+ _Inout_ PSCSI_REQUEST_BLOCK _Srb,
+ _In_reads_bytes_opt_(BufferLength) PVOID BufferAddress,
+ _In_ ULONG BufferLength,
+ _In_ BOOLEAN WriteToDevice
+ )
+{
+
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
+ IO_STATUS_BLOCK ioStatus = {0};
+ PIRP irp;
+ PIO_STACK_LOCATION irpStack;
+ KEVENT event;
+ PVOID senseInfoBuffer = NULL;
+ ULONG senseInfoBufferLength = SENSE_BUFFER_SIZE_EX;
+ ULONG retryCount = MAXIMUM_RETRIES;
+ NTSTATUS status;
+ BOOLEAN retry;
+ PSTORAGE_REQUEST_BLOCK_HEADER Srb = (PSTORAGE_REQUEST_BLOCK_HEADER)_Srb;
+
+ //
+ // NOTE: This code is only pagable because we are not freezing
+ // the queue. Allowing the queue to be frozen from a pagable
+ // routine could leave the queue frozen as we try to page in
+ // the code to unfreeze the queue. The result would be a nice
+ // case of deadlock. Therefore, since we are unfreezing the
+ // queue regardless of the result, just set the NO_FREEZE_QUEUE
+ // flag in the SRB.
+ //
+
+ NT_ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
+ NT_ASSERT(fdoExtension->CommonExtension.IsFdo);
+
+ if (Srb->Function != SRB_FUNCTION_STORAGE_REQUEST_BLOCK) {
+ //
+ // Write length to SRB.
+ //
+
+ Srb->Length = sizeof(SCSI_REQUEST_BLOCK);
+
+ //
+ // Set SCSI bus address.
+ //
+
+ Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
+ }
+
+ //
+ // The Srb->Function should have been set corresponding to SrbType.
+ //
+
+ NT_ASSERT( ((fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_SCSI_REQUEST_BLOCK) && (Srb->Function == SRB_FUNCTION_EXECUTE_SCSI)) ||
+ ((fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) && (Srb->Function == SRB_FUNCTION_STORAGE_REQUEST_BLOCK)) );
+
+ //
+ // Sense buffer is in aligned nonpaged pool.
+ //
+
+#if defined(_ARM_) || defined(_ARM64_)
+
+ //
+ // ARM has specific alignment requirements, although this will not have a functional impact on x86 or amd64
+ // based platforms. We are taking the conservative approach here.
+ //
+ senseInfoBufferLength = ALIGN_UP_BY(senseInfoBufferLength,KeGetRecommendedSharedDataAlignment());
+
+#endif
+
+ senseInfoBuffer = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
+ senseInfoBufferLength,
+ '7CcS');
+
+ if (senseInfoBuffer == NULL) {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClassSendSrbSynchronous: Can't allocate request sense "
+ "buffer\n"));
+ return(STATUS_INSUFFICIENT_RESOURCES);
+ }
+
+
+ //
+ // Enable auto request sense.
+ //
+ SrbSetSenseInfoBufferLength(Srb, SENSE_BUFFER_SIZE_EX);
+ SrbSetSenseInfoBuffer(Srb, senseInfoBuffer);
+
+ SrbSetDataBuffer(Srb, BufferAddress);
+
+
+ //
+ // Start retries here.
+ //
+
+retry:
+
+ //
+ // use fdoextension's flags by default.
+ // do not move out of loop, as the flag may change due to errors
+ // sending this command.
+ //
+
+ SrbAssignSrbFlags(Srb, fdoExtension->SrbFlags);
+
+ if (BufferAddress != NULL) {
+ if (WriteToDevice) {
+ SrbSetSrbFlags(Srb, SRB_FLAGS_DATA_OUT);
+ } else {
+ SrbSetSrbFlags(Srb, SRB_FLAGS_DATA_IN);
+ }
+ }
+
+
+ //
+ // Initialize the QueueAction field.
+ //
+
+ SrbSetRequestAttribute(Srb, SRB_SIMPLE_TAG_REQUEST);
+
+ //
+ // Disable synchronous transfer for these requests.
+ //
+ SrbSetSrbFlags(Srb, SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_NO_QUEUE_FREEZE);
+
+ //
+ // Set the event object to the unsignaled state.
+ // It will be used to signal request completion.
+ //
+
+ KeInitializeEvent(&event, NotificationEvent, FALSE);
+
+ //
+ // Build device I/O control request with METHOD_NEITHER data transfer.
+ // We'll queue a completion routine to cleanup the MDL's and such ourself.
+ //
+
+ irp = IoAllocateIrp(
+ (CCHAR) (fdoExtension->CommonExtension.LowerDeviceObject->StackSize + 1),
+ FALSE);
+
+ if (irp == NULL) {
+ FREE_POOL(senseInfoBuffer);
+ SrbSetSenseInfoBuffer(Srb, NULL);
+ SrbSetSenseInfoBufferLength(Srb, 0);
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClassSendSrbSynchronous: Can't allocate Irp\n"));
+ return(STATUS_INSUFFICIENT_RESOURCES);
+ }
+
+ //
+ // Get next stack location.
+ //
+
+ irpStack = IoGetNextIrpStackLocation(irp);
+
+ //
+ // Set up SRB for execute scsi request. Save SRB address in next stack
+ // for the port driver.
+ //
+
+ irpStack->MajorFunction = IRP_MJ_SCSI;
+ irpStack->Parameters.Scsi.Srb = (PSCSI_REQUEST_BLOCK)Srb;
+
+ IoSetCompletionRoutine(irp,
+ ClasspSendSynchronousCompletion,
+ Srb,
+ TRUE,
+ TRUE,
+ TRUE);
+
+ irp->UserIosb = &ioStatus;
+ irp->UserEvent = &event;
+
+ if (BufferAddress) {
+ //
+ // Build an MDL for the data buffer and stick it into the irp. The
+ // completion routine will unlock the pages and free the MDL.
+ //
+
+ irp->MdlAddress = IoAllocateMdl( BufferAddress,
+ BufferLength,
+ FALSE,
+ FALSE,
+ irp );
+ if (irp->MdlAddress == NULL) {
+ FREE_POOL(senseInfoBuffer);
+ SrbSetSenseInfoBuffer(Srb, NULL);
+ SrbSetSenseInfoBufferLength(Srb, 0);
+ IoFreeIrp( irp );
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClassSendSrbSynchronous: Can't allocate MDL\n"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ try {
+
+ //
+ // the io manager unlocks these pages upon completion
+ //
+
+ MmProbeAndLockPages( irp->MdlAddress,
+ KernelMode,
+ (WriteToDevice ? IoReadAccess :
+ IoWriteAccess));
+
+ #pragma warning(suppress: 6320) // We want to handle any exception that MmProbeAndLockPages might throw
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+ status = GetExceptionCode();
+
+ FREE_POOL(senseInfoBuffer);
+ SrbSetSenseInfoBuffer(Srb, NULL);
+ SrbSetSenseInfoBufferLength(Srb, 0);
+ IoFreeMdl(irp->MdlAddress);
+ IoFreeIrp(irp);
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClassSendSrbSynchronous: Exception %lx "
+ "locking buffer\n", status));
+ return status;
+ }
+ }
+
+ //
+ // Set the transfer length.
+ //
+
+ SrbSetDataTransferLength(Srb, BufferLength);
+
+ //
+ // Zero out status.
+ //
+
+ SrbSetScsiStatus(Srb, 0);
+ Srb->SrbStatus = 0;
+ SrbSetNextSrb(Srb, NULL);
+
+ //
+ // Set up IRP Address.
+ //
+
+ SrbSetOriginalRequest(Srb, irp);
+
+ //
+ // Call the port driver with the request and wait for it to complete.
+ //
+
+ status = IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp);
+
+ if (status == STATUS_PENDING) {
+ (VOID)KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
+ status = ioStatus.Status;
+ }
+
+// NT_ASSERT(SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_PENDING);
+ NT_ASSERT(status != STATUS_PENDING);
+ NT_ASSERT(!(Srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN));
+
+ //
+ // Clear the IRP address in SRB as IRP has been freed at this time
+ // and don't want to leave any references that may be accessed.
+ //
+
+ SrbSetOriginalRequest(Srb, NULL);
+
+ //
+ // Check that request completed without error.
+ //
+
+ if (SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_SUCCESS) {
+
+ LONGLONG retryInterval;
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClassSendSrbSynchronous - srb %ph failed (op=%s srbstat=%s(%xh), irpstat=%xh, sense=%s/%s/%s)", Srb,
+ DBGGETSCSIOPSTR(Srb),
+ DBGGETSRBSTATUSSTR(Srb), (ULONG)Srb->SrbStatus, status,
+ DBGGETSENSECODESTR(Srb),
+ DBGGETADSENSECODESTR(Srb),
+ DBGGETADSENSEQUALIFIERSTR(Srb)));
+
+ //
+ // assert that the queue is not frozen
+ //
+
+ NT_ASSERT(!TEST_FLAG(Srb->SrbStatus, SRB_STATUS_QUEUE_FROZEN));
+
+ //
+ // Update status and determine if request should be retried.
+ //
+
+ retry = InterpretSenseInfoWithoutHistory(Fdo,
+ NULL, // no valid irp exists
+ (PSCSI_REQUEST_BLOCK)Srb,
+ IRP_MJ_SCSI,
+ 0,
+ MAXIMUM_RETRIES - retryCount,
+ &status,
+ &retryInterval);
+
+ if (retry) {
+
+ BOOLEAN validSense = FALSE;
+ UCHAR additionalSenseCode = 0;
+
+ if (status == STATUS_DEVICE_NOT_READY) {
+
+ validSense = ScsiGetSenseKeyAndCodes(senseInfoBuffer,
+ SrbGetSenseInfoBufferLength(Srb),
+ SCSI_SENSE_OPTIONS_FIXED_FORMAT_IF_UNKNOWN_FORMAT_INDICATED,
+ NULL,
+ &additionalSenseCode,
+ NULL);
+ }
+
+ if ((validSense && additionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY) ||
+ (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_SELECTION_TIMEOUT)) {
+
+ LARGE_INTEGER delay;
+
+ //
+ // Delay for at least 2 seconds.
+ //
+
+ if (retryInterval < 2*1000*1000*10) {
+ retryInterval = 2*1000*1000*10;
+ }
+
+ delay.QuadPart = -retryInterval;
+
+ //
+ // Stall for a while to let the device become ready
+ //
+
+ KeDelayExecutionThread(KernelMode, FALSE, &delay);
+
+ }
+
+ //
+ // If retries are not exhausted then retry this operation.
+ //
+
+ if (retryCount--) {
+
+ if (PORT_ALLOCATED_SENSE_EX(fdoExtension, Srb)) {
+ FREE_PORT_ALLOCATED_SENSE_BUFFER_EX(fdoExtension, Srb);
+ }
+
+ goto retry;
+ }
+ }
+
+
+ } else {
+ fdoData->LoggedTURFailureSinceLastIO = FALSE;
+ status = STATUS_SUCCESS;
+ }
+
+ //
+ // required even though we allocated our own, since the port driver may
+ // have allocated one also
+ //
+
+ if (PORT_ALLOCATED_SENSE_EX(fdoExtension, Srb)) {
+ FREE_PORT_ALLOCATED_SENSE_BUFFER_EX(fdoExtension, Srb);
+ }
+
+ FREE_POOL(senseInfoBuffer);
+ SrbSetSenseInfoBuffer(Srb, NULL);
+ SrbSetSenseInfoBufferLength(Srb, 0);
+
+ return status;
+}
+
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassInterpretSenseInfo()
+
+Routine Description:
+
+ This routine interprets the data returned from the SCSI
+ request sense. It determines the status to return in the
+ IRP and whether this request can be retried.
+
+Arguments:
+
+ Fdo - Supplies the device object associated with this request.
+
+ Srb - Supplies the scsi request block which failed.
+
+ MajorFunctionCode - Supplies the function code to be used for logging.
+
+ IoDeviceCode - Supplies the device code to be used for logging.
+
+ RetryCount - Number of times that the request has been retried.
+
+ Status - Returns the status for the request.
+
+ RetryInterval - Number of seconds before the request should be retried.
+ Zero indicates the request should be immediately retried.
+
+Return Value:
+
+ BOOLEAN TRUE: Drivers should retry this request.
+ FALSE: Drivers should not retry this request.
+
+--*/
+BOOLEAN
+ClassInterpretSenseInfo(
+ _In_ PDEVICE_OBJECT Fdo,
+ _In_ PSCSI_REQUEST_BLOCK _Srb,
+ _In_ UCHAR MajorFunctionCode,
+ _In_ ULONG IoDeviceCode,
+ _In_ ULONG RetryCount,
+ _Out_ NTSTATUS *Status,
+ _Out_opt_ _Deref_out_range_(0,100) ULONG *RetryInterval
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
+ PSTORAGE_REQUEST_BLOCK_HEADER Srb = (PSTORAGE_REQUEST_BLOCK_HEADER)_Srb;
+ PVOID senseBuffer = SrbGetSenseInfoBuffer(Srb);
+ BOOLEAN retry = TRUE;
+ BOOLEAN logError = FALSE;
+ BOOLEAN unhandledError = FALSE;
+ BOOLEAN incrementErrorCount = FALSE;
+
+ //
+ // NOTE: This flag must be used only for read/write requests that
+ // fail with a unexpected retryable error.
+ //
+ BOOLEAN logRetryableError = TRUE;
+
+ //
+ // Indicates if we should log this error in our internal log.
+ //
+ BOOLEAN logErrorInternal = TRUE;
+
+ ULONGLONG badSector = 0;
+ ULONG uniqueId = 0;
+
+ NTSTATUS logStatus;
+
+ ULONGLONG readSector;
+ ULONG index;
+
+ ULONG retryInterval = 0;
+ KIRQL oldIrql;
+ PCDB cdb = SrbGetCdb(Srb);
+ UCHAR cdbOpcode = 0;
+ ULONG cdbLength = SrbGetCdbLength(Srb);
+
+#if DBG
+ BOOLEAN isReservationConflict = FALSE;
+#endif
+
+ if (cdb) {
+ cdbOpcode = cdb->CDB6GENERIC.OperationCode;
+ }
+
+ *Status = STATUS_IO_DEVICE_ERROR;
+ logStatus = -1;
+
+ if (TEST_FLAG(SrbGetSrbFlags(Srb), SRB_CLASS_FLAGS_PAGING)) {
+
+ //
+ // Log anything remotely incorrect about paging i/o
+ //
+
+ logError = TRUE;
+ uniqueId = 301;
+ logStatus = IO_WARNING_PAGING_FAILURE;
+ }
+
+ //
+ // Check that request sense buffer is valid.
+ //
+
+ NT_ASSERT(fdoExtension->CommonExtension.IsFdo);
+
+
+ //
+ // must handle the SRB_STATUS_INTERNAL_ERROR case first,
+ // as it has all the flags set.
+ //
+
+ if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_INTERNAL_ERROR) {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
+ "ClassInterpretSenseInfo: Internal Error code is %x\n",
+ SrbGetSystemStatus(Srb)));
+
+ retry = FALSE;
+ *Status = SrbGetSystemStatus(Srb);
+
+ } else if (SrbGetScsiStatus(Srb) == SCSISTAT_RESERVATION_CONFLICT) {
+
+ //
+ // Need to reserve STATUS_DEVICE_BUSY to convey reservation conflict
+ // for read/write requests as there are upper level components that
+ // have built-in assumptions that STATUS_DEVICE_BUSY implies reservation
+ // conflict.
+ //
+ *Status = STATUS_DEVICE_BUSY;
+ retry = FALSE;
+ logError = FALSE;
+#if DBG
+ isReservationConflict = TRUE;
+#endif
+
+ } else {
+
+ BOOLEAN validSense = FALSE;
+
+ if ((Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) && senseBuffer) {
+
+ UCHAR errorCode = 0;
+ UCHAR senseKey = 0;
+ UCHAR addlSenseCode = 0;
+ UCHAR addlSenseCodeQual = 0;
+ BOOLEAN isIncorrectLengthValid = FALSE;
+ BOOLEAN incorrectLength = FALSE;
+ BOOLEAN isInformationValid = FALSE;
+ ULONGLONG information = 0;
+
+
+ validSense = ScsiGetSenseKeyAndCodes(senseBuffer,
+ SrbGetSenseInfoBufferLength(Srb),
+ SCSI_SENSE_OPTIONS_NONE,
+ &senseKey,
+ &addlSenseCode,
+ &addlSenseCodeQual);
+
+ if (!validSense && !IsSenseDataFormatValueValid(senseBuffer)) {
+
+ NT_ASSERT(FALSE);
+
+ validSense = ScsiGetFixedSenseKeyAndCodes(senseBuffer,
+ SrbGetSenseInfoBufferLength(Srb),
+ &senseKey,
+ &addlSenseCode,
+ &addlSenseCodeQual);
+ }
+
+ if (!validSense) {
+ goto __ClassInterpretSenseInfo_ProcessingInvalidSenseBuffer;
+ }
+
+ errorCode = ScsiGetSenseErrorCode(senseBuffer);
+
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: Error code is %x\n", errorCode));
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: Sense key is %x\n", senseKey));
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: Additional sense code is %x\n", addlSenseCode));
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: Additional sense code qualifier is %x\n", addlSenseCodeQual));
+
+ if (IsDescriptorSenseDataFormat(senseBuffer)) {
+
+ //
+ // Sense data in Descriptor format
+ //
+
+ PVOID startBuffer = NULL;
+ UCHAR startBufferLength = 0;
+
+
+ if (ScsiGetSenseDescriptor(senseBuffer,
+ SrbGetSenseInfoBufferLength(Srb),
+ &startBuffer,
+ &startBufferLength)) {
+ UCHAR outType;
+ PVOID outBuffer = NULL;
+ UCHAR outBufferLength = 0;
+ BOOLEAN foundBlockCommandType = FALSE;
+ BOOLEAN foundInformationType = FALSE;
+ UCHAR descriptorLength = 0;
+
+ UCHAR typeList[2] = {SCSI_SENSE_DESCRIPTOR_TYPE_INFORMATION,
+ SCSI_SENSE_DESCRIPTOR_TYPE_BLOCK_COMMAND};
+
+ while ((!foundBlockCommandType || !foundInformationType) &&
+ ScsiGetNextSenseDescriptorByType(startBuffer,
+ startBufferLength,
+ typeList,
+ ARRAYSIZE(typeList),
+ &outType,
+ &outBuffer,
+ &outBufferLength)) {
+
+ descriptorLength = ScsiGetSenseDescriptorLength(outBuffer);
+
+ if (outBufferLength < descriptorLength) {
+
+ // Descriptor data is truncated.
+ // Complete searching descriptors. Exit the loop now.
+ break;
+ }
+
+ if (outType == SCSI_SENSE_DESCRIPTOR_TYPE_BLOCK_COMMAND) {
+
+ //
+ // Block Command type
+ //
+
+ if (!foundBlockCommandType) {
+
+ foundBlockCommandType = TRUE;
+
+ if (ScsiValidateBlockCommandSenseDescriptor(outBuffer, outBufferLength)) {
+ incorrectLength = ((PSCSI_SENSE_DESCRIPTOR_BLOCK_COMMAND)outBuffer)->IncorrectLength;
+ isIncorrectLengthValid = TRUE;
+ }
+ } else {
+
+ //
+ // A Block Command descriptor is already found earlier.
+ //
+ // T10 SPC specification only allows one descriptor for Block Command Descriptor type.
+ // Assert here to catch devices that violate this rule. Ignore this descriptor.
+ //
+ NT_ASSERT(FALSE);
+ }
+
+ } else if (outType == SCSI_SENSE_DESCRIPTOR_TYPE_INFORMATION) {
+
+ //
+ // Information type
+ //
+
+ if (!foundInformationType) {
+
+ foundInformationType = TRUE;
+
+ if (ScsiValidateInformationSenseDescriptor(outBuffer, outBufferLength)) {
+ REVERSE_BYTES_QUAD(&information, &(((PSCSI_SENSE_DESCRIPTOR_INFORMATION)outBuffer)->Information));
+ isInformationValid = TRUE;
+ }
+ } else {
+
+ //
+ // A Information descriptor is already found earlier.
+ //
+ // T10 SPC specification only allows one descriptor for Information Descriptor type.
+ // Assert here to catch devices that violate this rule. Ignore this descriptor.
+ //
+ NT_ASSERT(FALSE);
+ }
+
+ } else {
+
+ //
+ // ScsiGetNextDescriptorByType should only return a type that is specified by us.
+ //
+ NT_ASSERT(FALSE);
+ break;
+ }
+
+ //
+ // Advance to start address of next descriptor
+ //
+ startBuffer = (PUCHAR)outBuffer + descriptorLength;
+ startBufferLength = outBufferLength - descriptorLength;
+ }
+ }
+ } else {
+
+ //
+ // Sense data in Fixed format
+ //
+
+ incorrectLength = ((PFIXED_SENSE_DATA)(senseBuffer))->IncorrectLength;
+ REVERSE_BYTES(&information, &(((PFIXED_SENSE_DATA)senseBuffer)->Information));
+ isInformationValid = TRUE;
+ isIncorrectLengthValid = TRUE;
+ }
+
+
+ switch (senseKey) {
+
+ case SCSI_SENSE_NO_SENSE: {
+
+ //
+ // Check other indicators.
+ //
+
+ if (isIncorrectLengthValid && incorrectLength) {
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
+ "Incorrect length detected.\n"));
+ *Status = STATUS_INVALID_BLOCK_LENGTH ;
+ retry = FALSE;
+
+ } else {
+
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
+ "No specific sense key\n"));
+ *Status = STATUS_IO_DEVICE_ERROR;
+ retry = TRUE;
+ }
+
+ break;
+ } // end SCSI_SENSE_NO_SENSE
+
+ case SCSI_SENSE_RECOVERED_ERROR: {
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
+ "Recovered error\n"));
+ *Status = STATUS_SUCCESS;
+ retry = FALSE;
+ logError = TRUE;
+ uniqueId = 258;
+
+ switch(addlSenseCode) {
+ case SCSI_ADSENSE_TRACK_ERROR:
+ case SCSI_ADSENSE_SEEK_ERROR: {
+ logStatus = IO_ERR_SEEK_ERROR;
+ break;
+ }
+
+ case SCSI_ADSENSE_REC_DATA_NOECC:
+ case SCSI_ADSENSE_REC_DATA_ECC: {
+ logStatus = IO_RECOVERED_VIA_ECC;
+ break;
+ }
+
+ case SCSI_ADSENSE_FAILURE_PREDICTION_THRESHOLD_EXCEEDED: {
+
+ UCHAR wmiEventData[sizeof(ULONG)+sizeof(UCHAR)] = {0};
+
+ *((PULONG)wmiEventData) = sizeof(UCHAR);
+ wmiEventData[sizeof(ULONG)] = addlSenseCodeQual;
+
+ //
+ // Don't log another eventlog if we have already logged once
+ // NOTE: this should have been interlocked, but the structure
+ // was publicly defined to use a BOOLEAN (char). Since
+ // media only reports these errors once per X minutes,
+ // the potential race condition is nearly non-existant.
+ // the worst case is duplicate log entries, so ignore.
+ //
+
+ logError = FALSE;
+ if (fdoExtension->FailurePredicted == 0) {
+ logError = TRUE;
+ }
+ fdoExtension->FailureReason = addlSenseCodeQual;
+ logStatus = IO_WRN_FAILURE_PREDICTED;
+
+ ClassNotifyFailurePredicted(fdoExtension,
+ (PUCHAR)wmiEventData,
+ sizeof(wmiEventData),
+ FALSE, // do not log error
+ 4, // unique error value
+ SrbGetPathId(Srb),
+ SrbGetTargetId(Srb),
+ SrbGetLun(Srb));
+
+ fdoExtension->FailurePredicted = TRUE;
+ break;
+ }
+
+ default: {
+ logStatus = IO_ERR_CONTROLLER_ERROR;
+ break;
+ }
+
+ } // end switch(addlSenseCode)
+
+ if (isIncorrectLengthValid && incorrectLength) {
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
+ "Incorrect length detected.\n"));
+ *Status = STATUS_INVALID_BLOCK_LENGTH ;
+ }
+
+
+ break;
+ } // end SCSI_SENSE_RECOVERED_ERROR
+
+ case SCSI_SENSE_NOT_READY: {
+
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
+ "Device not ready\n"));
+ *Status = STATUS_DEVICE_NOT_READY;
+
+ switch (addlSenseCode) {
+
+ case SCSI_ADSENSE_LUN_NOT_READY: {
+
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
+ "Lun not ready\n"));
+
+ retryInterval = NOT_READY_RETRY_INTERVAL;
+
+ switch (addlSenseCodeQual) {
+
+ case SCSI_SENSEQ_BECOMING_READY: {
+ DEVICE_EVENT_BECOMING_READY notReady = {0};
+
+ logRetryableError = FALSE;
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
+ "In process of becoming ready\n"));
+
+ notReady.Version = 1;
+ notReady.Reason = 1;
+ notReady.Estimated100msToReady = retryInterval * 10;
+ ClassSendNotification(fdoExtension,
+ &GUID_IO_DEVICE_BECOMING_READY,
+ sizeof(DEVICE_EVENT_BECOMING_READY),
+ &notReady);
+ break;
+ }
+
+ case SCSI_SENSEQ_MANUAL_INTERVENTION_REQUIRED: {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
+ "Manual intervention required\n"));
+ *Status = STATUS_NO_MEDIA_IN_DEVICE;
+ retry = FALSE;
+ break;
+ }
+
+ case SCSI_SENSEQ_FORMAT_IN_PROGRESS: {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
+ "Format in progress\n"));
+ retry = FALSE;
+ break;
+ }
+
+ case SCSI_SENSEQ_OPERATION_IN_PROGRESS: {
+ DEVICE_EVENT_BECOMING_READY notReady = {0};
+
+ logRetryableError = FALSE;
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
+ "Operation In Progress\n"));
+
+ notReady.Version = 1;
+ notReady.Reason = 2;
+ notReady.Estimated100msToReady = retryInterval * 10;
+ ClassSendNotification(fdoExtension,
+ &GUID_IO_DEVICE_BECOMING_READY,
+ sizeof(DEVICE_EVENT_BECOMING_READY),
+ &notReady);
+
+ break;
+ }
+
+ case SCSI_SENSEQ_LONG_WRITE_IN_PROGRESS: {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
+ "Long write in progress\n"));
+ //
+ // This has been seen as a transcient failure on some cdrom
+ // drives. The cdrom class driver is going to override this
+ // setting but has no way of dropping the retry interval
+ //
+ retry = FALSE;
+ retryInterval = 1;
+ break;
+ }
+
+ case SCSI_SENSEQ_SPACE_ALLOC_IN_PROGRESS: {
+ logRetryableError = FALSE;
+ TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
+ "The device (%p) is busy allocating space.\n",
+ Fdo));
+
+ //
+ // This indicates that a thinly-provisioned device has hit
+ // a temporary resource exhaustion and is busy allocating
+ // more space. We need to retry the request as the device
+ // will eventually be able to service it.
+ //
+ *Status = STATUS_RETRY;
+ retry = TRUE;
+
+ break;
+ }
+
+ case SCSI_SENSEQ_CAUSE_NOT_REPORTABLE: {
+
+ if (!TEST_FLAG(fdoExtension->ScanForSpecialFlags,
+ CLASS_SPECIAL_CAUSE_NOT_REPORTABLE_HACK)) {
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
+ "ClassInterpretSenseInfo: "
+ "not ready, cause unknown\n"));
+ /*
+ Many non-WHQL certified drives (mostly CD-RW) return
+ this when they have no media instead of the obvious
+ choice of:
+
+ SCSI_SENSE_NOT_READY/SCSI_ADSENSE_NO_MEDIA_IN_DEVICE
+
+ These drives should not pass WHQL certification due
+ to this discrepency.
+
+ */
+ retry = FALSE;
+ break;
+
+ } else {
+
+ //
+ // Treat this as init command required and fall through.
+ //
+ }
+ }
+
+ case SCSI_SENSEQ_INIT_COMMAND_REQUIRED:
+ default: {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
+ "Initializing command required\n"));
+ retryInterval = 0; // go back to default
+ logRetryableError = FALSE;
+
+ //
+ // This sense code/additional sense code
+ // combination may indicate that the device
+ // needs to be started. Send an start unit if this
+ // is a disk device.
+ //
+ if (TEST_FLAG(fdoExtension->DeviceFlags, DEV_SAFE_START_UNIT) &&
+ !TEST_FLAG(SrbGetSrbFlags(Srb), SRB_CLASS_FLAGS_LOW_PRIORITY)){
+
+ ClassSendStartUnit(Fdo);
+ }
+ break;
+ }
+
+ } // end switch (addlSenseCodeQual)
+ break;
+ }
+
+ case SCSI_ADSENSE_NO_MEDIA_IN_DEVICE: {
+ TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
+ "No Media in device.\n"));
+ *Status = STATUS_NO_MEDIA_IN_DEVICE;
+ retry = FALSE;
+
+ //
+ // signal MCN that there isn't any media in the device
+ //
+ if (!TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)) {
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
+ "No Media in a non-removable device %p\n",
+ Fdo));
+ }
+
+ if (addlSenseCodeQual == 0xCC){
+ /*
+ * The IMAPI filter returns this ASCQ value when it is burning CD-R media.
+ * We want to indicate that the media is not present to most applications;
+ * but RSM has to know that the media is still in the drive (i.e. the drive is not free).
+ */
+ ClassSetMediaChangeState(fdoExtension, MediaUnavailable, FALSE);
+ }
+ else {
+ ClassSetMediaChangeState(fdoExtension, MediaNotPresent, FALSE);
+ }
+
+ break;
+ }
+ } // end switch (addlSenseCode)
+
+ break;
+ } // end SCSI_SENSE_NOT_READY
+
+ case SCSI_SENSE_MEDIUM_ERROR: {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
+ "Medium Error (bad block)\n"));
+ *Status = STATUS_DEVICE_DATA_ERROR;
+
+ retry = FALSE;
+ logError = TRUE;
+ uniqueId = 256;
+ logStatus = IO_ERR_BAD_BLOCK;
+
+ //
+ // Check if this error is due to unknown format
+ //
+ if (addlSenseCode == SCSI_ADSENSE_INVALID_MEDIA) {
+
+ switch (addlSenseCodeQual) {
+
+ case SCSI_SENSEQ_UNKNOWN_FORMAT: {
+
+ *Status = STATUS_UNRECOGNIZED_MEDIA;
+
+ //
+ // Log error only if this is a paging request
+ //
+ if (!TEST_FLAG(SrbGetSrbFlags(Srb), SRB_CLASS_FLAGS_PAGING)) {
+ logError = FALSE;
+ }
+ break;
+ }
+
+ case SCSI_SENSEQ_CLEANING_CARTRIDGE_INSTALLED: {
+
+ *Status = STATUS_CLEANER_CARTRIDGE_INSTALLED;
+ logError = FALSE;
+ break;
+
+ }
+ default: {
+ break;
+ }
+ } // end switch addlSenseCodeQual
+
+ } // end SCSI_ADSENSE_INVALID_MEDIA
+
+ break;
+
+ } // end SCSI_SENSE_MEDIUM_ERROR
+
+ case SCSI_SENSE_HARDWARE_ERROR: {
+ BOOLEAN logHardwareError = TRUE;
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
+ "Hardware error\n"));
+
+ if (fdoData->LegacyErrorHandling == FALSE) {
+ //
+ // Hardware errors indicate something has seriously gone
+ // wrong with the device and retries are very unlikely to
+ // succeed so fail this request back immediately.
+ //
+ retry = FALSE;
+ *Status = STATUS_DEVICE_HARDWARE_ERROR;
+ logError = FALSE;
+
+ } else {
+ //
+ // Revert to legacy behavior. That is, retry everything by default.
+ //
+ retry = TRUE;
+ *Status = STATUS_IO_DEVICE_ERROR;
+ logError = TRUE;
+ uniqueId = 257;
+ logStatus = IO_ERR_CONTROLLER_ERROR;
+ logHardwareError = FALSE;
+
+ //
+ // This indicates the possibility of a dropped FC packet.
+ //
+ if ((addlSenseCode == SCSI_ADSENSE_LOGICAL_UNIT_ERROR && addlSenseCodeQual == SCSI_SENSEQ_TIMEOUT_ON_LOGICAL_UNIT) ||
+ (addlSenseCode == SCSI_ADSENSE_DATA_TRANSFER_ERROR && addlSenseCodeQual == SCSI_SENSEQ_INITIATOR_RESPONSE_TIMEOUT)) {
+ //
+ // Fail requests that report this error back to the application.
+ //
+ retry = FALSE;
+
+ //
+ // Log a more descriptive error and avoid a second
+ // error message (IO_ERR_CONTROLLER_ERROR) being logged.
+ //
+ logHardwareError = TRUE;
+ logError = FALSE;
+ }
+ }
+
+ //
+ // If CRC error was returned, retry after a slight delay.
+ //
+ if (addlSenseCode == SCSI_ADSENSE_LUN_COMMUNICATION &&
+ addlSenseCodeQual == SCSI_SESNEQ_COMM_CRC_ERROR) {
+ retry = TRUE;
+ retryInterval = 1;
+ logHardwareError = FALSE;
+ logError = FALSE;
+ }
+
+ //
+ // Hardware errors warrant a more descriptive error.
+ // Specifically, we need to ensure this disk is easily
+ // identifiable.
+ //
+ if (logHardwareError) {
+ UCHAR senseInfoBufferLength = SrbGetSenseInfoBufferLength(Srb);
+ UCHAR senseBufferSize = 0;
+
+ if (ScsiGetTotalSenseByteCountIndicated(senseBuffer,
+ senseInfoBufferLength,
+ &senseBufferSize)) {
+
+ senseBufferSize = min(senseBufferSize, senseInfoBufferLength);
+
+ } else {
+ //
+ // it's smaller than required to read the total number of
+ // valid bytes, so just use the SenseInfoBufferLength field.
+ //
+ senseBufferSize = senseInfoBufferLength;
+ }
+
+ ClasspQueueLogIOEventWithContextWorker(Fdo,
+ senseBufferSize,
+ senseBuffer,
+ SRB_STATUS(Srb->SrbStatus),
+ SrbGetScsiStatus(Srb),
+ (ULONG)IO_ERROR_IO_HARDWARE_ERROR,
+ cdbLength,
+ cdb,
+ NULL);
+ }
+
+ break;
+ } // end SCSI_SENSE_HARDWARE_ERROR
+
+ case SCSI_SENSE_ILLEGAL_REQUEST: {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
+ "Illegal SCSI request\n"));
+ *Status = STATUS_INVALID_DEVICE_REQUEST;
+ retry = FALSE;
+
+ switch (addlSenseCode) {
+
+ case SCSI_ADSENSE_NO_SENSE: {
+
+ switch (addlSenseCodeQual) {
+
+ //
+ // 1. Duplicate List Identifier
+ //
+ case SCSI_SENSEQ_OPERATION_IS_IN_PROGRESS: {
+
+ //
+ // XCOPY, READ BUFFER and CHANGE ALIASES return this sense combination under
+ // certain conditions. Since these commands aren't sent down natively by the
+ // Windows OS, return the default error for them and only handle this sense
+ // combination for offload data transfer commands.
+ //
+ if (ClasspIsOffloadDataTransferCommand(cdb)) {
+
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClassInterpretSenseInfo (%p): Duplicate List Identifier (command %x, parameter field offset 0x%016llx)\n",
+ Fdo,
+ cdbOpcode,
+ information));
+
+ NT_ASSERTMSG("Duplicate list identifier specified", FALSE);
+
+ //
+ // The host should ensure that it uses unique list id for each TokenOperation request.
+ //
+ *Status = STATUS_OPERATION_IN_PROGRESS;
+ }
+ break;
+ }
+ }
+ break;
+ }
+
+ case SCSI_ADSENSE_LUN_COMMUNICATION: {
+
+ switch (addlSenseCodeQual) {
+
+ //
+ // 1. Source/Destination pairing can't communicate with each other or the copy manager.
+ //
+ case SCSI_SENSEQ_UNREACHABLE_TARGET: {
+
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClassInterpretSenseInfo (%p): Source-Destination LUNs can't communicate (command %x)\n",
+ Fdo,
+ cdbOpcode));
+
+ *Status = STATUS_DEVICE_UNREACHABLE;
+ break;
+ }
+ }
+ break;
+ }
+
+ case SCSI_ADSENSE_COPY_TARGET_DEVICE_ERROR: {
+
+ switch (addlSenseCodeQual) {
+
+ //
+ // 1. Sum of logical block fields in all block device range descriptors is greater than number
+ // of logical blocks in the ROD minus block offset into ROD
+ //
+ case SCSI_SENSEQ_DATA_UNDERRUN: {
+
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClassInterpretSenseInfo (%p): Host specified a transfer length greater than what is represented by the token (considering the offset) [command %x]\n",
+ Fdo,
+ cdbOpcode));
+
+ NT_ASSERTMSG("Host specified blocks to write beyond what is represented by the token", FALSE);
+
+ *Status = STATUS_DATA_OVERRUN;
+ break;
+ }
+ }
+ break;
+ }
+
+ //
+ // 1. Parameter data truncation (e.g. last descriptor was not fully specified)
+ //
+ case SCSI_ADSENSE_PARAMETER_LIST_LENGTH: {
+
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClassInterpretSenseInfo (%p): Target truncated the block device range descriptors in the parameter list (command %x)\n",
+ Fdo,
+ cdbOpcode));
+
+ NT_ASSERTMSG("Parameter data truncation", FALSE);
+
+ *Status = STATUS_DATA_OVERRUN;
+ break;
+ }
+
+ case SCSI_ADSENSE_ILLEGAL_COMMAND: {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
+ "Illegal command\n"));
+ break;
+ }
+
+ case SCSI_ADSENSE_ILLEGAL_BLOCK: {
+
+ LARGE_INTEGER logicalBlockAddr;
+ LARGE_INTEGER lastLBA;
+ ULONG numTransferBlocks = 0;
+
+ logicalBlockAddr.QuadPart = 0;
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: Illegal block address\n"));
+
+ *Status = STATUS_NONEXISTENT_SECTOR;
+
+ if (Fdo->DeviceType == FILE_DEVICE_DISK) {
+
+ if (IS_SCSIOP_READWRITE(cdbOpcode) && cdb) {
+
+ if (TEST_FLAG(fdoExtension->DeviceFlags, DEV_USE_16BYTE_CDB)) {
+ REVERSE_BYTES_QUAD(&logicalBlockAddr, &cdb->CDB16.LogicalBlock);
+ REVERSE_BYTES(&numTransferBlocks, &cdb->CDB16.TransferLength);
+ } else {
+ REVERSE_BYTES(&logicalBlockAddr.LowPart, &cdb->CDB10.LogicalBlockByte0);
+ REVERSE_BYTES_SHORT((PUSHORT)&numTransferBlocks, &cdb->CDB10.TransferBlocksMsb);
+ }
+
+ REVERSE_BYTES_QUAD(&lastLBA, &fdoData->LastKnownDriveCapacityData.LogicalBlockAddress);
+
+ if ((logicalBlockAddr.QuadPart > lastLBA.QuadPart) ||
+ ((logicalBlockAddr.QuadPart + numTransferBlocks - 1) > lastLBA.QuadPart)) {
+
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
+ "Request beyond boundary. Last LBA: 0x%I64X Read LBA: 0x%I64X Length: 0x%X\n",
+ (__int64) lastLBA.QuadPart, (__int64) logicalBlockAddr.QuadPart, numTransferBlocks));
+ } else {
+ //
+ // Should only retry these if the request was
+ // truly within our expected size.
+ //
+ // Fujitsu IDE drives have been observed to
+ // return this error transiently for a legal LBA;
+ // manual retry in the debugger then works, so
+ // there is a good chance that a programmed retry
+ // will also work.
+ //
+
+ retry = TRUE;
+ retryInterval = 5;
+ }
+ } else if (ClasspIsOffloadDataTransferCommand(cdb)) {
+
+ //
+ // 1. Number of logical blocks of block device range descriptor exceeds capacity of the medium
+ //
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClassInterpretSenseInfo (%p): LBA out of range (command %x, parameter field offset 0x%016llx)\n",
+ Fdo,
+ cdbOpcode,
+ information));
+
+ NT_ASSERTMSG("Number of blocks specified exceeds LUN capacity", FALSE);
+ }
+ }
+ break;
+ }
+
+ //
+ // 1. Generic error - cause not reportable
+ // 2. Insufficient resources to create ROD
+ // 3. Insufficient resources to create Token
+ // 4. Max number of tokens exceeded
+ // 5. Remote Token creation not supported
+ // 6. Token expired
+ // 7. Token unknown
+ // 8. Unsupported Token type
+ // 9. Token corrupt
+ // 10. Token revoked
+ // 11. Token cancelled
+ // 12. Remote Token usage not supported
+ //
+ case SCSI_ADSENSE_INVALID_TOKEN: {
+
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClassInterpretSenseInfo (%p): Invalid/Expired/Modified token specified (command %x, parameter field offset 0x%016llx)\n",
+ Fdo,
+ cdbOpcode,
+ information));
+
+ *Status = STATUS_INVALID_TOKEN;
+ break;
+ }
+
+ case SCSI_ADSENSE_INVALID_CDB: {
+ if (ClasspIsOffloadDataTransferCommand(cdb)) {
+
+ //
+ // 1. Mismatched I_T nexus and list identifier
+ //
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClassInterpretSenseInfo (%p): Incorrect I_T nexus likely used (command %x)\n",
+ Fdo,
+ cdbOpcode));
+
+ //
+ // The host should ensure that it sends TokenOperation and ReceiveTokenInformation for the same
+ // list Id using the same I_T nexus.
+ //
+ *Status = STATUS_INVALID_INITIATOR_TARGET_PATH;
+
+ } else {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
+ "Invalid CDB\n"));
+
+ //
+ // Note: the retry interval is not typically used.
+ // it is set here only because a ClassErrorHandler
+ // cannot set the retryInterval, and the error may
+ // require a few commands to be sent to clear whatever
+ // caused this condition (i.e. disk clears the write
+ // cache, requiring at least two commands)
+ //
+ // hopefully, this shortcoming can be changed for
+ // blackcomb.
+ //
+
+ retryInterval = 3;
+ }
+ break;
+ }
+
+ case SCSI_ADSENSE_INVALID_LUN: {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
+ "Invalid LUN\n"));
+ *Status = STATUS_NO_SUCH_DEVICE;
+ break;
+ }
+
+ case SCSI_ADSENSE_INVALID_FIELD_PARAMETER_LIST: {
+
+ switch (addlSenseCodeQual) {
+
+ //
+ // 1. Alignment violation (e.g. copy manager is unable to copy because destination offset is NOT aligned to LUN's granularity/alignment)
+ //
+ case SCSI_SENSEQ_INVALID_RELEASE_OF_PERSISTENT_RESERVATION: {
+
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClassInterpretSenseInfo (%p): Alignment violation for command %x.\n",
+ Fdo,
+ cdbOpcode));
+
+ NT_ASSERTMSG("Specified offset is not aligned to LUN's granularity", FALSE);
+
+ *Status = STATUS_INVALID_OFFSET_ALIGNMENT;
+ break;
+ }
+
+ //
+ // 1. Number of block device range descriptors is greater than maximum range descriptors
+ //
+ case SCSI_SENSEQ_TOO_MANY_SEGMENT_DESCRIPTORS: {
+
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClassInterpretSenseInfo (%p): Too many descriptors in parameter list for command %x (parameter field offset 0x%016llx)\n",
+ Fdo,
+ cdbOpcode,
+ information));
+
+ NT_ASSERTMSG("Too many descriptors specified", FALSE);
+
+ *Status = STATUS_TOO_MANY_SEGMENT_DESCRIPTORS;
+ break;
+ }
+
+ default: {
+
+ if (ClasspIsOffloadDataTransferCommand(cdb)) {
+
+ //
+ // 1. (Various) Invalid parameter length
+ // 2. Requested inactivity timeout is greater than maximum inactivity timeout
+ // 3. Same LBA is included in more than one block device range descriptor (overlapping LBAs)
+ // 4. Total number of logical blocks of all block range descriptors is greater than the maximum transfer size
+ // 5. Total number of logical blocks of all block range descriptors is greater than maximum token transfer size
+ // (e.g. WriteUsingToken descriptors specify a cumulative total block count that exceeds the PopulateToken that created the token)
+ // 6. Block offset into ROD specified an offset that is greater than or equal to the number of logical blocks in the ROD
+ // 7. Number of logical blocks in a block device range descriptor is greater than maximum transfer length in blocks
+ //
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClassInterpretSenseInfo (%p): Illegal field in parameter list for command %x (parameter field offset 0x%016llx) [AddSense %x, AddSenseQ %x]\n",
+ Fdo,
+ cdbOpcode,
+ information,
+ addlSenseCode,
+ addlSenseCodeQual));
+
+ NT_ASSERTMSG("Invalid field in parameter list", FALSE);
+
+ *Status = STATUS_INVALID_FIELD_IN_PARAMETER_LIST;
+ }
+
+ break;
+ }
+ }
+ break;
+ }
+
+ case SCSI_ADSENSE_COPY_PROTECTION_FAILURE: {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
+ "Copy protection failure\n"));
+
+ *Status = STATUS_COPY_PROTECTION_FAILURE;
+
+ switch (addlSenseCodeQual) {
+ case SCSI_SENSEQ_AUTHENTICATION_FAILURE:
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
+ "ClassInterpretSenseInfo: "
+ "Authentication failure\n"));
+ *Status = STATUS_CSS_AUTHENTICATION_FAILURE;
+ break;
+ case SCSI_SENSEQ_KEY_NOT_PRESENT:
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
+ "ClassInterpretSenseInfo: "
+ "Key not present\n"));
+ *Status = STATUS_CSS_KEY_NOT_PRESENT;
+ break;
+ case SCSI_SENSEQ_KEY_NOT_ESTABLISHED:
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
+ "ClassInterpretSenseInfo: "
+ "Key not established\n"));
+ *Status = STATUS_CSS_KEY_NOT_ESTABLISHED;
+ break;
+ case SCSI_SENSEQ_READ_OF_SCRAMBLED_SECTOR_WITHOUT_AUTHENTICATION:
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
+ "ClassInterpretSenseInfo: "
+ "Read of scrambled sector w/o "
+ "authentication\n"));
+ *Status = STATUS_CSS_SCRAMBLED_SECTOR;
+ break;
+ case SCSI_SENSEQ_MEDIA_CODE_MISMATCHED_TO_LOGICAL_UNIT:
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
+ "ClassInterpretSenseInfo: "
+ "Media region does not logical unit "
+ "region\n"));
+ *Status = STATUS_CSS_REGION_MISMATCH;
+ break;
+ case SCSI_SENSEQ_LOGICAL_UNIT_RESET_COUNT_ERROR:
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
+ "ClassInterpretSenseInfo: "
+ "Region set error -- region may "
+ "be permanent\n"));
+ *Status = STATUS_CSS_RESETS_EXHAUSTED;
+ break;
+ } // end switch of ASCQ for COPY_PROTECTION_FAILURE
+
+ break;
+ }
+
+ case SCSI_ADSENSE_MUSIC_AREA: {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
+ "Music area\n"));
+ break;
+ }
+
+ case SCSI_ADSENSE_DATA_AREA: {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
+ "Data area\n"));
+ break;
+ }
+
+ case SCSI_ADSENSE_VOLUME_OVERFLOW: {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
+ "Volume overflow\n"));
+ break;
+ }
+
+ } // end switch (addlSenseCode)
+
+ break;
+ } // end SCSI_SENSE_ILLEGAL_REQUEST
+
+ case SCSI_SENSE_UNIT_ATTENTION: {
+
+ ULONG count;
+
+ //
+ // A media change may have occured so increment the change
+ // count for the physical device
+ //
+
+ count = InterlockedIncrement((volatile LONG *)&fdoExtension->MediaChangeCount);
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClassInterpretSenseInfo: "
+ "Media change count for device %d incremented to %#lx\n",
+ fdoExtension->DeviceNumber, count));
+
+
+ switch (addlSenseCode) {
+ case SCSI_ADSENSE_MEDIUM_CHANGED: {
+ logRetryableError = FALSE;
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN, "ClassInterpretSenseInfo: "
+ "Media changed\n"));
+
+ if (!TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_MCN, "ClassInterpretSenseInfo: "
+ "Media Changed on non-removable device %p\n",
+ Fdo));
+ }
+ ClassSetMediaChangeState(fdoExtension, MediaPresent, FALSE);
+ break;
+ }
+
+ case SCSI_ADSENSE_BUS_RESET: {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
+ "Bus reset\n"));
+ break;
+ }
+
+ case SCSI_ADSENSE_PARAMETERS_CHANGED: {
+ logRetryableError = FALSE;
+ if (addlSenseCodeQual == SCSI_SENSEQ_CAPACITY_DATA_CHANGED) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
+ "Device capacity changed (e.g. thinly provisioned LUN). Retry the request.\n"));
+
+ ClassQueueCapacityChangedEventWorker(Fdo);
+
+ //
+ // Retry with 1 second delay as ClassQueueCapacityChangedEventWorker may trigger a couple of commands sent to disk.
+ //
+ retryInterval = 1;
+ retry = TRUE;
+ }
+ break;
+ }
+
+ case SCSI_ADSENSE_LB_PROVISIONING: {
+
+ switch (addlSenseCodeQual) {
+
+ case SCSI_SENSEQ_SOFT_THRESHOLD_REACHED: {
+
+ logRetryableError = FALSE;
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
+ "Device (%p) has hit a soft threshold.\n",
+ Fdo));
+
+ //
+ // This indicates that a resource provisioned or thinly
+ // provisioned device has hit a soft threshold. Queue a
+ // worker thread to log a system event and then retry the
+ // original request.
+ //
+ ClassQueueThresholdEventWorker(Fdo);
+ break;
+ }
+ default: {
+ retry = FALSE;
+ break;
+ }
+
+ } // end switch (addlSenseCodeQual)
+ break;
+ }
+
+ case SCSI_ADSENSE_OPERATING_CONDITIONS_CHANGED: {
+
+ if (addlSenseCodeQual == SCSI_SENSEQ_MICROCODE_CHANGED) {
+ //
+ // Device firmware has been changed. Retry the request.
+ //
+ logRetryableError = TRUE;
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
+ "Device firmware has been changed.\n"));
+
+ retryInterval = 1;
+ retry = TRUE;
+ } else {
+ //
+ // Device information has changed, we need to rescan the
+ // bus for changed information such as the capacity.
+ //
+ logRetryableError = FALSE;
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
+ "Device information changed. Invalidate the bus\n"));
+
+ if (addlSenseCodeQual == SCSI_SENSEQ_INQUIRY_DATA_CHANGED) {
+
+ ClassQueueProvisioningTypeChangedEventWorker(Fdo);
+ }
+
+ if (addlSenseCodeQual == SCSI_SENSEQ_INQUIRY_DATA_CHANGED ||
+ addlSenseCodeQual == SCSI_SENSEQ_OPERATING_DEFINITION_CHANGED) {
+
+ //
+ // Since either the LB provisioning type changed, or the block/slab size
+ // changed, next time anyone trying to query the FunctionSupportInfo, we
+ // will requery the device.
+ //
+ InterlockedIncrement((volatile LONG *)&fdoExtension->FunctionSupportInfo->ChangeRequestCount);
+ }
+
+ IoInvalidateDeviceRelations(fdoExtension->LowerPdo, BusRelations);
+ retryInterval = 5;
+ }
+ break;
+ }
+
+ case SCSI_ADSENSE_OPERATOR_REQUEST: {
+ switch (addlSenseCodeQual) {
+
+ case SCSI_SENSEQ_MEDIUM_REMOVAL: {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
+ "Ejection request received!\n"));
+ ClassSendEjectionNotification(fdoExtension);
+ break;
+ }
+
+ case SCSI_SENSEQ_WRITE_PROTECT_ENABLE: {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
+ "Operator selected write permit?! "
+ "(unsupported!)\n"));
+ break;
+ }
+
+ case SCSI_SENSEQ_WRITE_PROTECT_DISABLE: {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
+ "Operator selected write protect?! "
+ "(unsupported!)\n"));
+ break;
+ }
+ }
+ }
+
+ case SCSI_ADSENSE_FAILURE_PREDICTION_THRESHOLD_EXCEEDED: {
+
+ UCHAR wmiEventData[sizeof(ULONG)+sizeof(UCHAR)] = {0};
+
+ *((PULONG)wmiEventData) = sizeof(UCHAR);
+ wmiEventData[sizeof(ULONG)] = addlSenseCodeQual;
+
+ //
+ // Don't log another eventlog if we have already logged once
+ // NOTE: this should have been interlocked, but the structure
+ // was publicly defined to use a BOOLEAN (char). Since
+ // media only reports these errors once per X minutes,
+ // the potential race condition is nearly non-existant.
+ // the worst case is duplicate log entries, so ignore.
+ //
+
+ logError = FALSE;
+ if (fdoExtension->FailurePredicted == 0) {
+ logError = TRUE;
+ }
+ fdoExtension->FailureReason = addlSenseCodeQual;
+ logStatus = IO_WRN_FAILURE_PREDICTED;
+
+ ClassNotifyFailurePredicted(fdoExtension,
+ (PUCHAR)wmiEventData,
+ sizeof(wmiEventData),
+ FALSE, // do not log error
+ 4, // unique error value
+ SrbGetPathId(Srb),
+ SrbGetTargetId(Srb),
+ SrbGetLun(Srb));
+
+ fdoExtension->FailurePredicted = TRUE;
+
+ //
+ // Since this is a Unit Attention we need to make
+ // sure we retry this request.
+ //
+ retry = TRUE;
+
+ break;
+ }
+
+ default: {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
+ "Unit attention\n"));
+ break;
+ }
+
+ } // end switch (addlSenseCode)
+
+ if (TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA))
+ {
+
+ if ((ClassGetVpb(Fdo) != NULL) && (ClassGetVpb(Fdo)->Flags & VPB_MOUNTED))
+ {
+ //
+ // Set bit to indicate that media may have changed
+ // and volume needs verification.
+ //
+
+ SET_FLAG(Fdo->Flags, DO_VERIFY_VOLUME);
+
+ *Status = STATUS_VERIFY_REQUIRED;
+ retry = FALSE;
+ }
+ else {
+ *Status = STATUS_IO_DEVICE_ERROR;
+ }
+ }
+ else
+ {
+ *Status = STATUS_IO_DEVICE_ERROR;
+ }
+
+ break;
+
+ } // end SCSI_SENSE_UNIT_ATTENTION
+
+ case SCSI_SENSE_DATA_PROTECT: {
+
+ retry = FALSE;
+
+ if (addlSenseCode == SCSI_ADSENSE_WRITE_PROTECT)
+ {
+ switch (addlSenseCodeQual) {
+
+ case SCSI_SENSEQ_SPACE_ALLOC_FAILED_WRITE_PROTECT: {
+
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
+ "Device's (%p) resources are exhausted.\n",
+ Fdo));
+
+ ClassQueueResourceExhaustionEventWorker(Fdo);
+
+ //
+ // This indicates that a thinly-provisioned device has
+ // hit a permanent resource exhaustion. We need to
+ // return this status code so that patmgr can take the
+ // disk offline.
+ //
+ *Status = STATUS_DISK_RESOURCES_EXHAUSTED;
+ break;
+ }
+ default:
+ {
+ break;
+ }
+
+ } // end switch addlSenseCodeQual
+ }
+ else
+ {
+ if (IS_SCSIOP_WRITE(cdbOpcode)) {
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
+ "Media write protected\n"));
+ *Status = STATUS_MEDIA_WRITE_PROTECTED;
+ } else {
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
+ "Access denied\n"));
+ *Status = STATUS_ACCESS_DENIED;
+ }
+ }
+ break;
+ } // end SCSI_SENSE_DATA_PROTECT
+
+ case SCSI_SENSE_BLANK_CHECK: {
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
+ "Media blank check\n"));
+ retry = FALSE;
+ *Status = STATUS_NO_DATA_DETECTED;
+ break;
+ } // end SCSI_SENSE_BLANK_CHECK
+
+ case SCSI_SENSE_COPY_ABORTED: {
+
+ switch (addlSenseCode) {
+
+ case SCSI_ADSENSE_COPY_TARGET_DEVICE_ERROR: {
+
+ switch (addlSenseCodeQual) {
+
+ //
+ // 1. Target truncated the data transfer.
+ //
+ case SCSI_SENSEQ_DATA_UNDERRUN: {
+
+ TracePrint((TRACE_LEVEL_WARNING,
+ TRACE_FLAG_IOCTL,
+ "ClassInterpretSenseInfo (%p): Data transfer was truncated (command %x)\n",
+ Fdo,
+ cdbOpcode));
+
+ *Status = STATUS_SUCCESS;
+ retry = FALSE;
+ break;
+ }
+ }
+ break;
+ }
+ }
+ break;
+ }
+
+ case SCSI_SENSE_ABORTED_COMMAND: {
+ if (ClasspIsOffloadDataTransferCommand(cdb)) {
+
+ switch (addlSenseCode) {
+
+ case SCSI_ADSENSE_COPY_TARGET_DEVICE_ERROR: {
+
+ switch (addlSenseCodeQual) {
+
+ //
+ // 1. Target truncated the data transfer.
+ //
+ case SCSI_SENSEQ_DATA_UNDERRUN: {
+
+ TracePrint((TRACE_LEVEL_WARNING,
+ TRACE_FLAG_IOCTL,
+ "ClassInterpretSenseInfo (%p): Target has truncated the data transfer (command %x)\n",
+ Fdo,
+ cdbOpcode));
+
+ *Status = STATUS_SUCCESS;
+ retry = FALSE;
+ break;
+ }
+ }
+ break;
+ }
+
+ case SCSI_ADSENSE_RESOURCE_FAILURE: {
+
+ switch (addlSenseCodeQual) {
+
+ //
+ // 1. Copy manager wasn't able to finish the operation because of insuffient resources
+ // (e.g. microsnapshot failure on read, no space on write, etc.)
+ //
+ case SCSI_SENSEQ_INSUFFICIENT_RESOURCES: {
+
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClassInterpretSenseInfo (%p): Target has insufficient resources (command %x)\n",
+ Fdo,
+ cdb->CDB6GENERIC.OperationCode));
+
+ *Status = STATUS_INSUFFICIENT_RESOURCES;
+ retry = FALSE;
+ break;
+ }
+ }
+ break;
+ }
+ }
+ } else {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
+ "Command aborted\n"));
+ *Status = STATUS_IO_DEVICE_ERROR;
+ retryInterval = 1;
+ }
+ break;
+ } // end SCSI_SENSE_ABORTED_COMMAND
+
+ default: {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
+ "Unrecognized sense code\n"));
+ *Status = STATUS_IO_DEVICE_ERROR;
+ break;
+ }
+
+ } // end switch (senseKey)
+
+
+
+ //
+ // Try to determine bad sector information from sense data
+ //
+
+ if (((IS_SCSIOP_READWRITE(cdbOpcode)) ||
+ (cdbOpcode == SCSIOP_VERIFY) ||
+ (cdbOpcode == SCSIOP_VERIFY16)) && cdb) {
+
+ if (isInformationValid)
+ {
+ readSector = 0;
+ badSector = information;
+
+ if (cdbOpcode == SCSIOP_READ16 || cdbOpcode == SCSIOP_WRITE16 || cdbOpcode == SCSIOP_VERIFY16) {
+ REVERSE_BYTES_QUAD(&readSector, &(cdb->AsByte[2]));
+ } else {
+ REVERSE_BYTES(&readSector, &(cdb->AsByte[2]));
+ }
+
+ if (cdbOpcode == SCSIOP_READ || cdbOpcode == SCSIOP_WRITE || cdbOpcode == SCSIOP_VERIFY) {
+ REVERSE_BYTES_SHORT(&index, &(cdb->CDB10.TransferBlocksMsb));
+ } else if (cdbOpcode == SCSIOP_READ6 || cdbOpcode == SCSIOP_WRITE6) {
+ index = cdb->CDB6READWRITE.TransferBlocks;
+ } else if(cdbOpcode == SCSIOP_READ12 || cdbOpcode == SCSIOP_WRITE12) {
+ REVERSE_BYTES(&index, &(cdb->CDB12.TransferLength));
+ } else {
+ REVERSE_BYTES(&index, &(cdb->CDB16.TransferLength));
+ }
+
+ //
+ // Make sure the bad sector is within the read sectors.
+ //
+
+ if (!(badSector >= readSector && badSector < readSector + index)) {
+ badSector = readSector;
+ }
+ }
+ }
+ }
+
+__ClassInterpretSenseInfo_ProcessingInvalidSenseBuffer:
+
+ if (!validSense) {
+
+ //
+ // Request sense buffer not valid. No sense information
+ // to pinpoint the error. Return general request fail.
+ //
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassInterpretSenseInfo: "
+ "Request sense info not valid. SrbStatus %2x\n",
+ SRB_STATUS(Srb->SrbStatus)));
+ retry = TRUE;
+
+
+ switch (SRB_STATUS(Srb->SrbStatus)) {
+ case SRB_STATUS_ABORTED: {
+
+ //
+ // Update the error count for the device.
+ //
+
+ incrementErrorCount = TRUE;
+ *Status = STATUS_IO_TIMEOUT;
+ retryInterval = 1;
+ retry = TRUE;
+ break;
+ }
+
+ case SRB_STATUS_ERROR: {
+
+ *Status = STATUS_IO_DEVICE_ERROR;
+ if (SrbGetScsiStatus(Srb) == SCSISTAT_GOOD) {
+
+ //
+ // This is some strange return code. Update the error
+ // count for the device.
+ //
+
+ incrementErrorCount = TRUE;
+
+ } else if (SrbGetScsiStatus(Srb) == SCSISTAT_BUSY) {
+
+ *Status = STATUS_DEVICE_NOT_READY;
+ logRetryableError = FALSE;
+ }
+
+ break;
+ }
+
+ case SRB_STATUS_INVALID_REQUEST: {
+ *Status = STATUS_INVALID_DEVICE_REQUEST;
+ retry = FALSE;
+ break;
+ }
+
+ case SRB_STATUS_INVALID_PATH_ID:
+ case SRB_STATUS_NO_DEVICE:
+ case SRB_STATUS_NO_HBA:
+ case SRB_STATUS_INVALID_LUN:
+ case SRB_STATUS_INVALID_TARGET_ID: {
+ *Status = STATUS_NO_SUCH_DEVICE;
+ retry = FALSE;
+ break;
+ }
+
+ case SRB_STATUS_SELECTION_TIMEOUT: {
+ logError = TRUE;
+ logStatus = IO_ERR_NOT_READY;
+ uniqueId = 260;
+ *Status = STATUS_DEVICE_NOT_CONNECTED;
+ retry = FALSE;
+ break;
+ }
+
+ case SRB_STATUS_TIMEOUT:
+ case SRB_STATUS_COMMAND_TIMEOUT: {
+
+ //
+ // Update the error count for the device.
+ //
+ incrementErrorCount = TRUE;
+ *Status = STATUS_IO_TIMEOUT;
+ break;
+ }
+
+ case SRB_STATUS_PARITY_ERROR:
+ case SRB_STATUS_UNEXPECTED_BUS_FREE:
+
+ //
+ // Update the error count for the device
+ // and fall through to below
+ //
+ incrementErrorCount = TRUE;
+
+ case SRB_STATUS_BUS_RESET: {
+
+ *Status = STATUS_IO_DEVICE_ERROR;
+ logRetryableError = FALSE;
+ break;
+ }
+
+ case SRB_STATUS_DATA_OVERRUN: {
+
+ *Status = STATUS_DATA_OVERRUN;
+ retry = FALSE;
+
+ //
+ // For some commands, we allocate a buffer that may be
+ // larger than necessary. In these cases, the SRB may be
+ // returned with SRB_STATUS_DATA_OVERRUN to indicate a
+ // buffer *underrun*. However, the command was still
+ // successful so we ensure STATUS_SUCCESS is returned.
+ // We will also prevent these errors from causing noise in
+ // the error logs.
+ //
+ if ((cdbOpcode == SCSIOP_MODE_SENSE && SrbGetDataTransferLength(Srb) <= cdb->MODE_SENSE.AllocationLength) ||
+ (cdbOpcode == SCSIOP_INQUIRY && SrbGetDataTransferLength(Srb) <= cdb->CDB6INQUIRY.AllocationLength)) {
+ *Status = STATUS_SUCCESS;
+ logErrorInternal = FALSE;
+ logError = FALSE;
+ } else if (cdbOpcode == SCSIOP_MODE_SENSE10) {
+ USHORT allocationLength;
+ REVERSE_BYTES_SHORT(&(cdb->MODE_SENSE10.AllocationLength), &allocationLength);
+ if (SrbGetDataTransferLength(Srb) <= allocationLength) {
+ *Status = STATUS_SUCCESS;
+ logErrorInternal = FALSE;
+ logError = FALSE;
+ }
+ } else if (ClasspIsReceiveTokenInformation(cdb)) {
+ ULONG allocationLength;
+ REVERSE_BYTES(&(cdb->RECEIVE_TOKEN_INFORMATION.AllocationLength), &allocationLength);
+ if (SrbGetDataTransferLength(Srb) <= allocationLength) {
+ *Status = STATUS_SUCCESS;
+ logErrorInternal = FALSE;
+ logError = FALSE;
+ }
+ }
+
+ break;
+ }
+
+ case SRB_STATUS_PHASE_SEQUENCE_FAILURE: {
+
+ //
+ // Update the error count for the device.
+ //
+
+ incrementErrorCount = TRUE;
+ *Status = STATUS_IO_DEVICE_ERROR;
+
+ //
+ // If there was phase sequence error then limit the number of
+ // retries.
+ //
+
+ if (RetryCount > 1 ) {
+ retry = FALSE;
+ }
+
+ break;
+ }
+
+ case SRB_STATUS_REQUEST_FLUSHED: {
+
+ //
+ // If the status needs verification bit is set. Then set
+ // the status to need verification and no retry; otherwise,
+ // just retry the request.
+ //
+
+ if (TEST_FLAG(Fdo->Flags, DO_VERIFY_VOLUME)) {
+
+ *Status = STATUS_VERIFY_REQUIRED;
+ retry = FALSE;
+
+ } else {
+ *Status = STATUS_IO_DEVICE_ERROR;
+ logRetryableError = FALSE;
+ }
+
+ break;
+ }
+
+ default: {
+ logError = TRUE;
+ logStatus = IO_ERR_CONTROLLER_ERROR;
+ uniqueId = 259;
+ *Status = STATUS_IO_DEVICE_ERROR;
+ unhandledError = TRUE;
+ logRetryableError = FALSE;
+ break;
+ }
+ }
+
+
+ //
+ // NTRAID #183546 - if we support GESN subtype NOT_READY events, and
+ // we know from a previous poll when the device will be ready (ETA)
+ // we should delay the retry more appropriately than just guessing.
+ //
+ /*
+ if (fdoExtension->MediaChangeDetectionInfo &&
+ fdoExtension->MediaChangeDetectionInfo->Gesn.Supported &&
+ TEST_FLAG(fdoExtension->MediaChangeDetectionInfo->Gesn.EventMask,
+ NOTIFICATION_DEVICE_BUSY_CLASS_MASK)
+ ) {
+ // check if Gesn.ReadyTime if greater than current tick count
+ // if so, delay that long (from 1 to 30 seconds max?)
+ // else, leave the guess of time alone.
+ }
+ */
+
+ }
+
+ }
+
+ if (incrementErrorCount) {
+
+ //
+ // if any error count occurred, delay the retry of this io by
+ // at least one second, if caller supports it.
+ //
+
+ if (retryInterval == 0) {
+ retryInterval = 1;
+ }
+ ClasspPerfIncrementErrorCount(fdoExtension);
+ }
+
+ //
+ // If there is a class specific error handler call it.
+ //
+
+ if (fdoExtension->CommonExtension.DevInfo->ClassError != NULL) {
+
+ SCSI_REQUEST_BLOCK tempSrb = {0};
+ PSCSI_REQUEST_BLOCK srbPtr = (PSCSI_REQUEST_BLOCK)Srb;
+
+ //
+ // If class driver does not support extended SRB and this is
+ // an extended SRB, convert to legacy SRB and pass to class
+ // driver.
+ //
+ if ((Srb->Function == SRB_FUNCTION_STORAGE_REQUEST_BLOCK) &&
+ ((fdoExtension->CommonExtension.DriverExtension->SrbSupport &
+ CLASS_SRB_STORAGE_REQUEST_BLOCK) == 0)) {
+ ClasspConvertToScsiRequestBlock(&tempSrb, (PSTORAGE_REQUEST_BLOCK)Srb);
+ srbPtr = &tempSrb;
+ }
+
+ fdoExtension->CommonExtension.DevInfo->ClassError(Fdo,
+ srbPtr,
+ Status,
+ &retry);
+ }
+
+ //
+ // If the caller wants to know the suggested retry interval tell them.
+ //
+
+ if (ARGUMENT_PRESENT(RetryInterval)) {
+ *RetryInterval = retryInterval;
+ }
+
+ //
+ // The RESERVE(6) / RELEASE(6) commands are optional. So
+ // if they aren't supported, try the 10-byte equivalents
+ //
+
+ cdb = SrbGetCdb(Srb);
+ if (cdb) {
+ cdbOpcode = cdb->CDB6GENERIC.OperationCode;
+ }
+
+ if ((cdbOpcode == SCSIOP_RESERVE_UNIT ||
+ cdbOpcode == SCSIOP_RELEASE_UNIT) && cdb)
+ {
+ if (*Status == STATUS_INVALID_DEVICE_REQUEST)
+ {
+ SrbSetCdbLength(Srb, 10);
+ cdb->CDB10.OperationCode = (cdb->CDB6GENERIC.OperationCode == SCSIOP_RESERVE_UNIT) ? SCSIOP_RESERVE_UNIT10 : SCSIOP_RELEASE_UNIT10;
+
+ SET_FLAG(fdoExtension->PrivateFdoData->HackFlags, FDO_HACK_NO_RESERVE6);
+ retry = TRUE;
+ }
+ }
+
+#if DBG
+
+ //
+ // Ensure that for read/write requests, only return STATUS_DEVICE_BUSY if
+ // reservation conflict.
+ //
+ if (IS_SCSIOP_READWRITE(cdbOpcode) && (*Status == STATUS_DEVICE_BUSY)) {
+ NT_ASSERT(isReservationConflict == TRUE);
+ }
+
+#endif
+
+ /*
+ * LOG the error:
+ * If logErrorInternal is set, log the error in our internal log.
+ * If logError is set, also log the error in the system log.
+ */
+ if (logErrorInternal || logError) {
+ ULONG totalSize;
+ ULONG senseBufferSize = 0;
+ IO_ERROR_LOG_PACKET staticErrLogEntry = {0};
+ CLASS_ERROR_LOG_DATA staticErrLogData = {0};
+ SENSE_DATA convertedSenseBuffer = {0};
+ UCHAR convertedSenseBufferLength = 0;
+ BOOLEAN senseDataConverted = FALSE;
+
+ //
+ // Logic below assumes that IO_ERROR_LOG_PACKET + CLASS_ERROR_LOG_DATA
+ // is less than ERROR_LOG_MAXIMUM_SIZE which is not true for extended SRB.
+ // Given that classpnp currently does not use >16 byte CDB, we'll convert
+ // an extended SRB to SCSI_REQUEST_BLOCK instead of changing this code.
+ // More changes will need to be made when classpnp starts using >16 byte
+ // CDBs.
+ //
+
+ //
+ // Calculate the total size of the error log entry.
+ // add to totalSize in the order that they are used.
+ // the advantage to calculating all the sizes here is
+ // that we don't have to do a bunch of extraneous checks
+ // later on in this code path.
+ //
+ totalSize = sizeof(IO_ERROR_LOG_PACKET) // required
+ + sizeof(CLASS_ERROR_LOG_DATA);// struct for ease
+
+ //
+ // also save any available extra sense data, up to the maximum errlog
+ // packet size . WMI should be used for real-time analysis.
+ // the event log should only be used for post-mortem debugging.
+ //
+ if ((TEST_FLAG(Srb->SrbStatus, SRB_STATUS_AUTOSENSE_VALID)) && senseBuffer) {
+
+ UCHAR validSenseBytes = 0;
+ UCHAR senseInfoBufferLength = 0;
+
+ senseInfoBufferLength = SrbGetSenseInfoBufferLength(Srb);
+
+ //
+ // If sense data is in Descriptor format, convert it to Fixed format
+ // for the private log.
+ //
+
+ if (IsDescriptorSenseDataFormat(senseBuffer)) {
+
+ convertedSenseBufferLength = sizeof(convertedSenseBuffer);
+
+ senseDataConverted = ScsiConvertToFixedSenseFormat(senseBuffer,
+ senseInfoBufferLength,
+ (PVOID)&convertedSenseBuffer,
+ convertedSenseBufferLength);
+ }
+
+ //
+ // For System Log, copy the maximum amount of available sense data
+ //
+
+ if (ScsiGetTotalSenseByteCountIndicated(senseBuffer,
+ senseInfoBufferLength,
+ &validSenseBytes)) {
+
+ //
+ // If it is able to determine number of valid bytes,
+ // copy the maximum amount of available
+ // sense data that can be saved into the the errlog.
+ //
+
+ //
+ // set to save the most sense buffer possible
+ //
+
+ senseBufferSize = max(validSenseBytes, sizeof(staticErrLogData.SenseData));
+ senseBufferSize = min(senseBufferSize, senseInfoBufferLength);
+
+ } else {
+ //
+ // it's smaller than required to read the total number of
+ // valid bytes, so just use the SenseInfoBufferLength field.
+ //
+ senseBufferSize = senseInfoBufferLength;
+ }
+
+ /*
+ * Bump totalSize by the number of extra senseBuffer bytes
+ * (beyond the default sense buffer within CLASS_ERROR_LOG_DATA).
+ * Make sure to never allocate more than ERROR_LOG_MAXIMUM_SIZE.
+ */
+ if (senseBufferSize > sizeof(staticErrLogData.SenseData)){
+ totalSize += senseBufferSize-sizeof(staticErrLogData.SenseData);
+ if (totalSize > ERROR_LOG_MAXIMUM_SIZE){
+ senseBufferSize -= totalSize-ERROR_LOG_MAXIMUM_SIZE;
+ totalSize = ERROR_LOG_MAXIMUM_SIZE;
+ }
+ }
+ }
+
+ //
+ // If we've used up all of our retry attempts, set the final status to
+ // reflect the appropriate result.
+ //
+ // ISSUE: the test below should also check RetryCount to determine if we will actually retry,
+ // but there is no easy test because we'd have to consider the original retry count
+ // for the op; besides, InterpretTransferPacketError sometimes ignores the retry
+ // decision returned by this function. So just ErrorRetried to be true in the majority case.
+ //
+ if (retry){
+ staticErrLogEntry.FinalStatus = STATUS_SUCCESS;
+ staticErrLogData.ErrorRetried = TRUE;
+ } else {
+ staticErrLogEntry.FinalStatus = *Status;
+ }
+
+ //
+ // Don't log generic IO_WARNING_PAGING_FAILURE message if either the
+ // I/O is retried, or it completed successfully.
+ //
+ if (logStatus == IO_WARNING_PAGING_FAILURE &&
+ (retry || NT_SUCCESS(*Status)) ) {
+ logError = FALSE;
+ }
+
+ if (TEST_FLAG(SrbGetSrbFlags(Srb), SRB_CLASS_FLAGS_PAGING)) {
+ staticErrLogData.ErrorPaging = TRUE;
+ }
+ if (unhandledError) {
+ staticErrLogData.ErrorUnhandled = TRUE;
+ }
+
+ //
+ // Calculate the device offset if there is a geometry.
+ //
+ staticErrLogEntry.DeviceOffset.QuadPart = (LONGLONG)badSector;
+ staticErrLogEntry.DeviceOffset.QuadPart *= (LONGLONG)fdoExtension->DiskGeometry.BytesPerSector;
+ if (logStatus == -1){
+ staticErrLogEntry.ErrorCode = STATUS_IO_DEVICE_ERROR;
+ } else {
+ staticErrLogEntry.ErrorCode = logStatus;
+ }
+
+ /*
+ * The dump data follows the IO_ERROR_LOG_PACKET
+ */
+ staticErrLogEntry.DumpDataSize = (USHORT)totalSize - sizeof(IO_ERROR_LOG_PACKET);
+
+ staticErrLogEntry.SequenceNumber = 0;
+ staticErrLogEntry.MajorFunctionCode = MajorFunctionCode;
+ staticErrLogEntry.IoControlCode = IoDeviceCode;
+ staticErrLogEntry.RetryCount = (UCHAR) RetryCount;
+ staticErrLogEntry.UniqueErrorValue = uniqueId;
+
+ KeQueryTickCount(&staticErrLogData.TickCount);
+ staticErrLogData.PortNumber = (ULONG)-1;
+
+ /*
+ * Save the entire contents of the SRB.
+ */
+ if (Srb->Function == SRB_FUNCTION_STORAGE_REQUEST_BLOCK) {
+ ClasspConvertToScsiRequestBlock(&staticErrLogData.Srb, (PSTORAGE_REQUEST_BLOCK)Srb);
+ } else {
+ staticErrLogData.Srb = *(PSCSI_REQUEST_BLOCK)Srb;
+ }
+
+ /*
+ * For our private log, save just the default length of the SENSE_DATA.
+ */
+
+ if ((senseBufferSize != 0) && senseBuffer) {
+
+ //
+ // If sense buffer is in Fixed format, put it in the private log
+ //
+ // If sense buffer is in Descriptor format, put it in the private log if conversion to Fixed format
+ // succeeded. Otherwise, do not put it in the private log.
+ //
+ // If sense buffer is in unknown format, the device or the driver probably does not populate
+ // the first byte of sense data, we probably still want to log error in this case assuming
+ // it's fixed format, so that its sense key, its additional sense code, and its additional sense code
+ // qualifier would be shown in the debugger extension output. By doing so, it minimizes any potential
+ // negative impacts to our ability to diagnose issue.
+ //
+ if (IsDescriptorSenseDataFormat(senseBuffer)) {
+ if (senseDataConverted) {
+ RtlCopyMemory(&staticErrLogData.SenseData, &convertedSenseBuffer, min(convertedSenseBufferLength, sizeof(staticErrLogData.SenseData)));
+ }
+ } else {
+ RtlCopyMemory(&staticErrLogData.SenseData, senseBuffer, min(senseBufferSize, sizeof(staticErrLogData.SenseData)));
+ }
+ }
+
+ /*
+ * Save the error log in our context.
+ * We only save the default sense buffer length.
+ */
+ if (logErrorInternal) {
+ KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
+ fdoData->ErrorLogs[fdoData->ErrorLogNextIndex] = staticErrLogData;
+ fdoData->ErrorLogNextIndex++;
+ fdoData->ErrorLogNextIndex %= NUM_ERROR_LOG_ENTRIES;
+ KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
+ }
+
+ /*
+ * Log an event if an IO is being retried for reasons that may indicate
+ * a transient/permanent problem with the I_T_L nexus. But log the event
+ * only once per retried IO.
+ */
+ if (!IS_SCSIOP_READWRITE(cdbOpcode) ||
+ !retry ||
+ (RetryCount != 0)) {
+
+ logRetryableError = FALSE;
+ }
+
+ if (logRetryableError) {
+
+ logError = TRUE;
+ }
+
+ /*
+ * If logError is set, also save this log in the system's error log.
+ * But make sure we don't log TUR failures over and over
+ * (e.g. if an external drive was switched off and we're still sending TUR's to it every second).
+ */
+
+ if (logError)
+ {
+ //
+ // We do not want to log certain system events repetitively
+ //
+
+ cdb = SrbGetCdb(Srb);
+ if (cdb) {
+ switch (cdb->CDB10.OperationCode)
+ {
+ case SCSIOP_TEST_UNIT_READY:
+ {
+ if (fdoData->LoggedTURFailureSinceLastIO)
+ {
+ logError = FALSE;
+ }
+ else
+ {
+ fdoData->LoggedTURFailureSinceLastIO = TRUE;
+ }
+
+ break;
+ }
+
+ case SCSIOP_SYNCHRONIZE_CACHE:
+ {
+ if (fdoData->LoggedSYNCFailure)
+ {
+ logError = FALSE;
+ }
+ else
+ {
+ fdoData->LoggedSYNCFailure = TRUE;
+ }
+
+ break;
+ }
+ }
+ }
+ }
+
+ if (logError){
+
+ if (logRetryableError) {
+
+ NT_ASSERT(IS_SCSIOP_READWRITE(cdbOpcode));
+
+ //
+ // A large Disk TimeOutValue (like 60 seconds) results in giving a command a
+ // large window to complete in. However, if the target returns a retryable error
+ // just prior to the command timing out, and if multiple retries kick in, it may
+ // take a significantly long time for the request to complete back to the
+ // application, leading to a user perception of a hung system. So log an event
+ // for retried IO so that an admin can help explain the reason for this behavior.
+ //
+ ClasspQueueLogIOEventWithContextWorker(Fdo,
+ senseBufferSize,
+ senseBuffer,
+ SRB_STATUS(Srb->SrbStatus),
+ SrbGetScsiStatus(Srb),
+ (ULONG)IO_WARNING_IO_OPERATION_RETRIED,
+ cdbLength,
+ cdb,
+ NULL);
+
+ } else {
+
+ PIO_ERROR_LOG_PACKET errorLogEntry;
+ PCLASS_ERROR_LOG_DATA errlogData;
+
+ errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry(Fdo, (UCHAR)totalSize);
+ if (errorLogEntry){
+ errlogData = (PCLASS_ERROR_LOG_DATA)errorLogEntry->DumpData;
+
+ *errorLogEntry = staticErrLogEntry;
+ *errlogData = staticErrLogData;
+
+ /*
+ * For the system log, copy as much of the sense buffer as possible.
+ */
+ if ((senseBufferSize != 0) && senseBuffer) {
+ RtlCopyMemory(&errlogData->SenseData, senseBuffer, senseBufferSize);
+ }
+
+ /*
+ * Write the error log packet to the system error logging thread.
+ * It will be freed by the kernel.
+ */
+ IoWriteErrorLogEntry(errorLogEntry);
+ }
+ }
+ }
+ }
+
+ return retry;
+
+} // end ClassInterpretSenseInfo()
+
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassModeSense()
+
+Routine Description:
+
+ This routine sends a mode sense command to a target ID and returns
+ when it is complete.
+
+Arguments:
+
+ Fdo - Supplies the functional device object associated with this request.
+
+ ModeSenseBuffer - Supplies a buffer to store the sense data.
+
+ Length - Supplies the length in bytes of the mode sense buffer.
+
+ PageMode - Supplies the page or pages of mode sense data to be retrived.
+
+Return Value:
+
+ Length of the transferred data is returned.
+
+--*/
+ULONG ClassModeSense(
+ _In_ PDEVICE_OBJECT Fdo,
+ _In_reads_bytes_(Length) PCHAR ModeSenseBuffer,
+ _In_ ULONG Length,
+ _In_ UCHAR PageMode
+ )
+{
+ PAGED_CODE();
+
+ return ClasspModeSense(Fdo,
+ ModeSenseBuffer,
+ Length,
+ PageMode,
+ MODE_SENSE_CURRENT_VALUES);
+}
+
+
+ULONG
+ClassModeSenseEx(
+ _In_ PDEVICE_OBJECT Fdo,
+ _In_reads_bytes_(Length) PCHAR ModeSenseBuffer,
+ _In_ ULONG Length,
+ _In_ UCHAR PageMode,
+ _In_ UCHAR PageControl
+ )
+{
+ PAGED_CODE();
+#if (NTDDI_VERSION >= NTDDI_WINBLUE)
+ return ClasspModeSense(Fdo,
+ ModeSenseBuffer,
+ Length,
+ PageMode,
+ PageControl);
+#else
+ UNREFERENCED_PARAMETER(Fdo);
+ UNREFERENCED_PARAMETER(ModeSenseBuffer);
+ UNREFERENCED_PARAMETER(Length);
+ UNREFERENCED_PARAMETER(PageMode);
+ UNREFERENCED_PARAMETER(PageControl);
+ return 0;
+#endif
+}
+
+ULONG ClasspModeSense(
+ _In_ PDEVICE_OBJECT Fdo,
+ _In_reads_bytes_(Length) PCHAR ModeSenseBuffer,
+ _In_ ULONG Length,
+ _In_ UCHAR PageMode,
+ _In_ UCHAR PageControl
+ )
+/*
+Routine Description:
+
+ This routine sends a mode sense command to a target ID and returns
+ when it is complete.
+
+Arguments:
+
+ Fdo - Supplies the functional device object associated with this request.
+
+ ModeSenseBuffer - Supplies a buffer to store the sense data.
+
+ Length - Supplies the length in bytes of the mode sense buffer.
+
+ PageMode - Supplies the page or pages of mode sense data to be retrived.
+
+ PageControl - Supplies the page control value of the request, which is
+ one of the following:
+ MODE_SENSE_CURRENT_VALUES
+ MODE_SENSE_CHANGEABLE_VALUES
+ MODE_SENSE_DEFAULT_VAULES
+ MODE_SENSE_SAVED_VALUES
+
+Return Value:
+
+ Length of the transferred data is returned.
+
+--*/
+{
+ ULONG lengthTransferred = 0;
+ PMDL senseBufferMdl;
+
+ PAGED_CODE();
+
+ senseBufferMdl = BuildDeviceInputMdl(ModeSenseBuffer, Length);
+ if (senseBufferMdl){
+
+ TRANSFER_PACKET *pkt = DequeueFreeTransferPacket(Fdo, TRUE);
+ if (pkt){
+ KEVENT event;
+ IRP pseudoIrp = {0};
+
+ /*
+ * Store the number of packets servicing the irp (one)
+ * inside the original IRP. It will be used to counted down
+ * to zero when the packet completes.
+ * Initialize the original IRP's status to success.
+ * If the packet fails, we will set it to the error status.
+ */
+ pseudoIrp.Tail.Overlay.DriverContext[0] = LongToPtr(1);
+ pseudoIrp.IoStatus.Status = STATUS_SUCCESS;
+ pseudoIrp.IoStatus.Information = 0;
+ pseudoIrp.MdlAddress = senseBufferMdl;
+
+ /*
+ * Set this up as a SYNCHRONOUS transfer, submit it,
+ * and wait for the packet to complete. The result
+ * status will be written to the original irp.
+ */
+ NT_ASSERT(Length <= 0x0ff);
+ KeInitializeEvent(&event, SynchronizationEvent, FALSE);
+ SetupModeSenseTransferPacket(pkt, &event, ModeSenseBuffer, (UCHAR)Length, PageMode, 0, &pseudoIrp, PageControl);
+ SubmitTransferPacket(pkt);
+ (VOID)KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
+
+ if (NT_SUCCESS(pseudoIrp.IoStatus.Status)){
+ lengthTransferred = (ULONG)pseudoIrp.IoStatus.Information;
+ }
+ else {
+ /*
+ * This request can sometimes fail legitimately
+ * (e.g. when a SCSI device is attached but turned off)
+ * so this is not necessarily a device/driver bug.
+ */
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClasspModeSense on Fdo %ph failed with status %xh.", Fdo, pseudoIrp.IoStatus.Status));
+ }
+ }
+
+ FreeDeviceInputMdl(senseBufferMdl);
+ }
+
+ return lengthTransferred;
+}
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassFindModePage()
+
+Routine Description:
+
+ This routine scans through the mode sense data and finds the requested
+ mode sense page code.
+
+Arguments:
+ ModeSenseBuffer - Supplies a pointer to the mode sense data.
+
+ Length - Indicates the length of valid data.
+
+ PageMode - Supplies the page mode to be searched for.
+
+ Use6Byte - Indicates whether 6 or 10 byte mode sense was used.
+
+Return Value:
+
+ A pointer to the the requested mode page. If the mode page was not found
+ then NULL is return.
+
+--*/
+PVOID
+ClassFindModePage(
+ _In_reads_bytes_(Length) PCHAR ModeSenseBuffer,
+ _In_ ULONG Length,
+ _In_ UCHAR PageMode,
+ _In_ BOOLEAN Use6Byte
+ )
+{
+ PUCHAR limit;
+ ULONG parameterHeaderLength;
+ PVOID result = NULL;
+
+ limit = (PUCHAR)ModeSenseBuffer + Length;
+ parameterHeaderLength = (Use6Byte) ? sizeof(MODE_PARAMETER_HEADER) : sizeof(MODE_PARAMETER_HEADER10);
+
+ if (Length >= parameterHeaderLength) {
+
+ PMODE_PARAMETER_HEADER10 modeParam10;
+ ULONG blockDescriptorLength;
+
+ /*
+ * Skip the mode select header and block descriptors.
+ */
+ if (Use6Byte){
+ blockDescriptorLength = ((PMODE_PARAMETER_HEADER) ModeSenseBuffer)->BlockDescriptorLength;
+ }
+ else {
+ modeParam10 = (PMODE_PARAMETER_HEADER10) ModeSenseBuffer;
+ blockDescriptorLength = modeParam10->BlockDescriptorLength[1];
+ }
+
+ ModeSenseBuffer += parameterHeaderLength + blockDescriptorLength;
+
+ //
+ // ModeSenseBuffer now points at pages. Walk the pages looking for the
+ // requested page until the limit is reached.
+ //
+
+ while (ModeSenseBuffer +
+ RTL_SIZEOF_THROUGH_FIELD(MODE_DISCONNECT_PAGE, PageLength) < (PCHAR)limit) {
+
+ if (((PMODE_DISCONNECT_PAGE) ModeSenseBuffer)->PageCode == PageMode) {
+
+ /*
+ * found the mode page. make sure it's safe to touch it all
+ * before returning the pointer to caller
+ */
+
+ if (ModeSenseBuffer + ((PMODE_DISCONNECT_PAGE)ModeSenseBuffer)->PageLength > (PCHAR)limit) {
+ /*
+ * Return NULL since the page is not safe to access in full
+ */
+ result = NULL;
+ }
+ else {
+ result = ModeSenseBuffer;
+ }
+ break;
+ }
+
+ //
+ // Advance to the next page which is 4-byte-aligned offset after this page.
+ //
+ ModeSenseBuffer +=
+ ((PMODE_DISCONNECT_PAGE) ModeSenseBuffer)->PageLength +
+ RTL_SIZEOF_THROUGH_FIELD(MODE_DISCONNECT_PAGE, PageLength);
+
+ }
+ }
+
+ return result;
+} // end ClassFindModePage()
+
+
+NTSTATUS
+ClassModeSelect(
+ _In_ PDEVICE_OBJECT Fdo,
+ _In_reads_bytes_(Length) PCHAR ModeSelectBuffer,
+ _In_ ULONG Length,
+ _In_ BOOLEAN SavePages
+ )
+{
+#if (NTDDI_VERSION >= NTDDI_WINBLUE)
+ return ClasspModeSelect(Fdo,
+ ModeSelectBuffer,
+ Length,
+ SavePages);
+#else
+ UNREFERENCED_PARAMETER(Fdo);
+ UNREFERENCED_PARAMETER(ModeSelectBuffer);
+ UNREFERENCED_PARAMETER(Length);
+ UNREFERENCED_PARAMETER(SavePages);
+ return STATUS_NOT_SUPPORTED;
+#endif
+}
+
+/*++
+ClasspModeSelect()
+
+Routine Description:
+
+ This routine sends a mode select command to a target ID and returns
+ when it is complete.
+
+Arguments:
+
+ Fdo - Supplies the functional device object associated with this request.
+
+ ModeSelectBuffer - Supplies a buffer to the select data.
+
+ Length - Supplies the length in bytes of the mode select buffer.
+
+ SavePages - Specifies the value of the save pages (SP) bit in the mode
+ select command.
+
+Return Value:
+
+ NTSTATUS code of the request.
+
+--*/
+NTSTATUS
+ClasspModeSelect(
+ _In_ PDEVICE_OBJECT Fdo,
+ _In_reads_bytes_(Length) PCHAR ModeSelectBuffer,
+ _In_ ULONG Length,
+ _In_ BOOLEAN SavePages
+ )
+{
+
+ PMDL senseBufferMdl;
+ NTSTATUS status = STATUS_UNSUCCESSFUL;
+
+ senseBufferMdl = BuildDeviceInputMdl(ModeSelectBuffer, Length);
+ if (senseBufferMdl) {
+
+ TRANSFER_PACKET *pkt = DequeueFreeTransferPacket(Fdo, TRUE);
+ if (pkt){
+ KEVENT event;
+ IRP pseudoIrp = {0};
+
+ /*
+ * Store the number of packets servicing the irp (one)
+ * inside the original IRP. It will be used to counted down
+ * to zero when the packet completes.
+ * Initialize the original IRP's status to success.
+ * If the packet fails, we will set it to the error status.
+ */
+ pseudoIrp.Tail.Overlay.DriverContext[0] = LongToPtr(1);
+ pseudoIrp.IoStatus.Status = STATUS_SUCCESS;
+ pseudoIrp.IoStatus.Information = 0;
+ pseudoIrp.MdlAddress = senseBufferMdl;
+
+ /*
+ * Set this up as a SYNCHRONOUS transfer, submit it,
+ * and wait for the packet to complete. The result
+ * status will be written to the original irp.
+ */
+ NT_ASSERT(Length <= 0x0ff);
+ KeInitializeEvent(&event, SynchronizationEvent, FALSE);
+ SetupModeSelectTransferPacket(pkt, &event, ModeSelectBuffer, (UCHAR)Length, SavePages, &pseudoIrp);
+ SubmitTransferPacket(pkt);
+ (VOID)KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
+
+ if (!NT_SUCCESS(pseudoIrp.IoStatus.Status)){
+ /*
+ * This request can sometimes fail legitimately
+ * (e.g. when a SCSI device is attached but turned off)
+ * so this is not necessarily a device/driver bug.
+ */
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassModeSelect on Fdo %ph failed with status %xh.", Fdo, pseudoIrp.IoStatus.Status));
+ }
+
+ status = pseudoIrp.IoStatus.Status;
+ }
+
+ FreeDeviceInputMdl(senseBufferMdl);
+ } else {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ return status;
+}
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassSendSrbAsynchronous()
+
+Routine Description:
+
+ This routine takes a partially built Srb and an Irp and sends it down to
+ the port driver.
+
+ This routine must be called with the remove lock held for the specified
+ Irp.
+
+Arguments:
+
+ Fdo - Supplies the functional device object for the orginal request.
+
+ Srb - Supplies a paritally build ScsiRequestBlock. In particular, the
+ CDB and the SRB timeout value must be filled in. The SRB must not be
+ allocated from zone.
+
+ Irp - Supplies the requesting Irp.
+
+ BufferAddress - Supplies a pointer to the buffer to be transfered.
+
+ BufferLength - Supplies the length of data transfer.
+
+ WriteToDevice - Indicates the data transfer will be from system memory to
+ device.
+
+Return Value:
+
+ Returns STATUS_PENDING if the request is dispatched (since the
+ completion routine may change the irp's status value we cannot simply
+ return the value of the dispatch)
+
+ or returns a status value to indicate why it failed.
+
+--*/
+_Success_(return == STATUS_PENDING)
+NTSTATUS
+ClassSendSrbAsynchronous(
+ _In_ PDEVICE_OBJECT Fdo,
+ _Inout_ __on_failure(__drv_freesMem(Mem)) __drv_aliasesMem PSCSI_REQUEST_BLOCK _Srb,
+ _In_ PIRP Irp,
+ _In_reads_bytes_opt_(BufferLength) __drv_aliasesMem PVOID BufferAddress,
+ _In_ ULONG BufferLength,
+ _In_ BOOLEAN WriteToDevice
+ )
+{
+
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+ PIO_STACK_LOCATION irpStack;
+ PSTORAGE_REQUEST_BLOCK_HEADER Srb = (PSTORAGE_REQUEST_BLOCK_HEADER)_Srb;
+
+ ULONG savedFlags;
+
+ if (Srb->Function != SRB_FUNCTION_STORAGE_REQUEST_BLOCK) {
+ //
+ // Write length to SRB.
+ //
+
+ Srb->Length = sizeof(SCSI_REQUEST_BLOCK);
+
+ //
+ // Set SCSI bus address.
+ //
+
+ Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
+ }
+
+ //
+ // This is a violation of the SCSI spec but it is required for
+ // some targets.
+ //
+
+ // Srb->Cdb[1] |= deviceExtension->Lun << 5;
+
+ //
+ // Indicate auto request sense by specifying buffer and size.
+ //
+
+ SrbSetSenseInfoBuffer(Srb, fdoExtension->SenseData);
+ SrbSetSenseInfoBufferLength(Srb, GET_FDO_EXTENSON_SENSE_DATA_LENGTH(fdoExtension));
+
+ SrbSetDataBuffer(Srb, BufferAddress);
+
+ //
+ // Set the transfer length.
+ //
+ SrbSetDataTransferLength(Srb, BufferLength);
+
+ //
+ // Save the class driver specific flags away.
+ //
+
+ savedFlags = SrbGetSrbFlags(Srb) & SRB_FLAGS_CLASS_DRIVER_RESERVED;
+
+ //
+ // Allow the caller to specify that they do not wish
+ // IoStartNextPacket() to be called in the completion routine.
+ //
+
+ SET_FLAG(savedFlags, (SrbGetSrbFlags(Srb) & SRB_FLAGS_DONT_START_NEXT_PACKET));
+
+ //
+ // If caller wants to this request to be tagged, save this fact.
+ //
+
+ if ( TEST_FLAG(SrbGetSrbFlags(Srb), SRB_FLAGS_QUEUE_ACTION_ENABLE) &&
+ ( SRB_SIMPLE_TAG_REQUEST == SrbGetRequestAttribute(Srb) ||
+ SRB_HEAD_OF_QUEUE_TAG_REQUEST == SrbGetRequestAttribute(Srb) ||
+ SRB_ORDERED_QUEUE_TAG_REQUEST == SrbGetRequestAttribute(Srb) ) ) {
+
+ SET_FLAG(savedFlags, SRB_FLAGS_QUEUE_ACTION_ENABLE);
+ if (TEST_FLAG(SrbGetSrbFlags(Srb), SRB_FLAGS_NO_QUEUE_FREEZE)) {
+ SET_FLAG(savedFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
+ }
+ }
+
+ if (BufferAddress != NULL) {
+
+ //
+ // Build Mdl if necessary.
+ //
+
+ if (Irp->MdlAddress == NULL) {
+
+ PMDL mdl;
+
+ mdl = IoAllocateMdl(BufferAddress,
+ BufferLength,
+ FALSE,
+ FALSE,
+ Irp);
+
+ if ((mdl == NULL) || (Irp->MdlAddress == NULL)) {
+
+ Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ //
+ // ClassIoComplete() would have free'd the srb
+ //
+
+ if (PORT_ALLOCATED_SENSE_EX(fdoExtension, Srb)) {
+ FREE_PORT_ALLOCATED_SENSE_BUFFER_EX(fdoExtension, Srb);
+ }
+ ClassFreeOrReuseSrb(fdoExtension, (PSCSI_REQUEST_BLOCK)Srb);
+ ClassReleaseRemoveLock(Fdo, Irp);
+ ClassCompleteRequest(Fdo, Irp, IO_NO_INCREMENT);
+
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ SET_FLAG(savedFlags, SRB_CLASS_FLAGS_FREE_MDL);
+
+ MmBuildMdlForNonPagedPool(Irp->MdlAddress);
+
+ } else {
+
+ //
+ // Make sure the buffer requested matches the MDL.
+ //
+
+ NT_ASSERT(BufferAddress == MmGetMdlVirtualAddress(Irp->MdlAddress));
+ }
+
+ //
+ // Set read flag.
+ //
+
+ SrbAssignSrbFlags(Srb, WriteToDevice ? SRB_FLAGS_DATA_OUT : SRB_FLAGS_DATA_IN);
+
+ } else {
+
+ //
+ // Clear flags.
+ //
+
+ SrbAssignSrbFlags(Srb, SRB_FLAGS_NO_DATA_TRANSFER);
+ }
+
+ //
+ // Restore saved flags.
+ //
+
+ SrbSetSrbFlags(Srb, savedFlags);
+
+ //
+ // Disable synchronous transfer for these requests.
+ //
+
+ SrbSetSrbFlags(Srb, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
+
+ //
+ // Zero out status.
+ //
+
+ SrbSetScsiStatus(Srb, 0);
+ Srb->SrbStatus = 0;
+
+ SrbSetNextSrb(Srb, NULL);
+
+ //
+ // Save a few parameters in the current stack location.
+ //
+
+ irpStack = IoGetCurrentIrpStackLocation(Irp);
+
+ //
+ // Save retry count in current Irp stack.
+ //
+
+ irpStack->Parameters.Others.Argument4 = (PVOID)MAXIMUM_RETRIES;
+
+ //
+ // Set up IoCompletion routine address.
+ //
+
+ IoSetCompletionRoutine(Irp, ClassIoComplete, Srb, TRUE, TRUE, TRUE);
+
+ //
+ // Get next stack location and
+ // set major function code.
+ //
+
+ irpStack = IoGetNextIrpStackLocation(Irp);
+
+ irpStack->MajorFunction = IRP_MJ_SCSI;
+
+ //
+ // Save SRB address in next stack for port driver.
+ //
+
+ irpStack->Parameters.Scsi.Srb = (PSCSI_REQUEST_BLOCK)Srb;
+
+ //
+ // Set up Irp Address.
+ //
+
+ SrbSetOriginalRequest(Srb, Irp);
+
+ //
+ // Call the port driver to process the request.
+ //
+
+ IoMarkIrpPending(Irp);
+
+ IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, Irp);
+
+ return STATUS_PENDING;
+
+} // end ClassSendSrbAsynchronous()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassDeviceControlDispatch()
+
+Routine Description:
+
+ The routine is the common class driver device control dispatch entry point.
+ This routine is invokes the device-specific drivers DeviceControl routine,
+ (which may call the Class driver's common DeviceControl routine).
+
+Arguments:
+
+ DeviceObject - Supplies a pointer to the device object for this request.
+
+ Irp - Supplies the Irp making the request.
+
+Return Value:
+
+ Returns the status returned from the device-specific driver.
+
+--*/
+NTSTATUS
+ClassDeviceControlDispatch(
+ PDEVICE_OBJECT DeviceObject,
+ PIRP Irp
+ )
+{
+
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ ULONG isRemoved;
+
+ isRemoved = ClassAcquireRemoveLock(DeviceObject, Irp);
+ _Analysis_assume_(isRemoved);
+ if(isRemoved) {
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+
+ Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ return STATUS_DEVICE_DOES_NOT_EXIST;
+ }
+
+ //
+ // Call the class specific driver DeviceControl routine.
+ // If it doesn't handle it, it will call back into ClassDeviceControl.
+ //
+
+ NT_ASSERT(commonExtension->DevInfo->ClassDeviceControl);
+
+ return commonExtension->DevInfo->ClassDeviceControl(DeviceObject,Irp);
+} // end ClassDeviceControlDispatch()
+
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassDeviceControl()
+
+Routine Description:
+
+ The routine is the common class driver device control dispatch function.
+ This routine is called by a class driver when it get an unrecognized
+ device control request. This routine will perform the correct action for
+ common requests such as lock media. If the device request is unknown it
+ passed down to the next level.
+
+ This routine must be called with the remove lock held for the specified
+ irp.
+
+Arguments:
+
+ DeviceObject - Supplies a pointer to the device object for this request.
+
+ Irp - Supplies the Irp making the request.
+
+Return Value:
+
+ Returns back a STATUS_PENDING or a completion status.
+
+--*/
+NTSTATUS
+ClassDeviceControl(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _Inout_ PIRP Irp
+ )
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
+ PIO_STACK_LOCATION nextStack = NULL;
+
+ ULONG controlCode = irpStack->Parameters.DeviceIoControl.IoControlCode;
+
+ PSCSI_REQUEST_BLOCK srb = NULL;
+ PCDB cdb = NULL;
+
+ NTSTATUS status;
+ ULONG modifiedIoControlCode = 0;
+
+
+ //
+ // If this is a pass through I/O control, set the minor function code
+ // and device address and pass it to the port driver.
+ //
+
+ if ( (controlCode == IOCTL_SCSI_PASS_THROUGH) ||
+ (controlCode == IOCTL_SCSI_PASS_THROUGH_DIRECT) ||
+ (controlCode == IOCTL_SCSI_PASS_THROUGH_EX) ||
+ (controlCode == IOCTL_SCSI_PASS_THROUGH_DIRECT_EX) ) {
+
+
+
+ //
+ // Validiate the user buffer for SCSI pass through.
+ // For pass through EX: as the handler will validate the size anyway,
+ // do not apply the similar check and leave the work to the handler.
+ //
+ if ( (controlCode == IOCTL_SCSI_PASS_THROUGH) ||
+ (controlCode == IOCTL_SCSI_PASS_THROUGH_DIRECT) ) {
+
+ #if BUILD_WOW64_ENABLED && defined(_WIN64)
+
+ if (IoIs32bitProcess(Irp)) {
+
+ if (irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(SCSI_PASS_THROUGH32)){
+
+ Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+
+ status = STATUS_INVALID_PARAMETER;
+ goto SetStatusAndReturn;
+ }
+ }
+ else
+
+ #endif
+
+ {
+ if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
+ sizeof(SCSI_PASS_THROUGH)) {
+
+ Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+
+ status = STATUS_INVALID_PARAMETER;
+ goto SetStatusAndReturn;
+ }
+ }
+ }
+
+
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+
+ nextStack = IoGetNextIrpStackLocation(Irp);
+ nextStack->MinorFunction = 1;
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+
+ status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+ goto SetStatusAndReturn;
+
+ }
+
+ Irp->IoStatus.Information = 0;
+
+
+ switch (controlCode) {
+
+ case IOCTL_MOUNTDEV_QUERY_UNIQUE_ID: {
+
+ PMOUNTDEV_UNIQUE_ID uniqueId;
+
+ if (!commonExtension->MountedDeviceInterfaceName.Buffer) {
+ status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
+ sizeof(MOUNTDEV_UNIQUE_ID)) {
+
+ status = STATUS_BUFFER_TOO_SMALL;
+ Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID);
+ break;
+ }
+
+ uniqueId = Irp->AssociatedIrp.SystemBuffer;
+ RtlZeroMemory(uniqueId, sizeof(MOUNTDEV_UNIQUE_ID));
+ uniqueId->UniqueIdLength =
+ commonExtension->MountedDeviceInterfaceName.Length;
+
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
+ sizeof(USHORT) + uniqueId->UniqueIdLength) {
+
+ status = STATUS_BUFFER_OVERFLOW;
+ Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID);
+ break;
+ }
+
+ RtlCopyMemory(uniqueId->UniqueId,
+ commonExtension->MountedDeviceInterfaceName.Buffer,
+ uniqueId->UniqueIdLength);
+
+ status = STATUS_SUCCESS;
+ Irp->IoStatus.Information = sizeof(USHORT) +
+ uniqueId->UniqueIdLength;
+ break;
+ }
+
+ case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME: {
+
+ PMOUNTDEV_NAME name;
+
+ NT_ASSERT(commonExtension->DeviceName.Buffer);
+
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
+ sizeof(MOUNTDEV_NAME)) {
+
+ status = STATUS_BUFFER_TOO_SMALL;
+ Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME);
+ break;
+ }
+
+ name = Irp->AssociatedIrp.SystemBuffer;
+ RtlZeroMemory(name, sizeof(MOUNTDEV_NAME));
+ name->NameLength = commonExtension->DeviceName.Length;
+
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
+ sizeof(USHORT) + name->NameLength) {
+
+ status = STATUS_BUFFER_OVERFLOW;
+ Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME);
+ break;
+ }
+
+ RtlCopyMemory(name->Name, commonExtension->DeviceName.Buffer,
+ name->NameLength);
+
+ status = STATUS_SUCCESS;
+ Irp->IoStatus.Information = sizeof(USHORT) + name->NameLength;
+ break;
+ }
+
+ case IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME: {
+
+ PMOUNTDEV_SUGGESTED_LINK_NAME suggestedName;
+ WCHAR driveLetterNameBuffer[10] = {0};
+ RTL_QUERY_REGISTRY_TABLE queryTable[2] = {0};
+ PWSTR valueName;
+ UNICODE_STRING driveLetterName;
+
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
+ sizeof(MOUNTDEV_SUGGESTED_LINK_NAME)) {
+
+ status = STATUS_BUFFER_TOO_SMALL;
+ Irp->IoStatus.Information = sizeof(MOUNTDEV_SUGGESTED_LINK_NAME);
+ break;
+ }
+
+ valueName = ExAllocatePoolWithTag(
+ PagedPool,
+ commonExtension->DeviceName.Length + sizeof(WCHAR),
+ '8CcS');
+
+ if (!valueName) {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ break;
+ }
+
+ RtlCopyMemory(valueName, commonExtension->DeviceName.Buffer,
+ commonExtension->DeviceName.Length);
+ valueName[commonExtension->DeviceName.Length/sizeof(WCHAR)] = 0;
+
+ driveLetterName.Buffer = driveLetterNameBuffer;
+ driveLetterName.MaximumLength = sizeof(driveLetterNameBuffer);
+ driveLetterName.Length = 0;
+
+ queryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED |
+ RTL_QUERY_REGISTRY_DIRECT;
+ queryTable[0].Name = valueName;
+ queryTable[0].EntryContext = &driveLetterName;
+
+ status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
+ L"\\Registry\\Machine\\System\\DISK",
+ queryTable, NULL, NULL);
+
+ if (!NT_SUCCESS(status)) {
+ FREE_POOL(valueName);
+ break;
+ }
+
+ if (driveLetterName.Length == 4 &&
+ driveLetterName.Buffer[0] == '%' &&
+ driveLetterName.Buffer[1] == ':') {
+
+ driveLetterName.Buffer[0] = 0xFF;
+
+ } else if (driveLetterName.Length != 4 ||
+ driveLetterName.Buffer[0] < FirstDriveLetter ||
+ driveLetterName.Buffer[0] > LastDriveLetter ||
+ driveLetterName.Buffer[1] != ':') {
+
+ status = STATUS_NOT_FOUND;
+ FREE_POOL(valueName);
+ break;
+ }
+
+ suggestedName = Irp->AssociatedIrp.SystemBuffer;
+ RtlZeroMemory(suggestedName, sizeof(MOUNTDEV_SUGGESTED_LINK_NAME));
+ suggestedName->UseOnlyIfThereAreNoOtherLinks = TRUE;
+ suggestedName->NameLength = 28;
+
+ Irp->IoStatus.Information =
+ FIELD_OFFSET(MOUNTDEV_SUGGESTED_LINK_NAME, Name) + 28;
+
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
+ Irp->IoStatus.Information) {
+
+ Irp->IoStatus.Information =
+ sizeof(MOUNTDEV_SUGGESTED_LINK_NAME);
+ status = STATUS_BUFFER_OVERFLOW;
+ FREE_POOL(valueName);
+ break;
+ }
+
+ RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE,
+ L"\\Registry\\Machine\\System\\DISK",
+ valueName);
+
+ FREE_POOL(valueName);
+
+ RtlCopyMemory(suggestedName->Name, L"\\DosDevices\\", 24);
+ suggestedName->Name[12] = driveLetterName.Buffer[0];
+ suggestedName->Name[13] = ':';
+
+ //
+ // NT_SUCCESS(status) based on RtlQueryRegistryValues
+ //
+ status = STATUS_SUCCESS;
+
+ break;
+ }
+
+ default:
+ status = STATUS_PENDING;
+ break;
+ }
+
+ if (status != STATUS_PENDING) {
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ Irp->IoStatus.Status = status;
+
+
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+ return status;
+ }
+
+ if (commonExtension->IsFdo){
+
+ PULONG_PTR function;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)commonExtension;
+ size_t sizeNeeded;
+
+ //
+ // Allocate a SCSI SRB for handling various IOCTLs.
+ // NOTE - there is a case where an IOCTL is sent to classpnp before AdapterDescriptor
+ // is initialized. In this case, default to legacy SRB.
+ //
+ if ((fdoExtension->AdapterDescriptor != NULL) &&
+ (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK)) {
+ sizeNeeded = CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE;
+ } else {
+ sizeNeeded = sizeof(SCSI_REQUEST_BLOCK);
+ }
+
+ srb = ExAllocatePoolWithTag(NonPagedPoolNx,
+ sizeNeeded +
+ (sizeof(ULONG_PTR) * 2),
+ '9CcS');
+
+ if (srb == NULL) {
+
+ Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ goto SetStatusAndReturn;
+ }
+
+ if ((fdoExtension->AdapterDescriptor != NULL) &&
+ (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK)) {
+ status = InitializeStorageRequestBlock((PSTORAGE_REQUEST_BLOCK)srb,
+ STORAGE_ADDRESS_TYPE_BTL8,
+ CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE,
+ 1,
+ SrbExDataTypeScsiCdb16);
+ if (NT_SUCCESS(status)) {
+ ((PSTORAGE_REQUEST_BLOCK)srb)->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
+ function = (PULONG_PTR)((PCHAR)srb + sizeNeeded);
+ } else {
+ //
+ // Should not occur.
+ //
+ NT_ASSERT(FALSE);
+ goto SetStatusAndReturn;
+ }
+ } else {
+ RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
+ srb->Length = sizeof(SCSI_REQUEST_BLOCK);
+ srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
+ function = (PULONG_PTR) ((PSCSI_REQUEST_BLOCK) (srb + 1));
+ }
+
+ //
+ // Save the function code and the device object in the memory after
+ // the SRB.
+ //
+
+ *function = (ULONG_PTR) DeviceObject;
+ function++;
+ *function = (ULONG_PTR) controlCode;
+
+ } else {
+ srb = NULL;
+ }
+
+ //
+ // Change the device type to storage for the switch statement, but only
+ // if from a legacy device type
+ //
+
+ if (((controlCode & 0xffff0000) == (IOCTL_DISK_BASE << 16)) ||
+ ((controlCode & 0xffff0000) == (IOCTL_TAPE_BASE << 16)) ||
+ ((controlCode & 0xffff0000) == (IOCTL_CDROM_BASE << 16))
+ ) {
+
+ modifiedIoControlCode = (controlCode & ~0xffff0000);
+ modifiedIoControlCode |= (IOCTL_STORAGE_BASE << 16);
+
+ } else {
+
+ modifiedIoControlCode = controlCode;
+
+ }
+
+ TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_GENERAL, "> ioctl %xh (%s)", modifiedIoControlCode, DBGGETIOCTLSTR(modifiedIoControlCode)));
+
+
+ switch (modifiedIoControlCode) {
+
+ case IOCTL_STORAGE_GET_HOTPLUG_INFO: {
+
+ FREE_POOL(srb);
+
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
+ sizeof(STORAGE_HOTPLUG_INFO)) {
+
+ //
+ // Indicate unsuccessful status and no data transferred.
+ //
+
+ Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
+ Irp->IoStatus.Information = sizeof(STORAGE_HOTPLUG_INFO);
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ status = STATUS_BUFFER_TOO_SMALL;
+
+ } else if (!commonExtension->IsFdo) {
+
+
+ //
+ // Just forward this down and return
+ //
+
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+
+ } else {
+
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
+ PSTORAGE_HOTPLUG_INFO info;
+
+ fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)commonExtension;
+ info = Irp->AssociatedIrp.SystemBuffer;
+
+ *info = fdoExtension->PrivateFdoData->HotplugInfo;
+ Irp->IoStatus.Status = STATUS_SUCCESS;
+ Irp->IoStatus.Information = sizeof(STORAGE_HOTPLUG_INFO);
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ status = STATUS_SUCCESS;
+ }
+ break;
+ }
+
+ case IOCTL_STORAGE_SET_HOTPLUG_INFO: {
+
+ FREE_POOL(srb);
+
+ if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
+ sizeof(STORAGE_HOTPLUG_INFO)) {
+
+ //
+ // Indicate unsuccessful status and no data transferred.
+ //
+
+ Irp->IoStatus.Status = STATUS_INFO_LENGTH_MISMATCH;
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ status = STATUS_INFO_LENGTH_MISMATCH;
+ goto SetStatusAndReturn;
+
+ }
+
+ if (!commonExtension->IsFdo) {
+
+
+ //
+ // Just forward this down and return
+ //
+
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+
+ } else {
+
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)commonExtension;
+ PSTORAGE_HOTPLUG_INFO info = Irp->AssociatedIrp.SystemBuffer;
+
+ status = STATUS_SUCCESS;
+
+ if (info->Size != fdoExtension->PrivateFdoData->HotplugInfo.Size)
+ {
+ status = STATUS_INVALID_PARAMETER_1;
+ }
+
+ if (info->MediaRemovable != fdoExtension->PrivateFdoData->HotplugInfo.MediaRemovable)
+ {
+ status = STATUS_INVALID_PARAMETER_2;
+ }
+
+ if (info->MediaHotplug != fdoExtension->PrivateFdoData->HotplugInfo.MediaHotplug)
+ {
+ status = STATUS_INVALID_PARAMETER_3;
+ }
+
+ if (NT_SUCCESS(status))
+ {
+ if (info->WriteCacheEnableOverride != fdoExtension->PrivateFdoData->HotplugInfo.WriteCacheEnableOverride)
+ {
+ fdoExtension->PrivateFdoData->HotplugInfo.WriteCacheEnableOverride = info->WriteCacheEnableOverride;
+
+ //
+ // Store the user-defined override in the registry
+ //
+
+ ClassSetDeviceParameter(fdoExtension,
+ CLASSP_REG_SUBKEY_NAME,
+ CLASSP_REG_WRITE_CACHE_VALUE_NAME,
+ info->WriteCacheEnableOverride);
+ }
+
+ fdoExtension->PrivateFdoData->HotplugInfo.DeviceHotplug = info->DeviceHotplug;
+
+ //
+ // Store the user-defined override in the registry
+ //
+
+ ClassSetDeviceParameter(fdoExtension,
+ CLASSP_REG_SUBKEY_NAME,
+ CLASSP_REG_REMOVAL_POLICY_VALUE_NAME,
+ (info->DeviceHotplug) ? RemovalPolicyExpectSurpriseRemoval : RemovalPolicyExpectOrderlyRemoval);
+ }
+
+ Irp->IoStatus.Status = status;
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ }
+
+ break;
+ }
+
+ case IOCTL_STORAGE_CHECK_VERIFY:
+ case IOCTL_STORAGE_CHECK_VERIFY2: {
+
+ PIRP irp2 = NULL;
+ PIO_STACK_LOCATION newStack;
+
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = NULL;
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DeviceIoControl: Check verify\n"));
+
+ //
+ // If a buffer for a media change count was provided, make sure it's
+ // big enough to hold the result
+ //
+
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength) {
+
+ //
+ // If the buffer is too small to hold the media change count
+ // then return an error to the caller
+ //
+
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
+ sizeof(ULONG)) {
+
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL, "DeviceIoControl: media count "
+ "buffer too small\n"));
+
+ Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
+ Irp->IoStatus.Information = sizeof(ULONG);
+
+ FREE_POOL(srb);
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+
+ status = STATUS_BUFFER_TOO_SMALL;
+ goto SetStatusAndReturn;
+ }
+ }
+
+ if (!commonExtension->IsFdo) {
+
+
+ //
+ // If this is a PDO then we should just forward the request down
+ //
+ NT_ASSERT(!srb);
+
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+
+ status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+
+ goto SetStatusAndReturn;
+
+ } else {
+
+ fdoExtension = DeviceObject->DeviceExtension;
+
+ }
+
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength) {
+
+ //
+ // The caller has provided a valid buffer. Allocate an additional
+ // irp and stick the CheckVerify completion routine on it. We will
+ // then send this down to the port driver instead of the irp the
+ // caller sent in
+ //
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DeviceIoControl: Check verify wants "
+ "media count\n"));
+
+ //
+ // Allocate a new irp to send the TestUnitReady to the port driver
+ //
+
+ irp2 = IoAllocateIrp((CCHAR) (DeviceObject->StackSize + 3), FALSE);
+
+ if (irp2 == NULL) {
+ Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
+ Irp->IoStatus.Information = 0;
+ FREE_POOL(srb);
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ goto SetStatusAndReturn;
+
+ break;
+ }
+
+ //
+ // Make sure to acquire the lock for the new irp.
+ //
+
+ ClassAcquireRemoveLock(DeviceObject, irp2);
+
+ irp2->Tail.Overlay.Thread = Irp->Tail.Overlay.Thread;
+ IoSetNextIrpStackLocation(irp2);
+
+ //
+ // Set the top stack location and shove the master Irp into the
+ // top location
+ //
+
+ newStack = IoGetCurrentIrpStackLocation(irp2);
+ newStack->Parameters.Others.Argument1 = Irp;
+ newStack->DeviceObject = DeviceObject;
+
+ //
+ // Stick the check verify completion routine onto the stack
+ // and prepare the irp for the port driver
+ //
+
+ IoSetCompletionRoutine(irp2,
+ ClassCheckVerifyComplete,
+ NULL,
+ TRUE,
+ TRUE,
+ TRUE);
+
+ IoSetNextIrpStackLocation(irp2);
+ newStack = IoGetCurrentIrpStackLocation(irp2);
+ newStack->DeviceObject = DeviceObject;
+ newStack->MajorFunction = irpStack->MajorFunction;
+ newStack->MinorFunction = irpStack->MinorFunction;
+ newStack->Flags = irpStack->Flags;
+
+
+ //
+ // Mark the master irp as pending - whether the lower level
+ // driver completes it immediately or not this should allow it
+ // to go all the way back up.
+ //
+
+ IoMarkIrpPending(Irp);
+
+ Irp = irp2;
+
+ }
+
+ //
+ // Test Unit Ready
+ //
+
+ SrbSetCdbLength(srb, 6);
+ cdb = SrbGetCdb(srb);
+ cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY;
+
+ //
+ // Set timeout value.
+ //
+
+ SrbSetTimeOutValue(srb, fdoExtension->TimeOutValue);
+
+ //
+ // If this was a CV2 then mark the request as low-priority so we don't
+ // spin up the drive just to satisfy it.
+ //
+
+ if (controlCode == IOCTL_STORAGE_CHECK_VERIFY2) {
+ SrbSetSrbFlags(srb, SRB_CLASS_FLAGS_LOW_PRIORITY);
+ }
+
+ //
+ // Since this routine will always hand the request to the
+ // port driver if there isn't a data transfer to be done
+ // we don't have to worry about completing the request here
+ // on an error
+ //
+
+ //
+ // This routine uses a completion routine so we don't want to release
+ // the remove lock until then.
+ //
+
+ status = ClassSendSrbAsynchronous(DeviceObject,
+ srb,
+ Irp,
+ NULL,
+ 0,
+ FALSE);
+
+ break;
+ }
+
+ case IOCTL_STORAGE_MEDIA_REMOVAL:
+ case IOCTL_STORAGE_EJECTION_CONTROL: {
+
+ PPREVENT_MEDIA_REMOVAL mediaRemoval = Irp->AssociatedIrp.SystemBuffer;
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DiskIoControl: ejection control\n"));
+
+ FREE_POOL(srb);
+
+ if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
+ sizeof(PREVENT_MEDIA_REMOVAL)) {
+
+ //
+ // Indicate unsuccessful status and no data transferred.
+ //
+
+ Irp->IoStatus.Status = STATUS_INFO_LENGTH_MISMATCH;
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ status = STATUS_INFO_LENGTH_MISMATCH;
+ goto SetStatusAndReturn;
+ }
+
+ if (!commonExtension->IsFdo) {
+
+
+ //
+ // Just forward this down and return
+ //
+
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+ }
+ else {
+
+ // i don't believe this assertion is valid. this is a request
+ // from user-mode, so they could request this for any device
+ // they want? also, we handle it properly.
+ // NT_ASSERT(TEST_FLAG(DeviceObject->Characteristics, FILE_REMOVABLE_MEDIA));
+ status = ClasspEjectionControl(
+ DeviceObject,
+ Irp,
+ ((modifiedIoControlCode ==
+ IOCTL_STORAGE_EJECTION_CONTROL) ? SecureMediaLock :
+ SimpleMediaLock),
+ mediaRemoval->PreventMediaRemoval);
+
+ Irp->IoStatus.Status = status;
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ }
+
+ break;
+ }
+
+ case IOCTL_STORAGE_MCN_CONTROL: {
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DiskIoControl: MCN control\n"));
+
+ if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
+ sizeof(PREVENT_MEDIA_REMOVAL)) {
+
+ //
+ // Indicate unsuccessful status and no data transferred.
+ //
+
+ Irp->IoStatus.Status = STATUS_INFO_LENGTH_MISMATCH;
+ Irp->IoStatus.Information = 0;
+
+ FREE_POOL(srb);
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ status = STATUS_INFO_LENGTH_MISMATCH;
+ goto SetStatusAndReturn;
+ }
+
+ if (!commonExtension->IsFdo) {
+
+
+ //
+ // Just forward this down and return
+ //
+
+ FREE_POOL(srb);
+
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+
+ } else {
+
+ //
+ // Call to the FDO - handle the ejection control.
+ //
+
+ status = ClasspMcnControl(DeviceObject->DeviceExtension,
+ Irp,
+ srb);
+ }
+ goto SetStatusAndReturn;
+ }
+
+ case IOCTL_STORAGE_RESERVE:
+ case IOCTL_STORAGE_RELEASE: {
+
+ //
+ // Reserve logical unit.
+ //
+
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = NULL;
+
+ if (!commonExtension->IsFdo) {
+
+
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+ goto SetStatusAndReturn;
+
+ } else {
+ fdoExtension = DeviceObject->DeviceExtension;
+ }
+
+ if (TEST_FLAG(fdoExtension->PrivateFdoData->HackFlags, FDO_HACK_NO_RESERVE6))
+ {
+ SrbSetCdbLength(srb, 10);
+ cdb = SrbGetCdb(srb);
+ cdb->CDB10.OperationCode = (modifiedIoControlCode == IOCTL_STORAGE_RESERVE) ? SCSIOP_RESERVE_UNIT10 : SCSIOP_RELEASE_UNIT10;
+ }
+ else
+ {
+ SrbSetCdbLength(srb, 6);
+ cdb = SrbGetCdb(srb);
+ cdb->CDB6GENERIC.OperationCode = (modifiedIoControlCode == IOCTL_STORAGE_RESERVE) ? SCSIOP_RESERVE_UNIT : SCSIOP_RELEASE_UNIT;
+ }
+
+ //
+ // Set timeout value.
+ //
+
+ SrbSetTimeOutValue(srb, fdoExtension->TimeOutValue);
+
+ //
+ // Send reserves as tagged requests.
+ //
+
+ if ( IOCTL_STORAGE_RESERVE == modifiedIoControlCode ) {
+ SrbSetSrbFlags(srb, SRB_FLAGS_QUEUE_ACTION_ENABLE);
+ SrbSetRequestAttribute(srb, SRB_SIMPLE_TAG_REQUEST);
+ }
+
+ status = ClassSendSrbAsynchronous(DeviceObject,
+ srb,
+ Irp,
+ NULL,
+ 0,
+ FALSE);
+
+ break;
+ }
+
+ case IOCTL_STORAGE_PERSISTENT_RESERVE_IN:
+ case IOCTL_STORAGE_PERSISTENT_RESERVE_OUT: {
+
+ if (!commonExtension->IsFdo) {
+
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+ goto SetStatusAndReturn;
+ }
+
+ //
+ // Process Persistent Reserve
+ //
+
+ status = ClasspPersistentReserve(DeviceObject, Irp, srb);
+
+ break;
+
+ }
+
+ case IOCTL_STORAGE_EJECT_MEDIA:
+ case IOCTL_STORAGE_LOAD_MEDIA:
+ case IOCTL_STORAGE_LOAD_MEDIA2:{
+
+ //
+ // Eject media.
+ //
+
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = NULL;
+
+ if (!commonExtension->IsFdo) {
+
+
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+
+ status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+ goto SetStatusAndReturn;
+ } else {
+ fdoExtension = DeviceObject->DeviceExtension;
+ }
+
+ if (commonExtension->PagingPathCount != 0) {
+
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL, "ClassDeviceControl: call to eject paging device - "
+ "failure\n"));
+
+ status = STATUS_FILES_OPEN;
+ Irp->IoStatus.Status = status;
+
+ Irp->IoStatus.Information = 0;
+
+ FREE_POOL(srb);
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ goto SetStatusAndReturn;
+ }
+
+ //
+ // Synchronize with ejection control and ejection cleanup code as
+ // well as other eject/load requests.
+ //
+
+ KeEnterCriticalRegion();
+ (VOID)KeWaitForSingleObject(&(fdoExtension->EjectSynchronizationEvent),
+ UserRequest,
+ KernelMode,
+ FALSE,
+ NULL);
+
+ if (fdoExtension->ProtectedLockCount != 0) {
+
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL, "ClassDeviceControl: call to eject protected locked "
+ "device - failure\n"));
+
+ status = STATUS_DEVICE_BUSY;
+ Irp->IoStatus.Status = status;
+ Irp->IoStatus.Information = 0;
+
+ FREE_POOL(srb);
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+
+ KeSetEvent(&fdoExtension->EjectSynchronizationEvent,
+ IO_NO_INCREMENT,
+ FALSE);
+ KeLeaveCriticalRegion();
+
+ goto SetStatusAndReturn;
+ }
+
+ SrbSetCdbLength(srb, 6);
+ cdb = SrbGetCdb(srb);
+
+ cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;
+ cdb->START_STOP.LoadEject = 1;
+
+ if (modifiedIoControlCode == IOCTL_STORAGE_EJECT_MEDIA) {
+ cdb->START_STOP.Start = 0;
+ } else {
+ cdb->START_STOP.Start = 1;
+ }
+
+ //
+ // Set timeout value.
+ //
+
+ SrbSetTimeOutValue(srb, fdoExtension->TimeOutValue);
+ status = ClassSendSrbAsynchronous(DeviceObject,
+ srb,
+ Irp,
+ NULL,
+ 0,
+ FALSE);
+
+ KeSetEvent(&fdoExtension->EjectSynchronizationEvent, IO_NO_INCREMENT, FALSE);
+ KeLeaveCriticalRegion();
+
+ break;
+ }
+
+ case IOCTL_STORAGE_FIND_NEW_DEVICES: {
+
+ FREE_POOL(srb);
+
+ if (commonExtension->IsFdo) {
+
+ IoInvalidateDeviceRelations(
+ ((PFUNCTIONAL_DEVICE_EXTENSION) commonExtension)->LowerPdo,
+ BusRelations);
+
+ status = STATUS_SUCCESS;
+ Irp->IoStatus.Status = status;
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ }
+ else {
+
+
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+ }
+ break;
+ }
+
+ case IOCTL_STORAGE_GET_DEVICE_NUMBER: {
+
+ FREE_POOL(srb);
+
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength >=
+ sizeof(STORAGE_DEVICE_NUMBER)) {
+
+ PSTORAGE_DEVICE_NUMBER deviceNumber =
+ Irp->AssociatedIrp.SystemBuffer;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension =
+ commonExtension->PartitionZeroExtension;
+
+ deviceNumber->DeviceType = fdoExtension->CommonExtension.DeviceObject->DeviceType;
+ deviceNumber->DeviceNumber = fdoExtension->DeviceNumber;
+ deviceNumber->PartitionNumber = commonExtension->PartitionNumber;
+
+ status = STATUS_SUCCESS;
+ Irp->IoStatus.Information = sizeof(STORAGE_DEVICE_NUMBER);
+
+ } else {
+ status = STATUS_BUFFER_TOO_SMALL;
+ Irp->IoStatus.Information = sizeof(STORAGE_DEVICE_NUMBER);
+ }
+
+ Irp->IoStatus.Status = status;
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+
+ break;
+ }
+
+
+ case IOCTL_STORAGE_READ_CAPACITY: {
+
+ FREE_POOL(srb);
+
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
+ sizeof(STORAGE_READ_CAPACITY)) {
+
+ //
+ // Indicate unsuccessful status and no data transferred.
+ //
+
+ Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
+ Irp->IoStatus.Information = sizeof(STORAGE_READ_CAPACITY);
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ status = STATUS_BUFFER_TOO_SMALL;
+ break;
+ }
+
+ if (!commonExtension->IsFdo) {
+
+
+ //
+ // Just forward this down and return
+ //
+
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+ }
+ else {
+
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt = DeviceObject->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
+ PSTORAGE_READ_CAPACITY readCapacity = Irp->AssociatedIrp.SystemBuffer;
+ LARGE_INTEGER diskLength;
+
+ status = ClassReadDriveCapacity(DeviceObject);
+ if (NT_SUCCESS(status) && fdoData->IsCachedDriveCapDataValid) {
+
+ readCapacity->Version = sizeof(STORAGE_READ_CAPACITY);
+ readCapacity->Size = sizeof(STORAGE_READ_CAPACITY);
+
+ REVERSE_BYTES(&readCapacity->BlockLength,
+ &fdoData->LastKnownDriveCapacityData.BytesPerBlock);
+ REVERSE_BYTES_QUAD(&readCapacity->NumberOfBlocks,
+ &fdoData->LastKnownDriveCapacityData.LogicalBlockAddress);
+ readCapacity->NumberOfBlocks.QuadPart++;
+
+ readCapacity->DiskLength = fdoExt->CommonExtension.PartitionLength;
+
+ //
+ // Make sure the lengths are equal.
+ // Remove this after testing.
+ //
+ diskLength.QuadPart = readCapacity->NumberOfBlocks.QuadPart *
+ readCapacity->BlockLength;
+
+ Irp->IoStatus.Status = STATUS_SUCCESS;
+ Irp->IoStatus.Information = sizeof(STORAGE_READ_CAPACITY);
+
+ } else {
+ //
+ // Read capacity request failed.
+ //
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL, "ClassDeviceControl: ClassReadDriveCapacity failed: 0x%X IsCachedDriveCapDataValid: %d\n",
+ status, fdoData->IsCachedDriveCapDataValid));
+ Irp->IoStatus.Status = status;
+ Irp->IoStatus.Information = 0;
+ }
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ }
+
+ break;
+ }
+
+ case IOCTL_STORAGE_QUERY_PROPERTY: {
+
+ PSTORAGE_PROPERTY_QUERY query = Irp->AssociatedIrp.SystemBuffer;
+
+ if (irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(STORAGE_PROPERTY_QUERY)) {
+
+ status = STATUS_INFO_LENGTH_MISMATCH;
+ Irp->IoStatus.Status = status;
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ FREE_POOL(srb);
+ break;
+ }
+
+ if (!commonExtension->IsFdo) {
+
+
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+ FREE_POOL(srb);
+ break;
+ }
+
+ //
+ // Determine PropertyId type and either call appropriate routine
+ // or pass request to lower drivers.
+ //
+
+ switch ( query->PropertyId ) {
+
+ case StorageDeviceUniqueIdProperty: {
+
+ status = ClasspDuidQueryProperty(DeviceObject, Irp);
+ break;
+ }
+
+ case StorageDeviceWriteCacheProperty: {
+
+ status = ClasspWriteCacheProperty(DeviceObject, Irp, srb);
+ break;
+ }
+
+ // these propertyId has been implemented in some port driver and filter drivers.
+ // to keep the backwards compatibility, classpnp will send the request down if it's supported by lower layer.
+ // otherwise, classpnp sends SCSI command and then interprets the result.
+ case StorageAccessAlignmentProperty: {
+
+ status = ClasspAccessAlignmentProperty(DeviceObject, Irp, srb);
+ break;
+ }
+
+ case StorageDeviceSeekPenaltyProperty: {
+
+ status = ClasspDeviceSeekPenaltyProperty(DeviceObject, Irp, srb);
+ break;
+ }
+
+ case StorageDeviceTrimProperty: {
+
+ status = ClasspDeviceTrimProperty(DeviceObject, Irp, srb);
+ break;
+ }
+
+ case StorageDeviceLBProvisioningProperty: {
+
+ status = ClasspDeviceLBProvisioningProperty(DeviceObject, Irp, srb);
+ break;
+ }
+
+ case StorageDeviceCopyOffloadProperty: {
+
+ status = ClasspDeviceCopyOffloadProperty(DeviceObject, Irp, srb);
+ break;
+ }
+
+ case StorageDeviceMediumProductType: {
+
+ status = ClasspDeviceMediaTypeProperty(DeviceObject, Irp, srb);
+ break;
+ }
+
+ default: {
+
+ //
+ // Copy the Irp stack parameters to the next stack location.
+ //
+
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+ break;
+ }
+ } // end switch
+
+ FREE_POOL(srb);
+ break;
+ }
+
+ case IOCTL_STORAGE_CHECK_PRIORITY_HINT_SUPPORT: {
+
+ FREE_POOL(srb);
+
+ if (!commonExtension->IsFdo) {
+
+
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+ break;
+ }
+
+ //
+ // Process priority hit request
+ //
+
+ status = ClasspPriorityHint(DeviceObject, Irp);
+ break;
+ }
+
+ case IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES: {
+
+ PDEVICE_MANAGE_DATA_SET_ATTRIBUTES dsmAttributes = Irp->AssociatedIrp.SystemBuffer;
+
+ if (irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES)) {
+
+ status = STATUS_INVALID_PARAMETER;
+ Irp->IoStatus.Status = status;
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ FREE_POOL(srb);
+ break;
+ }
+
+ if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
+ (sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES) + dsmAttributes->ParameterBlockLength + dsmAttributes->DataSetRangesLength)) {
+
+ status = STATUS_INVALID_PARAMETER;
+ Irp->IoStatus.Status = status;
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ FREE_POOL(srb);
+ break;
+ }
+
+ if (!commonExtension->IsFdo) {
+
+
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+ FREE_POOL(srb);
+ break;
+ }
+
+ switch(dsmAttributes->Action) {
+
+ // only process Trim action in class layer if possible.
+ case DeviceDsmAction_Trim: {
+ status = ClasspDeviceTrimProcess(DeviceObject, Irp, srb);
+ break;
+ }
+
+ case DeviceDsmAction_OffloadRead: {
+ status = ClassDeviceProcessOffloadRead(DeviceObject, Irp, srb);
+ break;
+ }
+
+ case DeviceDsmAction_OffloadWrite: {
+ status = ClassDeviceProcessOffloadWrite(DeviceObject, Irp, srb);
+ break;
+ }
+
+ case DeviceDsmAction_Allocation: {
+ status = ClasspDeviceGetLBAStatus(DeviceObject, Irp, srb);
+ break;
+ }
+
+
+ default: {
+
+
+ //
+ // Copy the Irp stack parameters to the next stack location.
+ //
+
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+ break;
+ }
+ } // end switch
+
+ FREE_POOL(srb);
+ break;
+ }
+
+ case IOCTL_STORAGE_GET_LB_PROVISIONING_MAP_RESOURCES: {
+
+ if (commonExtension->IsFdo) {
+
+ status = ClassDeviceGetLBProvisioningResources(DeviceObject, Irp, srb);
+
+ } else {
+
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+ }
+
+ FREE_POOL(srb);
+
+ break;
+ }
+
+ case IOCTL_STORAGE_EVENT_NOTIFICATION: {
+
+ FREE_POOL(srb);
+
+ status = ClasspStorageEventNotification(DeviceObject, Irp);
+ break;
+ }
+
+#if (NTDDI_VERSION >= NTDDI_WINTRHESHOLD)
+ case IOCTL_STORAGE_FIRMWARE_GET_INFO: {
+ FREE_POOL(srb);
+
+ status = ClassDeviceHwFirmwareGetInfoProcess(DeviceObject, Irp);
+ break;
+ }
+
+ case IOCTL_STORAGE_FIRMWARE_DOWNLOAD: {
+ if (!commonExtension->IsFdo) {
+
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+ goto SetStatusAndReturn;
+ }
+
+ status = ClassDeviceHwFirmwareDownloadProcess(DeviceObject, Irp, srb);
+ break;
+ }
+
+ case IOCTL_STORAGE_FIRMWARE_ACTIVATE: {
+ if (!commonExtension->IsFdo) {
+
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+ goto SetStatusAndReturn;
+ }
+
+ status = ClassDeviceHwFirmwareActivateProcess(DeviceObject, Irp, srb);
+ break;
+ }
+#endif
+
+
+ default: {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL, "IoDeviceControl: Unsupported device IOCTL %x for %p\n",
+ controlCode, DeviceObject));
+
+
+ //
+ // Pass the device control to the next driver.
+ //
+
+ FREE_POOL(srb);
+
+ //
+ // Copy the Irp stack parameters to the next stack location.
+ //
+
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+ break;
+ }
+
+ } // end switch( ...
+
+SetStatusAndReturn:
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "< ioctl %xh (%s): status %xh.", modifiedIoControlCode, DBGGETIOCTLSTR(modifiedIoControlCode), status));
+
+ return status;
+} // end ClassDeviceControl()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassShutdownFlush()
+
+Routine Description:
+
+ This routine is called for a shutdown and flush IRPs. These are sent by the
+ system before it actually shuts down or when the file system does a flush.
+ If it exists, the device-specific driver's routine will be invoked. If there
+ wasn't one specified, the Irp will be completed with an Invalid device request.
+
+Arguments:
+
+ DriverObject - Pointer to device object to being shutdown by system.
+
+ Irp - IRP involved.
+
+Return Value:
+
+ NT Status
+
+--*/
+NTSTATUS
+ClassShutdownFlush(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+
+ ULONG isRemoved;
+
+ isRemoved = ClassAcquireRemoveLock(DeviceObject, Irp);
+ _Analysis_assume_(isRemoved);
+ if(isRemoved) {
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+
+ Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
+
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+
+ return STATUS_DEVICE_DOES_NOT_EXIST;
+ }
+
+ if (commonExtension->DevInfo->ClassShutdownFlush) {
+
+ //
+ // Call the device-specific driver's routine.
+ //
+
+ return commonExtension->DevInfo->ClassShutdownFlush(DeviceObject, Irp);
+ }
+
+ //
+ // Device-specific driver doesn't support this.
+ //
+
+ Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+
+ return STATUS_INVALID_DEVICE_REQUEST;
+} // end ClassShutdownFlush()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClasspIsPortable()
+
+Routine Description:
+
+ This routine is called during start device to determine whether the PDO
+ for a stack reports itself as portable.
+
+Arguments:
+
+ FdoExtension - Pointer to FDO whose PDO we check for portability.
+
+ IsPortable - Boolean pointer in which to store result.
+
+Return Value:
+
+ NT Status
+
+--*/
+NTSTATUS
+ClasspIsPortable(
+ _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ _Out_ PBOOLEAN IsPortable
+ )
+{
+ DEVPROP_BOOLEAN isInternal = DEVPROP_FALSE;
+ BOOLEAN isPortable = FALSE;
+ ULONG size = 0;
+ NTSTATUS status = STATUS_SUCCESS;
+ DEVPROPTYPE type = DEVPROP_TYPE_EMPTY;
+
+ PAGED_CODE();
+
+ *IsPortable = FALSE;
+
+ //
+ // Check to see if the underlying device
+ // object is in local machine container
+ //
+
+ status = IoGetDevicePropertyData(FdoExtension->LowerPdo,
+ &DEVPKEY_Device_InLocalMachineContainer,
+ 0,
+ 0,
+ sizeof(isInternal),
+ &isInternal,
+ &size,
+ &type);
+
+ if (!NT_SUCCESS(status)) {
+ goto cleanup;
+ }
+
+ NT_ASSERT(size == sizeof(isInternal));
+ NT_ASSERT(type == DEVPROP_TYPE_BOOLEAN);
+
+ //
+ // Volume is hot-pluggable if the disk pdo
+ // container id differs from that of root device
+ //
+
+ if (isInternal == DEVPROP_TRUE) {
+ goto cleanup;
+ }
+
+ isPortable = TRUE;
+
+ //
+ // Examine the bus type to ensure
+ // that this really is a fixed disk
+ //
+
+ if (FdoExtension->DeviceDescriptor->BusType == BusTypeFibre ||
+ FdoExtension->DeviceDescriptor->BusType == BusTypeiScsi ||
+ FdoExtension->DeviceDescriptor->BusType == BusTypeRAID) {
+
+ isPortable = FALSE;
+ }
+
+ *IsPortable = isPortable;
+
+cleanup:
+
+ return status;
+}
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassCreateDeviceObject()
+
+Routine Description:
+
+ This routine creates an object for the physical device specified and
+ sets up the deviceExtension's function pointers for each entry point
+ in the device-specific driver.
+
+Arguments:
+
+ DriverObject - Pointer to driver object created by system.
+
+ ObjectNameBuffer - Dir. name of the object to create.
+
+ LowerDeviceObject - Pointer to the lower device object
+
+ IsFdo - should this be an fdo or a pdo
+
+ DeviceObject - Pointer to the device object pointer we will return.
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+_Must_inspect_result_
+_Post_satisfies_(return <= 0)
+SCSIPORT_API
+NTSTATUS
+ClassCreateDeviceObject(
+ _In_ PDRIVER_OBJECT DriverObject,
+ _In_z_ PCCHAR ObjectNameBuffer,
+ _In_ PDEVICE_OBJECT LowerDevice,
+ _In_ BOOLEAN IsFdo,
+ _Outptr_result_nullonfailure_
+ _At_(*DeviceObject, __drv_allocatesMem(Mem) __drv_aliasesMem)
+ PDEVICE_OBJECT *DeviceObject
+ )
+{
+ BOOLEAN isPartitionable;
+ STRING ntNameString;
+ UNICODE_STRING ntUnicodeString;
+ NTSTATUS status;
+ PDEVICE_OBJECT deviceObject = NULL;
+
+ ULONG characteristics;
+ SIZE_T rundownSize = ExSizeOfRundownProtectionCacheAware();
+ PCHAR rundownAddr = NULL;
+ ULONG devExtSize;
+
+#pragma warning(suppress:4054) // okay to type cast function pointer as data pointer for this use case
+ PCLASS_DRIVER_EXTENSION driverExtension = IoGetDriverObjectExtension(DriverObject, CLASS_DRIVER_EXTENSION_KEY);
+
+ PCLASS_DEV_INFO devInfo;
+
+ PAGED_CODE();
+
+ _Analysis_assume_(driverExtension != NULL);
+
+ *DeviceObject = NULL;
+ RtlInitUnicodeString(&ntUnicodeString, NULL);
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT, "ClassCreateFdo: Create device object\n"));
+
+ NT_ASSERT(LowerDevice);
+
+ //
+ // Make sure that if we're making PDO's we have an enumeration routine
+ //
+
+ isPartitionable = (driverExtension->InitData.ClassEnumerateDevice != NULL);
+
+ NT_ASSERT(IsFdo || isPartitionable);
+
+ //
+ // Grab the correct dev-info structure out of the init data
+ //
+
+ if (IsFdo) {
+ devInfo = &(driverExtension->InitData.FdoData);
+ } else {
+ devInfo = &(driverExtension->InitData.PdoData);
+ }
+
+ characteristics = devInfo->DeviceCharacteristics;
+
+ if (ARGUMENT_PRESENT(ObjectNameBuffer)) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT, "ClassCreateFdo: Name is %s\n", ObjectNameBuffer));
+
+ RtlInitString(&ntNameString, ObjectNameBuffer);
+
+ status = RtlAnsiStringToUnicodeString(&ntUnicodeString, &ntNameString, TRUE);
+
+ if (!NT_SUCCESS(status)) {
+
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
+ "ClassCreateFdo: Cannot convert string %s\n",
+ ObjectNameBuffer));
+
+ ntUnicodeString.Buffer = NULL;
+ return status;
+ }
+ } else {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT, "ClassCreateFdo: Object will be unnamed\n"));
+
+ if (IsFdo == FALSE) {
+
+ //
+ // PDO's have to have some sort of name.
+ //
+
+ SET_FLAG(characteristics, FILE_AUTOGENERATED_DEVICE_NAME);
+ }
+
+ RtlInitUnicodeString(&ntUnicodeString, NULL);
+ }
+
+ devExtSize = devInfo->DeviceExtensionSize +
+ (ULONG)sizeof(CLASS_PRIVATE_COMMON_DATA) + (ULONG)rundownSize;
+ status = IoCreateDevice(DriverObject,
+ devExtSize,
+ &ntUnicodeString,
+ devInfo->DeviceType,
+ characteristics,
+ FALSE,
+ &deviceObject);
+
+ if (!NT_SUCCESS(status)) {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT, "ClassCreateFdo: Can not create device object %lx\n",
+ status));
+ NT_ASSERT(deviceObject == NULL);
+
+ //
+ // buffer is not used any longer here.
+ //
+
+ if (ntUnicodeString.Buffer != NULL) {
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT, "ClassCreateFdo: Freeing unicode name buffer\n"));
+ FREE_POOL(ntUnicodeString.Buffer);
+ RtlInitUnicodeString(&ntUnicodeString, NULL);
+ }
+
+ } else {
+
+ PCOMMON_DEVICE_EXTENSION commonExtension = deviceObject->DeviceExtension;
+
+ RtlZeroMemory(
+ deviceObject->DeviceExtension,
+ devExtSize);
+
+ //
+ // Setup version code
+ //
+
+ commonExtension->Version = 0x03;
+
+ //
+ // Setup the remove lock and event
+ //
+
+ commonExtension->IsRemoved = NO_REMOVE;
+
+#if DBG
+
+ commonExtension->RemoveLock = 0;
+
+#endif
+
+ KeInitializeEvent(&commonExtension->RemoveEvent,
+ SynchronizationEvent,
+ FALSE);
+
+
+ ClasspInitializeRemoveTracking(deviceObject);
+
+ //
+ // Initialize the PrivateCommonData
+ //
+
+ commonExtension->PrivateCommonData = (PCLASS_PRIVATE_COMMON_DATA)
+ ((PCHAR)deviceObject->DeviceExtension + devInfo->DeviceExtensionSize);
+ rundownAddr = (PCHAR)commonExtension->PrivateCommonData + sizeof(CLASS_PRIVATE_COMMON_DATA);
+ ExInitializeRundownProtectionCacheAware((PEX_RUNDOWN_REF_CACHE_AWARE)rundownAddr, rundownSize);
+ commonExtension->PrivateCommonData->RemoveLockFailAcquire = 0;
+
+ //
+ // Acquire the lock once. This reference will be released when the
+ // remove IRP has been received.
+ //
+
+ ClassAcquireRemoveLock(deviceObject, (PIRP) deviceObject);
+
+ //
+ // Store a pointer to the driver extension so we don't have to do
+ // lookups to get it.
+ //
+
+ commonExtension->DriverExtension = driverExtension;
+
+ //
+ // Fill in entry points
+ //
+
+ commonExtension->DevInfo = devInfo;
+
+ //
+ // Initialize some of the common values in the structure
+ //
+
+ commonExtension->DeviceObject = deviceObject;
+
+ commonExtension->LowerDeviceObject = NULL;
+
+ commonExtension->DispatchTable = driverExtension->DeviceMajorFunctionTable;
+
+ if(IsFdo) {
+
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PVOID) commonExtension;
+
+ commonExtension->PartitionZeroExtension = deviceObject->DeviceExtension;
+
+ //
+ // Set the initial device object flags.
+ //
+
+ SET_FLAG(deviceObject->Flags, DO_POWER_PAGABLE);
+ //
+ // Clear the PDO list
+ //
+
+ commonExtension->ChildList = NULL;
+
+ commonExtension->DriverData =
+ ((PFUNCTIONAL_DEVICE_EXTENSION) deviceObject->DeviceExtension + 1);
+
+ //
+ // The disk class driver creates only FDO. The partition number
+ // for the FDO must be 0.
+ //
+
+ if ((isPartitionable == TRUE) ||
+ (devInfo->DeviceType == FILE_DEVICE_DISK)) {
+
+ commonExtension->PartitionNumber = 0;
+ } else {
+ commonExtension->PartitionNumber = (ULONG) (-1L);
+ }
+
+ fdoExtension->DevicePowerState = PowerDeviceD0;
+
+ KeInitializeEvent(&fdoExtension->EjectSynchronizationEvent,
+ SynchronizationEvent,
+ TRUE);
+
+ KeInitializeEvent(&fdoExtension->ChildLock,
+ SynchronizationEvent,
+ TRUE);
+
+ status = ClasspAllocateReleaseRequest(deviceObject);
+
+ if(!NT_SUCCESS(status)) {
+ IoDeleteDevice(deviceObject);
+ *DeviceObject = NULL;
+
+ if (ntUnicodeString.Buffer != NULL) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT, "ClassCreateFdo: Freeing unicode name buffer\n"));
+ FREE_POOL(ntUnicodeString.Buffer);
+ RtlInitUnicodeString(&ntUnicodeString, NULL);
+ }
+
+ return status;
+ }
+
+ } else {
+
+ PPHYSICAL_DEVICE_EXTENSION pdoExtension =
+ deviceObject->DeviceExtension;
+
+ PFUNCTIONAL_DEVICE_EXTENSION p0Extension =
+ LowerDevice->DeviceExtension;
+
+ SET_FLAG(deviceObject->Flags, DO_POWER_PAGABLE);
+
+ commonExtension->PartitionZeroExtension = p0Extension;
+
+ //
+ // Stick this onto the PDO list
+ //
+
+ ClassAddChild(p0Extension, pdoExtension, TRUE);
+
+ commonExtension->DriverData = (PVOID) (pdoExtension + 1);
+
+ //
+ // Get the top of stack for the lower device - this allows
+ // filters to get stuck in between the partitions and the
+ // physical disk.
+ //
+
+ commonExtension->LowerDeviceObject =
+ IoGetAttachedDeviceReference(LowerDevice);
+
+ //
+ // Pnp will keep a reference to the lower device object long
+ // after this partition has been deleted. Dereference now so
+ // we don't have to deal with it later.
+ //
+
+ ObDereferenceObject(commonExtension->LowerDeviceObject);
+ }
+
+ KeInitializeEvent(&commonExtension->PathCountEvent, SynchronizationEvent, TRUE);
+
+ commonExtension->IsFdo = IsFdo;
+
+ commonExtension->DeviceName = ntUnicodeString;
+
+ commonExtension->PreviousState = 0xff;
+
+ InitializeDictionary(&(commonExtension->FileObjectDictionary));
+
+ commonExtension->CurrentState = IRP_MN_STOP_DEVICE;
+
+ if (commonExtension->DriverExtension->InitData.ClassStartIo) {
+ IoSetStartIoAttributes(deviceObject, TRUE, TRUE);
+ }
+ }
+
+ *DeviceObject = deviceObject;
+
+ return status;
+} // end ClassCreateDeviceObject()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassClaimDevice()
+
+Routine Description:
+
+ This function claims a device in the port driver. The port driver object
+ is updated with the correct driver object if the device is successfully
+ claimed.
+
+Arguments:
+
+ LowerDeviceObject - Supplies the base port device object.
+
+ Release - Indicates the logical unit should be released rather than claimed.
+
+Return Value:
+
+ Returns a status indicating success or failure of the operation.
+
+--*/
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+ClassClaimDevice(
+ _In_ PDEVICE_OBJECT LowerDeviceObject,
+ _In_ BOOLEAN Release
+ )
+{
+ IO_STATUS_BLOCK ioStatus;
+ PIRP irp;
+ PIO_STACK_LOCATION irpStack;
+ KEVENT event;
+ NTSTATUS status;
+ SCSI_REQUEST_BLOCK srb = {0};
+
+ PAGED_CODE();
+
+ //
+ // WORK ITEM - MPIO related. Need to think about how to handle.
+ //
+
+ srb.Length = sizeof(SCSI_REQUEST_BLOCK);
+
+ srb.Function = Release ? SRB_FUNCTION_RELEASE_DEVICE :
+ SRB_FUNCTION_CLAIM_DEVICE;
+
+ //
+ // Set the event object to the unsignaled state.
+ // It will be used to signal request completion
+ //
+
+ KeInitializeEvent(&event, SynchronizationEvent, FALSE);
+
+ //
+ // Build synchronous request with no transfer.
+ //
+
+ irp = IoBuildDeviceIoControlRequest(IOCTL_SCSI_EXECUTE_NONE,
+ LowerDeviceObject,
+ NULL,
+ 0,
+ NULL,
+ 0,
+ TRUE,
+ &event,
+ &ioStatus);
+
+ if (irp == NULL) {
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT, "ClassClaimDevice: Can't allocate Irp\n"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ irpStack = IoGetNextIrpStackLocation(irp);
+
+ //
+ // Save SRB address in next stack for port driver.
+ //
+
+ irpStack->Parameters.Scsi.Srb = &srb;
+
+ //
+ // Set up IRP Address.
+ //
+
+ srb.OriginalRequest = irp;
+
+ //
+ // Call the port driver with the request and wait for it to complete.
+ //
+
+ status = IoCallDriver(LowerDeviceObject, irp);
+ if (status == STATUS_PENDING) {
+
+ (VOID)KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
+ status = ioStatus.Status;
+ }
+
+ //
+ // If this is a release request, then just decrement the reference count
+ // and return. The status does not matter.
+ //
+
+ if (Release) {
+
+ // ObDereferenceObject(LowerDeviceObject);
+ return STATUS_SUCCESS;
+ }
+
+ if (!NT_SUCCESS(status)) {
+ return status;
+ }
+
+ NT_ASSERT(srb.DataBuffer != NULL);
+ NT_ASSERT(!TEST_FLAG(srb.SrbFlags, SRB_FLAGS_FREE_SENSE_BUFFER));
+
+ return status;
+} // end ClassClaimDevice()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassInternalIoControl()
+
+Routine Description:
+
+ This routine passes internal device controls to the port driver.
+ Internal device controls are used by higher level drivers both for ioctls
+ and to pass through scsi requests.
+
+ If the IoControlCode does not match any of the handled ioctls and is
+ a valid system address then the request will be treated as an SRB and
+ passed down to the lower driver. If the IoControlCode is not a valid
+ system address the ioctl will be failed.
+
+ Callers must therefore be extremely cautious to pass correct, initialized
+ values to this function.
+
+Arguments:
+
+ DeviceObject - Supplies a pointer to the device object for this request.
+
+ Irp - Supplies the Irp making the request.
+
+Return Value:
+
+ Returns back a STATUS_PENDING or a completion status.
+
+--*/
+NTSTATUS
+ClassInternalIoControl(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
+ PIO_STACK_LOCATION nextStack = IoGetNextIrpStackLocation(Irp);
+
+ ULONG isRemoved;
+
+ PSCSI_REQUEST_BLOCK srb;
+
+ isRemoved = ClassAcquireRemoveLock(DeviceObject, Irp);
+ _Analysis_assume_(isRemoved);
+ if(isRemoved) {
+
+ Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+
+ return STATUS_DEVICE_DOES_NOT_EXIST;
+ }
+
+ //
+ // Get a pointer to the SRB.
+ //
+
+ srb = irpStack->Parameters.Scsi.Srb;
+
+ //
+ // Set the parameters in the next stack location.
+ //
+
+ if(commonExtension->IsFdo) {
+ nextStack->Parameters.Scsi.Srb = srb;
+ nextStack->MajorFunction = IRP_MJ_SCSI;
+ nextStack->MinorFunction = IRP_MN_SCSI_CLASS;
+
+ } else {
+
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+ }
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+
+ return IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+} // end ClassInternalIoControl()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassQueryTimeOutRegistryValue()
+
+Routine Description:
+
+ This routine determines whether a reg key for a user-specified timeout
+ value exists. This should be called at initialization time.
+
+Arguments:
+
+ DeviceObject - Pointer to the device object we are retrieving the timeout
+ value for
+
+Return Value:
+
+ None, but it sets a new default timeout for a class of devices.
+
+--*/
+_IRQL_requires_max_(PASSIVE_LEVEL)
+ULONG
+ClassQueryTimeOutRegistryValue(
+ _In_ PDEVICE_OBJECT DeviceObject
+ )
+{
+ //
+ // Find the appropriate reg. key
+ //
+
+#pragma warning(suppress:4054) // okay to type cast function pointer as data pointer for this use case
+ PCLASS_DRIVER_EXTENSION driverExtension = IoGetDriverObjectExtension(DeviceObject->DriverObject, CLASS_DRIVER_EXTENSION_KEY);
+
+ PUNICODE_STRING registryPath = &(driverExtension->RegistryPath);
+
+ PRTL_QUERY_REGISTRY_TABLE parameters = NULL;
+ PWSTR path;
+ NTSTATUS status;
+ LONG timeOut = 0;
+ ULONG zero = 0;
+ ULONG size;
+
+ PAGED_CODE();
+
+ if (!registryPath) {
+ return 0;
+ }
+
+ parameters = ExAllocatePoolWithTag(NonPagedPoolNx,
+ sizeof(RTL_QUERY_REGISTRY_TABLE)*2,
+ '1BcS');
+
+ if (!parameters) {
+ return 0;
+ }
+
+ size = registryPath->MaximumLength + sizeof(WCHAR);
+ path = ExAllocatePoolWithTag(NonPagedPoolNx, size, '2BcS');
+
+ if (!path) {
+ FREE_POOL(parameters);
+ return 0;
+ }
+
+ RtlZeroMemory(path,size);
+ RtlCopyMemory(path, registryPath->Buffer, size - sizeof(WCHAR));
+
+
+ //
+ // Check for the Timeout value.
+ //
+
+ RtlZeroMemory(parameters,
+ (sizeof(RTL_QUERY_REGISTRY_TABLE)*2));
+
+ parameters[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
+ parameters[0].Name = L"TimeOutValue";
+ parameters[0].EntryContext = &timeOut;
+ parameters[0].DefaultType = REG_DWORD;
+ parameters[0].DefaultData = &zero;
+ parameters[0].DefaultLength = sizeof(ULONG);
+
+ status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL,
+ path,
+ parameters,
+ NULL,
+ NULL);
+
+ if (!(NT_SUCCESS(status))) {
+ timeOut = 0;
+ }
+
+ FREE_POOL(parameters);
+ FREE_POOL(path);
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
+ "ClassQueryTimeOutRegistryValue: Timeout value %d\n",
+ timeOut));
+
+
+ return timeOut;
+
+} // end ClassQueryTimeOutRegistryValue()
+
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassCheckVerifyComplete() ISSUE-2000/02/18-henrygab - why public?!
+
+Routine Description:
+
+ This routine executes when the port driver has completed a check verify
+ ioctl. It will set the status of the master Irp, copy the media change
+ count and complete the request.
+
+Arguments:
+
+ Fdo - Supplies the functional device object which represents the logical unit.
+
+ Irp - Supplies the Irp which has completed.
+
+ Context - NULL
+
+Return Value:
+
+ NT status
+
+--*/
+NTSTATUS
+ClassCheckVerifyComplete(
+ IN PDEVICE_OBJECT Fdo,
+ IN PIRP Irp,
+ IN PVOID Context
+ )
+{
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+
+ PIRP originalIrp;
+
+ UNREFERENCED_PARAMETER(Context);
+
+ ASSERT_FDO(Fdo);
+
+ originalIrp = irpStack->Parameters.Others.Argument1;
+
+ //
+ // Copy the media change count and status
+ //
+
+ *((PULONG) (originalIrp->AssociatedIrp.SystemBuffer)) =
+ fdoExtension->MediaChangeCount;
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "ClassCheckVerifyComplete - Media change count for"
+ "device %d is %lx - saved as %lx\n",
+ fdoExtension->DeviceNumber,
+ fdoExtension->MediaChangeCount,
+ *((PULONG) originalIrp->AssociatedIrp.SystemBuffer)));
+
+ originalIrp->IoStatus.Status = Irp->IoStatus.Status;
+ originalIrp->IoStatus.Information = sizeof(ULONG);
+
+ ClassReleaseRemoveLock(Fdo, originalIrp);
+ ClassCompleteRequest(Fdo, originalIrp, IO_DISK_INCREMENT);
+
+ IoFreeIrp(Irp);
+
+ return STATUS_MORE_PROCESSING_REQUIRED;
+
+} // end ClassCheckVerifyComplete()
+
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassGetDescriptor()
+
+Routine Description:
+
+ This routine will perform a query for the specified property id and will
+ allocate a non-paged buffer to store the data in. It is the responsibility
+ of the caller to ensure that this buffer is freed.
+
+ This routine must be run at IRQL_PASSIVE_LEVEL
+
+Arguments:
+
+ DeviceObject - the device to query
+ DeviceInfo - a location to store a pointer to the buffer we allocate
+
+Return Value:
+
+ status
+ if status is unsuccessful *DeviceInfo will be set to NULL, else the
+ buffer allocated on behalf of the caller.
+
+--*/
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+ClassGetDescriptor(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _In_ PSTORAGE_PROPERTY_ID PropertyId,
+ _Outptr_ PVOID *Descriptor
+ )
+{
+ STORAGE_PROPERTY_QUERY query = {0};
+ IO_STATUS_BLOCK ioStatus;
+
+ PSTORAGE_DESCRIPTOR_HEADER descriptor = NULL;
+ ULONG length;
+
+ PAGED_CODE();
+
+ //
+ // Set the passed-in descriptor pointer to NULL as default
+ //
+
+ *Descriptor = NULL;
+
+ query.PropertyId = *PropertyId;
+ query.QueryType = PropertyStandardQuery;
+
+ //
+ // On the first pass we just want to get the first few
+ // bytes of the descriptor so we can read it's size
+ //
+
+ descriptor = (PVOID)&query;
+
+ NT_ASSERT(sizeof(STORAGE_PROPERTY_QUERY) >= (sizeof(ULONG)*2));
+
+ ClassSendDeviceIoControlSynchronous(
+ IOCTL_STORAGE_QUERY_PROPERTY,
+ DeviceObject,
+ &query,
+ sizeof(STORAGE_PROPERTY_QUERY),
+ sizeof(ULONG) * 2,
+ FALSE,
+ &ioStatus
+ );
+
+ if(!NT_SUCCESS(ioStatus.Status)) {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT, "ClassGetDescriptor: error %lx trying to "
+ "query properties #1\n", ioStatus.Status));
+ return ioStatus.Status;
+ }
+
+ if (descriptor->Size == 0) {
+
+ //
+ // This DebugPrint is to help third-party driver writers
+ //
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT, "ClassGetDescriptor: size returned was zero?! (status "
+ "%x\n", ioStatus.Status));
+ return STATUS_UNSUCCESSFUL;
+
+ }
+
+ //
+ // This time we know how much data there is so we can
+ // allocate a buffer of the correct size
+ //
+
+ length = descriptor->Size;
+ NT_ASSERT(length >= sizeof(STORAGE_PROPERTY_QUERY));
+ length = max(length, sizeof(STORAGE_PROPERTY_QUERY));
+
+ descriptor = ExAllocatePoolWithTag(NonPagedPoolNx, length, '4BcS');
+
+ if(descriptor == NULL) {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT, "ClassGetDescriptor: unable to memory for descriptor "
+ "(%d bytes)\n", length));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ //
+ // setup the query again, as it was overwritten above
+ //
+
+ RtlZeroMemory(&query, sizeof(STORAGE_PROPERTY_QUERY));
+ query.PropertyId = *PropertyId;
+ query.QueryType = PropertyStandardQuery;
+
+ //
+ // copy the input to the new outputbuffer
+ //
+
+ RtlZeroMemory(descriptor, length);
+
+ RtlCopyMemory(descriptor,
+ &query,
+ sizeof(STORAGE_PROPERTY_QUERY)
+ );
+
+ ClassSendDeviceIoControlSynchronous(
+ IOCTL_STORAGE_QUERY_PROPERTY,
+ DeviceObject,
+ descriptor,
+ sizeof(STORAGE_PROPERTY_QUERY),
+ length,
+ FALSE,
+ &ioStatus
+ );
+
+ if(!NT_SUCCESS(ioStatus.Status)) {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT, "ClassGetDescriptor: error %lx trying to "
+ "query properties #1\n", ioStatus.Status));
+ FREE_POOL(descriptor);
+ return ioStatus.Status;
+ }
+
+ //
+ // return the memory we've allocated to the caller
+ //
+
+ *Descriptor = descriptor;
+ return ioStatus.Status;
+} // end ClassGetDescriptor()
+
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassSignalCompletion()
+
+Routine Description:
+
+ This completion routine will signal the event given as context and then
+ return STATUS_MORE_PROCESSING_REQUIRED to stop event completion. It is
+ the responsibility of the routine waiting on the event to complete the
+ request and free the event.
+
+Arguments:
+
+ DeviceObject - a pointer to the device object
+
+ Irp - a pointer to the irp
+
+ Event - a pointer to the event to signal
+
+Return Value:
+
+ STATUS_MORE_PROCESSING_REQUIRED
+
+--*/
+NTSTATUS
+ClassSignalCompletion(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ )
+{
+ PKEVENT Event = (PKEVENT)Context;
+
+ UNREFERENCED_PARAMETER(DeviceObject);
+ UNREFERENCED_PARAMETER(Irp);
+
+ if (Context == NULL) {
+ NT_ASSERT(Context != NULL);
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ KeSetEvent(Event, IO_NO_INCREMENT, FALSE);
+
+ return STATUS_MORE_PROCESSING_REQUIRED;
+} // end ClassSignalCompletion()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassPnpQueryFdoRelations()
+
+Routine Description:
+
+ This routine will call the driver's enumeration routine to update the
+ list of PDO's. It will then build a response to the
+ IRP_MN_QUERY_DEVICE_RELATIONS and place it into the information field in
+ the irp.
+
+Arguments:
+
+ Fdo - a pointer to the functional device object we are enumerating
+
+ Irp - a pointer to the enumeration request
+
+Return Value:
+
+ status
+
+--*/
+NTSTATUS
+ClassPnpQueryFdoRelations(
+ IN PDEVICE_OBJECT Fdo,
+ IN PIRP Irp
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+#pragma warning(suppress:4054) // okay to type cast function pointer as data pointer for this use case
+ PCLASS_DRIVER_EXTENSION driverExtension = IoGetDriverObjectExtension(Fdo->DriverObject, CLASS_DRIVER_EXTENSION_KEY);
+
+ PAGED_CODE();
+
+ _Analysis_assume_(driverExtension != NULL);
+
+ //
+ // If there's already an enumeration in progress then don't start another
+ // one.
+ //
+
+ if(InterlockedIncrement((volatile LONG *)&(fdoExtension->EnumerationInterlock)) == 1) {
+ driverExtension->InitData.ClassEnumerateDevice(Fdo);
+ }
+
+ Irp->IoStatus.Status = ClassRetrieveDeviceRelations(
+ Fdo,
+ BusRelations,
+ &((PDEVICE_RELATIONS) Irp->IoStatus.Information));
+ InterlockedDecrement((volatile LONG *)&(fdoExtension->EnumerationInterlock));
+
+ return Irp->IoStatus.Status;
+} // end ClassPnpQueryFdoRelations()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassMarkChildrenMissing()
+
+Routine Description:
+
+ This routine will call ClassMarkChildMissing() for all children.
+ It acquires the ChildLock before calling ClassMarkChildMissing().
+
+Arguments:
+
+ Fdo - the "bus's" device object, such as the disk FDO for non-removable
+ disks with multiple partitions.
+
+Return Value:
+
+ None
+
+--*/
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+ClassMarkChildrenMissing(
+ _In_ PFUNCTIONAL_DEVICE_EXTENSION Fdo
+ )
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = &(Fdo->CommonExtension);
+ PPHYSICAL_DEVICE_EXTENSION nextChild = commonExtension->ChildList;
+
+ PAGED_CODE();
+
+ ClassAcquireChildLock(Fdo);
+
+ while (nextChild){
+ PPHYSICAL_DEVICE_EXTENSION tmpChild;
+
+ /*
+ * ClassMarkChildMissing will also dequeue the child extension.
+ * So get the next pointer before calling ClassMarkChildMissing.
+ */
+ tmpChild = nextChild;
+ nextChild = tmpChild->CommonExtension.ChildList;
+ ClassMarkChildMissing(tmpChild, FALSE);
+ }
+ ClassReleaseChildLock(Fdo);
+ return;
+} // end ClassMarkChildrenMissing()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassMarkChildMissing()
+
+Routine Description:
+
+ This routine will make an active child "missing." If the device has never
+ been enumerated then it will be deleted on the spot. If the device has
+ not been enumerated then it will be marked as missing so that we can
+ not report it in the next device enumeration.
+
+Arguments:
+
+ Child - the child device to be marked as missing.
+
+ AcquireChildLock - TRUE if the child lock should be acquired before removing
+ the missing child. FALSE if the child lock is already
+ acquired by this thread.
+
+Return Value:
+
+ returns whether or not the child device object has previously been reported
+ to PNP.
+
+--*/
+_IRQL_requires_max_(PASSIVE_LEVEL)
+BOOLEAN
+ClassMarkChildMissing(
+ _In_ PPHYSICAL_DEVICE_EXTENSION Child,
+ _In_ BOOLEAN AcquireChildLock
+ )
+{
+ BOOLEAN returnValue = Child->IsEnumerated;
+
+ PAGED_CODE();
+ ASSERT_PDO(Child->DeviceObject);
+
+ Child->IsMissing = TRUE;
+
+ //
+ // Make sure this child is not in the active list.
+ //
+
+ ClassRemoveChild(Child->CommonExtension.PartitionZeroExtension,
+ Child,
+ AcquireChildLock);
+
+ if(Child->IsEnumerated == FALSE) {
+ PCOMMON_DEVICE_EXTENSION commonExtension = Child->DeviceObject->DeviceExtension;
+ commonExtension->IsRemoved = REMOVE_PENDING;
+ ClassRemoveDevice(Child->DeviceObject, IRP_MN_REMOVE_DEVICE);
+ }
+
+ return returnValue;
+} // end ClassMarkChildMissing()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassRetrieveDeviceRelations()
+
+Routine Description:
+
+ This routine will allocate a buffer to hold the specified list of
+ relations. It will then fill in the list with referenced device pointers
+ and will return the request.
+
+Arguments:
+
+ Fdo - pointer to the FDO being queried
+
+ RelationType - what type of relations are being queried
+
+ DeviceRelations - a location to store a pointer to the response
+
+Return Value:
+
+ status
+
+--*/
+NTSTATUS
+ClassRetrieveDeviceRelations(
+ IN PDEVICE_OBJECT Fdo,
+ IN DEVICE_RELATION_TYPE RelationType,
+ OUT PDEVICE_RELATIONS *DeviceRelations
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+
+ ULONG count = 0;
+ ULONG i;
+
+ PPHYSICAL_DEVICE_EXTENSION nextChild;
+
+ ULONG relationsSize;
+ PDEVICE_RELATIONS deviceRelations = NULL;
+ PDEVICE_RELATIONS oldRelations = *DeviceRelations;
+
+ NTSTATUS status;
+
+ UNREFERENCED_PARAMETER(RelationType);
+
+ PAGED_CODE();
+
+ ClassAcquireChildLock(fdoExtension);
+
+ nextChild = fdoExtension->CommonExtension.ChildList;
+
+ //
+ // Count the number of PDO's attached to this disk
+ //
+
+ while (nextChild != NULL) {
+ PCOMMON_DEVICE_EXTENSION commonExtension;
+
+ commonExtension = &(nextChild->CommonExtension);
+
+ NT_ASSERTMSG("ClassPnp internal error: missing child on active list\n",
+ (nextChild->IsMissing == FALSE));
+
+ nextChild = commonExtension->ChildList;
+
+ count++;
+ };
+
+ //
+ // If relations already exist in the QDR, adjust the current count
+ // to include the previous list.
+ //
+
+ if (oldRelations) {
+ count += oldRelations->Count;
+ }
+
+ relationsSize = (sizeof(DEVICE_RELATIONS) +
+ (count * sizeof(PDEVICE_OBJECT)));
+
+ deviceRelations = ExAllocatePoolWithTag(PagedPool, relationsSize, '5BcS');
+
+ if (deviceRelations == NULL) {
+
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_ENUM, "ClassRetrieveDeviceRelations: unable to allocate "
+ "%d bytes for device relations\n", relationsSize));
+
+ ClassReleaseChildLock(fdoExtension);
+
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlZeroMemory(deviceRelations, relationsSize);
+
+ if (oldRelations) {
+
+ //
+ // Copy the old relations to the new list and free the old list.
+ //
+
+ for (i = 0; i < oldRelations->Count; i++) {
+ deviceRelations->Objects[i] = oldRelations->Objects[i];
+ }
+
+ FREE_POOL(oldRelations);
+ }
+
+ nextChild = fdoExtension->CommonExtension.ChildList;
+ i = count;
+
+ while (nextChild != NULL) {
+ PCOMMON_DEVICE_EXTENSION commonExtension;
+
+ commonExtension = &(nextChild->CommonExtension);
+
+ NT_ASSERTMSG("ClassPnp internal error: missing child on active list\n",
+ (nextChild->IsMissing == FALSE));
+
+ _Analysis_assume_(i >= 1);
+ deviceRelations->Objects[--i] = nextChild->DeviceObject;
+
+ status = ObReferenceObjectByPointer(
+ nextChild->DeviceObject,
+ 0,
+ NULL,
+ KernelMode);
+ if (!NT_SUCCESS(status)) {
+ NT_ASSERT(!"Error referencing child device by pointer");
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_ENUM,
+ "ClassRetrieveDeviceRelations: Error referencing child "
+ "device %p by pointer\n", nextChild->DeviceObject));
+
+ }
+ nextChild->IsEnumerated = TRUE;
+ nextChild = commonExtension->ChildList;
+ }
+
+ NT_ASSERTMSG("Child list has changed: ", i == 0);
+
+ deviceRelations->Count = count;
+ *DeviceRelations = deviceRelations;
+
+ ClassReleaseChildLock(fdoExtension);
+ return STATUS_SUCCESS;
+} // end ClassRetrieveDeviceRelations()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassGetPdoId()
+
+Routine Description:
+
+ This routine will call into the driver to retrieve a copy of one of it's
+ id strings.
+
+Arguments:
+
+ Pdo - a pointer to the pdo being queried
+
+ IdType - which type of id string is being queried
+
+ IdString - an allocated unicode string structure which the driver
+ can fill in.
+
+Return Value:
+
+ status
+
+--*/
+NTSTATUS
+ClassGetPdoId(
+ IN PDEVICE_OBJECT Pdo,
+ IN BUS_QUERY_ID_TYPE IdType,
+ IN PUNICODE_STRING IdString
+ )
+{
+#pragma warning(suppress:4054) // okay to type cast function pointer as data pointer for this use case
+ PCLASS_DRIVER_EXTENSION driverExtension = IoGetDriverObjectExtension(Pdo->DriverObject, CLASS_DRIVER_EXTENSION_KEY);
+
+ _Analysis_assume_(driverExtension != NULL);
+
+ ASSERT_PDO(Pdo);
+ NT_ASSERT(driverExtension->InitData.ClassQueryId);
+
+ PAGED_CODE();
+
+ return driverExtension->InitData.ClassQueryId( Pdo, IdType, IdString);
+} // end ClassGetPdoId()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassQueryPnpCapabilities()
+
+Routine Description:
+
+ This routine will call into the class driver to retrieve it's pnp
+ capabilities.
+
+Arguments:
+
+ PhysicalDeviceObject - The physical device object to retrieve properties
+ for.
+
+Return Value:
+
+ status
+
+--*/
+NTSTATUS
+ClassQueryPnpCapabilities(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PDEVICE_CAPABILITIES Capabilities
+ )
+{
+ PCLASS_DRIVER_EXTENSION driverExtension =
+ ClassGetDriverExtension(DeviceObject->DriverObject);
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+
+ PCLASS_QUERY_PNP_CAPABILITIES queryRoutine = NULL;
+
+ PAGED_CODE();
+
+ NT_ASSERT(DeviceObject);
+ NT_ASSERT(Capabilities);
+
+ if(commonExtension->IsFdo) {
+ queryRoutine = driverExtension->InitData.FdoData.ClassQueryPnpCapabilities;
+ } else {
+ queryRoutine = driverExtension->InitData.PdoData.ClassQueryPnpCapabilities;
+ }
+
+ if(queryRoutine) {
+ return queryRoutine(DeviceObject,
+ Capabilities);
+ } else {
+ return STATUS_NOT_IMPLEMENTED;
+ }
+} // end ClassQueryPnpCapabilities()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassInvalidateBusRelations()
+
+Routine Description:
+
+ This routine re-enumerates the devices on the "bus". It will call into
+ the driver's ClassEnumerate routine to update the device objects
+ immediately. It will then schedule a bus re-enumeration for pnp by calling
+ IoInvalidateDeviceRelations.
+
+Arguments:
+
+ Fdo - a pointer to the functional device object for this bus
+
+Return Value:
+
+ none
+
+--*/
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+ClassInvalidateBusRelations(
+ _In_ PDEVICE_OBJECT Fdo
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+#pragma warning(suppress:4054) // okay to type cast function pointer as data pointer for this use case
+ PCLASS_DRIVER_EXTENSION driverExtension = IoGetDriverObjectExtension(Fdo->DriverObject, CLASS_DRIVER_EXTENSION_KEY);
+
+ NTSTATUS status = STATUS_SUCCESS;
+
+ PAGED_CODE();
+
+ _Analysis_assume_(driverExtension != NULL);
+
+ ASSERT_FDO(Fdo);
+ NT_ASSERT(driverExtension->InitData.ClassEnumerateDevice != NULL);
+
+ if(InterlockedIncrement((volatile LONG *)&(fdoExtension->EnumerationInterlock)) == 1) {
+ status = driverExtension->InitData.ClassEnumerateDevice(Fdo);
+ }
+ InterlockedDecrement((volatile LONG *)&(fdoExtension->EnumerationInterlock));
+
+ if(!NT_SUCCESS(status)) {
+
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_ENUM, "ClassInvalidateBusRelations: EnumerateDevice routine "
+ "returned %lx\n", status));
+ }
+
+ IoInvalidateDeviceRelations(fdoExtension->LowerPdo, BusRelations);
+
+ return;
+} // end ClassInvalidateBusRelations()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassRemoveDevice() ISSUE-2000/02/18-henrygab - why public?!
+
+Routine Description:
+
+ This routine is called to handle the "removal" of a device. It will
+ forward the request downwards if necesssary, call into the driver
+ to release any necessary resources (memory, events, etc) and then
+ will delete the device object.
+
+Arguments:
+
+ DeviceObject - a pointer to the device object being removed
+
+ RemoveType - indicates what type of remove this is (regular or surprise).
+
+Return Value:
+
+ status
+
+--*/
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+ClassRemoveDevice(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _In_ UCHAR RemoveType
+ )
+{
+#pragma warning(suppress:4054) // okay to type cast function pointer as data pointer for this use case
+ PCLASS_DRIVER_EXTENSION driverExtension = IoGetDriverObjectExtension(DeviceObject->DriverObject, CLASS_DRIVER_EXTENSION_KEY);
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PDEVICE_OBJECT lowerDeviceObject = commonExtension->LowerDeviceObject;
+ BOOLEAN proceedWithRemove = TRUE;
+ NTSTATUS status;
+ PEX_RUNDOWN_REF_CACHE_AWARE removeLockRundown = NULL;
+
+
+ PAGED_CODE();
+
+ _Analysis_assume_(driverExtension != NULL);
+
+ /*
+ * Deregister from WMI.
+ */
+ if (commonExtension->IsFdo ||
+ driverExtension->InitData.PdoData.ClassWmiInfo.GuidRegInfo) {
+ status = IoWMIRegistrationControl(DeviceObject, WMIREG_ACTION_DEREGISTER);
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "ClassRemoveDevice: IoWMIRegistrationControl(%p, WMI_ACTION_DEREGISTER) --> %lx", DeviceObject, status));
+ }
+
+ /*
+ * If we exposed a "shingle" (a named device interface openable by CreateFile)
+ * then delete it now.
+ */
+ if (commonExtension->MountedDeviceInterfaceName.Buffer){
+ (VOID)IoSetDeviceInterfaceState(&commonExtension->MountedDeviceInterfaceName, FALSE);
+ RtlFreeUnicodeString(&commonExtension->MountedDeviceInterfaceName);
+ RtlInitUnicodeString(&commonExtension->MountedDeviceInterfaceName, NULL);
+ }
+
+ //
+ // If this is a surprise removal we leave the device around - which means
+ // we don't have to (or want to) drop the remove lock and wait for pending
+ // requests to complete.
+ //
+
+ if (RemoveType == IRP_MN_REMOVE_DEVICE) {
+
+ //
+ // Release the lock we acquired when the device object was created.
+ //
+
+ ClassReleaseRemoveLock(DeviceObject, (PIRP) DeviceObject);
+
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_PNP, "ClasspRemoveDevice - Reference count is now %d\n",
+ commonExtension->RemoveLock));
+
+ //
+ // The RemoveLockRundown allows fast protection of the device object
+ // structure that is torn down by a single thread. While in the rundown process (the call to
+ // ExWaitForRundownProtectionReleaseCacheAware returns), the rundown object becomes
+ // invalid and the subsequent calls to ExAcquireRundownProtectionCacheAware will return FALSE.
+ // ExReInitializeRundownProtectionCacheAware needs to be called to re-initialize the
+ // RemoveLockRundown protection.
+ //
+
+ removeLockRundown = (PEX_RUNDOWN_REF_CACHE_AWARE)
+ ((PCHAR)commonExtension->PrivateCommonData +
+ sizeof(CLASS_PRIVATE_COMMON_DATA));
+ ExWaitForRundownProtectionReleaseCacheAware(removeLockRundown);
+
+ KeSetEvent(&commonExtension->RemoveEvent,
+ IO_NO_INCREMENT,
+ FALSE);
+
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_PNP, "ClasspRemoveDevice - removing device %p\n", DeviceObject));
+
+ if (commonExtension->IsFdo) {
+
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_PNP, "ClasspRemoveDevice - FDO %p has received a "
+ "remove request.\n", DeviceObject));
+
+ } else {
+ PPHYSICAL_DEVICE_EXTENSION pdoExtension = DeviceObject->DeviceExtension;
+
+ if (pdoExtension->IsMissing) {
+ /*
+ * The child partition PDO is missing, so we are going to go ahead
+ * and delete it for the remove.
+ */
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_PNP, "ClasspRemoveDevice - PDO %p is missing and will be removed", DeviceObject));
+ } else {
+ /*
+ * We got a remove for a child partition PDO which is not actually missing.
+ * So we will NOT actually delete it.
+ */
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_PNP, "ClasspRemoveDevice - PDO %p still exists and will be removed when it disappears", DeviceObject));
+
+ ExReInitializeRundownProtectionCacheAware(removeLockRundown);
+
+ //
+ // Reacquire the remove lock for the next time this comes around.
+ //
+
+ ClassAcquireRemoveLock(DeviceObject, (PIRP) DeviceObject);
+
+ //
+ // the device wasn't missing so it's not really been removed.
+ //
+
+ commonExtension->IsRemoved = NO_REMOVE;
+
+ IoInvalidateDeviceRelations(
+ commonExtension->PartitionZeroExtension->LowerPdo,
+ BusRelations);
+
+ proceedWithRemove = FALSE;
+ }
+ }
+ }
+
+
+ if (proceedWithRemove) {
+
+ /*
+ * Call the class driver's remove handler.
+ * All this is supposed to do is clean up its data and device interfaces.
+ */
+ NT_ASSERT(commonExtension->DevInfo->ClassRemoveDevice);
+ status = commonExtension->DevInfo->ClassRemoveDevice(DeviceObject, RemoveType);
+ NT_ASSERT(NT_SUCCESS(status));
+ status = STATUS_SUCCESS;
+ UNREFERENCED_PARAMETER(status); // disables prefast warning; defensive coding...
+
+ if (commonExtension->IsFdo) {
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+
+ ClasspDisableTimer(fdoExtension);
+
+ if (RemoveType == IRP_MN_REMOVE_DEVICE) {
+
+ PPHYSICAL_DEVICE_EXTENSION child;
+
+ //
+ // If this FDO is idle power managed, remove it from the
+ // list of idle power managed FDOs.
+ //
+ if (fdoExtension->FunctionSupportInfo &&
+ fdoExtension->FunctionSupportInfo->IdlePower.IdlePowerEnabled) {
+ PIDLE_POWER_FDO_LIST_ENTRY fdoEntry;
+
+ KeAcquireGuardedMutex(&IdlePowerFDOListMutex);
+ fdoEntry = (PIDLE_POWER_FDO_LIST_ENTRY)IdlePowerFDOList.Flink;
+ while ((PLIST_ENTRY)fdoEntry != &IdlePowerFDOList) {
+ PIDLE_POWER_FDO_LIST_ENTRY nextEntry = (PIDLE_POWER_FDO_LIST_ENTRY)fdoEntry->ListEntry.Flink;
+ if (fdoEntry->Fdo == DeviceObject) {
+ RemoveEntryList(&(fdoEntry->ListEntry));
+ ExFreePool(fdoEntry);
+ break;
+ }
+ fdoEntry = nextEntry;
+ }
+ KeReleaseGuardedMutex(&IdlePowerFDOListMutex);
+ }
+
+ //
+ // Cleanup the media detection resources now that the class driver
+ // has stopped it's timer (if any) and we can be sure they won't
+ // call us to do detection again.
+ //
+
+ ClassCleanupMediaChangeDetection(fdoExtension);
+
+ //
+ // Cleanup any Failure Prediction stuff
+ //
+ FREE_POOL(fdoExtension->FailurePredictionInfo);
+
+ /*
+ * Ordinarily all child PDOs will be removed by the time
+ * that the parent gets the REMOVE_DEVICE.
+ * However, if a child PDO has been created but has not
+ * been announced in a QueryDeviceRelations, then it is
+ * just a private data structure unknown to pnp, and we have
+ * to delete it ourselves.
+ */
+ ClassAcquireChildLock(fdoExtension);
+ child = ClassRemoveChild(fdoExtension, NULL, FALSE);
+ while (child) {
+ PCOMMON_DEVICE_EXTENSION childCommonExtension = child->DeviceObject->DeviceExtension;
+
+ //
+ // Yank the pdo. This routine will unlink the device from the
+ // pdo list so NextPdo will point to the next one when it's
+ // complete.
+ //
+ child->IsMissing = TRUE;
+ childCommonExtension->IsRemoved = REMOVE_PENDING;
+ ClassRemoveDevice(child->DeviceObject, IRP_MN_REMOVE_DEVICE);
+ child = ClassRemoveChild(fdoExtension, NULL, FALSE);
+ }
+ ClassReleaseChildLock(fdoExtension);
+ }
+ else if (RemoveType == IRP_MN_SURPRISE_REMOVAL){
+ /*
+ * This is a surprise-remove on the parent FDO.
+ * We will mark the child PDOs as missing so that they
+ * will actually get deleted when they get a REMOVE_DEVICE.
+ */
+ ClassMarkChildrenMissing(fdoExtension);
+ }
+
+ if (RemoveType == IRP_MN_REMOVE_DEVICE) {
+
+ ClasspFreeReleaseRequest(DeviceObject);
+
+ //
+ // Free FDO-specific data structs
+ //
+ if (fdoExtension->PrivateFdoData) {
+ //
+ // Only remove the entry if the list has been initialized, or
+ // else we will access invalid memory.
+ //
+ PLIST_ENTRY allFdosListEntry = &fdoExtension->PrivateFdoData->AllFdosListEntry;
+ if (allFdosListEntry->Flink && allFdosListEntry->Blink) {
+ //
+ // Remove the FDO from the static list.
+ // Pnp is synchronized so this shouldn't need any synchronization.
+ //
+ RemoveEntryList(allFdosListEntry);
+ }
+ InitializeListHead(allFdosListEntry);
+
+ DestroyAllTransferPackets(DeviceObject);
+
+ //
+ // Delete the tick timer now.
+ //
+ ClasspDeleteTimer(fdoExtension);
+
+
+ FREE_POOL(fdoExtension->PrivateFdoData->PowerProcessIrp);
+ FREE_POOL(fdoExtension->PrivateFdoData->FreeTransferPacketsLists);
+ FREE_POOL(fdoExtension->PrivateFdoData);
+ }
+
+#if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD)
+ FREE_POOL(fdoExtension->AdditionalFdoData);
+#endif
+
+ if (commonExtension->DeviceName.Buffer) {
+ FREE_POOL(commonExtension->DeviceName.Buffer);
+ RtlInitUnicodeString(&commonExtension->DeviceName, NULL);
+ }
+
+#if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD)
+ if (fdoExtension->FunctionSupportInfo != NULL) {
+ FREE_POOL(fdoExtension->FunctionSupportInfo->HwFirmwareInfo);
+ }
+#endif
+ FREE_POOL(fdoExtension->FunctionSupportInfo);
+
+ FREE_POOL(fdoExtension->MiniportDescriptor);
+
+ FREE_POOL(fdoExtension->AdapterDescriptor);
+
+ FREE_POOL(fdoExtension->DeviceDescriptor);
+
+ //
+ // Detach our device object from the stack - there's no reason
+ // to hold off our cleanup any longer.
+ //
+
+ IoDetachDevice(lowerDeviceObject);
+ }
+ }
+ else {
+ /*
+ * This is a child partition PDO.
+ * We have already determined that it was previously marked
+ * as missing. So if this is a REMOVE_DEVICE, we will actually
+ * delete it.
+ */
+ if (RemoveType == IRP_MN_REMOVE_DEVICE) {
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = commonExtension->PartitionZeroExtension;
+ PPHYSICAL_DEVICE_EXTENSION pdoExtension = (PPHYSICAL_DEVICE_EXTENSION)commonExtension;
+
+ //
+ // See if this device is in the child list (if this was a suprise
+ // removal it might be) and remove it.
+ //
+ ClassRemoveChild(fdoExtension, pdoExtension, TRUE);
+ }
+ }
+
+ commonExtension->PartitionLength.QuadPart = 0;
+
+ if (RemoveType == IRP_MN_REMOVE_DEVICE) {
+
+ ClasspUninitializeRemoveTracking(DeviceObject);
+
+ IoDeleteDevice(DeviceObject);
+ }
+ }
+
+ return STATUS_SUCCESS;
+} // end ClassRemoveDevice()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassGetDriverExtension()
+
+Routine Description:
+
+ This routine will return the classpnp's driver extension.
+
+Arguments:
+
+ DriverObject - the driver object for which to get classpnp's extension
+
+Return Value:
+
+ Either NULL if none, or a pointer to the driver extension
+
+--*/
+__drv_aliasesMem
+_IRQL_requires_max_(DISPATCH_LEVEL)
+PCLASS_DRIVER_EXTENSION
+ClassGetDriverExtension(
+ _In_ PDRIVER_OBJECT DriverObject
+ )
+{
+#pragma warning(suppress:4054) // okay to type cast function pointer as data pointer for this use case
+ return IoGetDriverObjectExtension(DriverObject, CLASS_DRIVER_EXTENSION_KEY);
+} // end ClassGetDriverExtension()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClasspStartIo()
+
+Routine Description:
+
+ This routine wraps the class driver's start io routine. If the device
+ is being removed it will complete any requests with
+ STATUS_DEVICE_DOES_NOT_EXIST and fire up the next packet.
+
+Arguments:
+
+Return Value:
+
+ none
+
+--*/
+VOID
+ClasspStartIo(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+
+ //
+ // We're already holding the remove lock so just check the variable and
+ // see what's going on.
+ //
+
+ if(commonExtension->IsRemoved) {
+
+ Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
+
+#pragma warning(suppress:4054) // okay to type cast function pointer to PIRP for this use case
+ ClassAcquireRemoveLock(DeviceObject, (PIRP) ClasspStartIo);
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_DISK_INCREMENT);
+ IoStartNextPacket(DeviceObject, TRUE); // Some IO is cancellable
+
+#pragma warning(suppress:4054) // okay to type cast function pointer to PIRP for this use case
+ ClassReleaseRemoveLock(DeviceObject, (PIRP) ClasspStartIo);
+
+ return;
+ }
+
+ commonExtension->DriverExtension->InitData.ClassStartIo(
+ DeviceObject,
+ Irp);
+
+ return;
+} // ClasspStartIo()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassUpdateInformationInRegistry()
+
+Routine Description:
+
+ This routine has knowledge about the layout of the device map information
+ in the registry. It will update this information to include a value
+ entry specifying the dos device name that is assumed to get assigned
+ to this NT device name. For more information on this assigning of the
+ dos device name look in the drive support routine in the hal that assigns
+ all dos names.
+
+ Since some versions of some device's firmware did not work and some
+ vendors did not bother to follow the specification, the entire inquiry
+ information must also be stored in the registry so than someone can
+ figure out the firmware version.
+
+Arguments:
+
+ DeviceObject - A pointer to the device object for the tape device.
+
+Return Value:
+
+ None
+
+--*/
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+ClassUpdateInformationInRegistry(
+ _In_ PDEVICE_OBJECT Fdo,
+ _In_ PCHAR DeviceName,
+ _In_ ULONG DeviceNumber,
+ _In_reads_bytes_opt_(InquiryDataLength) PINQUIRYDATA InquiryData,
+ _In_ ULONG InquiryDataLength
+ )
+{
+ NTSTATUS status;
+ SCSI_ADDRESS scsiAddress = {0};
+ OBJECT_ATTRIBUTES objectAttributes = {0};
+ STRING string;
+ UNICODE_STRING unicodeName = {0};
+ UNICODE_STRING unicodeRegistryPath = {0};
+ UNICODE_STRING unicodeData = {0};
+ HANDLE targetKey;
+ IO_STATUS_BLOCK ioStatus;
+ UCHAR buffer[256] = {0};
+
+ PAGED_CODE();
+
+ NT_ASSERT(DeviceName);
+ targetKey = NULL;
+
+ TRY {
+
+ //
+ // Issue GET_ADDRESS Ioctl to determine path, target, and lun information.
+ //
+
+ ClassSendDeviceIoControlSynchronous(
+ IOCTL_SCSI_GET_ADDRESS,
+ Fdo,
+ &scsiAddress,
+ 0,
+ sizeof(SCSI_ADDRESS),
+ FALSE,
+ &ioStatus
+ );
+
+ if (!NT_SUCCESS(ioStatus.Status)) {
+
+ status = ioStatus.Status;
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
+ "UpdateInformationInRegistry: Get Address failed %lx\n",
+ status));
+ LEAVE;
+
+ } else {
+
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
+ "GetAddress: Port %x, Path %x, Target %x, Lun %x\n",
+ scsiAddress.PortNumber,
+ scsiAddress.PathId,
+ scsiAddress.TargetId,
+ scsiAddress.Lun));
+
+ }
+
+ status = RtlStringCchPrintfA((NTSTRSAFE_PSTR)buffer,
+ sizeof(buffer)-1,
+ "\\Registry\\Machine\\Hardware\\DeviceMap\\Scsi\\Scsi Port %d\\Scsi Bus %d\\Target Id %d\\Logical Unit Id %d",
+ scsiAddress.PortNumber,
+ scsiAddress.PathId,
+ scsiAddress.TargetId,
+ scsiAddress.Lun);
+
+ if (!NT_SUCCESS(status)) {
+ LEAVE;
+ }
+
+ RtlInitString(&string, (PCSZ)buffer);
+
+ status = RtlAnsiStringToUnicodeString(&unicodeRegistryPath,
+ &string,
+ TRUE);
+
+ if (!NT_SUCCESS(status)) {
+ LEAVE;
+ }
+
+ //
+ // Open the registry key for the scsi information for this
+ // scsibus, target, lun.
+ //
+
+ InitializeObjectAttributes(&objectAttributes,
+ &unicodeRegistryPath,
+ OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+ NULL,
+ NULL);
+
+ status = ZwOpenKey(&targetKey,
+ KEY_READ | KEY_WRITE,
+ &objectAttributes);
+
+ if (!NT_SUCCESS(status)) {
+ LEAVE;
+ }
+
+ //
+ // Now construct and attempt to create the registry value
+ // specifying the device name in the appropriate place in the
+ // device map.
+ //
+
+ RtlInitUnicodeString(&unicodeName, L"DeviceName");
+
+ status = RtlStringCchPrintfA((NTSTRSAFE_PSTR)buffer, sizeof(buffer)-1, "%s%d", DeviceName, DeviceNumber);
+ if (!NT_SUCCESS(status)) {
+ LEAVE;
+ }
+
+ RtlInitString(&string, (PCSZ)buffer);
+ status = RtlAnsiStringToUnicodeString(&unicodeData,
+ &string,
+ TRUE);
+ if (NT_SUCCESS(status)) {
+ status = ZwSetValueKey(targetKey,
+ &unicodeName,
+ 0,
+ REG_SZ,
+ unicodeData.Buffer,
+ unicodeData.Length);
+ }
+
+ //
+ // if they sent in data, update the registry
+ //
+
+ if (NT_SUCCESS(status) && InquiryDataLength) {
+
+ NT_ASSERT(InquiryData);
+
+ RtlInitUnicodeString(&unicodeName, L"InquiryData");
+ status = ZwSetValueKey(targetKey,
+ &unicodeName,
+ 0,
+ REG_BINARY,
+ InquiryData,
+ InquiryDataLength);
+ }
+
+ // that's all, except to clean up.
+
+ } FINALLY {
+
+ if (!NT_SUCCESS(status)) {
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
+ "Failure to update information in registry: %08x\n",
+ status
+ ));
+ }
+
+ if (unicodeData.Buffer) {
+ RtlFreeUnicodeString(&unicodeData);
+ }
+ if (unicodeRegistryPath.Buffer) {
+ RtlFreeUnicodeString(&unicodeRegistryPath);
+ }
+ if (targetKey) {
+ ZwClose(targetKey);
+ }
+
+ }
+
+} // end ClassUpdateInformationInRegistry()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClasspSendSynchronousCompletion()
+
+Routine Description:
+
+ This completion routine will set the user event in the irp after
+ freeing the irp and the associated MDL (if any).
+
+Arguments:
+
+ DeviceObject - the device object which requested the completion routine
+
+ Irp - the irp being completed
+
+ Context - unused
+
+Return Value:
+
+ STATUS_MORE_PROCESSING_REQUIRED
+
+--*/
+NTSTATUS
+ClasspSendSynchronousCompletion(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ )
+{
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClasspSendSynchronousCompletion: %p %p %p\n",
+ DeviceObject, Irp, Context));
+ //
+ // First set the status and information fields in the io status block
+ // provided by the caller.
+ //
+
+ *(Irp->UserIosb) = Irp->IoStatus;
+
+ //
+ // Unlock the pages for the data buffer.
+ //
+
+ if(Irp->MdlAddress) {
+ MmUnlockPages(Irp->MdlAddress);
+ IoFreeMdl(Irp->MdlAddress);
+ }
+
+ //
+ // Signal the caller's event.
+ //
+
+ KeSetEvent(Irp->UserEvent, IO_NO_INCREMENT, FALSE);
+
+ //
+ // Free the MDL and the IRP.
+ //
+
+ IoFreeIrp(Irp);
+
+ return STATUS_MORE_PROCESSING_REQUIRED;
+} // end ClasspSendSynchronousCompletion()
+
+/*++
+
+ ISSUE-2000/02/20-henrygab Not documented ClasspRegisterMountedDeviceInterface
+
+--*/
+VOID
+ClasspRegisterMountedDeviceInterface(
+ IN PDEVICE_OBJECT DeviceObject
+ )
+{
+
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ BOOLEAN isFdo = commonExtension->IsFdo;
+ PDEVICE_OBJECT pdo;
+ UNICODE_STRING interfaceName;
+
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ if(isFdo) {
+
+ PFUNCTIONAL_DEVICE_EXTENSION functionalExtension;
+
+ functionalExtension =
+ (PFUNCTIONAL_DEVICE_EXTENSION) commonExtension;
+ pdo = functionalExtension->LowerPdo;
+ } else {
+ pdo = DeviceObject;
+ }
+
+#pragma prefast(suppress:6014, "The allocated memory that interfaceName points to will be freed in ClassRemoveDevice().")
+ status = IoRegisterDeviceInterface(
+ pdo,
+ &MOUNTDEV_MOUNTED_DEVICE_GUID,
+ NULL,
+ &interfaceName
+ );
+
+ if(NT_SUCCESS(status)) {
+
+ //
+ // Copy the interface name before setting the interface state - the
+ // name is needed by the components we notify.
+ //
+
+ commonExtension->MountedDeviceInterfaceName = interfaceName;
+ status = IoSetDeviceInterfaceState(&interfaceName, TRUE);
+
+ if(!NT_SUCCESS(status)) {
+ RtlFreeUnicodeString(&interfaceName);
+ }
+ }
+
+ if(!NT_SUCCESS(status)) {
+ RtlInitUnicodeString(&(commonExtension->MountedDeviceInterfaceName),
+ NULL);
+ }
+ return;
+} // end ClasspRegisterMountedDeviceInterface()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassSendDeviceIoControlSynchronous()
+
+Routine Description:
+
+ This routine is based upon IoBuildDeviceIoControlRequest(). It has been
+ modified to reduce code and memory by not double-buffering the io, using
+ the same buffer for both input and output, allocating and deallocating
+ the mdl on behalf of the caller, and waiting for the io to complete.
+
+ This routine also works around the rare cases in which APC's are disabled.
+ Since IoBuildDeviceIoControl() used APC's to signal completion, this had
+ led to a number of difficult-to-detect hangs, where the irp was completed,
+ but the event passed to IoBuild..() was still being waited upon by the
+ caller.
+
+Arguments:
+
+ IoControlCode - the IOCTL to send
+
+ TargetDeviceObject - the device object that should handle the ioctl
+
+ Buffer - the input and output buffer, or NULL if no input/output
+
+ InputBufferLength - the number of bytes prepared for the IOCTL in Buffer
+
+ OutputBufferLength - the number of bytes to be filled in upon success
+
+ InternalDeviceIoControl - if TRUE, uses IRP_MJ_INTERNAL_DEVICE_CONTROL
+
+ IoStatus - the status block that contains the results of the operation
+
+Return Value:
+
+--*/
+VOID
+ClassSendDeviceIoControlSynchronous(
+ _In_ ULONG IoControlCode,
+ _In_ PDEVICE_OBJECT TargetDeviceObject,
+ _Inout_updates_opt_(_Inexpressible_(max(InputBufferLength, OutputBufferLength))) PVOID Buffer,
+ _In_ ULONG InputBufferLength,
+ _In_ ULONG OutputBufferLength,
+ _In_ BOOLEAN InternalDeviceIoControl,
+ _Out_ PIO_STATUS_BLOCK IoStatus
+ )
+{
+ PIRP irp;
+ PIO_STACK_LOCATION irpSp;
+ ULONG method;
+
+ PAGED_CODE();
+
+ irp = NULL;
+ method = IoControlCode & 3;
+
+ #if DBG // Begin Argument Checking (nop in fre version)
+
+ NT_ASSERT(ARGUMENT_PRESENT(IoStatus));
+
+ if ((InputBufferLength != 0) || (OutputBufferLength != 0)) {
+ NT_ASSERT(ARGUMENT_PRESENT(Buffer));
+ }
+ else {
+ NT_ASSERT(!ARGUMENT_PRESENT(Buffer));
+ }
+ #endif
+
+ //
+ // Begin by allocating the IRP for this request. Do not charge quota to
+ // the current process for this IRP.
+ //
+
+ irp = IoAllocateIrp(TargetDeviceObject->StackSize, FALSE);
+ if (!irp) {
+ IoStatus->Information = 0;
+ IoStatus->Status = STATUS_INSUFFICIENT_RESOURCES;
+ return;
+ }
+
+ //
+ // Get a pointer to the stack location of the first driver which will be
+ // invoked. This is where the function codes and the parameters are set.
+ //
+
+ irpSp = IoGetNextIrpStackLocation(irp);
+
+ //
+ // Set the major function code based on the type of device I/O control
+ // function the caller has specified.
+ //
+
+ if (InternalDeviceIoControl) {
+ irpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
+ } else {
+ irpSp->MajorFunction = IRP_MJ_DEVICE_CONTROL;
+ }
+
+ //
+ // Copy the caller's parameters to the service-specific portion of the
+ // IRP for those parameters that are the same for all four methods.
+ //
+
+ irpSp->Parameters.DeviceIoControl.OutputBufferLength = OutputBufferLength;
+ irpSp->Parameters.DeviceIoControl.InputBufferLength = InputBufferLength;
+ irpSp->Parameters.DeviceIoControl.IoControlCode = IoControlCode;
+
+ //
+ // Get the method bits from the I/O control code to determine how the
+ // buffers are to be passed to the driver.
+ //
+
+ switch (method)
+ {
+ //
+ // case 0
+ //
+ case METHOD_BUFFERED:
+ {
+ if ((InputBufferLength != 0) || (OutputBufferLength != 0))
+ {
+ irp->AssociatedIrp.SystemBuffer = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
+ max(InputBufferLength, OutputBufferLength),
+ CLASS_TAG_DEVICE_CONTROL);
+ if (irp->AssociatedIrp.SystemBuffer == NULL)
+ {
+ IoFreeIrp(irp);
+
+ IoStatus->Information = 0;
+ IoStatus->Status = STATUS_INSUFFICIENT_RESOURCES;
+ return;
+ }
+
+ if (InputBufferLength != 0)
+ {
+ RtlCopyMemory(irp->AssociatedIrp.SystemBuffer, Buffer, InputBufferLength);
+ }
+ }
+
+ irp->UserBuffer = Buffer;
+
+ break;
+ }
+
+ //
+ // case 1, case 2
+ //
+ case METHOD_IN_DIRECT:
+ case METHOD_OUT_DIRECT:
+ {
+ if (InputBufferLength != 0)
+ {
+ irp->AssociatedIrp.SystemBuffer = Buffer;
+ }
+
+ if (OutputBufferLength != 0)
+ {
+ irp->MdlAddress = IoAllocateMdl(Buffer,
+ OutputBufferLength,
+ FALSE,
+ FALSE,
+ (PIRP) NULL);
+ if (irp->MdlAddress == NULL)
+ {
+ IoFreeIrp(irp);
+
+ IoStatus->Information = 0;
+ IoStatus->Status = STATUS_INSUFFICIENT_RESOURCES;
+ return;
+ }
+
+ try
+ {
+ MmProbeAndLockPages(irp->MdlAddress,
+ KernelMode,
+ (method == METHOD_IN_DIRECT) ? IoReadAccess : IoWriteAccess);
+ }
+ #pragma warning(suppress: 6320) // We want to handle any exception that MmProbeAndLockPages might throw
+ except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ IoFreeMdl(irp->MdlAddress);
+ IoFreeIrp(irp);
+
+ IoStatus->Information = 0;
+ IoStatus->Status = GetExceptionCode();
+ return;
+ }
+ }
+
+ break;
+ }
+
+ //
+ // case 3
+ //
+ case METHOD_NEITHER:
+ {
+ NT_ASSERT(!"ClassSendDeviceIoControlSynchronous does not support METHOD_NEITHER Ioctls");
+
+ IoFreeIrp(irp);
+
+ IoStatus->Information = 0;
+ IoStatus->Status = STATUS_NOT_SUPPORTED;
+ return;
+ }
+ }
+
+ irp->Tail.Overlay.Thread = PsGetCurrentThread();
+
+ //
+ // send the irp synchronously
+ //
+
+ ClassSendIrpSynchronous(TargetDeviceObject, irp);
+
+ //
+ // copy the iostatus block for the caller
+ //
+
+ *IoStatus = irp->IoStatus;
+
+ //
+ // free any allocated resources
+ //
+
+ switch (method) {
+ case METHOD_BUFFERED: {
+
+ NT_ASSERT(irp->UserBuffer == Buffer);
+
+ //
+ // first copy the buffered result, if any
+ // Note that there are no security implications in
+ // not checking for success since only drivers can
+ // call into this routine anyways...
+ //
+
+ if (OutputBufferLength != 0) {
+ #pragma warning(suppress: 6386) // Buffer's size is max(InputBufferLength, OutputBufferLength)
+ RtlCopyMemory(Buffer, // irp->UserBuffer
+ irp->AssociatedIrp.SystemBuffer,
+ OutputBufferLength
+ );
+ }
+
+ //
+ // then free the memory allocated to buffer the io
+ //
+
+ if ((InputBufferLength !=0) || (OutputBufferLength != 0)) {
+ FREE_POOL(irp->AssociatedIrp.SystemBuffer);
+ }
+ break;
+ }
+
+ case METHOD_IN_DIRECT:
+ case METHOD_OUT_DIRECT: {
+
+ //
+ // we alloc a mdl if there is an output buffer specified
+ // free it here after unlocking the pages
+ //
+
+ if (OutputBufferLength != 0) {
+ NT_ASSERT(irp->MdlAddress != NULL);
+ MmUnlockPages(irp->MdlAddress);
+ IoFreeMdl(irp->MdlAddress);
+ irp->MdlAddress = (PMDL) NULL;
+ }
+ break;
+ }
+
+ case METHOD_NEITHER: {
+ NT_ASSERT(!"Code is out of date");
+ break;
+ }
+ }
+
+ //
+ // we always have allocated an irp. free it here.
+ //
+
+ IoFreeIrp(irp);
+ irp = (PIRP) NULL;
+
+ //
+ // return the io status block's status to the caller
+ //
+
+ return;
+} // end ClassSendDeviceIoControlSynchronous()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassForwardIrpSynchronous()
+
+Routine Description:
+
+ Forwards a given irp to the next lower device object.
+
+Arguments:
+
+ CommonExtension - the common class extension
+
+ Irp - the request to forward down the stack
+
+Return Value:
+
+--*/
+NTSTATUS
+ClassForwardIrpSynchronous(
+ _In_ PCOMMON_DEVICE_EXTENSION CommonExtension,
+ _In_ PIRP Irp
+ )
+{
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+ return ClassSendIrpSynchronous(CommonExtension->LowerDeviceObject, Irp);
+} // end ClassForwardIrpSynchronous()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassSendIrpSynchronous()
+
+Routine Description:
+
+ This routine sends the given irp to the given device object, and waits for
+ it to complete. On debug versions, will print out a debug message and
+ optionally assert for "lost" irps based upon classpnp's globals
+
+Arguments:
+
+ TargetDeviceObject - the device object to handle this irp
+
+ Irp - the request to be sent
+
+Return Value:
+
+--*/
+NTSTATUS
+ClassSendIrpSynchronous(
+ _In_ PDEVICE_OBJECT TargetDeviceObject,
+ _In_ PIRP Irp
+ )
+{
+ KEVENT event;
+ NTSTATUS status;
+
+ NT_ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
+ NT_ASSERT(TargetDeviceObject != NULL);
+ NT_ASSERT(Irp != NULL);
+ NT_ASSERT(Irp->StackCount >= TargetDeviceObject->StackSize);
+
+ //
+ // ISSUE-2000/02/20-henrygab What if APCs are disabled?
+ // May need to enter critical section before IoCallDriver()
+ // until the event is hit?
+ //
+
+ KeInitializeEvent(&event, SynchronizationEvent, FALSE);
+ IoSetCompletionRoutine(Irp, ClassSignalCompletion, &event,
+ TRUE, TRUE, TRUE);
+
+ status = IoCallDriver(TargetDeviceObject, Irp);
+ _Analysis_assume_(status!=STATUS_PENDING);
+ if (status == STATUS_PENDING) {
+
+ #if DBG
+ LARGE_INTEGER timeout;
+
+ timeout.QuadPart = (LONGLONG)(-1 * 10 * 1000 * (LONGLONG)1000 *
+ ClasspnpGlobals.SecondsToWaitForIrps);
+
+ do {
+ status = KeWaitForSingleObject(&event,
+ Executive,
+ KernelMode,
+ FALSE,
+ &timeout);
+
+
+ if (status == STATUS_TIMEOUT) {
+
+ //
+ // This DebugPrint should almost always be investigated by the
+ // party who sent the irp and/or the current owner of the irp.
+ // Synchronous Irps should not take this long (currently 30
+ // seconds) without good reason. This points to a potentially
+ // serious problem in the underlying device stack.
+ //
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClassSendIrpSynchronous: (%p) irp %p did not "
+ "complete within %x seconds\n",
+ TargetDeviceObject, Irp,
+ ClasspnpGlobals.SecondsToWaitForIrps
+ ));
+
+ if (ClasspnpGlobals.BreakOnLostIrps != 0) {
+ NT_ASSERT(!" - Irp failed to complete within 30 seconds - ");
+ }
+ }
+
+
+ } while (status==STATUS_TIMEOUT);
+ #else
+ (VOID)KeWaitForSingleObject(&event,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL);
+ #endif
+
+ status = Irp->IoStatus.Status;
+ }
+
+ return status;
+} // end ClassSendIrpSynchronous()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassGetVpb()
+
+Routine Description:
+
+ This routine returns the current VPB (Volume Parameter Block) for the
+ given device object.
+ The Vpb field is only visible in the ntddk.h (not the wdm.h) definition
+ of DEVICE_OBJECT; hence this exported function.
+
+Arguments:
+
+ DeviceObject - the device to get the VPB for
+
+Return Value:
+
+ the VPB, or NULL if none.
+
+--*/
+PVPB
+ClassGetVpb(
+ _In_ PDEVICE_OBJECT DeviceObject
+ )
+{
+#pragma prefast(suppress:28175)
+ return DeviceObject->Vpb;
+} // end ClassGetVpb()
+
+/*++
+
+ ISSUE-2000/02/20-henrygab Not documented ClasspAllocateReleaseRequest
+
+--*/
+NTSTATUS
+ClasspAllocateReleaseRequest(
+ IN PDEVICE_OBJECT Fdo
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+
+ PAGED_CODE();
+
+ KeInitializeSpinLock(&(fdoExtension->ReleaseQueueSpinLock));
+
+ fdoExtension->ReleaseQueueNeeded = FALSE;
+ fdoExtension->ReleaseQueueInProgress = FALSE;
+ fdoExtension->ReleaseQueueIrpFromPool = FALSE;
+
+ //
+ // The class driver is responsible for allocating a properly sized irp,
+ // or ClassReleaseQueue will attempt to do it on the first error.
+ //
+
+ fdoExtension->ReleaseQueueIrp = NULL;
+
+ //
+ // Write length to SRB.
+ //
+
+ fdoExtension->ReleaseQueueSrb.Length = sizeof(SCSI_REQUEST_BLOCK);
+
+ return STATUS_SUCCESS;
+} // end ClasspAllocateReleaseRequest()
+
+/*++
+
+ ISSUE-2000/02/20-henrygab Not documented ClasspFreeReleaseRequest
+
+--*/
+VOID
+ClasspFreeReleaseRequest(
+ IN PDEVICE_OBJECT Fdo
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+
+ PAGED_CODE();
+
+ //KIRQL oldIrql;
+
+ NT_ASSERT(fdoExtension->CommonExtension.IsRemoved != NO_REMOVE);
+
+ //
+ // free anything the driver allocated
+ //
+
+ if (fdoExtension->ReleaseQueueIrp) {
+ if (fdoExtension->ReleaseQueueIrpFromPool) {
+ FREE_POOL(fdoExtension->ReleaseQueueIrp);
+ } else {
+ IoFreeIrp(fdoExtension->ReleaseQueueIrp);
+ }
+ fdoExtension->ReleaseQueueIrp = NULL;
+ }
+
+ //
+ // free anything that we allocated
+ //
+
+ if ((fdoExtension->PrivateFdoData) &&
+ (fdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated)) {
+
+ FREE_POOL(fdoExtension->PrivateFdoData->ReleaseQueueIrp);
+ fdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated = FALSE;
+ }
+
+ return;
+} // end ClasspFreeReleaseRequest()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassReleaseQueue()
+
+Routine Description:
+
+ This routine issues an internal device control command
+ to the port driver to release a frozen queue. The call
+ is issued asynchronously as ClassReleaseQueue will be invoked
+ from the IO completion DPC (and will have no context to
+ wait for a synchronous call to complete).
+
+ This routine must be called with the remove lock held.
+
+Arguments:
+
+ Fdo - The functional device object for the device with the frozen queue.
+
+Return Value:
+
+ None.
+
+--*/
+VOID
+ClassReleaseQueue(
+ _In_ PDEVICE_OBJECT Fdo
+ )
+{
+ ClasspReleaseQueue(Fdo, NULL);
+ return;
+} // end ClassReleaseQueue()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClasspAllocateReleaseQueueIrp()
+
+Routine Description:
+
+ This routine allocates the release queue irp held in classpnp's private
+ extension. This was added to allow no-memory conditions to be more
+ survivable.
+
+Return Value:
+
+ NT_SUCCESS value.
+
+Notes:
+
+ Does not grab the spinlock. Should only be called from StartDevice()
+ routine. May be called elsewhere for poorly-behaved drivers that cause
+ the queue to lockup before the device is started. This should *never*
+ occur, since it's illegal to send a request to a non-started PDO. This
+ condition is checked for in ClasspReleaseQueue().
+
+--*/
+NTSTATUS
+ClasspAllocateReleaseQueueIrp(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ )
+{
+ UCHAR lowerStackSize;
+
+ //
+ // do an initial check w/o the spinlock
+ //
+
+ if (FdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated) {
+ return STATUS_SUCCESS;
+ }
+
+
+ lowerStackSize = FdoExtension->CommonExtension.LowerDeviceObject->StackSize;
+
+ //
+ // don't allocate one if one is in progress! this means whoever called
+ // this routine didn't check if one was in progress.
+ //
+
+ NT_ASSERT(!(FdoExtension->ReleaseQueueInProgress));
+
+ FdoExtension->PrivateFdoData->ReleaseQueueIrp =
+ ExAllocatePoolWithTag(NonPagedPoolNx,
+ IoSizeOfIrp(lowerStackSize),
+ CLASS_TAG_RELEASE_QUEUE
+ );
+
+ if (FdoExtension->PrivateFdoData->ReleaseQueueIrp == NULL) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP, "ClassPnpStartDevice: Cannot allocate for "
+ "release queue irp\n"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+ IoInitializeIrp(FdoExtension->PrivateFdoData->ReleaseQueueIrp,
+ IoSizeOfIrp(lowerStackSize),
+ lowerStackSize);
+ FdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated = TRUE;
+
+ return STATUS_SUCCESS;
+}
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClasspAllocatePowerProcessIrp()
+
+Routine Description:
+
+ This routine allocates the power process irp.
+ This routine should be called after PrivateFdoData is allocated.
+
+Return Value:
+
+ NTSTATUS value.
+
+Notes:
+
+ Should only be called from StartDevice()
+
+--*/
+NTSTATUS
+ClasspAllocatePowerProcessIrp(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ )
+{
+ UCHAR stackSize;
+
+ NT_ASSERT(FdoExtension->PrivateFdoData != NULL);
+
+ stackSize = FdoExtension->CommonExtension.LowerDeviceObject->StackSize + 1;
+
+ FdoExtension->PrivateFdoData->PowerProcessIrp = ExAllocatePoolWithTag(NonPagedPoolNx,
+ IoSizeOfIrp(stackSize),
+ CLASS_TAG_POWER
+ );
+
+ if (FdoExtension->PrivateFdoData->PowerProcessIrp == NULL) {
+
+ return STATUS_INSUFFICIENT_RESOURCES;
+ } else {
+
+ IoInitializeIrp(FdoExtension->PrivateFdoData->PowerProcessIrp,
+ IoSizeOfIrp(stackSize),
+ stackSize);
+
+ return STATUS_SUCCESS;
+ }
+}
+
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClasspReleaseQueue()
+
+Routine Description:
+
+ This routine issues an internal device control command
+ to the port driver to release a frozen queue. The call
+ is issued asynchronously as ClassReleaseQueue will be invoked
+ from the IO completion DPC (and will have no context to
+ wait for a synchronous call to complete).
+
+ This routine must be called with the remove lock held.
+
+Arguments:
+
+ Fdo - The functional device object for the device with the frozen queue.
+
+ ReleaseQueueIrp - If this irp is supplied then the test to determine whether
+ a release queue request is in progress will be ignored.
+ The irp provided must be the IRP originally allocated
+ for release queue requests (so this parameter can only
+ really be provided by the release queue completion
+ routine.)
+
+Return Value:
+
+ None.
+
+--*/
+VOID
+ClasspReleaseQueue(
+ IN PDEVICE_OBJECT Fdo,
+ IN PIRP ReleaseQueueIrp OPTIONAL
+ )
+{
+ PIO_STACK_LOCATION irpStack;
+ PIRP irp;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+ PDEVICE_OBJECT lowerDevice;
+ PSTORAGE_REQUEST_BLOCK_HEADER srb;
+ KIRQL currentIrql;
+ ULONG function;
+
+ lowerDevice = fdoExtension->CommonExtension.LowerDeviceObject;
+
+ //
+ // we raise irql seperately so we're not swapped out or suspended
+ // while holding the release queue irp in this routine. this lets
+ // us release the spin lock before lowering irql.
+ //
+
+ KeRaiseIrql(DISPATCH_LEVEL, &currentIrql);
+
+ KeAcquireSpinLockAtDpcLevel(&(fdoExtension->ReleaseQueueSpinLock));
+
+ //
+ // make sure that if they passed us an irp, it matches our allocated irp.
+ //
+
+ NT_ASSERT((ReleaseQueueIrp == NULL) ||
+ (ReleaseQueueIrp == fdoExtension->PrivateFdoData->ReleaseQueueIrp));
+
+ //
+ // ASSERT that we've already allocated this. (should not occur)
+ // try to allocate it anyways, then finally bugcheck if
+ // there's still no memory...
+ //
+
+ NT_ASSERT(fdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated);
+ if (!fdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated) {
+ ClasspAllocateReleaseQueueIrp(fdoExtension);
+ }
+ if (!fdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated) {
+ KeBugCheckEx(SCSI_DISK_DRIVER_INTERNAL, 0x12, (ULONG_PTR)Fdo, 0x0, 0x0);
+ }
+
+ if ((fdoExtension->ReleaseQueueInProgress) && (ReleaseQueueIrp == NULL)) {
+
+ //
+ // Someone is already using the irp - just set the flag to indicate that
+ // we need to release the queue again.
+ //
+
+ fdoExtension->ReleaseQueueNeeded = TRUE;
+ KeReleaseSpinLockFromDpcLevel(&(fdoExtension->ReleaseQueueSpinLock));
+ KeLowerIrql(currentIrql);
+ return;
+
+ }
+
+ //
+ // Mark that there is a release queue in progress and drop the spinlock.
+ //
+
+ fdoExtension->ReleaseQueueInProgress = TRUE;
+ if (ReleaseQueueIrp) {
+ irp = ReleaseQueueIrp;
+ } else {
+ irp = fdoExtension->PrivateFdoData->ReleaseQueueIrp;
+ }
+
+ if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ srb = (PSTORAGE_REQUEST_BLOCK_HEADER)&(fdoExtension->PrivateFdoData->ReleaseQueueSrb.SrbEx);
+ } else {
+ srb = (PSTORAGE_REQUEST_BLOCK_HEADER)&(fdoExtension->ReleaseQueueSrb);
+ }
+
+ KeReleaseSpinLockFromDpcLevel(&(fdoExtension->ReleaseQueueSpinLock));
+
+ NT_ASSERT(irp != NULL);
+
+ irpStack = IoGetNextIrpStackLocation(irp);
+
+ irpStack->MajorFunction = IRP_MJ_SCSI;
+
+ SrbSetOriginalRequest(srb, irp);
+
+ //
+ // Store the SRB address in next stack for port driver.
+ //
+
+ irpStack->Parameters.Scsi.Srb = (PSCSI_REQUEST_BLOCK)srb;
+
+ //
+ // If this device is removable then flush the queue. This will also
+ // release it.
+ //
+
+ if (TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)){
+ function = SRB_FUNCTION_FLUSH_QUEUE;
+ }
+ else {
+ function = SRB_FUNCTION_RELEASE_QUEUE;
+ }
+
+ if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ ((PSTORAGE_REQUEST_BLOCK)srb)->SrbFunction = function;
+ } else {
+ srb->Function = (UCHAR)function;
+ }
+
+ ClassAcquireRemoveLock(Fdo, irp);
+
+ IoSetCompletionRoutine(irp,
+ ClassReleaseQueueCompletion,
+ Fdo,
+ TRUE,
+ TRUE,
+ TRUE);
+
+ IoCallDriver(lowerDevice, irp);
+
+ KeLowerIrql(currentIrql);
+
+ return;
+
+} // end ClassReleaseQueue()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassReleaseQueueCompletion()
+
+Routine Description:
+
+ This routine is called when an asynchronous I/O request
+ which was issused by the class driver completes. Examples of such requests
+ are release queue or START UNIT. This routine releases the queue if
+ necessary. It then frees the context and the IRP.
+
+Arguments:
+
+ DeviceObject - The device object for the logical unit; however since this
+ is the top stack location the value is NULL.
+
+ Irp - Supplies a pointer to the Irp to be processed.
+
+ Context - Supplies the context to be used to process this request.
+
+Return Value:
+
+ None.
+
+--*/
+NTSTATUS
+ClassReleaseQueueCompletion(
+ PDEVICE_OBJECT DeviceObject,
+ PIRP Irp,
+ PVOID Context
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
+ KIRQL oldIrql;
+
+ BOOLEAN releaseQueueNeeded;
+
+ if (Context == NULL) {
+ NT_ASSERT(Context != NULL);
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ DeviceObject = Context;
+
+ fdoExtension = DeviceObject->DeviceExtension;
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+
+ //
+ // Grab the spinlock and clear the release queue in progress flag so others
+ // can run. Save (and clear) the state of the release queue needed flag
+ // so that we can issue a new release queue outside the spinlock.
+ //
+
+ KeAcquireSpinLock(&(fdoExtension->ReleaseQueueSpinLock), &oldIrql);
+
+ releaseQueueNeeded = fdoExtension->ReleaseQueueNeeded;
+
+ fdoExtension->ReleaseQueueNeeded = FALSE;
+ fdoExtension->ReleaseQueueInProgress = FALSE;
+
+ KeReleaseSpinLock(&(fdoExtension->ReleaseQueueSpinLock), oldIrql);
+
+ //
+ // If we need a release queue then issue one now. Another processor may
+ // have already started one in which case we'll try to issue this one after
+ // it is done - but we should never recurse more than one deep.
+ //
+
+ if(releaseQueueNeeded) {
+ ClasspReleaseQueue(DeviceObject, Irp);
+ }
+
+ //
+ // Indicate the I/O system should stop processing the Irp completion.
+ //
+
+ return STATUS_MORE_PROCESSING_REQUIRED;
+
+} // ClassAsynchronousCompletion()
+
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassAcquireChildLock()
+
+Routine Description:
+
+ This routine acquires the lock protecting children PDOs. It may be
+ acquired recursively by the same thread, but must be release by the
+ thread once for each acquisition.
+
+Arguments:
+
+ FdoExtension - the device whose child list is protected.
+
+Return Value:
+
+ None
+
+--*/
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID
+ClassAcquireChildLock(
+ _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ )
+{
+ PAGED_CODE();
+
+ if(FdoExtension->ChildLockOwner != KeGetCurrentThread()) {
+ (VOID)KeWaitForSingleObject(&FdoExtension->ChildLock,
+ Executive, KernelMode,
+ FALSE, NULL);
+
+ NT_ASSERT(FdoExtension->ChildLockOwner == NULL);
+ NT_ASSERT(FdoExtension->ChildLockAcquisitionCount == 0);
+
+ FdoExtension->ChildLockOwner = KeGetCurrentThread();
+ } else {
+ NT_ASSERT(FdoExtension->ChildLockAcquisitionCount != 0);
+ }
+
+ FdoExtension->ChildLockAcquisitionCount++;
+ return;
+}
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassReleaseChildLock() ISSUE-2000/02/18-henrygab - not documented
+
+Routine Description:
+
+ This routine releases the lock protecting children PDOs. It must be
+ called once for each time ClassAcquireChildLock was called.
+
+Arguments:
+
+ FdoExtension - the device whose child list is protected
+
+Return Value:
+
+ None.
+
+--*/
+VOID
+ClassReleaseChildLock(
+ _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ )
+{
+ NT_ASSERT(FdoExtension->ChildLockOwner == KeGetCurrentThread());
+ NT_ASSERT(FdoExtension->ChildLockAcquisitionCount != 0);
+
+ FdoExtension->ChildLockAcquisitionCount -= 1;
+
+ if(FdoExtension->ChildLockAcquisitionCount == 0) {
+ FdoExtension->ChildLockOwner = NULL;
+ KeSetEvent(&FdoExtension->ChildLock, IO_NO_INCREMENT, FALSE);
+ }
+
+ return;
+} // end ClassReleaseChildLock(
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassAddChild()
+
+Routine Description:
+
+ This routine will insert a new child into the head of the child list.
+
+Arguments:
+
+ Parent - the child's parent (contains the head of the list)
+ Child - the child to be inserted.
+ AcquireLock - whether the child lock should be acquired (TRUE) or whether
+ it's already been acquired by or on behalf of the caller
+ (FALSE).
+
+Return Value:
+
+ None.
+
+--*/
+VOID
+ClassAddChild(
+ _In_ PFUNCTIONAL_DEVICE_EXTENSION Parent,
+ _In_ PPHYSICAL_DEVICE_EXTENSION Child,
+ _In_ BOOLEAN AcquireLock
+ )
+{
+ if(AcquireLock) {
+ ClassAcquireChildLock(Parent);
+ }
+
+ #if DBG
+ //
+ // Make sure this child's not already in the list.
+ //
+ {
+ PPHYSICAL_DEVICE_EXTENSION testChild;
+
+ for (testChild = Parent->CommonExtension.ChildList;
+ testChild != NULL;
+ testChild = testChild->CommonExtension.ChildList) {
+
+ NT_ASSERT(testChild != Child);
+ }
+ }
+ #endif
+
+ Child->CommonExtension.ChildList = Parent->CommonExtension.ChildList;
+ Parent->CommonExtension.ChildList = Child;
+
+ if(AcquireLock) {
+ ClassReleaseChildLock(Parent);
+ }
+ return;
+} // end ClassAddChild()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassRemoveChild()
+
+Routine Description:
+
+ This routine will remove a child from the child list.
+
+Arguments:
+
+ Parent - the parent to be removed from.
+
+ Child - the child to be removed or NULL if the first child should be
+ removed.
+
+ AcquireLock - whether the child lock should be acquired (TRUE) or whether
+ it's already been acquired by or on behalf of the caller
+ (FALSE).
+
+Return Value:
+
+ A pointer to the child which was removed or NULL if no such child could
+ be found in the list (or if Child was NULL but the list is empty).
+
+--*/
+PPHYSICAL_DEVICE_EXTENSION
+ClassRemoveChild(
+ IN PFUNCTIONAL_DEVICE_EXTENSION Parent,
+ IN PPHYSICAL_DEVICE_EXTENSION Child,
+ IN BOOLEAN AcquireLock
+ )
+{
+ if(AcquireLock) {
+ ClassAcquireChildLock(Parent);
+ }
+
+ TRY {
+ PCOMMON_DEVICE_EXTENSION previousChild = &Parent->CommonExtension;
+
+ //
+ // If the list is empty then bail out now.
+ //
+
+ if(Parent->CommonExtension.ChildList == NULL) {
+ Child = NULL;
+ LEAVE;
+ }
+
+ //
+ // If the caller specified a child then find the child object before
+ // it. If none was specified then the FDO is the child object before
+ // the one we want to remove.
+ //
+
+ if(Child != NULL) {
+
+ //
+ // Scan through the child list to find the entry which points to
+ // this one.
+ //
+
+ do {
+ NT_ASSERT(previousChild != &Child->CommonExtension);
+
+ if(previousChild->ChildList == Child) {
+ break;
+ }
+
+ previousChild = &previousChild->ChildList->CommonExtension;
+ } while(previousChild != NULL);
+
+ if(previousChild == NULL) {
+ Child = NULL;
+ LEAVE;
+ }
+ }
+
+ //
+ // Save the next child away then unlink it from the list.
+ //
+
+ Child = previousChild->ChildList;
+ previousChild->ChildList = Child->CommonExtension.ChildList;
+ Child->CommonExtension.ChildList = NULL;
+
+ } FINALLY {
+ if(AcquireLock) {
+ ClassReleaseChildLock(Parent);
+ }
+ }
+ return Child;
+} // end ClassRemoveChild()
+
+
+/*++
+
+ ISSUE-2000/02/20-henrygab Not documented ClasspRetryRequestDpc
+
+--*/
+VOID
+ClasspRetryRequestDpc(
+ IN PKDPC Dpc,
+ IN PVOID DeferredContext,
+ IN PVOID Arg1,
+ IN PVOID Arg2
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
+ PCOMMON_DEVICE_EXTENSION commonExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData;
+ PCLASS_RETRY_INFO retryList;
+ KIRQL irql;
+ PDEVICE_OBJECT DeviceObject = (PDEVICE_OBJECT)DeferredContext;
+
+ UNREFERENCED_PARAMETER(Dpc);
+ UNREFERENCED_PARAMETER(Arg1);
+ UNREFERENCED_PARAMETER(Arg2);
+
+ if (DeferredContext == NULL) {
+ NT_ASSERT(DeferredContext != NULL);
+ return;
+ }
+
+ commonExtension = DeviceObject->DeviceExtension;
+ NT_ASSERT(commonExtension->IsFdo);
+ fdoExtension = DeviceObject->DeviceExtension;
+ fdoData = fdoExtension->PrivateFdoData;
+
+
+ KeAcquireSpinLock(&fdoData->Retry.Lock, &irql);
+ {
+ LARGE_INTEGER now;
+ KeQueryTickCount(&now);
+
+ //
+ // if CurrentTick is less than now
+ // fire another DPC
+ // else
+ // retry entire list
+ // endif
+ //
+
+ if (now.QuadPart < fdoData->Retry.Tick.QuadPart) {
+
+ ClasspRetryDpcTimer(fdoData);
+ retryList = NULL;
+
+ } else {
+
+ retryList = fdoData->Retry.ListHead;
+ fdoData->Retry.ListHead = NULL;
+ fdoData->Retry.Delta.QuadPart = (LONGLONG)0;
+ fdoData->Retry.Tick.QuadPart = (LONGLONG)0;
+
+ }
+ }
+ KeReleaseSpinLock(&fdoData->Retry.Lock, irql);
+
+ while (retryList != NULL) {
+
+ PIRP irp;
+
+
+ irp = CONTAINING_RECORD(retryList, IRP, Tail.Overlay.DriverContext[0]);
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassRetry: -- %p\n", irp));
+ retryList = retryList->Next;
+ #if DBG
+ irp->Tail.Overlay.DriverContext[0] = ULongToPtr(0xdddddddd); // invalidate data
+ irp->Tail.Overlay.DriverContext[1] = ULongToPtr(0xdddddddd); // invalidate data
+ irp->Tail.Overlay.DriverContext[2] = ULongToPtr(0xdddddddd); // invalidate data
+ irp->Tail.Overlay.DriverContext[3] = ULongToPtr(0xdddddddd); // invalidate data
+ #endif
+
+
+ if (NO_REMOVE == InterlockedCompareExchange((volatile LONG *)&commonExtension->IsRemoved, REMOVE_PENDING, REMOVE_PENDING)) {
+
+ IoCallDriver(commonExtension->LowerDeviceObject, irp);
+
+ } else {
+
+ PIO_STACK_LOCATION irpStack;
+
+ //
+ // Ensure that we don't skip a completion routine (equivalent of sending down a request
+ // to a device after it has received a remove and it completes the request. We need to
+ // mimic that behavior here).
+ //
+ IoSetNextIrpStackLocation(irp);
+
+ irpStack = IoGetCurrentIrpStackLocation(irp);
+
+ if (irpStack->MajorFunction == IRP_MJ_SCSI) {
+
+ PSCSI_REQUEST_BLOCK srb = irpStack->Parameters.Scsi.Srb;
+
+ if (srb) {
+ srb->SrbStatus = SRB_STATUS_NO_DEVICE;
+ }
+ }
+
+ //
+ // Ensure that no retries will take place. This takes care of requests that are either
+ // not IRP_MJ_SCSI or for ones where the SRB was passed in to the completion routine
+ // as a context as opposed to an argument on the IRP stack location.
+ //
+ irpStack->Parameters.Others.Argument4 = (PVOID)0;
+
+ irp->IoStatus.Status = STATUS_NO_SUCH_DEVICE;
+ irp->IoStatus.Information = 0;
+ IoCompleteRequest(irp, IO_NO_INCREMENT);
+ }
+
+ }
+ return;
+
+} // end ClasspRetryRequestDpc()
+
+/*++
+
+ ISSUE-2000/02/20-henrygab Not documented ClassRetryRequest
+
+--*/
+VOID
+ClassRetryRequest(
+ IN PDEVICE_OBJECT SelfDeviceObject,
+ IN PIRP Irp,
+ _In_ _In_range_(0,MAXIMUM_RETRY_FOR_SINGLE_IO_IN_100NS_UNITS) // 100 seconds; already an assert on this...
+ IN LONGLONG TimeDelta100ns // in 100ns units
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData;
+ PCLASS_RETRY_INFO retryInfo;
+ LARGE_INTEGER delta;
+ KIRQL irql;
+
+ //
+ // this checks we aren't destroying irps
+ //
+ NT_ASSERT(sizeof(CLASS_RETRY_INFO) <= (4*sizeof(PVOID)));
+
+ fdoExtension = SelfDeviceObject->DeviceExtension;
+
+ if (!fdoExtension->CommonExtension.IsFdo) {
+
+ //
+ // this debug print/assertion should ALWAYS be investigated.
+ // ClassRetryRequest can currently only be used by FDO's
+ //
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClassRetryRequestEx: LOST IRP %p\n", Irp));
+ NT_ASSERT(!"ClassRetryRequestEx Called From PDO? LOST IRP");
+ return;
+
+ }
+
+ fdoData = fdoExtension->PrivateFdoData;
+
+ if (TimeDelta100ns < 0) {
+ NT_ASSERT(!"ClassRetryRequest - must use positive delay");
+ TimeDelta100ns *= -1;
+ }
+
+ /*
+ * We are going to queue the irp and send it down in a timer DPC.
+ * This means that we may be causing the irp to complete on a different thread than the issuing thread.
+ * So mark the irp pending.
+ */
+ IoMarkIrpPending(Irp);
+
+ //
+ // prepare what we can out of the loop
+ //
+
+ retryInfo = (PCLASS_RETRY_INFO)(&Irp->Tail.Overlay.DriverContext[0]);
+ RtlZeroMemory(retryInfo, sizeof(CLASS_RETRY_INFO));
+
+ delta.QuadPart = (TimeDelta100ns / fdoData->Retry.Granularity);
+ if (TimeDelta100ns % fdoData->Retry.Granularity) {
+ delta.QuadPart ++; // round up to next tick
+ }
+ if (delta.QuadPart == (LONGLONG)0) {
+ delta.QuadPart = MINIMUM_RETRY_UNITS;
+ }
+
+ //
+ // now determine if we should fire another DPC or not
+ //
+
+ KeAcquireSpinLock(&fdoData->Retry.Lock, &irql);
+
+ //
+ // always add request to the list
+ //
+
+ retryInfo->Next = fdoData->Retry.ListHead;
+ fdoData->Retry.ListHead = retryInfo;
+
+ if (fdoData->Retry.Delta.QuadPart == (LONGLONG)0) {
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassRetry: +++ %p\n", Irp));
+
+ //
+ // must be exactly one item on list
+ //
+
+ NT_ASSERT(fdoData->Retry.ListHead != NULL);
+ NT_ASSERT(fdoData->Retry.ListHead->Next == NULL);
+
+ //
+ // if currentDelta is zero, always fire a DPC
+ //
+
+ KeQueryTickCount(&fdoData->Retry.Tick);
+ fdoData->Retry.Tick.QuadPart += delta.QuadPart;
+ fdoData->Retry.Delta.QuadPart = delta.QuadPart;
+ ClasspRetryDpcTimer(fdoData);
+
+ } else if (delta.QuadPart > fdoData->Retry.Delta.QuadPart) {
+
+ //
+ // if delta is greater than the list's current delta,
+ // increase the DPC handling time by difference
+ // and update the delta to new larger value
+ // allow the DPC to re-fire itself if needed
+ //
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassRetry: ++ %p\n", Irp));
+
+ //
+ // must be at least two items on list
+ //
+
+ NT_ASSERT(fdoData->Retry.ListHead != NULL);
+ NT_ASSERT(fdoData->Retry.ListHead->Next != NULL);
+
+ fdoData->Retry.Tick.QuadPart -= fdoData->Retry.Delta.QuadPart;
+ fdoData->Retry.Tick.QuadPart += delta.QuadPart;
+
+ fdoData->Retry.Delta.QuadPart = delta.QuadPart;
+
+ } else {
+
+ //
+ // just inserting it on the list was enough
+ //
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassRetry: ++ %p\n", Irp));
+
+ }
+
+
+ KeReleaseSpinLock(&fdoData->Retry.Lock, irql);
+
+
+} // end ClassRetryRequest()
+
+/*++
+
+ ISSUE-2000/02/20-henrygab Not documented ClasspRetryDpcTimer
+
+--*/
+VOID
+ClasspRetryDpcTimer(
+ IN PCLASS_PRIVATE_FDO_DATA FdoData
+ )
+{
+ LARGE_INTEGER fire;
+
+ NT_ASSERT(FdoData->Retry.Tick.QuadPart != (LONGLONG)0);
+ NT_ASSERT(FdoData->Retry.ListHead != NULL); // never fire an empty list
+
+ //
+ // fire == (CurrentTick - now) * (100ns per tick)
+ //
+ // NOTE: Overflow is nearly impossible and is ignored here
+ //
+
+ KeQueryTickCount(&fire);
+ fire.QuadPart = FdoData->Retry.Tick.QuadPart - fire.QuadPart;
+ fire.QuadPart *= FdoData->Retry.Granularity;
+
+ //
+ // fire is now multiples of 100ns until should fire the timer.
+ // if timer should already have expired, or would fire too quickly,
+ // fire it in some arbitrary number of ticks to prevent infinitely
+ // recursing.
+ //
+
+ if (fire.QuadPart < MINIMUM_RETRY_UNITS) {
+ fire.QuadPart = MINIMUM_RETRY_UNITS;
+ }
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
+ "ClassRetry: ======= %I64x ticks\n",
+ fire.QuadPart));
+
+ //
+ // must use negative to specify relative time to fire
+ //
+
+ fire.QuadPart = fire.QuadPart * ((LONGLONG)-1);
+
+ //
+ // set the timer, since this is the first addition
+ //
+
+ KeSetTimerEx(&FdoData->Retry.Timer, fire, 0, &FdoData->Retry.Dpc);
+
+ return;
+} // end ClasspRetryDpcTimer()
+
+NTSTATUS
+ClasspInitializeHotplugInfo(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ )
+{
+ PCLASS_PRIVATE_FDO_DATA fdoData = FdoExtension->PrivateFdoData;
+ DEVICE_REMOVAL_POLICY deviceRemovalPolicy = 0;
+ NTSTATUS status;
+ ULONG resultLength = 0;
+ ULONG writeCacheOverride;
+
+ PAGED_CODE();
+
+ //
+ // start with some default settings
+ //
+ RtlZeroMemory(&(fdoData->HotplugInfo), sizeof(STORAGE_HOTPLUG_INFO));
+
+ //
+ // set the size (aka version)
+ //
+
+ fdoData->HotplugInfo.Size = sizeof(STORAGE_HOTPLUG_INFO);
+
+ //
+ // set if the device has removable media
+ //
+
+ if (FdoExtension->DeviceDescriptor->RemovableMedia) {
+ fdoData->HotplugInfo.MediaRemovable = TRUE;
+ } else {
+ fdoData->HotplugInfo.MediaRemovable = FALSE;
+ }
+
+ //
+ // this refers to devices which, for reasons not yet understood,
+ // do not fail PREVENT_MEDIA_REMOVAL requests even though they
+ // have no way to lock the media into the drive. this allows
+ // the filesystems to turn off delayed-write caching for these
+ // devices as well.
+ //
+
+ if (TEST_FLAG(FdoExtension->PrivateFdoData->HackFlags,
+ FDO_HACK_CANNOT_LOCK_MEDIA)) {
+ fdoData->HotplugInfo.MediaHotplug = TRUE;
+ } else {
+ fdoData->HotplugInfo.MediaHotplug = FALSE;
+ }
+
+ //
+ // Query the default removal policy from the kernel
+ //
+
+ status = IoGetDeviceProperty(FdoExtension->LowerPdo,
+ DevicePropertyRemovalPolicy,
+ sizeof(DEVICE_REMOVAL_POLICY),
+ (PVOID)&deviceRemovalPolicy,
+ &resultLength);
+ if (!NT_SUCCESS(status)) {
+ return status;
+ }
+
+ if (resultLength != sizeof(DEVICE_REMOVAL_POLICY)) {
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ //
+ // Look into the registry to see if the user has chosen
+ // to override the default setting for the removal policy.
+ // User can override only if the default removal policy is
+ // orderly or suprise removal.
+
+ if ((deviceRemovalPolicy == RemovalPolicyExpectOrderlyRemoval) ||
+ (deviceRemovalPolicy == RemovalPolicyExpectSurpriseRemoval)) {
+
+ DEVICE_REMOVAL_POLICY userRemovalPolicy = 0;
+
+ ClassGetDeviceParameter(FdoExtension,
+ CLASSP_REG_SUBKEY_NAME,
+ CLASSP_REG_REMOVAL_POLICY_VALUE_NAME,
+ (PULONG)&userRemovalPolicy);
+
+ //
+ // Validate the override value and use it only if it is an
+ // allowed value.
+ //
+ if ((userRemovalPolicy == RemovalPolicyExpectOrderlyRemoval) ||
+ (userRemovalPolicy == RemovalPolicyExpectSurpriseRemoval)) {
+ deviceRemovalPolicy = userRemovalPolicy;
+ }
+ }
+
+ //
+ // use this info to set the DeviceHotplug setting
+ // don't rely on DeviceCapabilities, since it can't properly
+ // determine device relations, etc. let the kernel figure this
+ // stuff out instead.
+ //
+
+ if (deviceRemovalPolicy == RemovalPolicyExpectSurpriseRemoval) {
+ fdoData->HotplugInfo.DeviceHotplug = TRUE;
+ } else {
+ fdoData->HotplugInfo.DeviceHotplug = FALSE;
+ }
+
+ //
+ // this refers to the *filesystem* caching, but has to be included
+ // here since it's a per-device setting. this may change to be
+ // stored by the system in the future.
+ //
+
+ writeCacheOverride = FALSE;
+ ClassGetDeviceParameter(FdoExtension,
+ CLASSP_REG_SUBKEY_NAME,
+ CLASSP_REG_WRITE_CACHE_VALUE_NAME,
+ &writeCacheOverride);
+
+ if (writeCacheOverride) {
+ fdoData->HotplugInfo.WriteCacheEnableOverride = TRUE;
+ } else {
+ fdoData->HotplugInfo.WriteCacheEnableOverride = FALSE;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+VOID
+ClasspScanForClassHacks(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN ULONG_PTR Data
+ )
+{
+ PAGED_CODE();
+
+ //
+ // remove invalid flags and save
+ //
+
+ CLEAR_FLAG(Data, FDO_HACK_INVALID_FLAGS);
+ SET_FLAG(FdoExtension->PrivateFdoData->HackFlags, Data);
+ return;
+}
+
+VOID
+ClasspScanForSpecialInRegistry(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ )
+{
+ HANDLE deviceParameterHandle; // device instance key
+ HANDLE classParameterHandle; // classpnp subkey
+ OBJECT_ATTRIBUTES objectAttributes = {0};
+ UNICODE_STRING subkeyName;
+ NTSTATUS status;
+
+ //
+ // seeded in the ENUM tree by ClassInstaller
+ //
+ ULONG deviceHacks;
+ RTL_QUERY_REGISTRY_TABLE queryTable[2] = {0}; // null terminated array
+
+ PAGED_CODE();
+
+ deviceParameterHandle = NULL;
+ classParameterHandle = NULL;
+ deviceHacks = 0;
+
+ status = IoOpenDeviceRegistryKey(FdoExtension->LowerPdo,
+ PLUGPLAY_REGKEY_DEVICE,
+ KEY_WRITE,
+ &deviceParameterHandle
+ );
+
+ if (!NT_SUCCESS(status)) {
+ goto cleanupScanForSpecial;
+ }
+
+ RtlInitUnicodeString(&subkeyName, CLASSP_REG_SUBKEY_NAME);
+ InitializeObjectAttributes(&objectAttributes,
+ &subkeyName,
+ OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+ deviceParameterHandle,
+ NULL
+ );
+
+ status = ZwOpenKey( &classParameterHandle,
+ KEY_READ,
+ &objectAttributes
+ );
+
+ if (!NT_SUCCESS(status)) {
+ goto cleanupScanForSpecial;
+ }
+
+ //
+ // Setup the structure to read
+ //
+
+ queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
+ queryTable[0].Name = CLASSP_REG_HACK_VALUE_NAME;
+ queryTable[0].EntryContext = &deviceHacks;
+ queryTable[0].DefaultType = REG_DWORD;
+ queryTable[0].DefaultData = &deviceHacks;
+ queryTable[0].DefaultLength = 0;
+
+ //
+ // read values
+ //
+
+ status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
+ (PWSTR)classParameterHandle,
+ &queryTable[0],
+ NULL,
+ NULL
+ );
+ if (!NT_SUCCESS(status)) {
+ goto cleanupScanForSpecial;
+ }
+
+ //
+ // remove unknown values and save...
+ //
+
+ CLEAR_FLAG(deviceHacks, FDO_HACK_INVALID_FLAGS);
+ SET_FLAG(FdoExtension->PrivateFdoData->HackFlags, deviceHacks);
+
+
+cleanupScanForSpecial:
+
+ if (deviceParameterHandle) {
+ ZwClose(deviceParameterHandle);
+ }
+
+ if (classParameterHandle) {
+ ZwClose(classParameterHandle);
+ }
+
+ //
+ // we should modify the system hive to include another key for us to grab
+ // settings from. in this case: Classpnp\HackFlags
+ //
+ // the use of a DWORD value for the HackFlags allows 32 hacks w/o
+ // significant use of the registry, and also reduces OEM exposure.
+ //
+ // definition of bit flags:
+ // 0x00000001 -- Device succeeds PREVENT_MEDIUM_REMOVAL, but
+ // cannot actually prevent removal.
+ // 0x00000002 -- Device hard-hangs or times out for GESN requests.
+ // 0xfffffffc -- Currently reserved, may be used later.
+ //
+
+ return;
+}
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClasspUpdateDiskProperties()
+
+Routine Description:
+
+ This routine will send IOCTL_DISK_UPDATE_PROPERTIES to top of stack - Partition Manager
+ to invalidate the cached geometry.
+
+Arguments:
+
+ Fdo - The device object whose capacity needs to be verified.
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+
+VOID
+ClasspUpdateDiskProperties(
+ IN PDEVICE_OBJECT Fdo,
+ IN PVOID Context
+ )
+{
+ PDEVICE_OBJECT topOfStack;
+ IO_STATUS_BLOCK ioStatus;
+ NTSTATUS status = STATUS_SUCCESS;
+ KEVENT event;
+ PIRP irp = NULL;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
+ PIO_WORKITEM WorkItem = (PIO_WORKITEM)Context;
+
+ KeInitializeEvent(&event, SynchronizationEvent, FALSE);
+
+ topOfStack = IoGetAttachedDeviceReference(Fdo);
+
+ //
+ // Send down irp to update properties
+ //
+
+ irp = IoBuildDeviceIoControlRequest(
+ IOCTL_DISK_UPDATE_PROPERTIES,
+ topOfStack,
+ NULL,
+ 0,
+ NULL,
+ 0,
+ FALSE,
+ &event,
+ &ioStatus);
+
+ if (irp != NULL) {
+
+
+ status = IoCallDriver(topOfStack, irp);
+ if (status == STATUS_PENDING) {
+ (VOID)KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
+ status = ioStatus.Status;
+ }
+
+ } else {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ InterlockedExchange((volatile LONG *)&fdoData->UpdateDiskPropertiesWorkItemActive, 0);
+
+ if (!NT_SUCCESS(status)) {
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT, "ClasspUpdateDiskProperties: Disk property update for fdo %p failed with status 0x%X.\n", Fdo, status));
+ } else {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT, "ClasspUpdateDiskProperties: Drive capacity has changed for %p.\n", Fdo));
+ }
+ ObDereferenceObject(topOfStack);
+
+ if (WorkItem != NULL) {
+ IoFreeWorkItem(WorkItem);
+ }
+
+ return;
+}
+
+BOOLEAN
+InterpretSenseInfoWithoutHistory(
+ _In_ PDEVICE_OBJECT Fdo,
+ _In_opt_ PIRP OriginalRequest,
+ _In_ PSCSI_REQUEST_BLOCK Srb,
+ UCHAR MajorFunctionCode,
+ ULONG IoDeviceCode,
+ ULONG PreviousRetryCount,
+ _Out_ NTSTATUS * Status,
+ _Out_opt_ _Deref_out_range_(0,MAXIMUM_RETRY_FOR_SINGLE_IO_IN_100NS_UNITS)
+ LONGLONG * RetryIn100nsUnits
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
+ LONGLONG tmpRetry = 0;
+ BOOLEAN retry = FALSE;
+
+ if (fdoData->InterpretSenseInfo != NULL)
+ {
+ SCSI_REQUEST_BLOCK tempSrb = {0};
+ PSCSI_REQUEST_BLOCK srbPtr = Srb;
+
+ // SAL annotations and ClassInitializeEx() both validate this
+ NT_ASSERT(fdoData->InterpretSenseInfo->Interpret != NULL);
+
+ //
+ // If class driver does not support extended SRB and this is
+ // an extended SRB, convert to legacy SRB and pass to class
+ // driver.
+ //
+ if ((Srb->Function == SRB_FUNCTION_STORAGE_REQUEST_BLOCK) &&
+ ((fdoExtension->CommonExtension.DriverExtension->SrbSupport &
+ CLASS_SRB_STORAGE_REQUEST_BLOCK) == 0)) {
+ ClasspConvertToScsiRequestBlock(&tempSrb, (PSTORAGE_REQUEST_BLOCK)Srb);
+ srbPtr = &tempSrb;
+ }
+
+ retry = fdoData->InterpretSenseInfo->Interpret(Fdo,
+ OriginalRequest,
+ srbPtr,
+ MajorFunctionCode,
+ IoDeviceCode,
+ PreviousRetryCount,
+ NULL,
+ Status,
+ &tmpRetry);
+ }
+ else
+ {
+ ULONG seconds = 0;
+
+ retry = ClassInterpretSenseInfo(Fdo,
+ Srb,
+ MajorFunctionCode,
+ IoDeviceCode,
+ PreviousRetryCount,
+ Status,
+ &seconds);
+ tmpRetry = ((LONGLONG)seconds) * 1000 * 1000 * 10;
+ }
+
+
+ if (RetryIn100nsUnits != NULL)
+ {
+ *RetryIn100nsUnits = tmpRetry;
+ }
+ return retry;
+}
+
+VOID
+ClasspGetInquiryVpdSupportInfo(
+ _Inout_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ )
+{
+ NTSTATUS status;
+ PCOMMON_DEVICE_EXTENSION commonExtension = (PCOMMON_DEVICE_EXTENSION)FdoExtension;
+ SCSI_REQUEST_BLOCK srb = {0};
+ PCDB cdb;
+ PVPD_SUPPORTED_PAGES_PAGE supportedPages = NULL;
+ UCHAR bufferLength = VPD_MAX_BUFFER_SIZE;
+ ULONG allocationBufferLength = bufferLength;
+ UCHAR srbExBuffer[CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE] = {0};
+ PSTORAGE_REQUEST_BLOCK_HEADER srbHeader;
+
+#if defined(_ARM_) || defined(_ARM64_)
+ //
+ // ARM has specific alignment requirements, although this will not have a functional impact on x86 or amd64
+ // based platforms. We are taking the conservative approach here.
+ //
+ allocationBufferLength = ALIGN_UP_BY(allocationBufferLength,KeGetRecommendedSharedDataAlignment());
+ supportedPages = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
+ allocationBufferLength,
+ '3CcS'
+ );
+
+#else
+ supportedPages = ExAllocatePoolWithTag(NonPagedPoolNx,
+ bufferLength,
+ '3CcS'
+ );
+#endif
+
+ if (supportedPages == NULL) {
+ // memory allocation failure.
+ return;
+ }
+
+ RtlZeroMemory(supportedPages, allocationBufferLength);
+
+ // prepare the Srb
+ if (FdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+
+ #pragma prefast(suppress:26015, "InitializeStorageRequestBlock ensures buffer access is bounded")
+ status = InitializeStorageRequestBlock((PSTORAGE_REQUEST_BLOCK)srbExBuffer,
+ STORAGE_ADDRESS_TYPE_BTL8,
+ sizeof(srbExBuffer),
+ 1,
+ SrbExDataTypeScsiCdb16);
+
+ if (!NT_SUCCESS(status)) {
+ //
+ // Should not happen. Revert to legacy SRB.
+ NT_ASSERT(FALSE);
+ srb.Length = SCSI_REQUEST_BLOCK_SIZE;
+ srb.Function = SRB_FUNCTION_EXECUTE_SCSI;
+ srbHeader = (PSTORAGE_REQUEST_BLOCK_HEADER)&srb;
+ } else {
+ ((PSTORAGE_REQUEST_BLOCK)srbExBuffer)->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
+ srbHeader = (PSTORAGE_REQUEST_BLOCK_HEADER)srbExBuffer;
+ }
+
+ } else {
+ srb.Length = SCSI_REQUEST_BLOCK_SIZE;
+ srb.Function = SRB_FUNCTION_EXECUTE_SCSI;
+ srbHeader = (PSTORAGE_REQUEST_BLOCK_HEADER)&srb;
+ }
+
+ SrbSetTimeOutValue(srbHeader, FdoExtension->TimeOutValue);
+ SrbSetRequestTag(srbHeader, SP_UNTAGGED);
+ SrbSetRequestAttribute(srbHeader, SRB_SIMPLE_TAG_REQUEST);
+ SrbAssignSrbFlags(srbHeader, FdoExtension->SrbFlags);
+ SrbSetCdbLength(srbHeader, 6);
+
+ cdb = SrbGetCdb(srbHeader);
+ cdb->CDB6INQUIRY3.OperationCode = SCSIOP_INQUIRY;
+ cdb->CDB6INQUIRY3.EnableVitalProductData = 1; //EVPD bit
+ cdb->CDB6INQUIRY3.PageCode = VPD_SUPPORTED_PAGES;
+ cdb->CDB6INQUIRY3.AllocationLength = bufferLength; //AllocationLength field in CDB6INQUIRY3 is only one byte.
+
+ status = ClassSendSrbSynchronous(commonExtension->DeviceObject,
+ (PSCSI_REQUEST_BLOCK)srbHeader,
+ supportedPages,
+ allocationBufferLength,
+ FALSE);
+
+ //
+ // Handle the case where we get back STATUS_DATA_OVERRUN b/c the input
+ // buffer was larger than necessary.
+ //
+ if (status == STATUS_DATA_OVERRUN && SrbGetDataTransferLength(srbHeader) < bufferLength)
+ {
+ status = STATUS_SUCCESS;
+ }
+
+ if ( NT_SUCCESS(status) &&
+ (supportedPages->PageLength > 0) &&
+ (supportedPages->SupportedPageList[0] > 0) ) {
+ // ataport treats all INQUIRY command as standard INQUIRY command, thus fills invalid info
+ // If VPD INQUIRY is supported, the first page reported (additional length field for standard INQUIRY data) should be '00'
+ status = STATUS_NOT_SUPPORTED;
+ }
+
+ if (NT_SUCCESS(status)) {
+ int i;
+
+ for (i = 0; i < supportedPages->PageLength; i++) {
+ if ( (i > 0) && (supportedPages->SupportedPageList[i] <= supportedPages->SupportedPageList[i - 1]) ) {
+ // shall be in ascending order beginning with page code 00h.
+ FdoExtension->FunctionSupportInfo->ValidInquiryPages.BlockDeviceRODLimits = FALSE;
+ FdoExtension->FunctionSupportInfo->ValidInquiryPages.BlockLimits = FALSE;
+ FdoExtension->FunctionSupportInfo->ValidInquiryPages.BlockDeviceCharacteristics = FALSE;
+ FdoExtension->FunctionSupportInfo->ValidInquiryPages.LBProvisioning = FALSE;
+
+
+ break;
+ }
+ switch (supportedPages->SupportedPageList[i]) {
+ case VPD_THIRD_PARTY_COPY:
+ FdoExtension->FunctionSupportInfo->ValidInquiryPages.BlockDeviceRODLimits = TRUE;
+ break;
+
+ case VPD_BLOCK_LIMITS:
+ FdoExtension->FunctionSupportInfo->ValidInquiryPages.BlockLimits = TRUE;
+ break;
+
+ case VPD_BLOCK_DEVICE_CHARACTERISTICS:
+ FdoExtension->FunctionSupportInfo->ValidInquiryPages.BlockDeviceCharacteristics = TRUE;
+ break;
+
+ case VPD_LOGICAL_BLOCK_PROVISIONING:
+ FdoExtension->FunctionSupportInfo->ValidInquiryPages.LBProvisioning = TRUE;
+ break;
+
+ }
+ }
+ }
+
+ ExFreePool(supportedPages);
+
+ return;
+}
+
+//
+// ClasspGetLBProvisioningInfo
+//
+// Description: This function does the work to get Logical Block Provisioning
+// (LBP) info for a given LUN, including UNMAP support and parameters. It
+// will attempt to get both the Logical Block Provisioning (0xB2) VPD page
+// and the Block Limits (0xB0) VPD page and cache the relevant values in
+// the given FDO extension.
+//
+// After calling this function, you can use the ClasspIsThinProvisioned()
+// function to determine if the device is thinly provisioned and the
+// ClasspSupportsUnmap() function to determine if the device supports the
+// UNMAP command.
+//
+// Arguments:
+// - FdoExtension: The FDO extension associated with the LUN for which Thin
+// Provisioning info is desired. The Thin Provisioning info is stored
+// in the FunctionSupportInfo member of this FDO extension.
+//
+// Returns:
+// - STATUS_INVALID_PARAMETER if the given FDO extension has not been
+// allocated properly.
+// - STATUS_INSUFFICIENT_RESOURCES if this function was unable to allocate
+// an SRB used to get the necessary VPD pages.
+// - STATUS_SUCCESS in all other cases. If any of the incidental functions
+// don't return STATUS_SUCCESS this function will just assume Thin
+// Provisioning is not enabled and return STATUS_SUCCESS.
+//
+NTSTATUS
+ClasspGetLBProvisioningInfo(
+ _Inout_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ )
+{
+ PSCSI_REQUEST_BLOCK srb = NULL;
+ ULONG srbSize = 0;
+
+ //
+ // Make sure we actually have data structures to work with.
+ //
+ if (FdoExtension == NULL ||
+ FdoExtension->FunctionSupportInfo == NULL) {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // Allocate an SRB for querying the device for LBP-related info if either
+ // the Logical Block Provisioning (0xB2) or Block Limits (0xB0) VPD page
+ // exists.
+ //
+ if ((FdoExtension->FunctionSupportInfo->ValidInquiryPages.LBProvisioning == TRUE) ||
+ (FdoExtension->FunctionSupportInfo->ValidInquiryPages.BlockLimits == TRUE)) {
+
+ if ((FdoExtension->AdapterDescriptor != NULL) &&
+ (FdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK)) {
+ srbSize = CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE;
+ } else {
+ srbSize = sizeof(SCSI_REQUEST_BLOCK);
+ }
+
+ srb = ExAllocatePoolWithTag(NonPagedPoolNx, srbSize, '0DcS');
+
+ if (srb == NULL) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+ }
+
+ //
+ // Get the Logical Block Provisioning VPD page (0xB2). This function will
+ // set some default values if the LBP VPD page is not supported.
+ //
+ ClasspDeviceGetLBProvisioningVPDPage(FdoExtension->DeviceObject, srb);
+
+
+ //
+ // Get the Block Limits VPD page (0xB0), which may override the default
+ // UNMAP parameter values set above.
+ //
+ ClasspDeviceGetBlockLimitsVPDPage(FdoExtension,
+ srb,
+ srbSize,
+ &FdoExtension->FunctionSupportInfo->BlockLimitsData);
+
+ FREE_POOL(srb);
+
+ return STATUS_SUCCESS;
+}
+
+
+
+_IRQL_requires_(PASSIVE_LEVEL)
+_IRQL_requires_same_
+NTSTATUS
+ClassDetermineTokenOperationCommandSupport(
+ _In_ PDEVICE_OBJECT DeviceObject
+ )
+
+/*++
+
+Routine Description:
+
+ This routine determines if Token Operation and Receive ROD Token Information commands
+ are supported by this device and updates the internal structures to reflect this.
+ In addition, it also queries the registry to determine the maximum listIdentifier
+ to use for TokenOperation commands.
+
+ This function must be called at IRQL == PASSIVE_LEVEL.
+
+Arguments:
+
+ DeviceObject - The device object for which we want to determine command support.
+
+Return Value:
+
+ Nothing
+
+--*/
+
+{
+ NTSTATUS status = STATUS_SUCCESS;
+
+ PAGED_CODE();
+
+ TracePrint((TRACE_LEVEL_VERBOSE,
+ TRACE_FLAG_PNP,
+ "ClassDetermineTokenOperationCommandSupport (%p): Entering function.\n",
+ DeviceObject));
+
+ //
+ // Send down Inquiry for VPD_THIRD_PARTY_COPY_PAGE and cache away the device parameters
+ // from WINDOWS_BLOCK_DEVICE_TOKEN_LIMITS_DESCRIPTOR.
+ //
+ status = ClasspGetBlockDeviceTokenLimitsInfo(DeviceObject);
+
+ if (NT_SUCCESS(status)) {
+
+ ULONG maxListIdentifier = MaxTokenOperationListIdentifier;
+
+ //
+ // Query the maximum list identifier to use for TokenOperation commands.
+ //
+ if (NT_SUCCESS(ClasspGetMaximumTokenListIdentifier(DeviceObject, REG_DISK_CLASS_CONTROL, &maxListIdentifier))) {
+ if (maxListIdentifier >= MIN_TOKEN_LIST_IDENTIFIERS) {
+
+ NT_ASSERT(maxListIdentifier <= MAX_TOKEN_LIST_IDENTIFIERS);
+ MaxTokenOperationListIdentifier = maxListIdentifier;
+ }
+ }
+
+ }
+
+ TracePrint((TRACE_LEVEL_VERBOSE,
+ TRACE_FLAG_PNP,
+ "ClassDetermineTokenOperationCommandSupport (%p): Exiting function with status %x.\n",
+ DeviceObject,
+ status));
+
+ return status;
+}
+
+
+_IRQL_requires_same_
+NTSTATUS
+ClasspGetBlockDeviceTokenLimitsInfo(
+ _Inout_ PDEVICE_OBJECT DeviceObject
+ )
+
+/*++
+
+Routine Description:
+
+ This routine does the work to get the Block Device Token Limits info for a given LUN.
+ This is done by sending Inquiry for the Third Party Copy VPD page.
+
+Arguments:
+
+ DeviceObject - The FDO associated with the LUN for which Block Device Token
+ limits info is desired.
+
+Return Value:
+
+ STATUS_DEVICE_FEATURE_NOT_SUPPORTED if either the Inquiry fails or validations fail.
+ STATUS_SUCCESS otherwise.
+
+--*/
+
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+ NTSTATUS status;
+ PSCSI_REQUEST_BLOCK srb = NULL;
+ ULONG srbSize = 0;
+ USHORT pageLength = 0;
+ USHORT descriptorLength = 0;
+ PVOID dataBuffer = NULL;
+ UCHAR bufferLength = VPD_MAX_BUFFER_SIZE;
+ ULONG allocationBufferLength = bufferLength;
+ PCDB cdb;
+ ULONG dataTransferLength = 0;
+ PVPD_THIRD_PARTY_COPY_PAGE operatingParameters = NULL;
+ PWINDOWS_BLOCK_DEVICE_TOKEN_LIMITS_DESCRIPTOR blockDeviceTokenLimits = NULL;
+
+ TracePrint((TRACE_LEVEL_VERBOSE,
+ TRACE_FLAG_PNP,
+ "ClasspGetBlockDeviceTokenLimitsInfo (%p): Entering function.\n",
+ DeviceObject));
+
+ //
+ // Allocate an SRB for querying the device for LBP-related info.
+ //
+ if ((fdoExtension->AdapterDescriptor != NULL) &&
+ (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK)) {
+ srbSize = CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE;
+ } else {
+ srbSize = sizeof(SCSI_REQUEST_BLOCK);
+ }
+
+ srb = ExAllocatePoolWithTag(NonPagedPoolNx, srbSize, CLASSPNP_POOL_TAG_SRB);
+
+ if (!srb) {
+
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_PNP,
+ "ClasspGetBlockDeviceTokenLimitsInfo (%p): Couldn't allocate SRB.\n",
+ DeviceObject));
+
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ goto __ClasspGetBlockDeviceTokenLimitsInfo_Exit;
+ }
+
+#if defined(_ARM_) || defined(_ARM64_)
+ //
+ // ARM has specific alignment requirements, although this will not have a functional impact on x86 or amd64
+ // based platforms. We are taking the conservative approach here.
+ //
+ allocationBufferLength = ALIGN_UP_BY(allocationBufferLength, KeGetRecommendedSharedDataAlignment());
+ dataBuffer = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned, allocationBufferLength, CLASSPNP_POOL_TAG_VPD);
+#else
+ dataBuffer = ExAllocatePoolWithTag(NonPagedPoolNx, bufferLength, CLASSPNP_POOL_TAG_VPD);
+#endif
+
+ if (!dataBuffer) {
+
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_PNP,
+ "ClasspGetBlockDeviceTokenLimitsInfo (%p): Couldn't allocate dataBuffer.\n",
+ DeviceObject));
+
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ goto __ClasspGetBlockDeviceTokenLimitsInfo_Exit;
+ }
+
+ operatingParameters = (PVPD_THIRD_PARTY_COPY_PAGE)dataBuffer;
+
+ RtlZeroMemory(dataBuffer, allocationBufferLength);
+
+ if ((fdoExtension->AdapterDescriptor != NULL) &&
+ (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK)) {
+ status = InitializeStorageRequestBlock((PSTORAGE_REQUEST_BLOCK)srb,
+ STORAGE_ADDRESS_TYPE_BTL8,
+ CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE,
+ 1,
+ SrbExDataTypeScsiCdb16);
+ if (NT_SUCCESS(status)) {
+
+ ((PSTORAGE_REQUEST_BLOCK)srb)->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
+
+ } else {
+
+ //
+ // Should not occur. Revert to legacy SRB.
+ //
+ NT_ASSERT(FALSE);
+
+ TracePrint((TRACE_LEVEL_WARNING,
+ TRACE_FLAG_PNP,
+ "ClasspGetBlockDeviceTokenLimitsInfo (%p): Falling back to using SRB (instead of SRB_EX).\n",
+ DeviceObject));
+
+ RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
+ srb->Length = sizeof(SCSI_REQUEST_BLOCK);
+ srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
+ }
+ } else {
+
+ RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
+ srb->Length = sizeof(SCSI_REQUEST_BLOCK);
+ srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
+ }
+
+ SrbSetTimeOutValue(srb, fdoExtension->TimeOutValue);
+ SrbSetRequestTag(srb, SP_UNTAGGED);
+ SrbSetRequestAttribute(srb, SRB_SIMPLE_TAG_REQUEST);
+ SrbAssignSrbFlags(srb, fdoExtension->SrbFlags);
+
+ SrbSetCdbLength(srb, 6);
+
+ cdb = SrbGetCdb(srb);
+ cdb->CDB6INQUIRY3.OperationCode = SCSIOP_INQUIRY;
+ cdb->CDB6INQUIRY3.EnableVitalProductData = 1;
+ cdb->CDB6INQUIRY3.PageCode = VPD_THIRD_PARTY_COPY;
+ cdb->CDB6INQUIRY3.AllocationLength = bufferLength;
+
+ status = ClassSendSrbSynchronous(fdoExtension->DeviceObject,
+ srb,
+ dataBuffer,
+ allocationBufferLength,
+ FALSE);
+
+ //
+ // Handle the case where we get back STATUS_DATA_OVERRUN because the input
+ // buffer was larger than necessary.
+ //
+ dataTransferLength = SrbGetDataTransferLength(srb);
+
+ if (status == STATUS_DATA_OVERRUN && dataTransferLength < bufferLength) {
+
+ status = STATUS_SUCCESS;
+ }
+
+ if (NT_SUCCESS(status)) {
+
+ REVERSE_BYTES_SHORT(&pageLength, &operatingParameters->PageLength);
+
+ } else {
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_PNP,
+ "ClasspGetBlockDeviceTokenLimitsInfo (%p): Inquiry for TPC VPD failed with %x.\n",
+ DeviceObject,
+ status));
+ }
+
+ if ((NT_SUCCESS(status)) &&
+ (pageLength >= sizeof(WINDOWS_BLOCK_DEVICE_TOKEN_LIMITS_DESCRIPTOR))) {
+
+ USHORT descriptorType;
+
+ blockDeviceTokenLimits = (PWINDOWS_BLOCK_DEVICE_TOKEN_LIMITS_DESCRIPTOR)operatingParameters->ThirdPartyCopyDescriptors;
+ REVERSE_BYTES_SHORT(&descriptorType, &blockDeviceTokenLimits->DescriptorType);
+ REVERSE_BYTES_SHORT(&descriptorLength, &blockDeviceTokenLimits->DescriptorLength);
+
+ if ((descriptorType == BLOCK_DEVICE_TOKEN_LIMITS_DESCRIPTOR_TYPE_WINDOWS) &&
+ (VPD_PAGE_HEADER_SIZE + descriptorLength == sizeof(WINDOWS_BLOCK_DEVICE_TOKEN_LIMITS_DESCRIPTOR))) {
+
+ fdoExtension->FunctionSupportInfo->ValidInquiryPages.BlockDeviceRODLimits = TRUE;
+
+ REVERSE_BYTES_SHORT(&fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.MaximumRangeDescriptors, &blockDeviceTokenLimits->MaximumRangeDescriptors);
+ REVERSE_BYTES(&fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.MaximumInactivityTimer, &blockDeviceTokenLimits->MaximumInactivityTimer);
+ REVERSE_BYTES(&fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.DefaultInactivityTimer, &blockDeviceTokenLimits->DefaultInactivityTimer);
+ REVERSE_BYTES_QUAD(&fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.MaximumTokenTransferSize, &blockDeviceTokenLimits->MaximumTokenTransferSize);
+ REVERSE_BYTES_QUAD(&fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.OptimalTransferCount, &blockDeviceTokenLimits->OptimalTransferCount);
+
+ TracePrint((TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_PNP,
+ "ClasspGetBlockDeviceTokenLimitsInfo (%p): %s %s (rev %s) reported following parameters: \
+ \n\t\t\tMaxRangeDescriptors: %u\n\t\t\tMaxIAT: %u\n\t\t\tDefaultIAT: %u \
+ \n\t\t\tMaxTokenTransferSize: %I64u\n\t\t\tOptimalTransferCount: %I64u\n\t\t\tOptimalTransferLengthGranularity: %u \
+ \n\t\t\tOptimalTransferLength: %u\n\t\t\tMaxTransferLength: %u\n",
+ DeviceObject,
+ (PCSZ)(((PUCHAR)fdoExtension->DeviceDescriptor) + fdoExtension->DeviceDescriptor->VendorIdOffset),
+ (PCSZ)(((PUCHAR)fdoExtension->DeviceDescriptor) + fdoExtension->DeviceDescriptor->ProductIdOffset),
+ (PCSZ)(((PUCHAR)fdoExtension->DeviceDescriptor) + fdoExtension->DeviceDescriptor->ProductRevisionOffset),
+ fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.MaximumRangeDescriptors,
+ fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.MaximumInactivityTimer,
+ fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.DefaultInactivityTimer,
+ fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.MaximumTokenTransferSize,
+ fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.OptimalTransferCount,
+ fdoExtension->FunctionSupportInfo->BlockLimitsData.OptimalTransferLengthGranularity,
+ fdoExtension->FunctionSupportInfo->BlockLimitsData.OptimalTransferLength,
+ fdoExtension->FunctionSupportInfo->BlockLimitsData.MaximumTransferLength));
+ } else {
+
+ TracePrint((TRACE_LEVEL_WARNING,
+ TRACE_FLAG_PNP,
+ "ClasspGetBlockDeviceTokenLimitsInfo (%p): ThirdPartyCopy VPD data doesn't have Windows OffloadDataTransfer descriptor.\n",
+ DeviceObject));
+
+ fdoExtension->FunctionSupportInfo->ValidInquiryPages.BlockDeviceRODLimits = FALSE;
+ status = STATUS_DEVICE_FEATURE_NOT_SUPPORTED;
+ }
+ } else {
+
+ TracePrint((TRACE_LEVEL_WARNING,
+ TRACE_FLAG_PNP,
+ "ClasspGetBlockDeviceTokenLimitsInfo (%p): TPC VPD data didn't return TPC descriptors of interest.\n",
+ DeviceObject));
+
+ fdoExtension->FunctionSupportInfo->ValidInquiryPages.BlockDeviceRODLimits = FALSE;
+ status = STATUS_DEVICE_FEATURE_NOT_SUPPORTED;
+ }
+
+__ClasspGetBlockDeviceTokenLimitsInfo_Exit:
+
+ FREE_POOL(dataBuffer);
+ FREE_POOL(srb);
+ fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.CommandStatus = status;
+
+ TracePrint((TRACE_LEVEL_VERBOSE,
+ TRACE_FLAG_PNP,
+ "ClasspGetBlockDeviceTokenLimitsInfo (%p): Exiting function with status %x.\n",
+ DeviceObject,
+ status));
+
+ return status;
+}
+
+
+_IRQL_requires_max_(APC_LEVEL)
+_IRQL_requires_min_(PASSIVE_LEVEL)
+_IRQL_requires_same_
+NTSTATUS
+ClassDeviceProcessOffloadRead(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _In_ PIRP Irp,
+ _Inout_ PSCSI_REQUEST_BLOCK Srb
+ )
+
+/*++
+
+Routine Description:
+
+ This routine services IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES for CopyOffload
+ Read. If the device supports copy offload, it performs the translation of the
+ IOCTL into the appropriate SCSI commands to complete the operation.
+
+ This function must be called at IRQL < DISPATCH_LEVEL.
+
+Arguments:
+
+ DeviceObject - Supplies the device object associated with this request
+ Irp - The IRP to be processed
+ Srb - An SRB that can be optinally used to process this request
+
+Return Value:
+
+ NTSTATUS code
+
+--*/
+
+{
+ PIO_STACK_LOCATION irpStack;
+ NTSTATUS status;
+ PDEVICE_MANAGE_DATA_SET_ATTRIBUTES dsmAttributes;
+ PDEVICE_DSM_OFFLOAD_READ_PARAMETERS offloadReadParameters;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
+
+ UNREFERENCED_PARAMETER(Srb);
+
+ //
+ // This function must be called at less than dispatch level.
+ // Fail if IRQL >= DISPATCH_LEVEL.
+ //
+ PAGED_CODE();
+
+ TracePrint((TRACE_LEVEL_VERBOSE,
+ TRACE_FLAG_IOCTL,
+ "ClassDeviceProcessOffloadRead (%p): Entering function. Irp %p\n",
+ DeviceObject,
+ Irp));
+
+
+ irpStack = IoGetCurrentIrpStackLocation (Irp);
+
+ //
+ // Validations
+ //
+ status = ClasspValidateOffloadSupported(DeviceObject, Irp);
+ if (!NT_SUCCESS(status)) {
+ goto __ClassDeviceProcessOffloadRead_CompleteAndExit;
+ }
+
+ if (KeGetCurrentIrql() >= DISPATCH_LEVEL) {
+
+ NT_ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClassDeviceProcessOffloadRead (%p): Called at raised IRQL.\n",
+ DeviceObject));
+
+ status = STATUS_INVALID_LEVEL;
+ goto __ClassDeviceProcessOffloadRead_CompleteAndExit;
+ }
+
+ //
+ // Ensure that this DSM IOCTL was generated in kernel
+ //
+ if (Irp->RequestorMode != KernelMode) {
+
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClassDeviceProcessOffloadRead (%p): Called from user mode.\n",
+ DeviceObject));
+
+ status = STATUS_ACCESS_DENIED;
+ goto __ClassDeviceProcessOffloadRead_CompleteAndExit;
+ }
+
+ status = ClasspValidateOffloadInputParameters(DeviceObject, Irp);
+ if (!NT_SUCCESS(status)) {
+ goto __ClassDeviceProcessOffloadRead_CompleteAndExit;
+ }
+
+ dsmAttributes = Irp->AssociatedIrp.SystemBuffer;
+
+ //
+ // Validate that we were passed in correct sized parameter block.
+ //
+ if (dsmAttributes->ParameterBlockLength < sizeof(DEVICE_DSM_OFFLOAD_READ_PARAMETERS)) {
+
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClassDeviceProcessOffloadRead (%p): Parameter block size (%u) too small. Required %u.\n",
+ DeviceObject,
+ dsmAttributes->ParameterBlockLength,
+ sizeof(DEVICE_DSM_OFFLOAD_READ_PARAMETERS)));
+
+ status = STATUS_INVALID_PARAMETER;
+ goto __ClassDeviceProcessOffloadRead_CompleteAndExit;
+ }
+
+ offloadReadParameters = Add2Ptr(dsmAttributes, dsmAttributes->ParameterBlockOffset);
+
+ fdoExtension = DeviceObject->DeviceExtension;
+
+ //
+ // If the request TTL is greater than the max supported by this storage, the target will
+ // end up failing this command, so might as well do the check up front.
+ //
+ if ((fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.MaximumInactivityTimer > 0) &&
+ (offloadReadParameters->TimeToLive > fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.MaximumInactivityTimer)) {
+
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClassDeviceProcessOffloadRead (%p): Requested TTL (%u) greater than max supported (%u).\n",
+ DeviceObject,
+ offloadReadParameters->TimeToLive,
+ fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.MaximumInactivityTimer));
+
+ status = STATUS_INVALID_PARAMETER;
+ goto __ClassDeviceProcessOffloadRead_CompleteAndExit;
+ }
+
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(STORAGE_OFFLOAD_READ_OUTPUT)) {
+
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClassDeviceProcessOffloadRead (%p): Output buffer size (%u) too small.\n",
+ DeviceObject,
+ irpStack->Parameters.DeviceIoControl.OutputBufferLength));
+
+ status = STATUS_BUFFER_TOO_SMALL;
+ goto __ClassDeviceProcessOffloadRead_CompleteAndExit;
+ }
+
+
+
+ status = ClasspServicePopulateTokenTransferRequest(DeviceObject, Irp);
+
+ if (status == STATUS_PENDING) {
+ goto __ClassDeviceProcessOffloadRead_Exit;
+ }
+
+__ClassDeviceProcessOffloadRead_CompleteAndExit:
+ ClasspCompleteOffloadRequest(DeviceObject, Irp, status);
+__ClassDeviceProcessOffloadRead_Exit:
+ TracePrint((TRACE_LEVEL_VERBOSE,
+ TRACE_FLAG_IOCTL,
+ "ClassDeviceProcessOffloadRead (%p): Exiting function Irp %p with status %x.\n",
+ DeviceObject,
+ Irp,
+ status));
+
+ return status;
+}
+
+VOID
+ClasspCompleteOffloadRequest(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _In_ PIRP Irp,
+ _In_ NTSTATUS CompletionStatus)
+{
+ NTSTATUS status = CompletionStatus;
+
+
+ Irp->IoStatus.Status = status;
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+
+ return;
+}
+
+
+_IRQL_requires_max_(APC_LEVEL)
+_IRQL_requires_min_(PASSIVE_LEVEL)
+_IRQL_requires_same_
+NTSTATUS
+ClassDeviceProcessOffloadWrite(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _In_ PIRP Irp,
+ _Inout_ PSCSI_REQUEST_BLOCK Srb
+ )
+
+/*++
+
+Routine Description:
+
+ This routine services IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES for CopyOffload
+ Write. If the device supports copy offload, it performs the translation of the
+ IOCTL into the appropriate SCSI commands to complete the operation.
+
+ This function must be called at IRQL < DISPATCH_LEVEL.
+
+Arguments:
+
+ DeviceObject - Supplies the device object associated with this request
+ Irp - The IRP to be processed
+ Srb - An SRB that can be optinally used to process this request
+
+Return Value:
+
+ NTSTATUS code
+
+--*/
+
+{
+ PIO_STACK_LOCATION irpStack;
+ NTSTATUS status;
+ PDEVICE_MANAGE_DATA_SET_ATTRIBUTES dsmAttributes;
+
+ UNREFERENCED_PARAMETER(Srb);
+
+ //
+ // This function must be called at less than dispatch level.
+ //
+ PAGED_CODE();
+
+ TracePrint((TRACE_LEVEL_VERBOSE,
+ TRACE_FLAG_IOCTL,
+ "ClassDeviceProcessOffloadWrite (%p): Entering function. Irp %p\n",
+ DeviceObject,
+ Irp));
+
+
+ irpStack = IoGetCurrentIrpStackLocation(Irp);
+
+ //
+ // Validations
+ //
+ status = ClasspValidateOffloadSupported(DeviceObject, Irp);
+ if (!NT_SUCCESS(status)) {
+ goto __ClassDeviceProcessOffloadWrite_CompleteAndExit;
+ }
+
+ if (KeGetCurrentIrql() >= DISPATCH_LEVEL) {
+
+ NT_ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClassDeviceProcessOffloadWrite (%p): Called at raised IRQL.\n",
+ DeviceObject));
+
+ status = STATUS_INVALID_LEVEL;
+ goto __ClassDeviceProcessOffloadWrite_CompleteAndExit;
+ }
+
+ //
+ // Ensure that this DSM IOCTL was generated in kernel
+ //
+ if (Irp->RequestorMode != KernelMode) {
+
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClassDeviceProcessOffloadWrite (%p): Called from user mode.\n",
+ DeviceObject));
+
+ status = STATUS_ACCESS_DENIED;
+ goto __ClassDeviceProcessOffloadWrite_CompleteAndExit;
+ }
+
+ status = ClasspValidateOffloadInputParameters(DeviceObject, Irp);
+ if (!NT_SUCCESS(status)) {
+ goto __ClassDeviceProcessOffloadWrite_CompleteAndExit;
+ }
+
+ dsmAttributes = Irp->AssociatedIrp.SystemBuffer;
+
+ //
+ // Validate that we were passed in correct sized parameter block.
+ //
+ if (dsmAttributes->ParameterBlockLength < sizeof(DEVICE_DSM_OFFLOAD_WRITE_PARAMETERS)) {
+
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClassDeviceProcessOffloadWrite (%p): Parameter block size (%u) too small. Required %u.\n",
+ DeviceObject,
+ dsmAttributes->ParameterBlockLength,
+ sizeof(DEVICE_DSM_OFFLOAD_WRITE_PARAMETERS)));
+
+ status = STATUS_INVALID_PARAMETER;
+ goto __ClassDeviceProcessOffloadWrite_CompleteAndExit;
+ }
+
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(STORAGE_OFFLOAD_WRITE_OUTPUT)) {
+
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClassDeviceProcessOffloadWrite (%p): Output buffer size (%u) too small.\n",
+ DeviceObject,
+ irpStack->Parameters.DeviceIoControl.OutputBufferLength));
+
+ status = STATUS_BUFFER_TOO_SMALL;
+ goto __ClassDeviceProcessOffloadWrite_CompleteAndExit;
+ }
+
+
+
+ status = ClasspServiceWriteUsingTokenTransferRequest(DeviceObject, Irp);
+
+ if (status == STATUS_PENDING) {
+ goto __ClassDeviceProcessOffloadWrite_Exit;
+ }
+
+__ClassDeviceProcessOffloadWrite_CompleteAndExit:
+ ClasspCompleteOffloadRequest(DeviceObject, Irp, status);
+__ClassDeviceProcessOffloadWrite_Exit:
+ TracePrint((TRACE_LEVEL_VERBOSE,
+ TRACE_FLAG_IOCTL,
+ "ClassDeviceProcessOffloadWrite (%p): Exiting function Irp %p with status %x\n",
+ DeviceObject,
+ Irp,
+ status));
+
+ return status;
+}
+
+
+_IRQL_requires_max_(APC_LEVEL)
+_IRQL_requires_min_(PASSIVE_LEVEL)
+_IRQL_requires_same_
+NTSTATUS
+ClasspServicePopulateTokenTransferRequest(
+ _In_ PDEVICE_OBJECT Fdo,
+ _In_ PIRP Irp
+ )
+
+/*++
+
+Routine description:
+
+ This routine processes offload read requests by building the SRB
+ for PopulateToken and its result (i.e. token).
+
+Arguments:
+
+ Fdo - The functional device object processing the request
+ Irp - The Io request to be processed
+
+Return Value:
+
+ STATUS_SUCCESS if successful, an error code otherwise
+
+--*/
+
+{
+ BOOLEAN allDataSetRangeFullyConverted;
+ UINT32 allocationSize;
+ ULONG blockDescrIndex;
+ PBLOCK_DEVICE_RANGE_DESCRIPTOR blockDescrPointer;
+ PVOID buffer;
+ ULONG bufferLength;
+ ULONG dataSetRangeIndex;
+ PDEVICE_DATA_SET_RANGE dataSetRanges;
+ ULONG dataSetRangesCount;
+ PDEVICE_MANAGE_DATA_SET_ATTRIBUTES dsmAttributes;
+ ULONGLONG entireXferLen;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt;
+ ULONG i;
+ ULONG lbaCount;
+ ULONG listIdentifier;
+ ULONG maxBlockDescrCount;
+ ULONGLONG maxLbaCount;
+ POFFLOAD_READ_CONTEXT offloadReadContext;
+ PDEVICE_DSM_OFFLOAD_READ_PARAMETERS offloadReadParameters;
+ PTRANSFER_PACKET pkt;
+ USHORT populateTokenDataLength;
+ USHORT populateTokenDescriptorsLength;
+ PMDL populateTokenMdl;
+ PIRP pseudoIrp;
+ ULONG receiveTokenInformationBufferLength;
+ NTSTATUS status;
+ DEVICE_DATA_SET_RANGE tempDataSetRange;
+ BOOLEAN tempDataSetRangeFullyConverted;
+ PUCHAR token;
+ ULONG tokenLength;
+ ULONG tokenOperationBufferLength;
+ ULONGLONG totalSectorCount;
+ ULONGLONG totalSectorsToProcess;
+ ULONG transferSize;
+
+ PAGED_CODE();
+
+ TracePrint((TRACE_LEVEL_VERBOSE,
+ TRACE_FLAG_IOCTL,
+ "ClasspServicePopulateTokenTransferRequest (%p): Entering function. Irp %p\n",
+ Fdo,
+ Irp));
+
+ fdoExt = Fdo->DeviceExtension;
+ status = STATUS_SUCCESS;
+ dsmAttributes = Irp->AssociatedIrp.SystemBuffer;
+ buffer = NULL;
+ pkt = NULL;
+ populateTokenMdl = NULL;
+ offloadReadParameters = Add2Ptr(dsmAttributes, dsmAttributes->ParameterBlockOffset);
+ dataSetRanges = Add2Ptr(dsmAttributes, dsmAttributes->DataSetRangesOffset);
+ dataSetRangesCount = dsmAttributes->DataSetRangesLength / sizeof(DEVICE_DATA_SET_RANGE);
+ totalSectorsToProcess = 0;
+ token = NULL;
+ tokenLength = 0;
+ bufferLength = 0;
+ offloadReadContext = NULL;
+
+ NT_ASSERT(fdoExt->FunctionSupportInfo->ValidInquiryPages.BlockDeviceRODLimits &&
+ NT_SUCCESS(fdoExt->FunctionSupportInfo->BlockDeviceRODLimitsData.CommandStatus));
+
+
+ for (i = 0, entireXferLen = 0; i < dataSetRangesCount; i++) {
+ entireXferLen += dataSetRanges[i].LengthInBytes;
+ }
+
+ //
+ // We need to truncate the read request based on the following hardware limitations:
+ // 1. The size of the data buffer containing the TokenOperation command's parameters must
+ // not exceed the MaximumTransferLength (and max physical pages) of the underlying
+ // adapter.
+ // 2. The number of descriptors specified in the TokenOperation command must not exceed
+ // the MaximumRangeDescriptors.
+ // 3. The cumulative total of the number of transfer blocks in all the descriptors in
+ // the TokenOperation command must not exceed the MaximumTokenTransferSize.
+ // 4. If the TTL has been set to the 0 (i.e. indicates the use of the default TLT),
+ // limit the cumulative total of the number of transfer blocks in all the descriptors
+ // in the TokenOperation command to the OptimalTransferCount.
+ // NOTE: only a TTL of 0 has an implicit indication that the initiator of the
+ // request would like the storage stack to be smart about the amount of data
+ // transfer.
+ //
+ // In addition to the above, we need to ensure that for each of the descriptors in the
+ // TokenOperation command:
+ // 1. The number of blocks specified is an exact multiple of the OptimalTransferLengthGranularity.
+ // 2. The number of blocks specified is limited to the MaximumTransferLength. (We shall
+ // however, limit the number of blocks specified in each descriptor to be a maximum of
+ // OptimalTransferLength or MaximumTransferLength, whichever is lesser).
+ //
+ // Finally, we shall always send down the PopulateToken command using IMMED = 0 for this
+ // release. This makes it simpler to handle multi-initiator scenarios since we won't need
+ // to deal with the PopulateToken (with IMMED = 1) succeeding but ReceiveRODTokenInformation
+ // failing due to the path/node failing, thus making it impossible to retrieve the token.
+ //
+ // The LBA ranges is in DEVICE_DATA_SET_RANGE format, it needs to be converted into
+ // WINDOWS_RANGE_DESCRIPTOR Block Descriptors.
+ //
+
+ ClasspGetTokenOperationCommandBufferLength(Fdo,
+ SERVICE_ACTION_POPULATE_TOKEN,
+ &bufferLength,
+ &tokenOperationBufferLength,
+ &receiveTokenInformationBufferLength);
+
+ allocationSize = sizeof(OFFLOAD_READ_CONTEXT) + bufferLength;
+
+ offloadReadContext = ExAllocatePoolWithTag(
+ NonPagedPoolNx,
+ allocationSize,
+ CLASSPNP_POOL_TAG_TOKEN_OPERATION);
+
+ if (!offloadReadContext) {
+
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClasspServicePopulateTokenTransferRequest (%p): Failed to allocate buffer for PopulateToken operations.\n",
+ Fdo));
+
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ goto __ClasspServicePopulateTokenTransferRequest_ErrorExit;
+ }
+
+ RtlZeroMemory(offloadReadContext, allocationSize);
+
+ offloadReadContext->Fdo = Fdo;
+ offloadReadContext->OffloadReadDsmIrp = Irp;
+
+ //
+ // The buffer for the commands is after the offloadReadContext.
+ //
+ buffer = (offloadReadContext + 1);
+
+ //
+ // No matter how large the offload read request, we'll be sending it down in one shot.
+ // So we need only one transfer packet.
+ //
+ pkt = DequeueFreeTransferPacket(Fdo, TRUE);
+ if (!pkt){
+
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClasspServicePopulateTokenTransferRequest (%p): Failed to retrieve transfer packet for TokenOperation (PopulateToken) operation.\n",
+ Fdo));
+
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ goto __ClasspServicePopulateTokenTransferRequest_ErrorExit;
+ }
+
+ offloadReadContext->Pkt = pkt;
+
+ ClasspGetTokenOperationDescriptorLimits(Fdo,
+ SERVICE_ACTION_POPULATE_TOKEN,
+ tokenOperationBufferLength,
+ &maxBlockDescrCount,
+ &maxLbaCount);
+
+ //
+ // We will limit the maximum data transfer in an offload read operation to:
+ // - OTC if TTL was specified as 0
+ // - MaximumTransferTransferSize if lesser than above, or if TTL was specified was non-zero
+ //
+ if ((offloadReadParameters->TimeToLive == 0) && (fdoExt->FunctionSupportInfo->BlockDeviceRODLimitsData.OptimalTransferCount > 0)) {
+
+ maxLbaCount = MIN(maxLbaCount, fdoExt->FunctionSupportInfo->BlockDeviceRODLimitsData.OptimalTransferCount);
+ }
+
+ //
+ // Since we do not want very fragmented files to end up causing the PopulateToken command to take
+ // too long (and potentially timeout), we will limit the max number of descriptors sent down in a
+ // command.
+ //
+ maxBlockDescrCount = MIN(maxBlockDescrCount, MAX_NUMBER_BLOCK_DEVICE_DESCRIPTORS);
+
+ TracePrint((TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_IOCTL,
+ "ClasspServicePopulateTokenTransferRequest (%p): Using MaxBlockDescrCount %u and MaxLbaCount %I64u.\n",
+ Fdo,
+ maxBlockDescrCount,
+ maxLbaCount));
+
+ allDataSetRangeFullyConverted = FALSE;
+ tempDataSetRangeFullyConverted = TRUE;
+ dataSetRangeIndex = (ULONG)-1;
+
+ blockDescrPointer = (PBLOCK_DEVICE_RANGE_DESCRIPTOR)
+ &((PPOPULATE_TOKEN_HEADER)buffer)->BlockDeviceRangeDescriptor[0];
+
+ RtlZeroMemory(&tempDataSetRange, sizeof(tempDataSetRange));
+
+ blockDescrIndex = 0;
+ lbaCount = 0;
+
+ //
+ // Send PopulateToken command when the buffer is full or all input entries are converted.
+ //
+ while (!((blockDescrIndex == maxBlockDescrCount) || // buffer full or block descriptor count reached
+ (lbaCount == maxLbaCount) || // block LBA count reached
+ (allDataSetRangeFullyConverted))) { // all DataSetRanges have been converted
+
+ //
+ // If the previous entry conversion completed, continue the next one;
+ // Otherwise, still process the left part of the un-completed entry.
+ //
+ if (tempDataSetRangeFullyConverted) {
+ dataSetRangeIndex++;
+ tempDataSetRange.StartingOffset = dataSetRanges[dataSetRangeIndex].StartingOffset;
+ tempDataSetRange.LengthInBytes = dataSetRanges[dataSetRangeIndex].LengthInBytes;
+ }
+
+ totalSectorCount = 0;
+
+ ClasspConvertDataSetRangeToBlockDescr(Fdo,
+ blockDescrPointer,
+ &blockDescrIndex,
+ maxBlockDescrCount,
+ &lbaCount,
+ maxLbaCount,
+ &tempDataSetRange,
+ &totalSectorCount);
+
+ tempDataSetRangeFullyConverted = (tempDataSetRange.LengthInBytes == 0) ? TRUE : FALSE;
+
+ allDataSetRangeFullyConverted = tempDataSetRangeFullyConverted && ((dataSetRangeIndex + 1) == dataSetRangesCount);
+
+ totalSectorsToProcess += totalSectorCount;
+ }
+
+ //
+ // Calculate transfer size, including the header
+ //
+ transferSize = (blockDescrIndex * sizeof(BLOCK_DEVICE_RANGE_DESCRIPTOR)) + FIELD_OFFSET(POPULATE_TOKEN_HEADER, BlockDeviceRangeDescriptor);
+
+ NT_ASSERT(transferSize <= MAX_TOKEN_OPERATION_PARAMETER_DATA_LENGTH);
+
+ populateTokenDataLength = (USHORT)transferSize - RTL_SIZEOF_THROUGH_FIELD(POPULATE_TOKEN_HEADER, PopulateTokenDataLength);
+ REVERSE_BYTES_SHORT(((PPOPULATE_TOKEN_HEADER)buffer)->PopulateTokenDataLength, &populateTokenDataLength);
+
+ ((PPOPULATE_TOKEN_HEADER)buffer)->Immediate = 0;
+
+ REVERSE_BYTES(((PPOPULATE_TOKEN_HEADER)buffer)->InactivityTimeout, &offloadReadParameters->TimeToLive);
+
+ populateTokenDescriptorsLength = (USHORT)transferSize - FIELD_OFFSET(POPULATE_TOKEN_HEADER, BlockDeviceRangeDescriptor);
+ REVERSE_BYTES_SHORT(((PPOPULATE_TOKEN_HEADER)buffer)->BlockDeviceRangeDescriptorListLength, &populateTokenDescriptorsLength);
+
+ //
+ // Reuse a single buffer for both TokenOperation and ReceiveTokenInformation. This has the one disadvantage
+ // that we'll be marking the page(s) as IoWriteAccess even though we only need read access for token
+ // operation command. However, the advantage is that we eliminate the possibility of any potential failures
+ // when trying to allocate an MDL for the ReceiveTokenInformation later on.
+ //
+ bufferLength = max(transferSize, receiveTokenInformationBufferLength);
+
+ populateTokenMdl = ClasspBuildDeviceMdl(buffer, bufferLength, FALSE);
+ if (!populateTokenMdl) {
+
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClasspServicePopulateTokenTransferRequest (%p): Failed to allocate MDL for PopulateToken operations.\n",
+ Fdo));
+
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ goto __ClasspServicePopulateTokenTransferRequest_ErrorExit;
+ }
+
+ offloadReadContext->PopulateTokenMdl = populateTokenMdl;
+
+ pseudoIrp = &offloadReadContext->PseudoIrp;
+
+
+ pseudoIrp->IoStatus.Status = STATUS_SUCCESS;
+ pseudoIrp->IoStatus.Information = 0;
+ pseudoIrp->Tail.Overlay.DriverContext[0] = LongToPtr(1);
+ pseudoIrp->MdlAddress = populateTokenMdl;
+
+ InterlockedCompareExchange((volatile LONG *)&TokenOperationListIdentifier, -1, MaxTokenOperationListIdentifier);
+ listIdentifier = InterlockedIncrement((volatile LONG *)&TokenOperationListIdentifier);
+
+ ClasspSetupPopulateTokenTransferPacket(
+ offloadReadContext,
+ pkt,
+ transferSize,
+ (PUCHAR)buffer,
+ pseudoIrp,
+ listIdentifier);
+
+ TracePrint((TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_IOCTL,
+ "ClasspServicePopulateTokenTransferRequest (%p): Generate token for %I64u bytes (versus %I64u) [via %u descriptors]. \
+ \n\t\t\tDataLength: %u, DescriptorsLength: %u. Pkt %p (list id %x). Requested TTL: %u secs.\n",
+ Fdo,
+ totalSectorsToProcess * fdoExt->DiskGeometry.BytesPerSector,
+ entireXferLen,
+ blockDescrIndex,
+ populateTokenDataLength,
+ populateTokenDescriptorsLength,
+ pkt,
+ listIdentifier,
+ offloadReadParameters->TimeToLive));
+
+ //
+ // Save (into the offloadReadContext) any remaining things that
+ // ClasspPopulateTokenTransferPacketDone() will need.
+ //
+
+ offloadReadContext->ListIdentifier = listIdentifier;
+ offloadReadContext->BufferLength = bufferLength;
+ offloadReadContext->ReceiveTokenInformationBufferLength = receiveTokenInformationBufferLength;
+ offloadReadContext->TotalSectorsToProcess = totalSectorsToProcess; // so far
+ offloadReadContext->EntireXferLen = entireXferLen;
+
+ NT_ASSERT(status == STATUS_SUCCESS); // so far.
+
+ IoMarkIrpPending(Irp);
+ SubmitTransferPacket(pkt);
+
+ status = STATUS_PENDING;
+ goto __ClasspServicePopulateTokenTransferRequest_Exit;
+
+ //
+ // Error cleanup label only - not used in success case:
+ //
+
+__ClasspServicePopulateTokenTransferRequest_ErrorExit:
+
+ NT_ASSERT(status != STATUS_PENDING);
+
+ if (offloadReadContext != NULL) {
+ ClasspCleanupOffloadReadContext(offloadReadContext);
+ offloadReadContext = NULL;
+ }
+
+__ClasspServicePopulateTokenTransferRequest_Exit:
+
+ TracePrint((TRACE_LEVEL_VERBOSE,
+ TRACE_FLAG_IOCTL,
+ "ClasspServicePopulateTokenTransferRequest (%p): Exiting function (Irp %p) with status %x.\n",
+ Fdo,
+ Irp,
+ status));
+
+ return status;
+}
+
+
+VOID
+ClasspPopulateTokenTransferPacketDone(
+ _In_ PVOID Context
+ )
+
+/*++
+
+Routine description:
+
+ This routine continues an offload read operation on completion of the
+ populate token transfer packet.
+
+ This function is responsible for continuing or completing the offload read
+ operation.
+
+Arguments:
+
+ Context - Pointer to the OFFLOAD_READ_CONTEXT for the offload read
+ operation.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PDEVICE_OBJECT fdo;
+ ULONG listIdentifier;
+ POFFLOAD_READ_CONTEXT offloadReadContext;
+ PTRANSFER_PACKET pkt;
+ PIRP pseudoIrp;
+ NTSTATUS status;
+
+ offloadReadContext = Context;
+ pseudoIrp = &offloadReadContext->PseudoIrp;
+ pkt = offloadReadContext->Pkt;
+ fdo = offloadReadContext->Fdo;
+ listIdentifier = offloadReadContext->ListIdentifier;
+
+ offloadReadContext->Pkt = NULL;
+
+
+ status = pseudoIrp->IoStatus.Status;
+ NT_ASSERT(status != STATUS_PENDING);
+
+ if (!NT_SUCCESS(status)) {
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClasspPopulateTokenTransferPacketDone (%p): Generate token for list Id %x failed with %x (Pkt %p).\n",
+ fdo,
+ listIdentifier,
+ status,
+ pkt));
+ goto __ClasspPopulateTokenTransferPacketDone_ErrorExit;
+ }
+
+ //
+ // If a token was successfully generated, it is now time to retrieve the token.
+ // The called function is responsible for completing the offload read DSM IRP.
+ //
+ ClasspReceivePopulateTokenInformation(offloadReadContext);
+
+ //
+ // ClasspReceivePopulateTokenInformation() takes care of completing the IRP,
+ // so this function is done regardless of success or failure in
+ // ClasspReceivePopulateTokenInformation().
+ //
+
+ return;
+
+ //
+ // Error cleanup label only - not used in success case:
+ //
+
+__ClasspPopulateTokenTransferPacketDone_ErrorExit:
+
+ NT_ASSERT(!NT_SUCCESS(status));
+
+ //
+ // ClasspCompleteOffloadRead also cleans up offloadReadContext.
+ //
+
+ ClasspCompleteOffloadRead(offloadReadContext, status);
+
+ return;
+}
+
+
+VOID
+ClasspCompleteOffloadRead(
+ _In_ POFFLOAD_READ_CONTEXT OffloadReadContext,
+ _In_ NTSTATUS CompletionStatus
+ )
+
+/*++
+
+Routine description:
+
+ This routine completes an offload read operation with given status, and
+ cleans up the OFFLOAD_READ_CONTEXT.
+
+Arguments:
+
+ OffloadReadContext - Pointer to the OFFLOAD_READ_CONTEXT for the offload
+ read operation.
+
+ CompletionStatus - The completion status for the offload read operation.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PDEVICE_MANAGE_DATA_SET_ATTRIBUTES dsmAttributes;
+ ULONGLONG entireXferLen;
+ PDEVICE_OBJECT fdo;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt;
+ PIRP irp;
+ NTSTATUS status;
+ PUCHAR token;
+ ULONGLONG totalSectorsProcessed;
+
+ status = CompletionStatus;
+ dsmAttributes = OffloadReadContext->OffloadReadDsmIrp->AssociatedIrp.SystemBuffer;
+ totalSectorsProcessed = OffloadReadContext->TotalSectorsProcessed;
+ fdoExt = OffloadReadContext->Fdo->DeviceExtension;
+ entireXferLen = OffloadReadContext->EntireXferLen;
+ token = OffloadReadContext->Token;
+ irp = OffloadReadContext->OffloadReadDsmIrp;
+ fdo = OffloadReadContext->Fdo;
+
+ ((PSTORAGE_OFFLOAD_READ_OUTPUT)dsmAttributes)->OffloadReadFlags = 0;
+ ((PSTORAGE_OFFLOAD_READ_OUTPUT)dsmAttributes)->Reserved = 0;
+
+ if (NT_SUCCESS(status)) {
+ ULONGLONG totalBytesProcessed = totalSectorsProcessed * fdoExt->DiskGeometry.BytesPerSector;
+
+ TracePrint((totalBytesProcessed == entireXferLen ? TRACE_LEVEL_INFORMATION : TRACE_LEVEL_WARNING,
+ TRACE_FLAG_IOCTL,
+ "ClasspCompleteOffloadRead (%p): Successfully populated token with %I64u (out of %I64u) bytes (list Id %x).\n",
+ fdo,
+ totalBytesProcessed,
+ entireXferLen,
+ OffloadReadContext->ListIdentifier));
+
+ if (totalBytesProcessed < entireXferLen) {
+ SET_FLAG(((PSTORAGE_OFFLOAD_READ_OUTPUT)dsmAttributes)->OffloadReadFlags, STORAGE_OFFLOAD_READ_RANGE_TRUNCATED);
+ }
+ ((PSTORAGE_OFFLOAD_READ_OUTPUT)dsmAttributes)->LengthProtected = totalBytesProcessed;
+ ((PSTORAGE_OFFLOAD_READ_OUTPUT)dsmAttributes)->TokenLength = STORAGE_OFFLOAD_MAX_TOKEN_LENGTH;
+ RtlCopyMemory(&((PSTORAGE_OFFLOAD_READ_OUTPUT)dsmAttributes)->Token, token, STORAGE_OFFLOAD_MAX_TOKEN_LENGTH);
+ } else {
+ ((PSTORAGE_OFFLOAD_READ_OUTPUT)dsmAttributes)->LengthProtected = 0;
+ ((PSTORAGE_OFFLOAD_READ_OUTPUT)dsmAttributes)->TokenLength = 0;
+ }
+
+ irp->IoStatus.Information = sizeof(STORAGE_OFFLOAD_READ_OUTPUT);
+
+ ClasspCompleteOffloadRequest(fdo, irp, status);
+ ClasspCleanupOffloadReadContext(OffloadReadContext);
+ OffloadReadContext = NULL;
+
+ return;
+}
+
+
+VOID
+ClasspCleanupOffloadReadContext(
+ _In_ __drv_freesMem(mem) POFFLOAD_READ_CONTEXT OffloadReadContext
+ )
+
+/*++
+
+Routine description:
+
+ This routine cleans up an OFFLOAD_READ_CONTEXT.
+
+Arguments:
+
+ OffloadReadContext - Pointer to the OFFLOAD_READ_CONTEXT for the offload
+ read operation.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PMDL populateTokenMdl;
+
+ populateTokenMdl = OffloadReadContext->PopulateTokenMdl;
+
+ NT_ASSERT(OffloadReadContext != NULL);
+
+ if (populateTokenMdl) {
+ ClasspFreeDeviceMdl(populateTokenMdl);
+ }
+ FREE_POOL(OffloadReadContext);
+
+ return;
+}
+
+
+_IRQL_requires_same_
+VOID
+ClasspReceivePopulateTokenInformation(
+ _In_ POFFLOAD_READ_CONTEXT OffloadReadContext
+ )
+
+/*++
+
+Routine description:
+
+ This routine retrieves the token after a PopulateToken command
+ has been sent down.
+
+ Can also be called repeatedly for a single offload op when a previous
+ RECEIVE ROD TOKEN INFORMATION for that op indicated that the operation was
+ not yet complete.
+
+ This function is responsible for continuing or completing the offload read
+ operation.
+
+Arguments:
+
+ OffloadReadContext - Pointer to the OFFLOAD_READ_CONTEXT for the offload
+ read operation.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PVOID buffer;
+ ULONG bufferLength;
+ ULONG cdbLength;
+ PDEVICE_OBJECT fdo;
+ PIRP irp;
+ ULONG listIdentifier;
+ PTRANSFER_PACKET pkt;
+ PIRP pseudoIrp;
+ ULONG receiveTokenInformationBufferLength;
+ PSCSI_REQUEST_BLOCK srb;
+ NTSTATUS status;
+ ULONG tempSizeUlong;
+ PULONGLONG totalSectorsProcessed;
+
+ totalSectorsProcessed = &OffloadReadContext->TotalSectorsProcessed;
+ buffer = OffloadReadContext + 1;
+ bufferLength = OffloadReadContext->BufferLength;
+ fdo = OffloadReadContext->Fdo;
+ irp = OffloadReadContext->OffloadReadDsmIrp;
+ receiveTokenInformationBufferLength = OffloadReadContext->ReceiveTokenInformationBufferLength;
+ listIdentifier = OffloadReadContext->ListIdentifier;
+
+ TracePrint((TRACE_LEVEL_VERBOSE,
+ TRACE_FLAG_IOCTL,
+ "ClasspReceivePopulateTokenInformation (%p): Entering function. Irp %p\n",
+ fdo,
+ irp));
+
+ srb = &OffloadReadContext->Srb;
+ *totalSectorsProcessed = 0;
+
+ pkt = DequeueFreeTransferPacket(fdo, TRUE);
+ if (!pkt){
+
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClasspReceivePopulateTokenInformation (%p): Failed to retrieve transfer packet for ReceiveTokenInformation (PopulateToken) operation.\n",
+ fdo));
+
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ goto __ClasspReceivePopulateTokenInformation_ErrorExit;
+ }
+
+ OffloadReadContext->Pkt = pkt;
+
+ RtlZeroMemory(buffer, bufferLength);
+
+ tempSizeUlong = receiveTokenInformationBufferLength - 4;
+ REVERSE_BYTES(((PRECEIVE_TOKEN_INFORMATION_HEADER)buffer)->AvailableData, &tempSizeUlong);
+
+ pseudoIrp = &OffloadReadContext->PseudoIrp;
+ RtlZeroMemory(pseudoIrp, sizeof(IRP));
+
+
+ pseudoIrp->IoStatus.Status = STATUS_SUCCESS;
+ pseudoIrp->IoStatus.Information = 0;
+ pseudoIrp->Tail.Overlay.DriverContext[0] = LongToPtr(1);
+ pseudoIrp->MdlAddress = OffloadReadContext->PopulateTokenMdl;
+
+ ClasspSetupReceivePopulateTokenInformationTransferPacket(
+ OffloadReadContext,
+ pkt,
+ receiveTokenInformationBufferLength,
+ (PUCHAR)buffer,
+ pseudoIrp,
+ listIdentifier);
+
+ //
+ // Cache away the CDB as it may be required for forwarded sense data
+ // after this command completes.
+ //
+ RtlZeroMemory(srb, sizeof(*srb));
+ cdbLength = SrbGetCdbLength(pkt->Srb);
+ if (cdbLength <= 16) {
+ RtlCopyMemory(&srb->Cdb, SrbGetCdb(pkt->Srb), cdbLength);
+ }
+
+ SubmitTransferPacket(pkt);
+
+ return;
+
+ //
+ // Error cleanup label only - not used in success case:
+ //
+
+__ClasspReceivePopulateTokenInformation_ErrorExit:
+
+ NT_ASSERT(!NT_SUCCESS(status));
+
+ //
+ // ClasspCompleteOffloadRead also cleans up offloadReadContext.
+ //
+
+ ClasspCompleteOffloadRead(OffloadReadContext, status);
+
+ return;
+}
+
+
+VOID
+ClasspReceivePopulateTokenInformationTransferPacketDone(
+ _In_ PVOID Context
+ )
+
+/*++
+
+Routine description:
+
+ This routine continues an offload read operation on completion of the
+ RECEIVE ROD TOKEN INFORMATION transfer packet.
+
+ This routine is responsible for continuing or completing the offload read
+ operation.
+
+Arguments:
+
+ Context - Pointer to the OFFLOAD_READ_CONTEXT for the offload read
+ operation.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ ULONG availableData;
+ PVOID buffer;
+ UCHAR completionStatus;
+ ULONG estimatedRetryInterval;
+ PDEVICE_OBJECT fdo;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt;
+ PIRP irp;
+ ULONG listIdentifier;
+ POFFLOAD_READ_CONTEXT offloadReadContext;
+ BOOLEAN operationCompleted;
+ UCHAR operationStatus;
+ PIRP pseudoIrp;
+ USHORT segmentsProcessed;
+ PSENSE_DATA senseData;
+ ULONG senseDataFieldLength;
+ UCHAR senseDataLength;
+ PSCSI_REQUEST_BLOCK srb;
+ NTSTATUS status;
+ PUCHAR token;
+ PVOID tokenAscii;
+ PBLOCK_DEVICE_TOKEN_DESCRIPTOR tokenDescriptor;
+ ULONG tokenDescriptorLength;
+ PRECEIVE_TOKEN_INFORMATION_HEADER tokenInformationResults;
+ PRECEIVE_TOKEN_INFORMATION_RESPONSE_HEADER tokenInformationResultsResponse;
+ ULONG tokenLength;
+ ULONG tokenSize;
+ PULONGLONG totalSectorsProcessed;
+ ULONGLONG totalSectorsToProcess;
+ ULONGLONG transferBlockCount;
+
+ offloadReadContext = Context;
+ fdo = offloadReadContext->Fdo;
+ fdoExt = fdo->DeviceExtension;
+ buffer = offloadReadContext + 1;
+ listIdentifier = offloadReadContext->ListIdentifier;
+ irp = offloadReadContext->OffloadReadDsmIrp;
+ totalSectorsToProcess = offloadReadContext->TotalSectorsToProcess;
+ totalSectorsProcessed = &offloadReadContext->TotalSectorsProcessed;
+ srb = &offloadReadContext->Srb;
+ tokenAscii = NULL;
+ tokenSize = BLOCK_DEVICE_TOKEN_SIZE;
+ tokenInformationResults = (PRECEIVE_TOKEN_INFORMATION_HEADER)buffer;
+ senseData = (PSENSE_DATA)((PUCHAR)tokenInformationResults + FIELD_OFFSET(RECEIVE_TOKEN_INFORMATION_HEADER, SenseData));
+ transferBlockCount = 0;
+ tokenInformationResultsResponse = NULL;
+ tokenDescriptor = NULL;
+ operationCompleted = FALSE;
+ tokenDescriptorLength = 0;
+ tokenLength = 0;
+ token = NULL;
+ pseudoIrp = &offloadReadContext->PseudoIrp;
+
+ status = pseudoIrp->IoStatus.Status;
+ NT_ASSERT(status != STATUS_PENDING);
+
+ //
+ // The buffer we hand allows for the max sizes for all the fields whereas the returned
+ // data may be lesser (e.g. sense data info will almost never be MAX_SENSE_BUFFER_SIZE, etc.
+ // so handle underrun "error"
+ //
+ if (status == STATUS_DATA_OVERRUN) {
+
+ status = STATUS_SUCCESS;
+ }
+
+ if (!NT_SUCCESS(status)) {
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClasspReceivePopulateTokenInformationTransferPacketDone (%p): Token retrieval failed for list Id %x with %x.\n",
+ fdo,
+ listIdentifier,
+ status));
+ goto __ClasspReceivePopulateTokenInformationTransferPacketDone_Exit;
+ }
+
+ REVERSE_BYTES(&availableData, &tokenInformationResults->AvailableData);
+
+ NT_ASSERT(availableData <= FIELD_OFFSET(RECEIVE_TOKEN_INFORMATION_HEADER, SenseData) + MAX_SENSE_BUFFER_SIZE +
+ FIELD_OFFSET(RECEIVE_TOKEN_INFORMATION_RESPONSE_HEADER, TokenDescriptor) + BLOCK_DEVICE_TOKEN_SIZE);
+
+ NT_ASSERT(tokenInformationResults->ResponseToServiceAction == SERVICE_ACTION_POPULATE_TOKEN);
+
+ operationStatus = tokenInformationResults->OperationStatus;
+ operationCompleted = ClasspIsTokenOperationComplete(operationStatus);
+ NT_ASSERT(operationCompleted);
+
+ REVERSE_BYTES(&estimatedRetryInterval, &tokenInformationResults->EstimatedStatusUpdateDelay);
+
+ completionStatus = tokenInformationResults->CompletionStatus;
+
+ NT_ASSERT(tokenInformationResults->TransferCountUnits == TRANSFER_COUNT_UNITS_NUMBER_BLOCKS);
+ REVERSE_BYTES_QUAD(&transferBlockCount, &tokenInformationResults->TransferCount);
+
+ REVERSE_BYTES_SHORT(&segmentsProcessed, &tokenInformationResults->SegmentsProcessed);
+ NT_ASSERT(segmentsProcessed == 0);
+
+ if (operationCompleted) {
+
+ if (transferBlockCount > totalSectorsToProcess) {
+
+ //
+ // Buggy or hostile target. Don't let it claim more was procesed
+ // than was requested. Since this is likely a bug and it's unknown
+ // how much was actually transferred, assume no data was
+ // transferred.
+ //
+
+ NT_ASSERT(transferBlockCount <= totalSectorsToProcess);
+ transferBlockCount = 0;
+ }
+
+ if (operationStatus != OPERATION_COMPLETED_WITH_SUCCESS &&
+ operationStatus != OPERATION_COMPLETED_WITH_RESIDUAL_DATA) {
+
+ //
+ // Assert on buggy response from target, but in any case, make sure not
+ // to claim that any data was written.
+ //
+
+ NT_ASSERT(transferBlockCount == 0);
+ transferBlockCount = 0;
+ }
+
+ //
+ // Since the TokenOperation was sent down synchronously, the operation is complete as soon as the command returns.
+ //
+
+ senseDataFieldLength = tokenInformationResults->SenseDataFieldLength;
+ senseDataLength = tokenInformationResults->SenseDataLength;
+ NT_ASSERT(senseDataFieldLength >= senseDataLength);
+
+ tokenInformationResultsResponse = (PRECEIVE_TOKEN_INFORMATION_RESPONSE_HEADER)((PUCHAR)tokenInformationResults +
+ FIELD_OFFSET(RECEIVE_TOKEN_INFORMATION_HEADER, SenseData) +
+ tokenInformationResults->SenseDataFieldLength);
+
+ REVERSE_BYTES(&tokenDescriptorLength, &tokenInformationResultsResponse->TokenDescriptorsLength);
+
+ if (tokenDescriptorLength > 0) {
+
+ NT_ASSERT(tokenDescriptorLength == sizeof(BLOCK_DEVICE_TOKEN_DESCRIPTOR));
+
+ if (tokenDescriptorLength != sizeof(BLOCK_DEVICE_TOKEN_DESCRIPTOR)) {
+
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClasspReceivePopulateTokenInformationTransferPacketDone (%p): Bad firmware, token descriptor length %u.\n",
+ fdo,
+ tokenDescriptorLength));
+
+ NT_ASSERT((*totalSectorsProcessed) == 0);
+ NT_ASSERT(tokenLength == 0);
+
+ } else {
+
+ USHORT restrictedId;
+
+ tokenDescriptor = (PBLOCK_DEVICE_TOKEN_DESCRIPTOR)tokenInformationResultsResponse->TokenDescriptor;
+
+ REVERSE_BYTES_SHORT(&restrictedId, &tokenDescriptor->TokenIdentifier);
+ NT_ASSERT(restrictedId == 0);
+
+ tokenLength = BLOCK_DEVICE_TOKEN_SIZE;
+ token = tokenDescriptor->Token;
+
+ *totalSectorsProcessed = transferBlockCount;
+
+ if (transferBlockCount < totalSectorsToProcess) {
+
+ NT_ASSERT(operationStatus == OPERATION_COMPLETED_WITH_RESIDUAL_DATA ||
+ operationStatus == OPERATION_COMPLETED_WITH_ERROR ||
+ operationStatus == OPERATION_TERMINATED);
+
+ if (transferBlockCount == 0) {
+ //
+ // Treat the same as not getting a token.
+ //
+
+ tokenLength = 0;
+ }
+
+ } else {
+
+ NT_ASSERT(operationStatus == OPERATION_COMPLETED_WITH_SUCCESS);
+ NT_ASSERT(transferBlockCount == totalSectorsToProcess);
+ }
+
+ //
+ // Need to convert to ascii.
+ //
+ tokenAscii = ClasspBinaryToAscii((PUCHAR)token,
+ tokenSize,
+ &tokenSize);
+
+ TracePrint((transferBlockCount == totalSectorsToProcess ? TRACE_LEVEL_INFORMATION : TRACE_LEVEL_WARNING,
+ TRACE_FLAG_IOCTL,
+ "ClasspReceivePopulateTokenInformationTransferPacketDone (%p): %wsToken %s generated successfully for list Id %x for data size %I64u bytes.\n",
+ fdo,
+ transferBlockCount == totalSectorsToProcess ? L"" : L"Target truncated read. ",
+ (tokenAscii == NULL) ? "" : tokenAscii,
+ listIdentifier,
+ (*totalSectorsProcessed) * fdoExt->DiskGeometry.BytesPerSector));
+
+ FREE_POOL(tokenAscii);
+ }
+ } else {
+
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClasspReceivePopulateTokenInformationTransferPacketDone (%p): Target failed to generate a token for list Id %x for data size %I64u bytes (requested %I64u bytes).\n",
+ fdo,
+ listIdentifier,
+ transferBlockCount * fdoExt->DiskGeometry.BytesPerSector,
+ totalSectorsToProcess * fdoExt->DiskGeometry.BytesPerSector));
+
+ *totalSectorsProcessed = 0;
+
+ NT_ASSERT(operationStatus == OPERATION_COMPLETED_WITH_ERROR);
+ }
+
+ //
+ // Operation that completes with success can have sense data (for target to pass on some extra info)
+ // but we don't care about such sense info.
+ // Operation that complete but not with success, may not have sense data associated, but may
+ // have valid CompletionStatus.
+ //
+ // The "status" may be overriden by ClassInterpretSenseInfo(). Final
+ // status is determined a bit later - this is just the default status
+ // when ClassInterpretSenseInfo() doesn't get to run here.
+ //
+ status = STATUS_SUCCESS;
+ if (operationStatus == OPERATION_COMPLETED_WITH_ERROR ||
+ operationStatus == OPERATION_COMPLETED_WITH_RESIDUAL_DATA ||
+ operationStatus == OPERATION_TERMINATED) {
+
+ SrbSetScsiStatus((PSTORAGE_REQUEST_BLOCK_HEADER)srb, completionStatus);
+
+ if (senseDataLength) {
+
+ ULONG retryInterval;
+
+ NT_ASSERT(senseDataLength <= sizeof(SENSE_DATA));
+
+ srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
+
+ srb->SrbStatus = SRB_STATUS_AUTOSENSE_VALID | SRB_STATUS_ERROR;
+ SrbSetSenseInfoBuffer((PSTORAGE_REQUEST_BLOCK_HEADER)srb, senseData);
+ SrbSetSenseInfoBufferLength((PSTORAGE_REQUEST_BLOCK_HEADER)srb, senseDataLength);
+
+ ClassInterpretSenseInfo(fdo,
+ srb,
+ IRP_MJ_SCSI,
+ 0,
+ 0,
+ &status,
+ &retryInterval);
+
+ TracePrint((TRACE_LEVEL_WARNING,
+ TRACE_FLAG_IOCTL,
+ "ClasspReceivePopulateTokenInformationTransferPacketDone (%p): Reason for truncation/failure: %x - for list Id %x for data size %I64u bytes.\n",
+ fdo,
+ status,
+ listIdentifier,
+ transferBlockCount * fdoExt->DiskGeometry.BytesPerSector));
+ } else {
+
+ TracePrint((TRACE_LEVEL_WARNING,
+ TRACE_FLAG_IOCTL,
+ "ClasspReceivePopulateTokenInformationTransferPacketDone (%p): No sense data available but reason for truncation/failure, possibly: %x - for list Id %x for data size %I64u bytes.\n",
+ fdo,
+ completionStatus,
+ listIdentifier,
+ transferBlockCount * fdoExt->DiskGeometry.BytesPerSector));
+ }
+ }
+
+ if (tokenLength > 0) {
+
+ offloadReadContext->Token = token;
+
+ //
+ // Even if target returned an error, from the OS upper layers' perspective,
+ // it is a success (with truncation) if any data at all was read.
+ //
+ status = STATUS_SUCCESS;
+
+ } else {
+
+ if (NT_SUCCESS(status)) {
+ //
+ // Make sure status is a failing status, without throwing away an
+ // already-failing status obtained from sense data.
+ //
+ status = STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ //
+ // Done with the operation.
+ //
+
+ NT_ASSERT(status != STATUS_PENDING);
+ goto __ClasspReceivePopulateTokenInformationTransferPacketDone_Exit;
+
+ } else {
+
+ status = STATUS_UNSUCCESSFUL;
+
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClasspReceivePopulateTokenInformationTransferPacketDone (%p): Token retrieval failed for list Id %x with %x.\n",
+ fdo,
+ listIdentifier,
+ status));
+
+ NT_ASSERT(*totalSectorsProcessed == 0);
+ goto __ClasspReceivePopulateTokenInformationTransferPacketDone_Exit;
+ }
+
+__ClasspReceivePopulateTokenInformationTransferPacketDone_Exit:
+
+ if (status != STATUS_PENDING) {
+
+ //
+ // The "status" value can be success or failure at this point, as
+ // appropriate.
+ //
+
+ ClasspCompleteOffloadRead(offloadReadContext, status);
+ }
+
+ //
+ // Due to tracing a potentially freed pointer value "Irp", this trace could
+ // be delayed beyond another offload op picking up the same pointer value
+ // for its Irp. This function exits after the operation is complete when
+ // status != STATUS_PENDING.
+ //
+
+ TracePrint((TRACE_LEVEL_VERBOSE,
+ TRACE_FLAG_IOCTL,
+ "ClasspReceivePopulateTokenInformationTransferPacketDone (%p): Exiting function (Irp %p) with internal status %x.\n",
+ fdo,
+ irp,
+ status));
+
+ return;
+}
+
+
+_IRQL_requires_max_(APC_LEVEL)
+_IRQL_requires_min_(PASSIVE_LEVEL)
+_IRQL_requires_same_
+NTSTATUS
+ClasspServiceWriteUsingTokenTransferRequest(
+ _In_ PDEVICE_OBJECT Fdo,
+ _In_ PIRP Irp
+ )
+
+/*++
+
+Routine description:
+
+ This routine processes offload write requests by building the SRB
+ for WriteUsingToken.
+
+Arguments:
+
+ Fdo - The functional device object processing the request
+ Irp - The Io request to be processed
+
+Return Value:
+
+ STATUS_SUCCESS if successful, an error code otherwise
+
+--*/
+
+{
+ ULONG allocationSize;
+ PVOID buffer;
+ ULONG bufferLength;
+ PDEVICE_DATA_SET_RANGE dataSetRanges;
+ ULONG dataSetRangesCount;
+ PDEVICE_MANAGE_DATA_SET_ATTRIBUTES dsmAttributes;
+ ULONGLONG entireXferLen;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt;
+ ULONG i;
+ ULONGLONG logicalBlockOffset;
+ ULONG maxBlockDescrCount;
+ ULONGLONG maxLbaCount;
+ POFFLOAD_WRITE_CONTEXT offloadWriteContext;
+ PDEVICE_DSM_OFFLOAD_WRITE_PARAMETERS offloadWriteParameters;
+ ULONG receiveTokenInformationBufferLength;
+ NTSTATUS status;
+ NTSTATUS tempStatus;
+ BOOLEAN tokenInvalidated;
+ ULONG tokenOperationBufferLength;
+ PMDL writeUsingTokenMdl;
+
+ PAGED_CODE();
+
+ TracePrint((TRACE_LEVEL_VERBOSE,
+ TRACE_FLAG_IOCTL,
+ "ClasspServiceWriteUsingTokenTransferRequest (%p): Entering function. Irp %p.\n",
+ Fdo,
+ Irp));
+
+ fdoExt = Fdo->DeviceExtension;
+ status = STATUS_SUCCESS;
+ tempStatus = STATUS_SUCCESS;
+ dsmAttributes = Irp->AssociatedIrp.SystemBuffer;
+ buffer = NULL;
+ writeUsingTokenMdl = NULL;
+ offloadWriteParameters = Add2Ptr(dsmAttributes, dsmAttributes->ParameterBlockOffset);
+ dataSetRanges = Add2Ptr(dsmAttributes, dsmAttributes->DataSetRangesOffset);
+ dataSetRangesCount = dsmAttributes->DataSetRangesLength / sizeof(DEVICE_DATA_SET_RANGE);
+ logicalBlockOffset = offloadWriteParameters->TokenOffset / fdoExt->DiskGeometry.BytesPerSector;
+ tokenInvalidated = FALSE;
+ bufferLength = 0;
+
+
+ NT_ASSERT(fdoExt->FunctionSupportInfo->ValidInquiryPages.BlockDeviceRODLimits &&
+ NT_SUCCESS(fdoExt->FunctionSupportInfo->BlockDeviceRODLimitsData.CommandStatus));
+
+ for (i = 0, entireXferLen = 0; i < dataSetRangesCount; i++) {
+ entireXferLen += dataSetRanges[i].LengthInBytes;
+ }
+
+ //
+ // We need to split the write request based on the following hardware limitations:
+ // 1. The size of the data buffer containing the TokenOperation command's parameters must
+ // not exceed the MaximumTransferLength (and max physical pages) of the underlying
+ // adapter.
+ // 2. The number of descriptors specified in the TokenOperation command must not exceed
+ // the MaximumRangeDescriptors.
+ // 3. The cumulative total of the number of transfer blocks in all the descriptors in
+ // the TokenOperation command must not exceed the MaximumTokenTransferSize.
+ //
+ // In addition to the above, we need to ensure that for each of the descriptors in the
+ // TokenOperation command:
+ // 1. The number of blocks specified is an exact multiple of the OptimalTransferLengthGranularity.
+ // 2. The number of blocks specified is limited to the MaximumTransferLength. (We shall
+ // however, limit the number of blocks specified in each descriptor to be a maximum of
+ // OptimalTransferLength or MaximumTransferLength, whichever is lesser).
+ //
+ // Finally, we shall always send down the WriteUsingToken command using IMMED = 0 for this
+ // release. This makes it simpler to handle multi-initiator scenarios since we won't need
+ // to deal with the WriteUsingToken (with IMMED = 1) succeeding but ReceiveRODTokenInformation
+ // failing due to the path/node failing, thus making it impossible to retrieve results of
+ // the data transfer operation, or to cancel the data transfer via CopyOperationAbort.
+ // Since a write data transfer of a large amount of data may take a long time when sent
+ // down IMMED = 0, we shall limit the size to a maximum of 256MB even if it means truncating
+ // the original requested size to this capped size. The application is expected to deal with
+ // this truncation.
+ // (NOTE: the cap of 256MB is chosen to match with the Copy Engine's chunk size).
+ //
+ // The LBA ranges is in DEVICE_DATA_SET_RANGE format, it needs to be converted into
+ // WINDOWS_BLOCK_DEVICE_RANGE_DESCRIPTOR Block Descriptors.
+ //
+
+ ClasspGetTokenOperationCommandBufferLength(Fdo,
+ SERVICE_ACTION_WRITE_USING_TOKEN,
+ &bufferLength,
+ &tokenOperationBufferLength,
+ &receiveTokenInformationBufferLength);
+
+ allocationSize = sizeof(OFFLOAD_WRITE_CONTEXT) + bufferLength;
+
+ offloadWriteContext = ExAllocatePoolWithTag(
+ NonPagedPoolNx,
+ allocationSize,
+ CLASSPNP_POOL_TAG_TOKEN_OPERATION);
+
+ if (!offloadWriteContext) {
+
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClasspServiceWriteUsingTokenTransferRequest (%p): Failed to allocate buffer for WriteUsingToken operations.\n",
+ Fdo));
+
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ goto __ClasspServiceWriteUsingTokenTransferRequest_ErrorExit;
+ }
+
+ //
+ // Only zero the context portion here. The buffer portion is zeroed for
+ // each sub-request.
+ //
+ RtlZeroMemory(offloadWriteContext, sizeof(OFFLOAD_WRITE_CONTEXT));
+
+ offloadWriteContext->Fdo = Fdo;
+ offloadWriteContext->OffloadWriteDsmIrp = Irp;
+ offloadWriteContext->OperationStartTime = KeQueryInterruptTime();
+
+ //
+ // The buffer for the commands is after the offloadWriteContext.
+ //
+ buffer = (offloadWriteContext + 1);
+
+ //
+ // Set up fields that allow iterating through whole request, by issuing sub-
+ // requests which each do some of the writing. Because of truncation by the
+ // target, it's not known exactly how many bytes will be written by the
+ // target in each sub-request (can be less than requested), so the
+ // progress through the outer request must only commit the move through the
+ // upper DSM ranges when a lower request is done and the number of written
+ // sectors is known.
+ //
+
+ NT_ASSERT(offloadWriteContext->TotalSectorsProcessedSuccessfully == 0);
+ offloadWriteContext->TotalRequestSizeSectors = entireXferLen / fdoExt->DiskGeometry.BytesPerSector;
+ NT_ASSERT(offloadWriteContext->DataSetRangeIndex == 0);
+ NT_ASSERT(offloadWriteContext->DataSetRangeByteOffset == 0);
+ offloadWriteContext->DataSetRangesCount = dataSetRangesCount;
+
+ offloadWriteContext->DsmAttributes = dsmAttributes;
+ offloadWriteContext->OffloadWriteParameters = offloadWriteParameters;
+ offloadWriteContext->DataSetRanges = dataSetRanges;
+ offloadWriteContext->LogicalBlockOffset = logicalBlockOffset;
+
+ ClasspGetTokenOperationDescriptorLimits(Fdo,
+ SERVICE_ACTION_WRITE_USING_TOKEN,
+ tokenOperationBufferLength,
+ &maxBlockDescrCount,
+ &maxLbaCount);
+
+ if (fdoExt->FunctionSupportInfo->BlockDeviceRODLimitsData.OptimalTransferCount && fdoExt->FunctionSupportInfo->BlockDeviceRODLimitsData.MaximumTokenTransferSize) {
+
+ NT_ASSERT(fdoExt->FunctionSupportInfo->BlockDeviceRODLimitsData.OptimalTransferCount <= fdoExt->FunctionSupportInfo->BlockDeviceRODLimitsData.MaximumTokenTransferSize);
+ }
+
+ //
+ // We will limit the maximum data transfer in an offload write operation to:
+ // - 64MB if OptimalTransferCount = 0
+ // - OptimalTransferCount if < 256MB
+ // - 256MB if OptimalTransferCount >= 256MB
+ // - MaximumTokenTransferSize if lesser than above chosen size
+ //
+ if (0 == fdoExt->FunctionSupportInfo->BlockDeviceRODLimitsData.OptimalTransferCount) {
+
+ maxLbaCount = MIN(maxLbaCount, (DEFAULT_MAX_NUMBER_BYTES_PER_SYNC_WRITE_USING_TOKEN / (ULONGLONG)fdoExt->DiskGeometry.BytesPerSector));
+
+ } else if (fdoExt->FunctionSupportInfo->BlockDeviceRODLimitsData.OptimalTransferCount < (MAX_NUMBER_BYTES_PER_SYNC_WRITE_USING_TOKEN / (ULONGLONG)fdoExt->DiskGeometry.BytesPerSector)) {
+
+ maxLbaCount = MIN(maxLbaCount, fdoExt->FunctionSupportInfo->BlockDeviceRODLimitsData.OptimalTransferCount);
+
+ } else {
+
+ maxLbaCount = MIN(maxLbaCount, (MAX_NUMBER_BYTES_PER_SYNC_WRITE_USING_TOKEN / (ULONGLONG)fdoExt->DiskGeometry.BytesPerSector));
+ }
+
+ //
+ // Since we do not want very fragmented files to end up causing the WriteUsingToken command to take
+ // too long (and potentially timeout), we will limit the max number of descriptors sent down in a
+ // command.
+ //
+ maxBlockDescrCount = MIN(maxBlockDescrCount, MAX_NUMBER_BLOCK_DEVICE_DESCRIPTORS);
+
+ TracePrint((TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_IOCTL,
+ "ClasspServiceWriteUsingTokenTransferRequest (%p): Using MaxBlockDescrCount %u and MaxLbaCount %I64u.\n",
+ Fdo,
+ maxBlockDescrCount,
+ maxLbaCount));
+
+ offloadWriteContext->MaxBlockDescrCount = maxBlockDescrCount;
+ offloadWriteContext->MaxLbaCount = maxLbaCount;
+
+ //
+ // Reuse a single buffer for both TokenOperation and ReceiveTokenInformation. This has the one disadvantage
+ // that we'll be marking the page(s) as IoWriteAccess even though we only need read access for token
+ // operation command and we may not even need to send down a ReceiveTokenInformation (in case of a successful
+ // synchronous transfer). However, the advantage is that we eliminate the possibility of any potential
+ // failures when trying to allocate an MDL for the ReceiveTokenInformation later on if we need to send it.
+ //
+ writeUsingTokenMdl = ClasspBuildDeviceMdl(buffer, bufferLength, FALSE);
+ if (!writeUsingTokenMdl) {
+
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClasspServiceWriteUsingTokenTransferRequest (%p): Failed to allocate MDL for WriteUsingToken operations.\n",
+ Fdo));
+
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ goto __ClasspServiceWriteUsingTokenTransferRequest_ErrorExit;
+ }
+
+ offloadWriteContext->WriteUsingTokenMdl = writeUsingTokenMdl;
+
+ //
+ // There are potentially two approaches that we can take:
+ // 1. Determine how many transfer packets we need (in case we need to split the request), get
+ // them all up-front and then send down all the split WriteUsingToken commands in parallel.
+ // The benefit of this approach is that the performance will be improved in the success case.
+ // But error case handling becomes very complex to handle, since if one of the intermediate
+ // write fails, there is no way to cancel the remaining writes that were sent. Waiting for
+ // such requests to complete in geo-distributed source and target cases can be very time
+ // consuming. The complexity gets worse in the case that the target succeeds only a partial
+ // amount of data for one the of intermediate split commands.
+ // [OR]
+ // 2. Until the entire data set range is processed, build the command for as much of the range as
+ // possible, send down a packet, and once it completes, repeat sequentially in a loop.
+ // The advantage of this approach is its simplistic nature. In the success case, it will
+ // be less performant as compared to the previous approach, but since the gain of offload
+ // copy is so significant compared to native buffered-copy, the tradeoff is acceptable.
+ // In the failure case the simplicity offers the following benefit - if any command fails,
+ // there is no further processing that is needed. And the cumulative total bytes that succeeded
+ // (until the failing split WriteUsingToken command) is easily tracked.
+ //
+ // Given the above, we're going with the second approach.
+ //
+
+ NT_ASSERT(status == STATUS_SUCCESS); // so far
+
+ //
+ // Save (into the offloadReadContext) any remaining things that
+ // ClasspPopulateTokenTransferPacketDone() will need.
+ //
+
+ offloadWriteContext->BufferLength = bufferLength;
+ offloadWriteContext->ReceiveTokenInformationBufferLength = receiveTokenInformationBufferLength;
+ offloadWriteContext->EntireXferLen = entireXferLen;
+
+ IoMarkIrpPending(Irp);
+ ClasspContinueOffloadWrite(offloadWriteContext);
+
+ status = STATUS_PENDING;
+ goto __ClasspServiceWriteUsingTokenTransferRequest_Exit;
+
+ //
+ // Error label only - not used in success case:
+ //
+
+__ClasspServiceWriteUsingTokenTransferRequest_ErrorExit:
+
+ NT_ASSERT(status != STATUS_PENDING);
+
+ if (offloadWriteContext != NULL) {
+ ClasspCleanupOffloadWriteContext(offloadWriteContext);
+ offloadWriteContext = NULL;
+ }
+
+__ClasspServiceWriteUsingTokenTransferRequest_Exit:
+
+ TracePrint((TRACE_LEVEL_VERBOSE,
+ TRACE_FLAG_IOCTL,
+ "ClasspServiceWriteUsingTokenTransferRequest (%p): Exiting function (Irp %p) with status %x.\n",
+ Fdo,
+ Irp,
+ status));
+
+ return status;
+}
+
+
+VOID
+#pragma warning(suppress: 28194) // This function will either alias or free OffloadWriteContext
+ClasspContinueOffloadWrite(
+ _In_ __drv_aliasesMem POFFLOAD_WRITE_CONTEXT OffloadWriteContext
+ )
+
+/*++
+
+Routine description:
+
+ This routine continues an offload write operation. This routine expects the
+ offload write operation to be set up and ready to start a WRITE USING TOKEN,
+ but with no WRITE USING TOKEN currently in flight.
+
+ This routine is responsible for continuing or completing the offload write
+ operation.
+
+Arguments:
+
+ OffloadWriteContext - Pointer to the OFFLOAD_WRITE_CONTEXT for the offload
+ write operation.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ BOOLEAN allDataSetRangeFullyConverted;
+ ULONG blockDescrIndex;
+ PBLOCK_DEVICE_RANGE_DESCRIPTOR blockDescrPointer;
+ PVOID buffer;
+ ULONG bufferLength;
+ ULONGLONG dataSetRangeByteOffset;
+ ULONG dataSetRangeIndex;
+ PDEVICE_DATA_SET_RANGE dataSetRanges;
+ ULONG dataSetRangesCount;
+ ULONGLONG entireXferLen;
+ PDEVICE_OBJECT fdo;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt;
+ PIRP irp;
+ ULONG lbaCount;
+ ULONG listIdentifier;
+ ULONGLONG logicalBlockOffset;
+ ULONG maxBlockDescrCount;
+ ULONGLONG maxLbaCount;
+ PDEVICE_DSM_OFFLOAD_WRITE_PARAMETERS offloadWriteParameters;
+ PTRANSFER_PACKET pkt;
+ PIRP pseudoIrp;
+ NTSTATUS status;
+ DEVICE_DATA_SET_RANGE tempDataSetRange;
+ BOOLEAN tempDataSetRangeFullyConverted;
+ PVOID tokenAscii;
+ ULONG tokenSize;
+ ULONGLONG totalSectorCount;
+ ULONGLONG totalSectorsProcessedSuccessfully;
+ ULONGLONG totalSectorsToProcess;
+ ULONG transferSize;
+ USHORT writeUsingTokenDataLength;
+ USHORT writeUsingTokenDescriptorsLength;
+ PMDL writeUsingTokenMdl;
+
+ tokenAscii = NULL;
+ tokenSize = BLOCK_DEVICE_TOKEN_SIZE;
+ tempDataSetRangeFullyConverted = FALSE;
+ allDataSetRangeFullyConverted = FALSE;
+ fdo = OffloadWriteContext->Fdo;
+ fdoExt = fdo->DeviceExtension;
+ irp = OffloadWriteContext->OffloadWriteDsmIrp;
+ buffer = OffloadWriteContext + 1;
+ bufferLength = OffloadWriteContext->BufferLength;
+ dataSetRanges = OffloadWriteContext->DataSetRanges;
+ offloadWriteParameters = OffloadWriteContext->OffloadWriteParameters;
+ pseudoIrp = &OffloadWriteContext->PseudoIrp;
+ writeUsingTokenMdl = OffloadWriteContext->WriteUsingTokenMdl;
+ entireXferLen = OffloadWriteContext->EntireXferLen;
+
+ RtlZeroMemory(buffer, bufferLength);
+
+ pkt = DequeueFreeTransferPacket(fdo, TRUE);
+ if (!pkt){
+
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClasspContinueOffloadWrite (%p): Failed to retrieve transfer packet for TokenOperation (WriteUsingToken) operation.\n",
+ fdo));
+
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ goto __ClasspContinueOffloadWrite_ErrorExit;
+ }
+
+ OffloadWriteContext->Pkt = pkt;
+
+ blockDescrPointer = (PBLOCK_DEVICE_RANGE_DESCRIPTOR)
+ &((PWRITE_USING_TOKEN_HEADER)buffer)->BlockDeviceRangeDescriptor[0];
+
+ blockDescrIndex = 0;
+ lbaCount = 0;
+
+ totalSectorsToProcess = 0;
+
+ maxBlockDescrCount = OffloadWriteContext->MaxBlockDescrCount;
+ maxLbaCount = OffloadWriteContext->MaxLbaCount;
+
+ //
+ // The OffloadWriteContext->DataSetRangeIndex, DataSetRangeByteOffset, and
+ // TotalSectorsProcessedSuccessfully don't move forward until RRTI has
+ // reported the actual amount written.
+ //
+ // For that reason, this function only updates
+ // OffloadWriteContext->TotalSectorsProcessed, which tracks the number of
+ // sectors requested to be written by the current WRITE USING TOKEN command
+ // (not all will necessarily be written).
+ //
+
+ dataSetRangeIndex = OffloadWriteContext->DataSetRangeIndex;
+ dataSetRangesCount = OffloadWriteContext->DataSetRangesCount;
+ dataSetRangeByteOffset = OffloadWriteContext->DataSetRangeByteOffset;
+ totalSectorsProcessedSuccessfully = OffloadWriteContext->TotalSectorsProcessedSuccessfully;
+
+ //
+ // Send WriteUsingToken commands when the buffer is full or all input entries are converted.
+ //
+ while (!((blockDescrIndex == maxBlockDescrCount) || // buffer full or block descriptor count reached
+ (lbaCount == maxLbaCount) || // block LBA count reached
+ (allDataSetRangeFullyConverted))) { // all DataSetRanges have been converted
+
+ NT_ASSERT(dataSetRangeIndex < dataSetRangesCount);
+ NT_ASSERT(dataSetRangeByteOffset < dataSetRanges[dataSetRangeIndex].LengthInBytes);
+
+ tempDataSetRange.StartingOffset = dataSetRanges[dataSetRangeIndex].StartingOffset + dataSetRangeByteOffset;
+ tempDataSetRange.LengthInBytes = dataSetRanges[dataSetRangeIndex].LengthInBytes - dataSetRangeByteOffset;
+
+ totalSectorCount = 0;
+
+ ClasspConvertDataSetRangeToBlockDescr(fdo,
+ blockDescrPointer,
+ &blockDescrIndex,
+ maxBlockDescrCount,
+ &lbaCount,
+ maxLbaCount,
+ &tempDataSetRange,
+ &totalSectorCount);
+
+ tempDataSetRangeFullyConverted = (tempDataSetRange.LengthInBytes == 0) ? TRUE : FALSE;
+
+ allDataSetRangeFullyConverted = tempDataSetRangeFullyConverted && ((dataSetRangeIndex + 1) == dataSetRangesCount);
+
+ if (tempDataSetRangeFullyConverted) {
+ dataSetRangeIndex += 1;
+ dataSetRangeByteOffset = 0;
+ NT_ASSERT(dataSetRangeIndex <= dataSetRangesCount);
+ } else {
+ dataSetRangeByteOffset += totalSectorCount * fdoExt->DiskGeometry.BytesPerSector;
+ NT_ASSERT(dataSetRangeByteOffset < dataSetRanges[dataSetRangeIndex].LengthInBytes);
+ }
+
+ totalSectorsToProcess += totalSectorCount;
+ }
+
+ //
+ // Save the number of sectors being attempted in this WRITE USING TOKEN
+ // command, so that a success return from the command will know how much
+ // was written, without needing to issue a RECEIVE ROD TOKEN INFORMATION
+ // command.
+ //
+ OffloadWriteContext->TotalSectorsToProcess = totalSectorsToProcess;
+ OffloadWriteContext->TotalSectorsProcessed = 0;
+
+ //
+ // Calculate transfer size, including the header
+ //
+ transferSize = (blockDescrIndex * sizeof(BLOCK_DEVICE_RANGE_DESCRIPTOR)) + FIELD_OFFSET(WRITE_USING_TOKEN_HEADER, BlockDeviceRangeDescriptor);
+
+ NT_ASSERT(transferSize <= MAX_TOKEN_OPERATION_PARAMETER_DATA_LENGTH);
+
+ writeUsingTokenDataLength = (USHORT)transferSize - RTL_SIZEOF_THROUGH_FIELD(WRITE_USING_TOKEN_HEADER, WriteUsingTokenDataLength);
+ REVERSE_BYTES_SHORT(((PWRITE_USING_TOKEN_HEADER)buffer)->WriteUsingTokenDataLength, &writeUsingTokenDataLength);
+
+ ((PWRITE_USING_TOKEN_HEADER)buffer)->Immediate = 0;
+
+ logicalBlockOffset = OffloadWriteContext->LogicalBlockOffset + totalSectorsProcessedSuccessfully;
+ REVERSE_BYTES_QUAD(((PWRITE_USING_TOKEN_HEADER)buffer)->BlockOffsetIntoToken, &logicalBlockOffset);
+
+ RtlCopyMemory(((PWRITE_USING_TOKEN_HEADER)buffer)->Token,
+ &offloadWriteParameters->Token,
+ BLOCK_DEVICE_TOKEN_SIZE);
+
+ writeUsingTokenDescriptorsLength = (USHORT)transferSize - FIELD_OFFSET(WRITE_USING_TOKEN_HEADER, BlockDeviceRangeDescriptor);
+ REVERSE_BYTES_SHORT(((PWRITE_USING_TOKEN_HEADER)buffer)->BlockDeviceRangeDescriptorListLength, &writeUsingTokenDescriptorsLength);
+
+ RtlZeroMemory(pseudoIrp, sizeof(IRP));
+
+ pseudoIrp->IoStatus.Status = STATUS_SUCCESS;
+ pseudoIrp->IoStatus.Information = 0;
+ pseudoIrp->Tail.Overlay.DriverContext[0] = LongToPtr(1);
+ pseudoIrp->MdlAddress = writeUsingTokenMdl;
+
+ InterlockedCompareExchange((volatile LONG *)&TokenOperationListIdentifier, -1, MaxTokenOperationListIdentifier);
+ listIdentifier = InterlockedIncrement((volatile LONG *)&TokenOperationListIdentifier);
+
+ ClasspSetupWriteUsingTokenTransferPacket(
+ OffloadWriteContext,
+ pkt,
+ transferSize,
+ (PUCHAR)buffer,
+ pseudoIrp,
+ listIdentifier);
+
+ tokenAscii = ClasspBinaryToAscii((PUCHAR)(((PWRITE_USING_TOKEN_HEADER)buffer)->Token),
+ tokenSize,
+ &tokenSize);
+
+ TracePrint((TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_IOCTL,
+ "ClasspContinueOffloadWrite (%p): Offloading write for %I64u bytes (versus %I64u) [via %u descriptors]. \
+ \n\t\t\tDataLength: %u, DescriptorsLength: %u. Pkt %p (list id %x) [Token: %s]\n",
+ fdo,
+ totalSectorsToProcess * fdoExt->DiskGeometry.BytesPerSector,
+ entireXferLen,
+ blockDescrIndex,
+ writeUsingTokenDataLength,
+ writeUsingTokenDescriptorsLength,
+ pkt,
+ listIdentifier,
+ (tokenAscii == NULL) ? "" : tokenAscii));
+
+ FREE_POOL(tokenAscii);
+
+ OffloadWriteContext->ListIdentifier = listIdentifier;
+
+ SubmitTransferPacket(pkt);
+
+ //
+ // ClasspWriteUsingTokenTransferPacketDone() takes care of completing the
+ // IRP, so this function is done.
+ //
+
+ return;
+
+ //
+ // Error cleaup label only - not used in success case:
+ //
+
+__ClasspContinueOffloadWrite_ErrorExit:
+
+ NT_ASSERT(!NT_SUCCESS(status));
+
+ //
+ // ClasspCompleteOffloadWrite also cleans up offloadWriteContext.
+ //
+
+ ClasspCompleteOffloadWrite(OffloadWriteContext, status);
+
+ return;
+}
+
+
+VOID
+ClasspAdvanceOffloadWritePosition(
+ _In_ POFFLOAD_WRITE_CONTEXT OffloadWriteContext,
+ _In_ ULONGLONG SectorsToAdvance
+ )
+
+/*++
+
+Routine description:
+
+ After the target has responded to WRITE USING TOKEN with success, or RRTI
+ with a specific TRANSFER COUNT, this routine is used to update the relative
+ position within the overall offload write request. This position includes
+ the TotalSectorsProcessedSuccessfully, the DataSetRangeIndex, and the
+ DataSetRangeByteOffset.
+
+ The caller is responsible for continuing or completing the offload write
+ operation (this routine doesn't do that).
+
+Arguments:
+
+ OffloadWriteContext - Pointer to the OFFLOAD_WRITE_CONTEXT for the offload
+ write operation.
+
+ SectorsToAdvance - The number of sectors which were just written
+ successfully (not the total for the offload write operation overall,
+ just the number done by the most recent WRITE USING TOKEN).
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ ULONGLONG bytesToAdvance;
+ ULONGLONG bytesToDo;
+ PULONGLONG dataSetRangeByteOffset;
+ PULONG dataSetRangeIndex;
+ PDEVICE_DATA_SET_RANGE dataSetRanges;
+ PDEVICE_OBJECT fdo;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt;
+ PULONGLONG totalSectorsProcessedSuccessfully;
+
+ fdo = OffloadWriteContext->Fdo;
+ fdoExt = fdo->DeviceExtension;
+ dataSetRanges = OffloadWriteContext->DataSetRanges;
+ dataSetRangeByteOffset = &OffloadWriteContext->DataSetRangeByteOffset;
+ dataSetRangeIndex = &OffloadWriteContext->DataSetRangeIndex;
+ totalSectorsProcessedSuccessfully = &OffloadWriteContext->TotalSectorsProcessedSuccessfully;
+ bytesToAdvance = SectorsToAdvance * fdoExt->DiskGeometry.BytesPerSector;
+
+ (*totalSectorsProcessedSuccessfully) += SectorsToAdvance;
+ NT_ASSERT((*totalSectorsProcessedSuccessfully) <= OffloadWriteContext->TotalRequestSizeSectors);
+
+ while (bytesToAdvance != 0) {
+ bytesToDo = dataSetRanges[*dataSetRangeIndex].LengthInBytes - *dataSetRangeByteOffset;
+ if (bytesToDo > bytesToAdvance) {
+ bytesToDo = bytesToAdvance;
+ }
+ (*dataSetRangeByteOffset) += bytesToDo;
+ bytesToAdvance -= bytesToDo;
+ if ((*dataSetRangeByteOffset) == dataSetRanges[*dataSetRangeIndex].LengthInBytes) {
+ (*dataSetRangeIndex) += 1;
+ (*dataSetRangeByteOffset) = 0;
+ }
+ }
+
+ NT_ASSERT((*dataSetRangeIndex) <= OffloadWriteContext->DataSetRangesCount);
+
+ return;
+}
+
+
+VOID
+ClasspWriteUsingTokenTransferPacketDone(
+ _In_ PVOID Context
+ )
+
+/*++
+
+Routine description:
+
+ This routine continues an offload write operation when the WRITE USING
+ TOKEN transfer packet completes.
+
+ This routine may be able to determine that all requested sectors were
+ written if the WRITE USING TOKEN completed with success, or may need to
+ issue a RECEIVE ROD TOKEN INFORMATION if the WRITE USING TOKEN indicated
+ check condition.
+
+ This routine is responsible for continuing or completing the offload write
+ operation.
+
+Arguments:
+
+ Context - Pointer to the OFFLOAD_WRITE_CONTEXT for the offload write
+ operation.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ ULONGLONG entireXferLen;
+ PDEVICE_OBJECT fdo;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt;
+ ULONG listIdentifier;
+ POFFLOAD_WRITE_CONTEXT offloadWriteContext;
+ PTRANSFER_PACKET pkt;
+ PIRP pseudoIrp;
+ NTSTATUS status;
+ PBOOLEAN tokenInvalidated;
+ ULONGLONG totalSectorsToProcess;
+
+ offloadWriteContext = Context;
+ pseudoIrp = &offloadWriteContext->PseudoIrp;
+ fdo = offloadWriteContext->Fdo;
+ fdoExt = fdo->DeviceExtension;
+ listIdentifier = offloadWriteContext->ListIdentifier;
+ totalSectorsToProcess = offloadWriteContext->TotalSectorsToProcess;
+ entireXferLen = offloadWriteContext->EntireXferLen;
+ tokenInvalidated = &offloadWriteContext->TokenInvalidated;
+ pkt = offloadWriteContext->Pkt;
+
+ offloadWriteContext->Pkt = NULL;
+
+
+ status = pseudoIrp->IoStatus.Status;
+ NT_ASSERT(status != STATUS_PENDING);
+
+ //
+ // If the request failed with any of the following errors, then it is meaningless to send
+ // down a ReceiveTokenInformation (regardless of whether the transfer was requested as sync
+ // or async), since the target has no saved information about the command:
+ // - STATUS_INVALID_TOKEN
+ // - STATUS_INVALID_PARAMETER
+ //
+ if (status == STATUS_INVALID_PARAMETER ||
+ status == STATUS_INVALID_TOKEN) {
+
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClasspWriteUsingTokenTransferPacketDone (%p): Write failed with %x (list id %x).\n",
+ fdo,
+ status,
+ listIdentifier));
+
+ //
+ // If the token isn't valid any longer, we should let the upper layers know so that
+ // they don't waste time retrying the write with the same token.
+ //
+ if (status == STATUS_INVALID_TOKEN) {
+
+ *tokenInvalidated = TRUE;
+ }
+
+ NT_ASSERT(status != STATUS_PENDING && !NT_SUCCESS(status));
+ goto __ClasspWriteUsingTokenTransferPacketDone_Exit;
+
+ } else if ((NT_SUCCESS(status)) &&
+ (pkt->Srb->SrbStatus == SRB_STATUS_SUCCESS || pkt->TransferCount != 0)) {
+
+ //
+ // If the TokenOperation command was sent to the target requesting synchronous data
+ // transfer, a success indicates that the command is complete.
+ // This could either be because of a successful completion of the entire transfer
+ // or because of a partial transfer due to target truncation. If it is the latter,
+ // and the information field of the sense data has returned the TransferCount, we
+ // can avoid sending down an RRTI.
+ //
+ if (pkt->Srb->SrbStatus == SRB_STATUS_SUCCESS) {
+
+ //
+ // The entire transfer has completed successfully.
+ //
+ offloadWriteContext->TotalSectorsProcessed = totalSectorsToProcess;
+ TracePrint((TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_IOCTL,
+ "ClasspWriteUsingTokenTransferPacketDone (%p): Successfully wrote using token %I64u (out of %I64u) bytes (list Id %x).\n",
+ fdo,
+ totalSectorsToProcess * fdoExt->DiskGeometry.BytesPerSector,
+ entireXferLen,
+ listIdentifier));
+ } else {
+
+ //
+ // The target has returned how much data it transferred in the response to the
+ // WUT command itself, allowing us to optimize by removing the necessaity for
+ // sending down an RRTI to query the TransferCount.
+ //
+ NT_ASSERT(pkt->TransferCount);
+
+ offloadWriteContext->TotalSectorsProcessed = totalSectorsToProcess = pkt->TransferCount;
+ TracePrint((TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_IOCTL,
+ "ClasspWriteUsingTokenTransferPacketDone (%p): Target truncated write using token %I64u (out of %I64u) bytes (list Id %x).\n",
+ fdo,
+ totalSectorsToProcess * fdoExt->DiskGeometry.BytesPerSector,
+ entireXferLen,
+ listIdentifier));
+ }
+
+ ClasspAdvanceOffloadWritePosition(offloadWriteContext, totalSectorsToProcess);
+
+ NT_ASSERT(status != STATUS_PENDING);
+
+ //
+ // ClasspReceiveWriteUsingTokenInformationDone() takes care of
+ // completing the operation (eventually), so pending from point of view
+ // of this function.
+ //
+
+ ClasspReceiveWriteUsingTokenInformationDone(offloadWriteContext, status);
+ status = STATUS_PENDING;
+
+ goto __ClasspWriteUsingTokenTransferPacketDone_Exit;
+
+ } else {
+
+ //
+ // Since the TokenOperation was failed (or the target truncated the transfer but
+ // didn't indicate the amount), we need to send down ReceiveTokenInformation.
+ //
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClasspWriteUsingTokenTransferPacketDone (%p): Write failed with status %x, %x (list id %x).\n",
+ fdo,
+ status,
+ pkt->Srb->SrbStatus,
+ listIdentifier));
+
+ ClasspReceiveWriteUsingTokenInformation(offloadWriteContext);
+
+ status = STATUS_PENDING;
+ goto __ClasspWriteUsingTokenTransferPacketDone_Exit;
+ }
+
+__ClasspWriteUsingTokenTransferPacketDone_Exit:
+
+ if (status != STATUS_PENDING) {
+ ClasspCompleteOffloadWrite(offloadWriteContext, status);
+ }
+
+ return;
+}
+
+
+VOID
+ClasspReceiveWriteUsingTokenInformationDone(
+ _In_ POFFLOAD_WRITE_CONTEXT OffloadWriteContext,
+ _In_ NTSTATUS CompletionCausingStatus
+ )
+
+/*++
+
+Routine description:
+
+ This routine continues an offload write operation when a WRITE USING TOKEN
+ and possible associated RECEIVE ROD TOKEN INFORMATION have both fully
+ completed and the RRTI has indicated completion of the WUT.
+
+ This routine checks to see if the total sectors written is already equal to
+ the overall total requested sector count, and if so, completes the offload
+ write operation. If not, this routine continues the operation by issuing
+ another WUT.
+
+ This routine is responsible for continuing or completing the offload write
+ operation.
+
+Arguments:
+
+ OffloadWriteContext - Pointer to the OFFLOAD_WRITE_CONTEXT for the offload
+ write operation.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NT_ASSERT(
+ OffloadWriteContext->TotalSectorsProcessedSuccessfully <=
+ OffloadWriteContext->TotalRequestSizeSectors);
+
+ // Time taken in 100 ns units
+ ULONGLONG durationIn100ns = (KeQueryInterruptTime() - OffloadWriteContext->OperationStartTime);
+
+
+ if (OffloadWriteContext->TotalSectorsProcessedSuccessfully == OffloadWriteContext->TotalRequestSizeSectors) {
+
+ ClasspCompleteOffloadWrite(OffloadWriteContext, CompletionCausingStatus);
+
+ goto __ClasspReceiveWriteUsingTokenInformationDone_Exit;
+ }
+
+ //
+ // Since we don't want a layered timeout mechanism (e.g. guest and parent OS in Hyper-V scenarios)
+ // to cause a SCSI timeout for the higher layer token operations.
+ //
+ if (MAX_TARGET_DURATION <= durationIn100ns) {
+
+ TracePrint((TRACE_LEVEL_WARNING,
+ TRACE_FLAG_IOCTL,
+ "ClasspReceiveWriteUsingTokenInformationDone (%p): Truncating write (list id %x) because of max-duration-rule.\n",
+ OffloadWriteContext->Fdo,
+ OffloadWriteContext->ListIdentifier));
+
+ //
+ // We could technically pass in STATUS_IO_OPERATION_TIMEOUT, but ClasspCompleteOffloadWrite
+ // won't end up doing anything (useful) with this status, since some bytes would already
+ // have been transferred.
+ //
+ ClasspCompleteOffloadWrite(OffloadWriteContext, STATUS_UNSUCCESSFUL);
+
+ goto __ClasspReceiveWriteUsingTokenInformationDone_Exit;
+ }
+
+ NT_ASSERT(
+ OffloadWriteContext->TotalSectorsProcessedSuccessfully <
+ OffloadWriteContext->TotalRequestSizeSectors);
+
+ //
+ // Keep going with the next sub-request.
+ //
+
+ ClasspContinueOffloadWrite(OffloadWriteContext);
+
+__ClasspReceiveWriteUsingTokenInformationDone_Exit:
+
+ return;
+}
+
+
+VOID
+ClasspCompleteOffloadWrite(
+ _In_ __drv_freesMem(Mem) POFFLOAD_WRITE_CONTEXT OffloadWriteContext,
+ _In_ NTSTATUS CompletionCausingStatus
+ )
+
+/*++
+
+Routine description:
+
+ This routine is used to complete an offload write operation.
+
+ The input CompletionCausingStatus doesn't necessarily drive the completion
+ status of the offload write operation overall, if the offload write
+ operation overall has previously written some sectors successfully.
+
+ This routine completes the offload write operation.
+
+Arguments:
+
+ OffloadWriteContext - Pointer to the OFFLOAD_WRITE_CONTEXT for the offload
+ write operation.
+
+ CompletionCausingStatus - Status code indicating a reason that the offload
+ write operation is completing. For success this will be STATUS_SUCCESS,
+ but for a failure, this status will indicate what failure occurred.
+ This status doesn't directly propagate to the completion status of the
+ overall offload write operation if this status is failure and the
+ overall offload write operation has already previously written some
+ sectors successfully.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PDEVICE_OBJECT fdo;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt;
+ PDEVICE_MANAGE_DATA_SET_ATTRIBUTES dsmAttributes;
+ PULONGLONG totalSectorsProcessedSuccessfully;
+ ULONGLONG entireXferLen;
+ PIRP irp;
+ PBOOLEAN tokenInvalidated;
+ ULONG listIdentifier;
+ ULONGLONG totalSectorsProcessed;
+ NTSTATUS status;
+ ULONGLONG totalBytesProcessed;
+
+ fdo = OffloadWriteContext->Fdo;
+ fdoExt = fdo->DeviceExtension;
+ dsmAttributes = OffloadWriteContext->DsmAttributes;
+ totalSectorsProcessedSuccessfully = &OffloadWriteContext->TotalSectorsProcessedSuccessfully;
+ entireXferLen = OffloadWriteContext->EntireXferLen;
+ irp = OffloadWriteContext->OffloadWriteDsmIrp;
+ tokenInvalidated = &OffloadWriteContext->TokenInvalidated;
+ listIdentifier = OffloadWriteContext->ListIdentifier;
+ totalSectorsProcessed = OffloadWriteContext->TotalSectorsProcessed;
+ status = CompletionCausingStatus;
+
+ ((PSTORAGE_OFFLOAD_WRITE_OUTPUT)dsmAttributes)->OffloadWriteFlags = 0;
+ ((PSTORAGE_OFFLOAD_WRITE_OUTPUT)dsmAttributes)->Reserved = 0;
+
+ totalBytesProcessed = (*totalSectorsProcessedSuccessfully) * fdoExt->DiskGeometry.BytesPerSector;
+
+ TracePrint((totalBytesProcessed == entireXferLen ? TRACE_LEVEL_INFORMATION : TRACE_LEVEL_WARNING,
+ TRACE_FLAG_IOCTL,
+ "ClasspCompleteOffloadWrite (%p): %ws wrote using token %I64u (out of %I64u) bytes (Irp %p).\n",
+ fdo,
+ NT_SUCCESS(status) ? L"Successful" : L"Failed",
+ totalBytesProcessed,
+ entireXferLen,
+ irp));
+
+ if (totalBytesProcessed > 0 && totalBytesProcessed < entireXferLen) {
+ SET_FLAG(((PSTORAGE_OFFLOAD_WRITE_OUTPUT)dsmAttributes)->OffloadWriteFlags, STORAGE_OFFLOAD_WRITE_RANGE_TRUNCATED);
+ }
+ if (*tokenInvalidated) {
+ SET_FLAG(((PSTORAGE_OFFLOAD_WRITE_OUTPUT)dsmAttributes)->OffloadWriteFlags, STORAGE_OFFLOAD_TOKEN_INVALID);
+ }
+ ((PSTORAGE_OFFLOAD_WRITE_OUTPUT)dsmAttributes)->LengthCopied = totalBytesProcessed;
+
+
+ if (!NT_SUCCESS(status)) {
+
+ TracePrint((TRACE_LEVEL_WARNING,
+ TRACE_FLAG_IOCTL,
+ "ClasspCompleteOffloadWrite (%p): TokenOperation for WriteUsingToken (list Id %u) completed with %x writing %I64u blocks (currentTotal %I64u blocks).\n",
+ fdo,
+ listIdentifier,
+ status,
+ totalSectorsProcessed,
+ *totalSectorsProcessedSuccessfully));
+
+ //
+ // Even if target returned an error, from the OS upper layers' perspective,
+ // it is a success (with truncation) if any data at all was written.
+ //
+ if (*totalSectorsProcessedSuccessfully) {
+ status = STATUS_SUCCESS;
+ }
+ }
+
+ irp->IoStatus.Information = sizeof(STORAGE_OFFLOAD_WRITE_OUTPUT);
+
+ ClasspCompleteOffloadRequest(fdo, irp, status);
+ ClasspCleanupOffloadWriteContext(OffloadWriteContext);
+ OffloadWriteContext = NULL;
+
+ return;
+}
+
+
+VOID
+ClasspCleanupOffloadWriteContext(
+ _In_ __drv_freesMem(mem) POFFLOAD_WRITE_CONTEXT OffloadWriteContext
+ )
+
+/*++
+
+Routine description:
+
+ This routine cleans up an offload write context.
+
+Arguments:
+
+ OffloadWriteContext - Pointer to the OFFLOAD_WRITE_CONTEXT for the offload
+ write operation.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PMDL writeUsingTokenMdl = OffloadWriteContext->WriteUsingTokenMdl;
+
+ if (writeUsingTokenMdl) {
+ ClasspFreeDeviceMdl(writeUsingTokenMdl);
+ }
+ FREE_POOL(OffloadWriteContext);
+
+ return;
+}
+
+
+_IRQL_requires_same_
+VOID
+ClasspReceiveWriteUsingTokenInformation(
+ _In_ POFFLOAD_WRITE_CONTEXT OffloadWriteContext
+ )
+
+/*++
+
+Routine description:
+
+ This routine retrieves the token after a WriteUsingToken command
+ has been sent down in case of an error or if there is a need to
+ poll for the result.
+
+ This routine is responsible for continuing or completing the offload write
+ operation.
+
+Arguments:
+
+ OffloadWriteContext - Pointer to the OFFLOAD_WRITE_CONTEXT for the offload
+ write operation.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PVOID buffer;
+ ULONG bufferLength;
+ ULONG cdbLength;
+ PDEVICE_OBJECT fdo;
+ PIRP irp;
+ ULONG listIdentifier;
+ PTRANSFER_PACKET pkt;
+ PIRP pseudoIrp;
+ ULONG receiveTokenInformationBufferLength;
+ PSCSI_REQUEST_BLOCK srb;
+ NTSTATUS status;
+ ULONG tempSizeUlong;
+ PMDL writeUsingTokenMdl;
+
+ fdo = OffloadWriteContext->Fdo;
+ irp = OffloadWriteContext->OffloadWriteDsmIrp;
+ pseudoIrp = &OffloadWriteContext->PseudoIrp;
+ buffer = OffloadWriteContext + 1;
+ bufferLength = OffloadWriteContext->BufferLength;
+ receiveTokenInformationBufferLength = OffloadWriteContext->ReceiveTokenInformationBufferLength;
+ writeUsingTokenMdl = OffloadWriteContext->WriteUsingTokenMdl;
+ listIdentifier = OffloadWriteContext->ListIdentifier;
+ srb = &OffloadWriteContext->Srb;
+ status = STATUS_SUCCESS;
+
+ TracePrint((TRACE_LEVEL_VERBOSE,
+ TRACE_FLAG_IOCTL,
+ "ClasspReceiveWriteUsingTokenInformation (%p): Entering function. Irp %p\n",
+ fdo,
+ irp));
+
+ //
+ // The WRITE USING TOKEN wasn't immediately fully successful, so that means
+ // the only way to find out how many sectors were processed by the WRITE
+ // USING TOKEN is to get a successful RECEIVE ROD TOKEN INFORMATION that
+ // indicates the operation is complete.
+ //
+
+ NT_ASSERT(OffloadWriteContext->TotalSectorsProcessed == 0);
+
+ pkt = DequeueFreeTransferPacket(fdo, TRUE);
+
+ if (!pkt) {
+
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClasspReceiveWriteUsingTokenInformation (%p): Failed to retrieve transfer packet for ReceiveTokenInformation (WriteUsingToken) operation.\n",
+ fdo));
+
+ status = STATUS_INSUFFICIENT_RESOURCES;
+
+ goto __ClasspReceiveWriteUsingTokenInformation_ErrorExit;
+ }
+
+ RtlZeroMemory(buffer, bufferLength);
+
+ tempSizeUlong = receiveTokenInformationBufferLength - 4;
+ REVERSE_BYTES(((PRECEIVE_TOKEN_INFORMATION_HEADER)buffer)->AvailableData, &tempSizeUlong);
+
+ RtlZeroMemory(pseudoIrp, sizeof(IRP));
+
+ pseudoIrp->IoStatus.Status = STATUS_SUCCESS;
+ pseudoIrp->IoStatus.Information = 0;
+ pseudoIrp->Tail.Overlay.DriverContext[0] = LongToPtr(1);
+ pseudoIrp->MdlAddress = writeUsingTokenMdl;
+
+ ClasspSetupReceiveWriteUsingTokenInformationTransferPacket(
+ OffloadWriteContext,
+ pkt,
+ bufferLength,
+ (PUCHAR)buffer,
+ pseudoIrp,
+ listIdentifier);
+
+ //
+ // Cache away the CDB as it may be required for forwarded sense data
+ // after this command completes.
+ //
+ RtlZeroMemory(srb, sizeof(*srb));
+ cdbLength = SrbGetCdbLength(pkt->Srb);
+ if (cdbLength <= 16) {
+ RtlCopyMemory(&srb->Cdb, SrbGetCdb(pkt->Srb), cdbLength);
+ }
+
+ SubmitTransferPacket(pkt);
+
+ return;
+
+ //
+ // Error label only - not used by success cases:
+ //
+
+__ClasspReceiveWriteUsingTokenInformation_ErrorExit:
+
+ NT_ASSERT(!NT_SUCCESS(status));
+
+ //
+ // ClasspCompleteOffloadWrite also cleans up OffloadWriteContext.
+ //
+
+ ClasspCompleteOffloadWrite(OffloadWriteContext, status);
+
+ return;
+}
+
+
+VOID
+ClasspReceiveWriteUsingTokenInformationTransferPacketDone(
+ _In_ POFFLOAD_WRITE_CONTEXT OffloadWriteContext
+ )
+
+/*++
+
+Routine description:
+
+ This routine continues an offload write operation when a RECEIVE ROD TOKEN
+ INFORMATION transfer packet is done.
+
+ This routine may need to send another RRTI, or it may be able to indicate
+ that this WUT is done via call to
+ ClasspReceiveWriteUsingTokenInformationDone().
+
+ This routine is responsible for continuing or completing the offload write
+ operation.
+
+Arguments:
+
+ OffloadWriteContext - Pointer to the OFFLOAD_WRITE_CONTEXT for the offload
+ write operation.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ ULONG availableData;
+ PVOID buffer;
+ UCHAR completionStatus;
+ ULONG estimatedRetryInterval;
+ PDEVICE_OBJECT fdo;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt;
+ PIRP irp;
+ ULONG listIdentifier;
+ BOOLEAN operationCompleted;
+ UCHAR operationStatus;
+ PIRP pseudoIrp;
+ USHORT segmentsProcessed;
+ PSENSE_DATA senseData;
+ ULONG senseDataFieldLength;
+ UCHAR senseDataLength;
+ PSCSI_REQUEST_BLOCK srb;
+ NTSTATUS status;
+ ULONG tokenDescriptorLength;
+ PRECEIVE_TOKEN_INFORMATION_HEADER tokenInformationResults;
+ PRECEIVE_TOKEN_INFORMATION_RESPONSE_HEADER tokenInformationResponsePadding;
+ PBOOLEAN tokenInvalidated;
+ PULONGLONG totalSectorsProcessed;
+ ULONGLONG totalSectorsToProcess;
+ ULONGLONG transferBlockCount;
+
+ fdo = OffloadWriteContext->Fdo;
+ fdoExt = fdo->DeviceExtension;
+ listIdentifier = OffloadWriteContext->ListIdentifier;
+ totalSectorsProcessed = &OffloadWriteContext->TotalSectorsProcessed;
+ totalSectorsToProcess = OffloadWriteContext->TotalSectorsToProcess;
+ irp = OffloadWriteContext->OffloadWriteDsmIrp;
+ pseudoIrp = &OffloadWriteContext->PseudoIrp;
+ tokenInvalidated = &OffloadWriteContext->TokenInvalidated;
+ srb = &OffloadWriteContext->Srb;
+ operationCompleted = FALSE;
+ buffer = OffloadWriteContext + 1;
+ tokenInformationResults = (PRECEIVE_TOKEN_INFORMATION_HEADER)buffer;
+ senseData = (PSENSE_DATA)((PUCHAR)tokenInformationResults + FIELD_OFFSET(RECEIVE_TOKEN_INFORMATION_HEADER, SenseData));
+ transferBlockCount = 0;
+ tokenInformationResponsePadding = NULL;
+ tokenDescriptorLength = 0;
+
+ NT_ASSERT((*totalSectorsProcessed) == 0);
+
+ OffloadWriteContext->Pkt = NULL;
+
+
+ status = pseudoIrp->IoStatus.Status;
+ NT_ASSERT(status != STATUS_PENDING);
+
+ //
+ // The buffer we hand allows for the max sizes for all the fields whereas the returned
+ // data may be lesser (e.g. sense data info will almost never be MAX_SENSE_BUFFER_SIZE, etc.
+ // so handle underrun "error"
+ //
+ if (status == STATUS_DATA_OVERRUN) {
+
+ status = STATUS_SUCCESS;
+ }
+
+ if (!NT_SUCCESS(status)) {
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClasspReceiveWriteUsingTokenInformationTransferPacketDone (%p): Failed with %x to retrieve write results for list Id %x for data size %I64u bytes.\n",
+ fdo,
+ status,
+ listIdentifier,
+ totalSectorsToProcess * fdoExt->DiskGeometry.BytesPerSector));
+
+ NT_ASSERT((*totalSectorsProcessed) == 0);
+
+ goto __ClasspReceiveWriteUsingTokenInformationTransferPacketDone_ErrorExit;
+ }
+
+ REVERSE_BYTES(&availableData, &tokenInformationResults->AvailableData);
+
+ NT_ASSERT(availableData <= FIELD_OFFSET(RECEIVE_TOKEN_INFORMATION_HEADER, SenseData) + MAX_SENSE_BUFFER_SIZE);
+
+ NT_ASSERT(tokenInformationResults->ResponseToServiceAction == SERVICE_ACTION_WRITE_USING_TOKEN);
+
+ operationStatus = tokenInformationResults->OperationStatus;
+ operationCompleted = ClasspIsTokenOperationComplete(operationStatus);
+ NT_ASSERT(operationCompleted);
+
+ REVERSE_BYTES(&estimatedRetryInterval, &tokenInformationResults->EstimatedStatusUpdateDelay);
+
+ completionStatus = tokenInformationResults->CompletionStatus;
+
+ senseDataFieldLength = tokenInformationResults->SenseDataFieldLength;
+ senseDataLength = tokenInformationResults->SenseDataLength;
+ NT_ASSERT(senseDataFieldLength >= senseDataLength);
+
+ tokenInformationResponsePadding = (PRECEIVE_TOKEN_INFORMATION_RESPONSE_HEADER)((PUCHAR)tokenInformationResults +
+ FIELD_OFFSET(RECEIVE_TOKEN_INFORMATION_HEADER, SenseData) +
+ tokenInformationResults->SenseDataFieldLength);
+
+ REVERSE_BYTES(&tokenDescriptorLength, &tokenInformationResponsePadding->TokenDescriptorsLength);
+ NT_ASSERT(tokenDescriptorLength == 0);
+
+ NT_ASSERT(tokenInformationResults->TransferCountUnits == TRANSFER_COUNT_UNITS_NUMBER_BLOCKS);
+ REVERSE_BYTES_QUAD(&transferBlockCount, &tokenInformationResults->TransferCount);
+
+ REVERSE_BYTES_SHORT(&segmentsProcessed, &tokenInformationResults->SegmentsProcessed);
+ NT_ASSERT(segmentsProcessed == 0);
+
+ if (operationCompleted) {
+
+ if (transferBlockCount > totalSectorsToProcess) {
+
+ //
+ // Buggy or hostile target. Don't let it claim more was procesed
+ // than was requested. Since this is likely a bug and it's unknown
+ // how much was actually transferred, assume no data was
+ // transferred.
+ //
+
+ NT_ASSERT(transferBlockCount <= totalSectorsToProcess);
+ transferBlockCount = 0;
+ }
+
+ if (operationStatus != OPERATION_COMPLETED_WITH_SUCCESS &&
+ operationStatus != OPERATION_COMPLETED_WITH_RESIDUAL_DATA) {
+
+ //
+ // Assert on buggy response from target, but in any case, make sure not
+ // to claim that any data was written.
+ //
+
+ NT_ASSERT(transferBlockCount == 0);
+ transferBlockCount = 0;
+ }
+
+ //
+ // Since the TokenOperation was sent down synchronously but failed, the operation is complete as soon as the
+ // ReceiveTokenInformation command returns.
+ //
+
+ NT_ASSERT((*totalSectorsProcessed) == 0);
+ *totalSectorsProcessed = transferBlockCount;
+ ClasspAdvanceOffloadWritePosition(OffloadWriteContext, transferBlockCount);
+
+ if (transferBlockCount < totalSectorsToProcess) {
+
+ NT_ASSERT(operationStatus == OPERATION_COMPLETED_WITH_RESIDUAL_DATA ||
+ operationStatus == OPERATION_COMPLETED_WITH_ERROR ||
+ operationStatus == OPERATION_TERMINATED);
+
+ } else {
+
+ NT_ASSERT(operationStatus == OPERATION_COMPLETED_WITH_SUCCESS);
+ }
+
+ TracePrint((transferBlockCount == totalSectorsToProcess ? TRACE_LEVEL_INFORMATION : TRACE_LEVEL_WARNING,
+ TRACE_FLAG_IOCTL,
+ "ClasspReceiveWriteUsingTokenInformationTransferPacketDone (%p): %wsSuccessfully wrote (for list Id %x) for data size %I64u bytes\n",
+ fdo,
+ transferBlockCount == totalSectorsToProcess ? L"" : L"Target truncated write. ",
+ listIdentifier,
+ (*totalSectorsProcessed) * fdoExt->DiskGeometry.BytesPerSector));
+
+ //
+ // Operation that completes with success can have sense data (for target to pass on some extra info)
+ // but we don't care about such sense info.
+ // Operation that complete but not with success, may not have sense data associated, but may
+ // have valid CompletionStatus.
+ //
+ // The "status" may be overriden by ClassInterpretSenseInfo(). Final
+ // status is determined a bit later - this is just the default status
+ // when ClassInterpretSenseInfo() doesn't get to run here.
+ //
+ status = STATUS_SUCCESS;
+ if (operationStatus == OPERATION_COMPLETED_WITH_ERROR ||
+ operationStatus == OPERATION_COMPLETED_WITH_RESIDUAL_DATA ||
+ operationStatus == OPERATION_TERMINATED) {
+
+ SrbSetScsiStatus((PSTORAGE_REQUEST_BLOCK_HEADER)srb, completionStatus);
+
+ if (senseDataLength) {
+
+ ULONG retryInterval;
+
+ NT_ASSERT(senseDataLength <= sizeof(SENSE_DATA));
+
+ srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
+
+ srb->SrbStatus = SRB_STATUS_AUTOSENSE_VALID | SRB_STATUS_ERROR;
+ SrbSetSenseInfoBuffer((PSTORAGE_REQUEST_BLOCK_HEADER)srb, senseData);
+ SrbSetSenseInfoBufferLength((PSTORAGE_REQUEST_BLOCK_HEADER)srb, senseDataLength);
+
+ ClassInterpretSenseInfo(fdo,
+ srb,
+ IRP_MJ_SCSI,
+ 0,
+ 0,
+ &status,
+ &retryInterval);
+
+ TracePrint((TRACE_LEVEL_WARNING,
+ TRACE_FLAG_IOCTL,
+ "ClasspReceiveWriteUsingTokenInformationTransferPacketDone (%p): Reason for truncation/failure: %x - for list Id %x for data size %I64u bytes.\n",
+ fdo,
+ status,
+ listIdentifier,
+ transferBlockCount * fdoExt->DiskGeometry.BytesPerSector));
+
+ //
+ // If the token isn't valid any longer, we should let the upper layers know so that
+ // they don't waste time retrying the write with the same token.
+ //
+ if (status == STATUS_INVALID_TOKEN) {
+
+ *tokenInvalidated = TRUE;
+ }
+ } else {
+
+ TracePrint((TRACE_LEVEL_WARNING,
+ TRACE_FLAG_IOCTL,
+ "ClasspReceiveWriteUsingTokenInformationTransferPacketDone (%p): No sense data available but reason for truncation/failure, possibly: %x - for list Id %x for data size %I64u bytes.\n",
+ fdo,
+ completionStatus,
+ listIdentifier,
+ transferBlockCount * fdoExt->DiskGeometry.BytesPerSector));
+ }
+ }
+
+ //
+ // Initialize status. Upper layer needs to know if command failed because it
+ // timed out without doing any writing. ClasspCompleteOffloadWrite() will
+ // force status to success if any data was written, so it's this function's
+ // job to set the status appropriately based on the outcome of this
+ // WRITE USING TOKEN command, and then ClasspCompleteOffloadWrite()
+ // can override with success if previos WRITE USING TOKEN commands
+ // issued for the same upper request were able to write some data.
+ //
+ if (transferBlockCount != 0) {
+ status = STATUS_SUCCESS;
+ } else {
+
+ if (NT_SUCCESS(status)) {
+ //
+ // Make sure status is a failing status, without throwing away an
+ // already-failing status obtained from sense data.
+ //
+ status = STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ NT_ASSERT(status != STATUS_PENDING);
+
+ if (!NT_SUCCESS(status)) {
+ goto __ClasspReceiveWriteUsingTokenInformationTransferPacketDone_ErrorExit;
+ }
+
+ ClasspReceiveWriteUsingTokenInformationDone(OffloadWriteContext, status);
+ status = STATUS_PENDING;
+ goto __ClasspReceiveWriteUsingTokenInformationTransferPacketDone_Exit;
+
+ } else {
+
+ status = STATUS_UNSUCCESSFUL;
+
+ goto __ClasspReceiveWriteUsingTokenInformationTransferPacketDone_ErrorExit;
+ }
+
+ //
+ // Error label only - not used in success case:
+ //
+
+__ClasspReceiveWriteUsingTokenInformationTransferPacketDone_ErrorExit:
+
+ NT_ASSERT(!NT_SUCCESS(status));
+
+ ClasspCompleteOffloadWrite(OffloadWriteContext, status);
+
+__ClasspReceiveWriteUsingTokenInformationTransferPacketDone_Exit:
+
+ //
+ // Due to tracing a potentially freed pointer value "Irp", this trace could
+ // be delayed beyond another offload op picking up the same pointer value
+ // for its Irp.
+ //
+
+ TracePrint((TRACE_LEVEL_VERBOSE,
+ TRACE_FLAG_IOCTL,
+ "ClasspReceiveWriteUsingTokenInformationTransferPacketDone (%p): Exiting function (Irp %p) with status %x.\n",
+ fdo,
+ irp,
+ status));
+
+ return;
+}
+
+
+NTSTATUS
+ClasspRefreshFunctionSupportInfo(
+ _Inout_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ _In_ BOOLEAN ForceQuery
+ )
+/*
+Routine Description:
+
+ This function is to update various properties described in FDO extension's
+ CLASS_FUNCTION_SUPPORT_INFO structure by requerying the device's VPD pages.
+ Although this function is capable of updating any properties in the
+ CLASS_FUNCTION_SUPPORT_INFO structure, it will initially support only
+ a small number of proporties in the block limit data
+
+Arguments:
+
+ FdoExtension : FDO extension
+
+ ForceQuery : TRUE if the caller wants to force a query of the device's
+ VPD pages. Otherwise, the function may use cached data.
+
+Return Value:
+
+ STATUS_SUCCESS or an error status
+
+--*/
+{
+ NTSTATUS status;
+ PSCSI_REQUEST_BLOCK srb = NULL;
+ ULONG srbSize;
+ CLASS_VPD_B0_DATA blockLimitsDataNew;
+ PCLASS_VPD_B0_DATA blockLimitsDataOriginal;
+ KLOCK_QUEUE_HANDLE lockHandle;
+ ULONG generationCount;
+ ULONG changeRequestCount;
+
+ //
+ // ChangeRequestCount is incremented every time we get an unit attention with
+ // SCSI_ADSENSE_OPERATING_CONDITIONS_CHANGED. GenerationCount will be set to
+ // ChangeRequestCount after CLASS_FUNCTION_SUPPORT_INFO is refreshed with the latest
+ // VPD data. i.e. if both values are the same, data in
+ // CLASS_FUNCTION_SUPPORT_INFO is current
+ //
+
+ generationCount = FdoExtension->FunctionSupportInfo->GenerationCount;
+ changeRequestCount = FdoExtension->FunctionSupportInfo->ChangeRequestCount;
+ if (!ForceQuery && generationCount == changeRequestCount) {
+ return STATUS_SUCCESS;
+ }
+
+ //
+ // Allocate an SRB for querying the device for LBP-related info if either
+ // the Logical Block Provisioning (0xB2) or Block Limits (0xB0) VPD page
+ // exists.
+ //
+ if ((FdoExtension->AdapterDescriptor != NULL) &&
+ (FdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK)) {
+ srbSize = CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE;
+ } else {
+ srbSize = sizeof(SCSI_REQUEST_BLOCK);
+ }
+
+ srb = ExAllocatePoolWithTag(NonPagedPoolNx, srbSize, '1DcS');
+ if (srb == NULL) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ status = ClasspDeviceGetBlockLimitsVPDPage(FdoExtension,
+ srb,
+ srbSize,
+ &blockLimitsDataNew);
+
+ if (NT_SUCCESS(status)) {
+
+ KeAcquireInStackQueuedSpinLock(&FdoExtension->FunctionSupportInfo->SyncLock, &lockHandle);
+
+ //
+ // If the generationCount didn't change since we looked at it last time, it means
+ // no one has tried to update the CLASS_FUNCTION_SUPPORT_INFO data; otherwise, someone
+ // else has beat us to it.
+ //
+ if (generationCount == FdoExtension->FunctionSupportInfo->GenerationCount) {
+
+ blockLimitsDataOriginal = &FdoExtension->FunctionSupportInfo->BlockLimitsData;
+ if (blockLimitsDataOriginal->CommandStatus == -1) {
+ //
+ // CommandStatus == -1 means this is the first time we have
+ // gotten the block limits data.
+ //
+ *blockLimitsDataOriginal = blockLimitsDataNew;
+ } else {
+ //
+ // We only expect the Optimal Unmap Granularity (and alignment)
+ // to change, so those are the only parameters we update.
+ //
+ blockLimitsDataOriginal->UGAVALID = blockLimitsDataNew.UGAVALID;
+ blockLimitsDataOriginal->UnmapGranularityAlignment = blockLimitsDataNew.UnmapGranularityAlignment;
+ blockLimitsDataOriginal->OptimalUnmapGranularity = blockLimitsDataNew.OptimalUnmapGranularity;
+ }
+ FdoExtension->FunctionSupportInfo->GenerationCount = changeRequestCount;
+ }
+
+ KeReleaseInStackQueuedSpinLock(&lockHandle);
+ }
+
+ FREE_POOL(srb);
+ return status;
+}
+
+NTSTATUS
+ClasspBlockLimitsDataSnapshot(
+ _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ _In_ BOOLEAN ForceQuery,
+ _Out_ PCLASS_VPD_B0_DATA BlockLimitsData,
+ _Out_ PULONG GenerationCount
+ )
+/*
+Routine Description:
+
+ This function is to get a copy of the latest block limits data.
+
+ When this function is called multiple times, GenerationCount can change (value always goes up)
+ while BlockLimitsData stays the same. In this case, the caller should assume BlockLimitsData
+ has changed to different values and eventually changed back to the same state when the first
+ call to this function was made.
+
+Arguments:
+
+ FdoExtension : FDO extension
+
+ ForceQuery : TRUE if the caller wants to force a query of the device's
+ VPD pages. Otherwise, the function may use cached data.
+
+ BlockLimitsData : pointer to memory that will receive the block limits data
+
+ GenerationCount : generation count of the block limit data.
+
+ DataIsOutdated: set to TRUE if the BlockLimitsData is old but this function fails to
+ query the latest data from the device due to insufficient resources
+
+Return Value:
+
+ STATUS_SUCCESS or an error status
+
+--*/
+{
+ NTSTATUS status;
+ KLOCK_QUEUE_HANDLE lockHandle;
+
+ status = ClasspRefreshFunctionSupportInfo(FdoExtension, ForceQuery);
+
+ KeAcquireInStackQueuedSpinLock(&FdoExtension->FunctionSupportInfo->SyncLock, &lockHandle);
+ *BlockLimitsData = FdoExtension->FunctionSupportInfo->BlockLimitsData;
+ *GenerationCount = FdoExtension->FunctionSupportInfo->GenerationCount;
+ KeReleaseInStackQueuedSpinLock(&lockHandle);
+
+ return status;
+}
+
diff --git a/storage/class/classpnp/src/class.def b/storage/class/classpnp/src/class.def
new file mode 100644
index 00000000..0cba071f
--- /dev/null
+++ b/storage/class/classpnp/src/class.def
@@ -0,0 +1,119 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+NAME CLASSPNP.SYS
+
+EXPORTS
+ ClassInitialize
+ ClassInitializeEx
+ ClassGetDescriptor
+ ClassReadDriveCapacity
+ ClassReleaseQueue
+ ClassAsynchronousCompletion
+ ClassSplitRequest
+ ClassDeviceControl
+ ClassIoComplete
+ ClassIoCompleteAssociated
+ ClassInterpretSenseInfo
+ ClassSendDeviceIoControlSynchronous
+ ClassSendIrpSynchronous
+ ClassForwardIrpSynchronous
+ ClassSendSrbSynchronous
+ ClassSendSrbAsynchronous
+ ClassBuildRequest
+ ClassModeSense
+ ClassModeSenseEx
+ ClassModeSelect
+ ClassFindModePage
+ ClassClaimDevice
+ ClassInternalIoControl
+ ClassCreateDeviceObject
+ ClassRemoveDevice
+ ClassInitializeSrbLookasideList
+ ClassDeleteSrbLookasideList
+ ClassQueryTimeOutRegistryValue
+ ClassInvalidateBusRelations
+ ClassMarkChildrenMissing
+ ClassMarkChildMissing
+ ClassDebugPrint
+ ClassGetDriverExtension
+ ClassCompleteRequest
+ ClassReleaseRemoveLock
+ ClassAcquireRemoveLockEx
+ ClassUpdateInformationInRegistry
+ ClassWmiCompleteRequest
+ ClassWmiFireEvent
+ ClassGetVpb
+ ClassSetFailurePredictionPoll
+ ClassNotifyFailurePredicted
+ ClassInitializeTestUnitPolling
+ ClassSignalCompletion
+ ClassSendStartUnit
+
+
+
+
+ ClassSetMediaChangeState
+ ClassResetMediaChangeTimer
+ ClassCheckMediaState
+ ClassInitializeMediaChangeDetection
+ ClassCleanupMediaChangeDetection
+ ClassEnableMediaChangeDetection
+ ClassDisableMediaChangeDetection
+
+
+
+
+
+ ClassSpinDownPowerHandler
+ ClassStopUnitPowerHandler
+
+
+
+
+
+ ClassAcquireChildLock
+ ClassReleaseChildLock
+
+
+
+
+
+
+
+ ClassScanForSpecial
+
+
+
+
+
+ ClassSetDeviceParameter
+ ClassGetDeviceParameter
+
+
+
+
+
+
+ ClassGetFsContext
+
+
+
+
+ ClassSendNotification
+
+
+
+
+ DllUnload PRIVATE
+
diff --git a/storage/class/classpnp/src/class.rc b/storage/class/classpnp/src/class.rc
new file mode 100644
index 00000000..fb40e033
--- /dev/null
+++ b/storage/class/classpnp/src/class.rc
@@ -0,0 +1,22 @@
+//+-------------------------------------------------------------------------
+//
+// Microsoft Windows
+//
+// Copyright (C) Microsoft Corporation, 1996 - 1999
+//
+// File: class.rc
+//
+//--------------------------------------------------------------------------
+
+#include <verrsrc.h>
+
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_DRV
+#define VER_FILESUBTYPE VFT2_DRV_SYSTEM
+#define VER_FILEDESCRIPTION_STR "SCSI Class System Dll"
+#define VER_INTERNALNAME_STR "Classpnp.sys"
+#define VER_ORIGINALFILENAME_STR "Classpnp.sys"
+#define VER_LANGNEUTRAL
+
+#include "common.ver"
diff --git a/storage/class/classpnp/src/classlog.mof b/storage/class/classpnp/src/classlog.mof
new file mode 100644
index 00000000..b82d2e81
--- /dev/null
+++ b/storage/class/classpnp/src/classlog.mof
@@ -0,0 +1,175 @@
+[WMI, guid("F903D6EC-8647-438f-9E42-891F4733EDAF")]
+
+class MSStorageDriver_ScsiRequestBlock {
+ [read, WmiDataId(1), Description("Length")]
+ uint16 length;
+
+ [read, WmiDataId(2), DisplayInHex, Description("Function")]
+ uint8 function;
+
+ [read, WmiDataId(3), DisplayInHex, Description("SRB Status")]
+ uint8 srbStatus;
+
+ [read, WmiDataId(4), DisplayInHex, Description("SCSI Status")]
+ uint8 scsiStatus;
+
+ [read, WmiDataId(5), DisplayInHex, Description("Path ID")]
+ uint8 pathID;
+
+ [read, WmiDataId(6), DisplayInHex, Description("Target ID")]
+ uint8 targetID;
+
+ [read, WmiDataId(7), DisplayInHex, Description("LUN")]
+ uint8 lun;
+
+ [read, WmiDataId(8), DisplayInHex, Description("Queue Tag")]
+ uint8 queueTag;
+
+ [read, WmiDataId(9), DisplayInHex, Description("Queue Action")]
+ uint8 queueAction;
+
+ [read, WmiDataId(10), DisplayInHex, Description("CDB Length")]
+ uint8 cdbLength;
+
+ [read, WmiDataId(11), DisplayInHex, Description("Sense Info Buffer Length")]
+ uint8 senseInfoBufferLength;
+
+ [read, WmiDataId(12), DisplayInHex, Description("SRB Flags")]
+ uint32 srbFlags;
+
+ [read, WmiDataId(13), DisplayInHex, Description("Data Transfer Length")]
+ uint32 dataTransferLength;
+
+ [read, WmiDataId(14), DisplayInHex, Description("Time Out Value")]
+ uint32 timeOutValue;
+
+ [read, WmiDataId(15), DisplayInHex, Description("Data Buffer Pointer")]
+ uint64 dataBuffer;
+
+ [read, WmiDataId(16), DisplayInHex, Description("Sense Info Buffer Pointer")]
+ uint64 senseInfoBuffer;
+
+ [read, WmiDataId(17), DisplayInHex, Description("Next SRB Pointer")]
+ uint64 nextSRB;
+
+ [read, WmiDataId(18), DisplayInHex, Description("Original Request Pointer")]
+ uint64 originalRequest;
+
+ [read, WmiDataId(19), DisplayInHex, Description("SRB Extension Pointer")]
+ uint64 srbExtension;
+
+ [read, WmiDataId(20), DisplayInHex, Description("Internal Status")]
+ uint32 internalStatus;
+
+ [read, WmiDataId(21), DisplayInHex, Description("Reserved (only available in Win64)")]
+ uint32 reserved;
+
+ [read, WmiDataId(22), DisplayInHex, Description("CDB")]
+ uint8 cdb[16];
+};
+
+[WMI, guid("9065566F-5FD6-4b40-9961-98E3A3DD174E")]
+
+class MSStorageDriver_SenseData {
+ [read, WmiDataId(1), Description("Error Code")]
+ uint8 errorCode;
+
+ [read, WmiDataId(2), Description("Valid")]
+ boolean valid;
+
+ [read, WmiDataId(3), Description("Segment Number")]
+ uint8 segmentNumber;
+
+ [read, WmiDataId(4), Description("Sense Key")]
+ uint8 senseKey;
+
+ [read, WmiDataId(5), Description("Reserved")]
+ boolean reserved;
+
+ [read, WmiDataId(6), Description("Incorrect Length")]
+ boolean incorrectLength;
+
+ [read, WmiDataId(7), Description("End Of Media")]
+ boolean endOfMedia;
+
+ [read, WmiDataId(8), Description("File Mark")]
+ boolean fileMark;
+
+ [read, WmiDataId(9), Description("Information")]
+ uint8 information[4];
+
+ [read, WmiDataId(10), Description("Additional Sense Length")]
+ uint8 additionalSenseLength;
+
+ [read, WmiDataId(11), Description("Command Specific Information")]
+ uint8 commandSpecificInformation[4];
+
+ [read, WmiDataId(12), Description("Additional Sense Code")]
+ uint8 additionalSenseCode;
+
+ [read, WmiDataId(13), Description("Additional Sense Code Qualifier")]
+ uint8 additionalSenseCodeQualifier;
+
+ [read, WmiDataId(14), Description("Field Replaceable Unit Code")]
+ uint8 fieldReplaceableUnitCode;
+
+ [read, WmiDataId(15), Description("Sense Key Specific")]
+ uint8 senseKeySpecific[3];
+};
+
+[WMI, guid("0C9BF007-50E9-407e-A9DA-0F33800E4B45")]
+
+class MSStorageDriver_ClassErrorLogEntry {
+ [read, WmiDataId(1), Description("Tick Count")]
+ uint64 tickCount;
+
+ [read, WmiDataId(2), Description("Port Number")]
+ uint32 portNumber;
+
+ [read, WmiDataId(3), Description("Error Paging")]
+ boolean errorPaging;
+
+ [read, WmiDataId(4), Description("Error Retried")]
+ boolean errorRetried;
+
+ [read, WmiDataId(5), Description("Error Unhandled")]
+ boolean errorUnhandled;
+
+ [read, WmiDataId(6), DisplayInHex, Description("Error Reserved")]
+ uint8 errorReserved;
+
+ [read, WmiDataId(7), DisplayInHex, Description("Reserved")]
+ uint8 reserved[3];
+
+ [read, WmiDataId(8), Description("SCSI Request Block")]
+ MSStorageDriver_ScsiRequestBlock srb;
+
+ [read, WmiDataId(9), Description("Sense Data")]
+ MSStorageDriver_SenseData senseData;
+
+ [read, WmiDataId(10), Description("Event Time")]
+ datetime eventTime;
+};
+
+[Dynamic, Provider("WMIProv"),
+WMI, Description("MS Storage Class Driver Error Log"),
+guid("D5A9A51E-03F9-404d-9722-15F90EB07038"),
+locale("MS\\0x409")]
+
+class MSStorageDriver_ClassErrorLog {
+ [key, read]
+ string InstanceName;
+
+ [read]
+ boolean Active;
+
+ [read,
+ WmiDataId(1),
+ Description("Number of Error Log Entries")]
+ uint32 numEntries;
+
+ [read,
+ WmiDataId(2),
+ Description("Error Log Array")]
+ MSStorageDriver_ClassErrorLogEntry logEntries[16];
+}; \ No newline at end of file
diff --git a/storage/class/classpnp/src/classp.h b/storage/class/classpnp/src/classp.h
new file mode 100644
index 00000000..3c32c25d
--- /dev/null
+++ b/storage/class/classpnp/src/classp.h
@@ -0,0 +1,2619 @@
+/*++
+
+Copyright (C) Microsoft Corporation, 1991 - 2010
+
+Module Name:
+
+ classp.h
+
+Abstract:
+
+ Private header file for classpnp.sys modules. This contains private
+ structure and function declarations as well as constant values which do
+ not need to be exported.
+
+Author:
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+
+Revision History:
+
+--*/
+
+#define RTL_USE_AVL_TABLES 0
+
+#include <stddef.h>
+#include <stdarg.h>
+#include <stdlib.h>
+
+#include <ntddk.h>
+
+#include <scsi.h>
+
+#include <wmidata.h>
+#include <classpnp.h>
+#include <storduid.h>
+
+#if CLASS_INIT_GUID
+#include <initguid.h>
+#endif
+
+#include <mountdev.h>
+#include <ioevent.h>
+#include <ntstrsafe.h>
+#include <ntintsafe.h>
+
+#include <wdmguid.h>
+
+#if (NTDDI_VERSION >= NTDDI_WIN8)
+
+#include <ntpoapi.h>
+
+#include <srbhelper.h>
+
+#endif
+
+//
+// Set component ID for DbgPrintEx calls
+//
+#ifndef DEBUG_COMP_ID
+#define DEBUG_COMP_ID DPFLTR_CLASSPNP_ID
+#endif
+
+//
+// Include header file and setup GUID for tracing
+//
+#include <storswtr.h>
+#define WPP_GUID_CLASSPNP (FA8DE7C4, ACDE, 4443, 9994, C4E2359A9EDB)
+#ifndef WPP_CONTROL_GUIDS
+#define WPP_CONTROL_GUIDS WPP_CONTROL_GUIDS_NORMAL_FLAGS(WPP_GUID_CLASSPNP)
+#endif
+
+
+/*
+ * IA64 requires 8-byte alignment for pointers, but the IA64 NT kernel expects 16-byte alignment
+ */
+#ifdef _WIN64
+ #define PTRALIGN DECLSPEC_ALIGN(16)
+#else
+ #define PTRALIGN
+#endif
+
+
+extern CLASSPNP_SCAN_FOR_SPECIAL_INFO ClassBadItems[];
+
+extern GUID ClassGuidQueryRegInfoEx;
+extern GUID ClassGuidSenseInfo2;
+extern GUID ClassGuidWorkingSet;
+extern GUID ClassGuidSrbSupport;
+
+extern ULONG ClassMaxInterleavePerCriticalIo;
+
+
+#define Add2Ptr(P,I) ((PVOID)((PUCHAR)(P) + (I)))
+
+#define CLASSP_REG_SUBKEY_NAME (L"Classpnp")
+
+#define CLASSP_REG_HACK_VALUE_NAME (L"HackMask")
+#define CLASSP_REG_MMC_DETECTION_VALUE_NAME (L"MMCDetectionState")
+#define CLASSP_REG_WRITE_CACHE_VALUE_NAME (L"WriteCacheEnableOverride")
+#define CLASSP_REG_PERF_RESTORE_VALUE_NAME (L"RestorePerfAtCount")
+#define CLASSP_REG_REMOVAL_POLICY_VALUE_NAME (L"UserRemovalPolicy")
+#define CLASSP_REG_IDLE_INTERVAL_NAME (L"IdleInterval")
+#define CLASSP_REG_IDLE_ACTIVE_MAX (L"IdleOutstandingIoMax")
+#define CLASSP_REG_IDLE_PRIORITY_SUPPORTED (L"IdlePrioritySupported")
+#define CLASSP_REG_ACCESS_ALIGNMENT_NOT_SUPPORTED (L"AccessAlignmentQueryNotSupported")
+#define CLASSP_REG_DISBALE_IDLE_POWER_NAME (L"DisableIdlePowerManagement")
+#define CLASSP_REG_IDLE_TIMEOUT_IN_SECONDS (L"IdleTimeoutInSeconds")
+#define CLASSP_REG_DISABLE_D3COLD (L"DisableD3Cold")
+#define CLASSP_REG_QERR_OVERRIDE_MODE (L"QERROverrideMode")
+#define CLASSP_REG_LEGACY_ERROR_HANDLING (L"LegacyErrorHandling")
+#define CLASSP_REG_IO_TIMEOUT_RETRY_COUNT (L"MaxIoTimeoutRetryCount")
+
+#define CLASS_PERF_RESTORE_MINIMUM (0x10)
+#define CLASS_ERROR_LEVEL_1 (0x4)
+#define CLASS_ERROR_LEVEL_2 (0x8)
+#define CLASS_MAX_INTERLEAVE_PER_CRITICAL_IO (0x4)
+
+#define FDO_HACK_CANNOT_LOCK_MEDIA (0x00000001)
+#define FDO_HACK_GESN_IS_BAD (0x00000002)
+#define FDO_HACK_NO_SYNC_CACHE (0x00000004)
+#define FDO_HACK_NO_RESERVE6 (0x00000008)
+#define FDO_HACK_GESN_IGNORE_OPCHANGE (0x00000010)
+
+#define FDO_HACK_VALID_FLAGS (0x0000001F)
+#define FDO_HACK_INVALID_FLAGS (~FDO_HACK_VALID_FLAGS)
+
+/*
+ * Lots of retries of synchronized SCSI commands that devices may not
+ * even support really slows down the system (especially while booting).
+ * (Even GetDriveCapacity may be failed on purpose if an external disk is powered off).
+ * If a disk cannot return a small initialization buffer at startup
+ * in two attempts (with delay interval) then we cannot expect it to return
+ * data consistently with four retries.
+ * So don't set the retry counts as high here as for data SRBs.
+ *
+ * If we find that these requests are failing consecutively,
+ * despite the retry interval, on otherwise reliable media,
+ * then we should either increase the retry interval for
+ * that failure or (by all means) increase these retry counts as appropriate.
+ */
+#define NUM_LOCKMEDIAREMOVAL_RETRIES 1
+#define NUM_MODESENSE_RETRIES 1
+#define NUM_MODESELECT_RETRIES 1
+#define NUM_DRIVECAPACITY_RETRIES 1
+
+#if (NTDDI_VERSION >= NTDDI_WINBLUE)
+
+//
+// New code should use the MAXIMUM_RETRIES value.
+//
+#define NUM_IO_RETRIES MAXIMUM_RETRIES
+#define LEGACY_NUM_IO_RETRIES 8
+
+#else
+
+/*
+ * We retry failed I/O requests at 1-second intervals.
+ * In the case of a failure due to bus reset, we want to make sure that we retry after the allowable
+ * reset time. For SCSI, the allowable reset time is 5 seconds. ScsiPort queues requests during
+ * a bus reset, which should cause us to retry after the reset is over; but the requests queued in
+ * the miniport are failed all the way back to us immediately. In any event, in order to make
+ * extra sure that our retries span the allowable reset time, we should retry more than 5 times.
+ */
+#define NUM_IO_RETRIES 8
+
+#endif // NTDDI_VERSION >= NTDDI_WINBLUE
+
+#define CLASS_FILE_OBJECT_EXTENSION_KEY 'eteP'
+#define CLASSP_VOLUME_VERIFY_CHECKED 0x34
+
+#define CLASS_TAG_PRIVATE_DATA 'CPcS'
+#define CLASS_TAG_SENSE2 '2ScS'
+#define CLASS_TAG_WORKING_SET 'sWcS'
+#define CLASSPNP_POOL_TAG_GENERIC 'pCcS'
+#define CLASSPNP_POOL_TAG_TOKEN_OPERATION 'oTcS'
+#define CLASSPNP_POOL_TAG_SRB 'rScS'
+#define CLASSPNP_POOL_TAG_VPD 'pVcS'
+#define CLASSPNP_POOL_TAG_LOG_MESSAGE 'mlcS'
+#define CLASSPNP_POOL_TAG_ADDITIONAL_DATA 'DAcS'
+#define CLASSPNP_POOL_TAG_FIRMWARE 'wFcS'
+
+//
+// Macros related to Token Operation commands
+//
+#define MAX_LIST_IDENTIFIER MAXULONG
+#define NUM_POPULATE_TOKEN_RETRIES 1
+#define NUM_WRITE_USING_TOKEN_RETRIES 2
+#define NUM_RECEIVE_TOKEN_INFORMATION_RETRIES 2
+#define MAX_TOKEN_OPERATION_PARAMETER_DATA_LENGTH MAXUSHORT
+#define MAX_RECEIVE_TOKEN_INFORMATION_PARAMETER_DATA_LENGTH MAXULONG
+#define MAX_TOKEN_TRANSFER_SIZE MAXULONGLONG
+#define MAX_NUMBER_BLOCKS_PER_BLOCK_DEVICE_RANGE_DESCRIPTOR MAXULONG
+#define MAX_TARGET_DURATION (4ULL * 10 * 1000 * 1000) // 4sec in 100ns units
+#define DEFAULT_MAX_NUMBER_BYTES_PER_SYNC_WRITE_USING_TOKEN (64ULL * 1024 * 1024) // 64MB
+#define MAX_NUMBER_BYTES_PER_SYNC_WRITE_USING_TOKEN (256ULL * 1024 * 1024) // 256MB
+#define MIN_TOKEN_LIST_IDENTIFIERS 256
+#define MAX_TOKEN_LIST_IDENTIFIERS MAXULONG
+#define MAX_NUMBER_BLOCK_DEVICE_DESCRIPTORS 64
+
+#define REG_DISK_CLASS_CONTROL L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Control\\DISK"
+#define REG_MAX_LIST_IDENTIFIER_VALUE L"MaximumListIdentifier"
+
+#define VPD_PAGE_HEADER_SIZE 0x04
+
+
+//
+// Number of times to retry get LBA status in case of an error
+// that can be caused by VPD data change
+//
+
+#define GET_LBA_STATUS_RETRY_COUNT_MAX (2)
+
+extern ULONG MaxTokenOperationListIdentifier;
+extern volatile ULONG TokenOperationListIdentifier;
+
+extern LIST_ENTRY IdlePowerFDOList;
+extern PVOID PowerSettingNotificationHandle;
+extern PVOID ScreenStateNotificationHandle;
+extern BOOLEAN ClasspScreenOff;
+extern KGUARDED_MUTEX IdlePowerFDOListMutex;
+extern ULONG DiskIdleTimeoutInMS;
+
+//
+// Definitions from ntos\rtl\time.c
+//
+
+extern CONST LARGE_INTEGER Magic10000;
+#define SHIFT10000 13
+
+#define Convert100nsToMilliseconds(LARGE_INTEGER) \
+ ( \
+ RtlExtendedMagicDivide((LARGE_INTEGER), Magic10000, SHIFT10000) \
+ )
+
+#define ConvertMillisecondsTo100ns(MILLISECONDS) ( \
+ RtlExtendedIntegerMultiply ((MILLISECONDS), 10000) \
+ )
+
+typedef struct _MEDIA_CHANGE_DETECTION_INFO {
+
+ //
+ // Mutex to synchronize enable/disable requests and media state changes
+ //
+
+ KMUTEX MediaChangeMutex;
+
+ //
+ // The current state of the media (present, not present, unknown)
+ // protected by MediaChangeSynchronizationEvent
+ //
+
+ MEDIA_CHANGE_DETECTION_STATE MediaChangeDetectionState;
+
+ //
+ // This is a count of how many time MCD has been disabled. if it is
+ // set to zero, then we'll poll the device for MCN events with the
+ // then-current method (ie. TEST UNIT READY or GESN). this is
+ // protected by MediaChangeMutex
+ //
+
+ LONG MediaChangeDetectionDisableCount;
+
+
+ //
+ // The timer value to support media change events. This is a countdown
+ // value used to determine when to poll the device for a media change.
+ // The max value for the timer is 255 seconds. This is not protected
+ // by an event -- simply InterlockedExchanged() as needed.
+ //
+
+ LONG MediaChangeCountDown;
+
+ //
+ // recent changes allowed instant retries of the MCN irp. Since this
+ // could cause an infinite loop, keep a count of how many times we've
+ // retried immediately so that we can catch if the count exceeds an
+ // arbitrary limit.
+ //
+
+ LONG MediaChangeRetryCount;
+
+ //
+ // use GESN if it's available
+ //
+
+ struct {
+ BOOLEAN Supported;
+ BOOLEAN HackEventMask;
+ UCHAR EventMask;
+ UCHAR NoChangeEventMask;
+ PUCHAR Buffer;
+ PMDL Mdl;
+ ULONG BufferSize;
+ } Gesn;
+
+ //
+ // If this value is one, then the irp is currently in use.
+ // If this value is zero, then the irp is available.
+ // Use InterlockedCompareExchange() to set from "available" to "in use".
+ // ASSERT that InterlockedCompareExchange() showed previous value of
+ // "in use" when changing back to "available" state.
+ // This also implicitly protects the MediaChangeSrb and SenseBuffer
+ //
+
+ LONG MediaChangeIrpInUse;
+
+ //
+ // Pointer to the irp to be used for media change detection.
+ // protected by Interlocked MediaChangeIrpInUse
+ //
+
+ PIRP MediaChangeIrp;
+
+ //
+ // The srb for the media change detection.
+ // protected by Interlocked MediaChangeIrpInUse
+ //
+
+#if (NTDDI_VERSION >= NTDDI_WIN8)
+ union {
+ SCSI_REQUEST_BLOCK Srb;
+ STORAGE_REQUEST_BLOCK SrbEx;
+ UCHAR SrbExBuffer[CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE];
+ } MediaChangeSrb;
+#else
+ SCSI_REQUEST_BLOCK MediaChangeSrb;
+#endif
+ PUCHAR SenseBuffer;
+ ULONG SrbFlags;
+
+ //
+ // Second timer to keep track of how long the media change IRP has been
+ // in use. If this value exceeds the timeout (#defined) then we should
+ // print out a message to the user and set the MediaChangeIrpLost flag
+ // protected by using Interlocked() operations in ClasspSendMediaStateIrp,
+ // the only routine which should modify this value.
+ //
+
+ LONG MediaChangeIrpTimeInUse;
+
+ //
+ // Set by CdRomTickHandler when we determine that the media change irp has
+ // been lost
+ //
+
+ BOOLEAN MediaChangeIrpLost;
+
+ //
+ // Buffer size of SenseBuffer
+ //
+ UCHAR SenseBufferLength;
+
+};
+
+typedef enum {
+ SimpleMediaLock,
+ SecureMediaLock,
+ InternalMediaLock
+} MEDIA_LOCK_TYPE, *PMEDIA_LOCK_TYPE;
+
+typedef struct _FAILURE_PREDICTION_INFO {
+ FAILURE_PREDICTION_METHOD Method;
+ ULONG CountDown; // Countdown timer
+ ULONG Period; // Countdown period
+
+ PIO_WORKITEM WorkQueueItem;
+
+ KEVENT Event;
+
+ //
+ // Timestamp of last time the failure prediction info was queried.
+ //
+ LARGE_INTEGER LastFailurePredictionQueryTime;
+
+} FAILURE_PREDICTION_INFO, *PFAILURE_PREDICTION_INFO;
+
+
+
+//
+// This struct must always fit within four PVOIDs of info,
+// as it uses the irp's "PVOID DriverContext[4]" to store
+// this info
+//
+typedef struct _CLASS_RETRY_INFO {
+ struct _CLASS_RETRY_INFO *Next;
+} CLASS_RETRY_INFO, *PCLASS_RETRY_INFO;
+
+typedef struct _CSCAN_LIST {
+
+ //
+ // The current block which has an outstanding request.
+ //
+
+ ULONGLONG BlockNumber;
+
+ //
+ // The list of blocks past the CurrentBlock to which we're going to do
+ // i/o. This list is maintained in sorted order.
+ //
+
+ LIST_ENTRY CurrentSweep;
+
+ //
+ // The list of blocks behind the current block for which we'll have to
+ // wait until the next scan across the disk. This is kept as a stack,
+ // the cost of sorting it is taken when it's moved over to be the
+ // running list.
+ //
+
+ LIST_ENTRY NextSweep;
+
+} CSCAN_LIST, *PCSCAN_LIST;
+
+//
+// add to the front of this structure to help prevent illegal
+// snooping by other utilities.
+//
+
+
+
+typedef enum _CLASS_DETECTION_STATE {
+ ClassDetectionUnknown = 0,
+ ClassDetectionUnsupported = 1,
+ ClassDetectionSupported = 2
+} CLASS_DETECTION_STATE, *PCLASS_DETECTION_STATE;
+
+#if _MSC_VER >= 1600
+#pragma warning(push)
+#endif
+#pragma warning(disable:4214) // bit field types other than int
+//
+// CLASS_ERROR_LOG_DATA will still use SCSI_REQUEST_BLOCK even
+// when using extended SRB as an extended SRB is too large to
+// fit into. Should revisit this code once classpnp starts to
+// use greater than 16 byte CDB.
+//
+typedef struct _CLASS_ERROR_LOG_DATA {
+ LARGE_INTEGER TickCount; // Offset 0x00
+ ULONG PortNumber; // Offset 0x08
+
+ UCHAR ErrorPaging : 1; // Offset 0x0c
+ UCHAR ErrorRetried : 1;
+ UCHAR ErrorUnhandled : 1;
+ UCHAR ErrorReserved : 5;
+
+ UCHAR Reserved[3];
+
+ SCSI_REQUEST_BLOCK Srb; // Offset 0x10
+
+ /*
+ * We define the SenseData as the default length.
+ * Since the sense data returned by the port driver may be longer,
+ * SenseData must be at the end of this structure.
+ * For our internal error log, we only log the default length.
+ */
+ SENSE_DATA SenseData; // Offset 0x50 for x86 (or 0x68 for ia64) (ULONG32 Alignment required!)
+
+} CLASS_ERROR_LOG_DATA, *PCLASS_ERROR_LOG_DATA;
+#if _MSC_VER >= 1600
+#pragma warning(pop)
+#endif
+
+#define NUM_ERROR_LOG_ENTRIES 16
+#define DBG_NUM_PACKET_LOG_ENTRIES (64*2) // 64 send&receive's
+
+#if (NTDDI_VERSION >= NTDDI_WIN8)
+typedef
+VOID
+(*PCONTINUATION_ROUTINE)(
+ _In_ PVOID Context
+ );
+#endif
+
+typedef struct _TRANSFER_PACKET {
+
+ LIST_ENTRY AllPktsListEntry; // entry in fdoData's static AllTransferPacketsList
+ SLIST_ENTRY SlistEntry; // for when in free list (use fast slist)
+
+ PIRP Irp;
+ PDEVICE_OBJECT Fdo;
+
+ /*
+ * This is the client IRP that this TRANSFER_PACKET is currently
+ * servicing.
+ */
+ PIRP OriginalIrp;
+ BOOLEAN CompleteOriginalIrpWhenLastPacketCompletes;
+
+ /*
+ * Stuff for retrying the transfer.
+ */
+#if (NTDDI_VERSION >= NTDDI_WINBLUE)
+ USHORT NumRetries; // Total number of retries remaining.
+ UCHAR NumIoTimeoutRetries; // Number of retries remaining for a timed-out request.
+ UCHAR TimedOut; // Indicates if this packet has timed-out.
+#else
+ ULONG NumRetries;
+#endif
+ KTIMER RetryTimer;
+ KDPC RetryTimerDPC;
+
+ _Field_range_(0,MAXIMUM_RETRY_FOR_SINGLE_IO_IN_100NS_UNITS)
+ LONGLONG RetryIn100nsUnits;
+
+ /*
+ * Event for synchronizing the transfer (optional).
+ * (Note that we can't have the event in the packet itself because
+ * by the time a thread waits on an event the packet may have
+ * been completed and re-issued.
+ */
+ PKEVENT SyncEventPtr;
+
+ /*
+ * Stuff for retrying during extreme low-memory stress
+ * (when we retry 1 page at a time).
+ * NOTE: These fields are also used for StartIO-based
+ * class drivers, even when not in low memory conditions.
+ */
+ BOOLEAN DriverUsesStartIO; // if this is set, then the below low-mem flags are always used
+ BOOLEAN InLowMemRetry;
+ PUCHAR LowMemRetry_remainingBufPtr;
+ ULONG LowMemRetry_remainingBufLen;
+ LARGE_INTEGER LowMemRetry_nextChunkTargetLocation;
+
+ /*
+ * Fields used for cancelling the packet.
+ */
+ // BOOLEAN Cancelled;
+ // KEVENT CancelledEvent;
+
+ /*
+ * We keep the buffer and length values here as well
+ * as in the SRB because some miniports return
+ * the transferred length in SRB.DataTransferLength,
+ * and if the SRB failed we need that value again for the retry.
+ * We don't trust the lower stack to preserve any of these values in the SRB.
+ */
+ PUCHAR BufPtrCopy;
+ ULONG BufLenCopy;
+ LARGE_INTEGER TargetLocationCopy;
+
+ /*
+ * This is a standard SCSI structure that receives a detailed
+ * report about a SCSI error on the hardware.
+ */
+ SENSE_DATA_EX SrbErrorSenseData;
+
+ /*
+ * This is the SRB block for this TRANSFER_PACKET.
+ * For IOCTLs, the SRB block includes two DWORDs for
+ * device object and ioctl code; so these must
+ * immediately follow the SRB block.
+ */
+
+#if (NTDDI_VERSION >= NTDDI_WIN8)
+ PSTORAGE_REQUEST_BLOCK_HEADER Srb;
+#else
+ SCSI_REQUEST_BLOCK Srb;
+#endif
+ // ULONG SrbIoctlDevObj; // not handling ioctls yet
+ // ULONG SrbIoctlCode;
+
+ #if DBG
+ LARGE_INTEGER DbgTimeSent;
+ LARGE_INTEGER DbgTimeReturned;
+ ULONG DbgPktId;
+ IRP DbgOriginalIrpCopy;
+ MDL DbgMdlCopy;
+ #endif
+
+ BOOLEAN UsePartialMdl;
+ PMDL PartialMdl;
+
+ PSRB_HISTORY RetryHistory;
+
+ // The time at which this request was sent to port driver.
+ ULONGLONG RequestStartTime;
+
+#if (NTDDI_VERSION >= NTDDI_WIN8)
+ // ActivityId that is associated with the IRP that this transfer packet services.
+ GUID ActivityId;
+
+ // If non-NULL, called at packet completion with this context.
+ PCONTINUATION_ROUTINE ContinuationRoutine;
+ PVOID ContinuationContext;
+ ULONGLONG TransferCount;
+ ULONG AllocateNode;
+#endif
+} TRANSFER_PACKET, *PTRANSFER_PACKET;
+
+/*
+ * MIN_INITIAL_TRANSFER_PACKETS is the minimum number of packets that
+ * we preallocate at startup for each device (we need at least one packet
+ * to guarantee forward progress during memory stress).
+ * MIN_WORKINGSET_TRANSFER_PACKETS is the number of TRANSFER_PACKETs
+ * we allow to build up and remain for each device;
+ * we _lazily_ work down to this number when they're not needed.
+ * MAX_WORKINGSET_TRANSFER_PACKETS is the number of TRANSFER_PACKETs
+ * that we _immediately_ reduce to when they are not needed.
+ *
+ * The absolute maximum number of packets that we will allocate is
+ * whatever is required by the current activity, up to the memory limit;
+ * as soon as stress ends, we snap down to MAX_WORKINGSET_TRANSFER_PACKETS;
+ * we then lazily work down to MIN_WORKINGSET_TRANSFER_PACKETS.
+ */
+#define MIN_INITIAL_TRANSFER_PACKETS 1
+#define MIN_WORKINGSET_TRANSFER_PACKETS_Client 16
+#define MAX_WORKINGSET_TRANSFER_PACKETS_Client 32
+#define MIN_WORKINGSET_TRANSFER_PACKETS_Server_UpperBound 256
+#define MIN_WORKINGSET_TRANSFER_PACKETS_Server_LowerBound 32
+#define MAX_WORKINGSET_TRANSFER_PACKETS_Server 1024
+#define MIN_WORKINGSET_TRANSFER_PACKETS_SPACES 512
+#define MAX_WORKINGSET_TRANSFER_PACKETS_SPACES 2048
+#define MAX_OUTSTANDING_IO_PER_LUN_DEFAULT 16
+#define MAX_CLEANUP_TRANSFER_PACKETS_AT_ONCE 8192
+
+
+
+typedef struct _PNL_SLIST_HEADER {
+ DECLSPEC_CACHEALIGN SLIST_HEADER SListHeader;
+ DECLSPEC_CACHEALIGN ULONG NumFreeTransferPackets;
+ ULONG NumTotalTransferPackets;
+ ULONG DbgPeakNumTransferPackets;
+} PNL_SLIST_HEADER, *PPNL_SLIST_HEADER;
+
+//
+// !!! WARNING !!!
+// DO NOT use the following structure in code outside of classpnp
+// as structure will not be guaranteed between OS versions.
+//
+// add to the front of this structure to help prevent illegal
+// snooping by other utilities.
+//
+struct _CLASS_PRIVATE_FDO_DATA {
+
+
+#if (NTDDI_VERSION >= NTDDI_WIN8)
+
+ //
+ // Periodic timer for polling for media change detection, failure prediction
+ // and class tick function.
+ //
+
+#if (NTDDI_VERSION >= NTDDI_WINBLUE)
+ PEX_TIMER TickTimer;
+ LONGLONG CurrentNoWakeTolerance;
+#else
+ KTIMER TickTimer;
+ KDPC TickTimerDpc;
+#endif // (NTDDI_VERSION >= NTDDI_WINBLUE)
+
+ //
+ // Power related and release queue SRBs
+ //
+ union {
+ STORAGE_REQUEST_BLOCK SrbEx;
+ UCHAR PowerSrbBuffer[CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE];
+ } PowerSrb;
+
+ union {
+ STORAGE_REQUEST_BLOCK SrbEx;
+ UCHAR ReleaseQueueSrbBuffer[CLASS_SRBEX_NO_SRBEX_DATA_BUFFER_SIZE];
+ } ReleaseQueueSrb;
+
+#endif
+
+ ULONG TrackingFlags;
+
+ /*
+ * Flag to detect recursion caused by devices
+ * reporting different capacity per each request
+ */
+ ULONG UpdateDiskPropertiesWorkItemActive;
+
+ //
+ // Local equivalents of MinWorkingSetTransferPackets and MaxWorkingSetTransferPackets.
+ // These values are initialized by the global equivalents but are then adjusted as
+ // requested by the class driver.
+ //
+ ULONG LocalMinWorkingSetTransferPackets;
+ ULONG LocalMaxWorkingSetTransferPackets;
+
+#if DBG
+
+ ULONG MaxOutstandingIOPerLUN;
+
+#endif
+
+ /*
+ * Entry in static list used by debug extension to quickly find all class FDOs.
+ */
+ LIST_ENTRY AllFdosListEntry;
+
+ //
+ // this private structure allows us to
+ // dynamically re-enable the perf benefits
+ // lost due to transient error conditions.
+ // in w2k, a reboot was required. :(
+ //
+ struct {
+ ULONG OriginalSrbFlags;
+ ULONG SuccessfulIO;
+ ULONG ReEnableThreshhold; // 0 means never
+ } Perf;
+
+ ULONG_PTR HackFlags;
+
+ STORAGE_HOTPLUG_INFO HotplugInfo;
+
+ // Legacy. Still used by obsolete legacy code.
+ struct {
+ LARGE_INTEGER Delta; // in ticks
+ LARGE_INTEGER Tick; // when it should fire
+ PCLASS_RETRY_INFO ListHead; // singly-linked list
+ ULONG Granularity; // static
+ KSPIN_LOCK Lock; // protective spin lock
+ KDPC Dpc; // DPC routine object
+ KTIMER Timer; // timer to fire DPC
+ } Retry;
+
+ BOOLEAN TimerInitialized;
+ BOOLEAN LoggedTURFailureSinceLastIO;
+ BOOLEAN LoggedSYNCFailure;
+
+ //
+ // privately allocated release queue irp
+ // protected by fdoExtension->ReleaseQueueSpinLock
+ //
+ BOOLEAN ReleaseQueueIrpAllocated;
+ PIRP ReleaseQueueIrp;
+
+ /*
+ * Queues for TRANSFER_PACKETs that contextualize the IRPs and SRBs
+ * that we send down to the port driver.
+ * (The free list is an slist so that we can use fast
+ * interlocked operations on it; but the relatively-static
+ * AllTransferPacketsList list has to be
+ * a doubly-linked list since we have to dequeue from the middle).
+ */
+ LIST_ENTRY AllTransferPacketsList;
+ PPNL_SLIST_HEADER FreeTransferPacketsLists;
+
+ /*
+ * Queue for deferred client irps
+ */
+ LIST_ENTRY DeferredClientIrpList;
+
+ /*
+ * Precomputed maximum transfer length for the hardware.
+ */
+ ULONG HwMaxXferLen;
+
+ /*
+ * SCSI_REQUEST_BLOCK template preconfigured with the constant values.
+ * This is slapped into the SRB in the TRANSFER_PACKET for each transfer.
+ */
+
+#if (NTDDI_VERSION >= NTDDI_WIN8)
+ PSTORAGE_REQUEST_BLOCK_HEADER SrbTemplate;
+#else
+ SCSI_REQUEST_BLOCK SrbTemplate;
+#endif
+
+ KSPIN_LOCK SpinLock;
+
+ /*
+ * For non-removable media, we read the drive capacity at start time and cache it.
+ * This is so that ReadDriveCapacity failures at runtime (e.g. due to memory stress)
+ * don't cause I/O on the paging disk to start failing.
+ */
+ READ_CAPACITY_DATA_EX LastKnownDriveCapacityData;
+ BOOLEAN IsCachedDriveCapDataValid;
+
+ //
+ // Idle priority support flag
+ //
+ BOOLEAN IdlePrioritySupported;
+
+ //
+ // Tick timer enabled
+ //
+ BOOLEAN TickTimerEnabled;
+
+ BOOLEAN ReservedBoolean;
+
+ /*
+ * Circular array of timestamped logs of errors that occurred on this device.
+ */
+ ULONG ErrorLogNextIndex;
+ CLASS_ERROR_LOG_DATA ErrorLogs[NUM_ERROR_LOG_ENTRIES];
+
+ //
+ // Number of outstanding critical Io requests from Mm
+ //
+ ULONG NumHighPriorityPagingIo;
+
+ //
+ // Maximum number of normal Io requests that can be interleaved with the critical ones
+ //
+ ULONG MaxInterleavedNormalIo;
+
+ //
+ // The timestamp when entering throttle mode
+ //
+ LARGE_INTEGER ThrottleStartTime;
+
+ //
+ // The timestamp when exiting throttle mode
+ //
+ LARGE_INTEGER ThrottleStopTime;
+
+ //
+ // The longest time ever spent in throttle mode
+ //
+ LARGE_INTEGER LongestThrottlePeriod;
+
+ #if DBG
+ ULONG DbgMaxPktId;
+
+ /*
+ * Logging fields for ForceUnitAccess and Flush
+ */
+ BOOLEAN DbgInitFlushLogging; // must reset this to 1 for each logging session
+ ULONG DbgNumIORequests;
+ ULONG DbgNumFUAs; // num I/O requests with ForceUnitAccess bit set
+ ULONG DbgNumFlushes; // num SRB_FUNCTION_FLUSH_QUEUE
+ ULONG DbgIOsSinceFUA;
+ ULONG DbgIOsSinceFlush;
+ ULONG DbgAveIOsToFUA; // average number of I/O requests between FUAs
+ ULONG DbgAveIOsToFlush; // ...
+ ULONG DbgMaxIOsToFUA;
+ ULONG DbgMaxIOsToFlush;
+ ULONG DbgMinIOsToFUA;
+ ULONG DbgMinIOsToFlush;
+
+ /*
+ * Debug log of previously sent packets (including retries).
+ */
+ ULONG DbgPacketLogNextIndex;
+ TRANSFER_PACKET DbgPacketLogs[DBG_NUM_PACKET_LOG_ENTRIES];
+ #endif
+
+ //
+ // Spin lock for low priority I/O list
+ //
+ KSPIN_LOCK IdleListLock;
+
+ //
+ // Queue for low priority I/O
+ //
+ LIST_ENTRY IdleIrpList;
+
+ //
+ // Timer for low priority I/O
+ //
+ KTIMER IdleTimer;
+
+ //
+ // DPC for low priority I/O
+ //
+ KDPC IdleDpc;
+
+#if (NTDDI_VERSION >= NTDDI_WIN8)
+
+ //
+ // Time (ms) since the completion of the last non-idle request before the
+ // first idle request should be issued. Due to the coarseness of the idle
+ // timer frequency, some variability in the idle interval will be tolerated
+ // such that it is the desired idle interval on average.
+ //
+ USHORT IdleInterval;
+
+ //
+ // Max number of active idle requests.
+ //
+ USHORT IdleActiveIoMax;
+
+#endif
+
+ //
+ // Timer interval for sending low priority I/O
+ //
+ USHORT IdleTimerInterval;
+
+ //
+ // Idle counts required to process idle request
+ // to avoid starvation
+ //
+ USHORT StarvationCount;
+
+ //
+ // Idle timer tick count
+ //
+ ULONG IdleTimerTicks;
+
+ //
+ // Idle timer tick count
+ //
+ ULONG IdleTicks;
+
+ //
+ // Idle I/O count
+ //
+ ULONG IdleIoCount;
+
+ //
+ // Flag to indicate timer status
+ //
+ LONG IdleTimerStarted;
+
+ //
+ // Normal priority I/O time
+ //
+ LARGE_INTEGER LastIoTime;
+
+ //
+ // Count of active normal priority I/O
+ //
+ LONG ActiveIoCount;
+
+ //
+ // Count of active idle priority I/O
+ //
+ LONG ActiveIdleIoCount;
+
+ //
+ // Support for class drivers to extend
+ // the interpret sense information routine
+ // and retry history per-packet. Copy of
+ // values in driver extension.
+ //
+ PCLASS_INTERPRET_SENSE_INFO2 InterpretSenseInfo;
+
+ //
+ // power process parameters. they work closely with CLASS_POWER_CONTEXT structure.
+ //
+ ULONG MaxPowerOperationRetryCount;
+ PIRP PowerProcessIrp;
+
+ // Counter frequency : Currently used for KeQueryPerferformanceCounter
+ LARGE_INTEGER PerfCounterFrequency;
+
+ //
+ // Indicates legacy error handling should be used.
+ // This means:
+ // - Max number of retries for an IO request is 8 (instead of 4).
+ //
+ BOOLEAN LegacyErrorHandling;
+
+ //
+ // Maximum number of retries allowed for IO requests for this device.
+ //
+ UCHAR MaxNumberOfIoRetries;
+};
+
+//
+// !!! WARNING !!!
+// DO NOT use the following structure in code outside of classpnp
+// as structure will not be guaranteed between OS versions.
+//
+// EX_RUNDOWN_REF_CACHE_AWARE is variable size and follows
+// RemoveLockFailAcquire. EX_RUNDOWN_REF_CACHE_AWARE must be part
+// of the device extension allocation to avoid issues with a device
+// that has been PNP remove but still has outstanding references.
+// In this case, the removed object may still receive incoming requests.
+//
+// There are code dependencies on the structure layout. To minimize
+// code changes, new fields to _CLASS_PRIVATE_COMMON_DATA should be
+// added based on the following guidance.
+// - Fixed size: beginning of _CLASS_PRIVATE_COMMON_DATA
+// - Variable size: at the end of _CLASS_PRIVATE_COMMON_DATA after the
+// last variable size field.
+//
+
+struct _CLASS_PRIVATE_COMMON_DATA {
+
+ //
+ // Cacheaware rundown lock reference
+ //
+
+ LONG RemoveLockFailAcquire;
+
+ //
+ // N.B. EX_RUNDOWN_REF_CACHE_AWARE begins with a pointer-sized item that is
+ // accessed interlocked, and must be aligned on ARM platforms. In order
+ // for this to work on ARM64, an additional 32-bit slot must be allocated.
+ //
+
+#if defined(_WIN64)
+ LONG Align;
+#endif
+
+ // EX_RUNDOWN_REF_CACHE_AWARE (variable size) follows
+
+};
+
+//
+// Verify that the size of _CLASS_PRIVATE_COMMON_DATA is pointer size aligned
+// to ensure the EX_RUNDOWN_REF_CACHE_AWARE following it is properly aligned.
+//
+
+C_ASSERT((sizeof(struct _CLASS_PRIVATE_COMMON_DATA) % sizeof(PVOID)) == 0);
+
+typedef struct _IDLE_POWER_FDO_LIST_ENTRY {
+ LIST_ENTRY ListEntry;
+ PDEVICE_OBJECT Fdo;
+} IDLE_POWER_FDO_LIST_ENTRY, *PIDLE_POWER_FDO_LIST_ENTRY;
+
+typedef struct _OFFLOAD_READ_CONTEXT {
+
+ PDEVICE_OBJECT Fdo;
+
+ //
+ // Upper offload read DSM irp.
+ //
+
+ PIRP OffloadReadDsmIrp;
+
+ //
+ // A pseudo-irp is used despite the operation being async. This is in
+ // contrast to normal read and write, which let TransferPktComplete()
+ // complete the upper IRP directly. Offload requests are enough different
+ // that it makes more sense to let them manage their own async steps with
+ // minimal help from TransferPktComplete() (just a continuation function
+ // call during TransferPktComplete()).
+ //
+
+ IRP PseudoIrp;
+
+ //
+ // The offload read context tracks one packet in flight at a time - it'll be
+ // the POPULATE TOKEN packet first, then RECEIVE ROD TOKEN INFORMATION.
+ //
+ // This field exists only for debug purposes.
+ //
+
+ PTRANSFER_PACKET Pkt;
+
+ PMDL PopulateTokenMdl;
+
+ ULONG BufferLength;
+
+ ULONG ListIdentifier;
+
+ ULONG ReceiveTokenInformationBufferLength;
+
+ //
+ // Total sectors that the operation is attempting to process.
+ //
+
+ ULONGLONG TotalSectorsToProcess;
+
+ //
+ // Total sectors actually processed.
+ //
+
+ ULONGLONG TotalSectorsProcessed;
+
+ //
+ // Total upper request size in bytes.
+ //
+
+ ULONGLONG EntireXferLen;
+
+ //
+ // Just a cached copy of what was in the transfer packet.
+ //
+
+ SCSI_REQUEST_BLOCK Srb;
+
+ //
+ // Pointer into the token part of the SCSI buffer (the buffer immediately
+ // after this struct), for easy reference.
+ //
+
+ PUCHAR Token;
+
+ // The SCSI buffer (in/out buffer, not CDB) for the commands immediately
+ // follows this struct, so no need to have a field redundantly pointing to
+ // the buffer.
+} OFFLOAD_READ_CONTEXT, *POFFLOAD_READ_CONTEXT;
+
+
+typedef struct _OFFLOAD_WRITE_CONTEXT {
+
+ PDEVICE_OBJECT Fdo;
+
+ PIRP OffloadWriteDsmIrp;
+
+ ULONGLONG EntireXferLen;
+ ULONGLONG TotalRequestSizeSectors;
+
+ ULONG DataSetRangesCount;
+
+ PDEVICE_MANAGE_DATA_SET_ATTRIBUTES DsmAttributes;
+ PDEVICE_DATA_SET_RANGE DataSetRanges;
+ PDEVICE_DSM_OFFLOAD_WRITE_PARAMETERS OffloadWriteParameters;
+ ULONGLONG LogicalBlockOffset;
+
+ ULONG MaxBlockDescrCount;
+ ULONGLONG MaxLbaCount;
+
+ ULONG BufferLength;
+ ULONG ReceiveTokenInformationBufferLength;
+
+ IRP PseudoIrp;
+
+ PMDL WriteUsingTokenMdl;
+
+ ULONGLONG TotalSectorsProcessedSuccessfully;
+ ULONG DataSetRangeIndex;
+ ULONGLONG DataSetRangeByteOffset;
+
+ PTRANSFER_PACKET Pkt;
+
+ //
+ // Per-WUT (WRITE USING TOKEN), not overall.
+ //
+
+ ULONGLONG TotalSectorsToProcess;
+ ULONGLONG TotalSectorsProcessed;
+
+ ULONG ListIdentifier;
+
+ BOOLEAN TokenInvalidated;
+
+ //
+ // Just a cached copy of what was in the transfer packet.
+ //
+
+ SCSI_REQUEST_BLOCK Srb;
+
+ ULONGLONG OperationStartTime;
+
+} OFFLOAD_WRITE_CONTEXT, *POFFLOAD_WRITE_CONTEXT;
+
+
+typedef struct _OPCODE_SENSE_DATA_IO_LOG_MESSAGE_CONTEXT_HEADER {
+ PIO_WORKITEM WorkItem;
+ PVOID SenseData;
+ ULONG SenseDataSize;
+ UCHAR SrbStatus;
+ UCHAR ScsiStatus;
+ UCHAR OpCode;
+ UCHAR Reserved;
+ ULONG ErrorCode;
+} OPCODE_SENSE_DATA_IO_LOG_MESSAGE_CONTEXT_HEADER, *POPCODE_SENSE_DATA_IO_LOG_MESSAGE_CONTEXT_HEADER;
+
+typedef struct _IO_RETRIED_LOG_MESSAGE_CONTEXT {
+ OPCODE_SENSE_DATA_IO_LOG_MESSAGE_CONTEXT_HEADER ContextHeader;
+ LARGE_INTEGER Lba;
+ ULONG DeviceNumber;
+} IO_RETRIED_LOG_MESSAGE_CONTEXT, *PIO_RETRIED_LOG_MESSAGE_CONTEXT;
+
+
+#define QERR_SET_ZERO_ODX_OR_TP_ONLY 0
+#define QERR_SET_ZERO_ALWAYS 1
+#define QERR_SET_ZERO_NEVER 2
+
+
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+
+
+#define NOT_READY_RETRY_INTERVAL 10
+#define MINIMUM_RETRY_UNITS ((LONGLONG)32)
+#define MODE_PAGE_DATA_SIZE 192
+
+#define CLASS_IDLE_INTERVAL 50 // 50 milliseconds
+#define CLASS_STARVATION_INTERVAL 500 // 500 milliseconds
+#define CLASS_IDLE_TIMER_TICKS 4
+
+
+/*
+ * Simple singly-linked-list queuing macros, with no synchronization.
+ */
+__inline VOID SimpleInitSlistHdr(SINGLE_LIST_ENTRY *SListHdr)
+{
+ SListHdr->Next = NULL;
+}
+__inline VOID SimplePushSlist(SINGLE_LIST_ENTRY *SListHdr, SINGLE_LIST_ENTRY *SListEntry)
+{
+ SListEntry->Next = SListHdr->Next;
+ SListHdr->Next = SListEntry;
+}
+__inline SINGLE_LIST_ENTRY *SimplePopSlist(SINGLE_LIST_ENTRY *SListHdr)
+{
+ SINGLE_LIST_ENTRY *sListEntry = SListHdr->Next;
+ if (sListEntry){
+ SListHdr->Next = sListEntry->Next;
+ sListEntry->Next = NULL;
+ }
+ return sListEntry;
+}
+__inline BOOLEAN SimpleIsSlistEmpty(SINGLE_LIST_ENTRY *SListHdr)
+{
+ return (SListHdr->Next == NULL);
+}
+
+__inline
+BOOLEAN
+ClasspIsIdleRequestSupported(
+ PCLASS_PRIVATE_FDO_DATA FdoData,
+ PIRP Irp
+ )
+{
+ IO_PRIORITY_HINT ioPriority = IoGetIoPriorityHint(Irp);
+ return ((ioPriority <= IoPriorityLow) && (FdoData->IdlePrioritySupported == TRUE));
+}
+
+__inline
+VOID
+ClasspMarkIrpAsIdle(
+ PIRP Irp,
+ BOOLEAN Idle
+ )
+{
+// truncation is not an issue for this use case
+// nonstandard extension used is not an issue for this use case
+#pragma warning(suppress:4305; suppress:4213)
+ ((BOOLEAN)Irp->Tail.Overlay.DriverContext[1]) = Idle;
+}
+
+__inline
+BOOLEAN
+ClasspIsIdleRequest(
+ PIRP Irp
+ )
+{
+#pragma warning(suppress:4305) // truncation is not an issue for this use case
+ return ((BOOLEAN)Irp->Tail.Overlay.DriverContext[1]);
+}
+
+extern BOOLEAN UseQPCTime;
+
+__inline
+LARGE_INTEGER
+ClasspGetCurrentTime(
+ PLARGE_INTEGER Frequency
+ )
+{
+ LARGE_INTEGER currentTime;
+
+ if (UseQPCTime) {
+ currentTime = KeQueryPerformanceCounter(Frequency);
+ } else {
+ currentTime.QuadPart = (LONGLONG)KeQueryUnbiasedInterruptTime();
+ }
+
+ return currentTime;
+}
+
+__inline
+ULONGLONG
+ClasspTimeDiffToMs(
+ PCLASS_PRIVATE_FDO_DATA FdoData,
+ ULONGLONG TimeDiff
+ )
+{
+ if (UseQPCTime) {
+ TimeDiff *= 1000;
+ TimeDiff /= FdoData->PerfCounterFrequency.QuadPart;
+ } else {
+ TimeDiff /= (10 * 1000);
+ }
+
+ return TimeDiff;
+}
+
+__inline
+BOOLEAN
+ClasspSupportsUnmap(
+ _In_ PCLASS_FUNCTION_SUPPORT_INFO SupportInfo
+ )
+{
+ return SupportInfo->LBProvisioningData.LBPU;
+}
+
+__inline
+BOOLEAN
+ClasspIsThinProvisioned(
+ _In_ PCLASS_FUNCTION_SUPPORT_INFO SupportInfo
+ )
+{
+ //
+ // We only support thinly provisioned devices that also support UNMAP.
+ //
+ if (SupportInfo->LBProvisioningData.ProvisioningType == PROVISIONING_TYPE_THIN &&
+ SupportInfo->LBProvisioningData.LBPU == TRUE)
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+__inline
+BOOLEAN
+ClasspIsObsoletePortDriver(
+ _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ )
+{
+ if ( (FdoExtension->MiniportDescriptor != NULL) &&
+ (FdoExtension->MiniportDescriptor->Portdriver == StoragePortCodeSetSCSIport) ) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+ULONG
+ClasspCalculateLogicalSectorSize (
+ _In_ PDEVICE_OBJECT Fdo,
+ _In_ ULONG BytesPerBlockInBigEndian
+ );
+
+DRIVER_INITIALIZE DriverEntry;
+
+DRIVER_UNLOAD ClassUnload;
+
+_Dispatch_type_(IRP_MJ_CREATE)
+_Dispatch_type_(IRP_MJ_CLOSE)
+DRIVER_DISPATCH ClassCreateClose;
+
+NTSTATUS
+ClasspCreateClose(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+VOID
+ClasspCleanupProtectedLocks(
+ IN PFILE_OBJECT_EXTENSION FsContext
+ );
+
+NTSTATUS
+ClasspEjectionControl(
+ IN PDEVICE_OBJECT Fdo,
+ IN PIRP Irp,
+ IN MEDIA_LOCK_TYPE LockType,
+ IN BOOLEAN Lock
+ );
+
+_Dispatch_type_(IRP_MJ_READ)
+_Dispatch_type_(IRP_MJ_WRITE)
+DRIVER_DISPATCH ClassReadWrite;
+
+_Dispatch_type_(IRP_MJ_DEVICE_CONTROL)
+DRIVER_DISPATCH ClassDeviceControlDispatch;
+
+_Dispatch_type_(IRP_MJ_PNP)
+DRIVER_DISPATCH ClassDispatchPnp;
+
+NTSTATUS
+ClassPnpStartDevice(
+ IN PDEVICE_OBJECT DeviceObject
+ );
+
+_Dispatch_type_(IRP_MJ_SHUTDOWN)
+_Dispatch_type_(IRP_MJ_FLUSH_BUFFERS)
+DRIVER_DISPATCH ClassShutdownFlush;
+
+_Dispatch_type_(IRP_MJ_SYSTEM_CONTROL)
+DRIVER_DISPATCH ClassSystemControl;
+
+
+//
+// Class internal routines
+//
+
+DRIVER_ADD_DEVICE ClassAddDevice;
+
+IO_COMPLETION_ROUTINE ClasspSendSynchronousCompletion;
+
+VOID
+RetryRequest(
+ PDEVICE_OBJECT DeviceObject,
+ PIRP Irp,
+ PSCSI_REQUEST_BLOCK Srb,
+ BOOLEAN Associated,
+ LONGLONG TimeDelta100ns
+ );
+
+NTSTATUS
+ClassIoCompletion(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ );
+
+NTSTATUS
+ClassPnpQueryFdoRelations(
+ IN PDEVICE_OBJECT Fdo,
+ IN PIRP Irp
+ );
+
+NTSTATUS
+ClassRetrieveDeviceRelations(
+ IN PDEVICE_OBJECT Fdo,
+ IN DEVICE_RELATION_TYPE RelationType,
+ OUT PDEVICE_RELATIONS *DeviceRelations
+ );
+
+NTSTATUS
+ClassGetPdoId(
+ IN PDEVICE_OBJECT Pdo,
+ IN BUS_QUERY_ID_TYPE IdType,
+ IN PUNICODE_STRING IdString
+ );
+
+NTSTATUS
+ClassQueryPnpCapabilities(
+ IN PDEVICE_OBJECT PhysicalDeviceObject,
+ IN PDEVICE_CAPABILITIES Capabilities
+ );
+
+DRIVER_STARTIO ClasspStartIo;
+
+NTSTATUS
+ClasspPagingNotificationCompletion(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PDEVICE_OBJECT RealDeviceObject
+ );
+
+NTSTATUS
+ClasspMediaChangeCompletion(
+ PDEVICE_OBJECT DeviceObject,
+ PIRP Irp,
+ PVOID Context
+ );
+
+NTSTATUS
+ClasspMcnControl(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN PIRP Irp,
+ IN PSCSI_REQUEST_BLOCK Srb
+ );
+
+VOID
+ClasspRegisterMountedDeviceInterface(
+ IN PDEVICE_OBJECT DeviceObject
+ );
+
+VOID
+ClasspDisableTimer(
+ _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ );
+
+VOID
+ClasspEnableTimer(
+ _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ );
+
+NTSTATUS
+ClasspInitializeTimer(
+ _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+);
+
+VOID
+ClasspDeleteTimer(
+ _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+);
+
+#if (NTDDI_VERSION >= NTDDI_WINBLUE)
+BOOLEAN
+ClasspUpdateTimerNoWakeTolerance(
+ _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+);
+#endif
+
+NTSTATUS
+ClasspDuidQueryProperty(
+ PDEVICE_OBJECT DeviceObject,
+ PIRP Irp
+ );
+
+_Dispatch_type_(IRP_MJ_CREATE)
+_Dispatch_type_(IRP_MJ_CLOSE)
+_Dispatch_type_(IRP_MJ_READ)
+_Dispatch_type_(IRP_MJ_WRITE)
+_Dispatch_type_(IRP_MJ_SCSI)
+_Dispatch_type_(IRP_MJ_DEVICE_CONTROL)
+_Dispatch_type_(IRP_MJ_SHUTDOWN)
+_Dispatch_type_(IRP_MJ_FLUSH_BUFFERS)
+_Dispatch_type_(IRP_MJ_PNP)
+_Dispatch_type_(IRP_MJ_POWER)
+_Dispatch_type_(IRP_MJ_SYSTEM_CONTROL)
+DRIVER_DISPATCH ClassGlobalDispatch;
+
+VOID
+ClassInitializeDispatchTables(
+ PCLASS_DRIVER_EXTENSION DriverExtension
+ );
+
+NTSTATUS
+ClasspPersistentReserve(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _In_ PIRP Irp,
+ _Inout_ PSCSI_REQUEST_BLOCK Srb
+ );
+
+//
+// routines for dictionary list support
+//
+
+VOID
+InitializeDictionary(
+ IN PDICTIONARY Dictionary
+ );
+
+BOOLEAN
+TestDictionarySignature(
+ IN PDICTIONARY Dictionary
+ );
+
+NTSTATUS
+AllocateDictionaryEntry(
+ IN PDICTIONARY Dictionary,
+ IN ULONGLONG Key,
+ IN ULONG Size,
+ IN ULONG Tag,
+ OUT PVOID *Entry
+ );
+
+PVOID
+GetDictionaryEntry(
+ IN PDICTIONARY Dictionary,
+ IN ULONGLONG Key
+ );
+
+VOID
+FreeDictionaryEntry(
+ IN PDICTIONARY Dictionary,
+ IN PVOID Entry
+ );
+
+
+NTSTATUS
+ClasspAllocateReleaseRequest(
+ IN PDEVICE_OBJECT Fdo
+ );
+
+VOID
+ClasspFreeReleaseRequest(
+ IN PDEVICE_OBJECT Fdo
+ );
+
+IO_COMPLETION_ROUTINE ClassReleaseQueueCompletion;
+
+VOID
+ClasspReleaseQueue(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP ReleaseQueueIrp
+ );
+
+VOID
+ClasspDisablePowerNotification(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+);
+
+//
+// class power routines
+//
+
+_Dispatch_type_(IRP_MJ_POWER)
+DRIVER_DISPATCH ClassDispatchPower;
+
+NTSTATUS
+ClassMinimalPowerHandler(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+_IRQL_requires_same_
+NTSTATUS
+ClasspEnableIdlePower(
+ _In_ PDEVICE_OBJECT DeviceObject
+ );
+
+POWER_SETTING_CALLBACK ClasspPowerSettingCallback;
+
+//
+// Child list routines
+//
+
+VOID
+ClassAddChild(
+ _In_ PFUNCTIONAL_DEVICE_EXTENSION Parent,
+ _In_ PPHYSICAL_DEVICE_EXTENSION Child,
+ _In_ BOOLEAN AcquireLock
+ );
+
+PPHYSICAL_DEVICE_EXTENSION
+ClassRemoveChild(
+ IN PFUNCTIONAL_DEVICE_EXTENSION Parent,
+ IN PPHYSICAL_DEVICE_EXTENSION Child,
+ IN BOOLEAN AcquireLock
+ );
+
+VOID
+ClasspRetryDpcTimer(
+ IN PCLASS_PRIVATE_FDO_DATA FdoData
+ );
+
+KDEFERRED_ROUTINE ClasspRetryRequestDpc;
+
+VOID
+ClassFreeOrReuseSrb(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN __drv_freesMem(mem) PSCSI_REQUEST_BLOCK Srb
+ );
+
+VOID
+ClassRetryRequest(
+ IN PDEVICE_OBJECT SelfDeviceObject,
+ IN PIRP Irp,
+ _In_ _In_range_(0,MAXIMUM_RETRY_FOR_SINGLE_IO_IN_100NS_UNITS) // this is 100 seconds; already an assert in classpnp based on this
+ IN LONGLONG TimeDelta100ns // in 100ns units
+ );
+
+VOID
+ClasspBuildRequestEx(
+ _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ _In_ PIRP Irp,
+ _In_ __drv_aliasesMem PSCSI_REQUEST_BLOCK Srb
+ );
+
+NTSTATUS
+ClasspAllocateReleaseQueueIrp(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ );
+
+NTSTATUS
+ClasspAllocatePowerProcessIrp(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ );
+
+NTSTATUS
+ClasspInitializeGesn(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN PMEDIA_CHANGE_DETECTION_INFO Info
+ );
+
+VOID
+ClassSendEjectionNotification(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ );
+
+VOID
+ClasspScanForSpecialInRegistry(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ );
+
+VOID
+ClasspScanForClassHacks(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN ULONG_PTR Data
+ );
+
+NTSTATUS
+ClasspInitializeHotplugInfo(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ );
+
+VOID
+ClasspPerfIncrementErrorCount(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ );
+VOID
+ClasspPerfIncrementSuccessfulIo(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ );
+
+IO_WORKITEM_ROUTINE ClasspUpdateDiskProperties;
+
+__drv_allocatesMem(Mem)
+PTRANSFER_PACKET NewTransferPacket(PDEVICE_OBJECT Fdo);
+VOID DestroyTransferPacket(_In_ __drv_freesMem(mem) PTRANSFER_PACKET Pkt);
+VOID EnqueueFreeTransferPacket(PDEVICE_OBJECT Fdo, __drv_aliasesMem PTRANSFER_PACKET Pkt);
+PTRANSFER_PACKET DequeueFreeTransferPacket(PDEVICE_OBJECT Fdo, BOOLEAN AllocIfNeeded);
+PTRANSFER_PACKET DequeueFreeTransferPacketEx(_In_ PDEVICE_OBJECT Fdo, _In_ BOOLEAN AllocIfNeeded, _In_ ULONG Node);
+VOID SetupReadWriteTransferPacket(PTRANSFER_PACKET pkt, PVOID Buf, ULONG Len, LARGE_INTEGER DiskLocation, PIRP OriginalIrp);
+NTSTATUS SubmitTransferPacket(PTRANSFER_PACKET Pkt);
+IO_COMPLETION_ROUTINE TransferPktComplete;
+NTSTATUS ServiceTransferRequest(PDEVICE_OBJECT Fdo, PIRP Irp, BOOLEAN PostToDpc);
+VOID TransferPacketQueueRetryDpc(PTRANSFER_PACKET Pkt);
+KDEFERRED_ROUTINE TransferPacketRetryTimerDpc;
+BOOLEAN InterpretTransferPacketError(PTRANSFER_PACKET Pkt);
+BOOLEAN RetryTransferPacket(PTRANSFER_PACKET Pkt);
+VOID EnqueueDeferredClientIrp(PDEVICE_OBJECT Fdo, PIRP Irp);
+PIRP DequeueDeferredClientIrp(PDEVICE_OBJECT Fdo);
+VOID InitLowMemRetry(PTRANSFER_PACKET Pkt, PVOID BufPtr, ULONG Len, LARGE_INTEGER TargetLocation);
+BOOLEAN StepLowMemRetry(PTRANSFER_PACKET Pkt);
+VOID SetupEjectionTransferPacket(TRANSFER_PACKET *Pkt, BOOLEAN PreventMediaRemoval, PKEVENT SyncEventPtr, PIRP OriginalIrp);
+VOID SetupModeSenseTransferPacket(TRANSFER_PACKET *Pkt, PKEVENT SyncEventPtr, PVOID ModeSenseBuffer, UCHAR ModeSenseBufferLen, UCHAR PageMode, UCHAR SubPage, PIRP OriginalIrp, UCHAR PageControl);
+VOID SetupModeSelectTransferPacket(TRANSFER_PACKET *Pkt, PKEVENT SyncEventPtr, PVOID ModeSelectBuffer, UCHAR ModeSelectBufferLen, BOOLEAN SavePages, PIRP OriginalIrp);
+VOID SetupDriveCapacityTransferPacket(TRANSFER_PACKET *Pkt, PVOID ReadCapacityBuffer, ULONG ReadCapacityBufferLen, PKEVENT SyncEventPtr, PIRP OriginalIrp, BOOLEAN Use16ByteCdb);
+PMDL BuildDeviceInputMdl(PVOID Buffer, ULONG BufferLen);
+PMDL ClasspBuildDeviceMdl(PVOID Buffer, ULONG BufferLen, BOOLEAN WriteToDevice);
+VOID FreeDeviceInputMdl(PMDL Mdl);
+VOID ClasspFreeDeviceMdl(PMDL Mdl);
+NTSTATUS InitializeTransferPackets(PDEVICE_OBJECT Fdo);
+VOID DestroyAllTransferPackets(PDEVICE_OBJECT Fdo);
+VOID InterpretCapacityData(PDEVICE_OBJECT Fdo, PREAD_CAPACITY_DATA_EX ReadCapacityData);
+IO_WORKITEM_ROUTINE_EX CleanupTransferPacketToWorkingSetSizeWorker;
+VOID CleanupTransferPacketToWorkingSetSize(_In_ PDEVICE_OBJECT Fdo, _In_ BOOLEAN LimitNumPktToDelete, _In_ ULONG Node);
+
+_IRQL_requires_max_(APC_LEVEL)
+_IRQL_requires_min_(PASSIVE_LEVEL)
+_IRQL_requires_same_
+VOID
+ClasspSetupPopulateTokenTransferPacket(
+ _In_ __drv_aliasesMem POFFLOAD_READ_CONTEXT OffloadReadContext,
+ _In_ PTRANSFER_PACKET Pkt,
+ _In_ ULONG Length,
+ _In_reads_bytes_(Length) PUCHAR PopulateTokenBuffer,
+ _In_ PIRP OriginalIrp,
+ _In_ ULONG ListIdentifier
+ );
+
+_IRQL_requires_max_(APC_LEVEL)
+_IRQL_requires_min_(PASSIVE_LEVEL)
+_IRQL_requires_same_
+VOID
+ClasspSetupReceivePopulateTokenInformationTransferPacket(
+ _In_ POFFLOAD_READ_CONTEXT OffloadReadContext,
+ _In_ PTRANSFER_PACKET Pkt,
+ _In_ ULONG Length,
+ _In_reads_bytes_(Length) PUCHAR ReceivePopulateTokenInformationBuffer,
+ _In_ PIRP OriginalIrp,
+ _In_ ULONG ListIdentifier
+ );
+
+_IRQL_requires_max_(APC_LEVEL)
+_IRQL_requires_min_(PASSIVE_LEVEL)
+_IRQL_requires_same_
+VOID
+ClasspSetupWriteUsingTokenTransferPacket(
+ _In_ __drv_aliasesMem POFFLOAD_WRITE_CONTEXT OffloadWriteContext,
+ _In_ PTRANSFER_PACKET Pkt,
+ _In_ ULONG Length,
+ _In_reads_bytes_(Length) PUCHAR WriteUsingTokenBuffer,
+ _In_ PIRP OriginalIrp,
+ _In_ ULONG ListIdentifier
+ );
+
+_IRQL_requires_max_(APC_LEVEL)
+_IRQL_requires_min_(PASSIVE_LEVEL)
+_IRQL_requires_same_
+VOID
+ClasspSetupReceiveWriteUsingTokenInformationTransferPacket(
+ _In_ POFFLOAD_WRITE_CONTEXT OffloadWriteContext,
+ _In_ PTRANSFER_PACKET Pkt,
+ _In_ ULONG Length,
+ _In_reads_bytes_(Length) PUCHAR ReceiveWriteUsingTokenInformationBuffer,
+ _In_ PIRP OriginalIrp,
+ _In_ ULONG ListIdentifier
+ );
+
+ULONG ClasspModeSense(
+ _In_ PDEVICE_OBJECT Fdo,
+ _In_reads_bytes_(Length) PCHAR ModeSenseBuffer,
+ _In_ ULONG Length,
+ _In_ UCHAR PageMode,
+ _In_ UCHAR PageControl
+ );
+
+NTSTATUS
+ClasspModeSelect(
+ _In_ PDEVICE_OBJECT Fdo,
+ _In_reads_bytes_(Length) PCHAR ModeSelectBuffer,
+ _In_ ULONG Length,
+ _In_ BOOLEAN SavePages
+ );
+
+NTSTATUS ClasspWriteCacheProperty(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _In_ PIRP Irp,
+ _Inout_ PSCSI_REQUEST_BLOCK Srb
+ );
+
+NTSTATUS ClasspAccessAlignmentProperty(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _In_ PIRP Irp,
+ _Inout_ PSCSI_REQUEST_BLOCK Srb
+ );
+
+NTSTATUS ClasspDeviceSeekPenaltyProperty(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _In_ PIRP Irp,
+ _Inout_ PSCSI_REQUEST_BLOCK Srb
+ );
+
+NTSTATUS ClasspDeviceGetLBProvisioningVPDPage(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _Inout_opt_ PSCSI_REQUEST_BLOCK Srb
+ );
+
+NTSTATUS ClasspDeviceGetBlockDeviceCharacteristicsVPDPage(
+ _In_ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension,
+ _In_ PSCSI_REQUEST_BLOCK Srb
+ );
+
+NTSTATUS ClasspDeviceGetBlockLimitsVPDPage(
+ _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ _Inout_bytecount_(SrbSize) PSCSI_REQUEST_BLOCK Srb,
+ _In_ ULONG SrbSize,
+ _Out_ PCLASS_VPD_B0_DATA BlockLimitsData
+ );
+
+NTSTATUS ClasspDeviceTrimProperty(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _In_ PIRP Irp,
+ _Inout_ PSCSI_REQUEST_BLOCK Srb
+ );
+
+NTSTATUS ClasspDeviceLBProvisioningProperty(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _Inout_ PIRP Irp,
+ _Inout_ PSCSI_REQUEST_BLOCK Srb
+ );
+
+NTSTATUS ClasspDeviceTrimProcess(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _In_ PIRP Irp,
+ _Inout_ PSCSI_REQUEST_BLOCK Srb
+ );
+
+NTSTATUS ClasspDeviceGetLBAStatus(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _Inout_ PIRP Irp,
+ _Inout_ PSCSI_REQUEST_BLOCK Srb
+ );
+
+NTSTATUS ClasspDeviceGetLBAStatusWorker(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _In_ PCLASS_VPD_B0_DATA BlockLimitsData,
+ _In_ ULONGLONG StartingOffset,
+ _In_ ULONGLONG LengthInBytes,
+ _Out_ PDEVICE_MANAGE_DATA_SET_ATTRIBUTES_OUTPUT DsmOutput,
+ _Inout_ PULONG DsmOutputLength,
+ _Inout_ PSCSI_REQUEST_BLOCK Srb,
+ _In_ BOOLEAN ConsolidateableBlocksOnly,
+ _In_ ULONG OutputVersion,
+ _Out_ PBOOLEAN BlockLimitsDataMayHaveChanged
+ );
+
+VOID ClassQueueThresholdEventWorker(
+ _In_ PDEVICE_OBJECT DeviceObject
+ );
+
+VOID ClassQueueResourceExhaustionEventWorker(
+ _In_ PDEVICE_OBJECT DeviceObject
+ );
+
+VOID ClassQueueCapacityChangedEventWorker(
+ _In_ PDEVICE_OBJECT DeviceObject
+ );
+
+VOID ClassQueueProvisioningTypeChangedEventWorker(
+ _In_ PDEVICE_OBJECT DeviceObject
+ );
+
+IO_WORKITEM_ROUTINE ClasspLogIOEventWithContext;
+
+VOID
+ClasspQueueLogIOEventWithContextWorker(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _In_ ULONG SenseBufferSize,
+ _In_ PVOID SenseData,
+ _In_ UCHAR SrbStatus,
+ _In_ UCHAR ScsiStatus,
+ _In_ ULONG ErrorCode,
+ _In_ ULONG CdbLength,
+ _In_opt_ PCDB Cdb,
+ _In_opt_ PTRANSFER_PACKET Pkt
+ );
+
+VOID
+ClasspZeroQERR(
+ _In_ PDEVICE_OBJECT DeviceObject
+ );
+
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+ClasspGetMaximumTokenListIdentifier(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _In_z_ PWSTR RegistryPath,
+ _Out_ PULONG MaximumListIdentifier
+ );
+
+_IRQL_requires_max_(APC_LEVEL)
+_IRQL_requires_min_(PASSIVE_LEVEL)
+_IRQL_requires_same_
+NTSTATUS
+ClasspDeviceCopyOffloadProperty(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _Inout_ PIRP Irp,
+ _Inout_ PSCSI_REQUEST_BLOCK Srb
+ );
+
+_IRQL_requires_max_(APC_LEVEL)
+_IRQL_requires_min_(PASSIVE_LEVEL)
+_IRQL_requires_same_
+NTSTATUS
+ClasspValidateOffloadSupported(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _In_ PIRP Irp
+ );
+
+_IRQL_requires_max_(APC_LEVEL)
+_IRQL_requires_min_(PASSIVE_LEVEL)
+_IRQL_requires_same_
+NTSTATUS
+ClasspValidateOffloadInputParameters(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _In_ PIRP Irp
+ );
+
+_IRQL_requires_same_
+NTSTATUS
+ClasspGetTokenOperationCommandBufferLength(
+ _In_ PDEVICE_OBJECT Fdo,
+ _In_ ULONG ServiceAction,
+ _Inout_ PULONG CommandBufferLength,
+ _Out_opt_ PULONG TokenOperationBufferLength,
+ _Out_opt_ PULONG ReceiveTokenInformationBufferLength
+ );
+
+_IRQL_requires_same_
+NTSTATUS
+ClasspGetTokenOperationDescriptorLimits(
+ _In_ PDEVICE_OBJECT Fdo,
+ _In_ ULONG ServiceAction,
+ _In_ ULONG MaxParameterBufferLength,
+ _Out_ PULONG MaxBlockDescriptorsCount,
+ _Out_ PULONGLONG MaxBlockDescriptorsLength
+ );
+
+_IRQL_requires_max_(APC_LEVEL)
+_IRQL_requires_min_(PASSIVE_LEVEL)
+_IRQL_requires_same_
+VOID
+ClasspConvertDataSetRangeToBlockDescr(
+ _In_ PDEVICE_OBJECT Fdo,
+ _In_ PVOID BlockDescr,
+ _Inout_ PULONG CurrentBlockDescrIndex,
+ _In_ ULONG MaxBlockDescrCount,
+ _Inout_ PULONG CurrentLbaCount,
+ _In_ ULONGLONG MaxLbaCount,
+ _Inout_ PDEVICE_DATA_SET_RANGE DataSetRange,
+ _Inout_ PULONGLONG TotalSectorsProcessed
+ );
+
+NTSTATUS
+ClasspDeviceMediaTypeProperty(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _Inout_ PIRP Irp,
+ _Inout_ PSCSI_REQUEST_BLOCK Srb
+ );
+
+_IRQL_requires_same_
+PUCHAR
+ClasspBinaryToAscii(
+ _In_reads_(Length) PUCHAR HexBuffer,
+ _In_ ULONG Length,
+ _Inout_ PULONG UpdateLength
+ );
+
+__inline
+BOOLEAN
+ClasspIsTokenOperationComplete(
+ _In_ ULONG CurrentStatus
+ )
+{
+ BOOLEAN operationCompleted = FALSE;
+
+ switch (CurrentStatus) {
+ case OPERATION_COMPLETED_WITH_SUCCESS:
+ case OPERATION_COMPLETED_WITH_ERROR:
+ case OPERATION_COMPLETED_WITH_RESIDUAL_DATA:
+ case OPERATION_TERMINATED: {
+
+ operationCompleted = TRUE;
+ }
+ }
+
+ return operationCompleted;
+}
+
+__inline
+BOOLEAN
+ClasspIsTokenOperation(
+ _In_ PCDB Cdb
+ )
+{
+ BOOLEAN tokenOperation = FALSE;
+
+ if (Cdb) {
+ ULONG opCode = Cdb->AsByte[0];
+ ULONG serviceAction = Cdb->AsByte[1];
+
+ if ((opCode == SCSIOP_POPULATE_TOKEN && serviceAction == SERVICE_ACTION_POPULATE_TOKEN) ||
+ (opCode == SCSIOP_WRITE_USING_TOKEN && serviceAction == SERVICE_ACTION_WRITE_USING_TOKEN)) {
+
+ tokenOperation = TRUE;
+ }
+ }
+
+ return tokenOperation;
+}
+
+__inline
+BOOLEAN
+ClasspIsReceiveTokenInformation(
+ _In_ PCDB Cdb
+ )
+{
+ BOOLEAN receiveTokenInformation = FALSE;
+
+ if (Cdb) {
+ ULONG opCode = Cdb->AsByte[0];
+ ULONG serviceAction = Cdb->AsByte[1];
+
+ if (opCode == SCSIOP_RECEIVE_ROD_TOKEN_INFORMATION && serviceAction == SERVICE_ACTION_RECEIVE_TOKEN_INFORMATION) {
+
+ receiveTokenInformation = TRUE;
+ }
+ }
+
+ return receiveTokenInformation;
+}
+
+__inline
+BOOLEAN
+ClasspIsOffloadDataTransferCommand(
+ _In_ PCDB Cdb
+ )
+{
+ BOOLEAN offloadCommand = (ClasspIsTokenOperation(Cdb) || ClasspIsReceiveTokenInformation(Cdb)) ? TRUE : FALSE;
+
+ return offloadCommand;
+}
+
+extern LIST_ENTRY AllFdosList;
+
+
+VOID
+ClasspInitializeIdleTimer(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ );
+
+NTSTATUS
+ClasspIsPortable(
+ _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ _Out_ PBOOLEAN IsPortable
+ );
+
+VOID
+ClasspGetInquiryVpdSupportInfo(
+ _Inout_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ );
+
+NTSTATUS
+ClasspGetLBProvisioningInfo(
+ _Inout_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ );
+
+
+_IRQL_requires_(PASSIVE_LEVEL)
+_IRQL_requires_same_
+NTSTATUS
+ClassDetermineTokenOperationCommandSupport(
+ _In_ PDEVICE_OBJECT DeviceObject
+ );
+
+_IRQL_requires_same_
+NTSTATUS
+ClasspGetBlockDeviceTokenLimitsInfo(
+ _Inout_ PDEVICE_OBJECT DeviceObject
+ );
+
+_IRQL_requires_max_(APC_LEVEL)
+_IRQL_requires_min_(PASSIVE_LEVEL)
+_IRQL_requires_same_
+NTSTATUS
+ClassDeviceProcessOffloadRead(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _In_ PIRP Irp,
+ _Inout_ PSCSI_REQUEST_BLOCK Srb
+ );
+
+_IRQL_requires_max_(APC_LEVEL)
+_IRQL_requires_min_(PASSIVE_LEVEL)
+_IRQL_requires_same_
+NTSTATUS
+ClassDeviceProcessOffloadWrite(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _In_ PIRP Irp,
+ _Inout_ PSCSI_REQUEST_BLOCK Srb
+ );
+
+_IRQL_requires_max_(APC_LEVEL)
+_IRQL_requires_min_(PASSIVE_LEVEL)
+_IRQL_requires_same_
+NTSTATUS
+ClasspServicePopulateTokenTransferRequest(
+ _In_ PDEVICE_OBJECT Fdo,
+ _In_ PIRP Irp
+ );
+
+_IRQL_requires_same_
+VOID
+ClasspReceivePopulateTokenInformation(
+ _In_ POFFLOAD_READ_CONTEXT OffloadReadContext
+ );
+
+_IRQL_requires_max_(APC_LEVEL)
+_IRQL_requires_min_(PASSIVE_LEVEL)
+_IRQL_requires_same_
+NTSTATUS
+ClasspServiceWriteUsingTokenTransferRequest(
+ _In_ PDEVICE_OBJECT Fdo,
+ _In_ PIRP Irp
+ );
+
+_IRQL_requires_same_
+VOID
+ClasspReceiveWriteUsingTokenInformation(
+ _In_ POFFLOAD_WRITE_CONTEXT OffloadWriteContext
+ );
+
+VOID
+ClasspCompleteOffloadRequest(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _In_ PIRP Irp,
+ _In_ NTSTATUS CompletionStatus
+ );
+
+VOID
+ClasspCleanupOffloadReadContext(
+ _In_ __drv_freesMem(mem) POFFLOAD_READ_CONTEXT OffloadReadContext
+ );
+
+VOID
+ClasspCompleteOffloadRead(
+ _In_ POFFLOAD_READ_CONTEXT OffloadReadContext,
+ _In_ NTSTATUS CompletionStatus
+ );
+
+// PCONTINUATION_ROUTINE
+VOID
+ClasspPopulateTokenTransferPacketDone(
+ _In_ PVOID Context
+ );
+
+// PCONTINUATION_ROUTINE
+VOID
+ClasspReceivePopulateTokenInformationTransferPacketDone(
+ _In_ PVOID Context
+ );
+
+VOID
+ClasspContinueOffloadWrite(
+ _In_ __drv_aliasesMem POFFLOAD_WRITE_CONTEXT OffloadWriteContext
+ );
+
+VOID
+ClasspCleanupOffloadWriteContext(
+ _In_ __drv_freesMem(mem) POFFLOAD_WRITE_CONTEXT OffloadWriteContext
+ );
+
+VOID
+ClasspCompleteOffloadWrite(
+ _In_ __drv_freesMem(Mem) POFFLOAD_WRITE_CONTEXT OffloadWriteContext,
+ _In_ NTSTATUS CompletionCausingStatus
+ );
+
+VOID
+ClasspReceiveWriteUsingTokenInformationDone(
+ _In_ POFFLOAD_WRITE_CONTEXT OffloadWriteContext,
+ _In_ NTSTATUS CompletionCausingStatus
+ );
+
+VOID
+ClasspWriteUsingTokenTransferPacketDone(
+ _In_ PVOID Context
+ );
+
+VOID
+ClasspReceiveWriteUsingTokenInformationTransferPacketDone(
+ _In_ POFFLOAD_WRITE_CONTEXT OffloadWriteContext
+ );
+
+NTSTATUS
+ClasspRefreshFunctionSupportInfo(
+ _Inout_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ _In_ BOOLEAN ForceQuery
+ );
+
+NTSTATUS
+ClasspBlockLimitsDataSnapshot(
+ _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ _In_ BOOLEAN ForceQuery,
+ _Out_ PCLASS_VPD_B0_DATA BlockLimitsData,
+ _Out_ PULONG GenerationCount
+ );
+
+NTSTATUS
+InterpretReadCapacity16Data (
+ _Inout_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ _In_ PREAD_CAPACITY16_DATA ReadCapacity16Data
+ );
+
+NTSTATUS
+ClassReadCapacity16 (
+ _Inout_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ _Inout_ PSCSI_REQUEST_BLOCK Srb
+ );
+
+NTSTATUS
+ClassDeviceGetLBProvisioningResources(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _Inout_ PIRP Irp,
+ _Inout_ PSCSI_REQUEST_BLOCK Srb
+ );
+
+_IRQL_requires_same_
+NTSTATUS
+ClasspStorageEventNotification(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _In_ PIRP Irp
+ );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+ClasspPowerActivateDevice(
+ _In_ PDEVICE_OBJECT DeviceObject
+ );
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+ClasspPowerIdleDevice(
+ _In_ PDEVICE_OBJECT DeviceObject
+ );
+
+IO_WORKITEM_ROUTINE ClassLogThresholdEvent;
+
+NTSTATUS
+ClasspLogSystemEventWithDeviceNumber(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _In_ NTSTATUS IoErrorCode
+ );
+
+IO_WORKITEM_ROUTINE ClassLogResourceExhaustionEvent;
+
+NTSTATUS
+ClasspEnqueueIdleRequest(
+ PDEVICE_OBJECT DeviceObject,
+ PIRP Irp
+ );
+
+VOID
+ClasspCompleteIdleRequest(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ );
+
+NTSTATUS
+ClasspPriorityHint(
+ PDEVICE_OBJECT DeviceObject,
+ PIRP Irp
+ );
+
+VOID
+HistoryInitializeRetryLogs(
+ _Out_ PSRB_HISTORY History,
+ ULONG HistoryCount
+ );
+#define HISTORYINITIALIZERETRYLOGS(_packet) \
+ { \
+ if (_packet->RetryHistory != NULL) \
+ { \
+ HistoryInitializeRetryLogs( \
+ _packet->RetryHistory, \
+ _packet->RetryHistory->TotalHistoryCount \
+ ); \
+ } \
+ }
+
+VOID
+HistoryLogSendPacket(
+ TRANSFER_PACKET *Pkt
+ );
+#define HISTORYLOGSENDPACKET(_packet) \
+ { \
+ if (_packet->RetryHistory != NULL) { \
+ HistoryLogSendPacket(_packet); \
+ } \
+ }
+
+VOID
+HistoryLogReturnedPacket(
+ TRANSFER_PACKET *Pkt
+ );
+
+#define HISTORYLOGRETURNEDPACKET(_packet) \
+ { \
+ if (_packet->RetryHistory != NULL) { \
+ HistoryLogReturnedPacket(_packet); \
+ } \
+ }
+
+BOOLEAN
+InterpretSenseInfoWithoutHistory(
+ _In_ PDEVICE_OBJECT Fdo,
+ _In_opt_ PIRP OriginalRequest,
+ _In_ PSCSI_REQUEST_BLOCK Srb,
+ UCHAR MajorFunctionCode,
+ ULONG IoDeviceCode,
+ ULONG PreviousRetryCount,
+ _Out_ NTSTATUS * Status,
+ _Out_opt_ _Deref_out_range_(0,MAXIMUM_RETRY_FOR_SINGLE_IO_IN_100NS_UNITS)
+ LONGLONG * RetryIn100nsUnits
+ );
+
+BOOLEAN
+ClasspMyStringMatches(
+ _In_opt_z_ PCHAR StringToMatch,
+ _In_z_ PCHAR TargetString
+ );
+
+
+
+#define TRACKING_FORWARD_PROGRESS_PATH1 (0x00000001)
+#define TRACKING_FORWARD_PROGRESS_PATH2 (0x00000002)
+#define TRACKING_FORWARD_PROGRESS_PATH3 (0x00000004)
+
+
+VOID
+ClasspInitializeRemoveTracking(
+ _In_ PDEVICE_OBJECT DeviceObject
+ );
+
+VOID
+ClasspUninitializeRemoveTracking(
+ _In_ PDEVICE_OBJECT DeviceObject
+ );
+
+RTL_GENERIC_COMPARE_ROUTINE RemoveTrackingCompareRoutine;
+
+RTL_GENERIC_ALLOCATE_ROUTINE RemoveTrackingAllocateRoutine;
+
+RTL_GENERIC_FREE_ROUTINE RemoveTrackingFreeRoutine;
+
+#if (NTDDI_VERSION >= NTDDI_WIN8)
+
+typedef PVOID
+(*PSRB_ALLOCATE_ROUTINE) (
+ _In_ CLONG ByteSize
+ );
+
+PVOID
+DefaultStorageRequestBlockAllocateRoutine(
+ _In_ CLONG ByteSize
+ );
+
+
+NTSTATUS
+CreateStorageRequestBlock(
+ _Inout_ PSTORAGE_REQUEST_BLOCK *Srb,
+ _In_ USHORT AddressType,
+ _In_opt_ PSRB_ALLOCATE_ROUTINE AllocateRoutine,
+ _Inout_opt_ ULONG *ByteSize,
+ _In_ ULONG NumSrbExData,
+ ...
+ );
+
+NTSTATUS
+InitializeStorageRequestBlock(
+ _Inout_bytecount_(ByteSize) PSTORAGE_REQUEST_BLOCK Srb,
+ _In_ USHORT AddressType,
+ _In_ ULONG ByteSize,
+ _In_ ULONG NumSrbExData,
+ ...
+ );
+
+VOID
+ClasspConvertToScsiRequestBlock(
+ _Out_ PSCSI_REQUEST_BLOCK Srb,
+ _In_ PSTORAGE_REQUEST_BLOCK SrbEx
+ );
+
+__inline PCDB
+ClasspTransferPacketGetCdb(
+ _In_ PTRANSFER_PACKET Pkt
+ )
+{
+ return SrbGetCdb(Pkt->Srb);
+}
+
+//
+// This inline function calculates number of retries already happened till now for known operation codes
+// and set the out parameter - TimesAlreadyRetried with the value, returns True
+//
+// For unknown operation codes this function will return false and will set TimesAlreadyRetried with zero
+//
+__inline BOOLEAN
+ClasspTransferPacketGetNumberOfRetriesDone(
+ _In_ PTRANSFER_PACKET Pkt,
+ _In_ PCDB Cdb,
+ _Out_ PULONG TimesAlreadyRetried
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Pkt->Fdo->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
+
+ if (Cdb->MEDIA_REMOVAL.OperationCode == SCSIOP_MEDIUM_REMOVAL)
+ {
+ *TimesAlreadyRetried = NUM_LOCKMEDIAREMOVAL_RETRIES - Pkt->NumRetries;
+ }
+ else if ((Cdb->MODE_SENSE.OperationCode == SCSIOP_MODE_SENSE) ||
+ (Cdb->MODE_SENSE.OperationCode == SCSIOP_MODE_SENSE10))
+ {
+ *TimesAlreadyRetried = NUM_MODESENSE_RETRIES - Pkt->NumRetries;
+ }
+ else if ((Cdb->CDB10.OperationCode == SCSIOP_READ_CAPACITY) ||
+ (Cdb->CDB16.OperationCode == SCSIOP_READ_CAPACITY16))
+ {
+ *TimesAlreadyRetried = NUM_DRIVECAPACITY_RETRIES - Pkt->NumRetries;
+ }
+ else if (IS_SCSIOP_READWRITE(Cdb->CDB10.OperationCode))
+ {
+ *TimesAlreadyRetried = fdoData->MaxNumberOfIoRetries - Pkt->NumRetries;
+ }
+ else if (Cdb->TOKEN_OPERATION.OperationCode == SCSIOP_POPULATE_TOKEN &&
+ Cdb->TOKEN_OPERATION.ServiceAction == SERVICE_ACTION_POPULATE_TOKEN)
+ {
+ *TimesAlreadyRetried = NUM_POPULATE_TOKEN_RETRIES - Pkt->NumRetries;
+ }
+ else if (Cdb->TOKEN_OPERATION.OperationCode == SCSIOP_WRITE_USING_TOKEN &&
+ Cdb->TOKEN_OPERATION.ServiceAction == SERVICE_ACTION_WRITE_USING_TOKEN)
+ {
+ *TimesAlreadyRetried = NUM_WRITE_USING_TOKEN_RETRIES - Pkt->NumRetries;
+ }
+ else if (ClasspIsReceiveTokenInformation(Cdb))
+ {
+ *TimesAlreadyRetried = NUM_RECEIVE_TOKEN_INFORMATION_RETRIES - Pkt->NumRetries;
+ }
+
+ else
+ {
+ *TimesAlreadyRetried = 0;
+ return FALSE;
+ }
+
+
+ return TRUE;
+}
+
+
+__inline PVOID
+ClasspTransferPacketGetSenseInfoBuffer(
+ _In_ PTRANSFER_PACKET Pkt
+ )
+{
+ return SrbGetSenseInfoBuffer(Pkt->Srb);
+}
+
+__inline UCHAR
+ClasspTransferPacketGetSenseInfoBufferLength(
+ _In_ PTRANSFER_PACKET Pkt
+ )
+{
+ return SrbGetSenseInfoBufferLength(Pkt->Srb);
+}
+
+
+__inline VOID
+ClasspSrbSetOriginalIrp(
+ _In_ PSTORAGE_REQUEST_BLOCK_HEADER Srb,
+ _In_ PIRP Irp
+ )
+{
+ if (Srb->Function == SRB_FUNCTION_STORAGE_REQUEST_BLOCK)
+ {
+ ((PSTORAGE_REQUEST_BLOCK)Srb)->MiniportContext = (PVOID)Irp;
+ }
+ else
+ {
+ ((PSCSI_REQUEST_BLOCK)Srb)->SrbExtension = (PVOID)Irp;
+ }
+}
+
+__inline
+BOOLEAN
+PORT_ALLOCATED_SENSE_EX(
+ _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ _In_ PSTORAGE_REQUEST_BLOCK_HEADER Srb
+ )
+{
+ return ((BOOLEAN)((TEST_FLAG(SrbGetSrbFlags(Srb), SRB_FLAGS_PORT_DRIVER_ALLOCSENSE) &&
+ TEST_FLAG(SrbGetSrbFlags(Srb), SRB_FLAGS_FREE_SENSE_BUFFER)) &&
+ (SrbGetSenseInfoBuffer(Srb) != FdoExtension->SenseData))
+ );
+}
+
+__inline
+VOID
+FREE_PORT_ALLOCATED_SENSE_BUFFER_EX(
+ _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ _In_ PSTORAGE_REQUEST_BLOCK_HEADER Srb
+ )
+{
+ NT_ASSERT(TEST_FLAG(SrbGetSrbFlags(Srb), SRB_FLAGS_PORT_DRIVER_ALLOCSENSE));
+ NT_ASSERT(TEST_FLAG(SrbGetSrbFlags(Srb), SRB_FLAGS_FREE_SENSE_BUFFER));
+ NT_ASSERT(SrbGetSenseInfoBuffer(Srb) != FdoExtension->SenseData);
+
+ ExFreePool(SrbGetSenseInfoBuffer(Srb));
+ SrbSetSenseInfoBuffer(Srb, FdoExtension->SenseData);
+ SrbSetSenseInfoBufferLength(Srb, GET_FDO_EXTENSON_SENSE_DATA_LENGTH(FdoExtension));
+ SrbClearSrbFlags(Srb, SRB_FLAGS_FREE_SENSE_BUFFER);
+ return;
+}
+
+#endif //NTDDI_WIN8
+
+BOOLEAN
+ClasspFailurePredictionPeriodMissed(
+ _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ );
+
+__inline
+ULONG
+ClasspGetMaxUsableBufferLengthFromOffset(
+ _In_ PVOID BaseAddress,
+ _In_ ULONG OffsetInBytes,
+ _In_ ULONG BaseStructureSizeInBytes
+ )
+/*++
+
+Routine Description:
+
+ This routine returns the maximum size of a buffer that starts at a given offset,
+ based on the size of the containing structure.
+
+Arguments:
+
+ BaseAddress - The base address of the structure. The offset is computed relative to this.
+
+ OffsetInBytes - (BaseAddress + OffsetInBytes) points to the beginning of the buffer.
+
+ BaseStructureSizeInBytes - The size of the structure which contains the buffer.
+
+Return Value:
+
+ max(BaseStructureSizeInBytes - OffsetInBytes, 0). If any operations wrap around,
+ the return value is 0.
+
+--*/
+
+{
+
+ ULONG_PTR offsetAddress = ((ULONG_PTR)BaseAddress + OffsetInBytes);
+
+ if (offsetAddress < (ULONG_PTR)BaseAddress) {
+ //
+ // This means BaseAddress + OffsetInBytes > ULONG_PTR_MAX.
+ //
+ return 0;
+ }
+
+ if (OffsetInBytes > BaseStructureSizeInBytes) {
+ return 0;
+ }
+
+ return BaseStructureSizeInBytes - OffsetInBytes;
+}
+
+
+__inline
+BOOLEAN
+ClasspLowerLayerNotSupport (
+ _In_ NTSTATUS Status
+ )
+{
+ return ((Status == STATUS_NOT_SUPPORTED) ||
+ (Status == STATUS_NOT_IMPLEMENTED) ||
+ (Status == STATUS_INVALID_DEVICE_REQUEST) ||
+ (Status == STATUS_INVALID_PARAMETER_1));
+}
+
+NTSTATUS
+ClassDeviceHwFirmwareGetInfoProcess(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _Inout_ PIRP Irp
+ );
+
+NTSTATUS
+ClassDeviceHwFirmwareDownloadProcess(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _Inout_ PIRP Irp,
+ _Inout_ PSCSI_REQUEST_BLOCK Srb
+ );
+
+NTSTATUS
+ClassDeviceHwFirmwareActivateProcess(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _Inout_ PIRP Irp,
+ _Inout_ PSCSI_REQUEST_BLOCK Srb
+ );
+
diff --git a/storage/class/classpnp/src/classpnp.htm b/storage/class/classpnp/src/classpnp.htm
new file mode 100644
index 00000000..4c2b159e
--- /dev/null
+++ b/storage/class/classpnp/src/classpnp.htm
@@ -0,0 +1,273 @@
+<html xmlns:v="urn:schemas-microsoft-com:vml"
+xmlns:o="urn:schemas-microsoft-com:office:office"
+xmlns:w="urn:schemas-microsoft-com:office:word"
+xmlns="http://www.w3.org/TR/REC-html40">
+
+<head>
+<meta http-equiv=Content-Type content="text/html; charset=windows-1252">
+<meta name=ProgId content=Word.Document>
+<meta name=Generator content="Microsoft Word 11">
+<meta name=Originator content="Microsoft Word 11">
+<link rel=File-List href="classpnp_files/filelist.xml">
+<title>ClassPnP</title>
+<!--[if gte mso 9]><xml>
+ <w:WordDocument>
+ <w:Zoom>BestFit</w:Zoom>
+ <w:SpellingState>Clean</w:SpellingState>
+ <w:GrammarState>Clean</w:GrammarState>
+ <w:ValidateAgainstSchemas/>
+ <w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid>
+ <w:IgnoreMixedContent>false</w:IgnoreMixedContent>
+ <w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText>
+ <w:BrowserLevel>MicrosoftInternetExplorer4</w:BrowserLevel>
+ </w:WordDocument>
+</xml><![endif]--><!--[if gte mso 9]><xml>
+ <w:LatentStyles DefLockedState="false" LatentStyleCount="156">
+ </w:LatentStyles>
+</xml><![endif]-->
+<style>
+<!--
+ /* Font Definitions */
+ @font-face
+ {font-family:"MS Mincho";
+ panose-1:2 2 6 9 4 2 5 8 3 4;
+ mso-font-alt:"\FF2D\FF33 \660E\671D";
+ mso-font-charset:128;
+ mso-generic-font-family:roman;
+ mso-font-format:other;
+ mso-font-pitch:fixed;
+ mso-font-signature:1 134676480 16 0 131072 0;}
+@font-face
+ {font-family:Verdana;
+ panose-1:2 11 6 4 3 5 4 4 2 4;
+ mso-font-alt:Tahoma;
+ mso-font-charset:0;
+ mso-generic-font-family:swiss;
+ mso-font-pitch:variable;
+ mso-font-signature:536871559 0 0 0 415 0;}
+@font-face
+ {font-family:"MS Sans Serif";
+ panose-1:0 0 0 0 0 0 0 0 0 0;
+ mso-font-alt:"Times New Roman";
+ mso-font-charset:0;
+ mso-generic-font-family:roman;
+ mso-font-format:other;
+ mso-font-pitch:auto;
+ mso-font-signature:0 0 0 0 0 0;}
+@font-face
+ {font-family:"\@MS Mincho";
+ mso-font-charset:128;
+ mso-generic-font-family:modern;
+ mso-font-pitch:fixed;
+ mso-font-signature:-1610612033 1757936891 16 0 131231 0;}
+ /* Style Definitions */
+ p.MsoNormal, li.MsoNormal, div.MsoNormal
+ {mso-style-parent:"";
+ margin:0in;
+ margin-bottom:.0001pt;
+ mso-pagination:widow-orphan;
+ font-size:12.0pt;
+ font-family:"Times New Roman";
+ mso-fareast-font-family:"MS Mincho";
+ color:black;}
+h2
+ {mso-margin-top-alt:auto;
+ margin-right:0in;
+ mso-margin-bottom-alt:auto;
+ margin-left:0in;
+ mso-pagination:widow-orphan;
+ mso-outline-level:2;
+ font-size:18.0pt;
+ font-family:"Times New Roman";
+ mso-fareast-font-family:"Times New Roman";
+ color:black;
+ font-weight:bold;}
+h3
+ {mso-margin-top-alt:auto;
+ margin-right:0in;
+ mso-margin-bottom-alt:auto;
+ margin-left:0in;
+ mso-pagination:widow-orphan;
+ mso-outline-level:3;
+ font-size:13.5pt;
+ font-family:"Times New Roman";
+ mso-fareast-font-family:"Times New Roman";
+ color:black;
+ font-weight:bold;}
+h4
+ {mso-margin-top-alt:auto;
+ margin-right:0in;
+ mso-margin-bottom-alt:auto;
+ margin-left:0in;
+ mso-pagination:widow-orphan;
+ mso-outline-level:4;
+ font-size:12.0pt;
+ font-family:"Times New Roman";
+ mso-fareast-font-family:"Times New Roman";
+ color:black;
+ font-weight:bold;}
+a:link, span.MsoHyperlink
+ {color:blue;
+ text-decoration:underline;
+ text-underline:single;}
+a:visited, span.MsoHyperlinkFollowed
+ {color:purple;
+ text-decoration:underline;
+ text-underline:single;}
+p
+ {mso-margin-top-alt:auto;
+ margin-right:0in;
+ mso-margin-bottom-alt:auto;
+ margin-left:0in;
+ mso-pagination:widow-orphan;
+ font-size:12.0pt;
+ font-family:"Times New Roman";
+ mso-fareast-font-family:"MS Mincho";
+ color:black;}
+pre
+ {margin:0in;
+ margin-bottom:.0001pt;
+ mso-pagination:widow-orphan;
+ tab-stops:45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt;
+ font-size:10.0pt;
+ font-family:"Courier New";
+ mso-fareast-font-family:"MS Mincho";
+ color:black;}
+span.SpellE
+ {mso-style-name:"";
+ mso-spl-e:yes;}
+span.GramE
+ {mso-style-name:"";
+ mso-gram-e:yes;}
+@page Section1
+ {size:8.5in 11.0in;
+ margin:1.0in 1.25in 1.0in 1.25in;
+ mso-header-margin:.5in;
+ mso-footer-margin:.5in;
+ mso-paper-source:0;}
+div.Section1
+ {page:Section1;}
+-->
+</style>
+<!--[if gte mso 10]>
+<style>
+ /* Style Definitions */
+ table.MsoNormalTable
+ {mso-style-name:"Table Normal";
+ mso-tstyle-rowband-size:0;
+ mso-tstyle-colband-size:0;
+ mso-style-noshow:yes;
+ mso-style-parent:"";
+ mso-padding-alt:0in 5.4pt 0in 5.4pt;
+ mso-para-margin:0in;
+ mso-para-margin-bottom:.0001pt;
+ mso-pagination:widow-orphan;
+ font-size:10.0pt;
+ font-family:"Times New Roman";
+ mso-ansi-language:#0400;
+ mso-fareast-language:#0400;
+ mso-bidi-language:#0400;}
+</style>
+<![endif]-->
+<meta name=Template content="C:\PROGRAM FILES\MICROSOFT OFFICE\OFFICE\html.dot">
+<!--[if gte mso 9]><xml>
+ <o:shapedefaults v:ext="edit" spidmax="3074"/>
+</xml><![endif]--><!--[if gte mso 9]><xml>
+ <o:shapelayout v:ext="edit">
+ <o:idmap v:ext="edit" data="1"/>
+ </o:shapelayout></xml><![endif]-->
+</head>
+
+<body bgcolor=white lang=EN-US link=blue vlink=purple style='tab-interval:.5in'
+leftmargin=8>
+
+<div class=Section1>
+
+<h2><a name=classpnp></a><span class=SpellE><span style='mso-bookmark:classpnp'><span
+style='font-family:Verdana'>ClassPnP</span></span></span><span
+style='mso-bookmark:classpnp'></span><span style='font-family:Verdana'> <o:p></o:p></span></h2>
+
+<h3><span style='font-family:Verdana'>Summary<o:p></o:p></span></h3>
+
+<p><span style='font-size:10.0pt;font-family:Verdana'>This is the library for
+all storage drivers. It simplifies writing a storage driver by implementing 90 percent
+of the code required to support Plug and Play, Power Management, et cetera.
+This library is used by <span class=SpellE>disk.sys</span>, <span class=SpellE>cdrom.sys</span>
+and the tape class drivers.<o:p></o:p></span></p>
+
+<p><span style='font-size:10.0pt;font-family:Verdana'>No INF file is needed to
+install this library. The library is 64-bit compliant.<o:p></o:p></span></p>
+
+<h3><span style='font-family:Verdana'>Building the Sample<o:p></o:p></span></h3>
+
+<p><span style='font-size:10.0pt;font-family:Verdana'>To build the sample, run <b>build</b>.
+Once built, one binary will be created: <span class=SpellE>classpnp.sys</span>.
+This sample is based on live source code, and only builds in the current OS
+build environment.<span style='mso-spacerun:yes'>� </span><o:p></o:p></span></p>
+
+<h3><span style='font-family:Verdana'>CODE TOUR<o:p></o:p></span></h3>
+
+<h4><span style='font-family:Verdana'>File Manifest<o:p></o:p></span></h4>
+
+<pre><u>File<span style='mso-tab-count:3'>������������������ </span>Description<o:p></o:p></u></pre><pre><o:p>&nbsp;</o:p></pre><pre><span
+class=SpellE>Autorun.c</span><span style='mso-tab-count:2'>������������� </span>Media change notification (MCN) code</pre><pre><span
+class=SpellE>Class.c</span><span style='mso-tab-count:2'>������� </span><span
+style='mso-tab-count:1'>������� </span>Main code base</pre><pre><span
+class=SpellE>Class.rc</span><span style='mso-tab-count:2'>�������������� </span>Resource file</pre><pre><span
+class=SpellE>Class.src</span><span style='mso-tab-count:2'>������������� </span>Exports</pre><pre><span
+class=SpellE>Classp.h</span><span style='mso-tab-count:2'>�������������� </span>Private <span
+class=GramE>header</span></pre><pre><span class=SpellE>Classwmi.c</span><span
+style='mso-tab-count:2'>������������ </span>WMI functionality</pre><pre><span
+class=SpellE>Clntirp.c</span><span style='mso-tab-count:2'>������������� </span>Client IRP queuing code</pre><pre><span
+class=SpellE>Create.c</span><span style='mso-tab-count:2'>�������������� </span>Create IRP code</pre><pre><span
+class=SpellE>Data.c</span><span style='mso-tab-count:2'>�������� </span><span
+style='mso-tab-count:1'>������� </span>Static driver data</pre><pre><span
+class=SpellE>Debug.c</span><span style='mso-tab-count:2'>������� </span><span
+style='mso-tab-count:1'>������� </span>Debug code and data</pre><pre><span
+class=SpellE>Debug.h</span><span style='mso-tab-count:2'>������� </span><span
+style='mso-tab-count:1'>������� </span>Debug header file</pre><pre><span
+class=SpellE>Dictlib.c</span><span style='mso-tab-count:2'>������������� </span>File system dictionary code</pre><pre><span
+class=SpellE>Lock.c</span><span style='mso-tab-count:2'>�������� </span><span
+style='mso-tab-count:1'>������� </span>Storage <span class=GramE>remove</span> lock implementation</pre><pre><span
+class=SpellE>Makefile</span><span style='mso-tab-count:2'>�������������� </span><span
+class=SpellE>Makefile</span></pre><pre><span class=SpellE>Obsolete.c</span><span
+style='mso-tab-count:2'>������������ </span>Obsolete code used by legacy drivers</pre><pre><span
+class=SpellE>Power.c</span><span style='mso-tab-count:2'>������� </span><span
+style='mso-tab-count:1'>������� </span>Power code</pre><pre><span class=SpellE>Retry.c</span><span
+style='mso-tab-count:2'>������� </span><span style='mso-tab-count:1'>������� </span>Transfer <span
+class=GramE>packet retry</span> code</pre><pre>Sources<span style='mso-tab-count:
+2'>������� </span><span style='mso-tab-count:1'>������� </span>Sources file</pre><pre><span
+class=SpellE>Utils.c</span><span style='mso-tab-count:2'>������� </span><span
+style='mso-tab-count:1'>������� </span>Utility code</pre><pre><span
+class=SpellE>Xferpkt.c</span><span style='mso-tab-count:2'>������������� </span>Transfer packet processing code</pre><pre><o:p>&nbsp;</o:p></pre><pre><o:p>&nbsp;</o:p></pre>
+
+<p align=center style='text-align:center;tab-stops:45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt'><span
+style='font-size:10.0pt;font-family:"Courier New"'><a href="#top"><span
+style='font-family:Verdana'>Top of page</span></a></span><span
+style='font-size:10.0pt;font-family:Verdana;mso-bidi-font-family:"Courier New"'>
+<o:p></o:p></span></p>
+
+<pre><o:p>&nbsp;</o:p></pre>
+
+<table class=MsoNormalTable border=0 cellspacing=0 cellpadding=0 width=624
+ style='width:6.5in;mso-cellspacing:0in;mso-padding-alt:0in 5.4pt 0in 5.4pt'>
+ <tr style='mso-yfti-irow:0;mso-yfti-firstrow:yes;mso-yfti-lastrow:yes;
+ height:1.5pt'>
+ <td style='background:aqua;padding:.75pt .75pt .75pt .75pt;height:1.5pt'>
+ <p class=MsoNormal><o:p>&nbsp;</o:p></p>
+ </td>
+ </tr>
+</table>
+
+<pre><o:p>&nbsp;</o:p></pre><pre><o:p>&nbsp;</o:p></pre>
+
+<p style='tab-stops:45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt'><span
+style='font-size:7.5pt;font-family:"MS Sans Serif";mso-bidi-font-family:"Courier New"'>�
+2004 Microsoft Corporation</span><span style='font-size:10.0pt;font-family:
+Verdana;mso-bidi-font-family:"Courier New"'> <o:p></o:p></span></p>
+
+</div>
+
+</body>
+
+</html>
diff --git a/storage/class/classpnp/src/classpnp.vcxproj b/storage/class/classpnp/src/classpnp.vcxproj
new file mode 100644
index 00000000..1a18fbc8
--- /dev/null
+++ b/storage/class/classpnp/src/classpnp.vcxproj
@@ -0,0 +1,322 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{6AEA931E-5110-494C-A1DB-9FDE40CE7133}</ProjectGuid>
+ <RootNamespace>$(MSBuildProjectName)</RootNamespace>
+ <SupportsPackaging>false</SupportsPackaging>
+ <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration>
+ <Platform Condition="'$(Platform)' == ''">Win32</Platform>
+ <SampleGuid>{FDD23D45-9A28-4A9B-8C8A-3B388DEAC852}</SampleGuid>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>False</UseDebugLibraries>
+ <DriverTargetPlatform>Universal</DriverTargetPlatform>
+ <DriverType>ExportDriver</DriverType>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>Driver</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>True</UseDebugLibraries>
+ <DriverTargetPlatform>Universal</DriverTargetPlatform>
+ <DriverType>ExportDriver</DriverType>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>Driver</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>False</UseDebugLibraries>
+ <DriverTargetPlatform>Universal</DriverTargetPlatform>
+ <DriverType>ExportDriver</DriverType>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>Driver</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>True</UseDebugLibraries>
+ <DriverTargetPlatform>Universal</DriverTargetPlatform>
+ <DriverType>ExportDriver</DriverType>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>Driver</ConfigurationType>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <PropertyGroup>
+ <OutDir>$(IntDir)</OutDir>
+ </PropertyGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ItemGroup Label="WrappedTaskItems">
+ <ClCompile Include="autorun.c; class.c; classwmi.c; create.c; data.c; dictlib.c; dispatch.c; history.c; lock.c; power.c; xferpkt.c; clntirp.c; retry.c; utils.c; obsolete.c; debug.c; srblib.c">
+ <WppEnabled Condition="'$(UseDebugLibraries)'=='false'">true</WppEnabled>
+ <WppKernelMode Condition="'$(UseDebugLibraries)'=='false'">true</WppKernelMode>
+ <WppTraceFunction Condition="'$(UseDebugLibraries)'=='false'">TracePrint((LEVEL,FLAGS,MSG,...))</WppTraceFunction>
+ </ClCompile>
+ <OtherWpp Include="class.rc; classlog.mof">
+ <WppEnabled Condition="'$(UseDebugLibraries)'=='false'">true</WppEnabled>
+ <WppKernelMode Condition="'$(UseDebugLibraries)'=='false'">true</WppKernelMode>
+ <WppTraceFunction Condition="'$(UseDebugLibraries)'=='false'">TracePrint((LEVEL,FLAGS,MSG,...))</WppTraceFunction>
+ </OtherWpp>
+ <Wmimofck Include=".\$(IntDir)\classlog.bmf">
+ <HeaderOutputFile>.\$(IntDir)\classlog.h</HeaderOutputFile>
+ <HexdumpOutputFile>.\$(IntDir)\classlog.x</HexdumpOutputFile>
+ <GenerateStructureDefinitionsForDatablocks>true</GenerateStructureDefinitionsForDatablocks>
+ </Wmimofck>
+ </ItemGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <TargetName>classpnp</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <TargetName>classpnp</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <TargetName>classpnp</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <TargetName>classpnp</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <WarningLevel>Level4</WarningLevel>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc;..\..\inc</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_BREAK_ON_LOST_IRPS=0</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_SECONDS_TO_WAIT_FOR_SYNCHRONOUS_SRB=100</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_USE_DELAYED_RETRY=1</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_BUFFERED_DEBUG_PRINT=0</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFER_SIZE=512</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFERS=512</PreprocessorDefinitions>
+ </ClCompile>
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc;..\..\inc</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_BREAK_ON_LOST_IRPS=0</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_SECONDS_TO_WAIT_FOR_SYNCHRONOUS_SRB=100</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_USE_DELAYED_RETRY=1</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_BUFFERED_DEBUG_PRINT=0</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFER_SIZE=512</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFERS=512</PreprocessorDefinitions>
+ </ResourceCompile>
+ <Midl>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc;..\..\inc</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_BREAK_ON_LOST_IRPS=0</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_SECONDS_TO_WAIT_FOR_SYNCHRONOUS_SRB=100</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_USE_DELAYED_RETRY=1</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_BUFFERED_DEBUG_PRINT=0</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFER_SIZE=512</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFERS=512</PreprocessorDefinitions>
+ </Midl>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <WarningLevel>Level4</WarningLevel>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc;..\..\inc</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_BREAK_ON_LOST_IRPS=0</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_SECONDS_TO_WAIT_FOR_SYNCHRONOUS_SRB=100</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_USE_DELAYED_RETRY=1</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_BUFFERED_DEBUG_PRINT=0</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFER_SIZE=512</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFERS=512</PreprocessorDefinitions>
+ </ClCompile>
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc;..\..\inc</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_BREAK_ON_LOST_IRPS=0</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_SECONDS_TO_WAIT_FOR_SYNCHRONOUS_SRB=100</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_USE_DELAYED_RETRY=1</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_BUFFERED_DEBUG_PRINT=0</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFER_SIZE=512</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFERS=512</PreprocessorDefinitions>
+ </ResourceCompile>
+ <Midl>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc;..\..\inc</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_BREAK_ON_LOST_IRPS=0</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_SECONDS_TO_WAIT_FOR_SYNCHRONOUS_SRB=100</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_USE_DELAYED_RETRY=1</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_BUFFERED_DEBUG_PRINT=0</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFER_SIZE=512</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFERS=512</PreprocessorDefinitions>
+ </Midl>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <WarningLevel>Level4</WarningLevel>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc;..\..\inc</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_BREAK_ON_LOST_IRPS=0</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_SECONDS_TO_WAIT_FOR_SYNCHRONOUS_SRB=100</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_USE_DELAYED_RETRY=1</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_BUFFERED_DEBUG_PRINT=0</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFER_SIZE=512</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFERS=512</PreprocessorDefinitions>
+ </ClCompile>
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc;..\..\inc</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_BREAK_ON_LOST_IRPS=0</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_SECONDS_TO_WAIT_FOR_SYNCHRONOUS_SRB=100</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_USE_DELAYED_RETRY=1</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_BUFFERED_DEBUG_PRINT=0</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFER_SIZE=512</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFERS=512</PreprocessorDefinitions>
+ </ResourceCompile>
+ <Midl>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc;..\..\inc</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_BREAK_ON_LOST_IRPS=0</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_SECONDS_TO_WAIT_FOR_SYNCHRONOUS_SRB=100</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_USE_DELAYED_RETRY=1</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_BUFFERED_DEBUG_PRINT=0</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFER_SIZE=512</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFERS=512</PreprocessorDefinitions>
+ </Midl>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <WarningLevel>Level4</WarningLevel>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc;..\..\inc</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_BREAK_ON_LOST_IRPS=0</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_SECONDS_TO_WAIT_FOR_SYNCHRONOUS_SRB=100</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_USE_DELAYED_RETRY=1</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_BUFFERED_DEBUG_PRINT=0</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFER_SIZE=512</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFERS=512</PreprocessorDefinitions>
+ </ClCompile>
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc;..\..\inc</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_BREAK_ON_LOST_IRPS=0</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_SECONDS_TO_WAIT_FOR_SYNCHRONOUS_SRB=100</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_USE_DELAYED_RETRY=1</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_BUFFERED_DEBUG_PRINT=0</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFER_SIZE=512</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFERS=512</PreprocessorDefinitions>
+ </ResourceCompile>
+ <Midl>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);..\inc;..\..\inc</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_BREAK_ON_LOST_IRPS=0</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_SECONDS_TO_WAIT_FOR_SYNCHRONOUS_SRB=100</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_USE_DELAYED_RETRY=1</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_BUFFERED_DEBUG_PRINT=0</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFER_SIZE=512</PreprocessorDefinitions>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFERS=512</PreprocessorDefinitions>
+ </Midl>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <PreprocessorDefinitions Condition="!('$(UseDebugLibraries)'=='false')">%(PreprocessorDefinitions);DEBUG_USE_KDPRINT</PreprocessorDefinitions>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ <Midl>
+ <PreprocessorDefinitions Condition="!('$(UseDebugLibraries)'=='false')">%(PreprocessorDefinitions);DEBUG_USE_KDPRINT</PreprocessorDefinitions>
+ </Midl>
+ <ResourceCompile>
+ <PreprocessorDefinitions Condition="!('$(UseDebugLibraries)'=='false')">%(PreprocessorDefinitions);DEBUG_USE_KDPRINT</PreprocessorDefinitions>
+ </ResourceCompile>
+ <Link>
+ <ModuleDefinitionFile>class.def</ModuleDefinitionFile>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PreprocessorDefinitions Condition="!('$(UseDebugLibraries)'=='false')">%(PreprocessorDefinitions);DEBUG_USE_KDPRINT</PreprocessorDefinitions>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ <Midl>
+ <PreprocessorDefinitions Condition="!('$(UseDebugLibraries)'=='false')">%(PreprocessorDefinitions);DEBUG_USE_KDPRINT</PreprocessorDefinitions>
+ </Midl>
+ <ResourceCompile>
+ <PreprocessorDefinitions Condition="!('$(UseDebugLibraries)'=='false')">%(PreprocessorDefinitions);DEBUG_USE_KDPRINT</PreprocessorDefinitions>
+ </ResourceCompile>
+ <Link>
+ <ModuleDefinitionFile>class.def</ModuleDefinitionFile>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <PreprocessorDefinitions Condition="!('$(UseDebugLibraries)'=='false')">%(PreprocessorDefinitions);DEBUG_USE_KDPRINT</PreprocessorDefinitions>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ <Midl>
+ <PreprocessorDefinitions Condition="!('$(UseDebugLibraries)'=='false')">%(PreprocessorDefinitions);DEBUG_USE_KDPRINT</PreprocessorDefinitions>
+ </Midl>
+ <ResourceCompile>
+ <PreprocessorDefinitions Condition="!('$(UseDebugLibraries)'=='false')">%(PreprocessorDefinitions);DEBUG_USE_KDPRINT</PreprocessorDefinitions>
+ </ResourceCompile>
+ <Link>
+ <ModuleDefinitionFile>class.def</ModuleDefinitionFile>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PreprocessorDefinitions Condition="!('$(UseDebugLibraries)'=='false')">%(PreprocessorDefinitions);DEBUG_USE_KDPRINT</PreprocessorDefinitions>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ <Midl>
+ <PreprocessorDefinitions Condition="!('$(UseDebugLibraries)'=='false')">%(PreprocessorDefinitions);DEBUG_USE_KDPRINT</PreprocessorDefinitions>
+ </Midl>
+ <ResourceCompile>
+ <PreprocessorDefinitions Condition="!('$(UseDebugLibraries)'=='false')">%(PreprocessorDefinitions);DEBUG_USE_KDPRINT</PreprocessorDefinitions>
+ </ResourceCompile>
+ <Link>
+ <ModuleDefinitionFile>class.def</ModuleDefinitionFile>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <MofComp Include="classlog.mof" />
+ <ResourceCompile Include="class.rc" />
+ </ItemGroup>
+ <Target Name="Custom Build Target 0" BeforeTargets="BeforeClCompile">
+ <ItemGroup>
+ <CustomBuildTarget0Input Include=".\$(IntDir)\classlog.mof" />
+ </ItemGroup>
+ <Exec Command="if not exist &quot;%(CustomBuildTarget0Input.Identity)&quot; copy &quot;.\%(CustomBuildTarget0Input.Filename)%(CustomBuildTarget0Input.Extension)&quot; &quot;%(CustomBuildTarget0Input.Identity)&quot;" WorkingDirectory="$(MSBuildProjectDirectory)" />
+ <Exec Command="mofcomp -Amendment:ms_409 -MFL:$(IntDir)\MFL.MFL -MOF:$(IntDir)\MOF.MOF &quot;%(CustomBuildTarget0Input.Identity)&quot;" WorkingDirectory="$(MSBuildProjectDirectory)" />
+ <Exec Command="wmimofck -y$(IntDir)\MOF.MOF -z$(IntDir)\MFL.MFL $(IntDir)\MOFMFL.MOF" WorkingDirectory="$(MSBuildProjectDirectory)" />
+ <Exec Command="mofcomp -B:&quot;.\$(IntDir)\classlog.bmf&quot; $(IntDir)\MOFMFL.MOF" WorkingDirectory="$(MSBuildProjectDirectory)" />
+ </Target>
+ <ItemGroup>
+ <Inf Exclude="@(Inf)" Include="*.inf" />
+ <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" />
+ <FilesToPackage Include="@(Inf->'%(CopyOutput)')" Condition="'@(Inf)'!=''" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Exclude="@(None)" Include="*.txt;*.htm;*.html" />
+ <None Exclude="@(None)" Include="*.ico;*.cur;*.bmp;*.dlg;*.rct;*.gif;*.jpg;*.jpeg;*.wav;*.jpe;*.tiff;*.tif;*.png;*.rc2" />
+ <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+</Project> \ No newline at end of file
diff --git a/storage/class/classpnp/src/classpnp.vcxproj.Filters b/storage/class/classpnp/src/classpnp.vcxproj.Filters
new file mode 100644
index 00000000..4ee10b9f
--- /dev/null
+++ b/storage/class/classpnp/src/classpnp.vcxproj.Filters
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions>
+ <UniqueIdentifier>{30C77614-BC54-48A1-B49D-DA6B50452C12}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files">
+ <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+ <UniqueIdentifier>{289E064C-1506-47AC-86D9-854F5A784947}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Resource Files">
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions>
+ <UniqueIdentifier>{7085ABD8-FCBB-43B4-993A-2B3187FFA33E}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Driver Files">
+ <Extensions>inf;inv;inx;mof;mc;</Extensions>
+ <UniqueIdentifier>{26B13FCD-BE1B-4A53-B538-39878886CB7C}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="autorun.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="class.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="classwmi.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="clntirp.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="create.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="data.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="debug.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="dictlib.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="dispatch.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="history.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="lock.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="obsolete.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="power.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="retry.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="srblib.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="utils.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="xferpkt.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <None Include="class.def">
+ <Filter>Source Files</Filter>
+ </None>
+ </ItemGroup>
+ <ItemGroup>
+ <MofComp Include="classlog.mof">
+ <Filter>Driver Files</Filter>
+ </MofComp>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="class.rc">
+ <Filter>Resource Files</Filter>
+ </ResourceCompile>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/storage/class/classpnp/src/classwmi.c b/storage/class/classpnp/src/classwmi.c
new file mode 100644
index 00000000..1df85512
--- /dev/null
+++ b/storage/class/classpnp/src/classwmi.c
@@ -0,0 +1,1224 @@
+/*++
+
+Copyright (C) Microsoft Corporation, 1991 - 1999
+
+Module Name:
+
+ classwmi.c
+
+Abstract:
+
+ SCSI class driver routines
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+
+Revision History:
+
+--*/
+
+#include "stddef.h"
+#include "ntddk.h"
+#include "scsi.h"
+
+#include "classpnp.h"
+
+#include "mountdev.h"
+
+#include <stdarg.h>
+
+#include "classp.h"
+#include <wmistr.h>
+#include <wmidata.h>
+#include <classlog.h>
+
+#ifdef DEBUG_USE_WPP
+#include "classwmi.tmh"
+#endif
+
+const UCHAR wmiInternalMOF[] = {
+#include "classlog.x"
+};
+
+#define TIME_STRING_LENGTH 25
+
+BOOLEAN
+ClassFindGuid(
+ PGUIDREGINFO GuidList,
+ ULONG GuidCount,
+ LPGUID Guid,
+ PULONG GuidIndex
+ );
+
+NTSTATUS
+ClassQueryInternalDataBlock(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN ULONG GuidIndex,
+ IN ULONG BufferAvail,
+ OUT PUCHAR Buffer
+ );
+
+PWCHAR
+ConvertTickToDateTime(
+ IN LARGE_INTEGER Tick,
+ _Out_writes_(TIME_STRING_LENGTH) PWCHAR String
+ );
+
+BOOLEAN
+ClassFindInternalGuid(
+ LPGUID Guid,
+ PULONG GuidIndex
+ );
+
+
+//
+// This is the name for the MOF resource that must be part of all drivers that
+// register via this interface.
+#define MOFRESOURCENAME L"MofResourceName"
+
+//
+// What can be paged ???
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE, ClassSystemControl)
+#pragma alloc_text(PAGE, ClassFindGuid)
+#pragma alloc_text(PAGE, ClassFindInternalGuid)
+#endif
+
+//
+// Define WMI interface to all class drivers
+//
+GUIDREGINFO wmiClassGuids[] =
+{
+ {
+ MSWmi_MofDataGuid, 1, 0
+ },
+ {
+ MSStorageDriver_ClassErrorLogGuid, 1, 0
+ }
+};
+
+#define MSWmi_MofData_GUID_Index 0
+#define MSStorageDriver_ClassErrorLogGuid_Index 1
+#define NUM_CLASS_WMI_GUIDS (sizeof(wmiClassGuids) / sizeof(GUIDREGINFO))
+
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassFindGuid()
+
+Routine Description:
+
+ This routine will search the list of guids registered and return
+ the index for the one that was registered.
+
+Arguments:
+
+ GuidList is the list of guids to search
+
+ GuidCount is the count of guids in the list
+
+ Guid is the guid being searched for
+
+ *GuidIndex returns the index to the guid
+
+Return Value:
+
+ TRUE if guid is found else FALSE
+
+--*/
+BOOLEAN
+ClassFindGuid(
+ PGUIDREGINFO GuidList,
+ ULONG GuidCount,
+ LPGUID Guid,
+ PULONG GuidIndex
+ )
+{
+ ULONG i;
+
+ PAGED_CODE();
+
+ for (i = 0; i < GuidCount; i++)
+ {
+ if (IsEqualGUID(Guid, &GuidList[i].Guid))
+ {
+ *GuidIndex = i;
+ return(TRUE);
+ }
+ }
+ return(FALSE);
+} // end ClassFindGuid()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassFindInternalGuid()
+
+Routine Description:
+
+ This routine will search the list of internal guids registered and return
+ the index for the one that was registered.
+
+Arguments:
+
+ Guid is the guid being searched for
+
+ *GuidIndex returns the index to the guid
+
+Return Value:
+
+ TRUE if guid is found else FALSE
+
+--*/
+BOOLEAN
+ClassFindInternalGuid(
+ LPGUID Guid,
+ PULONG GuidIndex
+ )
+{
+ ULONG i;
+
+ PAGED_CODE();
+
+ for (i = 0; i < NUM_CLASS_WMI_GUIDS; i++)
+ {
+ if (IsEqualGUID(Guid, &wmiClassGuids[i].Guid))
+ {
+ *GuidIndex = i;
+ return(TRUE);
+ }
+ }
+
+ return(FALSE);
+} // end ClassFindGuid()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassSystemControl()
+
+Routine Description:
+
+ Dispatch routine for IRP_MJ_SYSTEM_CONTROL. This routine will process
+ all wmi requests received, forwarding them if they are not for this
+ driver or determining if the guid is valid and if so passing it to
+ the driver specific function for handing wmi requests.
+
+Arguments:
+
+ DeviceObject - Supplies a pointer to the device object for this request.
+
+ Irp - Supplies the Irp making the request.
+
+Return Value:
+
+ status
+
+--*/
+NTSTATUS
+ClassSystemControl(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PCLASS_DRIVER_EXTENSION driverExtension;
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
+ ULONG isRemoved;
+ ULONG bufferSize;
+ PUCHAR buffer;
+ NTSTATUS status;
+ UCHAR minorFunction;
+ ULONG guidIndex = (ULONG)-1;
+ PCLASS_WMI_INFO classWmiInfo;
+ BOOLEAN isInternalGuid = FALSE;
+
+ PAGED_CODE();
+
+ //
+ // Make sure device has not been removed
+ isRemoved = ClassAcquireRemoveLock(DeviceObject, Irp);
+ if(isRemoved)
+ {
+ Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ return STATUS_DEVICE_DOES_NOT_EXIST;
+ }
+
+ //
+ // If the irp is not a WMI irp or it is not targetted at this device
+ // or this device has not regstered with WMI then just forward it on.
+ minorFunction = irpStack->MinorFunction;
+ if ((minorFunction > IRP_MN_EXECUTE_METHOD) ||
+ (irpStack->Parameters.WMI.ProviderId != (ULONG_PTR)DeviceObject) ||
+ ((minorFunction != IRP_MN_REGINFO) &&
+ (commonExtension->GuidCount == 0)))
+ {
+ //
+ // CONSIDER: Do I need to hang onto lock until IoCallDriver returns ?
+ IoSkipCurrentIrpStackLocation(Irp);
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ return(IoCallDriver(commonExtension->LowerDeviceObject, Irp));
+ }
+
+ buffer = (PUCHAR)irpStack->Parameters.WMI.Buffer;
+ bufferSize = irpStack->Parameters.WMI.BufferSize;
+
+ if (minorFunction != IRP_MN_REGINFO)
+ {
+ //
+ // For all requests other than query registration info we are passed
+ // a guid. Determine if the guid is one that is supported by the
+ // device.
+ if (commonExtension->GuidRegInfo != NULL &&
+ ClassFindGuid(commonExtension->GuidRegInfo,
+ commonExtension->GuidCount,
+ (LPGUID)irpStack->Parameters.WMI.DataPath,
+ &guidIndex))
+ {
+ isInternalGuid = FALSE;
+ status = STATUS_SUCCESS;
+ } else if (ClassFindInternalGuid((LPGUID)irpStack->Parameters.WMI.DataPath,
+ &guidIndex)) {
+ isInternalGuid = TRUE;
+ status = STATUS_SUCCESS;
+ } else {
+ status = STATUS_WMI_GUID_NOT_FOUND;
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_WMI, "WMI GUID not found!"));
+ }
+
+ TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_WMI, "WMI Find Guid = %x, isInternalGuid = %x", status, isInternalGuid));
+ if (NT_SUCCESS(status) &&
+ ((minorFunction == IRP_MN_QUERY_SINGLE_INSTANCE) ||
+ (minorFunction == IRP_MN_CHANGE_SINGLE_INSTANCE) ||
+ (minorFunction == IRP_MN_CHANGE_SINGLE_ITEM) ||
+ (minorFunction == IRP_MN_EXECUTE_METHOD)))
+ {
+ if ( (((PWNODE_HEADER)buffer)->Flags) &
+ WNODE_FLAG_STATIC_INSTANCE_NAMES)
+ {
+ if ( ((PWNODE_SINGLE_INSTANCE)buffer)->InstanceIndex != 0 )
+ {
+ status = STATUS_WMI_INSTANCE_NOT_FOUND;
+ }
+ } else {
+ status = STATUS_WMI_INSTANCE_NOT_FOUND;
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_WMI, "WMI Instance not found!"));
+ }
+ }
+
+ if (! NT_SUCCESS(status))
+ {
+ Irp->IoStatus.Status = status;
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ return(status);
+ }
+ }
+
+ driverExtension = commonExtension->DriverExtension;
+
+ classWmiInfo = commonExtension->IsFdo ?
+ &driverExtension->InitData.FdoData.ClassWmiInfo :
+ &driverExtension->InitData.PdoData.ClassWmiInfo;
+ switch(minorFunction)
+ {
+ case IRP_MN_REGINFO:
+ {
+ ULONG guidCount;
+ PGUIDREGINFO guidList;
+ PWMIREGINFOW wmiRegInfo;
+ PWMIREGGUIDW wmiRegGuid;
+ PUNICODE_STRING regPath;
+ PWCHAR stringPtr;
+ ULONG retSize;
+ ULONG registryPathOffset;
+ ULONG mofResourceOffset;
+ ULONG bufferNeeded;
+ ULONG i;
+ ULONG_PTR nameInfo;
+ ULONG nameSize, nameOffset, nameFlags;
+ UNICODE_STRING name, mofName;
+ PCLASS_QUERY_WMI_REGINFO_EX ClassQueryWmiRegInfoEx;
+
+ name.Buffer = NULL;
+ name.Length = 0;
+ name.MaximumLength = 0;
+ nameFlags = 0;
+
+ ClassQueryWmiRegInfoEx = commonExtension->IsFdo ?
+ driverExtension->ClassFdoQueryWmiRegInfoEx :
+ driverExtension->ClassPdoQueryWmiRegInfoEx;
+
+ if ((classWmiInfo->GuidRegInfo != NULL) &&
+ (classWmiInfo->ClassQueryWmiRegInfo != NULL) &&
+ (ClassQueryWmiRegInfoEx == NULL))
+ {
+ status = classWmiInfo->ClassQueryWmiRegInfo(
+ DeviceObject,
+ &nameFlags,
+ &name);
+
+ RtlInitUnicodeString(&mofName, MOFRESOURCENAME);
+
+ } else if ((classWmiInfo->GuidRegInfo != NULL) && (ClassQueryWmiRegInfoEx != NULL)) {
+ RtlInitUnicodeString(&mofName, L"");
+
+ status = (*ClassQueryWmiRegInfoEx)(
+ DeviceObject,
+ &nameFlags,
+ &name,
+ &mofName);
+ } else {
+ RtlInitUnicodeString(&mofName, L"");
+ nameFlags = WMIREG_FLAG_INSTANCE_PDO;
+ status = STATUS_SUCCESS;
+ }
+
+ if (NT_SUCCESS(status) &&
+ (! (nameFlags & WMIREG_FLAG_INSTANCE_PDO) &&
+ (name.Buffer == NULL)))
+ {
+ //
+ // if PDO flag not specified then an instance name must be
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_WMI, "Invalid Device Request!"));
+ }
+
+ if (NT_SUCCESS(status))
+ {
+ guidList = classWmiInfo->GuidRegInfo;
+ guidCount = (classWmiInfo->GuidRegInfo == NULL ? 0 : classWmiInfo->GuidCount) + NUM_CLASS_WMI_GUIDS;
+
+ nameOffset = sizeof(WMIREGINFO) +
+ guidCount * sizeof(WMIREGGUIDW);
+
+ if (nameFlags & WMIREG_FLAG_INSTANCE_PDO)
+ {
+ nameSize = 0;
+ nameInfo = commonExtension->IsFdo ?
+ (ULONG_PTR)((PFUNCTIONAL_DEVICE_EXTENSION)commonExtension)->LowerPdo :
+ (ULONG_PTR)DeviceObject;
+ } else {
+ nameFlags |= WMIREG_FLAG_INSTANCE_LIST;
+ nameSize = name.Length + sizeof(USHORT);
+ nameInfo = nameOffset;
+ }
+
+ mofResourceOffset = nameOffset + nameSize;
+
+ registryPathOffset = mofResourceOffset +
+ mofName.Length + sizeof(USHORT);
+
+ regPath = &driverExtension->RegistryPath;
+
+ bufferNeeded = registryPathOffset + regPath->Length;
+ bufferNeeded += sizeof(USHORT);
+
+ if (bufferNeeded <= bufferSize)
+ {
+ retSize = bufferNeeded;
+
+ commonExtension->GuidCount = guidCount;
+ commonExtension->GuidRegInfo = guidList;
+
+ wmiRegInfo = (PWMIREGINFO)buffer;
+ wmiRegInfo->BufferSize = bufferNeeded;
+ wmiRegInfo->NextWmiRegInfo = 0;
+ wmiRegInfo->MofResourceName = mofResourceOffset;
+ wmiRegInfo->RegistryPath = registryPathOffset;
+ wmiRegInfo->GuidCount = guidCount;
+
+ for (i = 0; i < classWmiInfo->GuidCount; i++)
+ {
+ wmiRegGuid = &wmiRegInfo->WmiRegGuid[i];
+ wmiRegGuid->Guid = guidList[i].Guid;
+ wmiRegGuid->Flags = guidList[i].Flags | nameFlags;
+ wmiRegGuid->InstanceInfo = nameInfo;
+ wmiRegGuid->InstanceCount = 1;
+ }
+ for (i = 0; i < NUM_CLASS_WMI_GUIDS; i++)
+ {
+ wmiRegGuid = &wmiRegInfo->WmiRegGuid[i + classWmiInfo->GuidCount];
+ wmiRegGuid->Guid = wmiClassGuids[i].Guid;
+ wmiRegGuid->Flags = wmiClassGuids[i].Flags | nameFlags;
+ wmiRegGuid->InstanceInfo = nameInfo;
+ wmiRegGuid->InstanceCount = 1;
+ }
+
+ if ( nameFlags & WMIREG_FLAG_INSTANCE_LIST)
+ {
+ bufferNeeded = nameOffset + sizeof(WCHAR);
+ bufferNeeded += name.Length;
+
+ if (bufferSize >= bufferNeeded){
+ stringPtr = (PWCHAR)((PUCHAR)buffer + nameOffset);
+ *stringPtr++ = name.Length;
+ RtlCopyMemory(stringPtr, name.Buffer, name.Length);
+ }
+ else {
+ NT_ASSERT(bufferSize >= bufferNeeded);
+ status = STATUS_INVALID_BUFFER_SIZE;
+ }
+ }
+
+ bufferNeeded = mofResourceOffset + sizeof(WCHAR);
+ bufferNeeded += mofName.Length;
+
+ if (bufferSize >= bufferNeeded){
+ stringPtr = (PWCHAR)((PUCHAR)buffer + mofResourceOffset);
+ *stringPtr++ = mofName.Length;
+ RtlCopyMemory(stringPtr, mofName.Buffer, mofName.Length);
+ }
+ else {
+ NT_ASSERT(bufferSize >= bufferNeeded);
+ status = STATUS_INVALID_BUFFER_SIZE;
+ }
+
+ bufferNeeded = registryPathOffset + sizeof(WCHAR);
+ bufferNeeded += regPath->Length;
+
+ if (bufferSize >= bufferNeeded){
+ stringPtr = (PWCHAR)((PUCHAR)buffer + registryPathOffset);
+ *stringPtr++ = regPath->Length;
+ RtlCopyMemory(stringPtr,
+ regPath->Buffer,
+ regPath->Length);
+ }
+ else {
+
+ NT_ASSERT(bufferSize >= bufferNeeded);
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_WMI, "Invalid Buffer Size!"));
+ status = STATUS_INVALID_BUFFER_SIZE;
+ }
+
+ } else {
+ *((PULONG)buffer) = bufferNeeded;
+ retSize = sizeof(ULONG);
+ }
+ } else {
+ retSize = 0;
+ }
+
+ FREE_POOL(name.Buffer);
+
+ Irp->IoStatus.Status = status;
+ Irp->IoStatus.Information = retSize;
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ return(status);
+ }
+
+ case IRP_MN_QUERY_ALL_DATA:
+ {
+ PWNODE_ALL_DATA wnode;
+ ULONG bufferAvail;
+
+ wnode = (PWNODE_ALL_DATA)buffer;
+
+ if (bufferSize < sizeof(WNODE_ALL_DATA))
+ {
+ bufferAvail = 0;
+ } else {
+ bufferAvail = bufferSize - sizeof(WNODE_ALL_DATA);
+ }
+
+ wnode->DataBlockOffset = sizeof(WNODE_ALL_DATA);
+
+ NT_ASSERT(guidIndex != (ULONG)-1);
+ _Analysis_assume_(isInternalGuid);
+ if (isInternalGuid)
+ {
+ status = ClassQueryInternalDataBlock(
+ DeviceObject,
+ Irp,
+ guidIndex,
+ bufferAvail,
+ buffer + sizeof(WNODE_ALL_DATA));
+ } else {
+ status = classWmiInfo->ClassQueryWmiDataBlock(
+ DeviceObject,
+ Irp,
+ guidIndex,
+ bufferAvail,
+ buffer + sizeof(WNODE_ALL_DATA));
+ }
+ break;
+ }
+
+ case IRP_MN_QUERY_SINGLE_INSTANCE:
+ {
+ PWNODE_SINGLE_INSTANCE wnode;
+ ULONG dataBlockOffset;
+
+ wnode = (PWNODE_SINGLE_INSTANCE)buffer;
+
+ dataBlockOffset = wnode->DataBlockOffset;
+
+ NT_ASSERT(guidIndex != (ULONG)-1);
+ _Analysis_assume_(isInternalGuid);
+ if (isInternalGuid)
+ {
+ status = ClassQueryInternalDataBlock(
+ DeviceObject,
+ Irp,
+ guidIndex,
+ bufferSize - dataBlockOffset,
+ (PUCHAR)wnode + dataBlockOffset);
+ } else {
+ status = classWmiInfo->ClassQueryWmiDataBlock(
+ DeviceObject,
+ Irp,
+ guidIndex,
+ bufferSize - dataBlockOffset,
+ (PUCHAR)wnode + dataBlockOffset);
+ }
+ break;
+ }
+
+ case IRP_MN_CHANGE_SINGLE_INSTANCE:
+ {
+ PWNODE_SINGLE_INSTANCE wnode;
+
+ wnode = (PWNODE_SINGLE_INSTANCE)buffer;
+ _Analysis_assume_(isInternalGuid);
+ if (isInternalGuid)
+ {
+ status = ClassWmiCompleteRequest(DeviceObject,
+ Irp,
+ STATUS_WMI_GUID_NOT_FOUND,
+ 0,
+ IO_NO_INCREMENT);
+ } else {
+
+ NT_ASSERT(guidIndex != (ULONG)-1);
+
+ status = classWmiInfo->ClassSetWmiDataBlock(
+ DeviceObject,
+ Irp,
+ guidIndex,
+ wnode->SizeDataBlock,
+ (PUCHAR)wnode + wnode->DataBlockOffset);
+ }
+
+ break;
+ }
+
+ case IRP_MN_CHANGE_SINGLE_ITEM:
+ {
+ PWNODE_SINGLE_ITEM wnode;
+
+ wnode = (PWNODE_SINGLE_ITEM)buffer;
+
+ NT_ASSERT(guidIndex != (ULONG)-1);
+ _Analysis_assume_(isInternalGuid);
+ if (isInternalGuid)
+ {
+ status = ClassWmiCompleteRequest(DeviceObject,
+ Irp,
+ STATUS_WMI_GUID_NOT_FOUND,
+ 0,
+ IO_NO_INCREMENT);
+ } else {
+
+ NT_ASSERT(guidIndex != (ULONG)-1);
+
+ status = classWmiInfo->ClassSetWmiDataItem(
+ DeviceObject,
+ Irp,
+ guidIndex,
+ wnode->ItemId,
+ wnode->SizeDataItem,
+ (PUCHAR)wnode + wnode->DataBlockOffset);
+
+ }
+
+ break;
+ }
+
+ case IRP_MN_EXECUTE_METHOD:
+ {
+ PWNODE_METHOD_ITEM wnode;
+
+ wnode = (PWNODE_METHOD_ITEM)buffer;
+ _Analysis_assume_(isInternalGuid);
+ if (isInternalGuid)
+ {
+ status = ClassWmiCompleteRequest(DeviceObject,
+ Irp,
+ STATUS_WMI_GUID_NOT_FOUND,
+ 0,
+ IO_NO_INCREMENT);
+ } else {
+
+ NT_ASSERT(guidIndex != (ULONG)-1);
+
+ status = classWmiInfo->ClassExecuteWmiMethod(
+ DeviceObject,
+ Irp,
+ guidIndex,
+ wnode->MethodId,
+ wnode->SizeDataBlock,
+ bufferSize - wnode->DataBlockOffset,
+ buffer + wnode->DataBlockOffset);
+ }
+
+ break;
+ }
+
+ case IRP_MN_ENABLE_EVENTS:
+ {
+ _Analysis_assume_(isInternalGuid);
+ if (isInternalGuid)
+ {
+ status = ClassWmiCompleteRequest(DeviceObject,
+ Irp,
+ STATUS_WMI_GUID_NOT_FOUND,
+ 0,
+ IO_NO_INCREMENT);
+ } else {
+
+ NT_ASSERT(guidIndex != (ULONG)-1);
+
+ status = classWmiInfo->ClassWmiFunctionControl(
+ DeviceObject,
+ Irp,
+ guidIndex,
+ EventGeneration,
+ TRUE);
+ }
+ break;
+ }
+
+ case IRP_MN_DISABLE_EVENTS:
+ {
+ _Analysis_assume_(isInternalGuid);
+ if (isInternalGuid)
+ {
+ status = ClassWmiCompleteRequest(DeviceObject,
+ Irp,
+ STATUS_WMI_GUID_NOT_FOUND,
+ 0,
+ IO_NO_INCREMENT);
+ } else {
+
+ NT_ASSERT(guidIndex != (ULONG)-1);
+
+ status = classWmiInfo->ClassWmiFunctionControl(
+ DeviceObject,
+ Irp,
+ guidIndex,
+ EventGeneration,
+ FALSE);
+ }
+ break;
+ }
+
+ case IRP_MN_ENABLE_COLLECTION:
+ {
+ _Analysis_assume_(isInternalGuid);
+ if (isInternalGuid)
+ {
+ status = ClassWmiCompleteRequest(DeviceObject,
+ Irp,
+ STATUS_WMI_GUID_NOT_FOUND,
+ 0,
+ IO_NO_INCREMENT);
+ } else {
+
+ NT_ASSERT(guidIndex != (ULONG)-1);
+
+ status = classWmiInfo->ClassWmiFunctionControl(
+ DeviceObject,
+ Irp,
+ guidIndex,
+ DataBlockCollection,
+ TRUE);
+ }
+ break;
+ }
+
+ case IRP_MN_DISABLE_COLLECTION:
+ {
+ _Analysis_assume_(isInternalGuid);
+ if (isInternalGuid)
+ {
+ status = ClassWmiCompleteRequest(DeviceObject,
+ Irp,
+ STATUS_WMI_GUID_NOT_FOUND,
+ 0,
+ IO_NO_INCREMENT);
+ } else {
+
+ NT_ASSERT(guidIndex != (ULONG)-1);
+
+ status = classWmiInfo->ClassWmiFunctionControl(
+ DeviceObject,
+ Irp,
+ guidIndex,
+ DataBlockCollection,
+ FALSE);
+ }
+
+ break;
+ }
+
+ default:
+ {
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ break;
+ }
+
+ }
+
+ return(status);
+} // end ClassSystemControl()
+
+
+NTSTATUS
+ClassQueryInternalDataBlock(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN ULONG GuidIndex,
+ IN ULONG BufferAvail,
+ OUT PUCHAR Buffer
+ )
+/*++
+
+Routine Description:
+
+ This routine allows querying for the contents of an internal WMI
+ data block. When the driver has finished filling the data block it
+ must call ClassWmiCompleteRequest to complete the irp.
+
+Arguments:
+
+ DeviceObject is the device whose data block is being queried
+
+ Irp is the Irp that makes this request
+
+ GuidIndex is the index into the list of guids provided when the
+ device registered
+
+ BufferAvail on has the maximum size available to write the data
+ block.
+
+ Buffer on return is filled with the returned data block
+
+
+Return Value:
+
+ status
+
+--*/
+{
+ NTSTATUS status;
+ ULONG sizeNeeded = 0, i;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt = DeviceObject->DeviceExtension;
+
+ if (GuidIndex == MSWmi_MofData_GUID_Index) {
+ sizeNeeded = sizeof(wmiInternalMOF);
+ if (BufferAvail >= sizeNeeded) {
+ RtlMoveMemory(Buffer, wmiInternalMOF, sizeof(wmiInternalMOF));
+ status = STATUS_SUCCESS;
+ } else {
+ status = STATUS_BUFFER_TOO_SMALL;
+ }
+ } else if (GuidIndex == MSStorageDriver_ClassErrorLogGuid_Index) {
+
+ //
+ // NOTE - ClassErrorLog is still using SCSI_REQUEST_BLOCK and will not be
+ // updated to support extended SRB until classpnp is updated to send >16
+ // byte CDBs. Extended SRBs will be translated to SCSI_REQUEST_BLOCK.
+ //
+ sizeNeeded = MSStorageDriver_ClassErrorLog_SIZE;
+ if (BufferAvail >= sizeNeeded) {
+ PMSStorageDriver_ClassErrorLog errorLog = (PMSStorageDriver_ClassErrorLog) Buffer;
+ PMSStorageDriver_ClassErrorLogEntry logEntry;
+ PMSStorageDriver_ScsiRequestBlock srbBlock;
+ PMSStorageDriver_SenseData senseData;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
+ PCLASS_ERROR_LOG_DATA fdoLogEntry;
+ PSCSI_REQUEST_BLOCK fdoSRBBlock;
+ PSENSE_DATA fdoSenseData;
+ errorLog->numEntries = NUM_ERROR_LOG_ENTRIES;
+ for (i = 0; i < NUM_ERROR_LOG_ENTRIES; i++) {
+ fdoLogEntry = &fdoData->ErrorLogs[i];
+ fdoSRBBlock = &fdoLogEntry->Srb;
+ fdoSenseData = &fdoLogEntry->SenseData;
+ logEntry = &errorLog->logEntries[i];
+ srbBlock = &logEntry->srb;
+ senseData = &logEntry->senseData;
+ logEntry->tickCount = fdoLogEntry->TickCount.QuadPart;
+ logEntry->portNumber = fdoLogEntry->PortNumber;
+ logEntry->errorPaging = (fdoLogEntry->ErrorPaging == 0 ? FALSE : TRUE);
+ logEntry->errorRetried = (fdoLogEntry->ErrorRetried == 0 ? FALSE : TRUE);
+ logEntry->errorUnhandled = (fdoLogEntry->ErrorUnhandled == 0 ? FALSE : TRUE);
+ logEntry->errorReserved = fdoLogEntry->ErrorReserved;
+ RtlMoveMemory(logEntry->reserved, fdoLogEntry->Reserved, sizeof(logEntry->reserved));
+ ConvertTickToDateTime(fdoLogEntry->TickCount, logEntry->eventTime);
+
+ srbBlock->length = fdoSRBBlock->Length;
+ srbBlock->function = fdoSRBBlock->Function;
+ srbBlock->srbStatus = fdoSRBBlock->SrbStatus;
+ srbBlock->scsiStatus = fdoSRBBlock->ScsiStatus;
+ srbBlock->pathID = fdoSRBBlock->PathId;
+ srbBlock->targetID = fdoSRBBlock->TargetId;
+ srbBlock->lun = fdoSRBBlock->Lun;
+ srbBlock->queueTag = fdoSRBBlock->QueueTag;
+ srbBlock->queueAction = fdoSRBBlock->QueueAction;
+ srbBlock->cdbLength = fdoSRBBlock->CdbLength;
+ srbBlock->senseInfoBufferLength = fdoSRBBlock->SenseInfoBufferLength;
+ srbBlock->srbFlags = fdoSRBBlock->SrbFlags;
+ srbBlock->dataTransferLength = fdoSRBBlock->DataTransferLength;
+ srbBlock->timeOutValue = fdoSRBBlock->TimeOutValue;
+ srbBlock->dataBuffer = (ULONGLONG) fdoSRBBlock->DataBuffer;
+ srbBlock->senseInfoBuffer = (ULONGLONG) fdoSRBBlock->SenseInfoBuffer;
+ srbBlock->nextSRB = (ULONGLONG) fdoSRBBlock->NextSrb;
+ srbBlock->originalRequest = (ULONGLONG) fdoSRBBlock->OriginalRequest;
+ srbBlock->srbExtension = (ULONGLONG) fdoSRBBlock->SrbExtension;
+ srbBlock->internalStatus = fdoSRBBlock->InternalStatus;
+#if defined(_WIN64)
+ srbBlock->reserved = fdoSRBBlock->Reserved;
+#else
+ srbBlock->reserved = 0;
+#endif
+ RtlMoveMemory(srbBlock->cdb, fdoSRBBlock->Cdb, sizeof(srbBlock->cdb));
+
+ //
+ // Note: Sense data has been converted into Fixed format before it was
+ // put in the log. Therefore, no conversion is needed here.
+ //
+ senseData->errorCode = fdoSenseData->ErrorCode;
+ senseData->valid = (fdoSenseData->Valid == 0 ? FALSE : TRUE);
+ senseData->segmentNumber = fdoSenseData->SegmentNumber;
+ senseData->senseKey = fdoSenseData->SenseKey;
+ senseData->reserved = (fdoSenseData->Reserved == 0 ? FALSE : TRUE);
+ senseData->incorrectLength = (fdoSenseData->IncorrectLength == 0 ? FALSE : TRUE);
+ senseData->endOfMedia = (fdoSenseData->EndOfMedia == 0 ? FALSE : TRUE);
+ senseData->fileMark = (fdoSenseData->FileMark == 0 ? FALSE : TRUE);
+ RtlMoveMemory(senseData->information, fdoSenseData->Information, sizeof(senseData->information));
+ senseData->additionalSenseLength = fdoSenseData->AdditionalSenseLength;
+ RtlMoveMemory(senseData->commandSpecificInformation, fdoSenseData->CommandSpecificInformation, sizeof(senseData->commandSpecificInformation));
+ senseData->additionalSenseCode = fdoSenseData->AdditionalSenseCode;
+ senseData->additionalSenseCodeQualifier = fdoSenseData->AdditionalSenseCodeQualifier;
+ senseData->fieldReplaceableUnitCode = fdoSenseData->FieldReplaceableUnitCode;
+ RtlMoveMemory(senseData->senseKeySpecific, fdoSenseData->SenseKeySpecific, sizeof(senseData->senseKeySpecific));
+ }
+ status = STATUS_SUCCESS;
+ } else {
+ status = STATUS_BUFFER_TOO_SMALL;
+ }
+ } else if (GuidIndex > 0 && GuidIndex < NUM_CLASS_WMI_GUIDS) {
+ status = STATUS_WMI_INSTANCE_NOT_FOUND;
+ } else {
+ status = STATUS_WMI_GUID_NOT_FOUND;
+ }
+ status = ClassWmiCompleteRequest(DeviceObject,
+ Irp,
+ status,
+ sizeNeeded,
+ IO_NO_INCREMENT);
+ return status;
+}
+
+PWCHAR
+ConvertTickToDateTime(
+ IN LARGE_INTEGER Tick,
+ _Out_writes_(TIME_STRING_LENGTH) PWCHAR String
+ )
+
+/*++
+
+Routine Description:
+
+ This routine converts a tick count to a datetime (MOF) data type
+
+Arguments:
+
+ Tick - The tick count that needs to be converted
+ String - The buffer to hold the time string, must be able to hold WCHAR[25]
+
+Return Value:
+
+ The time string
+
+--*/
+
+{
+ LARGE_INTEGER nowTick, nowTime, time;
+ ULONG maxInc = 0;
+ TIME_FIELDS timeFields = {0};
+ WCHAR outDateTime[TIME_STRING_LENGTH + 1];
+
+ nowTick.QuadPart = 0;
+ nowTime.QuadPart = 0;
+ //
+ // Translate the tick count to a system time
+ //
+ KeQueryTickCount(&nowTick);
+ maxInc = KeQueryTimeIncrement();
+ KeQuerySystemTime(&nowTime);
+ time.QuadPart = nowTime.QuadPart - ((nowTick.QuadPart - Tick.QuadPart) * maxInc);
+
+ RtlTimeToTimeFields(&time, &timeFields);
+
+ //
+ // The buffer String is of size MAX_PATH. Use that to specify the buffer size.
+ //
+ //yyyymmddhhmmss.mmmmmmsutc
+ RtlStringCbPrintfW(outDateTime, sizeof(outDateTime), L"%04d%02d%02d%02d%02d%02d.%03d***+000", timeFields.Year, timeFields.Month, timeFields.Day, timeFields.Hour, timeFields.Minute, timeFields.Second, timeFields.Milliseconds);
+ RtlMoveMemory(String, outDateTime, sizeof(WCHAR) * TIME_STRING_LENGTH);
+ return String;
+}
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassWmiCompleteRequest()
+
+Routine Description:
+
+
+ This routine will do the work of completing a WMI irp. Depending upon the
+ the WMI request this routine will fixup the returned WNODE appropriately.
+
+ NOTE: This routine assumes that the ClassRemoveLock is held and it will
+ release it.
+
+Arguments:
+
+ DeviceObject - Supplies a pointer to the device object for this request.
+
+ Irp - Supplies the Irp making the request.
+
+ Status - Status to complete the irp with. STATUS_BUFFER_TOO_SMALL is used
+ to indicate that more buffer is required for the data requested.
+
+ BufferUsed - number of bytes of actual data to return (not including WMI
+ specific structures)
+
+ PriorityBoost - priority boost to pass to ClassCompleteRequest
+
+Return Value:
+
+ status
+
+--*/
+SCSIPORT_API
+NTSTATUS
+ClassWmiCompleteRequest(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _Inout_ PIRP Irp,
+ _In_ NTSTATUS Status,
+ _In_ ULONG BufferUsed,
+ _In_ CCHAR PriorityBoost
+ )
+{
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
+ PUCHAR buffer;
+ ULONG retSize;
+ UCHAR minorFunction;
+
+ minorFunction = irpStack->MinorFunction;
+ buffer = (PUCHAR)irpStack->Parameters.WMI.Buffer;
+
+ switch(minorFunction)
+ {
+ case IRP_MN_QUERY_ALL_DATA:
+ {
+ PWNODE_ALL_DATA wnode;
+ PWNODE_TOO_SMALL wnodeTooSmall;
+ ULONG bufferNeeded;
+
+ wnode = (PWNODE_ALL_DATA)buffer;
+
+ bufferNeeded = sizeof(WNODE_ALL_DATA) + BufferUsed;
+
+ if (NT_SUCCESS(Status))
+ {
+ retSize = bufferNeeded;
+ wnode->WnodeHeader.BufferSize = bufferNeeded;
+ KeQuerySystemTime(&wnode->WnodeHeader.TimeStamp);
+ wnode->WnodeHeader.Flags |= WNODE_FLAG_FIXED_INSTANCE_SIZE;
+ wnode->FixedInstanceSize = BufferUsed;
+ wnode->InstanceCount = 1;
+
+ } else if (Status == STATUS_BUFFER_TOO_SMALL) {
+ wnodeTooSmall = (PWNODE_TOO_SMALL)wnode;
+
+ wnodeTooSmall->WnodeHeader.BufferSize = sizeof(WNODE_TOO_SMALL);
+ wnodeTooSmall->WnodeHeader.Flags = WNODE_FLAG_TOO_SMALL;
+ wnodeTooSmall->SizeNeeded = sizeof(WNODE_ALL_DATA) + BufferUsed;
+ retSize = sizeof(WNODE_TOO_SMALL);
+ Status = STATUS_SUCCESS;
+ } else {
+ retSize = 0;
+ }
+ break;
+ }
+
+ case IRP_MN_QUERY_SINGLE_INSTANCE:
+ {
+ PWNODE_SINGLE_INSTANCE wnode;
+ PWNODE_TOO_SMALL wnodeTooSmall;
+ ULONG bufferNeeded;
+
+ wnode = (PWNODE_SINGLE_INSTANCE)buffer;
+
+ bufferNeeded = wnode->DataBlockOffset + BufferUsed;
+
+ if (NT_SUCCESS(Status))
+ {
+ retSize = bufferNeeded;
+ wnode->WnodeHeader.BufferSize = bufferNeeded;
+ KeQuerySystemTime(&wnode->WnodeHeader.TimeStamp);
+ wnode->SizeDataBlock = BufferUsed;
+
+ } else if (Status == STATUS_BUFFER_TOO_SMALL) {
+ wnodeTooSmall = (PWNODE_TOO_SMALL)wnode;
+
+ wnodeTooSmall->WnodeHeader.BufferSize = sizeof(WNODE_TOO_SMALL);
+ wnodeTooSmall->WnodeHeader.Flags = WNODE_FLAG_TOO_SMALL;
+ wnodeTooSmall->SizeNeeded = bufferNeeded;
+ retSize = sizeof(WNODE_TOO_SMALL);
+ Status = STATUS_SUCCESS;
+ } else {
+ retSize = 0;
+ }
+ break;
+ }
+
+ case IRP_MN_EXECUTE_METHOD:
+ {
+ PWNODE_METHOD_ITEM wnode;
+ PWNODE_TOO_SMALL wnodeTooSmall;
+ ULONG bufferNeeded;
+
+ wnode = (PWNODE_METHOD_ITEM)buffer;
+
+ bufferNeeded = wnode->DataBlockOffset + BufferUsed;
+
+ if (NT_SUCCESS(Status))
+ {
+ retSize = bufferNeeded;
+ wnode->WnodeHeader.BufferSize = bufferNeeded;
+ KeQuerySystemTime(&wnode->WnodeHeader.TimeStamp);
+ wnode->SizeDataBlock = BufferUsed;
+
+ } else if (Status == STATUS_BUFFER_TOO_SMALL) {
+ wnodeTooSmall = (PWNODE_TOO_SMALL)wnode;
+
+ wnodeTooSmall->WnodeHeader.BufferSize = sizeof(WNODE_TOO_SMALL);
+ wnodeTooSmall->WnodeHeader.Flags = WNODE_FLAG_TOO_SMALL;
+ wnodeTooSmall->SizeNeeded = bufferNeeded;
+ retSize = sizeof(WNODE_TOO_SMALL);
+ Status = STATUS_SUCCESS;
+ } else {
+ retSize = 0;
+ }
+ break;
+ }
+
+ default:
+ {
+ //
+ // All other requests don't return any data
+ retSize = 0;
+ break;
+ }
+
+ }
+
+ Irp->IoStatus.Status = Status;
+ Irp->IoStatus.Information = retSize;
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, PriorityBoost);
+ return(Status);
+} // end ClassWmiCompleteRequest()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassWmiFireEvent()
+
+Routine Description:
+
+ This routine will fire a WMI event using the data buffer passed. This
+ routine may be called at or below DPC level
+
+Arguments:
+
+ DeviceObject - Supplies a pointer to the device object for this event
+
+ Guid is pointer to the GUID that represents the event
+
+ InstanceIndex is the index of the instance of the event
+
+ EventDataSize is the number of bytes of data that is being fired with
+ with the event
+
+ EventData is the data that is fired with the events. This may be NULL
+ if there is no data associated with the event
+
+
+Return Value:
+
+ status
+
+--*/
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+ClassWmiFireEvent(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _In_ LPGUID Guid,
+ _In_ ULONG InstanceIndex,
+ _In_ ULONG EventDataSize,
+ _In_reads_bytes_(EventDataSize) PVOID EventData
+ )
+{
+
+ ULONG sizeNeeded;
+ PWNODE_SINGLE_INSTANCE event;
+ NTSTATUS status;
+
+ if (EventData == NULL)
+ {
+ EventDataSize = 0;
+ }
+
+ sizeNeeded = sizeof(WNODE_SINGLE_INSTANCE) + EventDataSize;
+
+ event = ExAllocatePoolWithTag(NonPagedPoolNx, sizeNeeded, CLASS_TAG_WMI);
+ if (event != NULL)
+ {
+ RtlZeroMemory(event, sizeNeeded);
+ event->WnodeHeader.Guid = *Guid;
+ event->WnodeHeader.ProviderId = IoWMIDeviceObjectToProviderId(DeviceObject);
+ event->WnodeHeader.BufferSize = sizeNeeded;
+ event->WnodeHeader.Flags = WNODE_FLAG_SINGLE_INSTANCE |
+ WNODE_FLAG_EVENT_ITEM |
+ WNODE_FLAG_STATIC_INSTANCE_NAMES;
+ KeQuerySystemTime(&event->WnodeHeader.TimeStamp);
+
+ event->InstanceIndex = InstanceIndex;
+ event->SizeDataBlock = EventDataSize;
+ event->DataBlockOffset = sizeof(WNODE_SINGLE_INSTANCE);
+ if (EventData != NULL)
+ {
+ RtlCopyMemory( &event->VariableData, EventData, EventDataSize);
+ }
+
+ status = IoWMIWriteEvent(event);
+ if (! NT_SUCCESS(status))
+ {
+ FREE_POOL(event);
+ }
+ } else {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ return(status);
+} // end ClassWmiFireEvent()
diff --git a/storage/class/classpnp/src/clntirp.c b/storage/class/classpnp/src/clntirp.c
new file mode 100644
index 00000000..d615dce1
--- /dev/null
+++ b/storage/class/classpnp/src/clntirp.c
@@ -0,0 +1,783 @@
+/*++
+
+Copyright (C) Microsoft Corporation, 1991 - 1999
+
+Module Name:
+
+ clntirp.c
+
+Abstract:
+
+ Client IRP queuing routines for CLASSPNP
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+
+Revision History:
+
+--*/
+
+#include "classp.h"
+#include "debug.h"
+
+
+#ifdef DEBUG_USE_WPP
+#include "clntirp.tmh"
+#endif
+
+VOID
+ClasspStartIdleTimer(
+ IN PCLASS_PRIVATE_FDO_DATA FdoData,
+ IN ULONGLONG IdleInterval
+ );
+
+VOID
+ClasspStopIdleTimer(
+ PCLASS_PRIVATE_FDO_DATA FdoData
+ );
+
+KDEFERRED_ROUTINE ClasspIdleTimerDpc;
+
+VOID
+ClasspServiceIdleRequest(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ BOOLEAN PostToDpc
+ );
+
+PIRP
+ClasspDequeueIdleRequest(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ );
+
+
+/*++
+
+EnqueueDeferredClientIrp
+
+Routine Description:
+
+ Insert the deferred irp into the list.
+
+ Note: we currently do not support Cancel for storage irps.
+
+Arguments:
+
+ Fdo - Pointer to the device object
+ Irp - Pointer to the I/O request packet
+
+Return Value:
+
+ None
+
+--*/
+VOID
+EnqueueDeferredClientIrp(
+ PDEVICE_OBJECT Fdo,
+ PIRP Irp
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
+ KIRQL oldIrql;
+
+
+ KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
+ InsertTailList(&fdoData->DeferredClientIrpList, &Irp->Tail.Overlay.ListEntry);
+
+
+ KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
+}
+
+/*++
+
+DequeueDeferredClientIrp
+
+Routine Description:
+
+ Remove the deferred irp from the list.
+
+Arguments:
+
+ Fdo - Pointer to the device object
+
+Return Value:
+
+ Pointer to removed IRP
+
+--*/
+PIRP
+DequeueDeferredClientIrp(
+ PDEVICE_OBJECT Fdo
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
+ PIRP irp;
+
+ //
+ // The DeferredClientIrpList is almost always empty.
+ // We don't want to grab the spinlock every time we check it (which is on every xfer completion)
+ // so check once first before we grab the spinlock.
+ //
+ if (IsListEmpty(&fdoData->DeferredClientIrpList)){
+ irp = NULL;
+ }
+ else {
+ PLIST_ENTRY listEntry;
+ KIRQL oldIrql;
+
+ KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
+ if (IsListEmpty(&fdoData->DeferredClientIrpList)){
+ listEntry = NULL;
+ }
+ else {
+ listEntry = RemoveHeadList(&fdoData->DeferredClientIrpList);
+ }
+ KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
+
+ if (listEntry == NULL) {
+ irp = NULL;
+ }
+ else {
+ irp = CONTAINING_RECORD(listEntry, IRP, Tail.Overlay.ListEntry);
+ NT_ASSERT(irp->Type == IO_TYPE_IRP);
+
+
+ InitializeListHead(&irp->Tail.Overlay.ListEntry);
+ }
+ }
+
+ return irp;
+}
+
+/*++
+
+ClasspInitializeIdleTimer
+
+Routine Description:
+
+ Initialize the idle timer for the given device.
+
+Arguments:
+
+ FdoExtension - Pointer to the device extension
+ IdleInterval - Timer interval
+
+Return Value:
+
+ None
+
+--*/
+VOID
+ClasspInitializeIdleTimer(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ )
+{
+ PCLASS_PRIVATE_FDO_DATA fdoData = FdoExtension->PrivateFdoData;
+ ULONG idleInterval = CLASS_IDLE_INTERVAL;
+ ULONG idlePrioritySupported = TRUE;
+ ULONG activeIdleIoMax = 1;
+
+ ClassGetDeviceParameter(FdoExtension,
+ CLASSP_REG_SUBKEY_NAME,
+ CLASSP_REG_IDLE_PRIORITY_SUPPORTED,
+ &idlePrioritySupported);
+
+
+ if (idlePrioritySupported == FALSE) {
+ //
+ // User has set the registry to disable idle priority for this disk.
+ // No need to initialize any further.
+ // Always ensure that none of the other fields used for idle priority
+ // io are ever used without checking if it is supported.
+ //
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_TIMER, "ClasspInitializeIdleTimer: Idle priority not supported for disk %p\n", FdoExtension));
+ fdoData->IdlePrioritySupported = FALSE;
+ fdoData->IdleIoCount = 0;
+ fdoData->ActiveIoCount = 0;
+ return;
+ }
+
+ ClassGetDeviceParameter(FdoExtension,
+ CLASSP_REG_SUBKEY_NAME,
+ CLASSP_REG_IDLE_INTERVAL_NAME,
+ &idleInterval);
+
+ if ((idleInterval < CLASS_IDLE_TIMER_TICKS) || (idleInterval > USHORT_MAX)) {
+ //
+ // If the interval is too low or too high, reset it to the default value.
+ //
+ idleInterval = CLASS_IDLE_INTERVAL;
+ }
+
+ fdoData->IdlePrioritySupported = TRUE;
+ KeInitializeSpinLock(&fdoData->IdleListLock);
+ KeInitializeTimer(&fdoData->IdleTimer);
+ KeInitializeDpc(&fdoData->IdleDpc, ClasspIdleTimerDpc, FdoExtension);
+ InitializeListHead(&fdoData->IdleIrpList);
+ fdoData->IdleTimerStarted = FALSE;
+ fdoData->IdleTimerInterval = (USHORT) (idleInterval / CLASS_IDLE_TIMER_TICKS);
+ fdoData->StarvationCount = CLASS_STARVATION_INTERVAL / fdoData->IdleTimerInterval;
+
+ //
+ // Due to the coarseness of the idle timer frequency, some variability in
+ // the idle interval will be tolerated such that it is the desired idle
+ // interval on average.
+ fdoData->IdleInterval =
+ (USHORT)(idleInterval - (fdoData->IdleTimerInterval / 2));
+
+ fdoData->IdleTimerTicks = 0;
+ fdoData->IdleTicks = 0;
+ fdoData->IdleIoCount = 0;
+ fdoData->ActiveIoCount = 0;
+ fdoData->ActiveIdleIoCount = 0;
+
+ ClassGetDeviceParameter(FdoExtension,
+ CLASSP_REG_SUBKEY_NAME,
+ CLASSP_REG_IDLE_ACTIVE_MAX,
+ &activeIdleIoMax);
+
+ activeIdleIoMax = max(activeIdleIoMax, 1);
+ activeIdleIoMax = min(activeIdleIoMax, USHORT_MAX);
+
+ fdoData->IdleActiveIoMax = (USHORT)activeIdleIoMax;
+
+ return;
+}
+
+/*++
+
+ClasspStartIdleTimer
+
+Routine Description:
+
+ Start the idle timer if not already running. Reset the
+ timer counters before starting the timer. Use the IdleInterval
+ in the private fdo data to setup the timer.
+
+Arguments:
+
+ FdoData - Pointer to the private fdo data
+
+ IdleInterval - Amount of time since the completion of the last non-idle request
+
+Return Value:
+
+ None
+
+--*/
+VOID
+ClasspStartIdleTimer(
+ IN PCLASS_PRIVATE_FDO_DATA FdoData,
+ IN ULONGLONG IdleInterval
+ )
+{
+ LARGE_INTEGER dueTime;
+ LONG mstotimer;
+ LONG timerStarted;
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_TIMER, "ClasspStartIdleTimer: Start idle timer\n"));
+
+ timerStarted = InterlockedCompareExchange(&FdoData->IdleTimerStarted, 1, 0);
+
+ if (!timerStarted) {
+
+ //
+ // Reset the anti-starvation timer tick counter and set the idle tick
+ // counter according to the actual amount of idle time. The latter is
+ // important to do to ensure that if the idle queue drains and the timer
+ // has to be stopped and started on the arrival of the next idle request,
+ // those requests don't get delayed unnecessarily due to IdleTicks not
+ // reflecting actual idle time.
+ //
+ FdoData->IdleTimerTicks = 0;
+ FdoData->IdleTicks = (ULONG)(IdleInterval / FdoData->IdleTimerInterval);
+
+ //
+ // convert milliseconds to a relative 100ns
+ //
+ mstotimer = (-10) * 1000;
+
+ //
+ // multiply the period
+ //
+ dueTime.QuadPart = Int32x32To64(FdoData->IdleTimerInterval, mstotimer);
+
+ KeSetTimerEx(&FdoData->IdleTimer,
+ dueTime,
+ FdoData->IdleTimerInterval,
+ &FdoData->IdleDpc);
+ }
+ return;
+}
+
+/*++
+
+ClasspStopIdleTimer
+
+Routine Description:
+
+ Stop the idle timer if running. Also reset the timer counters.
+
+Arguments:
+
+ FdoData - Pointer to the private fdo data
+
+Return Value:
+
+ None
+
+--*/
+VOID
+ClasspStopIdleTimer(
+ PCLASS_PRIVATE_FDO_DATA FdoData
+ )
+{
+ LONG timerStarted;
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_TIMER, "ClasspStopIdleTimer: Stop idle timer\n"));
+
+ timerStarted = InterlockedCompareExchange(&FdoData->IdleTimerStarted, 0, 1);
+
+ if (timerStarted) {
+ (VOID)KeCancelTimer(&FdoData->IdleTimer);
+ }
+ return;
+}
+
+/*++
+
+ClasspGetIdleTime
+
+Routine Description:
+
+ This routine returns how long it has been since the last non-idle request
+ completed by checking the actual time.
+
+Arguments:
+
+ FdoData - Pointer to the private fdo data
+
+Return Value:
+
+ The idle interval in ms.
+
+--*/
+ULONGLONG
+ClasspGetIdleTime (
+ IN PCLASS_PRIVATE_FDO_DATA FdoData
+ )
+{
+ ULONGLONG idleTime;
+ LARGE_INTEGER currentTime;
+ NTSTATUS status;
+
+ //
+ // If there are any outstanding non-idle requests, then there has been no
+ // idle time.
+ //
+ if (FdoData->ActiveIoCount > 0) {
+ return 0;
+ }
+
+ //
+ // Get the time difference between current time and last I/O
+ // complete time.
+ //
+ currentTime = ClasspGetCurrentTime(NULL);
+
+ status = RtlULongLongSub((ULONGLONG)currentTime.QuadPart,
+ (ULONGLONG)FdoData->LastIoTime.QuadPart,
+ &idleTime);
+
+ if (NT_SUCCESS(status)) {
+ //
+ // Convert the time to milliseconds.
+ //
+ idleTime = ClasspTimeDiffToMs(FdoData, idleTime);
+ } else {
+ //
+ // Failed to get time difference, assume enough time passed.
+ //
+ idleTime = FdoData->IdleInterval;
+ }
+
+ return idleTime;
+}
+
+/*++
+
+ClasspIdleTicksSufficient
+
+Routine Description:
+
+ This routine whether enough idle ticks have occurred since the completion of
+ the last non-idle request.
+
+Arguments:
+
+ FdoData - Pointer to the private fdo data
+
+Return Value:
+
+ TRUE if sufficient idle ticks have expired to issue the next idle request.
+
+--*/
+LOGICAL
+ClasspIdleTicksSufficient (
+ IN PCLASS_PRIVATE_FDO_DATA FdoData
+ )
+{
+ ULONGLONG idleInterval;
+
+ //
+ // If it has been more than enough idle timer ticks since the completion of
+ // the last non-idle request, enough idle time has passed.
+ //
+
+ if (FdoData->IdleTicks > CLASS_IDLE_TIMER_TICKS) {
+ return TRUE;
+ }
+
+ //
+ // If there have not been enough timer ticks, then there has not been
+ // enough idle time.
+ //
+ if (FdoData->IdleTicks < CLASS_IDLE_TIMER_TICKS) {
+ return FALSE;
+ }
+
+ //
+ // IdleTicks can reach CLASS_IDLE_TIMER_TICKS before FdoData->IdleInterval
+ // worth of time elapses from the completion of the last non-idle request.
+ // This can happen because when the idle timer is running, the last non-idle
+ // request can complete at any time in the middle of the timer period (half
+ // on average) so on the next timer expiration, IdleTicks will transition
+ // 0->1 without its full time having passed since the completion of the last
+ // non-idle request. So when IdleTicks is exactly CLASS_IDLE_TIMER_TICKS,
+ // explicitly check whether an idle request should be issued now or on the
+ // next timer expiration.
+ //
+ idleInterval = ClasspGetIdleTime(FdoData);
+
+ if (idleInterval >= FdoData->IdleInterval) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/*++
+
+ClasspIdleTimerDpc
+
+Routine Description:
+
+ Timer dpc function. This function will be called once every
+ IdleInterval. This will increment the IdleTicks and
+ if it goes above 1 (i.e., disk is in idle state) then
+ it will service an idle request.
+
+ This function will increment IdleTimerTicks if the IdleTicks
+ does not go above 1 (i.e., disk is not in idle state). When it
+ reaches the starvation idle count (1 second) it will process
+ one idle request.
+
+Arguments:
+
+ Dpc - Pointer to DPC object
+ Context - Pointer to the fdo device extension
+ SystemArgument1 - Not used
+ SystemArgument2 - Not used
+
+Return Value:
+
+ None
+
+--*/
+VOID
+ClasspIdleTimerDpc(
+ IN PKDPC Dpc,
+ IN PVOID Context,
+ IN PVOID SystemArgument1,
+ IN PVOID SystemArgument2
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Context;
+ PCLASS_PRIVATE_FDO_DATA fdoData;
+
+ UNREFERENCED_PARAMETER(Dpc);
+ UNREFERENCED_PARAMETER(SystemArgument1);
+ UNREFERENCED_PARAMETER(SystemArgument2);
+
+ if (fdoExtension == NULL) {
+ NT_ASSERT(fdoExtension != NULL);
+ return;
+ }
+
+ fdoData = fdoExtension->PrivateFdoData;
+
+ if ((fdoData->ActiveIoCount <= 0) &&
+ (++fdoData->IdleTicks >= CLASS_IDLE_TIMER_TICKS)) {
+
+ //
+ // If there are max active idle request, do not issue another one here.
+ //
+ if (fdoData->ActiveIdleIoCount >= fdoData->IdleActiveIoMax) {
+ return;
+ }
+
+ //
+ // Check whether enough idle time has passed since the last non-idle
+ // request has completed.
+ //
+
+ if (ClasspIdleTicksSufficient(fdoData)) {
+ //
+ // We are going to issue an idle request so reset the anti-starvation
+ // timer counter.
+ //
+ fdoData->IdleTimerTicks = 0;
+ ClasspServiceIdleRequest(fdoExtension, FALSE);
+ }
+ return;
+ }
+
+ //
+ // If the timer is running then there must be at least one idle priority I/O pending
+ //
+ if (++fdoData->IdleTimerTicks >= fdoData->StarvationCount) {
+ fdoData->IdleTimerTicks = 0;
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_TIMER, "ClasspIdleTimerDpc: Starvation timer. Send one idle request\n"));
+ ClasspServiceIdleRequest(fdoExtension, FALSE);
+ }
+ return;
+}
+
+/*++
+
+ClasspEnqueueIdleRequest
+
+Routine Description:
+
+ This function will insert the idle request into the list.
+ If the inserted reqeust is the first request then it will
+ start the timer.
+
+Arguments:
+
+ DeviceObject - Pointer to device object
+ Irp - Pointer to the idle I/O request packet
+
+Return Value:
+
+ NT status code.
+
+--*/
+NTSTATUS
+ClasspEnqueueIdleRequest(
+ PDEVICE_OBJECT DeviceObject,
+ PIRP Irp
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
+ KIRQL oldIrql;
+ BOOLEAN issueRequest = TRUE;
+ ULONGLONG idleInterval;
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_TIMER, "ClasspEnqueueIdleRequest: Queue idle request %p\n", Irp));
+
+ IoMarkIrpPending(Irp);
+
+ //
+ // Get the time difference between current time and last non-idle request
+ // complete time. If the there has been enough idle time, then issue the
+ // request (unless other factors prevent us from doing so below) and set the
+ // idle time such that we starting the timer below, it would start off with
+ // enough idle ticks.
+ //
+ idleInterval = ClasspGetIdleTime(fdoData);
+
+ if (idleInterval >= fdoData->IdleInterval) {
+ idleInterval = fdoData->IdleTimerInterval * CLASS_IDLE_TIMER_TICKS;
+ } else {
+ issueRequest = FALSE;
+ }
+
+ //
+ // If there are already max active idle requests in the port driver, then
+ // queue this idle request.
+ //
+ if (fdoData->ActiveIdleIoCount >= fdoData->IdleActiveIoMax) {
+ issueRequest = FALSE;
+ }
+
+ TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_TIMER, "ClasspEnqueueIdleRequest: Diff time %I64d\n", idleInterval));
+
+ KeAcquireSpinLock(&fdoData->IdleListLock, &oldIrql);
+ if (IsListEmpty(&fdoData->IdleIrpList)) {
+ NT_ASSERT(fdoData->IdleIoCount == 0);
+ }
+ InsertTailList(&fdoData->IdleIrpList, &Irp->Tail.Overlay.ListEntry);
+
+
+ fdoData->IdleIoCount++;
+ if (!fdoData->IdleTimerStarted) {
+ ClasspStartIdleTimer(fdoData, idleInterval);
+ }
+
+ if (fdoData->IdleIoCount != 1) {
+ issueRequest = FALSE;
+ }
+
+
+ KeReleaseSpinLock(&fdoData->IdleListLock, oldIrql);
+
+ if (issueRequest) {
+ ClasspServiceIdleRequest(fdoExtension, FALSE);
+ }
+
+ return STATUS_PENDING;
+}
+
+/*++
+
+ClasspDequeueIdleRequest
+
+Routine Description:
+
+ This function will remove the next idle request from the list.
+ If there are no requests in the queue, then it will return NULL.
+
+Arguments:
+
+ FdoExtension - Pointer to the functional device extension
+
+Return Value:
+
+ Pointer to removed IRP
+
+--*/
+PIRP
+ClasspDequeueIdleRequest(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ )
+{
+ PCLASS_PRIVATE_FDO_DATA fdoData = FdoExtension->PrivateFdoData;
+ PLIST_ENTRY listEntry = NULL;
+ PIRP irp = NULL;
+ KIRQL oldIrql;
+
+ KeAcquireSpinLock(&fdoData->IdleListLock, &oldIrql);
+
+ if (fdoData->IdleIoCount > 0) {
+ listEntry = RemoveHeadList(&fdoData->IdleIrpList);
+ //
+ // Make sure we actaully removed a request from the list
+ //
+ NT_ASSERT(listEntry != &fdoData->IdleIrpList);
+ //
+ // Decrement the idle I/O count.
+ //
+ fdoData->IdleIoCount--;
+ //
+ // Stop the timer on last request
+ //
+ if (fdoData->IdleIoCount == 0) {
+ ClasspStopIdleTimer(fdoData);
+ }
+ irp = CONTAINING_RECORD(listEntry, IRP, Tail.Overlay.ListEntry);
+ NT_ASSERT(irp->Type == IO_TYPE_IRP);
+
+
+ InitializeListHead(&irp->Tail.Overlay.ListEntry);
+ }
+
+ KeReleaseSpinLock(&fdoData->IdleListLock, oldIrql);
+ return irp;
+}
+
+/*++
+
+ClasspCompleteIdleRequest
+
+Routine Description:
+
+ This function will be called every time an idle request is completed.
+ This will call ClasspServiceIdleRequest to process any other pending idle requests.
+
+Arguments:
+
+ FdoExtension - Pointer to the device extension
+
+Return Value:
+
+ None
+
+--*/
+VOID
+ClasspCompleteIdleRequest(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ )
+{
+ PCLASS_PRIVATE_FDO_DATA fdoData = FdoExtension->PrivateFdoData;
+
+ //
+ // Issue the next idle request if there are any left in the queue, there are
+ // no non-idle requests outstanding, there are less than max idle requests
+ // outstanding, and it has been long enough since the completion of the last
+ // non-idle request.
+ //
+ if ((fdoData->IdleIoCount > 0) &&
+ (fdoData->ActiveIdleIoCount < fdoData->IdleActiveIoMax) &&
+ (fdoData->ActiveIoCount <= 0) &&
+ (ClasspIdleTicksSufficient(fdoData))) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_TIMER, "ClasspCompleteIdleRequest: Service next idle reqeusts\n"));
+ ClasspServiceIdleRequest(FdoExtension, TRUE);
+ }
+
+ return;
+}
+
+/*++
+
+ClasspServiceIdleRequest
+
+Routine Description:
+
+ Remove the next pending idle request from the queue and process it.
+ If a request was removed then it will be processed otherwise it will
+ just return.
+
+Arguments:
+
+ FdoExtension - Pointer to the device extension
+ PostToDpc - Flag to pass to ServiceTransferRequest to indicate if request must be posted to a DPC
+
+Return Value:
+
+ None
+
+--*/
+VOID
+ClasspServiceIdleRequest(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ BOOLEAN PostToDpc
+ )
+{
+ PIRP irp;
+
+ irp = ClasspDequeueIdleRequest(FdoExtension);
+ if (irp != NULL) {
+ ServiceTransferRequest(FdoExtension->DeviceObject, irp, PostToDpc);
+ }
+ return;
+}
+
+
+
diff --git a/storage/class/classpnp/src/create.c b/storage/class/classpnp/src/create.c
new file mode 100644
index 00000000..f871ab26
--- /dev/null
+++ b/storage/class/classpnp/src/create.c
@@ -0,0 +1,1018 @@
+/*++
+
+Copyright (C) Microsoft Corporation, 1991 - 2010
+
+Module Name:
+
+ class.c
+
+Abstract:
+
+ SCSI class driver routines
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+
+Revision History:
+
+--*/
+
+#define CLASS_INIT_GUID 0
+#include "classp.h"
+#include "debug.h"
+
+#ifdef DEBUG_USE_WPP
+#include "create.tmh"
+#endif
+
+ULONG BreakOnClose = 0;
+
+const PCSZ LockTypeStrings[] = {
+ "Simple",
+ "Secure",
+ "Internal"
+};
+
+
+VOID
+ClasspCleanupDisableMcn(
+ IN PFILE_OBJECT_EXTENSION FsContext
+ );
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE, ClassCreateClose)
+#pragma alloc_text(PAGE, ClasspCreateClose)
+#pragma alloc_text(PAGE, ClasspCleanupProtectedLocks)
+#pragma alloc_text(PAGE, ClasspEjectionControl)
+#pragma alloc_text(PAGE, ClasspCleanupDisableMcn)
+#pragma alloc_text(PAGE, ClassGetFsContext)
+#endif
+
+NTSTATUS
+ClassCreateClose(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ SCSI class driver create and close routine. This is called by the I/O system
+ when the device is opened or closed.
+
+Arguments:
+
+ DriverObject - Pointer to driver object created by system.
+
+ Irp - IRP involved.
+
+Return Value:
+
+ Device-specific drivers return value or STATUS_SUCCESS.
+
+--*/
+
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ ULONG removeState;
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ //
+ // If we're getting a close request then we know the device object hasn't
+ // been completely destroyed. Let the driver cleanup if necessary.
+ //
+
+ removeState = ClassAcquireRemoveLock(DeviceObject, Irp);
+
+ //
+ // Invoke the device-specific routine, if one exists. Otherwise complete
+ // with SUCCESS
+ //
+
+ if((removeState == NO_REMOVE) ||
+ IS_CLEANUP_REQUEST(IoGetCurrentIrpStackLocation(Irp)->MajorFunction)) {
+
+ status = ClasspCreateClose(DeviceObject, Irp);
+
+ if((NT_SUCCESS(status)) &&
+ (commonExtension->DevInfo->ClassCreateClose)) {
+
+ return commonExtension->DevInfo->ClassCreateClose(DeviceObject, Irp);
+ }
+
+ } else {
+ status = STATUS_DEVICE_DOES_NOT_EXIST;
+ }
+
+ Irp->IoStatus.Status = status;
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ return status;
+}
+
+
+NTSTATUS
+ClasspCreateClose(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+/*++
+
+Routine Description:
+
+ This routine will handle create/close operations for a given classpnp
+ device if the class driver doesn't supply it's own handler. If there
+ is a file object supplied for our driver (if it's a FO_DIRECT_DEVICE_OPEN
+ file object) then it will initialize a file extension on create or destroy
+ the extension on a close.
+
+Arguments:
+
+ DeviceObject - the device object being opened or closed.
+
+ Irp - the create/close irp
+
+Return Value:
+
+ status
+
+--*/
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
+
+ PFILE_OBJECT fileObject = irpStack->FileObject;
+
+ NTSTATUS status = STATUS_SUCCESS;
+
+ PAGED_CODE();
+
+
+ //
+ // ISSUE-2000/3/28-henrygab - if lower stack fails create/close, we end up
+ // in an inconsistent state. re-write to verify all args and allocate all
+ // required resources, then pass the irp down, then complete the
+ // transaction. this is because we also cannot forward the irp, then fail
+ // it after it has succeeded a lower-level driver.
+ //
+
+ if(irpStack->MajorFunction == IRP_MJ_CREATE) {
+
+ PIO_SECURITY_CONTEXT securityContext =
+ irpStack->Parameters.Create.SecurityContext;
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
+ "ClasspCREATEClose: create received for device %p\n",
+ DeviceObject));
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
+ "ClasspCREATEClose: desired access %lx\n",
+ securityContext->DesiredAccess));
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
+ "ClasspCREATEClose: file object %p\n",
+ irpStack->FileObject));
+
+ NT_ASSERT(BreakOnClose == FALSE);
+
+ if(irpStack->FileObject != NULL) {
+
+ PFILE_OBJECT_EXTENSION fsContext;
+
+ //
+ // Allocate our own file object extension for this device object.
+ //
+
+ status = AllocateDictionaryEntry(
+ &commonExtension->FileObjectDictionary,
+ (ULONGLONG) irpStack->FileObject,
+ sizeof(FILE_OBJECT_EXTENSION),
+ CLASS_TAG_FILE_OBJECT_EXTENSION,
+ &fsContext);
+
+ if(NT_SUCCESS(status)) {
+
+ RtlZeroMemory(fsContext,
+ sizeof(FILE_OBJECT_EXTENSION));
+
+ fsContext->FileObject = irpStack->FileObject;
+ fsContext->DeviceObject = DeviceObject;
+ } else if (status == STATUS_OBJECT_NAME_COLLISION) {
+ status = STATUS_SUCCESS;
+ }
+ }
+
+ } else {
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
+ "ClasspCreateCLOSE: close received for device %p\n",
+ DeviceObject));
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
+ "ClasspCreateCLOSE: file object %p\n",
+ fileObject));
+
+ if(irpStack->FileObject != NULL) {
+
+ PFILE_OBJECT_EXTENSION fsContext =
+ ClassGetFsContext(commonExtension, irpStack->FileObject);
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
+ "ClasspCreateCLOSE: file extension %p\n",
+ fsContext));
+
+ if(fsContext != NULL) {
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
+ "ClasspCreateCLOSE: extension is ours - "
+ "freeing\n"));
+ NT_ASSERT(BreakOnClose == FALSE);
+
+ ClasspCleanupProtectedLocks(fsContext);
+
+ ClasspCleanupDisableMcn(fsContext);
+
+ FreeDictionaryEntry(&(commonExtension->FileObjectDictionary),
+ fsContext);
+ }
+ }
+ }
+
+ //
+ // Notify the lower levels about the create or close operation - give them
+ // a chance to cleanup too.
+ //
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
+ "ClasspCreateClose: %s for devobj %p\n",
+ (NT_SUCCESS(status) ? "Success" : "FAILED"),
+ DeviceObject));
+
+
+ if(NT_SUCCESS(status)) {
+
+ KEVENT event;
+
+ //
+ // Set up the event to wait on
+ //
+
+ KeInitializeEvent(&event, SynchronizationEvent, FALSE);
+
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+ IoSetCompletionRoutine( Irp, ClassSignalCompletion, &event,
+ TRUE, TRUE, TRUE);
+
+ status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+
+ if(status == STATUS_PENDING) {
+ (VOID)KeWaitForSingleObject(&event,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL);
+ status = Irp->IoStatus.Status;
+ }
+
+ if (!NT_SUCCESS(status)) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT,
+ "ClasspCreateClose: Lower driver failed, but we "
+ "succeeded. This is a problem, lock counts will be "
+ "out of sync between levels.\n"));
+ }
+
+ }
+
+
+ return status;
+}
+
+
+VOID
+ClasspCleanupProtectedLocks(
+ IN PFILE_OBJECT_EXTENSION FsContext
+ )
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension =
+ FsContext->DeviceObject->DeviceExtension;
+
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension =
+ commonExtension->PartitionZeroExtension;
+
+ ULONG newDeviceLockCount = 1;
+
+ PAGED_CODE();
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
+ "ClasspCleanupProtectedLocks called for %p\n",
+ FsContext->DeviceObject));
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
+ "ClasspCleanupProtectedLocks - FsContext %p is locked "
+ "%d times\n", FsContext, FsContext->LockCount));
+
+ NT_ASSERT(BreakOnClose == FALSE);
+
+ //
+ // Synchronize with ejection and ejection control requests.
+ //
+
+ KeEnterCriticalRegion();
+ (VOID)KeWaitForSingleObject(&(fdoExtension->EjectSynchronizationEvent),
+ UserRequest,
+ KernelMode,
+ FALSE,
+ NULL);
+
+ //
+ // For each secure lock on this handle decrement the secured lock count
+ // for the FDO. Keep track of the new value.
+ //
+
+ if (FsContext->LockCount != 0) {
+
+ do {
+
+ InterlockedDecrement((volatile LONG *)&FsContext->LockCount);
+
+ newDeviceLockCount =
+ InterlockedDecrement(&fdoExtension->ProtectedLockCount);
+
+ } while (FsContext->LockCount > 0);
+
+ //
+ // If the new lock count has been dropped to zero then issue a lock
+ // command to the device.
+ //
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
+ "ClasspCleanupProtectedLocks: FDO secured lock count = %d "
+ "lock count = %d\n",
+ fdoExtension->ProtectedLockCount,
+ fdoExtension->LockCount));
+
+ if ((newDeviceLockCount == 0) && (fdoExtension->LockCount == 0)) {
+
+ SCSI_REQUEST_BLOCK srb = {0};
+ UCHAR srbExBuffer[CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE] = {0};
+ PSTORAGE_REQUEST_BLOCK srbEx = (PSTORAGE_REQUEST_BLOCK)srbExBuffer;
+ PCDB cdb = NULL;
+ NTSTATUS status;
+ PSCSI_REQUEST_BLOCK srbPtr;
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
+ "ClasspCleanupProtectedLocks: FDO lock count dropped "
+ "to zero\n"));
+
+ if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ #pragma prefast(suppress:26015, "InitializeStorageRequestBlock ensures buffer access is bounded")
+ status = InitializeStorageRequestBlock(srbEx,
+ STORAGE_ADDRESS_TYPE_BTL8,
+ sizeof(srbExBuffer),
+ 1,
+ SrbExDataTypeScsiCdb16);
+ if (NT_SUCCESS(status)) {
+ srbEx->TimeOutValue = fdoExtension->TimeOutValue;
+ SrbSetCdbLength(srbEx, 6);
+ cdb = SrbGetCdb(srbEx);
+ srbPtr = (PSCSI_REQUEST_BLOCK)srbEx;
+ } else {
+ //
+ // Should not happen. Revert to legacy SRB.
+ //
+ NT_ASSERT(FALSE);
+ srb.TimeOutValue = fdoExtension->TimeOutValue;
+ srb.CdbLength = 6;
+ cdb = (PCDB) &(srb.Cdb);
+ srbPtr = &srb;
+ }
+
+ } else {
+
+ srb.TimeOutValue = fdoExtension->TimeOutValue;
+ srb.CdbLength = 6;
+ cdb = (PCDB) &(srb.Cdb);
+ srbPtr = &srb;
+
+ }
+
+ cdb->MEDIA_REMOVAL.OperationCode = SCSIOP_MEDIUM_REMOVAL;
+
+ //
+ // TRUE - prevent media removal.
+ // FALSE - allow media removal.
+ //
+
+ cdb->MEDIA_REMOVAL.Prevent = FALSE;
+
+ status = ClassSendSrbSynchronous(fdoExtension->DeviceObject,
+ srbPtr,
+ NULL,
+ 0,
+ FALSE);
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
+ "ClasspCleanupProtectedLocks: unlock request to drive "
+ "returned status %lx\n", status));
+ }
+ }
+
+ KeSetEvent(&fdoExtension->EjectSynchronizationEvent,
+ IO_NO_INCREMENT,
+ FALSE);
+ KeLeaveCriticalRegion();
+ return;
+}
+
+
+VOID
+ClasspCleanupDisableMcn(
+ IN PFILE_OBJECT_EXTENSION FsContext
+ )
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension =
+ FsContext->DeviceObject->DeviceExtension;
+
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension =
+ commonExtension->PartitionZeroExtension;
+
+ PAGED_CODE();
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+ "ClasspCleanupDisableMcn called for %p\n",
+ FsContext->DeviceObject));
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
+ "ClasspCleanupDisableMcn - FsContext %p is disabled "
+ "%d times\n", FsContext, FsContext->McnDisableCount));
+
+ //
+ // For each secure lock on this handle decrement the secured lock count
+ // for the FDO. Keep track of the new value.
+ //
+
+ while(FsContext->McnDisableCount != 0) {
+ FsContext->McnDisableCount--;
+ ClassEnableMediaChangeDetection(fdoExtension);
+ }
+
+ return;
+}
+
+
+#if 1
+/*
+ * ISSUE: REMOVE this old function implementation as soon as the
+ * boottime pagefile problems with the new one (below)
+ * are resolved.
+ */
+NTSTATUS
+ClasspEjectionControl(
+ IN PDEVICE_OBJECT Fdo,
+ IN PIRP Irp,
+ IN MEDIA_LOCK_TYPE LockType,
+ IN BOOLEAN Lock
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension = Fdo->DeviceExtension;
+ PCOMMON_DEVICE_EXTENSION commonExtension =
+ (PCOMMON_DEVICE_EXTENSION) FdoExtension;
+
+ PFILE_OBJECT_EXTENSION fsContext = NULL;
+ NTSTATUS status;
+ PSCSI_REQUEST_BLOCK srb = NULL;
+ BOOLEAN countChanged = FALSE;
+
+ PAGED_CODE();
+
+ /*
+ * Ensure that the user thread is not suspended while we are holding EjectSynchronizationEvent.
+ */
+ KeEnterCriticalRegion();
+
+ status = KeWaitForSingleObject(
+ &(FdoExtension->EjectSynchronizationEvent),
+ UserRequest,
+ KernelMode,
+ FALSE,
+ NULL);
+
+ NT_ASSERT(status == STATUS_SUCCESS);
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+ "ClasspEjectionControl: "
+ "Received request for %s lock type\n",
+ LockTypeStrings[LockType]
+ ));
+
+ try {
+ PCDB cdb = NULL;
+
+ //
+ // Determine if this is a "secured" request.
+ //
+
+ if (LockType == SecureMediaLock) {
+
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
+ PFILE_OBJECT fileObject = irpStack->FileObject;
+
+ //
+ // Make sure that the file object we are supplied has a
+ // proper FsContext before we try doing a secured lock.
+ //
+
+ if (fileObject != NULL) {
+ fsContext = ClassGetFsContext(commonExtension, fileObject);
+ }
+
+ if (fsContext == NULL) {
+
+ //
+ // This handle isn't setup correctly. We can't let the
+ // operation go.
+ //
+
+ status = STATUS_INVALID_PARAMETER;
+ leave;
+ }
+ }
+
+ if (Lock) {
+
+ //
+ // This is a lock command. Reissue the command in case bus or
+ // device was reset and the lock was cleared.
+ // note: may need to decrement count if actual lock operation
+ // failed....
+ //
+
+ switch (LockType) {
+
+ case SimpleMediaLock: {
+ FdoExtension->LockCount++;
+ countChanged = TRUE;
+ break;
+ }
+
+ case SecureMediaLock: {
+ fsContext->LockCount++;
+ FdoExtension->ProtectedLockCount++;
+ countChanged = TRUE;
+ break;
+ }
+
+ case InternalMediaLock: {
+ FdoExtension->InternalLockCount++;
+ countChanged = TRUE;
+ break;
+ }
+ }
+
+ } else {
+
+ //
+ // This is an unlock command. If it's a secured one then make sure
+ // the caller has a lock outstanding or return an error.
+ // note: may need to re-increment the count if actual unlock
+ // operation fails....
+ //
+
+ switch (LockType) {
+
+ case SimpleMediaLock: {
+ if(FdoExtension->LockCount != 0) {
+ FdoExtension->LockCount--;
+ countChanged = TRUE;
+ }
+ break;
+ }
+
+ case SecureMediaLock: {
+ if(fsContext->LockCount == 0) {
+ status = STATUS_INVALID_DEVICE_STATE;
+ leave;
+ }
+ fsContext->LockCount--;
+ FdoExtension->ProtectedLockCount--;
+ countChanged = TRUE;
+ break;
+ }
+
+ case InternalMediaLock: {
+ NT_ASSERT(FdoExtension->InternalLockCount != 0);
+ FdoExtension->InternalLockCount--;
+ countChanged = TRUE;
+ break;
+ }
+ }
+
+ //
+ // We only send an unlock command to the drive if both the
+ // secured and unsecured lock counts have dropped to zero.
+ //
+
+ if ((FdoExtension->ProtectedLockCount != 0) ||
+ (FdoExtension->InternalLockCount != 0) ||
+ (FdoExtension->LockCount != 0)) {
+
+ status = STATUS_SUCCESS;
+ leave;
+ }
+ }
+
+ status = STATUS_SUCCESS;
+ if (TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)) {
+
+ srb = (PSCSI_REQUEST_BLOCK)ClasspAllocateSrb(FdoExtension);
+
+ if (srb == NULL) {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ leave;
+ }
+
+ if (FdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+
+ //
+ // NOTE - this is based on size used in ClasspAllocateSrb
+ //
+
+ status = InitializeStorageRequestBlock((PSTORAGE_REQUEST_BLOCK)srb,
+ STORAGE_ADDRESS_TYPE_BTL8,
+ CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE,
+ 1,
+ SrbExDataTypeScsiCdb16);
+ if (!NT_SUCCESS(status)) {
+ NT_ASSERT(FALSE);
+ leave;
+ }
+
+ } else {
+ RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
+ }
+
+ SrbSetCdbLength(srb, 6);
+ cdb = SrbGetCdb(srb);
+ NT_ASSERT(cdb != NULL);
+
+ cdb->MEDIA_REMOVAL.OperationCode = SCSIOP_MEDIUM_REMOVAL;
+
+ //
+ // TRUE - prevent media removal.
+ // FALSE - allow media removal.
+ //
+
+ cdb->MEDIA_REMOVAL.Prevent = Lock;
+
+ //
+ // Set timeout value.
+ //
+
+ SrbSetTimeOutValue(srb, FdoExtension->TimeOutValue);
+
+ //
+ // The actual lock operation on the device isn't so important
+ // as the internal lock counts. Ignore failures.
+ //
+
+ status = ClassSendSrbSynchronous(FdoExtension->DeviceObject,
+ srb,
+ NULL,
+ 0,
+ FALSE);
+ }
+
+ } finally {
+
+ if (!NT_SUCCESS(status)) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+ "ClasspEjectionControl: FAILED status %x -- "
+ "reverting lock counts\n", status));
+
+ if (countChanged) {
+
+ //
+ // have to revert to previous counts if the
+ // lock/unlock operation actually failed.
+ //
+
+ if (Lock) {
+
+ switch (LockType) {
+
+ case SimpleMediaLock: {
+ FdoExtension->LockCount--;
+ break;
+ }
+
+ case SecureMediaLock: {
+ fsContext->LockCount--;
+ FdoExtension->ProtectedLockCount--;
+ break;
+ }
+
+ case InternalMediaLock: {
+ FdoExtension->InternalLockCount--;
+ break;
+ }
+ }
+
+ } else {
+
+ switch (LockType) {
+
+ case SimpleMediaLock: {
+ FdoExtension->LockCount++;
+ break;
+ }
+
+ case SecureMediaLock: {
+ fsContext->LockCount++;
+ FdoExtension->ProtectedLockCount++;
+ break;
+ }
+
+ case InternalMediaLock: {
+ FdoExtension->InternalLockCount++;
+ break;
+ }
+ }
+ }
+
+ }
+
+ } else {
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+ "ClasspEjectionControl: Succeeded\n"));
+
+ }
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
+ "ClasspEjectionControl: "
+ "Current Counts: Internal: %x Secure: %x Simple: %x\n",
+ FdoExtension->InternalLockCount,
+ FdoExtension->ProtectedLockCount,
+ FdoExtension->LockCount
+ ));
+
+ KeSetEvent(&(FdoExtension->EjectSynchronizationEvent),
+ IO_NO_INCREMENT,
+ FALSE);
+ KeLeaveCriticalRegion();
+
+ if (srb) {
+ ClassFreeOrReuseSrb(FdoExtension, srb);
+ }
+
+ }
+ return status;
+}
+
+#else
+
+/*
+ * ISSUE: RESTORE this (see above)
+ * This is a new implementation of the function that doesn't thrash memory
+ * or depend on the srbLookasideList.
+ * HOWEVER, it seems to cause pagefile initialization to fail during boot
+ * for some reason. Need to resolve this before switching to this function.
+ */
+NTSTATUS
+ClasspEjectionControl(
+ IN PDEVICE_OBJECT Fdo,
+ IN PIRP Irp,
+ IN MEDIA_LOCK_TYPE LockType,
+ IN BOOLEAN Lock
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
+ PFILE_OBJECT_EXTENSION fsContext;
+ BOOLEAN fileHandleOk = TRUE;
+ BOOLEAN countChanged = FALSE;
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ status = KeWaitForSingleObject(
+ &fdoExt->EjectSynchronizationEvent,
+ UserRequest,
+ KernelMode,
+ FALSE,
+ NULL);
+ NT_ASSERT(status == STATUS_SUCCESS);
+
+ /*
+ * If this is a "secured" request, we have to make sure
+ * that the file handle is valid.
+ */
+ if (LockType == SecureMediaLock){
+ PIO_STACK_LOCATION thisSp = IoGetCurrentIrpStackLocation(Irp);
+
+ /*
+ * Make sure that the file object we are supplied has a
+ * proper FsContext before we try doing a secured lock.
+ */
+ if (thisSp->FileObject){
+ PCOMMON_DEVICE_EXTENSION commonExt = (PCOMMON_DEVICE_EXTENSION)fdoExt;
+ fsContext = ClassGetFsContext(commonExt, thisSp->FileObject);
+ }
+ else {
+ fsContext = NULL;
+ }
+
+ if (!fsContext){
+ NT_ASSERT(fsContext);
+ fileHandleOk = FALSE;
+ }
+ }
+
+ if (fileHandleOk){
+
+ /*
+ * Adjust the lock counts and make sure they make sense.
+ */
+ status = STATUS_SUCCESS;
+ if (Lock){
+ switch(LockType) {
+ case SimpleMediaLock:
+ fdoExt->LockCount++;
+ countChanged = TRUE;
+ break;
+ case SecureMediaLock:
+ fsContext->LockCount++;
+ fdoExt->ProtectedLockCount++;
+ countChanged = TRUE;
+ break;
+ case InternalMediaLock:
+ fdoExt->InternalLockCount++;
+ countChanged = TRUE;
+ break;
+ }
+ }
+ else {
+ /*
+ * This is an unlock command. If it's a secured one then make sure
+ * the caller has a lock outstanding or return an error.
+ */
+ switch (LockType){
+ case SimpleMediaLock:
+ if (fdoExt->LockCount > 0){
+ fdoExt->LockCount--;
+ countChanged = TRUE;
+ }
+ else {
+ NT_ASSERT(fdoExt->LockCount > 0);
+ status = STATUS_INTERNAL_ERROR;
+ }
+ break;
+ case SecureMediaLock:
+ if (fsContext->LockCount > 0){
+ NT_ASSERT(fdoExt->ProtectedLockCount > 0);
+ fsContext->LockCount--;
+ fdoExt->ProtectedLockCount--;
+ countChanged = TRUE;
+ }
+ else {
+ NT_ASSERT(fsContext->LockCount > 0);
+ status = STATUS_INVALID_DEVICE_STATE;
+ }
+ break;
+ case InternalMediaLock:
+ NT_ASSERT(fdoExt->InternalLockCount > 0);
+ fdoExt->InternalLockCount--;
+ countChanged = TRUE;
+ break;
+ }
+ }
+
+ if (NT_SUCCESS(status)){
+ /*
+ * We only send an unlock command to the drive if
+ * all the lock counts have dropped to zero.
+ */
+ if (!Lock &&
+ (fdoExt->ProtectedLockCount ||
+ fdoExt->InternalLockCount ||
+ fdoExt->LockCount)){
+
+ /*
+ * The lock count is still positive, so don't unlock yet.
+ */
+ status = STATUS_SUCCESS;
+ }
+ else if (!TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)) {
+ /*
+ * The device isn't removable media. don't send a cmd.
+ */
+ status = STATUS_SUCCESS;
+ }
+ else {
+ TRANSFER_PACKET *pkt;
+
+ pkt = DequeueFreeTransferPacket(Fdo, TRUE);
+ if (pkt){
+ KEVENT event;
+
+ /*
+ * Store the number of packets servicing the irp (one)
+ * inside the original IRP. It will be used to counted down
+ * to zero when the packet completes.
+ * Initialize the original IRP's status to success.
+ * If the packet fails, we will set it to the error status.
+ */
+ Irp->Tail.Overlay.DriverContext[0] = LongToPtr(1);
+ Irp->IoStatus.Status = STATUS_SUCCESS;
+
+ /*
+ * Set this up as a SYNCHRONOUS transfer, submit it,
+ * and wait for the packet to complete. The result
+ * status will be written to the original irp.
+ */
+ KeInitializeEvent(&event, SynchronizationEvent, FALSE);
+ SetupEjectionTransferPacket(pkt, Lock, &event, Irp);
+ SubmitTransferPacket(pkt);
+ (VOID)KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
+ status = Irp->IoStatus.Status;
+ }
+ else {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+ }
+ }
+ }
+ else {
+ status = STATUS_INVALID_PARAMETER;
+ }
+
+ if (!NT_SUCCESS(status) && countChanged) {
+
+ //
+ // have to revert to previous counts if the
+ // lock/unlock operation actually failed.
+ //
+
+ if(Lock) {
+
+ switch(LockType) {
+
+ case SimpleMediaLock: {
+ FdoExtension->LockCount--;
+ break;
+ }
+
+ case SecureMediaLock: {
+ fsContext->LockCount--;
+ FdoExtension->ProtectedLockCount--;
+ break;
+ }
+
+ case InternalMediaLock: {
+ FdoExtension->InternalLockCount--;
+ break;
+ }
+ }
+
+ } else {
+
+ switch(LockType) {
+
+ case SimpleMediaLock: {
+ FdoExtension->LockCount++;
+ break;
+ }
+
+ case SecureMediaLock: {
+ fsContext->LockCount++;
+ FdoExtension->ProtectedLockCount++;
+ break;
+ }
+
+ case InternalMediaLock: {
+ FdoExtension->InternalLockCount++;
+ break;
+ }
+ }
+ }
+ }
+
+
+
+ KeSetEvent(&fdoExt->EjectSynchronizationEvent, IO_NO_INCREMENT, FALSE);
+
+ return status;
+}
+#endif
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+PFILE_OBJECT_EXTENSION
+ClassGetFsContext(
+ _In_ PCOMMON_DEVICE_EXTENSION CommonExtension,
+ _In_ PFILE_OBJECT FileObject
+ )
+{
+ PAGED_CODE();
+ return GetDictionaryEntry(&(CommonExtension->FileObjectDictionary),
+ (ULONGLONG) FileObject);
+}
diff --git a/storage/class/classpnp/src/data.c b/storage/class/classpnp/src/data.c
new file mode 100644
index 00000000..0faa333f
--- /dev/null
+++ b/storage/class/classpnp/src/data.c
@@ -0,0 +1,222 @@
+/*++
+
+Copyright (C) Microsoft Corporation, 1991 - 2010
+
+Module Name:
+
+ disk.c
+
+Abstract:
+
+ SCSI disk class driver
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+Revision History:
+
+--*/
+
+#include "classp.h"
+
+
+/*
+ * Entry in static list used by debug extension to quickly find all class FDOs.
+ */
+LIST_ENTRY AllFdosList = {&AllFdosList, &AllFdosList};
+
+#ifdef ALLOC_DATA_PRAGMA
+ #pragma data_seg("PAGEDATA")
+#endif
+
+/*
+#define FDO_HACK_CANNOT_LOCK_MEDIA (0x00000001)
+#define FDO_HACK_GESN_IS_BAD (0x00000002)
+#define FDO_HACK_NO_SYNC_CACHE (0x00000004)
+#define FDO_HACK_NO_RESERVE6 (0x00000008)
+#define FDO_HACK_GESN_IGNORE_OPCHANGE (0x00000010)
+*/
+
+CLASSPNP_SCAN_FOR_SPECIAL_INFO ClassBadItems[] = { // Type (HH, slim) + WHQL Date, if known
+ { "" , "MITSUMI CD-ROM FX240" , NULL , 0x02 },
+ { "" , "MITSUMI CD-ROM FX320" , NULL , 0x02 },
+ { "" , "MITSUMI CD-ROM FX322" , NULL , 0x02 },
+ { "" , "TEAC DV-28E-A" , "2.0A", 0x02 },
+ { "" , "HP CD-Writer cd16h" , "Q000", 0x02 },
+ { "" , "_NEC NR-7800A" , "1.33", 0x02 },
+ { "" , "COMPAQ CRD-8481B" , NULL , 0x04 },
+ // The following is a list of device that report too many OpChange/Add events.
+ // They require ignoring (or not sending) the OpChange flag in the GESN command.
+ // This list contains vendor ID and product ID as separate strings for USB/1394 interface.
+ { "HL-DT-ST", "DVDRAM GMA-4020B" , NULL , 0x10 }, // hh , 2002/04/22
+ { "HL-DT-ST", "DVD-RW GCA-4020B" , NULL , 0x10 }, // hh , 2002/05/14
+ { "HL-DT-ST", "DVDRAM GSA-4040B" , NULL , 0x10 }, // hh , 2003/05/06
+ { "HL-DT-ST", "DVDRAM GMA-4040B" , NULL , 0x10 }, // hh , 2003/07/27
+ { "HL-DT-ST", "DVD-RW GWA-4040B" , NULL , 0x10 }, // hh , 2003/11/18
+ { "HL-DT-ST", "DVDRAM GSA-4081B" , NULL , 0x10 }, // hh , 2003/11/06
+ { "HL-DT-ST", "DVDRAM GSA-4082B" , NULL , 0x10 }, // hh , 2004/01/27
+ { "HL-DT-ST", "DVD-RW GWA-4082B" , NULL , 0x10 }, // hh , 2004/03/11
+ { "HL-DT-ST", "DVDRAM GSA-4120B" , NULL , 0x10 }, // hh , 2004/05/16
+ { "HL-DT-ST", "DVD+RW GRA-4120B" , NULL , 0x10 }, // hh , 2004/04/28
+ { "HL-DT-ST", "DVDRAM GSA-4160B" , NULL , 0x10 }, // hh , 2004/08/12
+ { "HL-DT-ST", "DVD-RW GWA-4160B" , NULL , 0x10 }, // hh , 2004/08/24
+ { "HL-DT-ST", "DVDRAM GSA-4163B" , NULL , 0x10 }, // hh , 2004/11/09
+ { "HL-DT-ST", "DVD-RW GWA-4163B" , NULL , 0x10 }, // hh , 2004/12/29
+ { "HL-DT-ST", "DVDRAM GSA-4165B" , NULL , 0x10 }, // hh , 2005/06/09
+ { "HL-DT-ST", "DVDRAM_GSA-4165B" , NULL , 0x10 }, // hh , 2005/06/28
+ { "HL-DT-ST", "DVD-RW GWA-4165B" , NULL , 0x10 }, // hh , 2005/08/23
+ { "HL-DT-ST", "DVDRAM GSA-4167B" , NULL , 0x10 }, // hh , 2005/07/01
+ { "HL-DT-ST", "DVDRAM GSA-H10N" , NULL , 0x10 }, // hh , 2006/02/16
+ { "HL-DT-ST", "DVDRAM_GSA-H10N" , NULL , 0x10 }, // hh , 2006/02/16
+ { "HL-DT-ST", "DVDRAM GSA-H10L" , NULL , 0x10 }, // hh , 2006/02/27
+ { "HL-DT-ST", "DVDRAM_GSA-H10L" , NULL , 0x10 }, // hh , 2006/04/21
+ { "HL-DT-ST", "DVDRAM GSA-H10A" , NULL , 0x10 }, // hh , 2006/01/03
+ { "HL-DT-ST", "DVDRAM_GSA-H10A" , NULL , 0x10 }, // hh , 2006/05/14
+ { "HL-DT-ST", "DVD-RW GSA-H11N" , NULL , 0x10 }, // hh , 2006/04/28
+ { "HL-DT-ST", "DVD-RW_GSA-H11N" , NULL , 0x10 }, // hh , 2006/02/22
+
+ { "HL-DT-ST", "DVDRAM GSA-4080N" , NULL , 0x10 }, // slim, 2004/08/08
+ { "HL-DT-ST", "DVDRAM GMA-4080N" , NULL , 0x10 }, // slim, 2004/11/09
+ { "HL-DT-ST", "DVD-RW GCA-4080N" , NULL , 0x10 }, // slim, 2004/11/22
+ { "HL-DT-ST", "DVD-RW GWA-4080N" , NULL , 0x10 }, // slim, 2004/08/17
+ { "HL-DT-ST", "DVDRAM GSA-4082N" , NULL , 0x10 }, // slim, 2005/07/12
+ { "HL-DT-ST", "DVDRAM_GSA-4082N" , NULL , 0x10 }, // slim, 2005/09/21
+ { "HL-DT-ST", "DVDRAM GMA-4082N" , NULL , 0x10 }, // slim, 2005/10/20
+ { "HL-DT-ST", "DVD-RW GRA-4082N" , NULL , 0x10 }, // slim, 2006/06/07
+ { "HL-DT-ST", "DVD-RW GWA-4082N" , NULL , 0x10 }, // slim, 2005/05/24
+ { "HL-DT-ST", "DVDRAM GMA4082Nf" , NULL , 0x10 }, // slim, 2006/02/28
+ { "HL-DT-ST", "DVDRAM GMA4082Nj" , NULL , 0x10 }, // slim, 2006/01/26
+
+ { "HL-DT-ST", "DVDRAM GSA-4084N" , NULL , 0x10 }, // slim, 2005/12/21
+ { "HL-DT-ST", "DVDRAM GMA-4084N" , NULL , 0x10 }, // slim, 2006/02/15
+ { "HP" , "DVD Writer 550s" , NULL , 0x10 }, // slim, 2006/05/08
+ { "HL-DT-ST", "DVDRAM GSA-T10N" , NULL , 0x10 }, // slim, 2006/07/26
+ { "HL-DT-ST", "DVDRAM_GSA-T10N" , NULL , 0x10 }, // slim, 2006/07/26
+ { "HL-DT-ST", "DVD+-RW GSA-T11N" , NULL , 0x10 }, // slim, 2006/07/25
+
+ { "HL-DT-ST", "DVD-ROM GDR8160B" , NULL , 0x10 }, // hh , 2001/10/12
+ { "COMPAQ" , "DVD-ROM GDR8160B" , NULL , 0x10 }, // hh , 2001/11/08
+ { "HL-DT-ST", "DVD-ROM GDR8161B" , NULL , 0x10 }, // hh , 2002/07/19
+ { "HL-DT-ST", "DVD-ROM GDR8162B" , NULL , 0x10 }, // hh , 2003/04/22
+ { "HL-DT-ST", "DVD-ROM GDR8163B" , NULL , 0x10 }, // hh , 2004/05/19
+ { "HL-DT-ST", "DVD-ROM GDR8164B" , NULL , 0x10 }, // hh , 2005/06/29
+ { "HL-DT-ST", "DVD-ROM GDRH10N" , NULL , 0x10 }, // hh , 2006/03/07
+
+ { "HL-DT-ST", "DVD-ROM GDR8081N" , NULL , 0x10 }, // slim, 2001/08/27
+ { "HL-DT-ST", "DVD-ROM GDR8082N" , NULL , 0x10 }, // slim, 2003/02/02
+ { "HL-DT-ST", "DVD-ROM GDR8083N" , NULL , 0x10 }, // slim, 2003/02/02
+ { "HL-DT-ST", "DVD-ROM GDR8085N" , NULL , 0x10 }, // slim, 2005/11/10
+
+ { "HL-DT-ST", "RW/DVD GCC-4080N" , NULL , 0x10 }, // slim, 2001/08/21
+ { "HL-DT-ST", "RW/DVD_GCC-4080N" , NULL , 0x10 }, // slim,
+ { "HL-DT-ST", "RW/DVD GCC-4160N" , NULL , 0x10 }, // slim, 2002/04/08
+ { "HL-DT-ST", "RW/DVD GCC-4240N" , NULL , 0x10 }, // slim, 2002/04/26
+ { "HL-DT-ST", "RW/DVD GCC-4241N" , NULL , 0x10 }, // slim, 2003/04/07
+ { "HL-DT-ST", "RW/DVD_GCC-4241N" , NULL , 0x10 }, // slim, 2004/03/07
+ { "HL-DT-ST", "RW/DVD GCC-4242N" , NULL , 0x10 }, // slim, 2003/12/21
+ { "HL-DT-ST", "RW/DVD GCC-4246N" , NULL , 0x10 }, // slim, 2005/05/23
+ { "HL-DT-ST", "BD-RE GBW-H10N" , NULL , 0x10 }, // hh , 2006/06/27
+
+ { "HL-DT-ST", "DVDRAM GSA-4083N" , NULL , 0x10 }, // hh , 2006/05/17
+ { "HL-DT-ST", "DVD+-RW GWA4083N" , NULL , 0x10 }, // hh , 2006/06/05
+
+ { "PIONEER", "DVD-RW DVR-106D" , NULL , 0x10 }, // hh , ?
+ { "ASUS", "DVD-RW DRW-0402P" , NULL , 0x10 }, // hh , ?
+
+ //
+ // This list contains vendor ID and product ID as a single string for ATAPI interface.
+ //
+
+ { "", "HL-DT-ST DVDRAM GMA-4020B" , NULL , 0x10 }, // hh , 2002/04/22
+ { "", "HL-DT-ST DVD-RW GCA-4020B" , NULL , 0x10 }, // hh , 2002/05/14
+ { "", "HL-DT-ST DVDRAM GSA-4040B" , NULL , 0x10 }, // hh , 2003/05/06
+ { "", "HL-DT-ST DVDRAM GMA-4040B" , NULL , 0x10 }, // hh , 2003/07/27
+ { "", "HL-DT-ST DVD-RW GWA-4040B" , NULL , 0x10 }, // hh , 2003/11/18
+ { "", "HL-DT-ST DVDRAM GSA-4081B" , NULL , 0x10 }, // hh , 2003/11/06
+ { "", "HL-DT-ST DVDRAM GSA-4082B" , NULL , 0x10 }, // hh , 2004/01/27
+ { "", "HL-DT-ST DVD-RW GWA-4082B" , NULL , 0x10 }, // hh , 2004/03/11
+ { "", "HL-DT-ST DVDRAM GSA-4120B" , NULL , 0x10 }, // hh , 2004/05/16
+ { "", "HL-DT-ST DVD+RW GRA-4120B" , NULL , 0x10 }, // hh , 2004/04/28
+ { "", "HL-DT-ST DVDRAM GSA-4160B" , NULL , 0x10 }, // hh , 2004/08/12
+ { "", "HL-DT-ST DVD-RW GWA-4160B" , NULL , 0x10 }, // hh , 2004/08/24
+ { "", "HL-DT-ST DVDRAM GSA-4163B" , NULL , 0x10 }, // hh , 2004/11/09
+ { "", "HL-DT-ST DVD-RW GWA-4163B" , NULL , 0x10 }, // hh , 2004/12/29
+ { "", "HL-DT-ST DVDRAM GSA-4165B" , NULL , 0x10 }, // hh , 2005/06/09
+ { "", "HL-DT-ST DVDRAM_GSA-4165B" , NULL , 0x10 }, // hh , 2005/06/28
+ { "", "HL-DT-ST DVD-RW GWA-4165B" , NULL , 0x10 }, // hh , 2005/08/23
+ { "", "HL-DT-ST DVDRAM GSA-4167B" , NULL , 0x10 }, // hh , 2005/07/01
+ { "", "HL-DT-ST DVDRAM GSA-H10N" , NULL , 0x10 }, // hh , 2006/02/16
+ { "", "HL-DT-ST DVDRAM_GSA-H10N" , NULL , 0x10 }, // hh , 2006/02/16
+ { "", "HL-DT-ST DVDRAM GSA-H10L" , NULL , 0x10 }, // hh , 2006/02/27
+ { "", "HL-DT-ST DVDRAM_GSA-H10L" , NULL , 0x10 }, // hh , 2006/04/21
+ { "", "HL-DT-ST DVDRAM GSA-H10A" , NULL , 0x10 }, // hh , 2006/01/03
+ { "", "HL-DT-ST DVDRAM_GSA-H10A" , NULL , 0x10 }, // hh , 2006/05/14
+ { "", "HL-DT-ST DVD-RW GSA-H11N" , NULL , 0x10 }, // hh , 2006/04/28
+ { "", "HL-DT-ST DVD-RW_GSA-H11N" , NULL , 0x10 }, // hh , 2006/02/22
+
+ { "", "HL-DT-ST DVDRAM GSA-4080N" , NULL , 0x10 }, // slim, 2004/08/08
+ { "", "HL-DT-ST DVDRAM GMA-4080N" , NULL , 0x10 }, // slim, 2004/11/09
+ { "", "HL-DT-ST DVD-RW GCA-4080N" , NULL , 0x10 }, // slim, 2004/11/22
+ { "", "HL-DT-ST DVD-RW GWA-4080N" , NULL , 0x10 }, // slim, 2004/08/17
+ { "", "HL-DT-ST DVDRAM GSA-4082N" , NULL , 0x10 }, // slim, 2005/07/12
+ { "", "HL-DT-ST DVDRAM_GSA-4082N" , NULL , 0x10 }, // slim, 2005/09/21
+ { "", "HL-DT-ST DVDRAM GMA-4082N" , NULL , 0x10 }, // slim, 2005/10/20
+ { "", "HL-DT-ST DVD-RW GRA-4082N" , NULL , 0x10 }, // slim, 2006/06/07
+ { "", "HL-DT-ST DVD-RW GWA-4082N" , NULL , 0x10 }, // slim, 2005/05/24
+ { "", "HL-DT-ST DVDRAM GMA4082Nf" , NULL , 0x10 }, // slim, 2006/02/28
+ { "", "HL-DT-ST DVDRAM GMA4082Nj" , NULL , 0x10 }, // slim, 2006/01/26
+
+ { "", "HL-DT-ST DVDRAM GSA-4084N" , NULL , 0x10 }, // slim, 2005/12/21
+ { "", "HL-DT-ST DVDRAM GMA-4084N" , NULL , 0x10 }, // slim, 2006/02/15
+ { "", "HP DVD Writer 550s" , NULL , 0x10 }, // slim, 2006/05/08
+ { "", "HL-DT-ST DVDRAM GSA-T10N" , NULL , 0x10 }, // slim, 2006/07/26
+ { "", "HL-DT-ST DVDRAM_GSA-T10N" , NULL , 0x10 }, // slim, 2006/07/26
+ { "", "HL-DT-ST DVD+-RW GSA-T11N" , NULL , 0x10 }, // slim, 2006/07/25
+
+ { "", "HL-DT-ST DVD-ROM GDR8160B" , NULL , 0x10 }, // hh , 2001/10/12
+ { "", "COMPAQ DVD-ROM GDR8160B" , NULL , 0x10 }, // hh , 2001/11/08
+ { "", "HL-DT-ST DVD-ROM GDR8161B" , NULL , 0x10 }, // hh , 2002/07/19
+ { "", "HL-DT-ST DVD-ROM GDR8162B" , NULL , 0x10 }, // hh , 2003/04/22
+ { "", "HL-DT-ST DVD-ROM GDR8163B" , NULL , 0x10 }, // hh , 2004/05/19
+ { "", "HL-DT-ST DVD-ROM GDR8164B" , NULL , 0x10 }, // hh , 2005/06/29
+ { "", "HL-DT-ST DVD-ROM GDRH10N" , NULL , 0x10 }, // hh , 2006/03/07
+
+ { "", "HL-DT-ST DVD-ROM GDR8081N" , NULL , 0x10 }, // slim, 2001/08/27
+ { "", "HL-DT-ST DVD-ROM GDR8082N" , NULL , 0x10 }, // slim, 2003/02/02
+ { "", "HL-DT-ST DVD-ROM GDR8083N" , NULL , 0x10 }, // slim, 2003/02/02
+ { "", "HL-DT-ST DVD-ROM GDR8085N" , NULL , 0x10 }, // slim, 2005/11/10
+
+ { "", "HL-DT-ST RW/DVD GCC-4080N" , NULL , 0x10 }, // slim, 2001/08/21
+ { "", "HL-DT-ST RW/DVD_GCC-4080N" , NULL , 0x10 }, // slim,
+ { "", "HL-DT-ST RW/DVD GCC-4160N" , NULL , 0x10 }, // slim, 2002/04/08
+ { "", "HL-DT-ST RW/DVD GCC-4240N" , NULL , 0x10 }, // slim, 2002/04/26
+ { "", "HL-DT-ST RW/DVD GCC-4241N" , NULL , 0x10 }, // slim, 2003/04/07
+ { "", "HL-DT-ST RW/DVD_GCC-4241N" , NULL , 0x10 }, // slim, 2004/03/07
+ { "", "HL-DT-ST RW/DVD GCC-4242N" , NULL , 0x10 }, // slim, 2003/12/21
+ { "", "HL-DT-ST RW/DVD GCC-4246N" , NULL , 0x10 }, // slim, 2005/05/23
+ { "", "HL-DT-ST BD-RE GBW-H10N" , NULL , 0x10 }, // hh , 2006/06/27
+
+ { "", "HL-DT-ST DVDRAM GSA-4083N" , NULL , 0x10 }, // hh , 2006/05/17
+ { "", "HL-DT-ST DVD+-RW GWA4083N" , NULL , 0x10 }, // hh , 2006/06/05
+
+ { "", "PIONEER DVD-RW DVR-106D" , NULL , 0x10 }, // hh , ?
+ { "", "ASUS DVD-RW DRW-0402P" , NULL , 0x10 }, // hh , ?
+
+
+ // Sony sourced some drives from LG also....
+
+ { NULL , NULL , NULL , 0x00 },
+};
+
+
+GUID ClassGuidQueryRegInfoEx = GUID_CLASSPNP_QUERY_REGINFOEX;
+GUID ClassGuidSenseInfo2 = GUID_CLASSPNP_SENSEINFO2;
+GUID ClassGuidWorkingSet = GUID_CLASSPNP_WORKING_SET;
+GUID ClassGuidSrbSupport = GUID_CLASSPNP_SRB_SUPPORT;
+
+#ifdef ALLOC_DATA_PRAGMA
+ #pragma data_seg()
+#endif
diff --git a/storage/class/classpnp/src/debug.c b/storage/class/classpnp/src/debug.c
new file mode 100644
index 00000000..cc288ec6
--- /dev/null
+++ b/storage/class/classpnp/src/debug.c
@@ -0,0 +1,966 @@
+/*++
+
+Copyright (C) Microsoft Corporation, 1991 - 2010
+
+Module Name:
+
+ debug.c
+
+Abstract:
+
+ CLASSPNP debug code and data
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+
+Revision History:
+
+--*/
+
+
+#include "classp.h"
+#include "debug.h"
+
+#ifdef DEBUG_USE_WPP
+#include "debug.tmh"
+#endif
+
+#if DBG
+
+ //
+ // default to not breaking in for lost irps, five minutes before we even
+ // bother checking for lost irps, using standard debug print macros, and
+ // using a 64k debug print buffer
+ //
+
+ #ifndef CLASS_GLOBAL_BREAK_ON_LOST_IRPS
+ #error "CLASS_GLOBAL_BREAK_ON_LOST_IRPS undefined"
+ #define CLASS_GLOBAL_BREAK_ON_LOST_IRPS 0
+ #endif // CLASS_GLOBAL_BREAK_ON_LOST_IRPS
+
+ #ifndef CLASS_GLOBAL_SECONDS_TO_WAIT_FOR_SYNCHRONOUS_SRB
+ #error "CLASS_GLOBAL_SECONDS_TO_WAIT_FOR_SYNCHRONOUS_SRB undefined"
+ #define CLASS_GLOBAL_SECONDS_TO_WAIT_FOR_SYNCHRONOUS_SRB 300
+ #endif // CLASS_GLOBAL_SECONDS_TO_WAIT_FOR_SYNCHRONOUS_SRB
+
+ #ifndef CLASS_GLOBAL_BUFFERED_DEBUG_PRINT
+ #error "CLASS_GLOBAL_BUFFERED_DEBUG_PRINT undefined"
+ #define CLASS_GLOBAL_BUFFERED_DEBUG_PRINT 0
+ #endif // CLASS_GLOBAL_BUFFERED_DEBUG_PRINT
+
+ #ifndef CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFER_SIZE
+ #error "CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFER_SIZE undefined"
+ #define CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFER_SIZE 512
+ #endif // CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFER_SIZE
+
+ #ifndef CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFERS
+ #error "CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFERS undefined"
+ #define CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFERS 512
+ #endif // CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFERS
+
+ #pragma data_seg("NONPAGE")
+
+
+
+ CLASSPNP_GLOBALS ClasspnpGlobals;
+
+ //
+ // the low sixteen bits are used to see if the debug level is high enough
+ // the high sixteen bits are used to singly enable debug levels 1-16
+ //
+ LONG ClassDebug = 0x00000000;
+
+ BOOLEAN DebugTrapOnWarn = FALSE;
+
+ //
+ // Used to track callers when we receive an access and the disk
+ // is powered down.
+ //
+ ULONG DiskSpinupIndex = 0;
+ DISK_SPINUP_TRACES DiskSpinupTraces[NUMBER_OF_DISK_SPINUP_TRACES];
+
+ VOID ClasspInitializeDebugGlobals()
+ {
+ KIRQL irql;
+
+ if (InterlockedCompareExchange(&ClasspnpGlobals.Initializing, 1, 0) == 0) {
+
+ KeInitializeSpinLock(&ClasspnpGlobals.SpinLock);
+
+ KeAcquireSpinLock(&ClasspnpGlobals.SpinLock, &irql);
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT, "CLASSPNP.SYS => Initializing ClasspnpGlobals...\n"));
+
+ ClasspnpGlobals.Buffer = NULL;
+ ClasspnpGlobals.Index = (ULONG)-1;
+ ClasspnpGlobals.BreakOnLostIrps = CLASS_GLOBAL_BREAK_ON_LOST_IRPS;
+ ClasspnpGlobals.EachBufferSize = CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFER_SIZE;
+ ClasspnpGlobals.NumberOfBuffers = CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFERS;
+ ClasspnpGlobals.SecondsToWaitForIrps = CLASS_GLOBAL_SECONDS_TO_WAIT_FOR_SYNCHRONOUS_SRB;
+
+ //
+ // this should be the last item set
+ //
+
+ ClasspnpGlobals.UseBufferedDebugPrint = CLASS_GLOBAL_BUFFERED_DEBUG_PRINT;
+
+ KeReleaseSpinLock(&ClasspnpGlobals.SpinLock, irql);
+
+ InterlockedExchange(&ClasspnpGlobals.Initialized, 1);
+
+ }
+ }
+
+ /*++////////////////////////////////////////////////////////////////////////////
+
+ ClassDebugPrint()
+
+ Routine Description:
+
+ Debug print for all class drivers, NOOP on FRE versions.
+ Allows printing to a debug buffer (with auto fallback to kdprint) by
+ properly setting the Globals in classpnp on CHK versions.
+
+ Arguments:
+
+ Debug print level, or from 0 to 3 for legacy drivers.
+
+ Return Value:
+
+ None
+
+ --*/
+ VOID ClassDebugPrint(_In_ CLASS_DEBUG_LEVEL DebugPrintLevel, _In_z_ PCCHAR DebugMessage, ...)
+ {
+ va_list ap;
+ va_start(ap, DebugMessage);
+
+ if ((DebugPrintLevel <= (ClassDebug & 0x0000ffff)) ||
+ ((1 << (DebugPrintLevel + 15)) & ClassDebug)) {
+
+ if (ClasspnpGlobals.UseBufferedDebugPrint &&
+ ClasspnpGlobals.Buffer == NULL) {
+
+ //
+ // this double-check prevents always taking
+ // a spinlock just to ensure we have a buffer
+ //
+
+ KIRQL irql;
+
+ KeAcquireSpinLock(&ClasspnpGlobals.SpinLock, &irql);
+ if (ClasspnpGlobals.Buffer == NULL) {
+
+ SIZE_T bufferSize;
+ if (NT_SUCCESS(
+ RtlSIZETMult(ClasspnpGlobals.NumberOfBuffers,
+ ClasspnpGlobals.EachBufferSize,
+ &bufferSize))) {
+
+ DbgPrintEx(DPFLTR_CLASSPNP_ID, DPFLTR_ERROR_LEVEL,
+ "ClassDebugPrint: Allocating %x bytes for "
+ "classdebugprint buffer\n", (ULONG)bufferSize);
+ ClasspnpGlobals.Index = (ULONG)-1;
+ ClasspnpGlobals.Buffer =
+ ExAllocatePoolWithTag(NonPagedPoolNx, bufferSize, 'bDcS');
+ DbgPrintEx(DPFLTR_CLASSPNP_ID, DPFLTR_ERROR_LEVEL,
+ "ClassDebugPrint: Allocated buffer at %p\n",
+ ClasspnpGlobals.Buffer);
+
+ if (ClasspnpGlobals.Buffer) {
+ RtlZeroMemory(ClasspnpGlobals.Buffer, bufferSize);
+ }
+ }
+
+ }
+ KeReleaseSpinLock(&ClasspnpGlobals.SpinLock, irql);
+
+ }
+
+ if (ClasspnpGlobals.UseBufferedDebugPrint &&
+ ClasspnpGlobals.Buffer != NULL) {
+
+ //
+ // we never free the buffer, so once it exists,
+ // we can just print to it with immunity
+ //
+
+ ULONG index;
+ PUCHAR buffer;
+ NTSTATUS status;
+ index = InterlockedIncrement((volatile LONG *)&ClasspnpGlobals.Index);
+ index %= ClasspnpGlobals.NumberOfBuffers;
+ index *= (ULONG)ClasspnpGlobals.EachBufferSize;
+
+ buffer = ClasspnpGlobals.Buffer;
+ buffer += index;
+
+ RtlZeroMemory(buffer, ClasspnpGlobals.EachBufferSize);
+
+ status = RtlStringCchVPrintfA((NTSTRSAFE_PSTR)buffer, ClasspnpGlobals.EachBufferSize, DebugMessage, ap);
+ if (!NT_SUCCESS(status))
+ {
+ *buffer = 0; // force-null on failure
+ }
+
+ } else {
+
+ //
+ // either we could not allocate a buffer for debug prints
+ // or buffered debug prints are disabled
+ //
+
+ vDbgPrintEx(DPFLTR_CLASSPNP_ID, DPFLTR_INFO_LEVEL, DebugMessage, ap);
+
+ }
+
+ }
+
+ va_end(ap);
+
+ }
+
+
+ /*
+ * DbgCheckReturnedPkt
+ *
+ * Check a completed TRANSFER_PACKET for all sorts of error conditions
+ * and warn/trap appropriately.
+ */
+ VOID DbgCheckReturnedPkt(TRANSFER_PACKET *Pkt)
+ {
+ PCDB pCdb = ClasspTransferPacketGetCdb(Pkt);
+
+ NT_ASSERT(SrbGetOriginalRequest(Pkt->Srb) == Pkt->Irp);
+ NT_ASSERT(SrbGetDataBuffer(Pkt->Srb) == Pkt->BufPtrCopy);
+ NT_ASSERT(SrbGetDataTransferLength(Pkt->Srb) <= Pkt->BufLenCopy);
+ NT_ASSERT(!Pkt->Irp->CancelRoutine);
+
+ if (SRB_STATUS(Pkt->Srb->SrbStatus) == SRB_STATUS_PENDING){
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_RW, "SRB completed with status PENDING in packet %ph: (op=%s srbstat=%s(%xh), irpstat=%xh)",
+ Pkt,
+ DBGGETSCSIOPSTR(Pkt->Srb),
+ DBGGETSRBSTATUSSTR(Pkt->Srb),
+ (ULONG)Pkt->Srb->SrbStatus,
+ Pkt->Irp->IoStatus.Status));
+ }
+ else if (SRB_STATUS(Pkt->Srb->SrbStatus) == SRB_STATUS_SUCCESS){
+ /*
+ * Make sure SRB and IRP status match.
+ */
+ if (!NT_SUCCESS(Pkt->Irp->IoStatus.Status)){
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_RW, "SRB and IRP status don't match in packet %ph: (op=%s srbstat=%s(%xh), irpstat=%xh)",
+ Pkt,
+ DBGGETSCSIOPSTR(Pkt->Srb),
+ DBGGETSRBSTATUSSTR(Pkt->Srb),
+ (ULONG)Pkt->Srb->SrbStatus,
+ Pkt->Irp->IoStatus.Status));
+ }
+
+ if (Pkt->Irp->IoStatus.Information != SrbGetDataTransferLength(Pkt->Srb)){
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_RW, "SRB and IRP result transfer lengths don't match in succeeded packet %ph: (op=%s, SrbStatus=%s, Srb.DataTransferLength=%xh, Irp->IoStatus.Information=%Ixh).",
+ Pkt,
+ DBGGETSCSIOPSTR(Pkt->Srb),
+ DBGGETSRBSTATUSSTR(Pkt->Srb),
+ SrbGetDataTransferLength(Pkt->Srb),
+ Pkt->Irp->IoStatus.Information));
+ }
+ }
+ else {
+ if (NT_SUCCESS(Pkt->Irp->IoStatus.Status)){
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_RW, "SRB and IRP status don't match in packet %ph: (op=%s srbstat=%s(%xh), irpstat=%xh)",
+ Pkt,
+ DBGGETSCSIOPSTR(Pkt->Srb),
+ DBGGETSRBSTATUSSTR(Pkt->Srb),
+ (ULONG)Pkt->Srb->SrbStatus,
+ Pkt->Irp->IoStatus.Status));
+ }
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_RW, "Packet %ph failed (op=%s srbstat=%s(%xh), irpstat=%xh, sense=%s/%s/%s)",
+ Pkt,
+ DBGGETSCSIOPSTR(Pkt->Srb),
+ DBGGETSRBSTATUSSTR(Pkt->Srb),
+ (ULONG)Pkt->Srb->SrbStatus,
+ Pkt->Irp->IoStatus.Status,
+ DBGGETSENSECODESTR(Pkt->Srb),
+ DBGGETADSENSECODESTR(Pkt->Srb),
+ DBGGETADSENSEQUALIFIERSTR(Pkt->Srb)));
+
+ /*
+ * If the SRB failed with underrun or overrun, then the actual
+ * transferred length should be returned in both SRB and IRP.
+ * (SRB's only have an error status for overrun, so it's overloaded).
+ */
+ if ((SRB_STATUS(Pkt->Srb->SrbStatus) == SRB_STATUS_DATA_OVERRUN) &&
+ (Pkt->Irp->IoStatus.Information != SrbGetDataTransferLength(Pkt->Srb))){
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_RW, "SRB and IRP result transfer lengths don't match in failed packet %ph: (op=%s, SrbStatus=%s, Srb.DataTransferLength=%xh, Irp->IoStatus.Information=%Ixh).",
+ Pkt,
+ DBGGETSCSIOPSTR(Pkt->Srb),
+ DBGGETSRBSTATUSSTR(Pkt->Srb),
+ SrbGetDataTransferLength(Pkt->Srb),
+ Pkt->Irp->IoStatus.Information));
+ }
+ }
+
+ /*
+ * If the port driver returned STATUS_INSUFFICIENT_RESOURCES,
+ * make sure this is also the InternalStatus in the SRB so that we process it correctly.
+ */
+ if (Pkt->Irp->IoStatus.Status == STATUS_INSUFFICIENT_RESOURCES){
+ NT_ASSERT(SRB_STATUS(Pkt->Srb->SrbStatus) == SRB_STATUS_INTERNAL_ERROR);
+ NT_ASSERT(SrbGetSystemStatus(Pkt->Srb) == STATUS_INSUFFICIENT_RESOURCES);
+ }
+
+ /*
+ * Some miniport drivers have been caught changing the SCSI operation
+ * code in the SRB. This is absolutely disallowed as it breaks our error handling.
+ */
+ switch (pCdb->CDB10.OperationCode){
+ case SCSIOP_MEDIUM_REMOVAL:
+ case SCSIOP_MODE_SENSE:
+ case SCSIOP_READ_CAPACITY:
+ case SCSIOP_READ:
+ case SCSIOP_WRITE:
+ case SCSIOP_START_STOP_UNIT:
+ case SCSIOP_READ_CAPACITY16:
+ case SCSIOP_READ16:
+ case SCSIOP_WRITE16:
+ break;
+ default:
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_RW, "Miniport illegally changed Srb.Cdb.OperationCode in packet %ph failed (op=%s srbstat=%s(%xh), irpstat=%xh, sense=%s/%s/%s)",
+ Pkt,
+ DBGGETSCSIOPSTR(Pkt->Srb),
+ DBGGETSRBSTATUSSTR(Pkt->Srb),
+ (ULONG)Pkt->Srb->SrbStatus,
+ Pkt->Irp->IoStatus.Status,
+ DBGGETSENSECODESTR(Pkt->Srb),
+ DBGGETADSENSECODESTR(Pkt->Srb),
+ DBGGETADSENSEQUALIFIERSTR(Pkt->Srb)));
+ break;
+ }
+
+ }
+
+
+ VOID DbgLogSendPacket(TRANSFER_PACKET *Pkt)
+ {
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Pkt->Fdo->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
+ KIRQL oldIrql;
+
+ if (Pkt->OriginalIrp){
+ Pkt->DbgOriginalIrpCopy = *Pkt->OriginalIrp;
+ if (Pkt->OriginalIrp->MdlAddress){
+ Pkt->DbgMdlCopy = *Pkt->OriginalIrp->MdlAddress;
+ }
+ }
+
+ KeQueryTickCount(&Pkt->DbgTimeSent);
+ Pkt->DbgTimeReturned.QuadPart = 0L;
+
+ KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
+ fdoData->DbgPacketLogs[fdoData->DbgPacketLogNextIndex] = *Pkt;
+ fdoData->DbgPacketLogNextIndex++;
+ fdoData->DbgPacketLogNextIndex %= DBG_NUM_PACKET_LOG_ENTRIES;
+ KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
+ }
+
+ VOID DbgLogReturnPacket(TRANSFER_PACKET *Pkt)
+ {
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Pkt->Fdo->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
+ KIRQL oldIrql;
+
+ KeQueryTickCount(&Pkt->DbgTimeReturned);
+
+ #if 0
+ // ISSUE: there are some problems with this check (e.g. multiproc), so don't include it yet
+ if (Pkt->OriginalIrp){
+ /*
+ * No one should have touched the original irp while the packet was outstanding,
+ * except for a couple fields that we ourselves update during the transfer
+ * or that are allowed to change;
+ * make those couple fields the same and then to a bytewise compare
+ */
+ ULONG lenSame;
+
+ Pkt->DbgOriginalIrpCopy.IoStatus.Status = Pkt->OriginalIrp->IoStatus.Status;
+ Pkt->DbgOriginalIrpCopy.IoStatus.Information = Pkt->OriginalIrp->IoStatus.Information;
+ Pkt->DbgOriginalIrpCopy.Tail.Overlay.DriverContext[0] = Pkt->OriginalIrp->Tail.Overlay.DriverContext[0];
+ Pkt->DbgOriginalIrpCopy.ThreadListEntry = Pkt->OriginalIrp->ThreadListEntry;
+ Pkt->DbgOriginalIrpCopy.Cancel = Pkt->OriginalIrp->Cancel;
+
+ lenSame = (ULONG)RtlCompareMemory(Pkt->OriginalIrp, &Pkt->DbgOriginalIrpCopy, sizeof(IRP));
+ NT_ASSERT(lenSame == sizeof(IRP));
+ }
+ #endif
+
+ KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
+ fdoData->DbgPacketLogs[fdoData->DbgPacketLogNextIndex] = *Pkt;
+ fdoData->DbgPacketLogNextIndex++;
+ fdoData->DbgPacketLogNextIndex %= DBG_NUM_PACKET_LOG_ENTRIES;
+ KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
+ }
+
+
+ /*++////////////////////////////////////////////////////////////////////////////
+
+ DbgSafeInc()
+
+ Routine Description:
+
+ Safely increments a ULONG. If the increment would result in an overflow,
+ the value is unchanged.
+
+ Arguments:
+
+ A pointer to the value to be incremented.
+
+ --*/
+ __inline VOID DbgSafeInc(PULONG pValue)
+ {
+ ULONG incrementResult;
+ if(NT_SUCCESS(RtlULongAdd(*pValue, 1, &incrementResult))) {
+ *pValue = incrementResult;
+ } else {
+ //
+ // Leave *pValue unchanged (i.e. at ULONG_MAX).
+ //
+ }
+ }
+
+ VOID DbgLogFlushInfo(PCLASS_PRIVATE_FDO_DATA FdoData, BOOLEAN IsIO, BOOLEAN IsFUA, BOOLEAN IsFlush)
+ {
+
+ /*
+ * Reset all FUA/Flush logging fields.
+ */
+ if (FdoData->DbgInitFlushLogging){
+ FdoData->DbgNumIORequests = 0;
+ FdoData->DbgNumFUAs = 0;
+ FdoData->DbgNumFlushes = 0;
+ FdoData->DbgIOsSinceFUA = 0;
+ FdoData->DbgIOsSinceFlush = 0;
+ FdoData->DbgAveIOsToFUA = 0;
+ FdoData->DbgAveIOsToFlush = 0;
+ FdoData->DbgMaxIOsToFUA = 0;
+ FdoData->DbgMaxIOsToFlush = 0;
+ FdoData->DbgMinIOsToFUA = 0xffffffff;
+ FdoData->DbgMinIOsToFlush = 0xffffffff;
+ FdoData->DbgInitFlushLogging = FALSE;
+ }
+
+ //
+ // Using DbgSafeInc for all increments (instead of ++) guarantees
+ // that there will be no overflow hence no division by 0. All counters
+ // are capped at ULONG_MAX.
+ //
+
+ if (IsIO){
+ DbgSafeInc(&FdoData->DbgNumIORequests);
+ DbgSafeInc(&FdoData->DbgIOsSinceFlush);
+ if (IsFUA){
+ if (FdoData->DbgNumFUAs > 0){
+ FdoData->DbgMinIOsToFUA = min(FdoData->DbgMinIOsToFUA, FdoData->DbgIOsSinceFUA);
+ }
+ DbgSafeInc(&FdoData->DbgNumFUAs);
+ FdoData->DbgAveIOsToFUA = FdoData->DbgNumIORequests/FdoData->DbgNumFUAs;
+ FdoData->DbgIOsSinceFUA = 0;
+ }
+ else {
+ DbgSafeInc(&FdoData->DbgIOsSinceFUA);
+ FdoData->DbgMaxIOsToFUA = max(FdoData->DbgMaxIOsToFUA, FdoData->DbgIOsSinceFUA);
+ }
+ FdoData->DbgMaxIOsToFlush = max(FdoData->DbgMaxIOsToFlush, FdoData->DbgIOsSinceFlush);
+ }
+ else if (IsFlush){
+ if (FdoData->DbgNumFlushes > 0){
+ FdoData->DbgMinIOsToFlush = min(FdoData->DbgMinIOsToFlush, FdoData->DbgIOsSinceFlush);
+ }
+ DbgSafeInc(&FdoData->DbgNumFlushes);
+ FdoData->DbgAveIOsToFlush = FdoData->DbgNumIORequests/FdoData->DbgNumFlushes;
+ FdoData->DbgIOsSinceFlush = 0;
+ }
+
+ }
+
+
+ /*++////////////////////////////////////////////////////////////////////////////
+
+ SnapDiskStartup()
+
+ Routine Description:
+
+ This function will attempt to record the caller responsible for spinning
+ up the disk.
+
+ Arguments:
+
+ NONE.
+
+ Return Value:
+
+ NONE.
+
+ --*/
+ VOID
+ SnapDiskStartup(
+ VOID
+ )
+ {
+ ULONG Index;
+ PDISK_SPINUP_TRACES Entry;
+ LARGE_INTEGER SpinUpTime;
+
+#pragma warning(push)
+#pragma warning(disable:4210) // nonstandard extension used : function given file scope
+ extern NTSYSAPI USHORT NTAPI RtlCaptureStackBackTrace(
+ _In_ ULONG FramesToSkip,
+ _In_ ULONG FramesToCapture,
+ _Out_writes_to_(FramesToCapture, return) PVOID * BackTrace,
+ _Out_opt_ PULONG BackTraceHash );
+#pragma warning(pop)
+
+ //
+ // Grab the current count, then mod it so that it
+ // becomes an index into the DiskSpinupTraces array.
+ //
+ Index = InterlockedIncrement( (volatile LONG *)&DiskSpinupIndex );
+ Index = Index & (NUMBER_OF_DISK_SPINUP_TRACES - 1);
+ Entry = &DiskSpinupTraces[Index];
+
+ //
+ // Timestamp the instance.
+ //
+ KeQueryTickCount(&SpinUpTime);
+ SpinUpTime.QuadPart = (SpinUpTime.QuadPart * KeQueryTimeIncrement())/(10000000);
+
+
+ //
+ // Ask the kernel to read back up our stack by
+ // DISK_SPINUP_BACKTRACE_LENGTH frames.
+ //
+ Entry->TimeStamp.QuadPart = SpinUpTime.QuadPart;
+ RtlZeroMemory( &Entry->StackTrace[0], DISK_SPINUP_BACKTRACE_LENGTH * sizeof(PVOID) );
+ RtlCaptureStackBackTrace( 5, // stacks to skip
+ DISK_SPINUP_BACKTRACE_LENGTH, // buffer size
+ Entry->StackTrace,
+ &Index );
+ }
+
+#else
+
+ // We have to keep this in the retail build for legacy.
+ VOID ClassDebugPrint(_In_ CLASS_DEBUG_LEVEL DebugPrintLevel, _In_z_ PCCHAR DebugMessage, ...)
+ {
+ UNREFERENCED_PARAMETER(DebugPrintLevel);
+ UNREFERENCED_PARAMETER(DebugMessage);
+ }
+
+#endif
+
+ char *DbgGetIoctlStr(ULONG ioctl)
+ {
+ char *ioctlStr = "?";
+
+ switch (ioctl){
+
+ #undef MAKE_CASE
+ #define MAKE_CASE(ioctlCode) case ioctlCode: ioctlStr = #ioctlCode; break;
+
+ MAKE_CASE(IOCTL_STORAGE_CHECK_VERIFY)
+ MAKE_CASE(IOCTL_STORAGE_CHECK_VERIFY2)
+ MAKE_CASE(IOCTL_STORAGE_MEDIA_REMOVAL)
+ MAKE_CASE(IOCTL_STORAGE_EJECT_MEDIA)
+ MAKE_CASE(IOCTL_STORAGE_LOAD_MEDIA)
+ MAKE_CASE(IOCTL_STORAGE_LOAD_MEDIA2)
+ MAKE_CASE(IOCTL_STORAGE_RESERVE)
+ MAKE_CASE(IOCTL_STORAGE_RELEASE)
+ MAKE_CASE(IOCTL_STORAGE_PERSISTENT_RESERVE_IN)
+ MAKE_CASE(IOCTL_STORAGE_PERSISTENT_RESERVE_OUT)
+ MAKE_CASE(IOCTL_STORAGE_FIND_NEW_DEVICES)
+ MAKE_CASE(IOCTL_STORAGE_EJECTION_CONTROL)
+ MAKE_CASE(IOCTL_STORAGE_MCN_CONTROL)
+ MAKE_CASE(IOCTL_STORAGE_GET_MEDIA_TYPES)
+ MAKE_CASE(IOCTL_STORAGE_GET_MEDIA_TYPES_EX)
+ MAKE_CASE(IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER)
+ MAKE_CASE(IOCTL_STORAGE_GET_HOTPLUG_INFO)
+ MAKE_CASE(IOCTL_STORAGE_RESET_BUS)
+ MAKE_CASE(IOCTL_STORAGE_RESET_DEVICE)
+ MAKE_CASE(IOCTL_STORAGE_GET_DEVICE_NUMBER)
+ MAKE_CASE(IOCTL_STORAGE_PREDICT_FAILURE)
+ MAKE_CASE(IOCTL_STORAGE_QUERY_PROPERTY)
+ MAKE_CASE(OBSOLETE_IOCTL_STORAGE_RESET_BUS)
+ MAKE_CASE(OBSOLETE_IOCTL_STORAGE_RESET_DEVICE)
+ }
+
+ return ioctlStr;
+ }
+
+ char *DbgGetScsiOpStr(PSTORAGE_REQUEST_BLOCK_HEADER Srb)
+ {
+ PCDB pCdb = SrbGetCdb(Srb);
+ char *scsiOpStr = "?";
+
+ if (pCdb) {
+
+ switch (pCdb->CDB6GENERIC.OperationCode){
+
+ #undef MAKE_CASE
+ #define MAKE_CASE(scsiOpCode) case scsiOpCode: scsiOpStr = #scsiOpCode; break;
+
+ MAKE_CASE(SCSIOP_TEST_UNIT_READY)
+ MAKE_CASE(SCSIOP_REWIND) // aka SCSIOP_REZERO_UNIT
+ MAKE_CASE(SCSIOP_REQUEST_BLOCK_ADDR)
+ MAKE_CASE(SCSIOP_REQUEST_SENSE)
+ MAKE_CASE(SCSIOP_FORMAT_UNIT)
+ MAKE_CASE(SCSIOP_READ_BLOCK_LIMITS)
+ MAKE_CASE(SCSIOP_INIT_ELEMENT_STATUS) // aka SCSIOP_REASSIGN_BLOCKS
+ MAKE_CASE(SCSIOP_RECEIVE) // aka SCSIOP_READ6
+ MAKE_CASE(SCSIOP_SEND) // aka SCSIOP_WRITE6, SCSIOP_PRINT
+ MAKE_CASE(SCSIOP_SLEW_PRINT) // aka SCSIOP_SEEK6, SCSIOP_TRACK_SELECT
+ MAKE_CASE(SCSIOP_SEEK_BLOCK)
+ MAKE_CASE(SCSIOP_PARTITION)
+ MAKE_CASE(SCSIOP_READ_REVERSE)
+ MAKE_CASE(SCSIOP_FLUSH_BUFFER) // aka SCSIOP_WRITE_FILEMARKS
+ MAKE_CASE(SCSIOP_SPACE)
+ MAKE_CASE(SCSIOP_INQUIRY)
+ MAKE_CASE(SCSIOP_VERIFY6)
+ MAKE_CASE(SCSIOP_RECOVER_BUF_DATA)
+ MAKE_CASE(SCSIOP_MODE_SELECT)
+ MAKE_CASE(SCSIOP_RESERVE_UNIT)
+ MAKE_CASE(SCSIOP_RELEASE_UNIT)
+ MAKE_CASE(SCSIOP_COPY)
+ MAKE_CASE(SCSIOP_ERASE)
+ MAKE_CASE(SCSIOP_MODE_SENSE)
+ MAKE_CASE(SCSIOP_START_STOP_UNIT) // aka SCSIOP_STOP_PRINT, SCSIOP_LOAD_UNLOAD
+ MAKE_CASE(SCSIOP_RECEIVE_DIAGNOSTIC)
+ MAKE_CASE(SCSIOP_SEND_DIAGNOSTIC)
+ MAKE_CASE(SCSIOP_MEDIUM_REMOVAL)
+ MAKE_CASE(SCSIOP_READ_FORMATTED_CAPACITY)
+ MAKE_CASE(SCSIOP_READ_CAPACITY)
+ MAKE_CASE(SCSIOP_READ)
+ MAKE_CASE(SCSIOP_WRITE)
+ MAKE_CASE(SCSIOP_SEEK) // aka SCSIOP_LOCATE, SCSIOP_POSITION_TO_ELEMENT
+ MAKE_CASE(SCSIOP_WRITE_VERIFY)
+ MAKE_CASE(SCSIOP_VERIFY)
+ MAKE_CASE(SCSIOP_SEARCH_DATA_HIGH)
+ MAKE_CASE(SCSIOP_SEARCH_DATA_EQUAL)
+ MAKE_CASE(SCSIOP_SEARCH_DATA_LOW)
+ MAKE_CASE(SCSIOP_SET_LIMITS)
+ MAKE_CASE(SCSIOP_READ_POSITION)
+ MAKE_CASE(SCSIOP_SYNCHRONIZE_CACHE)
+ MAKE_CASE(SCSIOP_COMPARE)
+ MAKE_CASE(SCSIOP_COPY_COMPARE)
+ MAKE_CASE(SCSIOP_WRITE_DATA_BUFF)
+ MAKE_CASE(SCSIOP_READ_DATA_BUFF)
+ MAKE_CASE(SCSIOP_CHANGE_DEFINITION)
+ MAKE_CASE(SCSIOP_READ_SUB_CHANNEL)
+ MAKE_CASE(SCSIOP_READ_TOC)
+ MAKE_CASE(SCSIOP_READ_HEADER)
+ MAKE_CASE(SCSIOP_PLAY_AUDIO)
+ MAKE_CASE(SCSIOP_GET_CONFIGURATION)
+ MAKE_CASE(SCSIOP_PLAY_AUDIO_MSF)
+ MAKE_CASE(SCSIOP_PLAY_TRACK_INDEX)
+ MAKE_CASE(SCSIOP_PLAY_TRACK_RELATIVE)
+ MAKE_CASE(SCSIOP_GET_EVENT_STATUS)
+ MAKE_CASE(SCSIOP_PAUSE_RESUME)
+ MAKE_CASE(SCSIOP_LOG_SELECT)
+ MAKE_CASE(SCSIOP_LOG_SENSE)
+ MAKE_CASE(SCSIOP_STOP_PLAY_SCAN)
+ MAKE_CASE(SCSIOP_READ_DISK_INFORMATION)
+ MAKE_CASE(SCSIOP_READ_TRACK_INFORMATION)
+ MAKE_CASE(SCSIOP_RESERVE_TRACK_RZONE)
+ MAKE_CASE(SCSIOP_SEND_OPC_INFORMATION)
+ MAKE_CASE(SCSIOP_MODE_SELECT10)
+ MAKE_CASE(SCSIOP_MODE_SENSE10)
+ MAKE_CASE(SCSIOP_CLOSE_TRACK_SESSION)
+ MAKE_CASE(SCSIOP_READ_BUFFER_CAPACITY)
+ MAKE_CASE(SCSIOP_SEND_CUE_SHEET)
+ MAKE_CASE(SCSIOP_PERSISTENT_RESERVE_IN)
+ MAKE_CASE(SCSIOP_PERSISTENT_RESERVE_OUT)
+ MAKE_CASE(SCSIOP_REPORT_LUNS)
+ MAKE_CASE(SCSIOP_BLANK)
+ MAKE_CASE(SCSIOP_SEND_KEY)
+ MAKE_CASE(SCSIOP_REPORT_KEY)
+ MAKE_CASE(SCSIOP_MOVE_MEDIUM)
+ MAKE_CASE(SCSIOP_LOAD_UNLOAD_SLOT) // aka SCSIOP_EXCHANGE_MEDIUM
+ MAKE_CASE(SCSIOP_SET_READ_AHEAD)
+ MAKE_CASE(SCSIOP_READ_DVD_STRUCTURE)
+ MAKE_CASE(SCSIOP_REQUEST_VOL_ELEMENT)
+ MAKE_CASE(SCSIOP_SEND_VOLUME_TAG)
+ MAKE_CASE(SCSIOP_READ_ELEMENT_STATUS)
+ MAKE_CASE(SCSIOP_READ_CD_MSF)
+ MAKE_CASE(SCSIOP_SCAN_CD)
+ MAKE_CASE(SCSIOP_SET_CD_SPEED)
+ MAKE_CASE(SCSIOP_PLAY_CD)
+ MAKE_CASE(SCSIOP_MECHANISM_STATUS)
+ MAKE_CASE(SCSIOP_READ_CD)
+ MAKE_CASE(SCSIOP_SEND_DVD_STRUCTURE)
+ MAKE_CASE(SCSIOP_INIT_ELEMENT_RANGE)
+ MAKE_CASE(SCSIOP_READ16)
+ MAKE_CASE(SCSIOP_WRITE16)
+ MAKE_CASE(SCSIOP_VERIFY16)
+ MAKE_CASE(SCSIOP_SYNCHRONIZE_CACHE16)
+ MAKE_CASE(SCSIOP_READ_CAPACITY16)
+ }
+ }
+
+ return scsiOpStr;
+ }
+
+
+ char *DbgGetSrbStatusStr(PSTORAGE_REQUEST_BLOCK_HEADER Srb)
+ {
+ char *srbStatStr = "?";
+
+ switch (Srb->SrbStatus){
+
+ #undef MAKE_CASE
+ #define MAKE_CASE(srbStat) \
+ case srbStat: \
+ srbStatStr = #srbStat; \
+ break; \
+ case srbStat|SRB_STATUS_QUEUE_FROZEN: \
+ srbStatStr = #srbStat "|SRB_STATUS_QUEUE_FROZEN"; \
+ break; \
+ case srbStat|SRB_STATUS_AUTOSENSE_VALID: \
+ srbStatStr = #srbStat "|SRB_STATUS_AUTOSENSE_VALID"; \
+ break; \
+ case srbStat|SRB_STATUS_QUEUE_FROZEN|SRB_STATUS_AUTOSENSE_VALID: \
+ srbStatStr = #srbStat "|SRB_STATUS_QUEUE_FROZEN|SRB_STATUS_AUTOSENSE_VALID"; \
+ break;
+
+ MAKE_CASE(SRB_STATUS_PENDING)
+ MAKE_CASE(SRB_STATUS_SUCCESS)
+ MAKE_CASE(SRB_STATUS_ABORTED)
+ MAKE_CASE(SRB_STATUS_ABORT_FAILED)
+ MAKE_CASE(SRB_STATUS_ERROR)
+ MAKE_CASE(SRB_STATUS_BUSY)
+ MAKE_CASE(SRB_STATUS_INVALID_REQUEST)
+ MAKE_CASE(SRB_STATUS_INVALID_PATH_ID)
+ MAKE_CASE(SRB_STATUS_NO_DEVICE)
+ MAKE_CASE(SRB_STATUS_TIMEOUT)
+ MAKE_CASE(SRB_STATUS_SELECTION_TIMEOUT)
+ MAKE_CASE(SRB_STATUS_COMMAND_TIMEOUT)
+ MAKE_CASE(SRB_STATUS_MESSAGE_REJECTED)
+ MAKE_CASE(SRB_STATUS_BUS_RESET)
+ MAKE_CASE(SRB_STATUS_PARITY_ERROR)
+ MAKE_CASE(SRB_STATUS_REQUEST_SENSE_FAILED)
+ MAKE_CASE(SRB_STATUS_NO_HBA)
+ MAKE_CASE(SRB_STATUS_DATA_OVERRUN)
+ MAKE_CASE(SRB_STATUS_UNEXPECTED_BUS_FREE)
+ MAKE_CASE(SRB_STATUS_PHASE_SEQUENCE_FAILURE)
+ MAKE_CASE(SRB_STATUS_BAD_SRB_BLOCK_LENGTH)
+ MAKE_CASE(SRB_STATUS_REQUEST_FLUSHED)
+ MAKE_CASE(SRB_STATUS_INVALID_LUN)
+ MAKE_CASE(SRB_STATUS_INVALID_TARGET_ID)
+ MAKE_CASE(SRB_STATUS_BAD_FUNCTION)
+ MAKE_CASE(SRB_STATUS_ERROR_RECOVERY)
+ MAKE_CASE(SRB_STATUS_NOT_POWERED)
+ MAKE_CASE(SRB_STATUS_INTERNAL_ERROR)
+ }
+
+ return srbStatStr;
+ }
+
+
+ char *DbgGetSenseCodeStr(PSTORAGE_REQUEST_BLOCK_HEADER Srb)
+ {
+ char *senseCodeStr = "?";
+
+ if (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID){
+
+ PVOID senseData;
+ UCHAR senseCode;
+ BOOLEAN validSense;
+
+ senseData = SrbGetSenseInfoBuffer(Srb);
+ NT_ASSERT(senseData);
+
+ validSense = ScsiGetSenseKeyAndCodes(senseData,
+ SrbGetSenseInfoBufferLength(Srb),
+ SCSI_SENSE_OPTIONS_FIXED_FORMAT_IF_UNKNOWN_FORMAT_INDICATED,
+ &senseCode,
+ NULL,
+ NULL);
+ if (validSense) {
+ switch (senseCode){
+
+ #undef MAKE_CASE
+ #define MAKE_CASE(snsCod) case snsCod: senseCodeStr = #snsCod; break;
+
+ MAKE_CASE(SCSI_SENSE_NO_SENSE)
+ MAKE_CASE(SCSI_SENSE_RECOVERED_ERROR)
+ MAKE_CASE(SCSI_SENSE_NOT_READY)
+ MAKE_CASE(SCSI_SENSE_MEDIUM_ERROR)
+ MAKE_CASE(SCSI_SENSE_HARDWARE_ERROR)
+ MAKE_CASE(SCSI_SENSE_ILLEGAL_REQUEST)
+ MAKE_CASE(SCSI_SENSE_UNIT_ATTENTION)
+ MAKE_CASE(SCSI_SENSE_DATA_PROTECT)
+ MAKE_CASE(SCSI_SENSE_BLANK_CHECK)
+ MAKE_CASE(SCSI_SENSE_UNIQUE)
+ MAKE_CASE(SCSI_SENSE_COPY_ABORTED)
+ MAKE_CASE(SCSI_SENSE_ABORTED_COMMAND)
+ MAKE_CASE(SCSI_SENSE_EQUAL)
+ MAKE_CASE(SCSI_SENSE_VOL_OVERFLOW)
+ MAKE_CASE(SCSI_SENSE_MISCOMPARE)
+ MAKE_CASE(SCSI_SENSE_RESERVED)
+ }
+ }
+ }
+
+ return senseCodeStr;
+ }
+
+
+ char *DbgGetAdditionalSenseCodeStr(PSTORAGE_REQUEST_BLOCK_HEADER Srb)
+ {
+ char *adSenseCodeStr = "?";
+
+ if (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID){
+ PVOID senseData;
+ UCHAR adSenseCode;
+ BOOLEAN validSense;
+
+ senseData = SrbGetSenseInfoBuffer(Srb);
+ NT_ASSERT(senseData);
+
+ validSense = ScsiGetSenseKeyAndCodes(senseData,
+ SrbGetSenseInfoBufferLength(Srb),
+ SCSI_SENSE_OPTIONS_FIXED_FORMAT_IF_UNKNOWN_FORMAT_INDICATED,
+ NULL,
+ &adSenseCode,
+ NULL);
+
+ if (validSense) {
+ switch (adSenseCode){
+
+ #undef MAKE_CASE
+ #define MAKE_CASE(adSnsCod) case adSnsCod: adSenseCodeStr = #adSnsCod; break;
+
+ MAKE_CASE(SCSI_ADSENSE_NO_SENSE)
+ MAKE_CASE(SCSI_ADSENSE_LUN_NOT_READY)
+ MAKE_CASE(SCSI_ADSENSE_TRACK_ERROR)
+ MAKE_CASE(SCSI_ADSENSE_SEEK_ERROR)
+ MAKE_CASE(SCSI_ADSENSE_REC_DATA_NOECC)
+ MAKE_CASE(SCSI_ADSENSE_REC_DATA_ECC)
+ MAKE_CASE(SCSI_ADSENSE_ILLEGAL_COMMAND)
+ MAKE_CASE(SCSI_ADSENSE_ILLEGAL_BLOCK)
+ MAKE_CASE(SCSI_ADSENSE_INVALID_CDB)
+ MAKE_CASE(SCSI_ADSENSE_INVALID_LUN)
+ MAKE_CASE(SCSI_ADSENSE_WRITE_PROTECT) // aka SCSI_ADWRITE_PROTECT
+ MAKE_CASE(SCSI_ADSENSE_MEDIUM_CHANGED)
+ MAKE_CASE(SCSI_ADSENSE_BUS_RESET)
+ MAKE_CASE(SCSI_ADSENSE_INVALID_MEDIA)
+ MAKE_CASE(SCSI_ADSENSE_NO_MEDIA_IN_DEVICE)
+ MAKE_CASE(SCSI_ADSENSE_POSITION_ERROR)
+ MAKE_CASE(SCSI_ADSENSE_OPERATOR_REQUEST)
+ MAKE_CASE(SCSI_ADSENSE_FAILURE_PREDICTION_THRESHOLD_EXCEEDED)
+ MAKE_CASE(SCSI_ADSENSE_COPY_PROTECTION_FAILURE)
+ MAKE_CASE(SCSI_ADSENSE_VENDOR_UNIQUE)
+ MAKE_CASE(SCSI_ADSENSE_MUSIC_AREA)
+ MAKE_CASE(SCSI_ADSENSE_DATA_AREA)
+ MAKE_CASE(SCSI_ADSENSE_VOLUME_OVERFLOW)
+ }
+ }
+ }
+
+ return adSenseCodeStr;
+ }
+
+
+ char *DbgGetAdditionalSenseCodeQualifierStr(PSTORAGE_REQUEST_BLOCK_HEADER Srb)
+ {
+ char *adSenseCodeQualStr = "?";
+
+ if (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID){
+ PVOID senseData;
+ UCHAR adSenseCode;
+ UCHAR adSenseCodeQual;
+ BOOLEAN validSense;
+
+ senseData = SrbGetSenseInfoBuffer(Srb);
+ NT_ASSERT(senseData);
+
+ validSense = ScsiGetSenseKeyAndCodes(senseData,
+ SrbGetSenseInfoBufferLength(Srb),
+ SCSI_SENSE_OPTIONS_FIXED_FORMAT_IF_UNKNOWN_FORMAT_INDICATED,
+ NULL,
+ &adSenseCode,
+ &adSenseCodeQual);
+ if (validSense) {
+ switch (adSenseCode){
+
+ #undef MAKE_CASE
+ #define MAKE_CASE(adSnsCodQual) case adSnsCodQual: adSenseCodeQualStr = #adSnsCodQual; break;
+
+ case SCSI_ADSENSE_LUN_NOT_READY:
+ switch (adSenseCodeQual){
+ MAKE_CASE(SCSI_SENSEQ_CAUSE_NOT_REPORTABLE)
+ MAKE_CASE(SCSI_SENSEQ_BECOMING_READY)
+ MAKE_CASE(SCSI_SENSEQ_INIT_COMMAND_REQUIRED)
+ MAKE_CASE(SCSI_SENSEQ_MANUAL_INTERVENTION_REQUIRED)
+ MAKE_CASE(SCSI_SENSEQ_FORMAT_IN_PROGRESS)
+ MAKE_CASE(SCSI_SENSEQ_REBUILD_IN_PROGRESS)
+ MAKE_CASE(SCSI_SENSEQ_RECALCULATION_IN_PROGRESS)
+ MAKE_CASE(SCSI_SENSEQ_OPERATION_IN_PROGRESS)
+ MAKE_CASE(SCSI_SENSEQ_LONG_WRITE_IN_PROGRESS)
+ }
+ break;
+ case SCSI_ADSENSE_NO_SENSE:
+ switch (adSenseCodeQual){
+ MAKE_CASE(SCSI_SENSEQ_FILEMARK_DETECTED)
+ MAKE_CASE(SCSI_SENSEQ_END_OF_MEDIA_DETECTED)
+ MAKE_CASE(SCSI_SENSEQ_SETMARK_DETECTED)
+ MAKE_CASE(SCSI_SENSEQ_BEGINNING_OF_MEDIA_DETECTED)
+ }
+ break;
+ case SCSI_ADSENSE_ILLEGAL_BLOCK:
+ switch (adSenseCodeQual){
+ MAKE_CASE(SCSI_SENSEQ_ILLEGAL_ELEMENT_ADDR)
+ }
+ break;
+ case SCSI_ADSENSE_POSITION_ERROR:
+ switch (adSenseCodeQual){
+ MAKE_CASE(SCSI_SENSEQ_DESTINATION_FULL)
+ MAKE_CASE(SCSI_SENSEQ_SOURCE_EMPTY)
+ }
+ break;
+ case SCSI_ADSENSE_INVALID_MEDIA:
+ switch (adSenseCodeQual){
+ MAKE_CASE(SCSI_SENSEQ_INCOMPATIBLE_MEDIA_INSTALLED)
+ MAKE_CASE(SCSI_SENSEQ_UNKNOWN_FORMAT)
+ MAKE_CASE(SCSI_SENSEQ_INCOMPATIBLE_FORMAT)
+ MAKE_CASE(SCSI_SENSEQ_CLEANING_CARTRIDGE_INSTALLED)
+ }
+ break;
+ case SCSI_ADSENSE_OPERATOR_REQUEST:
+ switch (adSenseCodeQual){
+ MAKE_CASE(SCSI_SENSEQ_STATE_CHANGE_INPUT)
+ MAKE_CASE(SCSI_SENSEQ_MEDIUM_REMOVAL)
+ MAKE_CASE(SCSI_SENSEQ_WRITE_PROTECT_ENABLE)
+ MAKE_CASE(SCSI_SENSEQ_WRITE_PROTECT_DISABLE)
+ }
+ break;
+ case SCSI_ADSENSE_COPY_PROTECTION_FAILURE:
+ switch (adSenseCodeQual){
+ MAKE_CASE(SCSI_SENSEQ_AUTHENTICATION_FAILURE)
+ MAKE_CASE(SCSI_SENSEQ_KEY_NOT_PRESENT)
+ MAKE_CASE(SCSI_SENSEQ_KEY_NOT_ESTABLISHED)
+ MAKE_CASE(SCSI_SENSEQ_READ_OF_SCRAMBLED_SECTOR_WITHOUT_AUTHENTICATION)
+ MAKE_CASE(SCSI_SENSEQ_MEDIA_CODE_MISMATCHED_TO_LOGICAL_UNIT)
+ MAKE_CASE(SCSI_SENSEQ_LOGICAL_UNIT_RESET_COUNT_ERROR)
+ }
+ break;
+ }
+ }
+ }
+
+ return adSenseCodeQualStr;
+ }
+
+
diff --git a/storage/class/classpnp/src/debug.h b/storage/class/classpnp/src/debug.h
new file mode 100644
index 00000000..bd2e04ba
--- /dev/null
+++ b/storage/class/classpnp/src/debug.h
@@ -0,0 +1,132 @@
+/*++
+
+Copyright (C) Microsoft Corporation, 1991 - 1999
+
+Module Name:
+
+ debug.h
+
+Abstract:
+
+
+Author:
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+
+Revision History:
+
+--*/
+
+#define DBGGETIOCTLSTR(_ioctl) DbgGetIoctlStr(_ioctl)
+#define DBGGETSCSIOPSTR(_pSrb) DbgGetScsiOpStr(_pSrb)
+#define DBGGETSRBSTATUSSTR(_pSrb) DbgGetSrbStatusStr(_pSrb)
+#define DBGGETSENSECODESTR(_pSrb) DbgGetSenseCodeStr(_pSrb)
+#define DBGGETADSENSECODESTR(_pSrb) DbgGetAdditionalSenseCodeStr(_pSrb)
+#define DBGGETADSENSEQUALIFIERSTR(_pSrb) DbgGetAdditionalSenseCodeQualifierStr(_pSrb)
+
+char *DbgGetIoctlStr(ULONG ioctl);
+char *DbgGetScsiOpStr(PSTORAGE_REQUEST_BLOCK_HEADER Srb);
+char *DbgGetSrbStatusStr(PSTORAGE_REQUEST_BLOCK_HEADER Srb);
+char *DbgGetSenseCodeStr(PSTORAGE_REQUEST_BLOCK_HEADER Srb);
+char *DbgGetAdditionalSenseCodeStr(PSTORAGE_REQUEST_BLOCK_HEADER Srb);
+char *DbgGetAdditionalSenseCodeQualifierStr(PSTORAGE_REQUEST_BLOCK_HEADER Srb);
+
+#if DBG
+
+ typedef struct _CLASSPNP_GLOBALS {
+
+ //
+ // whether or not to NT_ASSERT for lost irps
+ //
+
+ ULONG BreakOnLostIrps;
+ ULONG SecondsToWaitForIrps;
+
+ //
+ // use a buffered debug print to help
+ // catch timing issues that do not
+ // reproduce with std debugprints enabled
+ //
+
+ ULONG UseBufferedDebugPrint;
+ ULONG UseDelayedRetry;
+
+ //
+ // the next four are the buffered printing support
+ // (currently unimplemented) and require the spinlock
+ // to use
+ //
+
+ ULONG Index; // index into buffer
+ KSPIN_LOCK SpinLock;
+ PUCHAR Buffer; // requires spinlock to access
+ ULONG NumberOfBuffers; // number of buffers available
+ SIZE_T EachBufferSize; // size of each buffer
+
+ //
+ // interlocked variables to initialize
+ // this data only once
+ //
+
+ LONG Initializing;
+ LONG Initialized;
+
+ } CLASSPNP_GLOBALS, *PCLASSPNP_GLOBALS;
+
+
+ //
+ // Define a structure used to capture stack traces when we
+ // get an access request and the disks are powered off. This
+ // will help us determine who's causing disk respins.
+ //
+
+ //
+ // How many stack frames to capture each time?
+ //
+ #define DISK_SPINUP_BACKTRACE_LENGTH (0x18)
+
+ //
+ // How many stack traces can we capture before
+ // out buffer wraps? (needs to be power of 2)
+ //
+ #define NUMBER_OF_DISK_SPINUP_TRACES (0x10)
+
+ typedef struct _DISK_SPINUP_TRACES {
+
+ LARGE_INTEGER TimeStamp; // timestamp of the spinup event.
+ PVOID StackTrace[DISK_SPINUP_BACKTRACE_LENGTH]; // Holds stack trace
+ } DISK_SPINUP_TRACES, *PDISK_SPINUP_TRACES;
+
+
+ #define DBGCHECKRETURNEDPKT(_pkt) DbgCheckReturnedPkt(_pkt)
+ #define DBGLOGSENDPACKET(_pkt) DbgLogSendPacket(_pkt)
+ #define DBGLOGRETURNPACKET(_pkt) DbgLogReturnPacket(_pkt)
+ #define DBGLOGFLUSHINFO(_fdoData, _isIO, _isFUA, _isFlush) DbgLogFlushInfo(_fdoData, _isIO, _isFUA, _isFlush)
+
+ VOID ClasspInitializeDebugGlobals();
+ VOID DbgCheckReturnedPkt(TRANSFER_PACKET *Pkt);
+ VOID DbgLogSendPacket(TRANSFER_PACKET *Pkt);
+ VOID DbgLogReturnPacket(TRANSFER_PACKET *Pkt);
+ VOID DbgLogFlushInfo(PCLASS_PRIVATE_FDO_DATA FdoData, BOOLEAN IsIO, BOOLEAN IsFUA, BOOLEAN IsFlush);
+ VOID SnapDiskStartup(VOID);
+ extern CLASSPNP_GLOBALS ClasspnpGlobals;
+ extern LONG ClassDebug;
+ extern BOOLEAN DebugTrapOnWarn;
+
+#else
+
+ #define ClasspInitializeDebugGlobals()
+ #define SnapDiskStartup()
+
+ #define DBGCHECKRETURNEDPKT(_pkt)
+ #define DBGLOGSENDPACKET(_pkt)
+ #define DBGLOGRETURNPACKET(_pkt)
+ #define DBGLOGFLUSHINFO(_fdoData, _isIO, _isFUA, _isFlush)
+
+#endif
+
diff --git a/storage/class/classpnp/src/dictlib.c b/storage/class/classpnp/src/dictlib.c
new file mode 100644
index 00000000..90a67117
--- /dev/null
+++ b/storage/class/classpnp/src/dictlib.c
@@ -0,0 +1,218 @@
+/*++
+
+Copyright (C) Microsoft Corporation, 1990 - 1999
+
+Module Name:
+
+ dictlib.c
+
+Abstract:
+
+ Support library for maintaining a dictionary list (list of objects
+ referenced by a key value).
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+ This module generates a static library
+
+Revision History:
+
+--*/
+
+#include <ntddk.h>
+#include <classpnp.h>
+
+#define DICTIONARY_SIGNATURE 'tciD'
+
+#pragma warning(push)
+#pragma warning(disable:4200) // nonstandard extension used : zero-sized array in struct/union
+struct _DICTIONARY_HEADER {
+ PDICTIONARY_HEADER Next;
+ ULONGLONG Key;
+ UCHAR Data[0];
+};
+#pragma warning(pop)
+
+struct _DICTIONARY_HEADER;
+typedef struct _DICTIONARY_HEADER DICTIONARY_HEADER, *PDICTIONARY_HEADER;
+
+
+VOID
+InitializeDictionary(
+ IN PDICTIONARY Dictionary
+ )
+{
+ RtlZeroMemory(Dictionary, sizeof(DICTIONARY));
+ Dictionary->Signature = DICTIONARY_SIGNATURE;
+ KeInitializeSpinLock(&Dictionary->SpinLock);
+ return;
+}
+
+
+BOOLEAN
+TestDictionarySignature(
+ IN PDICTIONARY Dictionary
+ )
+{
+ return Dictionary->Signature == DICTIONARY_SIGNATURE;
+}
+
+NTSTATUS
+AllocateDictionaryEntry(
+ IN PDICTIONARY Dictionary,
+ IN ULONGLONG Key,
+ _In_range_(0, sizeof(FILE_OBJECT_EXTENSION)) IN ULONG Size,
+ IN ULONG Tag,
+ OUT PVOID *Entry
+ )
+{
+ PDICTIONARY_HEADER header;
+ KIRQL oldIrql;
+ PDICTIONARY_HEADER *entry;
+
+ NTSTATUS status = STATUS_SUCCESS;
+
+ *Entry = NULL;
+
+ header = ExAllocatePoolWithTag(NonPagedPoolNx,
+ Size + sizeof(DICTIONARY_HEADER),
+ Tag);
+
+ if(header == NULL) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlZeroMemory(header, sizeof(DICTIONARY_HEADER) + Size);
+ header->Key = Key;
+
+ //
+ // Find the correct location for this entry in the dictionary.
+ //
+
+ KeAcquireSpinLock(&(Dictionary->SpinLock), &oldIrql);
+
+ TRY {
+
+ entry = &(Dictionary->List);
+
+ while(*entry != NULL) {
+ if((*entry)->Key == Key) {
+
+ //
+ // Dictionary must have unique keys.
+ //
+
+ status = STATUS_OBJECT_NAME_COLLISION;
+ LEAVE;
+
+ } else if ((*entry)->Key < Key) {
+
+ //
+ // We will go ahead and insert the key in here.
+ //
+ break;
+ } else {
+ entry = &((*entry)->Next);
+ }
+ }
+
+ //
+ // If we make it here then we will go ahead and do the insertion.
+ //
+
+ header->Next = *entry;
+ *entry = header;
+
+ } FINALLY {
+ KeReleaseSpinLock(&(Dictionary->SpinLock), oldIrql);
+
+ if(!NT_SUCCESS(status)) {
+ FREE_POOL(header);
+ } else {
+ *Entry = (PVOID) header->Data;
+ }
+ }
+ return status;
+}
+
+
+PVOID
+GetDictionaryEntry(
+ IN PDICTIONARY Dictionary,
+ IN ULONGLONG Key
+ )
+{
+ PDICTIONARY_HEADER entry;
+ PVOID data;
+ KIRQL oldIrql;
+
+
+ data = NULL;
+
+ KeAcquireSpinLock(&(Dictionary->SpinLock), &oldIrql);
+
+ entry = Dictionary->List;
+ while (entry != NULL) {
+
+ if (entry->Key == Key) {
+ data = entry->Data;
+ break;
+ } else {
+ entry = entry->Next;
+ }
+ }
+
+ KeReleaseSpinLock(&(Dictionary->SpinLock), oldIrql);
+
+ return data;
+}
+
+
+VOID
+FreeDictionaryEntry(
+ IN PDICTIONARY Dictionary,
+ IN PVOID Entry
+ )
+{
+ PDICTIONARY_HEADER header;
+ PDICTIONARY_HEADER *entry;
+ KIRQL oldIrql;
+ BOOLEAN found;
+
+ found = FALSE;
+ header = CONTAINING_RECORD(Entry, DICTIONARY_HEADER, Data);
+
+ KeAcquireSpinLock(&(Dictionary->SpinLock), &oldIrql);
+
+ entry = &(Dictionary->List);
+ while(*entry != NULL) {
+
+ if(*entry == header) {
+ *entry = header->Next;
+ found = TRUE;
+ break;
+ } else {
+ entry = &(*entry)->Next;
+ }
+ }
+
+ KeReleaseSpinLock(&(Dictionary->SpinLock), oldIrql);
+
+ //
+ // calling this w/an invalid pointer invalidates the dictionary system,
+ // so NT_ASSERT() that we never try to Free something not in the list
+ //
+
+ NT_ASSERT(found);
+ if (found) {
+ FREE_POOL(header);
+ }
+
+ return;
+
+}
+
diff --git a/storage/class/classpnp/src/dispatch.c b/storage/class/classpnp/src/dispatch.c
new file mode 100644
index 00000000..c0296d34
--- /dev/null
+++ b/storage/class/classpnp/src/dispatch.c
@@ -0,0 +1,131 @@
+/*++
+
+Copyright (C) Microsoft Corporation, 1991 - 2005
+
+Module Name:
+
+ dispatch.c
+
+Abstract:
+
+ Code to support multiple dispatch tables.
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+
+Revision History:
+
+--*/
+
+#include "classp.h"
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE, ClassInitializeDispatchTables)
+#endif
+
+DRIVER_DISPATCH ClassDispatchUnimplemented;
+
+//
+// Routines start
+//
+
+
+VOID
+ClassInitializeDispatchTables(
+ PCLASS_DRIVER_EXTENSION DriverExtension
+ )
+{
+ ULONG idx;
+
+ PAGED_CODE();
+
+ //
+ // Initialize the standard device dispatch table
+ //
+
+ for (idx = 0; idx <= IRP_MJ_MAXIMUM_FUNCTION; idx++) {
+ DriverExtension->DeviceMajorFunctionTable[idx] = ClassDispatchUnimplemented;
+ }
+
+ DriverExtension->DeviceMajorFunctionTable[IRP_MJ_CREATE] = ClassCreateClose;
+ DriverExtension->DeviceMajorFunctionTable[IRP_MJ_CLOSE] = ClassCreateClose;
+ DriverExtension->DeviceMajorFunctionTable[IRP_MJ_READ] = ClassReadWrite;
+ DriverExtension->DeviceMajorFunctionTable[IRP_MJ_WRITE] = ClassReadWrite;
+ DriverExtension->DeviceMajorFunctionTable[IRP_MJ_DEVICE_CONTROL] = ClassDeviceControlDispatch;
+ DriverExtension->DeviceMajorFunctionTable[IRP_MJ_SCSI] = ClassInternalIoControl;
+ DriverExtension->DeviceMajorFunctionTable[IRP_MJ_SHUTDOWN] = ClassShutdownFlush;
+ DriverExtension->DeviceMajorFunctionTable[IRP_MJ_FLUSH_BUFFERS] = ClassShutdownFlush;
+ DriverExtension->DeviceMajorFunctionTable[IRP_MJ_PNP] = ClassDispatchPnp;
+ DriverExtension->DeviceMajorFunctionTable[IRP_MJ_POWER] = ClassDispatchPower;
+ DriverExtension->DeviceMajorFunctionTable[IRP_MJ_SYSTEM_CONTROL] = ClassSystemControl;
+
+
+ return;
+}
+
+
+NTSTATUS
+ClassGlobalDispatch(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
+ // Code Analysis cannot analyze the code paths specific to clients.
+ _Analysis_assume_(FALSE);
+ return (commonExtension->DispatchTable[irpStack->MajorFunction])(DeviceObject, Irp);
+
+}
+
+NTSTATUS
+ClassDispatchUnimplemented(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+/*++
+
+Routine Description:
+
+ This function is the default dispatch routine. Its
+ responsibility is simply to set the status in the packet to indicate
+ that the operation requested is invalid for this device type, and then
+ complete the packet.
+
+Arguments:
+
+ DeviceObject - Specifies the device object for which this request is
+ bound. Ignored by this routine.
+
+ Irp - Specifies the address of the I/O Request Packet (IRP) for this
+ request.
+
+Return Value:
+
+ The final status is always STATUS_INVALID_DEVICE_REQUEST.
+
+
+--*/
+
+{
+ UNREFERENCED_PARAMETER( DeviceObject );
+
+ //
+ // Simply store the appropriate status, complete the request, and return
+ // the same status stored in the packet.
+ //
+
+ if ((IoGetCurrentIrpStackLocation(Irp))->MajorFunction == IRP_MJ_POWER) {
+ PoStartNextPowerIrp(Irp);
+ }
+ Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
+ IoCompleteRequest( Irp, IO_NO_INCREMENT );
+ return STATUS_INVALID_DEVICE_REQUEST;
+}
+
+
diff --git a/storage/class/classpnp/src/history.c b/storage/class/classpnp/src/history.c
new file mode 100644
index 00000000..c8505774
--- /dev/null
+++ b/storage/class/classpnp/src/history.c
@@ -0,0 +1,146 @@
+/*++
+
+
+Copyright (C) Microsoft Corporation, 1991 - 1999
+
+Module Name:
+
+ history.c
+
+Abstract:
+
+ Packet history routines for CLASSPNP
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+
+Revision History:
+
+--*/
+
+#include "classp.h"
+#include "debug.h"
+
+#ifdef DEBUG_USE_WPP
+#include "history.tmh"
+#endif
+
+//#ifdef ALLOC_PRAGMA
+// #pragma alloc_text(PAGE, InitializeTransferPackets)
+//#endif
+
+VOID HistoryInitializeRetryLogs(_Out_ PSRB_HISTORY History, ULONG HistoryCount) {
+ ULONG tmpSize = HistoryCount * sizeof(SRB_HISTORY_ITEM);
+ tmpSize += sizeof(SRB_HISTORY) - sizeof(SRB_HISTORY_ITEM);
+ RtlZeroMemory(History, tmpSize);
+ History->TotalHistoryCount = HistoryCount;
+ return;
+}
+
+
+VOID HistoryLogSendPacket(TRANSFER_PACKET * Pkt) {
+
+ PSRB_HISTORY history;
+ PSRB_HISTORY_ITEM item;
+
+ NT_ASSERT( Pkt->RetryHistory != NULL );
+ history = Pkt->RetryHistory;
+ // sending a packet implies a new history unit is to be used.
+ NT_ASSERT( history->UsedHistoryCount <= history->TotalHistoryCount );
+
+ // if already all used up, request class driver to remove at least one history unit
+ if (history->UsedHistoryCount == history->TotalHistoryCount )
+ {
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Pkt->Fdo->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
+ NT_ASSERT( fdoData->InterpretSenseInfo != NULL );
+ NT_ASSERT( fdoData->InterpretSenseInfo->Compress != NULL );
+ fdoData->InterpretSenseInfo->Compress( fdoExtension->DeviceObject, history );
+ NT_ASSERT( history->UsedHistoryCount < history->TotalHistoryCount );
+ }
+
+ // thus, since we are about to increment the count, it must now be less...
+ NT_ASSERT( history->UsedHistoryCount < history->TotalHistoryCount );
+
+ // increment the number of history units in use
+ history->UsedHistoryCount++;
+
+ // determine index to use
+ item = &( history->History[ history->UsedHistoryCount-1 ] );
+
+ // zero out the history item
+ RtlZeroMemory(item, sizeof(SRB_HISTORY_ITEM));
+
+ // Query the tick count and store in the history
+ KeQueryTickCount(&item->TickCountSent);
+ return;
+}
+
+VOID HistoryLogReturnedPacket(TRANSFER_PACKET *Pkt) {
+
+ PSRB_HISTORY history;
+ PSRB_HISTORY_ITEM item;
+ UCHAR senseSize;
+ PVOID senseInfoBuffer;
+ UCHAR senseInfoBufferLength;
+ SENSE_DATA convertedSenseBuffer = {0};
+ BOOLEAN validSense = TRUE;
+
+ NT_ASSERT( Pkt->RetryHistory != NULL );
+ history = Pkt->RetryHistory;
+ NT_ASSERT( history->UsedHistoryCount <= history->TotalHistoryCount );
+ item = &( history->History[ history->UsedHistoryCount-1 ] );
+
+ // Query the tick count and store in the history
+ KeQueryTickCount(&item->TickCountCompleted);
+
+ // Copy the SRB Status...
+ item->SrbStatus = Pkt->Srb->SrbStatus;
+
+ //
+ // Process sense data
+ //
+
+ senseInfoBuffer = ClasspTransferPacketGetSenseInfoBuffer(Pkt);
+ senseInfoBufferLength = ClasspTransferPacketGetSenseInfoBufferLength(Pkt);
+
+ if (IsDescriptorSenseDataFormat(senseInfoBuffer)) {
+
+ validSense = ScsiConvertToFixedSenseFormat(senseInfoBuffer,
+ senseInfoBufferLength,
+ (PVOID)&convertedSenseBuffer,
+ sizeof(convertedSenseBuffer));
+
+ if (validSense) {
+ senseInfoBuffer = (PVOID)&convertedSenseBuffer;
+ senseInfoBufferLength = sizeof(convertedSenseBuffer);
+ }
+ }
+
+ RtlZeroMemory(&(item->NormalizedSenseData), sizeof(item->NormalizedSenseData));
+
+ if (validSense) {
+
+ // Determine the amount of valid sense data
+
+ if (!ScsiGetTotalSenseByteCountIndicated(senseInfoBuffer,
+ senseInfoBufferLength,
+ &senseSize)) {
+ senseSize = senseInfoBufferLength;
+ }
+
+ // Normalize the sense data copy in the history
+ senseSize = min(senseSize, sizeof(item->NormalizedSenseData));
+ RtlCopyMemory(&(item->NormalizedSenseData),
+ senseInfoBuffer,
+ senseSize
+ );
+ }
+
+ return;
+}
+
diff --git a/storage/class/classpnp/src/lock.c b/storage/class/classpnp/src/lock.c
new file mode 100644
index 00000000..1b5a8862
--- /dev/null
+++ b/storage/class/classpnp/src/lock.c
@@ -0,0 +1,550 @@
+/*++
+
+Copyright (C) Microsoft Corporation, 1990 - 1998
+
+Module Name:
+
+ lock.c
+
+Abstract:
+
+ This is the NT SCSI port driver.
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+ This module is a driver dll for scsi miniports.
+
+Revision History:
+
+--*/
+
+#include "classp.h"
+#include "debug.h"
+
+#ifdef DEBUG_USE_WPP
+#include "lock.tmh"
+#endif
+
+
+LONG LockHighWatermark = 0;
+LONG LockLowWatermark = 0;
+LONG MaxLockedMinutes = 5;
+
+//
+// Structure used for tracking remove lock allocations in checked builds
+//
+typedef struct _REMOVE_TRACKING_BLOCK {
+ PVOID Tag;
+ LARGE_INTEGER TimeLocked;
+ PCSTR File;
+ ULONG Line;
+} REMOVE_TRACKING_BLOCK, *PREMOVE_TRACKING_BLOCK;
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+Classpnp RemoveLockRundown
+
+RemoveLockRundown is a cacheaware rundown protection for the classpnp device object. While this
+rundown protection is held successfully, the caller can assume that no pending pnp REMOVE
+requests will be completed.
+
+The RemoveLockRundown is a replacement of the original RemoveLock to improve the scalability.
+For backward compatibility, we still keep the RemoveLock field in the device common extension structure.
+However, the old RemoveLock is only being used in the DBG build.
+
+The usage of the RemoveLockRundown is slightly different from the normal rundown protection usage.
+The RemoveLockRundown is acquired via ClassAcquireRemoveLockEx() function
+and released via ClassReleaseRemoveLock() function. Usually, we bail out when the acquisition
+of rundown protection fails (calls to ExAcquireRundownProtectionCacheAware returns FALSE) and
+will not release the rundown protection in acquisition failure. For the RemoveLockRundown,
+the caller will always call ClassAcquireRemoveLockEx() and ClassReleaseRemoveLock() in a pair no
+matter the return value of ClassAcquireRemoveLockEx(). Therefore, a thread may still call
+ClassReleaseRemoveLock() even the previous acquisition RemoveLockRundown protection failed.
+
+To deal with the previous acquisition failure case, we introduced a new field RemoveLockFailAcquire
+as a counter for rundown acquisition failures. In the ClassReleaseRemoveLock() function, we only
+release the rundown protection when this counter is decremented to zero. Since the change of RemoveLockFailAcquire
+and release rundown protection is not protected by a lock as an atomic operation, we use a while loop over
+InterlockedCompareExchange operation to make sure when we release the rundown protection, this counter is
+actually zero.
+
+--*/
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassAcquireRemoveLockEx()
+
+Routine Description:
+
+ This routine is called to acquire the remove lock on the device object.
+ While the lock is held, the caller can assume that no pending pnp REMOVE
+ requests will be completed.
+
+ The lock should be acquired immediately upon entering a dispatch routine.
+ It should also be acquired before creating any new reference to the
+ device object if there's a chance of releasing the reference before the
+ new one is done.
+
+ This routine will return TRUE if the lock was successfully acquired or
+ FALSE if it cannot be because the device object has already been removed.
+
+Arguments:
+
+ DeviceObject - the device object to lock
+
+ Tag - Used for tracking lock allocation and release. If an irp is
+ specified when acquiring the lock then the same Tag must be
+ used to release the lock before the Tag is completed.
+
+Return Value:
+
+ The value of the IsRemoved flag in the device extension. If this is
+ non-zero then the device object has received a Remove irp and non-cleanup
+ IRP's should fail.
+
+ If the value is REMOVE_COMPLETE, the caller should not even release the
+ lock.
+
+--*/
+ULONG
+ClassAcquireRemoveLockEx(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _In_ PVOID Tag,
+ _In_ PCSTR File,
+ _In_ ULONG Line
+ )
+// This function implements the acquisition of Tag
+#pragma warning(suppress:28104)
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ BOOLEAN rundownAcquired;
+ PEX_RUNDOWN_REF_CACHE_AWARE removeLockRundown = NULL;
+
+ //
+ // Grab the remove lock
+ //
+
+ #if DBG
+
+ LONG lockValue;
+
+ lockValue = InterlockedIncrement(&commonExtension->RemoveLock);
+
+
+ TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_LOCK, "ClassAcquireRemoveLock: "
+ "Acquired for Object %p & irp %p - count is %d\n",
+ DeviceObject, Tag, lockValue));
+
+ NT_ASSERTMSG("ClassAcquireRemoveLock - lock value was negative : ",
+ (lockValue > 0));
+
+ NT_ASSERTMSG("RemoveLock increased to meet LockHighWatermark",
+ ((LockHighWatermark == 0) ||
+ (lockValue != LockHighWatermark)));
+
+ if (commonExtension->IsRemoved != REMOVE_COMPLETE) {
+ PRTL_GENERIC_TABLE removeTrackingList = NULL;
+ REMOVE_TRACKING_BLOCK trackingBlock;
+ PREMOVE_TRACKING_BLOCK insertedTrackingBlock = NULL;
+ BOOLEAN newElement = FALSE;
+
+ KIRQL oldIrql;
+
+ trackingBlock.Tag = Tag;
+
+ trackingBlock.File = File;
+ trackingBlock.Line = Line;
+
+ KeQueryTickCount((&trackingBlock.TimeLocked));
+
+ KeAcquireSpinLock(&commonExtension->RemoveTrackingSpinlock,
+ &oldIrql);
+
+ removeTrackingList = commonExtension->RemoveTrackingList;
+
+ if (removeTrackingList != NULL) {
+ insertedTrackingBlock = RtlInsertElementGenericTable(removeTrackingList,
+ &trackingBlock,
+ sizeof(REMOVE_TRACKING_BLOCK),
+ &newElement);
+ }
+
+ if (insertedTrackingBlock != NULL) {
+ if (!newElement) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_LOCK, ">>>>>ClassAcquireRemoveLock: "
+ "already tracking Tag %p\n", Tag));
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_LOCK, ">>>>>ClassAcquireRemoveLock: "
+ "acquired in file %s on line %d\n",
+ insertedTrackingBlock->File, insertedTrackingBlock->Line));
+// NT_ASSERT(FALSE);
+
+ }
+ } else {
+ commonExtension->RemoveTrackingUntrackedCount++;
+
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_LOCK, ">>>>>ClassAcquireRemoveLock: "
+ "Cannot track Tag %p - currently %d untracked requsts\n",
+ Tag, commonExtension->RemoveTrackingUntrackedCount));
+ }
+
+ KeReleaseSpinLock(&commonExtension->RemoveTrackingSpinlock, oldIrql);
+ }
+ #else
+
+ UNREFERENCED_PARAMETER(Tag);
+ UNREFERENCED_PARAMETER(File);
+ UNREFERENCED_PARAMETER(Line);
+
+ #endif
+
+ removeLockRundown = (PEX_RUNDOWN_REF_CACHE_AWARE)
+ ((PCHAR)commonExtension->PrivateCommonData + sizeof(CLASS_PRIVATE_COMMON_DATA));
+ rundownAcquired = ExAcquireRundownProtectionCacheAware(removeLockRundown);
+ if (!rundownAcquired) {
+ InterlockedIncrement((volatile LONG*) &(commonExtension->PrivateCommonData->RemoveLockFailAcquire));
+ TracePrint((TRACE_LEVEL_VERBOSE,
+ TRACE_FLAG_LOCK,
+ "ClassAcquireRemoveLockEx: RemoveLockRundown acquisition failed"
+ "RemoveLockFailAcquire = %d\n",
+ commonExtension->PrivateCommonData->RemoveLockFailAcquire));
+ }
+
+ return (commonExtension->IsRemoved);
+}
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassReleaseRemoveLock()
+
+Routine Description:
+
+ This routine is called to release the remove lock on the device object. It
+ must be called when finished using a previously locked reference to the
+ device object. If an Tag was specified when acquiring the lock then the
+ same Tag must be specified when releasing the lock.
+
+ When the lock count reduces to zero, this routine will signal the waiting
+ remove Tag to delete the device object. As a result the DeviceObject
+ pointer should not be used again once the lock has been released.
+
+Arguments:
+
+ DeviceObject - the device object to lock
+
+ Tag - The irp (if any) specified when acquiring the lock. This is used
+ for lock tracking purposes
+
+Return Value:
+
+ none
+
+--*/
+VOID
+ClassReleaseRemoveLock(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _In_opt_ PIRP Tag
+ )
+// This function implements the release of Tag
+#pragma warning(suppress:28103)
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ LONG lockValue;
+ LONG oldValue;
+ PEX_RUNDOWN_REF_CACHE_AWARE removeLockRundown = NULL;
+
+ #if DBG
+ PRTL_GENERIC_TABLE removeTrackingList = NULL;
+ REMOVE_TRACKING_BLOCK searchDataBlock;
+
+ BOOLEAN found = FALSE;
+
+ BOOLEAN isRemoved = (commonExtension->IsRemoved == REMOVE_COMPLETE);
+
+ KIRQL oldIrql;
+
+ if (isRemoved) {
+ TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_LOCK, "ClassReleaseRemoveLock: REMOVE_COMPLETE set; this should never happen"));
+ InterlockedDecrement(&(commonExtension->RemoveLock));
+ return;
+ }
+
+ KeAcquireSpinLock(&commonExtension->RemoveTrackingSpinlock,
+ &oldIrql);
+
+ removeTrackingList = commonExtension->RemoveTrackingList;
+
+ if (removeTrackingList != NULL) {
+ searchDataBlock.Tag = Tag;
+ found = RtlDeleteElementGenericTable(removeTrackingList, &searchDataBlock);
+ }
+
+ if (!found) {
+ if(commonExtension->RemoveTrackingUntrackedCount == 0) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_LOCK, ">>>>>ClassReleaseRemoveLock: "
+ "Couldn't find Tag %p in the lock tracking list\n", Tag));
+ //
+ // This might happen if the device is being removed and the tracking list
+ // has already been freed. Don't assert if that is the case.
+ //
+ NT_ASSERT((removeTrackingList == NULL) && (commonExtension->IsRemoved != NO_REMOVE));
+ } else {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_LOCK, ">>>>>ClassReleaseRemoveLock: "
+ "Couldn't find Tag %p in the lock tracking list - "
+ "may be one of the %d untracked requests still outstanding\n",
+ Tag, commonExtension->RemoveTrackingUntrackedCount));
+
+ commonExtension->RemoveTrackingUntrackedCount--;
+ NT_ASSERT(commonExtension->RemoveTrackingUntrackedCount >= 0);
+ }
+ }
+
+ KeReleaseSpinLock(&commonExtension->RemoveTrackingSpinlock,
+ oldIrql);
+
+ lockValue = InterlockedDecrement(&commonExtension->RemoveLock);
+
+ TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_LOCK, "ClassReleaseRemoveLock: "
+ "Released for Object %p & irp %p - count is %d\n",
+ DeviceObject, Tag, lockValue));
+
+ NT_ASSERT(lockValue >= 0);
+
+ NT_ASSERTMSG("RemoveLock decreased to meet LockLowWatermark",
+ ((LockLowWatermark == 0) || !(lockValue == LockLowWatermark)));
+
+ if (lockValue == 0) {
+
+ NT_ASSERT(commonExtension->IsRemoved);
+
+ //
+ // The device needs to be removed. Signal the remove event
+ // that it's safe to go ahead.
+ //
+
+ TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_LOCK, "ClassReleaseRemoveLock: "
+ "Release for object %p & irp %p caused lock to go to zero\n",
+ DeviceObject, Tag));
+
+ }
+
+ #else
+
+ UNREFERENCED_PARAMETER(Tag);
+
+ #endif
+
+ //
+ // Decrement the RemoveLockFailAcquire by 1 when RemoveLockFailAcquire is non-zero.
+ // Release the RemoveLockRundown only when RemoveLockFailAcquire is zero.
+ //
+
+ oldValue = 1;
+ lockValue = commonExtension->PrivateCommonData->RemoveLockFailAcquire;
+ while (lockValue != 0) {
+ oldValue =
+ InterlockedCompareExchange((volatile LONG *) &commonExtension->PrivateCommonData->RemoveLockFailAcquire,
+ lockValue - 1,
+ lockValue);
+
+ if (oldValue == lockValue) {
+ break;
+ }
+
+ lockValue = oldValue;
+ }
+
+ if (lockValue == 0) {
+ removeLockRundown = (PEX_RUNDOWN_REF_CACHE_AWARE)
+ ((PCHAR)commonExtension->PrivateCommonData + sizeof(CLASS_PRIVATE_COMMON_DATA));
+ ExReleaseRundownProtectionCacheAware(removeLockRundown);
+ }
+
+ return;
+}
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassCompleteRequest()
+
+Routine Description:
+
+ This routine is a wrapper around (and should be used instead of)
+ IoCompleteRequest. It is used primarily for debugging purposes.
+ The routine will assert if the Irp being completed is still holding
+ the release lock.
+
+Arguments:
+
+ DeviceObject - the device object that was handling this request
+
+ Irp - the irp to be completed by IoCompleteRequest
+
+ PriorityBoost - the priority boost to pass to IoCompleteRequest
+
+Return Value:
+
+ none
+
+--*/
+VOID
+ClassCompleteRequest(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _In_ PIRP Irp,
+ _In_ CCHAR PriorityBoost
+ )
+{
+ #if DBG
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+
+ PRTL_GENERIC_TABLE removeTrackingList = NULL;
+ REMOVE_TRACKING_BLOCK searchDataBlock;
+ PREMOVE_TRACKING_BLOCK foundTrackingBlock;
+
+ KIRQL oldIrql;
+
+ KeAcquireSpinLock(&commonExtension->RemoveTrackingSpinlock, &oldIrql);
+
+ removeTrackingList = commonExtension->RemoveTrackingList;
+
+ if (removeTrackingList != NULL)
+ {
+ searchDataBlock.Tag = Irp;
+
+ foundTrackingBlock = RtlLookupElementGenericTable(removeTrackingList, &searchDataBlock);
+
+ if(foundTrackingBlock != NULL) {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_LOCK, ">>>>>ClassCompleteRequest: "
+ "Irp %p completed while still holding the remove lock\n", Irp));
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_LOCK, ">>>>>ClassCompleteRequest: "
+ "Lock acquired in file %s on line %d\n",
+ foundTrackingBlock->File, foundTrackingBlock->Line));
+ NT_ASSERT(FALSE);
+ }
+ }
+
+ KeReleaseSpinLock(&commonExtension->RemoveTrackingSpinlock, oldIrql);
+ #endif
+
+
+ UNREFERENCED_PARAMETER(DeviceObject);
+
+ IoCompleteRequest(Irp, PriorityBoost);
+ return;
+} // end ClassCompleteRequest()
+
+
+RTL_GENERIC_COMPARE_RESULTS
+RemoveTrackingCompareRoutine(
+ PRTL_GENERIC_TABLE Table,
+ PVOID FirstStruct,
+ PVOID SecondStruct
+ )
+{
+ PVOID tag1, tag2;
+
+ UNREFERENCED_PARAMETER(Table);
+
+ tag1 = ((PREMOVE_TRACKING_BLOCK)FirstStruct)->Tag;
+ tag2 = ((PREMOVE_TRACKING_BLOCK)SecondStruct)->Tag;
+
+ if (tag1 < tag2)
+ {
+ return GenericLessThan;
+ }
+ else if (tag1 > tag2)
+ {
+ return GenericGreaterThan;
+ }
+
+ return GenericEqual;
+}
+
+PVOID
+RemoveTrackingAllocateRoutine(
+ PRTL_GENERIC_TABLE Table,
+ CLONG ByteSize
+ )
+{
+ UNREFERENCED_PARAMETER(Table);
+
+ return ExAllocatePoolWithTag(NonPagedPoolNx, ByteSize, CLASS_TAG_LOCK_TRACKING);
+}
+
+VOID
+RemoveTrackingFreeRoutine(
+ PRTL_GENERIC_TABLE Table,
+ PVOID Buffer
+ )
+{
+ UNREFERENCED_PARAMETER(Table);
+
+ FREE_POOL(Buffer);
+}
+
+VOID
+ClasspInitializeRemoveTracking(
+ _In_ PDEVICE_OBJECT DeviceObject
+ )
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+
+ #if DBG
+ KeInitializeSpinLock(&commonExtension->RemoveTrackingSpinlock);
+
+ commonExtension->RemoveTrackingList = ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(RTL_GENERIC_TABLE), CLASS_TAG_LOCK_TRACKING);
+
+ if (commonExtension->RemoveTrackingList != NULL)
+ {
+ RtlInitializeGenericTable(commonExtension->RemoveTrackingList,
+ RemoveTrackingCompareRoutine,
+ RemoveTrackingAllocateRoutine,
+ RemoveTrackingFreeRoutine,
+ NULL);
+ }
+ #else
+
+ UNREFERENCED_PARAMETER(DeviceObject);
+
+ commonExtension->RemoveTrackingSpinlock = (ULONG_PTR) -1;
+ commonExtension->RemoveTrackingList = NULL;
+ #endif
+}
+
+VOID
+ClasspUninitializeRemoveTracking(
+ _In_ PDEVICE_OBJECT DeviceObject
+ )
+{
+ #if DBG
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PRTL_GENERIC_TABLE removeTrackingList = commonExtension->RemoveTrackingList;
+
+ ASSERTMSG("Removing the device while still holding remove locks",
+ commonExtension->RemoveTrackingUntrackedCount == 0 &&
+ removeTrackingList != NULL ? RtlNumberGenericTableElements(removeTrackingList) == 0 : TRUE);
+
+ if (removeTrackingList != NULL)
+ {
+ KIRQL oldIrql;
+ KeAcquireSpinLock(&commonExtension->RemoveTrackingSpinlock, &oldIrql);
+
+ FREE_POOL(removeTrackingList);
+ commonExtension->RemoveTrackingList = NULL;
+
+ KeReleaseSpinLock(&commonExtension->RemoveTrackingSpinlock, oldIrql);
+ }
+
+ #else
+
+ UNREFERENCED_PARAMETER(DeviceObject);
+ #endif
+}
+
+
+
+
diff --git a/storage/class/classpnp/src/obsolete.c b/storage/class/classpnp/src/obsolete.c
new file mode 100644
index 00000000..aeeef81d
--- /dev/null
+++ b/storage/class/classpnp/src/obsolete.c
@@ -0,0 +1,1125 @@
+/*++
+
+Copyright (C) Microsoft Corporation, 1991 - 2010
+
+Module Name:
+
+ obsolete.c
+
+Abstract:
+
+ THESE ARE EXPORTED CLASSPNP FUNCTIONS (and their subroutines)
+ WHICH ARE NOW OBSOLETE.
+ BUT WE NEED TO KEEP THEM AROUND FOR LEGACY REASONS.
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+
+Revision History:
+
+--*/
+
+#include "classp.h"
+#include "debug.h"
+
+#ifdef DEBUG_USE_WPP
+#include "obsolete.tmh"
+#endif
+
+PIRP ClassRemoveCScanList(IN PCSCAN_LIST List);
+VOID ClasspInitializeCScanList(IN PCSCAN_LIST List);
+
+#ifdef ALLOC_PRAGMA
+ #pragma alloc_text(PAGE, ClassDeleteSrbLookasideList)
+ #pragma alloc_text(PAGE, ClassInitializeSrbLookasideList)
+ #pragma alloc_text(PAGE, ClasspInitializeCScanList)
+#endif
+
+typedef struct _CSCAN_LIST_ENTRY {
+ LIST_ENTRY Entry;
+ ULONGLONG BlockNumber;
+} CSCAN_LIST_ENTRY, *PCSCAN_LIST_ENTRY;
+
+
+
+
+
+/*
+ * ClassSplitRequest
+ *
+ * This is a legacy exported function.
+ * It is called by storage miniport driver that have their own
+ * StartIo routine when the transfer size is too large for the hardware.
+ * We map it to our new read/write handler.
+ */
+VOID ClassSplitRequest(_In_ PDEVICE_OBJECT Fdo, _In_ PIRP Irp, _In_ ULONG MaximumBytes)
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
+
+ if (MaximumBytes > fdoData->HwMaxXferLen) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_RW, "ClassSplitRequest - driver requesting split to size that "
+ "hardware is unable to handle!\n"));
+ }
+
+ if (MaximumBytes < fdoData->HwMaxXferLen){
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_RW, "ClassSplitRequest - driver requesting smaller HwMaxXferLen "
+ "than required"));
+ fdoData->HwMaxXferLen = MAX(MaximumBytes, PAGE_SIZE);
+ }
+
+ ServiceTransferRequest(Fdo, Irp, FALSE);
+}
+
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassIoCompleteAssociated()
+
+Routine Description:
+
+ This routine executes when the port driver has completed a request.
+ It looks at the SRB status in the completing SRB and if not success
+ it checks for valid request sense buffer information. If valid, the
+ info is used to update status with more precise message of type of
+ error. This routine deallocates the SRB. This routine is used for
+ requests which were build by split request. After it has processed
+ the request it decrements the Irp count in the master Irp. If the
+ count goes to zero then the master Irp is completed.
+
+Arguments:
+
+ Fdo - Supplies the functional device object which represents the target.
+
+ Irp - Supplies the Irp which has completed.
+
+ Context - Supplies a pointer to the SRB.
+
+Return Value:
+
+ NT status
+
+--*/
+NTSTATUS
+ClassIoCompleteAssociated(
+ IN PDEVICE_OBJECT Fdo,
+ IN PIRP Irp,
+ IN PVOID Context
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
+ PSCSI_REQUEST_BLOCK srb = Context;
+
+ PIRP originalIrp = Irp->AssociatedIrp.MasterIrp;
+ LONG irpCount;
+
+ NTSTATUS status;
+ BOOLEAN retry;
+
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassIoCompleteAssociated is OBSOLETE !"));
+
+ //
+ // Check SRB status for success of completing request.
+ //
+ if (SRB_STATUS(srb->SrbStatus) != SRB_STATUS_SUCCESS) {
+
+ LONGLONG retryInterval;
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassIoCompleteAssociated: IRP %p, SRB %p", Irp, srb));
+
+ //
+ // Release the queue if it is frozen.
+ //
+
+ if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) {
+ ClassReleaseQueue(Fdo);
+ }
+
+ retry = InterpretSenseInfoWithoutHistory(
+ Fdo,
+ Irp,
+ srb,
+ irpStack->MajorFunction,
+ irpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL ?
+ irpStack->Parameters.DeviceIoControl.IoControlCode :
+ 0,
+ MAXIMUM_RETRIES -
+ ((ULONG)(ULONG_PTR)irpStack->Parameters.Others.Argument4),
+ &status,
+ &retryInterval);
+
+ //
+ // If the status is verified required and the this request
+ // should bypass verify required then retry the request.
+ //
+
+ if (irpStack->Flags & SL_OVERRIDE_VERIFY_VOLUME &&
+ status == STATUS_VERIFY_REQUIRED) {
+
+ status = STATUS_IO_DEVICE_ERROR;
+ retry = TRUE;
+ }
+
+#pragma warning(suppress:4213) // okay to cast Arg4 as a ulong for this use case
+ if (retry && ((ULONG)(ULONG_PTR)irpStack->Parameters.Others.Argument4)--) {
+
+ //
+ // Retry request. If the class driver has supplied a StartIo,
+ // call it directly for retries.
+ //
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "Retry request %p\n", Irp));
+
+ if (PORT_ALLOCATED_SENSE(fdoExtension, srb)) {
+ FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, srb);
+ }
+
+ RetryRequest(Fdo, Irp, srb, TRUE, retryInterval);
+
+ return STATUS_MORE_PROCESSING_REQUIRED;
+ }
+
+ } else {
+
+ //
+ // Set status for successful request.
+ //
+
+ status = STATUS_SUCCESS;
+
+ } // end if (SRB_STATUS(srb->SrbStatus) ...
+
+ //
+ // Return SRB to list.
+ //
+
+ if (PORT_ALLOCATED_SENSE(fdoExtension, srb)) {
+ FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, srb);
+ }
+
+ ClassFreeOrReuseSrb(fdoExtension, srb);
+
+ //
+ // Set status in completing IRP.
+ //
+
+ Irp->IoStatus.Status = status;
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassIoCompleteAssociated: Partial xfer IRP %p\n", Irp));
+
+ //
+ // Get next stack location. This original request is unused
+ // except to keep track of the completing partial IRPs so the
+ // stack location is valid.
+ //
+
+ irpStack = IoGetNextIrpStackLocation(originalIrp);
+
+ //
+ // Update status only if error so that if any partial transfer
+ // completes with error, then the original IRP will return with
+ // error. If any of the asynchronous partial transfer IRPs fail,
+ // with an error then the original IRP will return 0 bytes transfered.
+ // This is an optimization for successful transfers.
+ //
+
+ if (!NT_SUCCESS(status)) {
+
+ originalIrp->IoStatus.Status = status;
+ originalIrp->IoStatus.Information = 0;
+
+ //
+ // Set the hard error if necessary.
+ //
+
+ if (IoIsErrorUserInduced(status) &&
+ (originalIrp->Tail.Overlay.Thread != NULL)) {
+
+ //
+ // Store DeviceObject for filesystem.
+ //
+
+ IoSetHardErrorOrVerifyDevice(originalIrp, Fdo);
+ }
+ }
+
+ //
+ // Decrement and get the count of remaining IRPs.
+ //
+
+ irpCount = InterlockedDecrement(
+ (PLONG)&irpStack->Parameters.Others.Argument1);
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "ClassIoCompleteAssociated: Partial IRPs left %d\n",
+ irpCount));
+
+ //
+ // Ensure that the irpCount doesn't go negative. This was happening once
+ // because classpnp would get confused if it ran out of resources when
+ // splitting the request.
+ //
+
+ NT_ASSERT(irpCount >= 0);
+
+ if (irpCount == 0) {
+
+ //
+ // All partial IRPs have completed.
+ //
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
+ "ClassIoCompleteAssociated: All partial IRPs complete %p\n",
+ originalIrp));
+
+ if (fdoExtension->CommonExtension.DriverExtension->InitData.ClassStartIo) {
+
+ //
+ // Acquire a separate copy of the remove lock so the debugging code
+ // works okay and we don't have to hold up the completion of this
+ // irp until after we start the next packet(s).
+ //
+
+ KIRQL oldIrql;
+ UCHAR uniqueAddress = 0;
+ ClassAcquireRemoveLock(Fdo, (PIRP)&uniqueAddress);
+ ClassReleaseRemoveLock(Fdo, originalIrp);
+ ClassCompleteRequest(Fdo, originalIrp, IO_DISK_INCREMENT);
+
+ KeRaiseIrql(DISPATCH_LEVEL, &oldIrql);
+ IoStartNextPacket(Fdo, TRUE); // yes, some IO is now cancellable
+ KeLowerIrql(oldIrql);
+
+ ClassReleaseRemoveLock(Fdo, (PIRP)&uniqueAddress);
+
+ } else {
+
+ //
+ // just complete this request
+ //
+
+ ClassReleaseRemoveLock(Fdo, originalIrp);
+ ClassCompleteRequest(Fdo, originalIrp, IO_DISK_INCREMENT);
+
+ }
+
+ }
+
+ //
+ // Deallocate IRP and indicate the I/O system should not attempt any more
+ // processing.
+ //
+
+ IoFreeIrp(Irp);
+ return STATUS_MORE_PROCESSING_REQUIRED;
+
+} // end ClassIoCompleteAssociated()
+
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+RetryRequest()
+
+Routine Description:
+
+ This is a wrapper around the delayed retry DPC routine, RetryRequestDPC.
+ This reinitalizes the necessary fields, queues the request, and sets
+ a timer to call the DPC if someone hasn't already done so.
+
+Arguments:
+
+ DeviceObject - Supplies the device object associated with this request.
+
+ Irp - Supplies the request to be retried.
+
+ Srb - Supplies a Pointer to the SCSI request block to be retied.
+
+ Assocaiated - Indicates this is an assocatied Irp created by split request.
+
+ TimeDelta100ns - How long, in 100ns units, before retrying the request.
+
+Return Value:
+
+ None
+
+--*/
+VOID
+RetryRequest(
+ PDEVICE_OBJECT DeviceObject,
+ PIRP Irp,
+ PSCSI_REQUEST_BLOCK Srb,
+ BOOLEAN Associated,
+ LONGLONG TimeDelta100ns
+ )
+{
+ PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
+ PIO_STACK_LOCATION nextIrpStack = IoGetNextIrpStackLocation(Irp);
+ ULONG transferByteCount;
+ ULONG dataTransferLength;
+ PSTORAGE_REQUEST_BLOCK_HEADER srbHeader = (PSTORAGE_REQUEST_BLOCK_HEADER)Srb;
+
+ // This function is obsolete but is still used by some of our class drivers.
+ // TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "RetryRequest is OBSOLETE !"));
+
+ //
+ // Determine the transfer count of the request. If this is a read or a
+ // write then the transfer count is in the Irp stack. Otherwise assume
+ // the MDL contains the correct length. If there is no MDL then the
+ // transfer length must be zero.
+ //
+
+ dataTransferLength = SrbGetDataTransferLength(srbHeader);
+ if (currentIrpStack->MajorFunction == IRP_MJ_READ ||
+ currentIrpStack->MajorFunction == IRP_MJ_WRITE) {
+
+ _Analysis_assume_(currentIrpStack->Parameters.Read.Length <= dataTransferLength);
+ transferByteCount = currentIrpStack->Parameters.Read.Length;
+
+ } else if (Irp->MdlAddress != NULL) {
+
+ //
+ // Note this assumes that only read and write requests are spilt and
+ // other request do not need to be. If the data buffer address in
+ // the MDL and the SRB don't match then transfer length is most
+ // likely incorrect.
+ //
+
+ NT_ASSERT(SrbGetDataBuffer(srbHeader) == MmGetMdlVirtualAddress(Irp->MdlAddress));
+ _Analysis_assume_(Irp->MdlAddress->ByteCount <= dataTransferLength);
+ transferByteCount = Irp->MdlAddress->ByteCount;
+
+ } else {
+
+ transferByteCount = 0;
+ }
+
+ //
+ // this is a safety net. this should not normally be hit, since we are
+ // not guaranteed to be an fdoExtension
+ //
+
+ NT_ASSERT(!TEST_FLAG(SrbGetSrbFlags(srbHeader), SRB_FLAGS_FREE_SENSE_BUFFER));
+
+ //
+ // Reset byte count of transfer in SRB Extension.
+ //
+
+ SrbSetDataTransferLength(srbHeader, transferByteCount);
+
+ //
+ // Zero SRB statuses.
+ //
+
+ srbHeader->SrbStatus = 0;
+ SrbSetScsiStatus(srbHeader, 0);
+
+ //
+ // If this is the last retry, then disable all the special flags.
+ //
+
+ if ( 0 == (ULONG)(ULONG_PTR)currentIrpStack->Parameters.Others.Argument4 ) {
+ //
+ // Set the no disconnect flag, disable synchronous data transfers and
+ // disable tagged queuing. This fixes some errors.
+ // NOTE: Cannot clear these flags, just add to them
+ //
+
+ SrbSetSrbFlags(srbHeader,
+ SRB_FLAGS_DISABLE_DISCONNECT | SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
+ SrbClearSrbFlags(srbHeader, SRB_FLAGS_QUEUE_ACTION_ENABLE);
+
+ SrbSetQueueTag(srbHeader, SP_UNTAGGED);
+ }
+
+
+ //
+ // Set up major SCSI function.
+ //
+
+ nextIrpStack->MajorFunction = IRP_MJ_SCSI;
+
+ //
+ // Save SRB address in next stack for port driver.
+ //
+
+ nextIrpStack->Parameters.Scsi.Srb = Srb;
+
+ if (Associated){
+ IoSetCompletionRoutine(Irp, ClassIoCompleteAssociated, Srb, TRUE, TRUE, TRUE);
+ }
+ else {
+ IoSetCompletionRoutine(Irp, ClassIoComplete, Srb, TRUE, TRUE, TRUE);
+ }
+
+ ClassRetryRequest(DeviceObject, Irp, TimeDelta100ns);
+ return;
+} // end RetryRequest()
+
+
+/*++
+
+ClassBuildRequest()
+
+Routine Description:
+
+ This routine allocates an SRB for the specified request then calls
+ ClasspBuildRequestEx to create a SCSI operation to read or write the device.
+
+ If no SRB is available then the request will be queued to be issued later
+ when requests are available. Drivers which do not want the queueing
+ behavior should allocate the SRB themselves and call ClasspBuildRequestEx
+ to issue it.
+
+Arguments:
+
+ Fdo - Supplies the functional device object associated with this request.
+
+ Irp - Supplies the request to be retried.
+
+Note:
+
+ If the IRP is for a disk transfer, the byteoffset field
+ will already have been adjusted to make it relative to
+ the beginning of the disk.
+
+
+Return Value:
+
+ NT Status
+
+--*/
+NTSTATUS
+ClassBuildRequest(
+ _In_ PDEVICE_OBJECT Fdo,
+ _In_ PIRP Irp
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+
+ PSCSI_REQUEST_BLOCK srb;
+
+ // This function is obsolete, but still called by CDROM.SYS .
+ // TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassBuildRequest is OBSOLETE !"));
+
+ //
+ // Allocate an Srb.
+ //
+
+ srb = ClasspAllocateSrb(fdoExtension);
+
+ if (srb == NULL) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ ClasspBuildRequestEx(fdoExtension, Irp, srb);
+ return STATUS_SUCCESS;
+
+} // end ClassBuildRequest()
+
+
+VOID
+#pragma prefast(suppress:28194) // Srb may not be aliased if it is NULL
+ClasspBuildRequestEx(
+ _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ _In_ PIRP Irp,
+ _In_ __drv_aliasesMem PSCSI_REQUEST_BLOCK Srb
+ )
+
+/*++
+
+ClasspBuildRequestEx()
+
+Routine Description:
+
+ This routine allocates and builds an Srb for a read or write request.
+ The block address and length are supplied by the Irp. The retry count
+ is stored in the current stack for use by ClassIoComplete which
+ processes these requests when they complete. The Irp is ready to be
+ passed to the port driver when this routine returns.
+
+Arguments:
+
+ FdoExtension - Supplies the device extension associated with this request.
+
+ Irp - Supplies the request to be issued.
+
+ Srb - Supplies an SRB to be used for the request.
+
+Note:
+
+ If the IRP is for a disk transfer, the byteoffset field
+ will already have been adjusted to make it relative to
+ the beginning of the disk.
+
+
+Return Value:
+
+ NT Status
+
+--*/
+{
+ PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
+ PIO_STACK_LOCATION nextIrpStack = IoGetNextIrpStackLocation(Irp);
+
+ LARGE_INTEGER startingOffset = currentIrpStack->Parameters.Read.ByteOffset;
+
+ PCDB cdb;
+ ULONG logicalBlockAddress;
+ USHORT transferBlocks;
+ NTSTATUS status;
+ PSTORAGE_REQUEST_BLOCK_HEADER srbHeader = (PSTORAGE_REQUEST_BLOCK_HEADER)Srb;
+
+ // This function is obsolete, but still called by CDROM.SYS .
+ // TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClasspBuildRequestEx is OBSOLETE !"));
+
+ if (Srb == NULL) {
+ NT_ASSERT(FALSE);
+ return;
+ }
+
+ //
+ // Calculate relative sector address.
+ //
+
+ logicalBlockAddress =
+ (ULONG)(Int64ShrlMod32(startingOffset.QuadPart,
+ FdoExtension->SectorShift));
+
+ //
+ // Prepare the SRB.
+ // NOTE - for extended SRB, size used is based on allocation in ClasspAllocateSrb.
+ //
+
+ if (FdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ status = InitializeStorageRequestBlock((PSTORAGE_REQUEST_BLOCK)Srb,
+ STORAGE_ADDRESS_TYPE_BTL8,
+ CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE,
+ 1,
+ SrbExDataTypeScsiCdb16);
+ if (!NT_SUCCESS(status)) {
+ NT_ASSERT(FALSE);
+ return;
+ }
+
+ ((PSTORAGE_REQUEST_BLOCK)Srb)->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
+ } else {
+ RtlZeroMemory(Srb, sizeof(SCSI_REQUEST_BLOCK));
+
+ //
+ // Write length to SRB.
+ //
+
+ Srb->Length = sizeof(SCSI_REQUEST_BLOCK);
+
+ Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
+ }
+
+
+ //
+ // Set up IRP Address.
+ //
+
+ SrbSetOriginalRequest(srbHeader, Irp);
+
+ //
+ // Set up data buffer
+ //
+
+ SrbSetDataBuffer(srbHeader,
+ MmGetMdlVirtualAddress(Irp->MdlAddress));
+
+ //
+ // Save byte count of transfer in SRB Extension.
+ //
+
+ SrbSetDataTransferLength(srbHeader,
+ currentIrpStack->Parameters.Read.Length);
+
+ //
+ // Initialize the queue actions field.
+ //
+
+ SrbSetRequestAttribute(srbHeader, SRB_SIMPLE_TAG_REQUEST);
+
+ //
+ // Queue sort key is Relative Block Address.
+ //
+
+ SrbSetQueueSortKey(srbHeader, logicalBlockAddress);
+
+ //
+ // Indicate auto request sense by specifying buffer and size.
+ //
+
+ SrbSetSenseInfoBuffer(srbHeader, FdoExtension->SenseData);
+ SrbSetSenseInfoBufferLength(srbHeader, GET_FDO_EXTENSON_SENSE_DATA_LENGTH(FdoExtension));
+
+ //
+ // Set timeout value of one unit per 64k bytes of data.
+ //
+
+ SrbSetTimeOutValue(srbHeader,
+ ((SrbGetDataTransferLength(srbHeader) + 0xFFFF) >> 16) *
+ FdoExtension->TimeOutValue);
+
+ //
+ // Indicate that 10-byte CDB's will be used.
+ //
+
+ SrbSetCdbLength(srbHeader, 10);
+
+ //
+ // Fill in CDB fields.
+ //
+
+ cdb = SrbGetCdb(srbHeader);
+ NT_ASSERT(cdb != NULL);
+
+ transferBlocks = (USHORT)(currentIrpStack->Parameters.Read.Length >>
+ FdoExtension->SectorShift);
+
+ //
+ // Move little endian values into CDB in big endian format.
+ //
+
+ cdb->CDB10.LogicalBlockByte0 = ((PFOUR_BYTE)&logicalBlockAddress)->Byte3;
+ cdb->CDB10.LogicalBlockByte1 = ((PFOUR_BYTE)&logicalBlockAddress)->Byte2;
+ cdb->CDB10.LogicalBlockByte2 = ((PFOUR_BYTE)&logicalBlockAddress)->Byte1;
+ cdb->CDB10.LogicalBlockByte3 = ((PFOUR_BYTE)&logicalBlockAddress)->Byte0;
+
+ cdb->CDB10.TransferBlocksMsb = ((PFOUR_BYTE)&transferBlocks)->Byte1;
+ cdb->CDB10.TransferBlocksLsb = ((PFOUR_BYTE)&transferBlocks)->Byte0;
+
+ //
+ // Set transfer direction flag and Cdb command.
+ //
+
+ if (currentIrpStack->MajorFunction == IRP_MJ_READ) {
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_RW, "ClassBuildRequest: Read Command\n"));
+
+ SrbSetSrbFlags(srbHeader, SRB_FLAGS_DATA_IN);
+ cdb->CDB10.OperationCode = SCSIOP_READ;
+
+ } else {
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_RW, "ClassBuildRequest: Write Command\n"));
+
+ SrbSetSrbFlags(srbHeader, SRB_FLAGS_DATA_OUT);
+ cdb->CDB10.OperationCode = SCSIOP_WRITE;
+
+ }
+
+ //
+ // If this is not a write-through request, then allow caching.
+ //
+
+ if (!(currentIrpStack->Flags & SL_WRITE_THROUGH)) {
+
+ SrbSetSrbFlags(srbHeader, SRB_FLAGS_ADAPTER_CACHE_ENABLE);
+
+ } else {
+
+ //
+ // If write caching is enable then force media access in the
+ // cdb.
+ //
+
+ cdb->CDB10.ForceUnitAccess = FdoExtension->CdbForceUnitAccess;
+ }
+
+ if (TEST_FLAG(Irp->Flags, (IRP_PAGING_IO | IRP_SYNCHRONOUS_PAGING_IO))) {
+ SrbSetSrbFlags(srbHeader, SRB_CLASS_FLAGS_PAGING);
+ }
+
+ //
+ // OR in the default flags from the device object.
+ //
+
+ SrbSetSrbFlags(srbHeader, FdoExtension->SrbFlags);
+
+ //
+ // Set up major SCSI function.
+ //
+
+ nextIrpStack->MajorFunction = IRP_MJ_SCSI;
+
+ //
+ // Save SRB address in next stack for port driver.
+ //
+
+ nextIrpStack->Parameters.Scsi.Srb = Srb;
+
+ //
+ // Save retry count in current IRP stack.
+ //
+
+ currentIrpStack->Parameters.Others.Argument4 = (PVOID)MAXIMUM_RETRIES;
+
+ //
+ // Set up IoCompletion routine address.
+ //
+
+ IoSetCompletionRoutine(Irp, ClassIoComplete, Srb, TRUE, TRUE, TRUE);
+
+}
+
+
+VOID ClasspInsertCScanList(IN PLIST_ENTRY ListHead, IN PCSCAN_LIST_ENTRY Entry)
+{
+ PCSCAN_LIST_ENTRY t;
+
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClasspInsertCScanList is OBSOLETE !"));
+
+ //
+ // Iterate through the list. Insert this entry in the sorted list in
+ // order (after other requests for the same block). At each stop if
+ // blockNumber(Entry) >= blockNumber(t) then move on.
+ //
+
+ for(t = (PCSCAN_LIST_ENTRY) ListHead->Flink;
+ t != (PCSCAN_LIST_ENTRY) ListHead;
+ t = (PCSCAN_LIST_ENTRY) t->Entry.Flink) {
+
+ if(Entry->BlockNumber < t->BlockNumber) {
+
+ //
+ // Set the pointers in entry to the right location.
+ //
+
+ Entry->Entry.Flink = &(t->Entry);
+ Entry->Entry.Blink = t->Entry.Blink;
+
+ //
+ // Set the pointers in the surrounding elements to refer to us.
+ //
+
+ t->Entry.Blink->Flink = &(Entry->Entry);
+ t->Entry.Blink = &(Entry->Entry);
+ return;
+ }
+ }
+
+ //
+ // Insert this entry at the tail of the list. If the list was empty this
+ // will also be the head of the list.
+ //
+
+ InsertTailList(ListHead, &(Entry->Entry));
+
+}
+
+
+VOID ClassInsertCScanList(IN PCSCAN_LIST List, IN PIRP Irp, IN ULONGLONG BlockNumber, IN BOOLEAN LowPriority)
+/*++
+
+Routine Description:
+
+ This routine inserts an entry into the CScan list based on it's block number
+ and priority. It is assumed that the caller is providing synchronization
+ to the access of the list.
+
+ Low priority requests are always scheduled to run on the next sweep across
+ the disk. Normal priority requests will be inserted into the current or
+ next sweep based on the standard C-SCAN algorithm.
+
+Arguments:
+
+ List - the list to insert into
+
+ Irp - the irp to be inserted.
+
+ BlockNumber - the block number for this request.
+
+ LowPriority - indicates that the request is lower priority and should be
+ done on the next sweep across the disk.
+
+Return Value:
+
+ none
+
+--*/
+{
+ PCSCAN_LIST_ENTRY entry = (PCSCAN_LIST_ENTRY)Irp->Tail.Overlay.DriverContext;
+
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassInsertCScanList is OBSOLETE !"));
+
+ //
+ // Set the block number in the entry. We need this to keep the list sorted.
+ //
+ entry->BlockNumber = BlockNumber;
+
+ //
+ // If it's a normal priority request and further down the disk than our
+ // current position then insert this entry into the current sweep.
+ //
+
+ if((LowPriority != TRUE) && (BlockNumber > List->BlockNumber)) {
+ ClasspInsertCScanList(&(List->CurrentSweep), entry);
+ } else {
+ ClasspInsertCScanList(&(List->NextSweep), entry);
+ }
+ return;
+}
+
+
+
+VOID ClassFreeOrReuseSrb( IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN __drv_freesMem(mem) PSCSI_REQUEST_BLOCK Srb)
+/*++
+
+Routine Description:
+
+ This routine will attempt to reuse the provided SRB to start a blocked
+ read/write request.
+ If there is no need to reuse the request it will be returned
+ to the SRB lookaside list.
+
+Arguments:
+
+ Fdo - the device extension
+
+ Srb - the SRB which is to be reused or freed.
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+ PCOMMON_DEVICE_EXTENSION commonExt = &FdoExtension->CommonExtension;
+
+ // This function is obsolete, but still called by DISK.SYS .
+ // TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassFreeOrReuseSrb is OBSOLETE !"));
+
+ //
+ // safety net. this should never occur. if it does, it's a potential
+ // memory leak.
+ //
+ NT_ASSERT(!TEST_FLAG(SrbGetSrbFlags(Srb), SRB_FLAGS_FREE_SENSE_BUFFER));
+
+ if (commonExt->IsSrbLookasideListInitialized){
+ /*
+ * Put the SRB back in our lookaside list.
+ *
+ * Note: Some class drivers use ClassIoComplete
+ * to complete SRBs that they themselves allocated.
+ * So we may be putting a "foreign" SRB
+ * (e.g. with a different pool tag) into our lookaside list.
+ */
+ ClasspFreeSrb(FdoExtension, Srb);
+ }
+ else {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,"ClassFreeOrReuseSrb: someone is trying to use an uninitialized SrbLookasideList !!!"));
+ FREE_POOL(Srb);
+ }
+}
+
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassDeleteSrbLookasideList()
+
+Routine Description:
+
+ This routine deletes a lookaside listhead for srbs, and should be called
+ only during the final removal.
+
+ If called at other times, the caller is responsible for
+ synchronization and removal issues.
+
+Arguments:
+
+ CommonExtension - Pointer to the CommonExtension containing the listhead.
+
+Return Value:
+
+ None
+
+--*/
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID ClassDeleteSrbLookasideList(_Inout_ PCOMMON_DEVICE_EXTENSION CommonExtension)
+{
+ PAGED_CODE();
+
+ // This function is obsolete, but is still called by some of our code.
+ // TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassDeleteSrbLookasideList is OBSOLETE !"));
+
+ if (CommonExtension->IsSrbLookasideListInitialized){
+ CommonExtension->IsSrbLookasideListInitialized = FALSE;
+ ExDeleteNPagedLookasideList(&CommonExtension->SrbLookasideList);
+ }
+ else {
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassDeleteSrbLookasideList: attempt to delete uninitialized or freed srblookasidelist"));
+ }
+}
+
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassInitializeSrbLookasideList()
+
+Routine Description:
+
+ This routine sets up a lookaside listhead for srbs, and should be called
+ only from the ClassInitDevice() routine to prevent race conditions.
+
+ If called from other locations, the caller is responsible for
+ synchronization and removal issues.
+
+Arguments:
+
+ CommonExtension - Pointer to the CommonExtension containing the listhead.
+
+ NumberElements - Supplies the maximum depth of the lookaside list.
+
+
+Note:
+
+ The Windows 2000 version of classpnp did not return any status value from
+ this call.
+
+--*/
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID ClassInitializeSrbLookasideList( _Inout_ PCOMMON_DEVICE_EXTENSION CommonExtension,
+ _In_ ULONG NumberElements)
+{
+ size_t sizeNeeded;
+ PFUNCTIONAL_DEVICE_EXTENSION fdo;
+
+ PAGED_CODE();
+
+ // This function is obsolete, but still called by DISK.SYS .
+ // TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL, "ClassInitializeSrbLookasideList is OBSOLETE !"));
+
+ NT_ASSERT(!CommonExtension->IsSrbLookasideListInitialized);
+ if (!CommonExtension->IsSrbLookasideListInitialized){
+
+ if (CommonExtension->IsFdo == TRUE) {
+ fdo = (PFUNCTIONAL_DEVICE_EXTENSION)CommonExtension;
+
+ //
+ // Check FDO extension on the SRB type supported
+ //
+ if (fdo->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+
+ //
+ // It's 16 byte CDBs for now. Need to change when classpnp uses >16
+ // byte CDBs or support new address types.
+ //
+ sizeNeeded = CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE;
+
+ } else {
+ sizeNeeded = sizeof(SCSI_REQUEST_BLOCK);
+ }
+
+ } else {
+
+ //
+ // For PDO, use the max of old and new SRB as can't guarantee we can get
+ // corresponding FDO to determine SRB support.
+ //
+ sizeNeeded = max(sizeof(SCSI_REQUEST_BLOCK), CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE);
+ }
+
+ ExInitializeNPagedLookasideList(&CommonExtension->SrbLookasideList,
+ NULL,
+ NULL,
+ POOL_NX_ALLOCATION,
+ sizeNeeded,
+ '$scS',
+ (USHORT)NumberElements);
+
+ CommonExtension->IsSrbLookasideListInitialized = TRUE;
+ }
+
+}
+
+
+
+
+VOID ClasspInitializeCScanList(IN PCSCAN_LIST List)
+{
+ PAGED_CODE();
+ RtlZeroMemory(List, sizeof(CSCAN_LIST));
+ InitializeListHead(&(List->CurrentSweep));
+ InitializeListHead(&(List->NextSweep));
+}
+
+
+
+VOID ClasspStartNextSweep(PCSCAN_LIST List)
+{
+ NT_ASSERT(IsListEmpty(&(List->CurrentSweep)) == TRUE);
+
+ //
+ // If the next sweep is empty then there's nothing to do.
+ //
+
+ if(IsListEmpty(&(List->NextSweep))) {
+ return;
+ }
+
+ //
+ // Copy the next sweep list head into the current sweep list head.
+ //
+
+ List->CurrentSweep = List->NextSweep;
+
+ //
+ // Unlink the next sweep list from the list head now that we have a copy
+ // of it.
+ //
+
+ InitializeListHead(&(List->NextSweep));
+
+ //
+ // Update the next sweep list to point back to the current sweep list head.
+ //
+
+ List->CurrentSweep.Flink->Blink = &(List->CurrentSweep);
+ List->CurrentSweep.Blink->Flink = &(List->CurrentSweep);
+
+ return;
+}
+
+
+
+PIRP ClassRemoveCScanList(IN PCSCAN_LIST List)
+{
+ PCSCAN_LIST_ENTRY entry;
+
+ //
+ // If the current sweep is empty then promote the next sweep.
+ //
+
+ if(IsListEmpty(&(List->CurrentSweep))) {
+ ClasspStartNextSweep(List);
+ }
+
+ //
+ // If the current sweep is still empty then we're done.
+ //
+
+ if(IsListEmpty(&(List->CurrentSweep))) {
+ return NULL;
+ }
+
+ //
+ // Remove the head entry from the current sweep. Record it's block number
+ // so that nothing before it on the disk gets into the current sweep.
+ //
+
+ entry = (PCSCAN_LIST_ENTRY) RemoveHeadList(&(List->CurrentSweep));
+
+ List->BlockNumber = entry->BlockNumber;
+
+ return CONTAINING_RECORD(entry, IRP, Tail.Overlay.DriverContext);
+}
diff --git a/storage/class/classpnp/src/power.c b/storage/class/classpnp/src/power.c
new file mode 100644
index 00000000..790dba4a
--- /dev/null
+++ b/storage/class/classpnp/src/power.c
@@ -0,0 +1,2650 @@
+/*++
+
+Copyright (C) Microsoft Corporation, 1991 - 2010
+
+Module Name:
+
+ power.c
+
+Abstract:
+
+ SCSI class driver routines
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+
+Revision History:
+
+--*/
+
+#include "stddef.h"
+#include "ntddk.h"
+#include "scsi.h"
+#include "classp.h"
+
+#include <stdarg.h>
+
+#ifdef DEBUG_USE_WPP
+#include "power.tmh"
+#endif
+
+#define CLASS_TAG_POWER 'WLcS'
+
+// constants for power transition process. (UNIT: seconds)
+#define DEFAULT_POWER_IRP_TIMEOUT_VALUE 10*60
+#define TIME_LEFT_FOR_LOWER_DRIVERS 30
+#define TIME_LEFT_FOR_UPPER_DRIVERS 5
+#define DEFAULT_IO_TIMEOUT_VALUE 10
+#define MINIMUM_STOP_UNIT_TIMEOUT_VALUE 2
+
+//
+// MINIMAL value is one that has some slack and is the value to use
+// if there is a shortened POWER IRP timeout value. If time remaining
+// is less than MINIMAL, we will use the MINIMUM value. Both values
+// are in the same unit as above (seconds).
+//
+#define MINIMAL_START_UNIT_TIMEOUT_VALUE 60
+#define MINIMUM_START_UNIT_TIMEOUT_VALUE 30
+
+// PoQueryWatchdogTime was introduced in Windows 7.
+// Returns TRUE if a watchdog-enabled power IRP is found, otherwise FALSE.
+#if (NTDDI_VERSION < NTDDI_WIN7)
+#define PoQueryWatchdogTime(A, B) FALSE
+#endif
+
+IO_COMPLETION_ROUTINE ClasspPowerDownCompletion;
+
+IO_COMPLETION_ROUTINE ClasspPowerUpCompletion;
+
+IO_COMPLETION_ROUTINE ClasspStartNextPowerIrpCompletion;
+IO_COMPLETION_ROUTINE ClasspDeviceLockFailurePowerIrpCompletion;
+
+NTSTATUS
+ClasspPowerHandler(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN CLASS_POWER_OPTIONS Options
+ );
+
+VOID
+RetryPowerRequest(
+ PDEVICE_OBJECT DeviceObject,
+ PIRP Irp,
+ PCLASS_POWER_CONTEXT Context
+ );
+
+#ifdef ALLOC_PRAGMA
+ #pragma alloc_text(PAGE, ClasspPowerSettingCallback)
+#endif
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassDispatchPower()
+
+Routine Description:
+
+ This routine acquires the removelock for the irp and then calls the
+ appropriate power callback.
+
+Arguments:
+
+ DeviceObject -
+ Irp -
+
+Return Value:
+
+--*/
+NTSTATUS
+ClassDispatchPower(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ ULONG isRemoved;
+
+ //
+ // NOTE: This code may be called at PASSIVE or DISPATCH, depending
+ // upon the device object it is being called for.
+ // don't do anything that would break under either circumstance.
+ //
+
+ //
+ // If device is added but not yet started, we need to send the Power
+ // request down the stack. If device is started and then stopped,
+ // we have enough state to process the power request.
+ //
+
+ if (!commonExtension->IsInitialized) {
+
+ PoStartNextPowerIrp(Irp);
+ IoSkipCurrentIrpStackLocation(Irp);
+ return PoCallDriver(commonExtension->LowerDeviceObject, Irp);
+ }
+
+ isRemoved = ClassAcquireRemoveLock(DeviceObject, Irp);
+
+ if (isRemoved) {
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
+ PoStartNextPowerIrp(Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ return STATUS_DEVICE_DOES_NOT_EXIST;
+ }
+
+ return commonExtension->DevInfo->ClassPowerDevice(DeviceObject, Irp);
+} // end ClassDispatchPower()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClasspPowerUpCompletion()
+
+Routine Description:
+
+ This routine is used for intermediate completion of a power up request.
+ PowerUp requires four requests to be sent to the lower driver in sequence.
+
+ * The queue is "power locked" to ensure that the class driver power-up
+ work can be done before request processing resumes.
+
+ * The power irp is sent down the stack for any filter drivers and the
+ port driver to return power and resume command processing for the
+ device. Since the queue is locked, no queued irps will be sent
+ immediately.
+
+ * A start unit command is issued to the device with appropriate flags
+ to override the "power locked" queue.
+
+ * The queue is "power unlocked" to start processing requests again.
+
+ This routine uses the function in the srb which just completed to determine
+ which state it is in.
+
+Arguments:
+
+ DeviceObject - the device object being powered up
+
+ Irp - Context->Irp: original power irp; fdoExtension->PrivateFdoData->PowerProcessIrp: power process irp
+
+ Context - Class power context used to perform port/class operations.
+
+Return Value:
+
+ STATUS_MORE_PROCESSING_REQUIRED or
+ STATUS_SUCCESS
+
+--*/
+NTSTATUS
+ClasspPowerUpCompletion(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ )
+{
+ PCLASS_POWER_CONTEXT PowerContext = (PCLASS_POWER_CONTEXT)Context;
+ PCOMMON_DEVICE_EXTENSION commonExtension;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
+ PIRP OriginalIrp;
+ PIO_STACK_LOCATION currentStack;
+ PIO_STACK_LOCATION nextStack;
+
+ NTSTATUS status = STATUS_MORE_PROCESSING_REQUIRED;
+ PSTORAGE_REQUEST_BLOCK_HEADER srbHeader;
+ ULONG srbFlags;
+ BOOLEAN FailurePredictionEnabled = FALSE;
+
+ UNREFERENCED_PARAMETER(DeviceObject);
+
+ if (PowerContext == NULL) {
+ NT_ASSERT(PowerContext != NULL);
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ commonExtension = PowerContext->DeviceObject->DeviceExtension;
+ fdoExtension = PowerContext->DeviceObject->DeviceExtension;
+ OriginalIrp = PowerContext->Irp;
+
+ // currentStack - from original power irp
+ // nextStack - from power process irp
+ currentStack = IoGetCurrentIrpStackLocation(OriginalIrp);
+ nextStack = IoGetNextIrpStackLocation(fdoExtension->PrivateFdoData->PowerProcessIrp);
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "ClasspPowerUpCompletion: Device Object %p, Irp %p, "
+ "Context %p\n",
+ PowerContext->DeviceObject, Irp, Context));
+
+ if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ srbHeader = (PSTORAGE_REQUEST_BLOCK_HEADER)&(fdoExtension->PrivateFdoData->PowerSrb.SrbEx);
+
+ //
+ // Check if reverted to using legacy SRB.
+ //
+ if (PowerContext->Srb.Length == sizeof(SCSI_REQUEST_BLOCK)) {
+ srbHeader = (PSTORAGE_REQUEST_BLOCK_HEADER)&(PowerContext->Srb);
+ }
+ } else {
+ srbHeader = (PSTORAGE_REQUEST_BLOCK_HEADER)&(PowerContext->Srb);
+ }
+
+ srbFlags = SrbGetSrbFlags(srbHeader);
+ NT_ASSERT(!TEST_FLAG(srbFlags, SRB_FLAGS_FREE_SENSE_BUFFER));
+ NT_ASSERT(!TEST_FLAG(srbFlags, SRB_FLAGS_PORT_DRIVER_ALLOCSENSE));
+ NT_ASSERT(PowerContext->Options.PowerDown == FALSE);
+ NT_ASSERT(PowerContext->Options.HandleSpinUp);
+
+ if ((Irp == OriginalIrp) && (Irp->PendingReturned)) {
+ // only for original power irp
+ IoMarkIrpPending(Irp);
+ }
+
+ PowerContext->PowerChangeState.PowerUp++;
+
+ switch (PowerContext->PowerChangeState.PowerUp) {
+
+ case PowerUpDeviceLocked: {
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tPreviously sent power lock\n", Irp));
+
+ //
+ // Lock Queue operation has been sent.
+ // Now, send the original power irp down to get lower driver and device ready.
+ //
+
+ IoCopyCurrentIrpStackLocationToNext(OriginalIrp);
+
+ if ((PowerContext->Options.LockQueue == TRUE) &&
+ (!NT_SUCCESS(Irp->IoStatus.Status))) {
+
+ //
+ // Lock was not successful:
+ // Issue the original power request to the lower driver and next power irp will be started in completion routine.
+ //
+
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tIrp status was %lx\n",
+ Irp, Irp->IoStatus.Status));
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tSrb status was %lx\n",
+ Irp, srbHeader->SrbStatus));
+
+ IoSetCompletionRoutine(OriginalIrp,
+ ClasspDeviceLockFailurePowerIrpCompletion,
+ PowerContext,
+ TRUE,
+ TRUE,
+ TRUE);
+
+ PoCallDriver(commonExtension->LowerDeviceObject, OriginalIrp);
+
+ return STATUS_MORE_PROCESSING_REQUIRED;
+
+ } else {
+ PowerContext->QueueLocked = (UCHAR)PowerContext->Options.LockQueue;
+ }
+
+ Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
+
+ PowerContext->PowerChangeState.PowerUp = PowerUpDeviceLocked;
+
+ IoSetCompletionRoutine(OriginalIrp,
+ ClasspPowerUpCompletion,
+ PowerContext,
+ TRUE,
+ TRUE,
+ TRUE);
+
+ status = PoCallDriver(commonExtension->LowerDeviceObject, OriginalIrp);
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tIoCallDriver returned %lx\n", OriginalIrp, status));
+ break;
+ }
+
+ case PowerUpDeviceOn: {
+
+ //
+ // Original power irp has been completed by lower driver.
+ //
+
+ if (NT_SUCCESS(Irp->IoStatus.Status)) {
+ //
+ // If power irp succeeded, START UNIT command will be sent.
+ //
+ PCDB cdb;
+ ULONG secondsRemaining = 0;
+ ULONG timeoutValue = 0;
+ ULONG startUnitTimeout;
+
+ if (PoQueryWatchdogTime(fdoExtension->LowerPdo, &secondsRemaining)) {
+
+ // do not exceed DEFAULT_POWER_IRP_TIMEOUT_VALUE.
+ secondsRemaining = min(secondsRemaining, DEFAULT_POWER_IRP_TIMEOUT_VALUE);
+
+ //
+ // It's possible for POWER IRP timeout value to be smaller than default of
+ // START_UNIT_TIMEOUT. If this is the case, use a smaller timeout value.
+ //
+ if (secondsRemaining >= START_UNIT_TIMEOUT) {
+ startUnitTimeout = START_UNIT_TIMEOUT;
+ } else {
+ startUnitTimeout = MINIMAL_START_UNIT_TIMEOUT_VALUE;
+ }
+
+ // plan to leave (TIME_LEFT_FOR_UPPER_DRIVERS) seconds to upper level drivers
+ // for processing original power irp.
+ if (secondsRemaining >= (TIME_LEFT_FOR_UPPER_DRIVERS + startUnitTimeout)) {
+ fdoExtension->PrivateFdoData->MaxPowerOperationRetryCount =
+ (secondsRemaining - TIME_LEFT_FOR_UPPER_DRIVERS) / startUnitTimeout;
+
+ // * No 'short' timeouts
+ //
+ //
+ // timeoutValue = (secondsRemaining - TIME_LEFT_FOR_UPPER_DRIVERS) %
+ // startUnitTimeout;
+ //
+
+ if (--fdoExtension->PrivateFdoData->MaxPowerOperationRetryCount)
+ {
+ timeoutValue = startUnitTimeout;
+ } else {
+ timeoutValue = secondsRemaining - TIME_LEFT_FOR_UPPER_DRIVERS;
+ }
+ } else {
+ // issue the command with minimum timeout value and do not retry on it.
+ // case of (secondsRemaining < DEFAULT_IO_TIMEOUT_VALUE) is ignored as it should not happen.
+ NT_ASSERT(secondsRemaining >= DEFAULT_IO_TIMEOUT_VALUE);
+
+ fdoExtension->PrivateFdoData->MaxPowerOperationRetryCount = 0;
+ timeoutValue = MINIMUM_START_UNIT_TIMEOUT_VALUE; // use the minimum value for this corner case.
+ }
+
+ } else {
+ // don't know how long left, do not exceed DEFAULT_POWER_IRP_TIMEOUT_VALUE.
+ fdoExtension->PrivateFdoData->MaxPowerOperationRetryCount =
+ DEFAULT_POWER_IRP_TIMEOUT_VALUE / START_UNIT_TIMEOUT - 1;
+ timeoutValue = START_UNIT_TIMEOUT;
+ }
+
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tSending start unit to device\n", Irp));
+
+ //
+ // Issue the start unit command to the device.
+ //
+
+ PowerContext->RetryCount = fdoExtension->PrivateFdoData->MaxPowerOperationRetryCount;
+
+ if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ status = InitializeStorageRequestBlock((PSTORAGE_REQUEST_BLOCK)srbHeader,
+ STORAGE_ADDRESS_TYPE_BTL8,
+ CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE,
+ 1,
+ SrbExDataTypeScsiCdb16);
+ if (NT_SUCCESS(status)) {
+ ((PSTORAGE_REQUEST_BLOCK)srbHeader)->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
+
+ //
+ // Set length field in Power Context SRB so we know legacy SRB is not being used.
+ //
+ PowerContext->Srb.Length = 0;
+
+ } else {
+ //
+ // Should not happen. Revert to legacy SRB.
+ //
+ NT_ASSERT(FALSE);
+ srbHeader = (PSTORAGE_REQUEST_BLOCK_HEADER)&(PowerContext->Srb);
+ RtlZeroMemory(srbHeader, sizeof(SCSI_REQUEST_BLOCK));
+ srbHeader->Length = sizeof(SCSI_REQUEST_BLOCK);
+ srbHeader->Function = SRB_FUNCTION_EXECUTE_SCSI;
+ }
+
+ } else {
+ RtlZeroMemory(srbHeader, sizeof(SCSI_REQUEST_BLOCK));
+ srbHeader->Length = sizeof(SCSI_REQUEST_BLOCK);
+ srbHeader->Function = SRB_FUNCTION_EXECUTE_SCSI;
+ }
+
+ SrbSetOriginalRequest(srbHeader, fdoExtension->PrivateFdoData->PowerProcessIrp);
+ SrbSetSenseInfoBuffer(srbHeader, commonExtension->PartitionZeroExtension->SenseData);
+ SrbSetSenseInfoBufferLength(srbHeader, GET_FDO_EXTENSON_SENSE_DATA_LENGTH(commonExtension->PartitionZeroExtension));
+
+ SrbSetTimeOutValue(srbHeader, timeoutValue);
+ SrbAssignSrbFlags(srbHeader,
+ (SRB_FLAGS_NO_DATA_TRANSFER |
+ SRB_FLAGS_DISABLE_AUTOSENSE |
+ SRB_FLAGS_DISABLE_SYNCH_TRANSFER |
+ SRB_FLAGS_NO_QUEUE_FREEZE));
+
+ if (PowerContext->Options.LockQueue) {
+ SrbSetSrbFlags(srbHeader, SRB_FLAGS_BYPASS_LOCKED_QUEUE);
+ }
+
+ SrbSetCdbLength(srbHeader, 6);
+
+ cdb = SrbGetCdb(srbHeader);
+ RtlZeroMemory(cdb, sizeof(CDB));
+
+ cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;
+ cdb->START_STOP.Start = 1;
+
+ PowerContext->PowerChangeState.PowerUp = PowerUpDeviceOn;
+
+ IoSetCompletionRoutine(fdoExtension->PrivateFdoData->PowerProcessIrp,
+ ClasspPowerUpCompletion,
+ PowerContext,
+ TRUE,
+ TRUE,
+ TRUE);
+
+ nextStack->Parameters.Scsi.Srb = (PSCSI_REQUEST_BLOCK)srbHeader;
+ nextStack->MajorFunction = IRP_MJ_SCSI;
+
+ status = IoCallDriver(commonExtension->LowerDeviceObject, fdoExtension->PrivateFdoData->PowerProcessIrp);
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tIoCallDriver returned %lx\n", fdoExtension->PrivateFdoData->PowerProcessIrp, status));
+
+ } else {
+
+ //
+ // power irp is failed by lower driver. we're done.
+ //
+
+ PowerContext->FinalStatus = Irp->IoStatus.Status;
+ goto ClasspPowerUpCompletionFailure;
+ }
+
+ break;
+ }
+
+ case PowerUpDeviceStarted: { // 3
+
+ //
+ // First deal with an error if one occurred.
+ //
+
+ if (SRB_STATUS(srbHeader->SrbStatus) != SRB_STATUS_SUCCESS) {
+
+ BOOLEAN retry;
+ LONGLONG delta100nsUnits = 0;
+ ULONG secondsRemaining = 0;
+ ULONG startUnitTimeout = START_UNIT_TIMEOUT;
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_POWER, "%p\tError occured when issuing START_UNIT "
+ "command to device. Srb %p, Status %x\n",
+ Irp,
+ srbHeader,
+ srbHeader->SrbStatus));
+
+ NT_ASSERT(!(TEST_FLAG(srbHeader->SrbStatus, SRB_STATUS_QUEUE_FROZEN)));
+ NT_ASSERT((srbHeader->Function == SRB_FUNCTION_EXECUTE_SCSI) ||
+ (((PSTORAGE_REQUEST_BLOCK)srbHeader)->SrbFunction == SRB_FUNCTION_EXECUTE_SCSI));
+
+ PowerContext->RetryInterval = 0;
+ retry = InterpretSenseInfoWithoutHistory(
+ fdoExtension->DeviceObject,
+ Irp,
+ (PSCSI_REQUEST_BLOCK)srbHeader,
+ IRP_MJ_SCSI,
+ IRP_MJ_POWER,
+ fdoExtension->PrivateFdoData->MaxPowerOperationRetryCount - PowerContext->RetryCount,
+ &status,
+ &delta100nsUnits);
+
+ // NOTE: Power context is a public structure, and thus cannot be
+ // updated to use 100ns units. Therefore, must store the
+ // one-second equivalent. Round up to ensure minimum delay
+ // requirements have been met.
+ delta100nsUnits += (10*1000*1000) - 1;
+ delta100nsUnits /= (10*1000*1000);
+ // guaranteed not to have high bits set per SAL annotations
+ PowerContext->RetryInterval = (ULONG)(delta100nsUnits);
+
+
+ if ((retry == TRUE) && (PowerContext->RetryCount-- != 0)) {
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tRetrying failed request\n", Irp));
+
+ //
+ // Decrement the state so we come back through here the
+ // next time.
+ //
+
+ PowerContext->PowerChangeState.PowerUp--;
+
+ //
+ // Adjust start unit timeout based on remaining time if needed.
+ //
+ if (PoQueryWatchdogTime(fdoExtension->LowerPdo, &secondsRemaining)) {
+
+ if (secondsRemaining >= TIME_LEFT_FOR_UPPER_DRIVERS) {
+ secondsRemaining -= TIME_LEFT_FOR_UPPER_DRIVERS;
+ }
+
+ if (secondsRemaining < MINIMAL_START_UNIT_TIMEOUT_VALUE) {
+ startUnitTimeout = MINIMUM_START_UNIT_TIMEOUT_VALUE;
+ } else if (secondsRemaining < START_UNIT_TIMEOUT) {
+ startUnitTimeout = MINIMAL_START_UNIT_TIMEOUT_VALUE;
+ }
+ }
+
+ SrbSetTimeOutValue(srbHeader, startUnitTimeout);
+
+ RetryPowerRequest(commonExtension->DeviceObject,
+ Irp,
+ PowerContext);
+
+ break;
+
+ }
+
+ // reset retry count for UNLOCK command.
+ fdoExtension->PrivateFdoData->MaxPowerOperationRetryCount = MAXIMUM_RETRIES;
+ PowerContext->RetryCount = MAXIMUM_RETRIES;
+ }
+
+ClasspPowerUpCompletionFailure:
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tPreviously spun device up\n", Irp));
+
+ if (PowerContext->QueueLocked) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tUnlocking queue\n", Irp));
+
+ if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ //
+ // Will reuse SRB for a non-SCSI SRB.
+ //
+ status = InitializeStorageRequestBlock((PSTORAGE_REQUEST_BLOCK)srbHeader,
+ STORAGE_ADDRESS_TYPE_BTL8,
+ CLASS_SRBEX_NO_SRBEX_DATA_BUFFER_SIZE,
+ 0);
+ if (NT_SUCCESS(status)) {
+ ((PSTORAGE_REQUEST_BLOCK)srbHeader)->SrbFunction = SRB_FUNCTION_UNLOCK_QUEUE;
+
+ //
+ // Set length field in Power Context SRB so we know legacy SRB is not being used.
+ //
+ PowerContext->Srb.Length = 0;
+
+ } else {
+ //
+ // Should not occur. Revert to legacy SRB.
+ NT_ASSERT(FALSE);
+ srbHeader = (PSTORAGE_REQUEST_BLOCK_HEADER)&(PowerContext->Srb);
+ RtlZeroMemory(srbHeader, sizeof(SCSI_REQUEST_BLOCK));
+ srbHeader->Length = sizeof(SCSI_REQUEST_BLOCK);
+ srbHeader->Function = SRB_FUNCTION_UNLOCK_QUEUE;
+ }
+ } else {
+ RtlZeroMemory(srbHeader, sizeof(SCSI_REQUEST_BLOCK));
+ srbHeader->Length = sizeof(SCSI_REQUEST_BLOCK);
+ srbHeader->Function = SRB_FUNCTION_UNLOCK_QUEUE;
+ }
+ SrbAssignSrbFlags(srbHeader, SRB_FLAGS_BYPASS_LOCKED_QUEUE);
+ SrbSetOriginalRequest(srbHeader, fdoExtension->PrivateFdoData->PowerProcessIrp);
+
+ nextStack->Parameters.Scsi.Srb = (PSCSI_REQUEST_BLOCK)srbHeader;
+ nextStack->MajorFunction = IRP_MJ_SCSI;
+
+ PowerContext->PowerChangeState.PowerUp = PowerUpDeviceStarted;
+
+ IoSetCompletionRoutine(fdoExtension->PrivateFdoData->PowerProcessIrp,
+ ClasspPowerUpCompletion,
+ PowerContext,
+ TRUE,
+ TRUE,
+ TRUE);
+
+ status = IoCallDriver(commonExtension->LowerDeviceObject, fdoExtension->PrivateFdoData->PowerProcessIrp);
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tIoCallDriver returned %lx\n",
+ fdoExtension->PrivateFdoData->PowerProcessIrp, status));
+ break;
+ }
+
+ // Fall-through to next case...
+
+ }
+
+ case PowerUpDeviceUnlocked: {
+
+ //
+ // This is the end of the dance.
+ // We're ignoring possible intermediate error conditions ....
+ //
+
+ if (PowerContext->QueueLocked) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tPreviously unlocked queue\n", OriginalIrp));
+
+ //
+ // If the lower device is being removed, the IRP's status may be STATUS_DELETE_PENDING or
+ // STATUS_DEVICE_DOES_NOT_EXIST.
+ //
+ if((NT_SUCCESS(Irp->IoStatus.Status) == FALSE) &&
+ (Irp->IoStatus.Status != STATUS_DELETE_PENDING) &&
+ (Irp->IoStatus.Status != STATUS_DEVICE_DOES_NOT_EXIST)) {
+
+
+ NT_ASSERT(FALSE);
+ }
+
+ } else {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tFall-through (queue not locked)\n", OriginalIrp));
+ }
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tFreeing srb and completing\n", OriginalIrp));
+
+ status = PowerContext->FinalStatus;
+ OriginalIrp->IoStatus.Status = status;
+
+ //
+ // Set the new power state
+ //
+
+ if (NT_SUCCESS(status)) {
+ fdoExtension->DevicePowerState = currentStack->Parameters.Power.State.DeviceState;
+ }
+
+ //
+ // Check whether failure detection is enabled
+ //
+
+ if ((fdoExtension->FailurePredictionInfo != NULL) &&
+ (fdoExtension->FailurePredictionInfo->Method != FailurePredictionNone)) {
+ FailurePredictionEnabled = TRUE;
+ }
+
+ //
+ // Enable tick timer at end of D0 processing if it was previously enabled.
+ //
+
+ if ((commonExtension->DriverExtension->InitData.ClassTick != NULL) ||
+ ((fdoExtension->MediaChangeDetectionInfo != NULL) &&
+ (fdoExtension->FunctionSupportInfo != NULL) &&
+ (fdoExtension->FunctionSupportInfo->AsynchronousNotificationSupported == FALSE)) ||
+ (FailurePredictionEnabled)) {
+
+
+ //
+ // If failure prediction is turned on and we've been powered
+ // off longer than the failure prediction query period then
+ // force the query on the next timer tick.
+ //
+
+ if ((FailurePredictionEnabled) && (ClasspFailurePredictionPeriodMissed(fdoExtension))) {
+ fdoExtension->FailurePredictionInfo->CountDown = 1;
+ }
+
+ //
+ // Finally, enable the timer.
+ //
+
+ ClasspEnableTimer(fdoExtension);
+ }
+
+ //
+ // Indicate to Po that we've been successfully powered up so
+ // it can do it's notification stuff.
+ //
+
+ PoSetPowerState(PowerContext->DeviceObject,
+ currentStack->Parameters.Power.Type,
+ currentStack->Parameters.Power.State);
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tStarting next power irp\n", OriginalIrp));
+
+ ClassReleaseRemoveLock(PowerContext->DeviceObject, OriginalIrp);
+
+ PowerContext->InUse = FALSE;
+
+ PoStartNextPowerIrp(OriginalIrp);
+
+ // prevent from completing the irp allocated by ourselves
+ if ((fdoExtension->PrivateFdoData) && (Irp == fdoExtension->PrivateFdoData->PowerProcessIrp)) {
+ // complete original irp if we are processing powerprocess irp,
+ // otherwise, by returning status other than STATUS_MORE_PROCESSING_REQUIRED, IO manager will complete it.
+ ClassCompleteRequest(commonExtension->DeviceObject, OriginalIrp, IO_NO_INCREMENT);
+ status = STATUS_MORE_PROCESSING_REQUIRED;
+ }
+
+ return status;
+ }
+ }
+
+ return STATUS_MORE_PROCESSING_REQUIRED;
+} // end ClasspPowerUpCompletion()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClasspPowerDownCompletion()
+
+Routine Description:
+
+ This routine is used for intermediate completion of a power down request.
+ PowerDown performs the following sequence to power down the device.
+
+ 1. The queue(s) in the lower stack is/are "power locked" to ensure new
+ requests are held until the power-down process is complete.
+
+ 2. A request to the lower layers to wait for all outstanding IO to
+ complete ("quiescence") is sent. This ensures we don't power down
+ the device while it's in the middle of handling IO.
+
+ 3. A request to flush the device's cache is sent. The device may lose
+ power when we forward the D-IRP so any data in volatile storage must
+ be committed to non-volatile storage first.
+
+ 4. A "stop unit" request is sent to the device to notify it that it
+ is about to be powered down.
+
+ 5. The D-IRP is forwarded down the stack. If D3Cold is supported and
+ enabled via ACPI, the ACPI filter driver may power off the device.
+
+ 6. Once the D-IRP is completed by the lower stack, we will "power
+ unlock" the queue(s). (It is the lower stack's responsibility to
+ continue to queue any IO that requires hardware access until the
+ device is powered up again.)
+
+Arguments:
+
+ DeviceObject - the device object being powered down
+
+ Irp - the IO_REQUEST_PACKET containing the power request
+
+ Context - the class power context used to perform port/class operations.
+
+Return Value:
+
+ STATUS_MORE_PROCESSING_REQUIRED or
+ STATUS_SUCCESS
+
+--*/
+NTSTATUS
+ClasspPowerDownCompletion(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ )
+{
+ PCLASS_POWER_CONTEXT PowerContext = (PCLASS_POWER_CONTEXT)Context;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = PowerContext->DeviceObject->DeviceExtension;
+ PCOMMON_DEVICE_EXTENSION commonExtension = PowerContext->DeviceObject->DeviceExtension;
+ PIRP OriginalIrp = PowerContext->Irp;
+
+ // currentStack is for original power irp
+ // nextStack is for power process irp
+ PIO_STACK_LOCATION currentStack = IoGetCurrentIrpStackLocation(OriginalIrp);
+ PIO_STACK_LOCATION nextStack = IoGetNextIrpStackLocation(fdoExtension->PrivateFdoData->PowerProcessIrp);
+
+ NTSTATUS status = STATUS_MORE_PROCESSING_REQUIRED;
+ PSTORAGE_REQUEST_BLOCK_HEADER srbHeader;
+ ULONG srbFlags;
+
+ UNREFERENCED_PARAMETER(DeviceObject);
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "ClasspPowerDownCompletion: Device Object %p, "
+ "Irp %p, Context %p\n",
+ PowerContext->DeviceObject, Irp, Context));
+
+ if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ srbHeader = (PSTORAGE_REQUEST_BLOCK_HEADER)&(fdoExtension->PrivateFdoData->PowerSrb.SrbEx);
+
+ //
+ // Check if reverted to using legacy SRB.
+ //
+ if (PowerContext->Srb.Length == sizeof(SCSI_REQUEST_BLOCK)) {
+ srbHeader = (PSTORAGE_REQUEST_BLOCK_HEADER)&(PowerContext->Srb);
+ }
+ } else {
+ srbHeader = (PSTORAGE_REQUEST_BLOCK_HEADER)&(PowerContext->Srb);
+ }
+
+ srbFlags = SrbGetSrbFlags(srbHeader);
+ NT_ASSERT(!TEST_FLAG(srbFlags, SRB_FLAGS_FREE_SENSE_BUFFER));
+ NT_ASSERT(!TEST_FLAG(srbFlags, SRB_FLAGS_PORT_DRIVER_ALLOCSENSE));
+ NT_ASSERT(PowerContext->Options.PowerDown == TRUE);
+ NT_ASSERT(PowerContext->Options.HandleSpinDown);
+
+ if ((Irp == OriginalIrp) && (Irp->PendingReturned)) {
+ // only for original power irp
+ IoMarkIrpPending(Irp);
+ }
+
+ PowerContext->PowerChangeState.PowerDown3++;
+
+ switch(PowerContext->PowerChangeState.PowerDown3) {
+
+ case PowerDownDeviceLocked3: {
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tPreviously sent power lock\n", Irp));
+
+ if ((PowerContext->Options.LockQueue == TRUE) &&
+ (!NT_SUCCESS(Irp->IoStatus.Status))) {
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tIrp status was %lx\n",
+ Irp,
+ Irp->IoStatus.Status));
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tSrb status was %lx\n",
+ Irp,
+ srbHeader->SrbStatus));
+
+
+
+ //
+ // Lock was not successful - throw down the power IRP
+ // by itself and don't try to spin down the drive or unlock
+ // the queue.
+ //
+
+ //
+ // Set the new power state
+ //
+
+ fdoExtension->DevicePowerState =
+ currentStack->Parameters.Power.State.DeviceState;
+
+ //
+ // Indicate to Po that we've been successfully powered down
+ // so it can do it's notification stuff.
+ //
+
+ IoCopyCurrentIrpStackLocationToNext(OriginalIrp);
+ IoSetCompletionRoutine(OriginalIrp,
+ ClasspStartNextPowerIrpCompletion,
+ PowerContext,
+ TRUE,
+ TRUE,
+ TRUE);
+
+ PoSetPowerState(PowerContext->DeviceObject,
+ currentStack->Parameters.Power.Type,
+ currentStack->Parameters.Power.State);
+
+ fdoExtension->PowerDownInProgress = FALSE;
+
+ ClassReleaseRemoveLock(commonExtension->DeviceObject,
+ OriginalIrp);
+
+ PoCallDriver(commonExtension->LowerDeviceObject, OriginalIrp);
+
+ return STATUS_MORE_PROCESSING_REQUIRED;
+
+ } else {
+ //
+ // Lock the device queue succeeded. Now wait for all outstanding IO to complete.
+ // To do this, Srb with SRB_FUNCTION_QUIESCE_DEVICE will be sent down with default timeout value.
+ // We need to tolerant failure of this request, no retry will be made.
+ //
+ PowerContext->QueueLocked = (UCHAR) PowerContext->Options.LockQueue;
+
+ //
+ // No retry on device quiescence reqeust
+ //
+ fdoExtension->PrivateFdoData->MaxPowerOperationRetryCount = 0;
+ PowerContext->RetryCount = 0;
+
+ if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ srbHeader = (PSTORAGE_REQUEST_BLOCK_HEADER)&(fdoExtension->PrivateFdoData->PowerSrb.SrbEx);
+
+ //
+ // Initialize extended SRB for a SRB_FUNCTION_LOCK_QUEUE
+ //
+ status = InitializeStorageRequestBlock((PSTORAGE_REQUEST_BLOCK)srbHeader,
+ STORAGE_ADDRESS_TYPE_BTL8,
+ CLASS_SRBEX_NO_SRBEX_DATA_BUFFER_SIZE,
+ 0);
+ if (NT_SUCCESS(status)) {
+ ((PSTORAGE_REQUEST_BLOCK)srbHeader)->SrbFunction = SRB_FUNCTION_QUIESCE_DEVICE;
+ } else {
+ //
+ // Should not happen. Revert to legacy SRB.
+ //
+ NT_ASSERT(FALSE);
+ srbHeader = (PSTORAGE_REQUEST_BLOCK_HEADER)&(PowerContext->Srb);
+ srbHeader->Length = sizeof(SCSI_REQUEST_BLOCK);
+ srbHeader->Function = SRB_FUNCTION_QUIESCE_DEVICE;
+ }
+ } else {
+ srbHeader = (PSTORAGE_REQUEST_BLOCK_HEADER)&(PowerContext->Srb);
+ srbHeader->Length = sizeof(SCSI_REQUEST_BLOCK);
+ srbHeader->Function = SRB_FUNCTION_QUIESCE_DEVICE;
+ }
+
+ SrbSetOriginalRequest(srbHeader, fdoExtension->PrivateFdoData->PowerProcessIrp);
+ SrbSetTimeOutValue(srbHeader, fdoExtension->TimeOutValue);
+
+ SrbAssignSrbFlags(srbHeader,
+ (SRB_FLAGS_NO_DATA_TRANSFER |
+ SRB_FLAGS_DISABLE_AUTOSENSE |
+ SRB_FLAGS_DISABLE_SYNCH_TRANSFER |
+ SRB_FLAGS_NO_QUEUE_FREEZE |
+ SRB_FLAGS_BYPASS_LOCKED_QUEUE |
+ SRB_FLAGS_D3_PROCESSING));
+
+ IoSetCompletionRoutine(fdoExtension->PrivateFdoData->PowerProcessIrp,
+ ClasspPowerDownCompletion,
+ PowerContext,
+ TRUE,
+ TRUE,
+ TRUE);
+
+ nextStack->Parameters.Scsi.Srb = (PSCSI_REQUEST_BLOCK)srbHeader;
+ nextStack->MajorFunction = IRP_MJ_SCSI;
+
+ status = IoCallDriver(commonExtension->LowerDeviceObject, fdoExtension->PrivateFdoData->PowerProcessIrp);
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tIoCallDriver returned %lx\n", fdoExtension->PrivateFdoData->PowerProcessIrp, status));
+ break;
+ }
+
+ }
+
+ case PowerDownDeviceQuiesced3: {
+
+ PCDB cdb;
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tPreviously sent device quiesce\n", Irp));
+
+ //
+ // don't care the result of device quiesce, we've made the effort.
+ // continue on sending other SCSI commands anyway.
+ //
+
+
+ if (!TEST_FLAG(fdoExtension->PrivateFdoData->HackFlags,
+ FDO_HACK_NO_SYNC_CACHE)) {
+
+ //
+ // send SCSIOP_SYNCHRONIZE_CACHE
+ //
+
+ fdoExtension->PrivateFdoData->MaxPowerOperationRetryCount = MAXIMUM_RETRIES;
+ PowerContext->RetryCount = MAXIMUM_RETRIES;
+
+ if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ status = InitializeStorageRequestBlock((PSTORAGE_REQUEST_BLOCK)srbHeader,
+ STORAGE_ADDRESS_TYPE_BTL8,
+ CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE,
+ 1,
+ SrbExDataTypeScsiCdb16);
+ if (NT_SUCCESS(status)) {
+ ((PSTORAGE_REQUEST_BLOCK)srbHeader)->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
+
+ //
+ // Set length field in Power Context SRB so we know legacy SRB is not being used.
+ //
+ PowerContext->Srb.Length = 0;
+
+ } else {
+ //
+ // Should not occur. Revert to legacy SRB.
+ NT_ASSERT(FALSE);
+ srbHeader = (PSTORAGE_REQUEST_BLOCK_HEADER)&(PowerContext->Srb);
+ RtlZeroMemory(srbHeader, sizeof(SCSI_REQUEST_BLOCK));
+ srbHeader->Length = sizeof(SCSI_REQUEST_BLOCK);
+ srbHeader->Function = SRB_FUNCTION_EXECUTE_SCSI;
+ }
+
+ } else {
+ RtlZeroMemory(srbHeader, sizeof(SCSI_REQUEST_BLOCK));
+ srbHeader->Length = sizeof(SCSI_REQUEST_BLOCK);
+ srbHeader->Function = SRB_FUNCTION_EXECUTE_SCSI;
+ }
+
+
+ SrbSetOriginalRequest(srbHeader, fdoExtension->PrivateFdoData->PowerProcessIrp);
+ SrbSetSenseInfoBuffer(srbHeader, commonExtension->PartitionZeroExtension->SenseData);
+ SrbSetSenseInfoBufferLength(srbHeader, GET_FDO_EXTENSON_SENSE_DATA_LENGTH(commonExtension->PartitionZeroExtension));
+ SrbSetTimeOutValue(srbHeader, fdoExtension->TimeOutValue);
+
+ SrbAssignSrbFlags(srbHeader,
+ (SRB_FLAGS_NO_DATA_TRANSFER |
+ SRB_FLAGS_DISABLE_AUTOSENSE |
+ SRB_FLAGS_DISABLE_SYNCH_TRANSFER |
+ SRB_FLAGS_NO_QUEUE_FREEZE |
+ SRB_FLAGS_BYPASS_LOCKED_QUEUE |
+ SRB_FLAGS_D3_PROCESSING));
+
+ SrbSetCdbLength(srbHeader, 10);
+
+ cdb = SrbGetCdb(srbHeader);
+
+ RtlZeroMemory(cdb, sizeof(CDB));
+ cdb->SYNCHRONIZE_CACHE10.OperationCode = SCSIOP_SYNCHRONIZE_CACHE;
+
+ IoSetCompletionRoutine(fdoExtension->PrivateFdoData->PowerProcessIrp,
+ ClasspPowerDownCompletion,
+ PowerContext,
+ TRUE,
+ TRUE,
+ TRUE);
+
+ nextStack->Parameters.Scsi.Srb = (PSCSI_REQUEST_BLOCK)srbHeader;
+ nextStack->MajorFunction = IRP_MJ_SCSI;
+
+ status = IoCallDriver(commonExtension->LowerDeviceObject, fdoExtension->PrivateFdoData->PowerProcessIrp);
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tIoCallDriver returned %lx\n", fdoExtension->PrivateFdoData->PowerProcessIrp, status));
+ break;
+
+ } else {
+
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_POWER, "(%p)\tPower Down: not sending SYNCH_CACHE\n",
+ PowerContext->DeviceObject));
+ PowerContext->PowerChangeState.PowerDown3++;
+ srbHeader->SrbStatus = SRB_STATUS_SUCCESS;
+ // and fall through....
+ }
+ // no break in case the device doesn't like synch_cache commands
+
+ }
+
+ case PowerDownDeviceFlushed3: {
+
+ PCDB cdb;
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tPreviously send SCSIOP_SYNCHRONIZE_CACHE\n",
+ Irp));
+
+ //
+ // SCSIOP_SYNCHRONIZE_CACHE was sent
+ //
+
+ if (SRB_STATUS(srbHeader->SrbStatus) != SRB_STATUS_SUCCESS) {
+
+ BOOLEAN retry;
+ LONGLONG delta100nsUnits = 0;
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_POWER, "(%p)\tError occured when issuing "
+ "SYNCHRONIZE_CACHE command to device. "
+ "Srb %p, Status %lx\n",
+ Irp,
+ srbHeader,
+ srbHeader->SrbStatus));
+
+ NT_ASSERT(!(TEST_FLAG(srbHeader->SrbStatus, SRB_STATUS_QUEUE_FROZEN)));
+ NT_ASSERT((srbHeader->Function == SRB_FUNCTION_EXECUTE_SCSI) ||
+ (((PSTORAGE_REQUEST_BLOCK)srbHeader)->SrbFunction == SRB_FUNCTION_EXECUTE_SCSI));
+
+ PowerContext->RetryInterval = 0;
+ retry = InterpretSenseInfoWithoutHistory(
+ fdoExtension->DeviceObject,
+ Irp,
+ (PSCSI_REQUEST_BLOCK)srbHeader,
+ IRP_MJ_SCSI,
+ IRP_MJ_POWER,
+ fdoExtension->PrivateFdoData->MaxPowerOperationRetryCount - PowerContext->RetryCount,
+ &status,
+ &delta100nsUnits);
+
+ // NOTE: Power context is a public structure, and thus cannot be
+ // updated to use 100ns units. Therefore, must store the
+ // one-second equivalent. Round up to ensure minimum delay
+ // requirements have been met.
+ delta100nsUnits += (10*1000*1000) - 1;
+ delta100nsUnits /= (10*1000*1000);
+ // guaranteed not to have high bits set per SAL annotations
+ PowerContext->RetryInterval = (ULONG)(delta100nsUnits);
+
+
+ if ((retry == TRUE) && (PowerContext->RetryCount-- != 0)) {
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tRetrying failed request\n", Irp));
+
+ //
+ // decrement the state so we come back through here
+ // the next time.
+ //
+
+ PowerContext->PowerChangeState.PowerDown3--;
+ RetryPowerRequest(commonExtension->DeviceObject,
+ Irp,
+ PowerContext);
+ break;
+ }
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tSYNCHRONIZE_CACHE not retried\n", Irp));
+ fdoExtension->PrivateFdoData->MaxPowerOperationRetryCount = MAXIMUM_RETRIES;
+ PowerContext->RetryCount = MAXIMUM_RETRIES;
+ } // end !SRB_STATUS_SUCCESS
+
+ //
+ // note: we are purposefully ignoring any errors. if the drive
+ // doesn't support a synch_cache, then we're up a creek
+ // anyways.
+ //
+
+ if ((currentStack->Parameters.Power.State.DeviceState == PowerDeviceD3) &&
+ (currentStack->Parameters.Power.ShutdownType == PowerActionHibernate) &&
+ (commonExtension->HibernationPathCount != 0)) {
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tPower Down: not sending SPIN DOWN due to hibernation path\n",
+ PowerContext->DeviceObject));
+
+ PowerContext->PowerChangeState.PowerDown3++;
+ srbHeader->SrbStatus = SRB_STATUS_SUCCESS;
+ status = STATUS_SUCCESS;
+
+ // Fall through to next case...
+
+ } else {
+ // Send STOP UNIT command. As "Imme" bit is set to '1', this command should be completed in short time.
+ // This command is at low importance, failure of this command has very small impact.
+
+ ULONG secondsRemaining;
+ ULONG timeoutValue;
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tSending stop unit to device\n", Irp));
+
+ if (PoQueryWatchdogTime(fdoExtension->LowerPdo, &secondsRemaining)) {
+ // plan to leave some time (TIME_LEFT_FOR_LOWER_DRIVERS) to lower level drivers
+ // for processing the original power irp.
+ if (secondsRemaining >= (TIME_LEFT_FOR_LOWER_DRIVERS + DEFAULT_IO_TIMEOUT_VALUE)) {
+ fdoExtension->PrivateFdoData->MaxPowerOperationRetryCount =
+ (secondsRemaining - TIME_LEFT_FOR_LOWER_DRIVERS) / DEFAULT_IO_TIMEOUT_VALUE;
+
+ // * No 'short' timeouts
+ //
+ // timeoutValue = (secondsRemaining - TIME_LEFT_FOR_LOWER_DRIVERS) %
+ // DEFAULT_IO_TIMEOUT_VALUE;
+ // if (timeoutValue < MINIMUM_STOP_UNIT_TIMEOUT_VALUE)
+ // {
+ if (--fdoExtension->PrivateFdoData->MaxPowerOperationRetryCount)
+ {
+ timeoutValue = DEFAULT_IO_TIMEOUT_VALUE;
+ } else {
+ timeoutValue = secondsRemaining - TIME_LEFT_FOR_LOWER_DRIVERS;
+ }
+ // }
+
+ // Limit to maximum retry count.
+ if (fdoExtension->PrivateFdoData->MaxPowerOperationRetryCount > MAXIMUM_RETRIES) {
+ fdoExtension->PrivateFdoData->MaxPowerOperationRetryCount = MAXIMUM_RETRIES;
+ }
+ } else {
+ // issue the command with minimum timeout value and do not retry on it.
+ fdoExtension->PrivateFdoData->MaxPowerOperationRetryCount = 0;
+
+ // minimum as MINIMUM_STOP_UNIT_TIMEOUT_VALUE.
+ if (secondsRemaining > 2 * MINIMUM_STOP_UNIT_TIMEOUT_VALUE) {
+ timeoutValue = secondsRemaining - MINIMUM_STOP_UNIT_TIMEOUT_VALUE;
+ } else {
+ timeoutValue = MINIMUM_STOP_UNIT_TIMEOUT_VALUE;
+ }
+
+ }
+
+ } else {
+ // do not know how long, use default values.
+ fdoExtension->PrivateFdoData->MaxPowerOperationRetryCount = MAXIMUM_RETRIES;
+ timeoutValue = DEFAULT_IO_TIMEOUT_VALUE;
+ }
+
+ //
+ // Issue STOP UNIT command to the device.
+ //
+
+ PowerContext->RetryCount = fdoExtension->PrivateFdoData->MaxPowerOperationRetryCount;
+
+ if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ status = InitializeStorageRequestBlock((PSTORAGE_REQUEST_BLOCK)srbHeader,
+ STORAGE_ADDRESS_TYPE_BTL8,
+ CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE,
+ 1,
+ SrbExDataTypeScsiCdb16);
+ if (NT_SUCCESS(status)) {
+ ((PSTORAGE_REQUEST_BLOCK)srbHeader)->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
+
+ //
+ // Set length field in Power Context SRB so we know legacy SRB is not being used.
+ //
+ PowerContext->Srb.Length = 0;
+
+ } else {
+ //
+ // Should not occur. Revert to legacy SRB.
+ //
+ NT_ASSERT(FALSE);
+ srbHeader = (PSTORAGE_REQUEST_BLOCK_HEADER)&(PowerContext->Srb);
+ RtlZeroMemory(srbHeader, sizeof(SCSI_REQUEST_BLOCK));
+ srbHeader->Length = sizeof(SCSI_REQUEST_BLOCK);
+ srbHeader->Function = SRB_FUNCTION_EXECUTE_SCSI;
+ }
+
+ } else {
+ RtlZeroMemory(srbHeader, sizeof(SCSI_REQUEST_BLOCK));
+ srbHeader->Length = sizeof(SCSI_REQUEST_BLOCK);
+ srbHeader->Function = SRB_FUNCTION_EXECUTE_SCSI;
+ }
+
+ SrbSetOriginalRequest(srbHeader, fdoExtension->PrivateFdoData->PowerProcessIrp);
+ SrbSetSenseInfoBuffer(srbHeader, commonExtension->PartitionZeroExtension->SenseData);
+ SrbSetSenseInfoBufferLength(srbHeader, GET_FDO_EXTENSON_SENSE_DATA_LENGTH(commonExtension->PartitionZeroExtension));
+ SrbSetTimeOutValue(srbHeader, timeoutValue);
+
+
+ SrbAssignSrbFlags(srbHeader,
+ (SRB_FLAGS_NO_DATA_TRANSFER |
+ SRB_FLAGS_DISABLE_AUTOSENSE |
+ SRB_FLAGS_DISABLE_SYNCH_TRANSFER |
+ SRB_FLAGS_NO_QUEUE_FREEZE |
+ SRB_FLAGS_BYPASS_LOCKED_QUEUE |
+ SRB_FLAGS_D3_PROCESSING));
+
+ SrbSetCdbLength(srbHeader, 6);
+
+ cdb = SrbGetCdb(srbHeader);
+ RtlZeroMemory(cdb, sizeof(CDB));
+
+ cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;
+ cdb->START_STOP.Start = 0;
+ cdb->START_STOP.Immediate = 1;
+
+ IoSetCompletionRoutine(fdoExtension->PrivateFdoData->PowerProcessIrp,
+ ClasspPowerDownCompletion,
+ PowerContext,
+ TRUE,
+ TRUE,
+ TRUE);
+
+ nextStack->Parameters.Scsi.Srb = (PSCSI_REQUEST_BLOCK)srbHeader;
+ nextStack->MajorFunction = IRP_MJ_SCSI;
+
+ status = IoCallDriver(commonExtension->LowerDeviceObject, fdoExtension->PrivateFdoData->PowerProcessIrp);
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tIoCallDriver returned %lx\n", fdoExtension->PrivateFdoData->PowerProcessIrp, status));
+ break;
+ }
+ }
+
+ case PowerDownDeviceStopped3: {
+
+ BOOLEAN ignoreError = TRUE;
+
+ //
+ // stop was sent
+ //
+
+ if (SRB_STATUS(srbHeader->SrbStatus) != SRB_STATUS_SUCCESS) {
+
+ BOOLEAN retry;
+ LONGLONG delta100nsUnits = 0;
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_POWER, "(%p)\tError occured when issueing STOP_UNIT "
+ "command to device. Srb %p, Status %lx\n",
+ Irp,
+ srbHeader,
+ srbHeader->SrbStatus));
+
+ NT_ASSERT(!(TEST_FLAG(srbHeader->SrbStatus, SRB_STATUS_QUEUE_FROZEN)));
+ NT_ASSERT((srbHeader->Function == SRB_FUNCTION_EXECUTE_SCSI) ||
+ (((PSTORAGE_REQUEST_BLOCK)srbHeader)->SrbFunction == SRB_FUNCTION_EXECUTE_SCSI));
+
+ PowerContext->RetryInterval = 0;
+ retry = InterpretSenseInfoWithoutHistory(
+ fdoExtension->DeviceObject,
+ Irp,
+ (PSCSI_REQUEST_BLOCK)srbHeader,
+ IRP_MJ_SCSI,
+ IRP_MJ_POWER,
+ fdoExtension->PrivateFdoData->MaxPowerOperationRetryCount - PowerContext->RetryCount,
+ &status,
+ &delta100nsUnits);
+
+ // NOTE: Power context is a public structure, and thus cannot be
+ // updated to use 100ns units. Therefore, must store the
+ // one-second equivalent. Round up to ensure minimum delay
+ // requirements have been met.
+ delta100nsUnits += (10*1000*1000) - 1;
+ delta100nsUnits /= (10*1000*1000);
+ // guaranteed not to have high bits set per SAL annotations
+ PowerContext->RetryInterval = (ULONG)(delta100nsUnits);
+
+
+ if ((retry == TRUE) && (PowerContext->RetryCount-- != 0)) {
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tRetrying failed request\n", Irp));
+
+ //
+ // decrement the state so we come back through here
+ // the next time.
+ //
+
+ PowerContext->PowerChangeState.PowerDown3--;
+
+ SrbSetTimeOutValue(srbHeader, DEFAULT_IO_TIMEOUT_VALUE);
+
+ RetryPowerRequest(commonExtension->DeviceObject,
+ Irp,
+ PowerContext);
+ break;
+ }
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tSTOP_UNIT not retried\n", Irp));
+ fdoExtension->PrivateFdoData->MaxPowerOperationRetryCount = MAXIMUM_RETRIES;
+ PowerContext->RetryCount = MAXIMUM_RETRIES;
+
+ } // end !SRB_STATUS_SUCCESS
+
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tPreviously sent stop unit\n", Irp));
+
+ //
+ // some operations, such as a physical format in progress,
+ // should not be ignored and should fail the power operation.
+ //
+
+ if (!NT_SUCCESS(status)) {
+
+ PVOID senseBuffer = SrbGetSenseInfoBuffer(srbHeader);
+
+ if (TEST_FLAG(srbHeader->SrbStatus, SRB_STATUS_AUTOSENSE_VALID) &&
+ (senseBuffer != NULL)) {
+
+ BOOLEAN validSense = FALSE;
+ UCHAR senseKey = 0;
+ UCHAR additionalSenseCode = 0;
+ UCHAR additionalSenseCodeQualifier = 0;
+
+ validSense = ScsiGetSenseKeyAndCodes(senseBuffer,
+ SrbGetSenseInfoBufferLength(srbHeader),
+ SCSI_SENSE_OPTIONS_FIXED_FORMAT_IF_UNKNOWN_FORMAT_INDICATED,
+ &senseKey,
+ &additionalSenseCode,
+ &additionalSenseCodeQualifier);
+
+ if (validSense) {
+ if ((senseKey == SCSI_SENSE_NOT_READY) &&
+ (additionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY) &&
+ (additionalSenseCodeQualifier == SCSI_SENSEQ_FORMAT_IN_PROGRESS)) {
+
+ ignoreError = FALSE;
+ PowerContext->FinalStatus = STATUS_DEVICE_BUSY;
+ status = PowerContext->FinalStatus;
+ }
+ }
+ }
+ }
+
+ if (NT_SUCCESS(status) || ignoreError) {
+
+ //
+ // Issue the original power request to the lower driver.
+ //
+
+ IoCopyCurrentIrpStackLocationToNext(OriginalIrp);
+
+ IoSetCompletionRoutine(OriginalIrp,
+ ClasspPowerDownCompletion,
+ PowerContext,
+ TRUE,
+ TRUE,
+ TRUE);
+
+ status = PoCallDriver(commonExtension->LowerDeviceObject, OriginalIrp);
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tPoCallDriver returned %lx\n", OriginalIrp, status));
+ break;
+ }
+
+ // else fall through w/o sending the power irp, since the device
+ // is reporting an error that would be "really bad" to power down
+ // during.
+
+ }
+
+ case PowerDownDeviceOff3: {
+
+ //
+ // SpinDown request completed ... whether it succeeded or not is
+ // another matter entirely.
+ //
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tPreviously sent power irp\n", OriginalIrp));
+
+ if (PowerContext->QueueLocked) {
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tUnlocking queue\n", OriginalIrp));
+
+ if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ //
+ // Will reuse SRB for a non-SCSI SRB.
+ //
+ status = InitializeStorageRequestBlock((PSTORAGE_REQUEST_BLOCK)srbHeader,
+ STORAGE_ADDRESS_TYPE_BTL8,
+ CLASS_SRBEX_NO_SRBEX_DATA_BUFFER_SIZE,
+ 0);
+ if (NT_SUCCESS(status)) {
+ ((PSTORAGE_REQUEST_BLOCK)srbHeader)->SrbFunction = SRB_FUNCTION_UNLOCK_QUEUE;
+
+ //
+ // Set length field in Power Context SRB so we know legacy SRB is not being used.
+ //
+ PowerContext->Srb.Length = 0;
+
+ } else {
+ //
+ // Should not occur. Revert to legacy SRB.
+ //
+ NT_ASSERT(FALSE);
+ srbHeader = (PSTORAGE_REQUEST_BLOCK_HEADER)&(PowerContext->Srb);
+ RtlZeroMemory(srbHeader, sizeof(SCSI_REQUEST_BLOCK));
+ srbHeader->Length = sizeof(SCSI_REQUEST_BLOCK);
+ srbHeader->Function = SRB_FUNCTION_UNLOCK_QUEUE;
+ }
+ } else {
+ RtlZeroMemory(srbHeader, sizeof(SCSI_REQUEST_BLOCK));
+ srbHeader->Length = sizeof(SCSI_REQUEST_BLOCK);
+ srbHeader->Function = SRB_FUNCTION_UNLOCK_QUEUE;
+ }
+
+ SrbSetOriginalRequest(srbHeader, fdoExtension->PrivateFdoData->PowerProcessIrp);
+ SrbAssignSrbFlags(srbHeader, (SRB_FLAGS_BYPASS_LOCKED_QUEUE |
+ SRB_FLAGS_D3_PROCESSING));
+
+ nextStack->Parameters.Scsi.Srb = (PSCSI_REQUEST_BLOCK)srbHeader;
+ nextStack->MajorFunction = IRP_MJ_SCSI;
+
+ IoSetCompletionRoutine(fdoExtension->PrivateFdoData->PowerProcessIrp,
+ ClasspPowerDownCompletion,
+ PowerContext,
+ TRUE,
+ TRUE,
+ TRUE);
+
+ status = IoCallDriver(commonExtension->LowerDeviceObject, fdoExtension->PrivateFdoData->PowerProcessIrp);
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tIoCallDriver returned %lx\n",
+ fdoExtension->PrivateFdoData->PowerProcessIrp,
+ status));
+ break;
+ }
+
+ }
+
+ case PowerDownDeviceUnlocked3: {
+
+ //
+ // This is the end of the dance.
+ // We're ignoring possible intermediate error conditions ....
+ //
+
+ if (PowerContext->QueueLocked == FALSE) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tFall through (queue not locked)\n", OriginalIrp));
+ } else {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tPreviously unlocked queue\n", OriginalIrp));
+ NT_ASSERT(NT_SUCCESS(Irp->IoStatus.Status));
+ NT_ASSERT(srbHeader->SrbStatus == SRB_STATUS_SUCCESS);
+
+ if (NT_SUCCESS(Irp->IoStatus.Status)) {
+ PowerContext->QueueLocked = FALSE;
+ }
+ }
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tFreeing srb and completing\n", OriginalIrp));
+ status = PowerContext->FinalStatus; // allow failure to propogate
+
+ OriginalIrp->IoStatus.Status = status;
+ OriginalIrp->IoStatus.Information = 0;
+
+ if (NT_SUCCESS(status)) {
+
+ //
+ // Set the new power state
+ //
+
+ fdoExtension->DevicePowerState =
+ currentStack->Parameters.Power.State.DeviceState;
+
+ }
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tStarting next power irp\n", OriginalIrp));
+
+ ClassReleaseRemoveLock(PowerContext->DeviceObject, OriginalIrp);
+
+ PowerContext->InUse = FALSE;
+
+ PoStartNextPowerIrp(OriginalIrp);
+
+ fdoExtension->PowerDownInProgress = FALSE;
+
+ // prevent from completing the irp allocated by ourselves
+ if (Irp == fdoExtension->PrivateFdoData->PowerProcessIrp) {
+ // complete original irp if we are processing powerprocess irp,
+ // otherwise, by returning status other than STATUS_MORE_PROCESSING_REQUIRED, IO manager will complete it.
+ ClassCompleteRequest(commonExtension->DeviceObject, OriginalIrp, IO_NO_INCREMENT);
+ status = STATUS_MORE_PROCESSING_REQUIRED;
+ }
+
+ return status;
+ }
+ }
+
+ return STATUS_MORE_PROCESSING_REQUIRED;
+} // end ClasspPowerDownCompletion()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClasspPowerHandler()
+
+Routine Description:
+
+ This routine reduces the number of useless spinups and spindown requests
+ sent to a given device by ignoring transitions to power states we are
+ currently in.
+
+ ISSUE-2000/02/20-henrygab - by ignoring spin-up requests, we may be
+ allowing the drive
+
+Arguments:
+
+ DeviceObject - the device object which is transitioning power states
+ Irp - the power irp
+ Options - a set of flags indicating what the device handles
+
+Return Value:
+
+--*/
+NTSTATUS
+ClasspPowerHandler(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN CLASS_POWER_OPTIONS Options // ISSUE-2000/02/20-henrygab - pass pointer, not whole struct
+ )
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PDEVICE_OBJECT lowerDevice = commonExtension->LowerDeviceObject;
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
+ PIO_STACK_LOCATION nextIrpStack;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+ PCLASS_POWER_CONTEXT context;
+ PSTORAGE_REQUEST_BLOCK_HEADER srbHeader;
+ ULONG srbFlags;
+ NTSTATUS status;
+
+ _Analysis_assume_(fdoExtension);
+ _Analysis_assume_(fdoExtension->PrivateFdoData);
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "ClasspPowerHandler: Power irp %p to %s %p\n",
+ Irp, (commonExtension->IsFdo ? "fdo" : "pdo"), DeviceObject));
+
+ if (!commonExtension->IsFdo) {
+
+ //
+ // certain assumptions are made here,
+ // particularly: having the fdoExtension
+ //
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_POWER, "ClasspPowerHandler: Called for PDO %p???\n",
+ DeviceObject));
+ NT_ASSERT(!"PDO using ClasspPowerHandler");
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
+ PoStartNextPowerIrp(Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ switch (irpStack->MinorFunction) {
+
+ case IRP_MN_SET_POWER: {
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tIRP_MN_SET_POWER\n", Irp));
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tSetting %s state to %d\n",
+ Irp,
+ (irpStack->Parameters.Power.Type == SystemPowerState ?
+ "System" : "Device"),
+ irpStack->Parameters.Power.State.SystemState));
+
+ switch (irpStack->Parameters.Power.ShutdownType){
+
+ case PowerActionNone:
+
+ //
+ // Skip if device doesn't need volume verification during idle power
+ // transitions.
+ //
+ if ((fdoExtension->FunctionSupportInfo) &&
+ (fdoExtension->FunctionSupportInfo->IdlePower.NoVerifyDuringIdlePower)) {
+ break;
+ }
+
+ case PowerActionSleep:
+ case PowerActionHibernate:
+ if (fdoData->HotplugInfo.MediaRemovable || fdoData->HotplugInfo.MediaHotplug) {
+ /*
+ * We are suspending device and this drive is either hot-pluggable
+ * or contains removeable media.
+ * Set the media dirty bit, since the media may change while
+ * we are suspended.
+ */
+ SET_FLAG(DeviceObject->Flags, DO_VERIFY_VOLUME);
+
+ //
+ // Bumping the media change count will force the
+ // file system to verify the volume when we resume
+ //
+
+ InterlockedIncrement((volatile LONG *)&fdoExtension->MediaChangeCount);
+ }
+
+ break;
+ }
+
+ break;
+ }
+
+ default: {
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tIrp minor code = %#x\n",
+ Irp, irpStack->MinorFunction));
+ break;
+ }
+ }
+
+ if (irpStack->Parameters.Power.Type != DevicePowerState ||
+ irpStack->MinorFunction != IRP_MN_SET_POWER) {
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tSending to lower device\n", Irp));
+
+ goto ClasspPowerHandlerCleanup;
+
+ }
+
+ //
+ // already in exact same state, don't work to transition to it.
+ //
+
+ if (irpStack->Parameters.Power.State.DeviceState ==
+ fdoExtension->DevicePowerState) {
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tAlready in device state %x\n",
+ Irp, fdoExtension->DevicePowerState));
+ goto ClasspPowerHandlerCleanup;
+
+ }
+
+ //
+ // or powering down from non-d0 state (device already stopped)
+ // NOTE -- we're not sure whether this case can exist or not (the
+ // power system may never send this sort of request) but it's trivial
+ // to deal with.
+ //
+
+ if ((irpStack->Parameters.Power.State.DeviceState != PowerDeviceD0) &&
+ (fdoExtension->DevicePowerState != PowerDeviceD0)) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tAlready powered down to %x???\n",
+ Irp, fdoExtension->DevicePowerState));
+ fdoExtension->DevicePowerState =
+ irpStack->Parameters.Power.State.DeviceState;
+ goto ClasspPowerHandlerCleanup;
+ }
+
+ //
+ // or when not handling powering up and are powering up
+ //
+
+ if ((!Options.HandleSpinUp) &&
+ (irpStack->Parameters.Power.State.DeviceState == PowerDeviceD0)) {
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tNot handling spinup to state %x\n",
+ Irp, fdoExtension->DevicePowerState));
+ fdoExtension->DevicePowerState =
+ irpStack->Parameters.Power.State.DeviceState;
+ goto ClasspPowerHandlerCleanup;
+
+ }
+
+ //
+ // or when not handling powering down and are powering down
+ //
+
+ if ((!Options.HandleSpinDown) &&
+ (irpStack->Parameters.Power.State.DeviceState != PowerDeviceD0)) {
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tNot handling spindown to state %x\n",
+ Irp, fdoExtension->DevicePowerState));
+ fdoExtension->DevicePowerState =
+ irpStack->Parameters.Power.State.DeviceState;
+ goto ClasspPowerHandlerCleanup;
+
+ }
+
+ //
+ // validation completed, start the real work.
+ //
+
+ IoReuseIrp(fdoExtension->PrivateFdoData->PowerProcessIrp, STATUS_SUCCESS);
+ IoSetNextIrpStackLocation(fdoExtension->PrivateFdoData->PowerProcessIrp);
+ nextIrpStack = IoGetNextIrpStackLocation(fdoExtension->PrivateFdoData->PowerProcessIrp);
+
+ context = &(fdoExtension->PowerContext);
+
+ NT_ASSERT(context->InUse == FALSE);
+
+ RtlZeroMemory(context, sizeof(CLASS_POWER_CONTEXT));
+ context->InUse = TRUE;
+
+ if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ srbHeader = (PSTORAGE_REQUEST_BLOCK_HEADER)&(fdoExtension->PrivateFdoData->PowerSrb.SrbEx);
+
+ //
+ // Initialize extended SRB for a SRB_FUNCTION_LOCK_QUEUE
+ //
+ status = InitializeStorageRequestBlock((PSTORAGE_REQUEST_BLOCK)srbHeader,
+ STORAGE_ADDRESS_TYPE_BTL8,
+ CLASS_SRBEX_NO_SRBEX_DATA_BUFFER_SIZE,
+ 0);
+ if (NT_SUCCESS(status)) {
+ ((PSTORAGE_REQUEST_BLOCK)srbHeader)->SrbFunction = SRB_FUNCTION_LOCK_QUEUE;
+ } else {
+ //
+ // Should not happen. Revert to legacy SRB.
+ //
+ NT_ASSERT(FALSE);
+ srbHeader = (PSTORAGE_REQUEST_BLOCK_HEADER)&(context->Srb);
+ srbHeader->Length = sizeof(SCSI_REQUEST_BLOCK);
+ srbHeader->Function = SRB_FUNCTION_LOCK_QUEUE;
+ }
+ } else {
+ srbHeader = (PSTORAGE_REQUEST_BLOCK_HEADER)&(context->Srb);
+ srbHeader->Length = sizeof(SCSI_REQUEST_BLOCK);
+ srbHeader->Function = SRB_FUNCTION_LOCK_QUEUE;
+ }
+ nextIrpStack->Parameters.Scsi.Srb = (PSCSI_REQUEST_BLOCK)srbHeader;
+ nextIrpStack->MajorFunction = IRP_MJ_SCSI;
+
+ context->FinalStatus = STATUS_SUCCESS;
+
+ SrbSetOriginalRequest(srbHeader, fdoExtension->PrivateFdoData->PowerProcessIrp);
+ SrbSetSrbFlags(srbHeader, (SRB_FLAGS_BYPASS_LOCKED_QUEUE | SRB_FLAGS_NO_QUEUE_FREEZE));
+
+ fdoExtension->PrivateFdoData->MaxPowerOperationRetryCount = MAXIMUM_RETRIES;
+ context->RetryCount = MAXIMUM_RETRIES;
+
+ context->Options = Options;
+ context->DeviceObject = DeviceObject;
+ context->Irp = Irp;
+
+ if (irpStack->Parameters.Power.State.DeviceState == PowerDeviceD0) {
+
+ NT_ASSERT(Options.HandleSpinUp);
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tpower up - locking queue\n", Irp));
+
+ //
+ // We need to issue a queue lock request so that we
+ // can spin the drive back up after the power is restored
+ // but before any requests are processed.
+ //
+
+ context->Options.PowerDown = FALSE;
+ context->PowerChangeState.PowerUp = PowerUpDeviceInitial;
+ context->CompletionRoutine = ClasspPowerUpCompletion;
+
+ } else {
+
+ NT_ASSERT(Options.HandleSpinDown);
+
+ fdoExtension->PowerDownInProgress = TRUE;
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tPowering down - locking queue\n", Irp));
+
+ //
+ // Disable tick timer at beginning of D3 processing if running.
+ //
+ if ((fdoExtension->PrivateFdoData->TickTimerEnabled)) {
+ ClasspDisableTimer(fdoExtension);
+ }
+
+ PoSetPowerState(DeviceObject,
+ irpStack->Parameters.Power.Type,
+ irpStack->Parameters.Power.State);
+
+ context->Options.PowerDown = TRUE;
+ context->PowerChangeState.PowerDown3 = PowerDownDeviceInitial3;
+ context->CompletionRoutine = ClasspPowerDownCompletion;
+
+ }
+
+ //
+ // we are not dealing with port-allocated sense in these routines.
+ //
+
+ srbFlags = SrbGetSrbFlags(srbHeader);
+ NT_ASSERT(!TEST_FLAG(srbFlags, SRB_FLAGS_FREE_SENSE_BUFFER));
+ NT_ASSERT(!TEST_FLAG(srbFlags, SRB_FLAGS_PORT_DRIVER_ALLOCSENSE));
+
+ //
+ // Mark the original power irp pending.
+ //
+
+ IoMarkIrpPending(Irp);
+
+ if (Options.LockQueue) {
+
+ //
+ // Send the lock irp down.
+ //
+
+ IoSetCompletionRoutine(fdoExtension->PrivateFdoData->PowerProcessIrp,
+ context->CompletionRoutine,
+ context,
+ TRUE,
+ TRUE,
+ TRUE);
+
+ IoCallDriver(lowerDevice, fdoExtension->PrivateFdoData->PowerProcessIrp);
+
+ } else {
+
+ //
+ // Call the completion routine directly. It won't care what the
+ // status of the "lock" was - it will just go and do the next
+ // step of the operation.
+ //
+
+ context->CompletionRoutine(DeviceObject, fdoExtension->PrivateFdoData->PowerProcessIrp, context);
+ }
+
+ return STATUS_PENDING;
+
+ClasspPowerHandlerCleanup:
+
+ //
+ // Send the original power irp down, we will start the next power irp in completion routine.
+ //
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tStarting next power irp\n", Irp));
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+ IoSetCompletionRoutine(Irp,
+ ClasspStartNextPowerIrpCompletion,
+ NULL,
+ TRUE,
+ TRUE,
+ TRUE);
+ return PoCallDriver(lowerDevice, Irp);
+} // end ClasspPowerHandler()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassMinimalPowerHandler()
+
+Routine Description:
+
+ This routine is the minimum power handler for a storage driver. It does
+ the least amount of work possible.
+
+--*/
+NTSTATUS
+ClassMinimalPowerHandler(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
+ NTSTATUS status;
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ PoStartNextPowerIrp(Irp);
+
+ switch (irpStack->MinorFunction)
+ {
+ case IRP_MN_SET_POWER:
+ {
+ switch (irpStack->Parameters.Power.ShutdownType)
+ {
+ case PowerActionNone:
+ case PowerActionSleep:
+ case PowerActionHibernate:
+ {
+ if (TEST_FLAG(DeviceObject->Characteristics, FILE_REMOVABLE_MEDIA))
+ {
+ if ((ClassGetVpb(DeviceObject) != NULL) && (ClassGetVpb(DeviceObject)->Flags & VPB_MOUNTED))
+ {
+ //
+ // This flag will cause the filesystem to verify the
+ // volume when coming out of hibernation or standby or runtime power
+ //
+ SET_FLAG(DeviceObject->Flags, DO_VERIFY_VOLUME);
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ //
+ // Fall through
+ //
+
+ case IRP_MN_QUERY_POWER:
+ {
+ if (!commonExtension->IsFdo)
+ {
+ Irp->IoStatus.Status = STATUS_SUCCESS;
+ Irp->IoStatus.Information = 0;
+ }
+ }
+ break;
+ }
+
+ if (commonExtension->IsFdo)
+ {
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+ status = PoCallDriver(commonExtension->LowerDeviceObject, Irp);
+ }
+ else
+ {
+ status = Irp->IoStatus.Status;
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ }
+
+ return status;
+} // end ClassMinimalPowerHandler()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassSpinDownPowerHandler()
+
+Routine Description:
+
+ This routine is a callback for disks and other things which require both
+ a start and a stop to be sent to the device. (actually the starts are
+ almost always optional, since most device power themselves on to process
+ commands, but i digress).
+
+ Determines proper use of spinup, spindown, and queue locking based upon
+ ScanForSpecialFlags in the FdoExtension. This is the most common power
+ handler passed into classpnp.sys
+
+Arguments:
+
+ DeviceObject - Supplies the functional device object
+
+ Irp - Supplies the request to be retried.
+
+Return Value:
+
+ None
+
+--*/
+__control_entrypoint(DeviceDriver)
+NTSTATUS
+ClassSpinDownPowerHandler(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _In_ PIRP Irp
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
+ CLASS_POWER_OPTIONS options = {0};
+
+ fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+
+ //
+ // check the flags to see what options we need to worry about
+ //
+
+ if (!TEST_FLAG(fdoExtension->ScanForSpecialFlags,
+ CLASS_SPECIAL_DISABLE_SPIN_DOWN)) {
+ options.HandleSpinDown = TRUE;
+ }
+
+ if (!TEST_FLAG(fdoExtension->ScanForSpecialFlags,
+ CLASS_SPECIAL_DISABLE_SPIN_UP)) {
+ options.HandleSpinUp = TRUE;
+ }
+
+ if (!TEST_FLAG(fdoExtension->ScanForSpecialFlags,
+ CLASS_SPECIAL_NO_QUEUE_LOCK)) {
+ options.LockQueue = TRUE;
+ }
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "ClasspPowerHandler: Devobj %p\n"
+ "\t%shandling spin down\n"
+ "\t%shandling spin up\n"
+ "\t%slocking queue\n",
+ DeviceObject,
+ (options.HandleSpinDown ? "" : "not "),
+ (options.HandleSpinUp ? "" : "not "),
+ (options.LockQueue ? "" : "not ")
+ ));
+
+ //
+ // do all the dirty work
+ //
+
+ return ClasspPowerHandler(DeviceObject, Irp, options);
+} // end ClassSpinDownPowerHandler()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassStopUnitPowerHandler()
+
+Routine Description:
+
+ This routine is an outdated call. To achieve equivalent functionality,
+ the driver should set the following flags in ScanForSpecialFlags in the
+ FdoExtension:
+
+ CLASS_SPECIAL_DISABLE_SPIN_UP
+ CLASS_SPECIAL_NO_QUEUE_LOCK
+
+--*/
+NTSTATUS
+ClassStopUnitPowerHandler(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _In_ PIRP Irp
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_POWER, "ClassStopUnitPowerHandler - Devobj %p using outdated call\n"
+ "Drivers should set the following flags in ScanForSpecialFlags "
+ " in the FDO extension:\n"
+ "\tCLASS_SPECIAL_DISABLE_SPIN_UP\n"
+ "\tCLASS_SPECIAL_NO_QUEUE_LOCK\n"
+ "This will provide equivalent functionality if the power "
+ "routine is then set to ClassSpinDownPowerHandler\n\n",
+ DeviceObject));
+
+ fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+
+ SET_FLAG(fdoExtension->ScanForSpecialFlags,
+ CLASS_SPECIAL_DISABLE_SPIN_UP);
+ SET_FLAG(fdoExtension->ScanForSpecialFlags,
+ CLASS_SPECIAL_NO_QUEUE_LOCK);
+
+ return ClassSpinDownPowerHandler(DeviceObject, Irp);
+} // end ClassStopUnitPowerHandler()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+RetryPowerRequest()
+
+Routine Description:
+
+ This routine reinitalizes the necessary fields, and sends the request
+ to the lower driver.
+
+Arguments:
+
+ DeviceObject - Supplies the device object associated with this request.
+
+ Irp - Supplies the request to be retried.
+
+ Context - Supplies a pointer to the power up context for this request.
+
+Return Value:
+
+ None
+
+--*/
+VOID
+RetryPowerRequest(
+ PDEVICE_OBJECT DeviceObject,
+ PIRP Irp,
+ PCLASS_POWER_CONTEXT Context
+ )
+{
+ PIO_STACK_LOCATION nextIrpStack = IoGetNextIrpStackLocation(Irp);
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension =
+ (PFUNCTIONAL_DEVICE_EXTENSION)Context->DeviceObject->DeviceExtension;
+ PSTORAGE_REQUEST_BLOCK_HEADER srb;
+ LONGLONG dueTime;
+ ULONG srbFlags;
+ ULONG srbFunction;
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tDelaying retry by queueing DPC\n", Irp));
+
+ //NT_ASSERT(Context->Irp == Irp);
+ if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ srb = (PSTORAGE_REQUEST_BLOCK_HEADER)&(fdoExtension->PrivateFdoData->PowerSrb.SrbEx);
+
+ //
+ // Check if reverted to using legacy SRB.
+ //
+ if (Context->Srb.Length == sizeof(SCSI_REQUEST_BLOCK)) {
+ srb = (PSTORAGE_REQUEST_BLOCK_HEADER)&(Context->Srb);
+ srbFunction = srb->Function;
+ } else {
+ srbFunction = ((PSTORAGE_REQUEST_BLOCK)srb)->SrbFunction;
+ }
+ } else {
+ srb = (PSTORAGE_REQUEST_BLOCK_HEADER)&(Context->Srb);
+ srbFunction = srb->Function;
+ }
+
+ NT_ASSERT(Context->DeviceObject == DeviceObject);
+ srbFlags = SrbGetSrbFlags(srb);
+ NT_ASSERT(!TEST_FLAG(srbFlags, SRB_FLAGS_FREE_SENSE_BUFFER));
+ NT_ASSERT(!TEST_FLAG(srbFlags, SRB_FLAGS_PORT_DRIVER_ALLOCSENSE));
+
+ if (Context->RetryInterval == 0) {
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tDelaying minimum time (.2 sec)\n", Irp));
+ dueTime = (LONGLONG)1000000 * 2;
+
+ } else {
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER, "(%p)\tDelaying %x seconds\n",
+ Irp, Context->RetryInterval));
+ dueTime = (LONGLONG)1000000 * 10 * Context->RetryInterval;
+
+ }
+
+ //
+ // reset the retry interval
+ //
+
+ Context->RetryInterval = 0;
+
+ //
+ // Reset byte count of transfer in SRB Extension.
+ //
+
+ SrbSetDataTransferLength(srb, 0);
+
+ //
+ // Zero SRB statuses.
+ //
+
+ srb->SrbStatus = 0;
+ if (srbFunction == SRB_FUNCTION_EXECUTE_SCSI) {
+ SrbSetScsiStatus(srb, 0);
+ }
+
+ //
+ // Set up major SCSI function.
+ //
+
+ nextIrpStack->MajorFunction = IRP_MJ_SCSI;
+
+ //
+ // Save SRB address in next stack for port driver.
+ //
+
+ nextIrpStack->Parameters.Scsi.Srb = (PSCSI_REQUEST_BLOCK)srb;
+
+ //
+ // Set the completion routine up again.
+ //
+
+ IoSetCompletionRoutine(Irp, Context->CompletionRoutine, Context,
+ TRUE, TRUE, TRUE);
+
+ ClassRetryRequest(DeviceObject, Irp, dueTime);
+
+ return;
+
+} // end RetryRequest()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClasspStartNextPowerIrpCompletion()
+
+Routine Description:
+
+ This routine guarantees that the next power irp (power up or down) is not
+ sent until the previous one has fully completed.
+
+--*/
+NTSTATUS
+ClasspStartNextPowerIrpCompletion(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ )
+{
+ PCLASS_POWER_CONTEXT PowerContext = (PCLASS_POWER_CONTEXT)Context;
+
+ UNREFERENCED_PARAMETER(DeviceObject);
+
+ if (Irp->PendingReturned) {
+ IoMarkIrpPending(Irp);
+ }
+
+ if (PowerContext != NULL)
+ {
+ PowerContext->InUse = FALSE;
+ }
+
+
+ PoStartNextPowerIrp(Irp);
+ return STATUS_SUCCESS;
+} // end ClasspStartNextPowerIrpCompletion()
+
+NTSTATUS
+ClasspDeviceLockFailurePowerIrpCompletion(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ )
+{
+ PCLASS_POWER_CONTEXT PowerContext = (PCLASS_POWER_CONTEXT)Context;
+ PCOMMON_DEVICE_EXTENSION commonExtension;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
+ PIO_STACK_LOCATION currentStack;
+ BOOLEAN FailurePredictionEnabled = FALSE;
+
+ UNREFERENCED_PARAMETER(DeviceObject);
+
+ commonExtension = PowerContext->DeviceObject->DeviceExtension;
+ fdoExtension = PowerContext->DeviceObject->DeviceExtension;
+
+ currentStack = IoGetCurrentIrpStackLocation(Irp);
+
+ //
+ // Set the new power state
+ //
+
+ fdoExtension->DevicePowerState = currentStack->Parameters.Power.State.DeviceState;
+
+ //
+ // We reach here becasue LockQueue operation was not successful.
+ // However, media change detection would not happen in case of resume becasue we
+ // had disabled the timer while going into lower power state.
+ // So, if the device goes into D0 then enable the tick timer.
+ //
+
+ if (fdoExtension->DevicePowerState == PowerDeviceD0) {
+ //
+ // Check whether failure detection is enabled
+ //
+
+ if ((fdoExtension->FailurePredictionInfo != NULL) &&
+ (fdoExtension->FailurePredictionInfo->Method != FailurePredictionNone)) {
+ FailurePredictionEnabled = TRUE;
+ }
+
+ //
+ // Enable tick timer at end of D0 processing if it was previously enabled.
+ //
+
+ if ((commonExtension->DriverExtension->InitData.ClassTick != NULL) ||
+ ((fdoExtension->MediaChangeDetectionInfo != NULL) &&
+ (fdoExtension->FunctionSupportInfo != NULL) &&
+ (fdoExtension->FunctionSupportInfo->AsynchronousNotificationSupported == FALSE)) ||
+ (FailurePredictionEnabled)) {
+
+ //
+ // If failure prediction is turned on and we've been powered
+ // off longer than the failure prediction query period then
+ // force the query on the next timer tick.
+ //
+
+ if ((FailurePredictionEnabled) && (ClasspFailurePredictionPeriodMissed(fdoExtension))) {
+ fdoExtension->FailurePredictionInfo->CountDown = 1;
+ }
+
+ //
+ // Finally, enable the timer.
+ //
+
+ ClasspEnableTimer(fdoExtension);
+ }
+ }
+
+ //
+ // Indicate to Po that we've been successfully powered up so
+ // it can do it's notification stuff.
+ //
+
+ PoSetPowerState(PowerContext->DeviceObject,
+ currentStack->Parameters.Power.Type,
+ currentStack->Parameters.Power.State);
+
+ PowerContext->InUse = FALSE;
+
+
+ ClassReleaseRemoveLock(commonExtension->DeviceObject, Irp);
+
+ //
+ // Start the next power IRP
+ //
+
+ if (Irp->PendingReturned) {
+ IoMarkIrpPending(Irp);
+ }
+
+ PoStartNextPowerIrp(Irp);
+
+ return STATUS_SUCCESS;
+}
+
+
+_IRQL_requires_same_
+NTSTATUS
+ClasspSendEnableIdlePowerIoctl(
+ _In_ PDEVICE_OBJECT DeviceObject
+ )
+/*++
+Description:
+
+ This function is used to send IOCTL_STORAGE_ENABLE_IDLE_POWER to the port
+ driver. It pulls the relevant idle power management properties from the
+ FDO's device extension.
+
+Arguments:
+
+ DeviceObject - The class FDO.
+
+Return Value:
+
+ The NTSTATUS code returned from the port driver. STATUS_SUCCESS indicates
+ this device is now enabled for idle (runtime) power management.
+
+--*/
+{
+ NTSTATUS status;
+ STORAGE_IDLE_POWER idlePower = {0};
+ IO_STATUS_BLOCK ioStatus = {0};
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+ PCOMMON_DEVICE_EXTENSION commonExtension = &(fdoExtension->CommonExtension);
+
+ idlePower.Version = 1;
+ idlePower.Size = sizeof(STORAGE_IDLE_POWER);
+ idlePower.WakeCapableHint = fdoExtension->FunctionSupportInfo->IdlePower.DeviceWakeable;
+ idlePower.D3ColdSupported = fdoExtension->FunctionSupportInfo->IdlePower.D3ColdSupported;
+ idlePower.D3IdleTimeout = fdoExtension->FunctionSupportInfo->IdlePower.D3IdleTimeout;
+
+ ClassSendDeviceIoControlSynchronous(
+ IOCTL_STORAGE_ENABLE_IDLE_POWER,
+ commonExtension->LowerDeviceObject,
+ &idlePower,
+ sizeof(STORAGE_IDLE_POWER),
+ 0,
+ FALSE,
+ &ioStatus
+ );
+
+ status = ioStatus.Status;
+
+ TracePrint((TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_POWER,
+ "ClasspSendEnableIdlePowerIoctl: Port driver returned status (%x) for FDO (%p)\n"
+ "\tWakeCapableHint: %u\n"
+ "\tD3ColdSupported: %u\n"
+ "\tD3IdleTimeout: %u (ms)",
+ status,
+ DeviceObject,
+ idlePower.WakeCapableHint,
+ idlePower.D3ColdSupported,
+ idlePower.D3IdleTimeout));
+
+ return status;
+}
+
+_Function_class_(POWER_SETTING_CALLBACK)
+_IRQL_requires_same_
+NTSTATUS
+ClasspPowerSettingCallback(
+ _In_ LPCGUID SettingGuid,
+ _In_reads_bytes_(ValueLength) PVOID Value,
+ _In_ ULONG ValueLength,
+ _Inout_opt_ PVOID Context
+)
+/*++
+Description:
+
+ This function is the callback for power setting notifications (registered
+ when ClasspGetD3IdleTimeout() is called for the first time).
+
+ Currently, this function is used to get the disk idle timeout value from
+ the system power settings.
+
+ This function is guaranteed to be called at PASSIVE_LEVEL.
+
+Arguments:
+
+ SettingGuid - The power setting GUID.
+ Value - Pointer to the power setting value.
+ ValueLength - Size of the Value buffer.
+ Context - The FDO's device extension.
+
+Return Value:
+
+ STATUS_SUCCESS
+
+--*/
+{
+ PIDLE_POWER_FDO_LIST_ENTRY fdoEntry = NULL;
+
+#pragma warning(suppress:4054) // okay to type cast function pointer to PIRP for this use case
+ PIRP removeLockTag = (PIRP)&ClasspPowerSettingCallback;
+
+ UNREFERENCED_PARAMETER(Context);
+
+ PAGED_CODE();
+
+ if (IsEqualGUID(SettingGuid, &GUID_DISK_IDLE_TIMEOUT)) {
+ if (ValueLength != sizeof(ULONG) || Value == NULL) {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // The value supplied by this GUID is already in milliseconds.
+ //
+ DiskIdleTimeoutInMS = *((PULONG)Value);
+
+ //
+ // For each FDO on the idle power list, grab the remove lock and send
+ // IOCTL_STORAGE_ENABLE_IDLE_POWER to the port driver to update the
+ // idle timeout value.
+ //
+ KeAcquireGuardedMutex(&IdlePowerFDOListMutex);
+ fdoEntry = (PIDLE_POWER_FDO_LIST_ENTRY)IdlePowerFDOList.Flink;
+ while ((PLIST_ENTRY)fdoEntry != &IdlePowerFDOList) {
+
+ ULONG isRemoved = ClassAcquireRemoveLock(fdoEntry->Fdo, removeLockTag);
+
+ if (!isRemoved) {
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)fdoEntry->Fdo->DeviceExtension;
+
+ //
+ // Apply the new timeout if the user hasn't overridden it via the registry.
+ //
+ if (!fdoExtension->FunctionSupportInfo->IdlePower.D3IdleTimeoutOverridden) {
+ fdoExtension->FunctionSupportInfo->IdlePower.D3IdleTimeout = DiskIdleTimeoutInMS;
+ ClasspSendEnableIdlePowerIoctl(fdoEntry->Fdo);
+ }
+ }
+
+ ClassReleaseRemoveLock(fdoEntry->Fdo, removeLockTag);
+
+ fdoEntry = (PIDLE_POWER_FDO_LIST_ENTRY)fdoEntry->ListEntry.Flink;
+ }
+ KeReleaseGuardedMutex(&IdlePowerFDOListMutex);
+
+ } else if (IsEqualGUID(SettingGuid, &GUID_CONSOLE_DISPLAY_STATE)) {
+
+ //
+ // If monitor is off, change media change requests to not
+ // keep device active. This allows removable media devices to
+ // go to sleep if there are no other active requests. Otherwise,
+ // let media change requests keep the device active.
+ //
+ if ((ValueLength == sizeof(ULONG)) && (Value != NULL)) {
+ if (*((PULONG)Value) == PowerMonitorOff) {
+ ClasspScreenOff = TRUE;
+ } else {
+ ClasspScreenOff = FALSE;
+ }
+
+ KeAcquireGuardedMutex(&IdlePowerFDOListMutex);
+ fdoEntry = (PIDLE_POWER_FDO_LIST_ENTRY)IdlePowerFDOList.Flink;
+ while ((PLIST_ENTRY)fdoEntry != &IdlePowerFDOList) {
+
+ ULONG isRemoved = ClassAcquireRemoveLock(fdoEntry->Fdo, removeLockTag);
+ if (!isRemoved) {
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)fdoEntry->Fdo->DeviceExtension;
+
+ if (ClasspScreenOff == FALSE) {
+ //
+ // Now that the screen is on, we may need to check for media
+ // for devices that are not in D0 and may have removable media.
+ // This is because the media change polling has been disabled
+ // for devices in D3 and now that the screen is on the user may
+ // have inserted some media that they want to interact with.
+ //
+ if ((fdoExtension->DevicePowerState != PowerDeviceD0) &&
+ (fdoExtension->MediaChangeDetectionInfo != NULL) &&
+ (fdoExtension->FunctionSupportInfo->AsynchronousNotificationSupported == FALSE)) {
+ ClassCheckMediaState(fdoExtension);
+ }
+
+ //
+ // We disabled failure prediction polling during screen-off
+ // so now check to see if we missed a failure prediction
+ // period and if so, force the IOCTL to be sent now.
+ //
+ if ((fdoExtension->FailurePredictionInfo != NULL) &&
+ (fdoExtension->FailurePredictionInfo->Method != FailurePredictionNone)) {
+ if (ClasspFailurePredictionPeriodMissed(fdoExtension)) {
+ fdoExtension->FailurePredictionInfo->CountDown = 1;
+ }
+ }
+ }
+
+#if (NTDDI_VERSION >= NTDDI_WINBLUE)
+ //
+ // Screen state has changed so attempt to update the tick
+ // timer's no-wake tolerance accordingly.
+ //
+ ClasspUpdateTimerNoWakeTolerance(fdoExtension);
+#endif
+ }
+ ClassReleaseRemoveLock(fdoEntry->Fdo, removeLockTag);
+
+ fdoEntry = (PIDLE_POWER_FDO_LIST_ENTRY)fdoEntry->ListEntry.Flink;
+ }
+ KeReleaseGuardedMutex(&IdlePowerFDOListMutex);
+ }
+
+ }
+
+ return STATUS_SUCCESS;
+}
+
+
+_IRQL_requires_same_
+NTSTATUS
+ClasspEnableIdlePower(
+ _In_ PDEVICE_OBJECT DeviceObject
+ )
+/*++
+Description:
+
+ This function is used to enable idle (runtime) power management for the
+ device. It will do the work to determine D3Cold support, idle timeout,
+ etc. and then notify the port driver that it wants to enable idle power
+ management.
+
+ This function may modify some of the idle power fields in the FDO's device
+ extension.
+
+Arguments:
+
+ DeviceObject - The class FDO.
+
+Return Value:
+
+ An NTSTATUS code indicating the status of the operation.
+
+--*/
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ ULONG d3ColdDisabledByUser = FALSE;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+ ULONG idleTimeoutOverrideInSeconds = 0;
+
+ //
+ // This function should only be called once.
+ //
+ NT_ASSERT(fdoExtension->FunctionSupportInfo->IdlePower.IdlePowerEnabled == FALSE);
+
+ ClassGetDeviceParameter(fdoExtension,
+ CLASSP_REG_SUBKEY_NAME,
+ CLASSP_REG_DISABLE_D3COLD,
+ &d3ColdDisabledByUser);
+
+ //
+ // If the device is hot-pluggable or the user has explicitly
+ // disabled D3Cold, do not enable D3Cold for this device.
+ //
+ if (d3ColdDisabledByUser || fdoExtension->PrivateFdoData->HotplugInfo.DeviceHotplug) {
+ fdoExtension->FunctionSupportInfo->IdlePower.D3ColdSupported = 0;
+ }
+
+ ClassGetDeviceParameter(fdoExtension,
+ CLASSP_REG_SUBKEY_NAME,
+ CLASSP_REG_IDLE_TIMEOUT_IN_SECONDS,
+ &idleTimeoutOverrideInSeconds);
+
+ //
+ // Set the idle timeout. If the user has not specified an override value,
+ // this will either be a default value or will have been updated by the
+ // power setting notification callback.
+ //
+ if (idleTimeoutOverrideInSeconds != 0) {
+ fdoExtension->FunctionSupportInfo->IdlePower.D3IdleTimeout = (idleTimeoutOverrideInSeconds * 1000);
+ fdoExtension->FunctionSupportInfo->IdlePower.D3IdleTimeoutOverridden = TRUE;
+ } else {
+ fdoExtension->FunctionSupportInfo->IdlePower.D3IdleTimeout = DiskIdleTimeoutInMS;
+ }
+
+ //
+ // We don't allow disks to be wakeable.
+ //
+ fdoExtension->FunctionSupportInfo->IdlePower.DeviceWakeable = FALSE;
+
+ //
+ // Send IOCTL_STORAGE_ENABLE_IDLE_POWER to the port driver to enable idle
+ // power management by the port driver.
+ //
+ status = ClasspSendEnableIdlePowerIoctl(DeviceObject);
+
+ if (NT_SUCCESS(status)) {
+ PIDLE_POWER_FDO_LIST_ENTRY fdoEntry = NULL;
+
+ //
+ // Put this FDO on the list of devices that are idle power managed.
+ //
+ fdoEntry = ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(IDLE_POWER_FDO_LIST_ENTRY), CLASS_TAG_POWER);
+ if (fdoEntry) {
+
+ fdoExtension->FunctionSupportInfo->IdlePower.IdlePowerEnabled = TRUE;
+
+ fdoEntry->Fdo = DeviceObject;
+
+ KeAcquireGuardedMutex(&IdlePowerFDOListMutex);
+ InsertHeadList(&IdlePowerFDOList, &(fdoEntry->ListEntry));
+ KeReleaseGuardedMutex(&IdlePowerFDOListMutex);
+
+ //
+ // If not registered already, register for disk idle timeout power
+ // setting notifications. The power manager will call our power
+ // setting callback very soon to set the idle timeout to the actual
+ // value.
+ //
+ if (PowerSettingNotificationHandle == NULL) {
+ PoRegisterPowerSettingCallback(DeviceObject,
+ &GUID_DISK_IDLE_TIMEOUT,
+ &ClasspPowerSettingCallback,
+ NULL,
+ &(PowerSettingNotificationHandle));
+ }
+ } else {
+ fdoExtension->FunctionSupportInfo->IdlePower.IdlePowerEnabled = FALSE;
+ status = STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ return status;
+}
+
diff --git a/storage/class/classpnp/src/retry.c b/storage/class/classpnp/src/retry.c
new file mode 100644
index 00000000..90d70ce3
--- /dev/null
+++ b/storage/class/classpnp/src/retry.c
@@ -0,0 +1,758 @@
+/*++
+
+Copyright (C) Microsoft Corporation, 1991 - 2010
+
+Module Name:
+
+ retry.c
+
+Abstract:
+
+ Packet retry routines for CLASSPNP
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+
+Revision History:
+
+--*/
+
+#include "classp.h"
+#include "debug.h"
+
+#ifdef DEBUG_USE_WPP
+#include "retry.tmh"
+#endif
+
+
+/*
+ * InterpretTransferPacketError
+ *
+ * Interpret the SRB error into a meaningful IRP status.
+ * ClassInterpretSenseInfo also may modify the SRB for the retry.
+ *
+ * Return TRUE iff packet should be retried.
+ */
+BOOLEAN InterpretTransferPacketError(PTRANSFER_PACKET Pkt)
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Pkt->Fdo->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
+ ULONG timesAlreadyRetried;
+ BOOLEAN shouldRetry = FALSE;
+ PCDB pCdb = ClasspTransferPacketGetCdb(Pkt);
+
+ /*
+ * Interpret the error using the returned sense info first.
+ */
+ Pkt->RetryIn100nsUnits = 0;
+
+
+ /*
+ * Pre-calculate the number of times the IO has already been
+ * retried, so that all InterpretSenseInfo routines get the right value.
+ */
+ if (ClasspTransferPacketGetNumberOfRetriesDone(Pkt, pCdb, &timesAlreadyRetried) == FALSE)
+ {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "Unhandled SRB Function %xh in error path for packet %p (did miniport change Srb.Cdb.OperationCode ?)", (ULONG)pCdb->CDB10.OperationCode, Pkt));
+ }
+
+ if (fdoData->InterpretSenseInfo != NULL) {
+
+ SCSI_REQUEST_BLOCK tempSrb = { 0 };
+ PSCSI_REQUEST_BLOCK srbPtr = (PSCSI_REQUEST_BLOCK)Pkt->Srb;
+
+ // SAL annotation and ClassInitializeEx() both validate this
+ NT_ASSERT(fdoData->InterpretSenseInfo->Interpret != NULL);
+
+ //
+ // If class driver does not support extended SRB and this is
+ // an extended SRB, convert to legacy SRB and pass to class
+ // driver.
+ //
+ if ((Pkt->Srb->Function == SRB_FUNCTION_STORAGE_REQUEST_BLOCK) &&
+ ((fdoExtension->CommonExtension.DriverExtension->SrbSupport &
+ CLASS_SRB_STORAGE_REQUEST_BLOCK) == 0)) {
+ ClasspConvertToScsiRequestBlock(&tempSrb, (PSTORAGE_REQUEST_BLOCK)Pkt->Srb);
+ srbPtr = &tempSrb;
+ }
+
+ shouldRetry = fdoData->InterpretSenseInfo->Interpret(Pkt->Fdo,
+ Pkt->OriginalIrp,
+ srbPtr,
+ IRP_MJ_SCSI,
+ 0,
+ timesAlreadyRetried,
+ Pkt->RetryHistory,
+ &Pkt->Irp->IoStatus.Status,
+ &Pkt->RetryIn100nsUnits);
+
+
+ } else {
+
+ //
+ // In this case, fdoData->InterpretSenseInfo == NULL so we must do our
+ // own error code and sense info processing.
+ //
+
+ PVOID senseInfoBuffer = ClasspTransferPacketGetSenseInfoBuffer(Pkt);
+ UCHAR senseInfoBufferLength = ClasspTransferPacketGetSenseInfoBufferLength(Pkt);
+ BOOLEAN validSense = FALSE;
+ UCHAR senseKey = 0;
+ UCHAR additionalSenseCode = 0;
+ UCHAR additionalSenseQual = 0;
+
+ NT_ASSERT(senseInfoBuffer);
+
+ validSense = ScsiGetSenseKeyAndCodes(senseInfoBuffer,
+ senseInfoBufferLength,
+ SCSI_SENSE_OPTIONS_FIXED_FORMAT_IF_UNKNOWN_FORMAT_INDICATED,
+ &senseKey,
+ &additionalSenseCode,
+ &additionalSenseQual);
+
+ if (pCdb->MEDIA_REMOVAL.OperationCode == SCSIOP_MEDIUM_REMOVAL) {
+
+ ULONG retryIntervalSeconds = 0;
+ /*
+ * This is an Ejection Control SRB. Interpret its sense info specially.
+ */
+ shouldRetry = ClassInterpretSenseInfo(
+ Pkt->Fdo,
+ (PSCSI_REQUEST_BLOCK)Pkt->Srb,
+ IRP_MJ_SCSI,
+ 0,
+ timesAlreadyRetried,
+ &Pkt->Irp->IoStatus.Status,
+ &retryIntervalSeconds);
+
+ if (shouldRetry) {
+ /*
+ * If the device is not ready, wait at least 2 seconds before retrying.
+ */
+ BOOLEAN setRetryIntervalSeconds = FALSE;
+
+ if (validSense) {
+
+ if ((Pkt->Irp->IoStatus.Status == STATUS_DEVICE_NOT_READY) &&
+ (additionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY)) {
+ setRetryIntervalSeconds = TRUE;
+ }
+ }
+
+ if (!setRetryIntervalSeconds && (SRB_STATUS(Pkt->Srb->SrbStatus) == SRB_STATUS_SELECTION_TIMEOUT)) {
+ setRetryIntervalSeconds = TRUE;
+ }
+
+ if (setRetryIntervalSeconds) {
+ retryIntervalSeconds = MAX(retryIntervalSeconds, 2);
+ }
+ }
+
+ if (shouldRetry)
+ {
+ Pkt->RetryIn100nsUnits = retryIntervalSeconds;
+ Pkt->RetryIn100nsUnits *= 1000 * 1000 * 10;
+ }
+
+ }
+ else if ((pCdb->MODE_SENSE.OperationCode == SCSIOP_MODE_SENSE) ||
+ (pCdb->MODE_SENSE.OperationCode == SCSIOP_MODE_SENSE10)) {
+
+ ULONG retryIntervalSeconds = 0;
+ /*
+ * This is an Mode Sense SRB. Interpret its sense info specially.
+ */
+ shouldRetry = ClassInterpretSenseInfo(
+ Pkt->Fdo,
+ (PSCSI_REQUEST_BLOCK)Pkt->Srb,
+ IRP_MJ_SCSI,
+ 0,
+ timesAlreadyRetried,
+ &Pkt->Irp->IoStatus.Status,
+ &retryIntervalSeconds);
+ if (shouldRetry) {
+ /*
+ * If the device is not ready, wait at least 2 seconds before retrying.
+ */
+ BOOLEAN setRetryIntervalSeconds = FALSE;
+
+ if (validSense) {
+ if ((Pkt->Irp->IoStatus.Status == STATUS_DEVICE_NOT_READY) &&
+ (additionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY)) {
+ setRetryIntervalSeconds = TRUE;
+ }
+ }
+
+ if (!setRetryIntervalSeconds && (SRB_STATUS(Pkt->Srb->SrbStatus) == SRB_STATUS_SELECTION_TIMEOUT)) {
+ setRetryIntervalSeconds = TRUE;
+ }
+
+ if (setRetryIntervalSeconds) {
+ retryIntervalSeconds = MAX(retryIntervalSeconds, 2);
+ }
+ }
+
+ /*
+ * Some special cases for mode sense.
+ */
+ if (Pkt->Irp->IoStatus.Status == STATUS_VERIFY_REQUIRED) {
+ shouldRetry = TRUE;
+ }
+ else if (SRB_STATUS(Pkt->Srb->SrbStatus) == SRB_STATUS_DATA_OVERRUN) {
+ /*
+ * This is a HACK.
+ * Atapi returns SRB_STATUS_DATA_OVERRUN when it really means
+ * underrun (i.e. success, and the buffer is longer than needed).
+ * So treat this as a success.
+ * When the caller of this function sees that the status was changed to success,
+ * it will add the transferred length to the original irp.
+ */
+ Pkt->Irp->IoStatus.Status = STATUS_SUCCESS;
+ shouldRetry = FALSE;
+ }
+
+ if (shouldRetry)
+ {
+ Pkt->RetryIn100nsUnits = retryIntervalSeconds;
+ Pkt->RetryIn100nsUnits *= 1000 * 1000 * 10;
+ }
+
+ }
+ else if ((pCdb->CDB10.OperationCode == SCSIOP_READ_CAPACITY) ||
+ (pCdb->CDB16.OperationCode == SCSIOP_READ_CAPACITY16)) {
+
+ ULONG retryIntervalSeconds = 0;
+
+ /*
+ * This is a Drive Capacity SRB. Interpret its sense info specially.
+ */
+ shouldRetry = ClassInterpretSenseInfo(
+ Pkt->Fdo,
+ (PSCSI_REQUEST_BLOCK)Pkt->Srb,
+ IRP_MJ_SCSI,
+ 0,
+ timesAlreadyRetried,
+ &Pkt->Irp->IoStatus.Status,
+ &retryIntervalSeconds);
+ if (Pkt->Irp->IoStatus.Status == STATUS_VERIFY_REQUIRED) {
+ shouldRetry = TRUE;
+ }
+
+ if (shouldRetry)
+ {
+ Pkt->RetryIn100nsUnits = retryIntervalSeconds;
+ Pkt->RetryIn100nsUnits *= 1000 * 1000 * 10;
+ }
+
+ }
+ else if (IS_SCSIOP_READWRITE(pCdb->CDB10.OperationCode)) {
+
+ ULONG retryIntervalSeconds = 0;
+ /*
+ * This is a Read/Write Data packet.
+ */
+ PIO_STACK_LOCATION origCurrentSp = IoGetCurrentIrpStackLocation(Pkt->OriginalIrp);
+
+ shouldRetry = ClassInterpretSenseInfo(Pkt->Fdo,
+ (PSCSI_REQUEST_BLOCK)Pkt->Srb,
+ origCurrentSp->MajorFunction,
+ 0,
+ timesAlreadyRetried,
+ &Pkt->Irp->IoStatus.Status,
+ &retryIntervalSeconds);
+
+ /*
+ * Deal with some special cases.
+ */
+ if (Pkt->Irp->IoStatus.Status == STATUS_INSUFFICIENT_RESOURCES) {
+ /*
+ * We are in extreme low-memory stress.
+ * We will retry in smaller chunks.
+ */
+ shouldRetry = TRUE;
+ }
+ else if (TEST_FLAG(origCurrentSp->Flags, SL_OVERRIDE_VERIFY_VOLUME) &&
+ (Pkt->Irp->IoStatus.Status == STATUS_VERIFY_REQUIRED)) {
+ /*
+ * We are still verifying a (possibly) reloaded disk/cdrom.
+ * So retry the request.
+ */
+ Pkt->Irp->IoStatus.Status = STATUS_IO_DEVICE_ERROR;
+ shouldRetry = TRUE;
+
+ }
+#if (NTDDI_VERSION >= NTDDI_WINBLUE)
+ else if (SrbGetSrbStatus(Pkt->Srb) == SRB_STATUS_BUS_RESET ||
+ SrbGetSrbStatus(Pkt->Srb) == SRB_STATUS_TIMEOUT ||
+ SrbGetSrbStatus(Pkt->Srb) == SRB_STATUS_COMMAND_TIMEOUT ||
+ SrbGetSrbStatus(Pkt->Srb) == SRB_STATUS_ABORTED) {
+
+ Pkt->TimedOut = TRUE;
+
+ if (shouldRetry) {
+ //
+ // For requests that have timed-out we may only perform a limited
+ // number of retries. This is typically less than the general
+ // number of retries allowed.
+ //
+ if (Pkt->NumIoTimeoutRetries == 0) {
+ shouldRetry = FALSE;
+ } else {
+ Pkt->NumIoTimeoutRetries--;
+ //
+ // We expect to be able to retry if there are some general retries remaining.
+ //
+ NT_ASSERT(Pkt->NumRetries > 0);
+ }
+ }
+ }
+#endif
+
+ if (shouldRetry)
+ {
+ Pkt->RetryIn100nsUnits = retryIntervalSeconds;
+ Pkt->RetryIn100nsUnits *= 1000 * 1000 * 10;
+ }
+
+ } else if (ClasspIsOffloadDataTransferCommand(pCdb)) {
+
+ ULONG retryIntervalSeconds = 0;
+
+ Pkt->TransferCount = 0;
+
+ shouldRetry = ClassInterpretSenseInfo(
+ Pkt->Fdo,
+ (PSCSI_REQUEST_BLOCK)Pkt->Srb,
+ IRP_MJ_SCSI,
+ 0,
+ timesAlreadyRetried,
+ &Pkt->Irp->IoStatus.Status,
+ &retryIntervalSeconds);
+
+ if (shouldRetry) {
+
+ Pkt->RetryIn100nsUnits = retryIntervalSeconds;
+ Pkt->RetryIn100nsUnits *= 1000 * 1000 * 10;
+
+ } else {
+
+ if (ClasspIsTokenOperation(pCdb)) {
+
+ BOOLEAN isInformationValid = FALSE;
+ ULONGLONG information = 0;
+
+ if (validSense) {
+
+ //
+ // If this is a data underrun condition (i.e. target truncated the offload data transfer),
+ // the SenseData's Information field may have the TransferCount.
+ //
+ if ((senseKey == SCSI_SENSE_COPY_ABORTED || senseKey == SCSI_SENSE_ABORTED_COMMAND) &&
+ (additionalSenseCode == SCSI_ADSENSE_COPY_TARGET_DEVICE_ERROR && additionalSenseQual == SCSI_SENSEQ_DATA_UNDERRUN)) {
+
+ //
+ // Sense data in Descriptor format
+ //
+ if (IsDescriptorSenseDataFormat(senseInfoBuffer)) {
+
+ PVOID startBuffer = NULL;
+ UCHAR startBufferLength = 0;
+
+
+ if (ScsiGetSenseDescriptor(senseInfoBuffer,
+ SrbGetSenseInfoBufferLength(Pkt->Srb),
+ &startBuffer,
+ &startBufferLength)) {
+ UCHAR outType;
+ PVOID outBuffer = NULL;
+ UCHAR outBufferLength = 0;
+
+ UCHAR typeList[1] = { SCSI_SENSE_DESCRIPTOR_TYPE_INFORMATION };
+
+ if (ScsiGetNextSenseDescriptorByType(startBuffer,
+ startBufferLength,
+ typeList,
+ ARRAYSIZE(typeList),
+ &outType,
+ &outBuffer,
+ &outBufferLength)) {
+
+ if (outType == SCSI_SENSE_DESCRIPTOR_TYPE_INFORMATION) {
+
+ if (ScsiValidateInformationSenseDescriptor(outBuffer, outBufferLength)) {
+ REVERSE_BYTES_QUAD(&information, &(((PSCSI_SENSE_DESCRIPTOR_INFORMATION)outBuffer)->Information));
+ isInformationValid = TRUE;
+ }
+
+ } else {
+
+ //
+ // ScsiGetNextDescriptorByType should only return a type that is specified by us.
+ //
+ NT_ASSERT(FALSE);
+ }
+ }
+ }
+ } else {
+
+ //
+ // Sense data in Fixed format
+ //
+ REVERSE_BYTES(&information, &(((PFIXED_SENSE_DATA)senseInfoBuffer)->Information));
+ isInformationValid = TRUE;
+ }
+
+ if (isInformationValid) {
+ Pkt->TransferCount = information;
+ }
+ }
+ }
+ }
+ }
+
+ }
+ else {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "Unhandled SRB Function %xh in error path for packet %p (did miniport change Srb.Cdb.OperationCode ?)", (ULONG)pCdb->CDB10.OperationCode, Pkt));
+ }
+ }
+
+ return shouldRetry;
+}
+
+
+/*
+ * RetryTransferPacket
+ *
+ * Retry sending a TRANSFER_PACKET.
+ *
+ * Return TRUE iff the packet is complete.
+ * (if so the status in pkt->irp is the final status).
+ */
+BOOLEAN RetryTransferPacket(PTRANSFER_PACKET Pkt)
+{
+ BOOLEAN packetDone;
+ BOOLEAN scaleDown = FALSE;
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "retrying failed transfer (pkt=%ph, op=%s)", Pkt, DBGGETSCSIOPSTR(Pkt->Srb)));
+
+ NT_ASSERT(Pkt->NumRetries > 0 || Pkt->RetryHistory);
+ Pkt->NumRetries--;
+
+ //
+ // If this is the last retry, then turn off disconnect, sync transfer,
+ // and tagged queuing. On all other retries, leave the original settings.
+ //
+ if (Pkt->NumRetries == 0) {
+ scaleDown = TRUE;
+ }
+
+#if (NTDDI_VERSION >= NTDDI_WINBLUE)
+ //
+ // If this request previously timed-out and there are no more retries left
+ // for timed-out requests then we should also apply the scale down.
+ //
+ if (Pkt->TimedOut &&
+ Pkt->NumIoTimeoutRetries == 0) {
+ scaleDown = TRUE;
+ }
+#endif
+
+ if (scaleDown) {
+ /*
+ * Tone down performance on the retry.
+ * This increases the chance for success on the retry.
+ * We've seen instances of drives that fail consistently but then start working
+ * once this scale-down is applied.
+ */
+ SrbSetSrbFlags(Pkt->Srb, SRB_FLAGS_DISABLE_DISCONNECT);
+ SrbSetSrbFlags(Pkt->Srb, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
+ SrbClearSrbFlags(Pkt->Srb, SRB_FLAGS_QUEUE_ACTION_ENABLE);
+ SrbSetRequestTag(Pkt->Srb, SP_UNTAGGED);
+ }
+
+ if (Pkt->Irp->IoStatus.Status == STATUS_INSUFFICIENT_RESOURCES) {
+
+ PCDB pCdb = SrbGetCdb(Pkt->Srb);
+ UCHAR cdbOpcode = 0;
+ BOOLEAN isReadWrite = FALSE;
+
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Pkt->Fdo->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
+
+ if (pCdb) {
+ cdbOpcode = pCdb->CDB10.OperationCode;
+ isReadWrite = IS_SCSIOP_READWRITE(cdbOpcode);
+ }
+
+ if ((Pkt->DriverUsesStartIO) &&
+ ( (cdbOpcode == SCSIOP_WRITE6 ) ||
+ (cdbOpcode == SCSIOP_WRITE ) ||
+ (cdbOpcode == SCSIOP_WRITE12) ||
+ (cdbOpcode == SCSIOP_WRITE16) )) {
+
+ /* don't retry writes in super-low-memory conditions if the
+ * driver must serialize against StartIO. This is because
+ * some write methods used in such drivers cannot accept
+ * random-sized writes. (i.e CD-RW in packet writing mode)
+ * Reads, however, are always safe to split up.
+ */
+ SET_FLAG(fdoData->TrackingFlags, TRACKING_FORWARD_PROGRESS_PATH1);
+ packetDone = TRUE;
+ }
+ else if (Pkt->InLowMemRetry || !isReadWrite){
+ /*
+ * This should never happen under normal circumstances.
+ * The memory manager guarantees that at least four pages will
+ * be available to allow forward progress in the port driver.
+ * So a one-page transfer should never fail with insufficient resources.
+ *
+ * However, it is possible to get in here with virtual storage
+ * or thin provisioned storage for example.
+ * A single sector write can trigger an allocation request and
+ * presently a forward progress guarantee is not provided.
+ * VHD also may have some limitations in forward progress guarantee.
+ * And USB too might also fall into this category.
+ */
+ SET_FLAG(fdoData->TrackingFlags, TRACKING_FORWARD_PROGRESS_PATH2);
+ packetDone = TRUE;
+ }
+ else {
+ /*
+ * We are in low-memory stress.
+ * Start the low-memory retry state machine, which tries to
+ * resend the packet in little one-page chunks.
+ */
+ SET_FLAG(fdoData->TrackingFlags, TRACKING_FORWARD_PROGRESS_PATH3);
+ InitLowMemRetry(Pkt,
+ Pkt->BufPtrCopy,
+ Pkt->BufLenCopy,
+ Pkt->TargetLocationCopy);
+ StepLowMemRetry(Pkt);
+ packetDone = FALSE;
+ }
+ }
+ else {
+ /*
+ * Retry the packet by simply resending it after a delay.
+ * Put the packet back in the pending queue and
+ * schedule a timer to retry the transfer.
+ *
+ * Do not call SetupReadWriteTransferPacket again because:
+ * (1) The minidriver may have set some bits
+ * in the SRB that it needs again and
+ * (2) doing so would reset numRetries.
+ *
+ * BECAUSE we do not call SetupReadWriteTransferPacket again,
+ * we have to reset a couple fields in the SRB that
+ * some miniports overwrite when they fail an SRB.
+ */
+
+ SrbSetDataBuffer(Pkt->Srb, Pkt->BufPtrCopy);
+ SrbSetDataTransferLength(Pkt->Srb, Pkt->BufLenCopy);
+
+ TransferPacketQueueRetryDpc(Pkt);
+
+ packetDone = FALSE;
+ }
+
+ return packetDone;
+}
+
+
+VOID TransferPacketQueueRetryDpc(PTRANSFER_PACKET Pkt)
+{
+ KeInitializeDpc(&Pkt->RetryTimerDPC, TransferPacketRetryTimerDpc, Pkt);
+
+ if (Pkt->RetryIn100nsUnits == 0){
+ KeInsertQueueDpc(&Pkt->RetryTimerDPC, NULL, NULL);
+ }
+ else {
+ LARGE_INTEGER timerPeriod;
+
+ NT_ASSERT(Pkt->RetryIn100nsUnits < 100 * 1000 * 1000 * 10); // sanity check -- 100 seconds is normally too long
+ timerPeriod.QuadPart = -(Pkt->RetryIn100nsUnits);
+ KeInitializeTimer(&Pkt->RetryTimer);
+ KeSetTimer(&Pkt->RetryTimer, timerPeriod, &Pkt->RetryTimerDPC);
+ }
+}
+
+
+VOID TransferPacketRetryTimerDpc( IN PKDPC Dpc,
+ IN PVOID DeferredContext,
+ IN PVOID SystemArgument1,
+ IN PVOID SystemArgument2)
+{
+ PTRANSFER_PACKET pkt;
+ PDEVICE_OBJECT fdo;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
+
+ _Analysis_assume_(DeferredContext != NULL);
+
+ pkt = (PTRANSFER_PACKET)DeferredContext;
+
+ fdo = pkt->Fdo;
+ fdoExtension = fdo->DeviceExtension;
+
+ UNREFERENCED_PARAMETER(Dpc);
+ UNREFERENCED_PARAMETER(SystemArgument1);
+ UNREFERENCED_PARAMETER(SystemArgument2);
+
+
+ /*
+ * Sometimes the port driver can allocates a new 'sense' buffer
+ * to report transfer errors, e.g. when the default sense buffer
+ * is too small. If so, it is up to us to free it.
+ * Now that we're done using the sense info, free it if appropriate.
+ * Then clear the sense buffer so it doesn't pollute future errors returned in this packet.
+ */
+ if (PORT_ALLOCATED_SENSE_EX(fdoExtension, pkt->Srb)) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_RW, "Freeing port-allocated sense buffer for pkt %ph.", pkt));
+ FREE_PORT_ALLOCATED_SENSE_BUFFER_EX(fdoExtension, pkt->Srb);
+ SrbSetSenseInfoBuffer(pkt->Srb, &pkt->SrbErrorSenseData);
+ SrbSetSenseInfoBufferLength(pkt->Srb, sizeof(pkt->SrbErrorSenseData));
+ }
+ else {
+ NT_ASSERT(SrbGetSenseInfoBuffer(pkt->Srb) == &pkt->SrbErrorSenseData);
+ NT_ASSERT(SrbGetSenseInfoBufferLength(pkt->Srb) <= sizeof(pkt->SrbErrorSenseData));
+ }
+
+ RtlZeroMemory(&pkt->SrbErrorSenseData, sizeof(pkt->SrbErrorSenseData));
+
+ SubmitTransferPacket(pkt);
+
+}
+
+
+VOID InitLowMemRetry(PTRANSFER_PACKET Pkt, PVOID BufPtr, ULONG Len, LARGE_INTEGER TargetLocation)
+{
+ NT_ASSERT(Len > 0);
+ NT_ASSERT(!Pkt->InLowMemRetry);
+
+ if (Pkt->DriverUsesStartIO)
+ {
+ /*
+ * special case: StartIO-based writing must stay serialized for performance
+ * and proper operations (i.e. sequential writing mode). If need more than
+ * one transfer to perform this operation, and it's a StartIO-based driver
+ * (such as CDROM), then just use a single packet and use the retry logic
+ * that's already built-in to the packet engine. Note that low-mem retry
+ * cannot be used directly because some write methods do not work if the
+ * writes are only PAGE_SIZE (i.e. packet writing may corrupt data).
+ */
+ Pkt->InLowMemRetry = FALSE;
+ }
+ else
+ {
+ Pkt->InLowMemRetry = TRUE;
+ }
+ Pkt->LowMemRetry_remainingBufPtr = BufPtr;
+ Pkt->LowMemRetry_remainingBufLen = Len;
+ Pkt->LowMemRetry_nextChunkTargetLocation = TargetLocation;
+}
+
+
+/*
+ * StepLowMemRetry
+ *
+ * During extreme low-memory stress, this function retries
+ * a packet in small one-page chunks, sent serially.
+ *
+ * Returns TRUE iff the packet is done.
+ */
+BOOLEAN StepLowMemRetry(PTRANSFER_PACKET Pkt)
+{
+ BOOLEAN packetDone;
+
+ if (Pkt->LowMemRetry_remainingBufLen == 0){
+ packetDone = TRUE;
+ }
+ else {
+ ULONG thisChunkLen;
+ if (Pkt->DriverUsesStartIO)
+ {
+ /*
+ * Need the fdoData for the HwMaxXferLen
+ */
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Pkt->Fdo->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
+
+ /*
+ * Need the adapterDesc to limit transfers based on byte count
+ */
+ PCOMMON_DEVICE_EXTENSION commonExtension = Pkt->Fdo->DeviceExtension;
+ PSTORAGE_ADAPTER_DESCRIPTOR adapterDesc = commonExtension->PartitionZeroExtension->AdapterDescriptor;
+
+ ULONG hwMaxXferLen;
+
+ /*
+ * special case: StartIO-based writing must stay serialized for performance
+ * and proper operations (i.e. sequential writing mode). If need more than
+ * one transfer to perform this operation, and it's a StartIO-based driver
+ * (such as CDROM), then just use a single packet and use the retry logic
+ * that's already built-in to the packet engine. Note that low-mem retry
+ * cannot be used directly because some write methods do not work if the
+ * writes are only PAGE_SIZE (i.e. packet writing may corrupt data).
+ */
+ NT_ASSERT(!Pkt->InLowMemRetry);
+
+ /*
+ * We precomputed fdoData->HwMaxXferLen using (MaximumPhysicalPages-1).
+ * If the buffer is page-aligned, that's one less page crossing so we can add the page back in.
+ * Note: adapters that return MaximumPhysicalPages=0x10 depend on this to
+ * transfer aligned 64K requests in one piece.
+ * Also note: make sure adding PAGE_SIZE back in doesn't wrap to zero.
+ */
+
+ if (((ULONG_PTR)(Pkt->LowMemRetry_remainingBufPtr) & (PAGE_SIZE-1)) || (fdoData->HwMaxXferLen > 0xffffffff-PAGE_SIZE)){
+ hwMaxXferLen = fdoData->HwMaxXferLen;
+ }
+ else {
+ NT_ASSERT((PAGE_SIZE%fdoExt->DiskGeometry.BytesPerSector) == 0);
+ hwMaxXferLen = min(fdoData->HwMaxXferLen+PAGE_SIZE, adapterDesc->MaximumTransferLength);
+ }
+ thisChunkLen = MIN(Pkt->LowMemRetry_remainingBufLen, hwMaxXferLen);
+ }
+ else {
+ /*
+ * Make sure the little chunk we send is <= a page length
+ * AND that it does not cross any page boundaries.
+ */
+ ULONG bytesToNextPageBoundary;
+ bytesToNextPageBoundary = PAGE_SIZE-(ULONG)((ULONG_PTR)Pkt->LowMemRetry_remainingBufPtr%PAGE_SIZE);
+ thisChunkLen = MIN(Pkt->LowMemRetry_remainingBufLen, bytesToNextPageBoundary);
+ NT_ASSERT(Pkt->InLowMemRetry);
+ }
+
+
+ /*
+ * Set up the transfer packet for the new little chunk.
+ * This will reset numRetries so that we retry each chunk as required.
+ */
+ SetupReadWriteTransferPacket(Pkt,
+ Pkt->LowMemRetry_remainingBufPtr,
+ thisChunkLen,
+ Pkt->LowMemRetry_nextChunkTargetLocation,
+ Pkt->OriginalIrp);
+
+ Pkt->LowMemRetry_remainingBufPtr += thisChunkLen;
+ Pkt->LowMemRetry_remainingBufLen -= thisChunkLen;
+ Pkt->LowMemRetry_nextChunkTargetLocation.QuadPart += thisChunkLen;
+
+ //
+ // When running in low-memory stress, always use a partial MDL.
+ // This allows lower drivers to potentially map a smaller buffer.
+ //
+ Pkt->UsePartialMdl = TRUE;
+
+ TransferPacketQueueRetryDpc(Pkt);
+
+ packetDone = FALSE;
+ }
+
+ return packetDone;
+}
+
diff --git a/storage/class/classpnp/src/srblib.c b/storage/class/classpnp/src/srblib.c
new file mode 100644
index 00000000..a888d390
--- /dev/null
+++ b/storage/class/classpnp/src/srblib.c
@@ -0,0 +1,374 @@
+/*++
+
+Copyright (C) Microsoft Corporation 2010
+
+Module Name:
+
+ srblib.c
+
+Abstract:
+
+ Header for SRB utility functions
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+
+Revision History:
+
+--*/
+
+
+#include <classp.h>
+
+PVOID
+DefaultStorageRequestBlockAllocateRoutine(
+ _In_ CLONG ByteSize
+ )
+/*++
+
+Routine Description:
+
+ Default allocation routine.
+
+Arguments:
+
+ ByteSize - SRB size in bytes.
+
+Return Value:
+
+ Pointer to the SRB buffer. NULL if SRB buffer could not be allocated.
+
+--*/
+{
+ return ExAllocatePoolWithTag(NonPagedPoolNx, ByteSize, '+brs');
+}
+
+
+NTSTATUS
+pInitializeStorageRequestBlock(
+ _Inout_bytecount_(ByteSize) PSTORAGE_REQUEST_BLOCK Srb,
+ _In_ USHORT AddressType,
+ _In_ ULONG ByteSize,
+ _In_ ULONG NumSrbExData,
+ _In_ va_list ap
+ )
+/*++
+
+Routine Description:
+
+ Initialize a STORAGE_REQUEST_BLOCK.
+
+Arguments:
+
+ Srb - Pointer to STORAGE_REQUEST_BLOCK to initialize.
+
+ AddressType - Storage address type.
+
+ ByteSize - STORAGE_REQUEST_BLOCK size in bytes.
+
+ NumSrbExData - Number of SRB extended data.
+
+ ap - Variable argument list matching the SRB extended data in the
+ STORAGE_REQUEST_BLOCK.
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PSTOR_ADDRESS address;
+ PSRBEX_DATA srbExData;
+ ULONG offset;
+ ULONG length = (ULONG)-1;
+ SRBEXDATATYPE type;
+ ULONG srbExDataLength = (ULONG)-1;
+ ULONG varLength;
+ ULONG i;
+
+ if (ByteSize < sizeof(STORAGE_REQUEST_BLOCK)) {
+ return STATUS_BUFFER_OVERFLOW;
+ }
+
+ RtlZeroMemory(Srb, ByteSize);
+
+ Srb->Length = FIELD_OFFSET(STORAGE_REQUEST_BLOCK, Signature);
+ Srb->Function = SRB_FUNCTION_STORAGE_REQUEST_BLOCK;
+ Srb->Signature = SRB_SIGNATURE;
+ Srb->Version = STORAGE_REQUEST_BLOCK_VERSION_1;
+ Srb->SrbLength = ByteSize;
+ Srb->NumSrbExData = NumSrbExData;
+
+ offset = sizeof(STORAGE_REQUEST_BLOCK);
+ if (NumSrbExData > 0) {
+ offset += ((NumSrbExData - 1) * sizeof(ULONG));
+
+ // Ensure offset is pointer type aligned
+ if (offset % sizeof(PVOID)) {
+ offset += (sizeof(PVOID) - (offset % sizeof(PVOID)));
+ }
+ }
+ Srb->AddressOffset = offset;
+
+ if (AddressType == STORAGE_ADDRESS_TYPE_BTL8)
+ {
+ if ((ByteSize < offset) ||
+ (ByteSize < (offset + sizeof(STOR_ADDR_BTL8)))) {
+ return STATUS_BUFFER_OVERFLOW;
+ }
+ address = (PSTOR_ADDRESS)((PUCHAR)Srb + offset);
+ address->Type = STOR_ADDRESS_TYPE_BTL8;
+ address->AddressLength = STOR_ADDR_BTL8_ADDRESS_LENGTH;
+ offset += sizeof(STOR_ADDR_BTL8);
+ } else
+ {
+ status = STATUS_INVALID_PARAMETER;
+ }
+
+ for (i = 0; i < NumSrbExData && status == STATUS_SUCCESS; i++)
+ {
+ if (ByteSize <= offset) {
+ status = STATUS_BUFFER_OVERFLOW;
+ break;
+ }
+ srbExData = (PSRBEX_DATA)((PUCHAR)Srb + offset);
+ Srb->SrbExDataOffset[i] = offset;
+
+ type = va_arg(ap, SRBEXDATATYPE);
+
+ switch (type)
+ {
+ case SrbExDataTypeBidirectional:
+ length = sizeof(SRBEX_DATA_BIDIRECTIONAL);
+ srbExDataLength = SRBEX_DATA_BIDIRECTIONAL_LENGTH;
+ break;
+ case SrbExDataTypeScsiCdb16:
+ length = sizeof(SRBEX_DATA_SCSI_CDB16);
+ srbExDataLength = SRBEX_DATA_SCSI_CDB16_LENGTH;
+ break;
+ case SrbExDataTypeScsiCdb32:
+ length = sizeof(SRBEX_DATA_SCSI_CDB32);
+ srbExDataLength = SRBEX_DATA_SCSI_CDB32_LENGTH;
+ break;
+ case SrbExDataTypeScsiCdbVar:
+ varLength = va_arg(ap, ULONG);
+ length = sizeof(SRBEX_DATA_SCSI_CDB_VAR) + varLength;
+ srbExDataLength = SRBEX_DATA_SCSI_CDB_VAR_LENGTH_MIN + varLength;
+ break;
+ case SrbExDataTypeWmi:
+ length = sizeof(SRBEX_DATA_WMI);
+ srbExDataLength = SRBEX_DATA_WMI_LENGTH;
+ break;
+ case SrbExDataTypePower:
+ length = sizeof(SRBEX_DATA_POWER);
+ srbExDataLength = SRBEX_DATA_POWER_LENGTH;
+ break;
+ case SrbExDataTypePnP:
+ length = sizeof(SRBEX_DATA_PNP);
+ srbExDataLength = SRBEX_DATA_PNP_LENGTH;
+ break;
+ case SrbExDataTypeIoInfo:
+ length = sizeof(SRBEX_DATA_IO_INFO);
+ srbExDataLength = SRBEX_DATA_IO_INFO_LENGTH;
+ break;
+ default:
+ status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ if (status == STATUS_SUCCESS)
+ {
+ NT_ASSERT(length != (ULONG)-1);
+
+ if (ByteSize < (offset + length)) {
+ status = STATUS_BUFFER_OVERFLOW;
+ break;
+ }
+
+ NT_ASSERT(srbExDataLength != (ULONG)-1);
+
+ srbExData->Type = type;
+ srbExData->Length = srbExDataLength;
+ offset += length;
+ }
+ }
+
+ return status;
+}
+
+
+NTSTATUS
+InitializeStorageRequestBlock(
+ _Inout_bytecount_(ByteSize) PSTORAGE_REQUEST_BLOCK Srb,
+ _In_ USHORT AddressType,
+ _In_ ULONG ByteSize,
+ _In_ ULONG NumSrbExData,
+ ...
+ )
+/*++
+
+Routine Description:
+
+ Initialize an extended SRB.
+
+Arguments:
+
+ Srb - Pointer to SRB buffer to initialize.
+
+ AddressType - Storage address type.
+
+ ByteSize - STORAGE_REQUEST_BLOCK size in bytes.
+
+ NumSrbExData - Number of SRB extended data.
+
+ ... - Variable argument list matching the SRB extended data in the
+ STORAGE_REQUEST_BLOCK.
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+{
+ NTSTATUS status;
+ va_list ap;
+ va_start(ap, NumSrbExData);
+ status = pInitializeStorageRequestBlock(Srb, AddressType, ByteSize, NumSrbExData, ap);
+ va_end(ap);
+ return status;
+}
+
+
+
+NTSTATUS
+CreateStorageRequestBlock(
+ _Inout_ PSTORAGE_REQUEST_BLOCK *Srb,
+ _In_ USHORT AddressType,
+ _In_opt_ PSRB_ALLOCATE_ROUTINE AllocateRoutine,
+ _Inout_opt_ ULONG *ByteSize,
+ _In_ ULONG NumSrbExData,
+ ...
+ )
+/*++
+
+Routine Description:
+
+ Create an extended SRB.
+
+Arguments:
+
+ Srb - Pointer to buffer to store SRB pointer.
+
+ AddressType - Storage address type.
+
+ AllocateRoutine - Buffer allocation function (optional).
+
+ ByteSize - Pointer to ULONG to store size of SRB in bytes (optional).
+
+ NumSrbExData - Number of SRB extended data.
+
+ ... - Variable argument list matching the SRB extended data in the
+ STORAGE_REQUEST_BLOCK.
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+{
+ ULONG sizeNeeded = 0;
+ va_list ap;
+ ULONG i;
+ NTSTATUS status = STATUS_SUCCESS;
+
+ // Ensure SrbExData offsets are pointer type aligned
+ sizeNeeded = sizeof(STORAGE_REQUEST_BLOCK);
+ if (NumSrbExData > 0) {
+ sizeNeeded += ((NumSrbExData - 1) * sizeof(ULONG));
+ if (sizeNeeded % sizeof(PVOID)) {
+ sizeNeeded += (sizeof(PVOID) - (sizeNeeded % sizeof(PVOID)));
+ }
+ }
+
+ if (AddressType == STORAGE_ADDRESS_TYPE_BTL8)
+ {
+ sizeNeeded += sizeof(STOR_ADDR_BTL8);
+ } else
+ {
+ status = STATUS_INVALID_PARAMETER;
+ }
+
+ va_start(ap, NumSrbExData);
+
+ for (i = 0; i < NumSrbExData && status == STATUS_SUCCESS; i++)
+ {
+ switch (va_arg(ap, SRBEXDATATYPE))
+ {
+ case SrbExDataTypeBidirectional:
+ sizeNeeded += sizeof(SRBEX_DATA_BIDIRECTIONAL);
+ break;
+ case SrbExDataTypeScsiCdb16:
+ sizeNeeded += sizeof(SRBEX_DATA_SCSI_CDB16);
+ break;
+ case SrbExDataTypeScsiCdb32:
+ sizeNeeded += sizeof(SRBEX_DATA_SCSI_CDB32);
+ break;
+ case SrbExDataTypeScsiCdbVar:
+ sizeNeeded += sizeof(SRBEX_DATA_SCSI_CDB_VAR) + va_arg(ap, ULONG);
+ break;
+ case SrbExDataTypeWmi:
+ sizeNeeded += sizeof(SRBEX_DATA_WMI);
+ break;
+ case SrbExDataTypePower:
+ sizeNeeded += sizeof(SRBEX_DATA_POWER);
+ break;
+ case SrbExDataTypePnP:
+ sizeNeeded += sizeof(SRBEX_DATA_PNP);
+ break;
+ case SrbExDataTypeIoInfo:
+ sizeNeeded += sizeof(SRBEX_DATA_IO_INFO);
+ break;
+ default:
+ status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+ }
+ va_end(ap);
+
+ if (status == STATUS_SUCCESS)
+ {
+ if (AllocateRoutine)
+ {
+ *Srb = AllocateRoutine(sizeNeeded);
+ if (*Srb == NULL)
+ {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+ }
+
+ if (ByteSize != NULL)
+ {
+ *ByteSize = sizeNeeded;
+ }
+
+ if (*Srb)
+ {
+ va_start(ap, NumSrbExData);
+ #pragma prefast(suppress:26015, "pInitializeStorageRequestBlock will set the SrbLength field")
+ status = pInitializeStorageRequestBlock(*Srb, AddressType, sizeNeeded, NumSrbExData, ap);
+ va_end(ap);
+ }
+
+ }
+
+ return status;
+}
+
+
+
+
diff --git a/storage/class/classpnp/src/utils.c b/storage/class/classpnp/src/utils.c
new file mode 100644
index 00000000..f8236c68
--- /dev/null
+++ b/storage/class/classpnp/src/utils.c
@@ -0,0 +1,8906 @@
+/*++
+
+Copyright (C) Microsoft Corporation, 1991 - 2010
+
+Module Name:
+
+ utils.c
+
+Abstract:
+
+ SCSI class driver routines
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+
+Revision History:
+
+--*/
+
+
+#include "classp.h"
+#include "debug.h"
+#include <ntiologc.h>
+
+
+#ifdef DEBUG_USE_WPP
+#include "utils.tmh"
+#endif
+
+//
+// Constant value used in firmware upgrade process.
+//
+#define FIRMWARE_ACTIVATE_TIMEOUT_VALUE 30
+
+
+#ifdef ALLOC_PRAGMA
+ #pragma alloc_text(PAGE, ClassGetDeviceParameter)
+ #pragma alloc_text(PAGE, ClassScanForSpecial)
+ #pragma alloc_text(PAGE, ClassSetDeviceParameter)
+ #pragma alloc_text(PAGE, ClasspMyStringMatches)
+ #pragma alloc_text(PAGE, ClasspDeviceCopyOffloadProperty)
+ #pragma alloc_text(PAGE, ClasspValidateOffloadSupported)
+ #pragma alloc_text(PAGE, ClasspValidateOffloadInputParameters)
+#endif
+
+// custom string match -- careful!
+BOOLEAN ClasspMyStringMatches(_In_opt_z_ PCHAR StringToMatch, _In_z_ PCHAR TargetString)
+{
+ ULONG length; // strlen returns an int, not size_t (!)
+ PAGED_CODE();
+ NT_ASSERT(TargetString);
+ // if no match requested, return TRUE
+ if (StringToMatch == NULL) {
+ return TRUE;
+ }
+ // cache the string length for efficiency
+ length = (ULONG)strlen(StringToMatch);
+ // ZERO-length strings may only match zero-length strings
+ if (length == 0) {
+ return (strlen(TargetString) == 0);
+ }
+ // strncmp returns zero if the strings match
+ return (strncmp(StringToMatch, TargetString, length) == 0);
+}
+
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID ClassGetDeviceParameter(
+ _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ _In_opt_ PWSTR SubkeyName,
+ _In_ PWSTR ParameterName,
+ _Inout_ PULONG ParameterValue // also default value
+ )
+{
+ NTSTATUS status;
+ RTL_QUERY_REGISTRY_TABLE queryTable[2] = {0};
+ HANDLE deviceParameterHandle = NULL;
+ HANDLE deviceSubkeyHandle = NULL;
+ ULONG defaultParameterValue;
+
+ PAGED_CODE();
+
+ //
+ // open the given parameter
+ //
+
+ status = IoOpenDeviceRegistryKey(FdoExtension->LowerPdo,
+ PLUGPLAY_REGKEY_DEVICE,
+ KEY_READ,
+ &deviceParameterHandle);
+
+ if (NT_SUCCESS(status) && (SubkeyName != NULL)) {
+
+ UNICODE_STRING subkeyName;
+ OBJECT_ATTRIBUTES objectAttributes = {0};
+
+ RtlInitUnicodeString(&subkeyName, SubkeyName);
+ InitializeObjectAttributes(&objectAttributes,
+ &subkeyName,
+ OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+ deviceParameterHandle,
+ NULL);
+
+ status = ZwOpenKey(&deviceSubkeyHandle,
+ KEY_READ,
+ &objectAttributes);
+ if (!NT_SUCCESS(status)) {
+ ZwClose(deviceParameterHandle);
+ }
+
+ }
+
+ if (NT_SUCCESS(status)) {
+
+ defaultParameterValue = *ParameterValue;
+
+ queryTable->Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
+ queryTable->Name = ParameterName;
+ queryTable->EntryContext = ParameterValue;
+ queryTable->DefaultType = REG_DWORD;
+ queryTable->DefaultData = NULL;
+ queryTable->DefaultLength = 0;
+
+ status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
+ (PWSTR)(SubkeyName ?
+ deviceSubkeyHandle :
+ deviceParameterHandle),
+ queryTable,
+ NULL,
+ NULL);
+ if (!NT_SUCCESS(status)) {
+ *ParameterValue = defaultParameterValue; // use default value
+ }
+
+ //
+ // close what we open
+ //
+
+ if (SubkeyName) {
+ ZwClose(deviceSubkeyHandle);
+ }
+
+ ZwClose(deviceParameterHandle);
+ }
+
+ if (!NT_SUCCESS(status)) {
+
+ //
+ // Windows 2000 SP3 uses the driver-specific key, so look in there
+ //
+
+ status = IoOpenDeviceRegistryKey(FdoExtension->LowerPdo,
+ PLUGPLAY_REGKEY_DRIVER,
+ KEY_READ,
+ &deviceParameterHandle);
+
+ if (NT_SUCCESS(status) && (SubkeyName != NULL)) {
+
+ UNICODE_STRING subkeyName;
+ OBJECT_ATTRIBUTES objectAttributes = {0};
+
+ RtlInitUnicodeString(&subkeyName, SubkeyName);
+ InitializeObjectAttributes(&objectAttributes,
+ &subkeyName,
+ OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+ deviceParameterHandle,
+ NULL);
+
+ status = ZwOpenKey(&deviceSubkeyHandle, KEY_READ, &objectAttributes);
+
+ if (!NT_SUCCESS(status)) {
+ ZwClose(deviceParameterHandle);
+ }
+ }
+
+ if (NT_SUCCESS(status)) {
+
+ defaultParameterValue = *ParameterValue;
+
+ queryTable->Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
+ queryTable->Name = ParameterName;
+ queryTable->EntryContext = ParameterValue;
+ queryTable->DefaultType = REG_DWORD;
+ queryTable->DefaultData = NULL;
+ queryTable->DefaultLength = 0;
+
+ status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
+ (PWSTR)(SubkeyName ?
+ deviceSubkeyHandle :
+ deviceParameterHandle),
+ queryTable,
+ NULL,
+ NULL);
+ if (NT_SUCCESS(status)) {
+
+ //
+ // Migrate the value over to the device-specific key
+ //
+
+ ClassSetDeviceParameter(FdoExtension, SubkeyName, ParameterName, *ParameterValue);
+
+ } else {
+
+ //
+ // Use the default value
+ //
+
+ *ParameterValue = defaultParameterValue;
+ }
+
+ if (SubkeyName) {
+ ZwClose(deviceSubkeyHandle);
+ }
+
+ ZwClose(deviceParameterHandle);
+ }
+ }
+
+ return;
+
+} // end ClassGetDeviceParameter()
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS ClassSetDeviceParameter(
+ _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ _In_opt_ PWSTR SubkeyName,
+ _In_ PWSTR ParameterName,
+ _In_ ULONG ParameterValue)
+{
+ NTSTATUS status;
+ HANDLE deviceParameterHandle = NULL;
+ HANDLE deviceSubkeyHandle = NULL;
+
+ PAGED_CODE();
+
+ //
+ // open the given parameter
+ //
+
+ status = IoOpenDeviceRegistryKey(FdoExtension->LowerPdo,
+ PLUGPLAY_REGKEY_DEVICE,
+ KEY_READ | KEY_WRITE,
+ &deviceParameterHandle);
+
+ if (NT_SUCCESS(status) && (SubkeyName != NULL)) {
+
+ UNICODE_STRING subkeyName;
+ OBJECT_ATTRIBUTES objectAttributes;
+
+ RtlInitUnicodeString(&subkeyName, SubkeyName);
+ InitializeObjectAttributes(&objectAttributes,
+ &subkeyName,
+ OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+ deviceParameterHandle,
+ NULL);
+
+ status = ZwCreateKey(&deviceSubkeyHandle,
+ KEY_READ | KEY_WRITE,
+ &objectAttributes,
+ 0, NULL, 0, NULL);
+ if (!NT_SUCCESS(status)) {
+ ZwClose(deviceParameterHandle);
+ }
+
+ }
+
+ if (NT_SUCCESS(status)) {
+
+ status = RtlWriteRegistryValue(
+ RTL_REGISTRY_HANDLE,
+ (PWSTR) (SubkeyName ?
+ deviceSubkeyHandle :
+ deviceParameterHandle),
+ ParameterName,
+ REG_DWORD,
+ &ParameterValue,
+ sizeof(ULONG));
+
+ //
+ // close what we open
+ //
+
+ if (SubkeyName) {
+ ZwClose(deviceSubkeyHandle);
+ }
+
+ ZwClose(deviceParameterHandle);
+ }
+
+ return status;
+
+} // end ClassSetDeviceParameter()
+
+
+/*
+ * ClassScanForSpecial
+ *
+ * This routine was written to simplify scanning for special
+ * hardware based upon id strings. it does not check the registry.
+ */
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+VOID ClassScanForSpecial(
+ _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ _In_ CLASSPNP_SCAN_FOR_SPECIAL_INFO DeviceList[],
+ _In_ PCLASS_SCAN_FOR_SPECIAL_HANDLER Function)
+{
+ PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor;
+ PUCHAR vendorId;
+ PUCHAR productId;
+ PUCHAR productRevision;
+ UCHAR nullString[] = "";
+
+ PAGED_CODE();
+ NT_ASSERT(DeviceList);
+ NT_ASSERT(Function);
+
+ deviceDescriptor = FdoExtension->DeviceDescriptor;
+
+ if (DeviceList == NULL) {
+ return;
+ }
+ if (Function == NULL) {
+ return;
+ }
+
+ //
+ // SCSI sets offsets to -1, ATAPI sets to 0. check for both.
+ //
+
+ if (deviceDescriptor->VendorIdOffset != 0 &&
+ deviceDescriptor->VendorIdOffset != -1) {
+ vendorId = ((PUCHAR)deviceDescriptor);
+ vendorId += deviceDescriptor->VendorIdOffset;
+ } else {
+ vendorId = nullString;
+ }
+ if (deviceDescriptor->ProductIdOffset != 0 &&
+ deviceDescriptor->ProductIdOffset != -1) {
+ productId = ((PUCHAR)deviceDescriptor);
+ productId += deviceDescriptor->ProductIdOffset;
+ } else {
+ productId = nullString;
+ }
+ if (deviceDescriptor->ProductRevisionOffset != 0 &&
+ deviceDescriptor->ProductRevisionOffset != -1) {
+ productRevision = ((PUCHAR)deviceDescriptor);
+ productRevision += deviceDescriptor->ProductRevisionOffset;
+ } else {
+ productRevision = nullString;
+ }
+
+ //
+ // loop while the device list is valid (not null-filled)
+ //
+
+ for (;(DeviceList->VendorId != NULL ||
+ DeviceList->ProductId != NULL ||
+ DeviceList->ProductRevision != NULL);DeviceList++) {
+
+ if (ClasspMyStringMatches(DeviceList->VendorId, (PCHAR)vendorId) &&
+ ClasspMyStringMatches(DeviceList->ProductId, (PCHAR)productId) &&
+ ClasspMyStringMatches(DeviceList->ProductRevision, (PCHAR)productRevision)
+ ) {
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT, "ClasspScanForSpecialByInquiry: Found matching "
+ "controller Ven: %s Prod: %s Rev: %s\n",
+ (PCSZ)vendorId, (PCSZ)productId, (PCSZ)productRevision));
+
+ //
+ // pass the context to the call back routine and exit
+ //
+
+ (Function)(FdoExtension, DeviceList->Data);
+
+ //
+ // for CHK builds, try to prevent wierd stacks by having a debug
+ // print here. it's a hack, but i know of no other way to prevent
+ // the stack from being wrong.
+ //
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT, "ClasspScanForSpecialByInquiry: "
+ "completed callback\n"));
+ return;
+
+ } // else the strings did not match
+
+ } // none of the devices matched.
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT, "ClasspScanForSpecialByInquiry: no match found for %p\n",
+ FdoExtension->DeviceObject));
+ return;
+
+} // end ClasspScanForSpecialByInquiry()
+
+
+//
+// In order to provide better performance without the need to reboot,
+// we need to implement a self-adjusting method to set and clear the
+// srb flags based upon current performance.
+//
+// whenever there is an error, immediately grab the spin lock. the
+// MP perf hit here is acceptable, since we're in an error path. this
+// is also neccessary because we are guaranteed to be modifying the
+// SRB flags here, setting SuccessfulIO to zero, and incrementing the
+// actual error count (which is always done within this spinlock).
+//
+// whenever there is no error, increment a counter. if there have been
+// errors on the device, and we've enabled dynamic perf, *and* we've
+// just crossed the perf threshhold, then grab the spin lock and
+// double check that the threshhold has, indeed been hit(*). then
+// decrement the error count, and if it's dropped sufficiently, undo
+// some of the safety changes made in the SRB flags due to the errors.
+//
+// * this works in all cases. even if lots of ios occur after the
+// previous guy went in and cleared the successfulio counter, that
+// just means that we've hit the threshhold again, and so it's proper
+// to run the inner loop again.
+//
+
+VOID
+ClasspPerfIncrementErrorCount(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ )
+{
+ PCLASS_PRIVATE_FDO_DATA fdoData = FdoExtension->PrivateFdoData;
+ KIRQL oldIrql;
+ ULONG errors;
+
+ KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
+
+ fdoData->Perf.SuccessfulIO = 0; // implicit interlock
+ errors = InterlockedIncrement((volatile LONG *)&FdoExtension->ErrorCount);
+
+ if (errors >= CLASS_ERROR_LEVEL_1) {
+
+ //
+ // If the error count has exceeded the error limit, then disable
+ // any tagged queuing, multiple requests per lu queueing
+ // and sychronous data transfers.
+ //
+ // Clearing the no queue freeze flag prevents the port driver
+ // from sending multiple requests per logical unit.
+ //
+
+ CLEAR_FLAG(FdoExtension->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
+ CLEAR_FLAG(FdoExtension->SrbFlags, SRB_FLAGS_QUEUE_ACTION_ENABLE);
+
+ SET_FLAG(FdoExtension->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClasspPerfIncrementErrorCount: "
+ "Too many errors; disabling tagged queuing and "
+ "synchronous data tranfers.\n"));
+
+ }
+
+ if (errors >= CLASS_ERROR_LEVEL_2) {
+
+ //
+ // If a second threshold is reached, disable disconnects.
+ //
+
+ SET_FLAG(FdoExtension->SrbFlags, SRB_FLAGS_DISABLE_DISCONNECT);
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClasspPerfIncrementErrorCount: "
+ "Too many errors; disabling disconnects.\n"));
+ }
+
+ KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
+ return;
+}
+
+VOID
+ClasspPerfIncrementSuccessfulIo(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ )
+{
+ PCLASS_PRIVATE_FDO_DATA fdoData = FdoExtension->PrivateFdoData;
+ KIRQL oldIrql;
+ ULONG errors;
+ ULONG succeeded = 0;
+
+ //
+ // don't take a hit from the interlocked op unless we're in
+ // a degraded state and we've got a threshold to hit.
+ //
+
+ if (FdoExtension->ErrorCount == 0) {
+ return;
+ }
+
+ if (fdoData->Perf.ReEnableThreshhold == 0) {
+ return;
+ }
+
+ succeeded = InterlockedIncrement((volatile LONG *)&fdoData->Perf.SuccessfulIO);
+ if (succeeded < fdoData->Perf.ReEnableThreshhold) {
+ return;
+ }
+
+ //
+ // if we hit the threshold, grab the spinlock and verify we've
+ // actually done so. this allows us to ignore the spinlock 99%
+ // of the time.
+ //
+
+ KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
+
+ //
+ // re-read the value, so we don't run this multiple times
+ // for a single threshhold being hit. this keeps errorcount
+ // somewhat useful.
+ //
+
+ succeeded = fdoData->Perf.SuccessfulIO;
+
+ if ((FdoExtension->ErrorCount != 0) &&
+ (fdoData->Perf.ReEnableThreshhold <= succeeded)
+ ) {
+
+ fdoData->Perf.SuccessfulIO = 0; // implicit interlock
+
+ NT_ASSERT(FdoExtension->ErrorCount > 0);
+ errors = InterlockedDecrement((volatile LONG *)&FdoExtension->ErrorCount);
+
+ //
+ // note: do in reverse order of the sets "just in case"
+ //
+
+ if (errors < CLASS_ERROR_LEVEL_2) {
+ if (errors == CLASS_ERROR_LEVEL_2 - 1) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClasspPerfIncrementSuccessfulIo: "
+ "Error level 2 no longer required.\n"));
+ }
+ if (!TEST_FLAG(fdoData->Perf.OriginalSrbFlags,
+ SRB_FLAGS_DISABLE_DISCONNECT)) {
+ CLEAR_FLAG(FdoExtension->SrbFlags,
+ SRB_FLAGS_DISABLE_DISCONNECT);
+ }
+ }
+
+ if (errors < CLASS_ERROR_LEVEL_1) {
+ if (errors == CLASS_ERROR_LEVEL_1 - 1) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClasspPerfIncrementSuccessfulIo: "
+ "Error level 1 no longer required.\n"));
+ }
+ if (!TEST_FLAG(fdoData->Perf.OriginalSrbFlags,
+ SRB_FLAGS_DISABLE_SYNCH_TRANSFER)) {
+ CLEAR_FLAG(FdoExtension->SrbFlags,
+ SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
+ }
+ if (TEST_FLAG(fdoData->Perf.OriginalSrbFlags,
+ SRB_FLAGS_QUEUE_ACTION_ENABLE)) {
+ SET_FLAG(FdoExtension->SrbFlags,
+ SRB_FLAGS_QUEUE_ACTION_ENABLE);
+ }
+ if (TEST_FLAG(fdoData->Perf.OriginalSrbFlags,
+ SRB_FLAGS_NO_QUEUE_FREEZE)) {
+ SET_FLAG(FdoExtension->SrbFlags,
+ SRB_FLAGS_NO_QUEUE_FREEZE);
+ }
+ }
+ } // end of threshhold definitely being hit for first time
+
+ KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
+ return;
+}
+
+
+PMDL ClasspBuildDeviceMdl(PVOID Buffer, ULONG BufferLen, BOOLEAN WriteToDevice)
+{
+ PMDL mdl;
+
+ mdl = IoAllocateMdl(Buffer, BufferLen, FALSE, FALSE, NULL);
+ if (mdl){
+ try {
+ MmProbeAndLockPages(mdl, KernelMode, WriteToDevice ? IoReadAccess : IoWriteAccess);
+ #pragma warning(suppress: 6320) // We want to handle any exception that MmProbeAndLockPages might throw
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+ NTSTATUS status = GetExceptionCode();
+
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT, "ClasspBuildDeviceMdl: MmProbeAndLockPages failed with %xh.", status));
+ IoFreeMdl(mdl);
+ mdl = NULL;
+ }
+ }
+ else {
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT, "ClasspBuildDeviceMdl: IoAllocateMdl failed"));
+ }
+
+ return mdl;
+}
+
+
+PMDL BuildDeviceInputMdl(PVOID Buffer, ULONG BufferLen)
+{
+ return ClasspBuildDeviceMdl(Buffer, BufferLen, FALSE);
+}
+
+
+VOID ClasspFreeDeviceMdl(PMDL Mdl)
+{
+ MmUnlockPages(Mdl);
+ IoFreeMdl(Mdl);
+}
+
+
+VOID FreeDeviceInputMdl(PMDL Mdl)
+{
+ ClasspFreeDeviceMdl(Mdl);
+ return;
+}
+
+
+#if 0
+ VOID
+ ClasspPerfResetCounters(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ )
+ {
+ PCLASS_PRIVATE_FDO_DATA fdoData = FdoExtension->PrivateFdoData;
+ KIRQL oldIrql;
+
+ KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "ClasspPerfResetCounters: "
+ "Resetting all perf counters.\n"));
+ fdoData->Perf.SuccessfulIO = 0;
+ FdoExtension->ErrorCount = 0;
+
+ if (!TEST_FLAG(fdoData->Perf.OriginalSrbFlags,
+ SRB_FLAGS_DISABLE_DISCONNECT)) {
+ CLEAR_FLAG(FdoExtension->SrbFlags,
+ SRB_FLAGS_DISABLE_DISCONNECT);
+ }
+ if (!TEST_FLAG(fdoData->Perf.OriginalSrbFlags,
+ SRB_FLAGS_DISABLE_SYNCH_TRANSFER)) {
+ CLEAR_FLAG(FdoExtension->SrbFlags,
+ SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
+ }
+ if (TEST_FLAG(fdoData->Perf.OriginalSrbFlags,
+ SRB_FLAGS_QUEUE_ACTION_ENABLE)) {
+ SET_FLAG(FdoExtension->SrbFlags,
+ SRB_FLAGS_QUEUE_ACTION_ENABLE);
+ }
+ if (TEST_FLAG(fdoData->Perf.OriginalSrbFlags,
+ SRB_FLAGS_NO_QUEUE_FREEZE)) {
+ SET_FLAG(FdoExtension->SrbFlags,
+ SRB_FLAGS_NO_QUEUE_FREEZE);
+ }
+ KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
+ return;
+ }
+#endif
+
+
+/*++
+
+ClasspDuidGetDeviceIdProperty
+
+Routine Description:
+
+ Add StorageDeviceIdProperty to the device unique ID structure.
+
+Arguments:
+
+ DeviceObject - a pointer to the device object
+ Irp - a pointer to the I/O request packet
+
+Return Value:
+
+ Status Code
+
+--*/
+NTSTATUS
+ClasspDuidGetDeviceIdProperty(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PSTORAGE_DEVICE_ID_DESCRIPTOR deviceIdDescriptor = NULL;
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
+ PSTORAGE_DESCRIPTOR_HEADER descHeader;
+ PSTORAGE_DEVICE_UNIQUE_IDENTIFIER storageDuid;
+ PUCHAR dest;
+
+ STORAGE_PROPERTY_ID propertyId = StorageDeviceIdProperty;
+
+ NTSTATUS status;
+
+ ULONG queryLength;
+ ULONG offset;
+
+ //
+ // Get the VPD page 83h data.
+ //
+
+ status = ClassGetDescriptor(commonExtension->LowerDeviceObject,
+ &propertyId,
+ &deviceIdDescriptor);
+
+ if (!NT_SUCCESS(status) || !deviceIdDescriptor) {
+ goto FnExit;
+ }
+
+ queryLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
+ descHeader = Irp->AssociatedIrp.SystemBuffer;
+
+ //
+ // Adjust required size and potential destination location.
+ //
+
+ offset = descHeader->Size;
+ dest = (PUCHAR)descHeader + offset;
+
+ descHeader->Size += deviceIdDescriptor->Size;
+
+ if (queryLength < descHeader->Size) {
+
+ //
+ // Output buffer is too small. Return error and make sure
+ // the caller gets info about required buffer size.
+ //
+
+ Irp->IoStatus.Information = sizeof(STORAGE_DESCRIPTOR_HEADER);
+ status = STATUS_BUFFER_OVERFLOW;
+ goto FnExit;
+ }
+
+ storageDuid = Irp->AssociatedIrp.SystemBuffer;
+ storageDuid->StorageDeviceIdOffset = offset;
+
+ RtlCopyMemory(dest,
+ deviceIdDescriptor,
+ deviceIdDescriptor->Size);
+
+ Irp->IoStatus.Information = storageDuid->Size;
+ status = STATUS_SUCCESS;
+
+FnExit:
+
+ FREE_POOL(deviceIdDescriptor);
+
+ return status;
+}
+
+
+
+/*++
+
+ClasspDuidGetDeviceProperty
+
+Routine Description:
+
+ Add StorageDeviceProperty to the device unique ID structure.
+
+Arguments:
+
+ DeviceObject - a pointer to the device object
+ Irp - a pointer to the I/O request packet
+
+Return Value:
+
+ Status Code
+
+--*/
+NTSTATUS
+ClasspDuidGetDeviceProperty(
+ PDEVICE_OBJECT DeviceObject,
+ PIRP Irp
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+ PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor = fdoExtension->DeviceDescriptor;
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
+ PSTORAGE_DESCRIPTOR_HEADER descHeader;
+ PSTORAGE_DEVICE_UNIQUE_IDENTIFIER storageDuid;
+ PUCHAR dest;
+
+ NTSTATUS status = STATUS_NOT_FOUND;
+
+ ULONG queryLength;
+ ULONG offset;
+
+ //
+ // Use the StorageDeviceProperty already cached in the device extension.
+ //
+
+ if (!deviceDescriptor) {
+ goto FnExit;
+ }
+
+ queryLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
+ descHeader = Irp->AssociatedIrp.SystemBuffer;
+
+ //
+ // Use this info only if serial number is available.
+ //
+
+ if (deviceDescriptor->SerialNumberOffset == 0) {
+ goto FnExit;
+ }
+
+ //
+ // Adjust required size and potential destination location.
+ //
+
+ offset = descHeader->Size;
+ dest = (PUCHAR)descHeader + offset;
+
+ descHeader->Size += deviceDescriptor->Size;
+
+ if (queryLength < descHeader->Size) {
+
+ //
+ // Output buffer is too small. Return error and make sure
+ // the caller get info about required buffer size.
+ //
+
+ Irp->IoStatus.Information = sizeof(STORAGE_DESCRIPTOR_HEADER);
+ status = STATUS_BUFFER_OVERFLOW;
+ goto FnExit;
+ }
+
+ storageDuid = Irp->AssociatedIrp.SystemBuffer;
+ storageDuid->StorageDeviceOffset = offset;
+
+ RtlCopyMemory(dest,
+ deviceDescriptor,
+ deviceDescriptor->Size);
+
+ Irp->IoStatus.Information = storageDuid->Size;
+ status = STATUS_SUCCESS;
+
+FnExit:
+
+ return status;
+}
+
+
+/*++
+
+ClasspDuidGetDriveLayout
+
+Routine Description:
+
+ Add drive layout signature to the device unique ID structure.
+ Layout signature is only added for disk-type devices.
+
+Arguments:
+
+ DeviceObject - a pointer to the device object
+ Irp - a pointer to the I/O request packet
+
+Return Value:
+
+ Status Code
+
+--*/
+NTSTATUS
+ClasspDuidGetDriveLayout(
+ PDEVICE_OBJECT DeviceObject,
+ PIRP Irp
+ )
+{
+ PDRIVE_LAYOUT_INFORMATION_EX layoutEx = NULL;
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
+ PSTORAGE_DESCRIPTOR_HEADER descHeader;
+ PSTORAGE_DEVICE_UNIQUE_IDENTIFIER storageDuid;
+ PSTORAGE_DEVICE_LAYOUT_SIGNATURE driveLayoutSignature;
+
+ NTSTATUS status = STATUS_NOT_FOUND;
+
+ ULONG queryLength;
+ ULONG offset;
+
+ //
+ // Only process disk-type devices.
+ //
+
+ if (DeviceObject->DeviceType != FILE_DEVICE_DISK) {
+ goto FnExit;
+ }
+
+ //
+ // Get current partition table and process only if GPT
+ // or MBR layout.
+ //
+
+ status = IoReadPartitionTableEx(DeviceObject, &layoutEx);
+
+ if (!NT_SUCCESS(status)) {
+ status = STATUS_NOT_FOUND;
+ goto FnExit;
+ }
+
+ if (layoutEx->PartitionStyle != PARTITION_STYLE_GPT &&
+ layoutEx->PartitionStyle != PARTITION_STYLE_MBR) {
+ status = STATUS_NOT_FOUND;
+ goto FnExit;
+ }
+
+ queryLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
+ descHeader = Irp->AssociatedIrp.SystemBuffer;
+
+ //
+ // Adjust required size and potential destination location.
+ //
+
+ offset = descHeader->Size;
+ driveLayoutSignature = (PSTORAGE_DEVICE_LAYOUT_SIGNATURE)((PUCHAR)descHeader + offset);
+
+ descHeader->Size += sizeof(STORAGE_DEVICE_LAYOUT_SIGNATURE);
+
+ if (queryLength < descHeader->Size) {
+
+ //
+ // Output buffer is too small. Return error and make sure
+ // the caller get info about required buffer size.
+ //
+
+ Irp->IoStatus.Information = sizeof(STORAGE_DESCRIPTOR_HEADER);
+ status = STATUS_BUFFER_OVERFLOW;
+ goto FnExit;
+ }
+
+ storageDuid = Irp->AssociatedIrp.SystemBuffer;
+
+ driveLayoutSignature->Size = sizeof(STORAGE_DEVICE_LAYOUT_SIGNATURE);
+ driveLayoutSignature->Version = DUID_VERSION_1;
+
+ if (layoutEx->PartitionStyle == PARTITION_STYLE_MBR) {
+
+ driveLayoutSignature->Mbr = TRUE;
+
+ RtlCopyMemory(&driveLayoutSignature->DeviceSpecific.MbrSignature,
+ &layoutEx->Mbr.Signature,
+ sizeof(layoutEx->Mbr.Signature));
+
+ } else {
+
+ driveLayoutSignature->Mbr = FALSE;
+
+ RtlCopyMemory(&driveLayoutSignature->DeviceSpecific.GptDiskId,
+ &layoutEx->Gpt.DiskId,
+ sizeof(layoutEx->Gpt.DiskId));
+ }
+
+ storageDuid->DriveLayoutSignatureOffset = offset;
+
+ Irp->IoStatus.Information = storageDuid->Size;
+ status = STATUS_SUCCESS;
+
+
+FnExit:
+
+ FREE_POOL(layoutEx);
+
+ return status;
+}
+
+
+/*++
+
+ClasspDuidQueryProperty
+
+Routine Description:
+
+ Handles IOCTL_STORAGE_QUERY_PROPERTY for device unique ID requests
+ (when PropertyId is StorageDeviceUniqueIdProperty).
+
+Arguments:
+
+ DeviceObject - a pointer to the device object
+ Irp - a pointer to the I/O request packet
+
+Return Value:
+
+ Status Code
+
+--*/
+NTSTATUS
+ClasspDuidQueryProperty(
+ PDEVICE_OBJECT DeviceObject,
+ PIRP Irp
+ )
+{
+ PSTORAGE_PROPERTY_QUERY query = Irp->AssociatedIrp.SystemBuffer;
+ PSTORAGE_DESCRIPTOR_HEADER descHeader;
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
+
+ NTSTATUS status;
+
+ ULONG outLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
+
+ BOOLEAN includeOptionalIds;
+ BOOLEAN overflow = FALSE;
+ BOOLEAN infoFound = FALSE;
+ BOOLEAN useStatus = TRUE; // Use the status directly instead of relying on overflow and infoFound flags.
+
+ //
+ // Must run at less then dispatch.
+ //
+
+ if (KeGetCurrentIrql() >= DISPATCH_LEVEL) {
+
+ NT_ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
+ status = STATUS_INVALID_LEVEL;
+ goto FnExit;
+ }
+
+ //
+ // Check proper query type.
+ //
+
+ if (query->QueryType == PropertyExistsQuery) {
+ Irp->IoStatus.Information = 0;
+ status = STATUS_SUCCESS;
+ goto FnExit;
+ }
+
+ if (query->QueryType != PropertyStandardQuery) {
+ status = STATUS_NOT_SUPPORTED;
+ goto FnExit;
+ }
+
+ //
+ // Check AdditionalParameters validity.
+ //
+
+ if (query->AdditionalParameters[0] == DUID_INCLUDE_SOFTWARE_IDS) {
+ includeOptionalIds = TRUE;
+ } else if (query->AdditionalParameters[0] == DUID_HARDWARE_IDS_ONLY) {
+ includeOptionalIds = FALSE;
+ } else {
+ status = STATUS_INVALID_PARAMETER;
+ goto FnExit;
+ }
+
+ //
+ // Verify output parameters.
+ //
+
+ if (outLength < sizeof(STORAGE_DESCRIPTOR_HEADER)) {
+
+ status = STATUS_INFO_LENGTH_MISMATCH;
+ goto FnExit;
+ }
+
+ //
+ // From this point forward the status depends on the overflow
+ // and infoFound flags.
+ //
+
+ useStatus = FALSE;
+
+ descHeader = Irp->AssociatedIrp.SystemBuffer;
+ RtlZeroMemory(descHeader, outLength);
+
+ descHeader->Version = DUID_VERSION_1;
+ descHeader->Size = sizeof(STORAGE_DEVICE_UNIQUE_IDENTIFIER);
+
+ //
+ // Try to build device unique id from StorageDeviceIdProperty.
+ //
+
+ status = ClasspDuidGetDeviceIdProperty(DeviceObject,
+ Irp);
+
+ if (status == STATUS_BUFFER_OVERFLOW) {
+ overflow = TRUE;
+ }
+
+ if (NT_SUCCESS(status)) {
+ infoFound = TRUE;
+ }
+
+ //
+ // Try to build device unique id from StorageDeviceProperty.
+ //
+
+ status = ClasspDuidGetDeviceProperty(DeviceObject,
+ Irp);
+
+ if (status == STATUS_BUFFER_OVERFLOW) {
+ overflow = TRUE;
+ }
+
+ if (NT_SUCCESS(status)) {
+ infoFound = TRUE;
+ }
+
+ //
+ // The following portion is optional and only included if the
+ // caller requested software IDs.
+ //
+
+ if (!includeOptionalIds) {
+ goto FnExit;
+ }
+
+ //
+ // Try to build device unique id from drive layout signature (disk
+ // devices only).
+ //
+
+ status = ClasspDuidGetDriveLayout(DeviceObject,
+ Irp);
+
+ if (status == STATUS_BUFFER_OVERFLOW) {
+ overflow = TRUE;
+ }
+
+ if (NT_SUCCESS(status)) {
+ infoFound = TRUE;
+ }
+
+FnExit:
+
+ if (!useStatus) {
+
+ //
+ // Return overflow, success, or a generic error.
+ //
+
+ if (overflow) {
+
+ //
+ // If output buffer is STORAGE_DESCRIPTOR_HEADER, then return
+ // success to the user. Otherwise, send an error so the user
+ // knows a larger buffer is required.
+ //
+
+ if (outLength == sizeof(STORAGE_DESCRIPTOR_HEADER)) {
+ status = STATUS_SUCCESS;
+ Irp->IoStatus.Information = sizeof(STORAGE_DESCRIPTOR_HEADER);
+ } else {
+ status = STATUS_BUFFER_OVERFLOW;
+ }
+
+ } else if (infoFound) {
+ status = STATUS_SUCCESS;
+
+ //
+ // Exercise the compare routine. This should always succeed.
+ //
+
+ NT_ASSERT(DuidExactMatch == CompareStorageDuids(Irp->AssociatedIrp.SystemBuffer,
+ Irp->AssociatedIrp.SystemBuffer));
+
+ } else {
+ status = STATUS_NOT_FOUND;
+ }
+ }
+
+ Irp->IoStatus.Status = status;
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+
+ return status;
+}
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClasspWriteCacheProperty()
+
+Routine Description:
+
+ This routine reads the caching mode page from the device to
+ build the Write Cache property page.
+
+Arguments:
+
+ DeviceObject - The device object to handle this irp
+
+ Irp - The IRP for this request
+
+ Srb - SRB allocated by the dispatch routine
+
+Return Value:
+
+--*/
+
+NTSTATUS ClasspWriteCacheProperty(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _In_ PIRP Irp,
+ _Inout_ PSCSI_REQUEST_BLOCK Srb
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+ PSTORAGE_WRITE_CACHE_PROPERTY writeCache;
+ PSTORAGE_PROPERTY_QUERY query = Irp->AssociatedIrp.SystemBuffer;
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
+ PMODE_PARAMETER_HEADER modeData = NULL;
+ PMODE_CACHING_PAGE pageData = NULL;
+ ULONG length, information = 0;
+ NTSTATUS status;
+ PCDB cdb;
+
+ //
+ // Must run at less then dispatch.
+ //
+
+ if (KeGetCurrentIrql() >= DISPATCH_LEVEL) {
+
+ NT_ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
+ status = STATUS_INVALID_LEVEL;
+ goto WriteCacheExit;
+ }
+
+ //
+ // Check proper query type.
+ //
+
+ if (query->QueryType == PropertyExistsQuery) {
+ status = STATUS_SUCCESS;
+ goto WriteCacheExit;
+ }
+
+ if (query->QueryType != PropertyStandardQuery) {
+ status = STATUS_NOT_SUPPORTED;
+ goto WriteCacheExit;
+ }
+
+ length = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
+
+ if (length < sizeof(STORAGE_DESCRIPTOR_HEADER)) {
+ status = STATUS_INFO_LENGTH_MISMATCH;
+ goto WriteCacheExit;
+ }
+
+ writeCache = (PSTORAGE_WRITE_CACHE_PROPERTY) Irp->AssociatedIrp.SystemBuffer;
+ RtlZeroMemory(writeCache, length);
+
+ //
+ // Set version and required size.
+ //
+
+ writeCache->Version = sizeof(STORAGE_WRITE_CACHE_PROPERTY);
+ writeCache->Size = sizeof(STORAGE_WRITE_CACHE_PROPERTY);
+
+ if (length < sizeof(STORAGE_WRITE_CACHE_PROPERTY)) {
+ information = sizeof(STORAGE_DESCRIPTOR_HEADER);
+ status = STATUS_SUCCESS;
+ goto WriteCacheExit;
+ }
+
+ //
+ // Set known values
+ //
+
+ writeCache->NVCacheEnabled = FALSE;
+ writeCache->UserDefinedPowerProtection = TEST_FLAG(fdoExtension->DeviceFlags, DEV_POWER_PROTECTED);
+
+ //
+ // Check for flush cache support by sending a sync cache command
+ // to the device.
+ //
+
+ //
+ // Set timeout value and mark the request as not being a tagged request.
+ //
+ SrbSetTimeOutValue(Srb, fdoExtension->TimeOutValue * 4);
+ SrbSetRequestTag(Srb, SP_UNTAGGED);
+ SrbSetRequestAttribute(Srb, SRB_SIMPLE_TAG_REQUEST);
+ SrbAssignSrbFlags(Srb, fdoExtension->SrbFlags);
+
+ SrbSetCdbLength(Srb, 10);
+ cdb = SrbGetCdb(Srb);
+ cdb->CDB10.OperationCode = SCSIOP_SYNCHRONIZE_CACHE;
+
+ status = ClassSendSrbSynchronous(DeviceObject,
+ Srb,
+ NULL,
+ 0,
+ TRUE);
+ if (NT_SUCCESS(status)) {
+ writeCache->FlushCacheSupported = TRUE;
+ } else {
+ //
+ // Device does not support sync cache
+ //
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "ClasspWriteCacheProperty: Synchronize cache failed with status 0x%X\n", status));
+ writeCache->FlushCacheSupported = FALSE;
+ //
+ // Reset the status if there was any failure
+ //
+ status = STATUS_SUCCESS;
+ }
+
+ modeData = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
+ MODE_PAGE_DATA_SIZE,
+ CLASS_TAG_MODE_DATA);
+
+ if (modeData == NULL) {
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL, "ClasspWriteCacheProperty: Unable to allocate mode data buffer\n"));
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ goto WriteCacheExit;
+ }
+
+ RtlZeroMemory(modeData, MODE_PAGE_DATA_SIZE);
+
+ length = ClassModeSense(DeviceObject,
+ (PCHAR) modeData,
+ MODE_PAGE_DATA_SIZE,
+ MODE_PAGE_CACHING);
+
+ if (length < sizeof(MODE_PARAMETER_HEADER)) {
+
+ //
+ // Retry the request in case of a check condition.
+ //
+
+ length = ClassModeSense(DeviceObject,
+ (PCHAR) modeData,
+ MODE_PAGE_DATA_SIZE,
+ MODE_PAGE_CACHING);
+
+ if (length < sizeof(MODE_PARAMETER_HEADER)) {
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL, "ClasspWriteCacheProperty: Mode Sense failed\n"));
+ status = STATUS_IO_DEVICE_ERROR;
+ goto WriteCacheExit;
+ }
+ }
+
+ //
+ // If the length is greater than length indicated by the mode data reset
+ // the data to the mode data.
+ //
+
+ if (length > (ULONG) (modeData->ModeDataLength + 1)) {
+ length = modeData->ModeDataLength + 1;
+ }
+
+ //
+ // Look for caching page in the returned mode page data.
+ //
+
+ pageData = ClassFindModePage((PCHAR) modeData,
+ length,
+ MODE_PAGE_CACHING,
+ TRUE);
+
+ //
+ // Check if valid caching page exists.
+ //
+
+ if (pageData == NULL) {
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "ClasspWriteCacheProperty: Unable to find caching mode page.\n"));
+ //
+ // Set write cache value as unknown.
+ //
+ writeCache->WriteCacheEnabled = WriteCacheEnableUnknown;
+ writeCache->WriteCacheType = WriteCacheTypeUnknown;
+ } else {
+ writeCache->WriteCacheEnabled = pageData->WriteCacheEnable ?
+ WriteCacheEnabled : WriteCacheDisabled;
+
+ writeCache->WriteCacheType = pageData->WriteCacheEnable ?
+ WriteCacheTypeWriteBack : WriteCacheTypeUnknown;
+ }
+
+ //
+ // Check write through support. If the device previously failed a write request
+ // with FUA bit is set, then CLASS_SPECIAL_FUA_NOT_SUPPORTED will be set,
+ // which means write through is not support by the device.
+ //
+
+ if ((modeData->DeviceSpecificParameter & MODE_DSP_FUA_SUPPORTED) &&
+ (!TEST_FLAG(fdoExtension->ScanForSpecialFlags, CLASS_SPECIAL_FUA_NOT_SUPPORTED))) {
+ writeCache->WriteThroughSupported = WriteThroughSupported;
+ } else {
+ writeCache->WriteThroughSupported = WriteThroughNotSupported;
+ }
+
+ //
+ // Get the changeable caching mode page and check write cache is changeable.
+ //
+
+ RtlZeroMemory(modeData, MODE_PAGE_DATA_SIZE);
+
+ length = ClasspModeSense(DeviceObject,
+ (PCHAR) modeData,
+ MODE_PAGE_DATA_SIZE,
+ MODE_PAGE_CACHING,
+ MODE_SENSE_CHANGEABLE_VALUES);
+
+ if (length < sizeof(MODE_PARAMETER_HEADER)) {
+
+ //
+ // Retry the request in case of a check condition.
+ //
+
+ length = ClasspModeSense(DeviceObject,
+ (PCHAR) modeData,
+ MODE_PAGE_DATA_SIZE,
+ MODE_PAGE_CACHING,
+ MODE_SENSE_CHANGEABLE_VALUES);
+
+ if (length < sizeof(MODE_PARAMETER_HEADER)) {
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "ClasspWriteCacheProperty: Mode Sense failed\n"));
+
+ //
+ // If the device fails to return changeable pages, then
+ // set the write cache changeable value to unknown.
+ //
+
+ writeCache->WriteCacheChangeable = WriteCacheChangeUnknown;
+ information = sizeof(STORAGE_WRITE_CACHE_PROPERTY);
+ goto WriteCacheExit;
+ }
+ }
+
+ //
+ // If the length is greater than length indicated by the mode data reset
+ // the data to the mode data.
+ //
+
+ if (length > (ULONG) (modeData->ModeDataLength + 1)) {
+ length = modeData->ModeDataLength + 1;
+ }
+
+ //
+ // Look for caching page in the returned mode page data.
+ //
+
+ pageData = ClassFindModePage((PCHAR) modeData,
+ length,
+ MODE_PAGE_CACHING,
+ TRUE);
+ //
+ // Check if valid caching page exists.
+ //
+
+ if (pageData == NULL) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "ClasspWriteCacheProperty: Unable to find caching mode page.\n"));
+ //
+ // Set write cache changeable value to unknown.
+ //
+ writeCache->WriteCacheChangeable = WriteCacheChangeUnknown;
+ } else {
+ writeCache->WriteCacheChangeable = pageData->WriteCacheEnable ?
+ WriteCacheChangeable : WriteCacheNotChangeable;
+ }
+
+ information = sizeof(STORAGE_WRITE_CACHE_PROPERTY);
+
+WriteCacheExit:
+
+ FREE_POOL(modeData);
+
+ //
+ // Set the size and status in IRP
+ //
+ Irp->IoStatus.Information = information;;
+ Irp->IoStatus.Status = status;
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+
+ return status;
+}
+
+ULONG
+ClasspCalculateLogicalSectorSize (
+ _In_ PDEVICE_OBJECT Fdo,
+ _In_ ULONG BytesPerBlockInBigEndian
+ )
+/*++
+ Convert the big-endian value.
+ if it's 0, default to the standard 512 bytes.
+ if it's not a power of 2 value, round down to power of 2.
+--*/
+{
+ ULONG logicalSectorSize;
+
+ REVERSE_BYTES(&logicalSectorSize, &BytesPerBlockInBigEndian);
+
+ if (logicalSectorSize == 0) {
+ logicalSectorSize = 512;
+ } else {
+ //
+ // Clear all but the highest set bit.
+ // That will give us a bytesPerSector value that is a power of 2.
+ //
+ if (logicalSectorSize & (logicalSectorSize-1)) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT, "FDO %ph has non-standard sector size 0x%x.", Fdo, logicalSectorSize));
+ do {
+ logicalSectorSize &= logicalSectorSize-1;
+ }
+ while (logicalSectorSize & (logicalSectorSize-1));
+ }
+ }
+
+ return logicalSectorSize;
+}
+
+NTSTATUS
+InterpretReadCapacity16Data (
+ _Inout_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ _In_ PREAD_CAPACITY16_DATA ReadCapacity16Data
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ USHORT lowestAlignedBlock;
+ USHORT logicalBlocksPerPhysicalBlock;
+ PCLASS_READ_CAPACITY16_DATA cachedData = &(FdoExtension->FunctionSupportInfo->ReadCapacity16Data);
+
+ // use Logical Sector Size from DiskGeometry to avoid duplicated calculation.
+ FdoExtension->FunctionSupportInfo->ReadCapacity16Data.BytesPerLogicalSector = ClasspCalculateLogicalSectorSize(FdoExtension->DeviceObject, ReadCapacity16Data->BytesPerBlock);
+
+ // FdoExtension->DiskGeometry.BytesPerSector might be 0 for class drivers that don't get READ CAPACITY info yet.
+ NT_ASSERT( (FdoExtension->DiskGeometry.BytesPerSector == 0) ||
+ (FdoExtension->DiskGeometry.BytesPerSector == FdoExtension->FunctionSupportInfo->ReadCapacity16Data.BytesPerLogicalSector) );
+
+ logicalBlocksPerPhysicalBlock = 1 << ReadCapacity16Data->LogicalPerPhysicalExponent;
+ lowestAlignedBlock = (ReadCapacity16Data->LowestAlignedBlock_MSB << 8) | ReadCapacity16Data->LowestAlignedBlock_LSB;
+
+ if (lowestAlignedBlock > logicalBlocksPerPhysicalBlock) {
+ // we get garbage data
+ status = STATUS_UNSUCCESSFUL;
+ } else {
+ // value of lowestAlignedBlock (from T10 spec) needs to be converted.
+ lowestAlignedBlock = (logicalBlocksPerPhysicalBlock - lowestAlignedBlock) % logicalBlocksPerPhysicalBlock;
+ }
+
+ if (NT_SUCCESS(status)) {
+ // fill output buffer
+ cachedData->BytesPerPhysicalSector = cachedData->BytesPerLogicalSector * logicalBlocksPerPhysicalBlock;
+ cachedData->BytesOffsetForSectorAlignment = cachedData->BytesPerLogicalSector * lowestAlignedBlock;
+
+ //
+ // Fill in the Logical Block Provisioning info. Note that we do not
+ // use these fields; we use the Provisioning Type and LBPRZ fields from
+ // the Logical Block Provisioning VPD page (0xB2).
+ //
+ cachedData->LBProvisioningEnabled = ReadCapacity16Data->LBPME;
+ cachedData->LBProvisioningReadZeros = ReadCapacity16Data->LBPRZ;
+
+ TracePrint((TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_INIT,
+ "InterpretReadCapacity16Data: Device\'s LBP enabled = %d\n",
+ cachedData->LBProvisioningEnabled));
+ }
+
+ return status;
+}
+
+NTSTATUS
+ClassReadCapacity16 (
+ _Inout_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ _Inout_ PSCSI_REQUEST_BLOCK Srb
+ )
+/*
+ This routine may send down a READ CAPACITY 16 command to retrieve info.
+ The info will be cached in FdoExtension->LowerLayerSupport->AccessAlignment.
+
+ After info retrieving finished, this function sets following field:
+ FdoExtension->LowerLayerSupport->AccessAlignment.LowerLayerSupported = Supported;
+ to indicate that info has been cached.
+
+ NOTE: some future processes may use this funciton to send the command anyway, it will be caller's decision
+ on checking 'AccessAlignment.LowerLayerSupported' in case the cached info is good enough.
+*/
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PREAD_CAPACITY16_DATA dataBuffer = NULL;
+ UCHAR bufferLength = sizeof(READ_CAPACITY16_DATA);
+ ULONG allocationBufferLength = bufferLength; //DMA buffer size for alignment
+ PCDB cdb;
+ ULONG dataTransferLength = 0;
+
+ //
+ // If the information retrieval has already been attempted, return the cached status.
+ //
+ if (FdoExtension->FunctionSupportInfo->ReadCapacity16Data.CommandStatus != -1) {
+ // get cached NTSTATUS from previous call.
+ return FdoExtension->FunctionSupportInfo->ReadCapacity16Data.CommandStatus;
+ }
+
+ if (ClasspIsObsoletePortDriver(FdoExtension)) {
+ FdoExtension->FunctionSupportInfo->ReadCapacity16Data.CommandStatus = STATUS_NOT_IMPLEMENTED;
+ return STATUS_NOT_IMPLEMENTED;
+ }
+
+#if defined(_ARM_) || defined(_ARM64_)
+ //
+ // ARM has specific alignment requirements, although this will not have a functional impact on x86 or amd64
+ // based platforms. We are taking the conservative approach here.
+ //
+ allocationBufferLength = ALIGN_UP_BY(allocationBufferLength,KeGetRecommendedSharedDataAlignment());
+ dataBuffer = (PREAD_CAPACITY16_DATA)ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned, allocationBufferLength, '4CcS');
+#else
+ dataBuffer = (PREAD_CAPACITY16_DATA)ExAllocatePoolWithTag(NonPagedPoolNx, bufferLength, '4CcS');
+#endif
+
+ if (dataBuffer == NULL) {
+ // return without updating FdoExtension->FunctionSupportInfo->ReadCapacity16Data.CommandStatus
+ // the field will remain value as "-1", so that the command will be attempted next time this function is called.
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlZeroMemory(dataBuffer, allocationBufferLength);
+
+ //
+ // Initialize the SRB.
+ //
+ if (FdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ status = InitializeStorageRequestBlock((PSTORAGE_REQUEST_BLOCK)Srb,
+ STORAGE_ADDRESS_TYPE_BTL8,
+ CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE,
+ 1,
+ SrbExDataTypeScsiCdb16);
+ if (NT_SUCCESS(status)) {
+ ((PSTORAGE_REQUEST_BLOCK)Srb)->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
+ } else {
+ //
+ // Should not occur.
+ //
+ NT_ASSERT(FALSE);
+ }
+ } else {
+ RtlZeroMemory(Srb, sizeof(SCSI_REQUEST_BLOCK));
+ Srb->Length = sizeof(SCSI_REQUEST_BLOCK);
+ Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
+ }
+
+ //prepare the Srb
+ if (NT_SUCCESS(status))
+ {
+ SrbSetTimeOutValue(Srb, FdoExtension->TimeOutValue);
+ SrbSetRequestTag(Srb, SP_UNTAGGED);
+ SrbSetRequestAttribute(Srb, SRB_SIMPLE_TAG_REQUEST);
+ SrbAssignSrbFlags(Srb, FdoExtension->SrbFlags);
+
+ SrbSetCdbLength(Srb, 16);
+
+ cdb = SrbGetCdb(Srb);
+ cdb->READ_CAPACITY16.OperationCode = SCSIOP_READ_CAPACITY16;
+ cdb->READ_CAPACITY16.ServiceAction = SERVICE_ACTION_READ_CAPACITY16;
+ cdb->READ_CAPACITY16.AllocationLength[3] = bufferLength;
+
+ status = ClassSendSrbSynchronous(FdoExtension->DeviceObject,
+ Srb,
+ dataBuffer,
+ allocationBufferLength,
+ FALSE);
+
+ dataTransferLength = SrbGetDataTransferLength(Srb);
+ }
+
+ if (NT_SUCCESS(status) && (dataTransferLength < 16))
+ {
+ // the device should return at least 16 bytes of data for this command.
+ status = STATUS_INFO_LENGTH_MISMATCH;
+ }
+
+ //
+ // Handle the case where we get back STATUS_DATA_OVERRUN b/c the input
+ // buffer was larger than necessary.
+ //
+ if (status == STATUS_DATA_OVERRUN && dataTransferLength < bufferLength)
+ {
+ status = STATUS_SUCCESS;
+ }
+
+ if (NT_SUCCESS(status))
+ {
+ // cache data into FdoExtension
+ status = InterpretReadCapacity16Data(FdoExtension, dataBuffer);
+ }
+
+ // cache the status indicates that this funciton has been called.
+ FdoExtension->FunctionSupportInfo->ReadCapacity16Data.CommandStatus = status;
+
+ ExFreePool(dataBuffer);
+
+ return status;
+}
+
+NTSTATUS ClasspAccessAlignmentProperty(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _In_ PIRP Irp,
+ _Inout_ PSCSI_REQUEST_BLOCK Srb
+ )
+/*
+ At first time of receiving the request, this function will forward it to lower stack to determine if it's supportted.
+ If it's not supported, SCSIOP_READ_CAPACITY16 will be sent down to retrieve the information.
+*/
+{
+ NTSTATUS status = STATUS_UNSUCCESSFUL;
+
+ PCOMMON_DEVICE_EXTENSION commonExtension = (PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+ PSTORAGE_PROPERTY_QUERY query = (PSTORAGE_PROPERTY_QUERY)Irp->AssociatedIrp.SystemBuffer;
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
+ ULONG length = 0;
+ ULONG information = 0;
+
+ PSTORAGE_ACCESS_ALIGNMENT_DESCRIPTOR accessAlignment;
+
+ //
+ // check registry setting and fail the IOCTL if it's required.
+ // this registry setting can be used to work around issues which upper layer doesn't support large physical sector size.
+ //
+ if (fdoExtension->FunctionSupportInfo->RegAccessAlignmentQueryNotSupported) {
+ status = STATUS_NOT_SUPPORTED;
+ goto Exit;
+ }
+
+ if ( (DeviceObject->DeviceType != FILE_DEVICE_DISK) ||
+ (TEST_FLAG(DeviceObject->Characteristics, FILE_FLOPPY_DISKETTE)) ||
+ (fdoExtension->FunctionSupportInfo->LowerLayerSupport.AccessAlignmentProperty == Supported) ) {
+ // if it's not disk, forward the request to lower layer,
+ // if the IOCTL is supported by lower stack, forward it down.
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+ return status;
+ }
+
+ //
+ // Check proper query type.
+ //
+
+ if (query->QueryType == PropertyExistsQuery) {
+ status = STATUS_SUCCESS;
+ goto Exit;
+ } else if (query->QueryType != PropertyStandardQuery) {
+ status = STATUS_NOT_SUPPORTED;
+ goto Exit;
+ }
+
+ //
+ // Request validation.
+ // Note that InputBufferLength and IsFdo have been validated beforing entering this routine.
+ //
+
+ if (KeGetCurrentIrql() >= DISPATCH_LEVEL) {
+ NT_ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
+ status = STATUS_INVALID_LEVEL;
+ goto Exit;
+ }
+
+ // do not touch this buffer because it can still be used as input buffer for lower layer in 'SupportUnknown' case.
+ accessAlignment = (PSTORAGE_ACCESS_ALIGNMENT_DESCRIPTOR)Irp->AssociatedIrp.SystemBuffer;
+
+ length = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
+
+ if (length < sizeof(STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR)) {
+
+ if (length >= sizeof(STORAGE_DESCRIPTOR_HEADER)) {
+
+ information = sizeof(STORAGE_DESCRIPTOR_HEADER);
+ accessAlignment->Version = sizeof(STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR);
+ accessAlignment->Size = sizeof(STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR);
+ status = STATUS_SUCCESS;
+ goto Exit;
+ }
+
+ status = STATUS_BUFFER_TOO_SMALL;
+ goto Exit;
+ }
+
+ // not support Cache Line,
+ // 'BytesPerCacheLine' and 'BytesOffsetForCacheAlignment' fields are zero-ed.
+
+ //
+ // note that 'Supported' case has been handled at the beginning of this function.
+ //
+ switch (fdoExtension->FunctionSupportInfo->LowerLayerSupport.AccessAlignmentProperty) {
+ case SupportUnknown: {
+ // send down request and wait for the request to complete.
+ status = ClassForwardIrpSynchronous(commonExtension, Irp);
+
+ if (ClasspLowerLayerNotSupport(status)) {
+ // case 1: the request is not supported by lower layer, sends down command
+ // some port drivers (or filter drivers) return STATUS_INVALID_DEVICE_REQUEST if a request is not supported.
+
+ // ClassReadCapacity16() will either return status from cached data or send command to retrieve the information.
+ if (ClasspIsObsoletePortDriver(fdoExtension) == FALSE) {
+ status = ClassReadCapacity16(fdoExtension, Srb);
+ } else {
+ fdoExtension->FunctionSupportInfo->ReadCapacity16Data.CommandStatus = status;
+ }
+
+ // data is ready in fdoExtension
+ // set the support status after the SCSI command is executed to avoid racing condition between multiple same type of requests.
+ fdoExtension->FunctionSupportInfo->LowerLayerSupport.AccessAlignmentProperty = NotSupported;
+
+ if (NT_SUCCESS(status)) {
+ // fill output buffer
+ RtlZeroMemory(accessAlignment, length);
+ accessAlignment->Version = sizeof(STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR);
+ accessAlignment->Size = sizeof(STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR);
+ accessAlignment->BytesPerLogicalSector = fdoExtension->FunctionSupportInfo->ReadCapacity16Data.BytesPerLogicalSector;
+ accessAlignment->BytesPerPhysicalSector = fdoExtension->FunctionSupportInfo->ReadCapacity16Data.BytesPerPhysicalSector;
+ accessAlignment->BytesOffsetForSectorAlignment = fdoExtension->FunctionSupportInfo->ReadCapacity16Data.BytesOffsetForSectorAlignment;
+
+ // set returned data length
+ information = sizeof(STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR);
+ } else {
+ information = 0;
+ }
+ goto Exit;
+ } else {
+ // case 2: the request is supported and it completes successfully
+ // case 3: the request is supported by lower stack but other failure status is returned.
+ // from now on, the same request will be send down to lower stack directly.
+ fdoExtension->FunctionSupportInfo->LowerLayerSupport.AccessAlignmentProperty = Supported;
+ information = (ULONG)Irp->IoStatus.Information;
+
+
+ goto Exit;
+ }
+ break;
+ }
+
+ case NotSupported: {
+
+ // ClassReadCapacity16() will either return status from cached data or send command to retrieve the information.
+ status = ClassReadCapacity16(fdoExtension, Srb);
+
+ if (NT_SUCCESS(status)) {
+ RtlZeroMemory(accessAlignment, length);
+ accessAlignment->Version = sizeof(STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR);
+ accessAlignment->Size = sizeof(STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR);
+ accessAlignment->BytesPerLogicalSector = fdoExtension->FunctionSupportInfo->ReadCapacity16Data.BytesPerLogicalSector;
+ accessAlignment->BytesPerPhysicalSector = fdoExtension->FunctionSupportInfo->ReadCapacity16Data.BytesPerPhysicalSector;
+ accessAlignment->BytesOffsetForSectorAlignment = fdoExtension->FunctionSupportInfo->ReadCapacity16Data.BytesOffsetForSectorAlignment;
+
+ information = sizeof(STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR);
+ } else {
+ information = 0;
+ }
+ goto Exit;
+
+ break;
+ }
+
+ case Supported: {
+ NT_ASSERT(FALSE); // this case is handled at the begining of the function.
+ status = STATUS_INTERNAL_ERROR;
+ break;
+ }
+
+ } // end of switch (fdoExtension->FunctionSupportInfo->LowerLayerSupport.AccessAlignmentProperty)
+
+Exit:
+
+ //
+ // Set the size and status in IRP
+ //
+ Irp->IoStatus.Information = information;
+ Irp->IoStatus.Status = status;
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+
+ return status;
+}
+
+NTSTATUS
+__inline
+IncursSeekPenalty (
+ _In_ USHORT MediumRotationRate,
+ _In_ PBOOLEAN IncursSeekPenalty
+ )
+{
+ NTSTATUS status;
+
+ if (MediumRotationRate == 0x0001) {
+ // Non-rotating media (e.g., solid state device)
+ *IncursSeekPenalty = FALSE;
+ status = STATUS_SUCCESS;
+ } else if ( (MediumRotationRate >= 0x401) &&
+ (MediumRotationRate <= 0xFFFE) ) {
+ // Nominal media rotation rate in rotations per minute (rpm)
+ *IncursSeekPenalty = TRUE;
+ status = STATUS_SUCCESS;
+ } else {
+ // Unknown cases:
+ // 0 - Rate not reported
+ // 0002h-0400h - Reserved
+ // FFFFh - Reserved
+ status = STATUS_UNSUCCESSFUL;
+ }
+
+ return status;
+}
+
+
+NTSTATUS
+ClasspDeviceMediaTypeProperty(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _Inout_ PIRP Irp,
+ _Inout_ PSCSI_REQUEST_BLOCK Srb
+ )
+/*++
+
+Routine Description:
+
+ This routine returns the medium product type reported by the device for the associated LU.
+
+ This function must be called at IRQL < DISPATCH_LEVEL.
+
+Arguments:
+
+ DeviceObject - Supplies the device object associated with this request
+ Irp - The IRP to be processed
+ Srb - The SRB associated with the request
+
+Return Value:
+
+ NTSTATUS code
+
+--*/
+{
+ NTSTATUS status = STATUS_UNSUCCESSFUL;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+ PSTORAGE_PROPERTY_QUERY query = (PSTORAGE_PROPERTY_QUERY)Irp->AssociatedIrp.SystemBuffer;
+ PSTORAGE_MEDIUM_PRODUCT_TYPE_DESCRIPTOR pDesc = (PSTORAGE_MEDIUM_PRODUCT_TYPE_DESCRIPTOR)Irp->AssociatedIrp.SystemBuffer;
+ PIO_STACK_LOCATION irpStack;
+ ULONG length = 0;
+ ULONG information = 0;
+
+ irpStack = IoGetCurrentIrpStackLocation(Irp);
+
+ TracePrint((TRACE_LEVEL_VERBOSE,
+ TRACE_FLAG_IOCTL,
+ "ClasspDeviceMediaTypeProperty (%p): Entering function.\n",
+ DeviceObject));
+
+ //
+ // Check proper query type.
+ //
+ if (query->QueryType == PropertyExistsQuery) {
+
+ //
+ // In order to maintain consistency with the how the rest of the properties
+ // are handled, always return success for PropertyExistsQuery.
+ //
+ status = STATUS_SUCCESS;
+ goto __ClasspDeviceMediaTypeProperty_Exit;
+
+ } else if (query->QueryType != PropertyStandardQuery) {
+
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClasspDeviceMediaTypeProperty (%p): Unsupported query type %x for media type property.\n",
+ DeviceObject,
+ query->QueryType));
+
+ status = STATUS_NOT_SUPPORTED;
+ goto __ClasspDeviceMediaTypeProperty_Exit;
+ }
+
+ //
+ // Validate the request.
+ // InputBufferLength and IsFdo have already been validated.
+ //
+
+ if (KeGetCurrentIrql() >= DISPATCH_LEVEL) {
+
+ NT_ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
+
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClasspDeviceMediaTypeProperty (%p): Query property for media type at incorrect IRQL.\n",
+ DeviceObject));
+
+ status = STATUS_INVALID_LEVEL;
+ goto __ClasspDeviceMediaTypeProperty_Exit;
+ }
+
+ length = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
+
+ if (length >= sizeof(STORAGE_DESCRIPTOR_HEADER)) {
+
+ information = sizeof(STORAGE_MEDIUM_PRODUCT_TYPE_DESCRIPTOR);
+ pDesc->Version = sizeof(STORAGE_MEDIUM_PRODUCT_TYPE_DESCRIPTOR);
+ pDesc->Size = sizeof(STORAGE_MEDIUM_PRODUCT_TYPE_DESCRIPTOR);
+ } else {
+
+ status = STATUS_BUFFER_TOO_SMALL;
+ goto __ClasspDeviceMediaTypeProperty_Exit;
+ }
+
+ if (length < sizeof(STORAGE_MEDIUM_PRODUCT_TYPE_DESCRIPTOR)) {
+
+ status = STATUS_SUCCESS;
+ goto __ClasspDeviceMediaTypeProperty_Exit;
+ }
+
+ //
+ // Only query BlockDeviceCharacteristics VPD page if device support has been confirmed.
+ //
+ if (fdoExtension->FunctionSupportInfo->ValidInquiryPages.BlockDeviceCharacteristics == TRUE) {
+ status = ClasspDeviceGetBlockDeviceCharacteristicsVPDPage(fdoExtension, Srb);
+ } else {
+ //
+ // Otherwise device was previously found lacking support for this VPD page. Fail the request.
+ //
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ goto __ClasspDeviceMediaTypeProperty_Exit;
+ }
+
+ if (!NT_SUCCESS(status)) {
+
+ status = fdoExtension->FunctionSupportInfo->DeviceCharacteristicsData.CommandStatus;
+ information = 0;
+
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClasspDeviceGetBlockDeviceCharacteristicsVPDPage (%p): VPD retrieval fails with %x.\n",
+ DeviceObject,
+ status));
+
+ goto __ClasspDeviceMediaTypeProperty_Exit;
+ }
+
+ //
+ // Fill in the output buffer. All data is copied from the FDO extension, cached
+ // from device response to earlier VPD_BLOCK_DEVICE_CHARACTERISTICS query.
+ //
+ pDesc->MediumProductType = fdoExtension->FunctionSupportInfo->DeviceCharacteristicsData.MediumProductType;
+ status = STATUS_SUCCESS;
+
+__ClasspDeviceMediaTypeProperty_Exit:
+
+ //
+ // Set the size and status in IRP
+ //
+ Irp->IoStatus.Information = information;
+ Irp->IoStatus.Status = status;
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+
+ TracePrint((TRACE_LEVEL_VERBOSE,
+ TRACE_FLAG_IOCTL,
+ "ClasspDeviceMediaTypeProperty (%p): Exiting function with status %x.\n",
+ DeviceObject,
+ status));
+
+ return status;
+}
+
+NTSTATUS ClasspDeviceGetBlockDeviceCharacteristicsVPDPage(
+ _In_ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension,
+ _In_ PSCSI_REQUEST_BLOCK Srb
+ )
+/*
+Routine Description:
+
+ This function sends an INQUIRY command request for VPD_BLOCK_DEVICE_CHARACTERISTICS to
+ the device. Relevant data from the response is cached in the FDO extension.
+
+Arguments:
+ FdoExtension: The FDO extension of the device to which the INQUIRY command will be sent.
+ Srb: Allocated by the caller.
+ SrbSize: The size of the Srb buffer in bytes.
+
+Return Value:
+
+ STATUS_INVALID_PARAMETER: May be returned if the LogPage buffer is NULL or
+ not large enough.
+ STATUS_SUCCESS: The log page was obtained and placed in the LogPage buffer.
+
+ This function may return other NTSTATUS codes from internal function calls.
+--*/
+{
+ NTSTATUS status = STATUS_UNSUCCESSFUL;
+ PCDB cdb;
+ UCHAR bufferLength = sizeof(VPD_BLOCK_DEVICE_CHARACTERISTICS_PAGE); // data is 64 bytes
+ ULONG allocationBufferLength = bufferLength;
+ PVPD_BLOCK_DEVICE_CHARACTERISTICS_PAGE dataBuffer = NULL;
+
+
+#if defined(_ARM_) || defined(_ARM64_)
+ //
+ // ARM has specific alignment requirements, although this will not have a functional impact on x86 or amd64
+ // based platforms. We are taking the conservative approach here.
+ //
+ allocationBufferLength = ALIGN_UP_BY(allocationBufferLength,KeGetRecommendedSharedDataAlignment());
+ dataBuffer = (PVPD_BLOCK_DEVICE_CHARACTERISTICS_PAGE)ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
+ allocationBufferLength,
+ '5CcS'
+ );
+#else
+
+ dataBuffer = (PVPD_BLOCK_DEVICE_CHARACTERISTICS_PAGE)ExAllocatePoolWithTag(NonPagedPoolNx,
+ bufferLength,
+ '5CcS'
+ );
+#endif
+ if (dataBuffer == NULL) {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Exit;
+ }
+
+ RtlZeroMemory(dataBuffer, allocationBufferLength);
+
+ // prepare the Srb
+ SrbSetTimeOutValue(Srb, fdoExtension->TimeOutValue);
+ SrbSetRequestTag(Srb, SP_UNTAGGED);
+ SrbSetRequestAttribute(Srb, SRB_SIMPLE_TAG_REQUEST);
+ SrbAssignSrbFlags(Srb, fdoExtension->SrbFlags);
+
+ SrbSetCdbLength(Srb, 6);
+
+ cdb = SrbGetCdb(Srb);
+ cdb->CDB6INQUIRY3.OperationCode = SCSIOP_INQUIRY;
+ cdb->CDB6INQUIRY3.EnableVitalProductData = 1; //EVPD bit
+ cdb->CDB6INQUIRY3.PageCode = VPD_BLOCK_DEVICE_CHARACTERISTICS;
+ cdb->CDB6INQUIRY3.AllocationLength = bufferLength; //AllocationLength field in CDB6INQUIRY3 is only one byte.
+
+ status = ClassSendSrbSynchronous(fdoExtension->CommonExtension.DeviceObject,
+ Srb,
+ dataBuffer,
+ allocationBufferLength,
+ FALSE);
+ if (NT_SUCCESS(status)) {
+ if (SrbGetDataTransferLength(Srb) < 0x8) {
+ // the device should return at least 8 bytes of data for use.
+ status = STATUS_UNSUCCESSFUL;
+ } else if ( (dataBuffer->PageLength != 0x3C) || (dataBuffer->PageCode != VPD_BLOCK_DEVICE_CHARACTERISTICS) ) {
+ // 'PageLength' shall be 0x3C; and 'PageCode' shall match.
+ status = STATUS_UNSUCCESSFUL;
+ } else {
+ // cache data into fdoExtension
+ fdoExtension->FunctionSupportInfo->DeviceCharacteristicsData.MediumRotationRate = (dataBuffer->MediumRotationRateMsb << 8) |
+ dataBuffer->MediumRotationRateLsb;
+ fdoExtension->FunctionSupportInfo->DeviceCharacteristicsData.MediumProductType = dataBuffer->MediumProductType;
+ fdoExtension->FunctionSupportInfo->DeviceCharacteristicsData.NominalFormFactor = dataBuffer->NominalFormFactor;
+ }
+ } else {
+ // the command failed, surface up the command error from 'status' variable. Nothing to do here.
+ }
+
+Exit:
+ if (dataBuffer != NULL) {
+ ExFreePool(dataBuffer);
+ }
+
+ return status;
+}
+
+NTSTATUS ClasspDeviceSeekPenaltyProperty(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _In_ PIRP Irp,
+ _Inout_ PSCSI_REQUEST_BLOCK Srb
+ )
+/*
+ At first time of receiving the request, this function will forward it to lower stack to determine if it's supportted.
+ If it's not supported, INQUIRY (Block Device Characteristics VPD page) will be sent down to retrieve the information.
+*/
+{
+ NTSTATUS status = STATUS_UNSUCCESSFUL;
+
+ PCOMMON_DEVICE_EXTENSION commonExtension = (PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+ PSTORAGE_PROPERTY_QUERY query = (PSTORAGE_PROPERTY_QUERY)Irp->AssociatedIrp.SystemBuffer;
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
+ ULONG length = 0;
+ ULONG information = 0;
+ BOOLEAN incursSeekPenalty = TRUE;
+ PDEVICE_SEEK_PENALTY_DESCRIPTOR seekPenalty;
+
+ if ( (DeviceObject->DeviceType != FILE_DEVICE_DISK) ||
+ (TEST_FLAG(DeviceObject->Characteristics, FILE_FLOPPY_DISKETTE)) ||
+ (fdoExtension->FunctionSupportInfo->LowerLayerSupport.SeekPenaltyProperty == Supported) ) {
+ // if it's not disk, forward the request to lower layer,
+ // if the IOCTL is supported by lower stack, forward it down.
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+ return status;
+ }
+
+ //
+ // Check proper query type.
+ //
+
+ if (query->QueryType == PropertyExistsQuery) {
+ status = STATUS_SUCCESS;
+ goto Exit;
+ } else if (query->QueryType != PropertyStandardQuery) {
+ status = STATUS_NOT_SUPPORTED;
+ goto Exit;
+ }
+
+ //
+ // Request validation.
+ // Note that InputBufferLength and IsFdo have been validated beforing entering this routine.
+ //
+
+ if (KeGetCurrentIrql() >= DISPATCH_LEVEL) {
+ NT_ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
+ status = STATUS_INVALID_LEVEL;
+ goto Exit;
+ }
+
+ // do not touch this buffer because it can still be used as input buffer for lower layer in 'SupportUnknown' case.
+ seekPenalty = (PDEVICE_SEEK_PENALTY_DESCRIPTOR)Irp->AssociatedIrp.SystemBuffer;
+
+ length = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
+
+ if (length < sizeof(DEVICE_SEEK_PENALTY_DESCRIPTOR)) {
+
+ if (length >= sizeof(STORAGE_DESCRIPTOR_HEADER)) {
+
+ information = sizeof(STORAGE_DESCRIPTOR_HEADER);
+ seekPenalty->Version = sizeof(DEVICE_SEEK_PENALTY_DESCRIPTOR);
+ seekPenalty->Size = sizeof(DEVICE_SEEK_PENALTY_DESCRIPTOR);
+ status = STATUS_SUCCESS;
+ goto Exit;
+ }
+
+ status = STATUS_BUFFER_TOO_SMALL;
+ goto Exit;
+ }
+
+ //
+ // note that 'Supported' case has been handled at the beginning of this function.
+ //
+ switch (fdoExtension->FunctionSupportInfo->LowerLayerSupport.SeekPenaltyProperty) {
+ case SupportUnknown: {
+ // send down request and wait for the request to complete.
+ status = ClassForwardIrpSynchronous(commonExtension, Irp);
+
+ if (ClasspLowerLayerNotSupport(status)) {
+ // case 1: the request is not supported by lower layer, sends down command
+ // some port drivers (or filter drivers) return STATUS_INVALID_DEVICE_REQUEST if a request is not supported.
+
+ // send INQUIRY command if the VPD page is supported.
+ if (fdoExtension->FunctionSupportInfo->ValidInquiryPages.BlockDeviceCharacteristics == TRUE) {
+ status = ClasspDeviceGetBlockDeviceCharacteristicsVPDPage(fdoExtension, Srb);
+ } else {
+ // the INQUIRY - VPD page command to discover the info is not supported, fail the request.
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ }
+
+ if (NT_SUCCESS(status)) {
+ status = IncursSeekPenalty(fdoExtension->FunctionSupportInfo->DeviceCharacteristicsData.MediumRotationRate, &incursSeekPenalty);
+ }
+
+ fdoExtension->FunctionSupportInfo->DeviceCharacteristicsData.CommandStatus = status;
+
+ // data is ready in fdoExtension
+ // set the support status after the SCSI command is executed to avoid racing condition between multiple same type of requests.
+ fdoExtension->FunctionSupportInfo->LowerLayerSupport.SeekPenaltyProperty = NotSupported;
+
+ // fill output buffer
+ if (NT_SUCCESS(status)) {
+ RtlZeroMemory(seekPenalty, length);
+ seekPenalty->Version = sizeof(DEVICE_SEEK_PENALTY_DESCRIPTOR);
+ seekPenalty->Size = sizeof(DEVICE_SEEK_PENALTY_DESCRIPTOR);
+ seekPenalty->IncursSeekPenalty = incursSeekPenalty;
+ information = sizeof(DEVICE_SEEK_PENALTY_DESCRIPTOR);
+
+
+ } else {
+ information = 0;
+ }
+
+ } else {
+ // case 2: the request is supported and it completes successfully
+ // case 3: the request is supported by lower stack but other failure status is returned.
+ // from now on, the same request will be send down to lower stack directly.
+ fdoExtension->FunctionSupportInfo->LowerLayerSupport.SeekPenaltyProperty = Supported;
+ information = (ULONG)Irp->IoStatus.Information;
+
+ }
+
+
+ goto Exit;
+
+ break;
+ }
+
+ case NotSupported: {
+ status = fdoExtension->FunctionSupportInfo->DeviceCharacteristicsData.CommandStatus;
+
+ if (NT_SUCCESS(status)) {
+ status = IncursSeekPenalty(fdoExtension->FunctionSupportInfo->DeviceCharacteristicsData.MediumRotationRate, &incursSeekPenalty);
+ }
+
+ if (NT_SUCCESS(status)) {
+ RtlZeroMemory(seekPenalty, length);
+ seekPenalty->Version = sizeof(DEVICE_SEEK_PENALTY_DESCRIPTOR);
+ seekPenalty->Size = sizeof(DEVICE_SEEK_PENALTY_DESCRIPTOR);
+ seekPenalty->IncursSeekPenalty = incursSeekPenalty;
+ information = sizeof(DEVICE_SEEK_PENALTY_DESCRIPTOR);
+
+ } else {
+ information = 0;
+ }
+
+ goto Exit;
+
+ break;
+ }
+
+ case Supported: {
+ NT_ASSERT(FALSE); // this case is handled at the begining of the function.
+ break;
+ }
+
+ } // end of switch (fdoExtension->FunctionSupportInfo->LowerLayerSupport.SeekPenaltyProperty)
+
+Exit:
+
+ //
+ // Set the size and status in IRP
+ //
+ Irp->IoStatus.Information = information;;
+ Irp->IoStatus.Status = status;
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+
+ return status;
+}
+
+NTSTATUS ClasspDeviceGetLBProvisioningVPDPage(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _Inout_opt_ PSCSI_REQUEST_BLOCK Srb
+ )
+{
+ NTSTATUS status = STATUS_UNSUCCESSFUL;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+ USHORT pageLength = 0;
+
+ PVOID dataBuffer = NULL;
+ UCHAR bufferLength = VPD_MAX_BUFFER_SIZE; // use biggest buffer possible
+ ULONG allocationBufferLength = bufferLength; // Since the CDB size may differ from the actual buffer allocation
+ PCDB cdb;
+ ULONG dataTransferLength = 0;
+ PVPD_LOGICAL_BLOCK_PROVISIONING_PAGE lbProvisioning = NULL;
+
+ //
+ // if the informaiton has been attempted to retrieve, return the cached status.
+ //
+ if (fdoExtension->FunctionSupportInfo->LBProvisioningData.CommandStatus != -1) {
+ // get cached NTSTATUS from previous call.
+ return fdoExtension->FunctionSupportInfo->LBProvisioningData.CommandStatus;
+ }
+
+ //
+ // Initialize LBProvisioningData fields to 'unsupported' defaults.
+ //
+ fdoExtension->FunctionSupportInfo->LBProvisioningData.ProvisioningType = PROVISIONING_TYPE_UNKNOWN;
+ fdoExtension->FunctionSupportInfo->LBProvisioningData.LBPRZ = FALSE;
+ fdoExtension->FunctionSupportInfo->LBProvisioningData.LBPU = FALSE;
+ fdoExtension->FunctionSupportInfo->LBProvisioningData.ANC_SUP = FALSE;
+ fdoExtension->FunctionSupportInfo->LBProvisioningData.ThresholdExponent = 0;
+
+ //
+ // Try to get the Thin Provisioning VPD page (0xB2), if it is supported.
+ //
+ if (fdoExtension->FunctionSupportInfo->ValidInquiryPages.LBProvisioning == TRUE &&
+ Srb != NULL)
+ {
+#if defined(_ARM_) || defined(_ARM64_)
+ //
+ // ARM has specific alignment requirements, although this will not have a functional impact on x86 or amd64
+ // based platforms. We are taking the conservative approach here.
+ //
+ //
+ allocationBufferLength = ALIGN_UP_BY(allocationBufferLength,KeGetRecommendedSharedDataAlignment());
+ dataBuffer = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned, allocationBufferLength,'0CcS');
+#else
+ dataBuffer = ExAllocatePoolWithTag(NonPagedPoolNx, bufferLength,'0CcS');
+#endif
+ if (dataBuffer == NULL) {
+ // return without updating FdoExtension->FunctionSupportInfo->LBProvisioningData.CommandStatus
+ // the field will remain value as "-1", so that the command will be attempted next time this function is called.
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Exit;
+ }
+
+ lbProvisioning = (PVPD_LOGICAL_BLOCK_PROVISIONING_PAGE)dataBuffer;
+
+ RtlZeroMemory(dataBuffer, allocationBufferLength);
+
+ if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ status = InitializeStorageRequestBlock((PSTORAGE_REQUEST_BLOCK)Srb,
+ STORAGE_ADDRESS_TYPE_BTL8,
+ CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE,
+ 1,
+ SrbExDataTypeScsiCdb16);
+ if (NT_SUCCESS(status)) {
+ ((PSTORAGE_REQUEST_BLOCK)Srb)->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
+ } else {
+ //
+ // Should not occur.
+ //
+ NT_ASSERT(FALSE);
+ }
+ } else {
+ RtlZeroMemory(Srb, sizeof(SCSI_REQUEST_BLOCK));
+ Srb->Length = sizeof(SCSI_REQUEST_BLOCK);
+ Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
+ status = STATUS_SUCCESS;
+ }
+
+ if (NT_SUCCESS(status)) {
+ // prepare the Srb
+ SrbSetTimeOutValue(Srb, fdoExtension->TimeOutValue);
+ SrbSetRequestTag(Srb, SP_UNTAGGED);
+ SrbSetRequestAttribute(Srb, SRB_SIMPLE_TAG_REQUEST);
+ SrbAssignSrbFlags(Srb, fdoExtension->SrbFlags);
+
+ SrbSetCdbLength(Srb, 6);
+
+ cdb = SrbGetCdb(Srb);
+ cdb->CDB6INQUIRY3.OperationCode = SCSIOP_INQUIRY;
+ cdb->CDB6INQUIRY3.EnableVitalProductData = 1; //EVPD bit
+ cdb->CDB6INQUIRY3.PageCode = VPD_LOGICAL_BLOCK_PROVISIONING;
+ cdb->CDB6INQUIRY3.AllocationLength = bufferLength; //AllocationLength field in CDB6INQUIRY3 is only one byte.
+
+ status = ClassSendSrbSynchronous(fdoExtension->DeviceObject,
+ Srb,
+ dataBuffer,
+ allocationBufferLength,
+ FALSE);
+
+ dataTransferLength = SrbGetDataTransferLength(Srb);
+ }
+
+ //
+ // Handle the case where we get back STATUS_DATA_OVERRUN b/c the input
+ // buffer was larger than necessary.
+ //
+ if (status == STATUS_DATA_OVERRUN && dataTransferLength < bufferLength)
+ {
+ status = STATUS_SUCCESS;
+ }
+
+ if (NT_SUCCESS(status)) {
+ REVERSE_BYTES_SHORT(&pageLength, &(lbProvisioning->PageLength));
+ }
+
+ if ( NT_SUCCESS(status) &&
+ ((dataTransferLength < 0x08) ||
+ (pageLength < (FIELD_OFFSET(VPD_LOGICAL_BLOCK_PROVISIONING_PAGE, Reserved2) - FIELD_OFFSET(VPD_LOGICAL_BLOCK_PROVISIONING_PAGE,ThresholdExponent))) ||
+ (lbProvisioning->PageCode != VPD_LOGICAL_BLOCK_PROVISIONING)) ) {
+ // the device should return at least 8 bytes of data for use.
+ // 'PageCode' shall match and we need all the relevant data after the header.
+ status = STATUS_INFO_LENGTH_MISMATCH;
+ }
+
+ //
+ // Fill in the FDO extension with either the data from the VPD page, or
+ // use defaults if there was an error.
+ //
+ if (NT_SUCCESS(status))
+ {
+ fdoExtension->FunctionSupportInfo->LBProvisioningData.ProvisioningType = lbProvisioning->ProvisioningType;
+ fdoExtension->FunctionSupportInfo->LBProvisioningData.LBPRZ = lbProvisioning->LBPRZ;
+ fdoExtension->FunctionSupportInfo->LBProvisioningData.LBPU = lbProvisioning->LBPU;
+ fdoExtension->FunctionSupportInfo->LBProvisioningData.ANC_SUP = lbProvisioning->ANC_SUP;
+ fdoExtension->FunctionSupportInfo->LBProvisioningData.ThresholdExponent = lbProvisioning->ThresholdExponent;
+
+ TracePrint((TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_PNP,
+ "ClasspDeviceGetLBProvisioningVPDPage (%p): %s %s (rev %s) reported following parameters: \
+ \n\t\t\tProvisioningType: %u \
+ \n\t\t\tLBPRZ: %u \
+ \n\t\t\tLBPU: %u \
+ \n\t\t\tANC_SUP: %I64u \
+ \n\t\t\tThresholdExponent: %u\n",
+ DeviceObject,
+ (PCSZ)(((PUCHAR)fdoExtension->DeviceDescriptor) + fdoExtension->DeviceDescriptor->VendorIdOffset),
+ (PCSZ)(((PUCHAR)fdoExtension->DeviceDescriptor) + fdoExtension->DeviceDescriptor->ProductIdOffset),
+ (PCSZ)(((PUCHAR)fdoExtension->DeviceDescriptor) + fdoExtension->DeviceDescriptor->ProductRevisionOffset),
+ lbProvisioning->ProvisioningType,
+ lbProvisioning->LBPRZ,
+ lbProvisioning->LBPU,
+ lbProvisioning->ANC_SUP,
+ lbProvisioning->ThresholdExponent));
+ }
+ } else {
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ }
+
+ fdoExtension->FunctionSupportInfo->LBProvisioningData.CommandStatus = status;
+
+Exit:
+ FREE_POOL(dataBuffer);
+
+ return status;
+}
+
+
+NTSTATUS ClasspDeviceGetBlockLimitsVPDPage(
+ _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ _Inout_bytecount_(SrbSize) PSCSI_REQUEST_BLOCK Srb,
+ _In_ ULONG SrbSize,
+ _Out_ PCLASS_VPD_B0_DATA BlockLimitsData
+ )
+{
+ NTSTATUS status = STATUS_UNSUCCESSFUL;
+ PVOID dataBuffer = NULL;
+ UCHAR bufferLength = VPD_MAX_BUFFER_SIZE; // use biggest buffer possible
+ ULONG allocationBufferLength = bufferLength;
+ PCDB cdb;
+ PVPD_BLOCK_LIMITS_PAGE blockLimits = NULL;
+ ULONG dataTransferLength = 0;
+
+ //
+ // Set default values for UNMAP parameters based upon UNMAP support or lack
+ // thereof.
+ //
+ if (FdoExtension->FunctionSupportInfo->LBProvisioningData.LBPU) {
+ //
+ // If UNMAP is supported, we default to the maximum LBA count and
+ // block descriptor count. We also default the UNMAP granularity to
+ // a single block and specify no granularity alignment.
+ //
+ BlockLimitsData->MaxUnmapLbaCount = (ULONG)-1;
+ BlockLimitsData->MaxUnmapBlockDescrCount = (ULONG)-1;
+ BlockLimitsData->OptimalUnmapGranularity = 1;
+ BlockLimitsData->UnmapGranularityAlignment = 0;
+ BlockLimitsData->UGAVALID = FALSE;
+ } else {
+ BlockLimitsData->MaxUnmapLbaCount = 0;
+ BlockLimitsData->MaxUnmapBlockDescrCount = 0;
+ BlockLimitsData->OptimalUnmapGranularity = 0;
+ BlockLimitsData->UnmapGranularityAlignment = 0;
+ BlockLimitsData->UGAVALID = FALSE;
+ }
+
+ //
+ // Try to get the Block Limits VPD page (0xB0), if it is supported.
+ //
+ if (FdoExtension->FunctionSupportInfo->ValidInquiryPages.BlockLimits == TRUE)
+ {
+#if defined(_ARM_) || defined(_ARM64_)
+ //
+ // ARM has specific alignment requirements, although this will not have a functional impact on x86 or amd64
+ // based platforms. We are taking the conservative approach here.
+ //
+ allocationBufferLength = ALIGN_UP_BY(allocationBufferLength, KeGetRecommendedSharedDataAlignment());
+ dataBuffer = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned, allocationBufferLength, '0CcS');
+#else
+ dataBuffer = ExAllocatePoolWithTag(NonPagedPoolNx, bufferLength, '0CcS');
+#endif
+ if (dataBuffer == NULL)
+ {
+ // return without updating FdoExtension->FunctionSupportInfo->BlockLimitsData.CommandStatus
+ // the field will remain value as "-1", so that the command will be attempted next time this function is called.
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Exit;
+ }
+
+ blockLimits = (PVPD_BLOCK_LIMITS_PAGE)dataBuffer;
+
+ RtlZeroMemory(dataBuffer, allocationBufferLength);
+
+ if (FdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+
+ #pragma prefast(suppress:26015, "InitializeStorageRequestBlock ensures buffer access is bounded")
+ status = InitializeStorageRequestBlock((PSTORAGE_REQUEST_BLOCK)Srb,
+ STORAGE_ADDRESS_TYPE_BTL8,
+ SrbSize,
+ 1,
+ SrbExDataTypeScsiCdb16);
+
+ if (NT_SUCCESS(status)) {
+ ((PSTORAGE_REQUEST_BLOCK)Srb)->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
+ } else {
+ //
+ // Should not occur.
+ //
+ NT_ASSERT(FALSE);
+ }
+ } else {
+ RtlZeroMemory(Srb, sizeof(SCSI_REQUEST_BLOCK));
+ Srb->Length = sizeof(SCSI_REQUEST_BLOCK);
+ Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
+ status = STATUS_SUCCESS;
+ }
+
+ if (NT_SUCCESS(status)) {
+ // prepare the Srb
+ SrbSetTimeOutValue(Srb, FdoExtension->TimeOutValue);
+ SrbSetRequestTag(Srb, SP_UNTAGGED);
+ SrbSetRequestAttribute(Srb, SRB_SIMPLE_TAG_REQUEST);
+ SrbAssignSrbFlags(Srb, FdoExtension->SrbFlags);
+
+ SrbSetCdbLength(Srb, 6);
+
+ cdb = SrbGetCdb(Srb);
+ cdb->CDB6INQUIRY3.OperationCode = SCSIOP_INQUIRY;
+ cdb->CDB6INQUIRY3.EnableVitalProductData = 1; //EVPD bit
+ cdb->CDB6INQUIRY3.PageCode = VPD_BLOCK_LIMITS;
+ cdb->CDB6INQUIRY3.AllocationLength = bufferLength; //AllocationLength field in CDB6INQUIRY3 is only one byte.
+
+ status = ClassSendSrbSynchronous(FdoExtension->DeviceObject,
+ Srb,
+ dataBuffer,
+ allocationBufferLength,
+ FALSE);
+ dataTransferLength = SrbGetDataTransferLength(Srb);
+ }
+
+ //
+ // Handle the case where we get back STATUS_DATA_OVERRUN b/c the input
+ // buffer was larger than necessary.
+ //
+
+ if (status == STATUS_DATA_OVERRUN && dataTransferLength < bufferLength)
+ {
+ status = STATUS_SUCCESS;
+ }
+
+ if (NT_SUCCESS(status))
+ {
+ USHORT pageLength;
+ REVERSE_BYTES_SHORT(&pageLength, &(blockLimits->PageLength));
+
+ //
+ // Regardless of the device's support for unmap, cache away at least the basic block limits information
+ //
+ if (dataTransferLength >= 0x10 && blockLimits->PageCode == VPD_BLOCK_LIMITS) {
+
+ // (6:7) OPTIMAL TRANSFER LENGTH GRANULARITY
+ REVERSE_BYTES_SHORT(&BlockLimitsData->OptimalTransferLengthGranularity, &blockLimits->OptimalTransferLengthGranularity);
+ // (8:11) MAXIMUM TRANSFER LENGTH
+ REVERSE_BYTES(&BlockLimitsData->MaximumTransferLength, &blockLimits->MaximumTransferLength);
+ // (12:15) OPTIMAL TRANSFER LENGTH
+ REVERSE_BYTES(&BlockLimitsData->OptimalTransferLength, &blockLimits->OptimalTransferLength);
+ }
+
+ if ((dataTransferLength < 0x24) ||
+ (pageLength < (FIELD_OFFSET(VPD_BLOCK_LIMITS_PAGE,Reserved1) - FIELD_OFFSET(VPD_BLOCK_LIMITS_PAGE,Reserved0))) ||
+ (blockLimits->PageCode != VPD_BLOCK_LIMITS))
+ {
+ // the device should return at least 36 bytes of data for use.
+ // 'PageCode' shall match and we need all the relevant data after the header.
+ status = STATUS_INFO_LENGTH_MISMATCH;
+ }
+ }
+
+ if (NT_SUCCESS(status))
+ {
+ // cache data into FdoExtension
+ // (20:23) MAXIMUM UNMAP LBA COUNT
+ REVERSE_BYTES(&BlockLimitsData->MaxUnmapLbaCount, &blockLimits->MaximumUnmapLBACount);
+ // (24:27) MAXIMUM UNMAP BLOCK DESCRIPTOR COUNT
+ REVERSE_BYTES(&BlockLimitsData->MaxUnmapBlockDescrCount, &blockLimits->MaximumUnmapBlockDescriptorCount);
+ // (28:31) OPTIMAL UNMAP GRANULARITY
+ REVERSE_BYTES(&BlockLimitsData->OptimalUnmapGranularity, &blockLimits->OptimalUnmapGranularity);
+
+ // (32:35) UNMAP GRANULARITY ALIGNMENT; (32) bit7: UGAVALID
+ BlockLimitsData->UGAVALID = blockLimits->UGAValid;
+ if (BlockLimitsData->UGAVALID == TRUE) {
+ REVERSE_BYTES(&BlockLimitsData->UnmapGranularityAlignment, &blockLimits->UnmapGranularityAlignment);
+ BlockLimitsData->UnmapGranularityAlignment &= 0x7FFFFFFF; // remove value of UGAVALID bit.
+ } else {
+ BlockLimitsData->UnmapGranularityAlignment = 0;
+ }
+
+ TracePrint((TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_PNP,
+ "ClasspDeviceGetBlockLimitsVPDPage (%p): %s %s (rev %s) reported following parameters: \
+ \n\t\t\tOptimalTransferLengthGranularity: %u \
+ \n\t\t\tMaximumTransferLength: %u \
+ \n\t\t\tOptimalTransferLength: %u \
+ \n\t\t\tMaximumUnmapLBACount: %u \
+ \n\t\t\tMaximumUnmapBlockDescriptorCount: %u \
+ \n\t\t\tOptimalUnmapGranularity: %u \
+ \n\t\t\tUGAValid: %u \
+ \n\t\t\tUnmapGranularityAlignment: %u\n",
+ FdoExtension->DeviceObject,
+ (PCSZ)(((PUCHAR)FdoExtension->DeviceDescriptor) + FdoExtension->DeviceDescriptor->VendorIdOffset),
+ (PCSZ)(((PUCHAR)FdoExtension->DeviceDescriptor) + FdoExtension->DeviceDescriptor->ProductIdOffset),
+ (PCSZ)(((PUCHAR)FdoExtension->DeviceDescriptor) + FdoExtension->DeviceDescriptor->ProductRevisionOffset),
+ BlockLimitsData->OptimalTransferLengthGranularity,
+ BlockLimitsData->MaximumTransferLength,
+ BlockLimitsData->OptimalTransferLength,
+ BlockLimitsData->MaxUnmapLbaCount,
+ BlockLimitsData->MaxUnmapBlockDescrCount,
+ BlockLimitsData->OptimalUnmapGranularity,
+ BlockLimitsData->UGAVALID,
+ BlockLimitsData->UnmapGranularityAlignment));
+
+ }
+ } else {
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ }
+
+ BlockLimitsData->CommandStatus = status;
+
+Exit:
+ FREE_POOL(dataBuffer);
+
+ return status;
+}
+
+
+NTSTATUS ClasspDeviceTrimProperty(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _In_ PIRP Irp,
+ _Inout_ PSCSI_REQUEST_BLOCK Srb
+ )
+/*
+ At first time of receiving the request, this function will forward it to lower stack to determine if it's supportted.
+ If it's not supported, INQUIRY (Block Limits VPD page) will be sent down to retrieve the information.
+*/
+{
+ NTSTATUS status = STATUS_SUCCESS;
+
+ PCOMMON_DEVICE_EXTENSION commonExtension = (PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+ PSTORAGE_PROPERTY_QUERY query = (PSTORAGE_PROPERTY_QUERY)Irp->AssociatedIrp.SystemBuffer;
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
+ ULONG length = 0;
+ ULONG information = 0;
+
+ PDEVICE_TRIM_DESCRIPTOR trimDescr;
+
+ UNREFERENCED_PARAMETER(Srb);
+
+ if ( (DeviceObject->DeviceType != FILE_DEVICE_DISK) ||
+ (TEST_FLAG(DeviceObject->Characteristics, FILE_FLOPPY_DISKETTE)) ||
+ (fdoExtension->FunctionSupportInfo->LowerLayerSupport.TrimProperty == Supported) ) {
+ // if it's not disk, forward the request to lower layer,
+ // if the IOCTL is supported by lower stack, forward it down.
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+ return status;
+ }
+
+ //
+ // Check proper query type.
+ //
+
+ if (query->QueryType == PropertyExistsQuery) {
+ status = STATUS_SUCCESS;
+ goto Exit;
+ } else if (query->QueryType != PropertyStandardQuery) {
+ status = STATUS_NOT_SUPPORTED;
+ goto Exit;
+ }
+
+ //
+ // Request validation.
+ // Note that InputBufferLength and IsFdo have been validated beforing entering this routine.
+ //
+
+ if (KeGetCurrentIrql() >= DISPATCH_LEVEL) {
+ NT_ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
+ status = STATUS_INVALID_LEVEL;
+ goto Exit;
+ }
+
+ // do not touch this buffer because it can still be used as input buffer for lower layer in 'SupportUnknown' case.
+ trimDescr = (PDEVICE_TRIM_DESCRIPTOR)Irp->AssociatedIrp.SystemBuffer;
+
+ length = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
+
+ if (length < sizeof(DEVICE_TRIM_DESCRIPTOR)) {
+
+ if (length >= sizeof(STORAGE_DESCRIPTOR_HEADER)) {
+
+ information = sizeof(STORAGE_DESCRIPTOR_HEADER);
+ trimDescr->Version = sizeof(DEVICE_TRIM_DESCRIPTOR);
+ trimDescr->Size = sizeof(DEVICE_TRIM_DESCRIPTOR);
+ status = STATUS_SUCCESS;
+ goto Exit;
+ }
+
+ status = STATUS_BUFFER_TOO_SMALL;
+ goto Exit;
+ }
+
+ //
+ // note that 'Supported' case has been handled at the beginning of this function.
+ //
+ switch (fdoExtension->FunctionSupportInfo->LowerLayerSupport.TrimProperty) {
+ case SupportUnknown: {
+ // send down request and wait for the request to complete.
+ status = ClassForwardIrpSynchronous(commonExtension, Irp);
+
+ if ( (status == STATUS_NOT_SUPPORTED) ||
+ (status == STATUS_NOT_IMPLEMENTED) ||
+ (status == STATUS_INVALID_DEVICE_REQUEST) ||
+ (status == STATUS_INVALID_PARAMETER_1) ) {
+ // case 1: the request is not supported by lower layer, sends down command
+ // some port drivers (or filter drivers) return STATUS_INVALID_DEVICE_REQUEST if a request is not supported.
+ status = fdoExtension->FunctionSupportInfo->LBProvisioningData.CommandStatus;
+ NT_ASSERT(status != -1);
+
+ // data is ready in fdoExtension
+ // set the support status after the SCSI command is executed to avoid racing condition between multiple same type of requests.
+ fdoExtension->FunctionSupportInfo->LowerLayerSupport.TrimProperty = NotSupported;
+
+ if (NT_SUCCESS(status)) {
+ // fill output buffer
+ RtlZeroMemory(trimDescr, length);
+ trimDescr->Version = sizeof(DEVICE_TRIM_DESCRIPTOR);
+ trimDescr->Size = sizeof(DEVICE_TRIM_DESCRIPTOR);
+ trimDescr->TrimEnabled = ClasspSupportsUnmap(fdoExtension->FunctionSupportInfo);
+
+ // set returned data length
+ information = sizeof(DEVICE_TRIM_DESCRIPTOR);
+ } else {
+ // there was error retrieving TrimProperty. Surface the error up from 'status' variable.
+ information = 0;
+ }
+ goto Exit;
+ } else {
+ // case 2: the request is supported and it completes successfully
+ // case 3: the request is supported by lower stack but other failure status is returned.
+ // from now on, the same request will be send down to lower stack directly.
+ fdoExtension->FunctionSupportInfo->LowerLayerSupport.TrimProperty = Supported;
+ information = (ULONG)Irp->IoStatus.Information;
+ goto Exit;
+ }
+ break;
+ }
+
+ case NotSupported: {
+ status = fdoExtension->FunctionSupportInfo->LBProvisioningData.CommandStatus;
+ NT_ASSERT(status != -1);
+
+ if (NT_SUCCESS(status)) {
+ RtlZeroMemory(trimDescr, length);
+ trimDescr->Version = sizeof(DEVICE_TRIM_DESCRIPTOR);
+ trimDescr->Size = sizeof(DEVICE_TRIM_DESCRIPTOR);
+ trimDescr->TrimEnabled = ClasspSupportsUnmap(fdoExtension->FunctionSupportInfo);
+
+ information = sizeof(DEVICE_TRIM_DESCRIPTOR);
+ } else {
+ information = 0;
+ }
+ goto Exit;
+
+ break;
+ }
+
+ case Supported: {
+ NT_ASSERT(FALSE); // this case is handled at the begining of the function.
+ break;
+ }
+
+ } // end of switch (fdoExtension->FunctionSupportInfo->LowerLayerSupport.TrimProperty)
+
+Exit:
+
+ //
+ // Set the size and status in IRP
+ //
+ Irp->IoStatus.Information = information;
+ Irp->IoStatus.Status = status;
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+
+ return status;
+}
+
+NTSTATUS ClasspDeviceLBProvisioningProperty(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _Inout_ PIRP Irp,
+ _Inout_ PSCSI_REQUEST_BLOCK Srb
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ NTSTATUS blockLimitsStatus;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+ PSTORAGE_PROPERTY_QUERY query = (PSTORAGE_PROPERTY_QUERY)Irp->AssociatedIrp.SystemBuffer;
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
+ ULONG length = 0;
+ ULONG information = 0;
+ CLASS_VPD_B0_DATA blockLimitsData;
+ ULONG generationCount;
+
+ PDEVICE_LB_PROVISIONING_DESCRIPTOR lbpDescr;
+
+ UNREFERENCED_PARAMETER(Srb);
+
+ //
+ // Check proper query type.
+ //
+ if (query->QueryType == PropertyExistsQuery) {
+ status = STATUS_SUCCESS;
+ goto Exit;
+ } else if (query->QueryType != PropertyStandardQuery) {
+ status = STATUS_NOT_SUPPORTED;
+ goto Exit;
+ }
+
+ //
+ // Request validation.
+ // Note that InputBufferLength and IsFdo have been validated beforing entering this routine.
+ //
+
+ if (KeGetCurrentIrql() >= DISPATCH_LEVEL) {
+ NT_ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
+ status = STATUS_INVALID_LEVEL;
+ goto Exit;
+ }
+
+ lbpDescr = (PDEVICE_LB_PROVISIONING_DESCRIPTOR)Irp->AssociatedIrp.SystemBuffer;
+
+ length = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
+
+ RtlZeroMemory(lbpDescr, length);
+
+ if (length < DEVICE_LB_PROVISIONING_DESCRIPTOR_V1_SIZE) {
+
+ if (length >= sizeof(STORAGE_DESCRIPTOR_HEADER)) {
+
+ information = sizeof(STORAGE_DESCRIPTOR_HEADER);
+ lbpDescr->Version = sizeof(DEVICE_LB_PROVISIONING_DESCRIPTOR);
+ lbpDescr->Size = sizeof(DEVICE_LB_PROVISIONING_DESCRIPTOR);
+ status = STATUS_SUCCESS;
+ goto Exit;
+ }
+
+ status = STATUS_BUFFER_TOO_SMALL;
+ goto Exit;
+ }
+
+ //
+ // Set the structure version/size based upon the size of the given output
+ // buffer. We may be working with an older component that was built with
+ // the V1 structure definition.
+ //
+ if (length < sizeof(DEVICE_LB_PROVISIONING_DESCRIPTOR)) {
+ lbpDescr->Version = DEVICE_LB_PROVISIONING_DESCRIPTOR_V1_SIZE;
+ lbpDescr->Size = DEVICE_LB_PROVISIONING_DESCRIPTOR_V1_SIZE;
+ information = DEVICE_LB_PROVISIONING_DESCRIPTOR_V1_SIZE;
+ } else {
+ lbpDescr->Version = sizeof(DEVICE_LB_PROVISIONING_DESCRIPTOR);
+ lbpDescr->Size = sizeof(DEVICE_LB_PROVISIONING_DESCRIPTOR);
+ information = sizeof(DEVICE_LB_PROVISIONING_DESCRIPTOR);
+ }
+
+ //
+ // Take a snapshot of the block limits data since it can change.
+ // If we failed to get the block limits data, we'll just set the Optimal
+ // Unmap Granularity (and alignment) will default to 0. We don't want to
+ // fail the request outright since there is some non-block limits data that
+ // we can return.
+ //
+ blockLimitsStatus = ClasspBlockLimitsDataSnapshot(fdoExtension,
+ TRUE,
+ &blockLimitsData,
+ &generationCount);
+
+ //
+ // Fill in the output buffer. All data is copied from the FDO extension where we
+ // cached Logical Block Provisioning info when the device was first initialized.
+ //
+
+ lbpDescr->ThinProvisioningEnabled = ClasspIsThinProvisioned(fdoExtension->FunctionSupportInfo);
+
+ //
+ // Make sure we have a non-zero value for the number of bytes per block.
+ //
+ if (fdoExtension->DiskGeometry.BytesPerSector == 0)
+ {
+ status = ClassReadDriveCapacity(fdoExtension->DeviceObject);
+ if(!NT_SUCCESS(status) || fdoExtension->DiskGeometry.BytesPerSector == 0)
+ {
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ information = 0;
+ goto Exit;
+ }
+ }
+
+ lbpDescr->ThinProvisioningReadZeros = fdoExtension->FunctionSupportInfo->LBProvisioningData.LBPRZ;
+ lbpDescr->AnchorSupported = fdoExtension->FunctionSupportInfo->LBProvisioningData.ANC_SUP;
+
+ if (NT_SUCCESS(blockLimitsStatus)) {
+ lbpDescr->UnmapGranularityAlignmentValid = blockLimitsData.UGAVALID;
+
+ //
+ // Granularity and Alignment are given to us in units of blocks,
+ // but we convert and return them in bytes as it is more convenient
+ // to the caller.
+ //
+ lbpDescr->OptimalUnmapGranularity = (ULONGLONG)blockLimitsData.OptimalUnmapGranularity * fdoExtension->DiskGeometry.BytesPerSector;
+ lbpDescr->UnmapGranularityAlignment = (ULONGLONG)blockLimitsData.UnmapGranularityAlignment * fdoExtension->DiskGeometry.BytesPerSector;
+
+#if (NTDDI_VERSION >= NTDDI_WINBLUE)
+ //
+ // If the output buffer is large enough (i.e. not a V1 structure) copy
+ // over the max UNMAP LBA count and max UNMAP block descriptor count.
+ //
+ if (length >= sizeof(DEVICE_LB_PROVISIONING_DESCRIPTOR)) {
+ lbpDescr->MaxUnmapLbaCount = blockLimitsData.MaxUnmapLbaCount;
+ lbpDescr->MaxUnmapBlockDescriptorCount = blockLimitsData.MaxUnmapBlockDescrCount;
+ }
+#endif
+ }
+
+Exit:
+
+ //
+ // Set the size and status in IRP
+ //
+ Irp->IoStatus.Information = information;
+ Irp->IoStatus.Status = status;
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+
+ return status;
+}
+
+
+VOID
+ConvertDataSetRangeToUnmapBlockDescr(
+ _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ _In_ PUNMAP_BLOCK_DESCRIPTOR BlockDescr,
+ _Inout_ PULONG CurrentBlockDescrIndex,
+ _In_ ULONG MaxBlockDescrIndex,
+ _Inout_ PULONGLONG CurrentLbaCount,
+ _In_ ULONGLONG MaxLbaCount,
+ _Inout_ PDEVICE_DATA_SET_RANGE DataSetRange
+ )
+/*++
+
+Routine Description:
+
+ Convert DEVICE_DATA_SET_RANGE entry to be UNMAP_BLOCK_DESCRIPTOR entries.
+
+ As LengthInBytes field in DEVICE_DATA_SET_RANGE structure is 64 bits (bytes)
+ and LbaCount field in UNMAP_BLOCK_DESCRIPTOR structure is 32 bits (sectors),
+ it's possible that one DEVICE_DATA_SET_RANGE entry needs multiple UNMAP_BLOCK_DESCRIPTOR entries.
+ We must also take the unmap granularity into consideration and split up the
+ the given ranges so that they are aligned with the specified granularity.
+
+Arguments:
+ All arguments must be validated by the caller.
+
+ FdoExtension - The FDO extension of the device to which the unmap
+ command that will use the resulting unmap block descriptors will be
+ sent.
+ BlockDescr - Pointer to a buffer that will contain the unmap block
+ descriptors. This buffer should be allocated by the caller and the
+ caller should also ensure that it is large enough to contain all the
+ requested descriptors. Its size is implied by MaxBlockDescrIndex.
+ CurrentBlockDescrIndex - This contains the next block descriptor index to
+ be processed when this function returns. This function should be called
+ again with the same parameter to continue processing.
+ MaxBlockDescrIndex - This is the index of the last unmap block descriptor,
+ provided so that the function does not go off the end of BlockDescr.
+ CurrentLbaCount - This contains the number of LBAs left to be processed
+ when this function returns. This function should be called again with
+ the same parameter to continue processing.
+ MaxLbaCount - This is the max number of LBAs that can be sent in a single
+ unmap command.
+ DataSetRange - This range will be modified to reflect the un-converted part.
+ It must be valid (including being granularity-aligned) when it is first
+ passed to this function.
+
+Return Value:
+
+ Count of UNMAP_BLOCK_DESCRIPTOR entries converted.
+
+ NOTE: if LengthInBytes does not reach to 0, the conversion for DEVICE_DATA_SET_RANGE entry
+ is not completed. Further conversion is needed by calling this function again.
+
+--*/
+{
+
+ ULONGLONG startingSector;
+ ULONGLONG sectorCount;
+
+ TracePrint((TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_IOCTL,
+ "ConvertDataSetRangeToUnmapBlockDescr (%p): Generating UNMAP Block Descriptors from DataSetRange: \
+ \n\t\tStartingOffset = %I64u bytes \
+ \n\t\tLength = %I64u bytes\n",
+ FdoExtension->DeviceObject,
+ DataSetRange->StartingOffset,
+ DataSetRange->LengthInBytes));
+
+ while ( (DataSetRange->LengthInBytes > 0) &&
+ (*CurrentBlockDescrIndex < MaxBlockDescrIndex) &&
+ (*CurrentLbaCount < MaxLbaCount) ) {
+
+ //
+ // Convert the starting offset and length from bytes to blocks.
+ //
+ startingSector = (ULONGLONG)(DataSetRange->StartingOffset / FdoExtension->DiskGeometry.BytesPerSector);
+ sectorCount = (DataSetRange->LengthInBytes / FdoExtension->DiskGeometry.BytesPerSector);
+
+ //
+ // Make sure the sector count isn't more than can be specified with a
+ // single descriptor.
+ //
+ if (sectorCount > MAXULONG) {
+ sectorCount = MAXULONG;
+ }
+
+ //
+ // The max LBA count is the max number of LBAs that can be unmapped with
+ // a single UNMAP command. Make sure we don't exceed this value.
+ //
+ if ((*CurrentLbaCount + sectorCount) > MaxLbaCount) {
+ sectorCount = MaxLbaCount - *CurrentLbaCount;
+ }
+
+ REVERSE_BYTES_QUAD(BlockDescr[*CurrentBlockDescrIndex].StartingLba, &startingSector);
+ REVERSE_BYTES(BlockDescr[*CurrentBlockDescrIndex].LbaCount, (PULONG)&sectorCount);
+
+ DataSetRange->StartingOffset += sectorCount * FdoExtension->DiskGeometry.BytesPerSector;
+ DataSetRange->LengthInBytes -= sectorCount * FdoExtension->DiskGeometry.BytesPerSector;
+
+ *CurrentBlockDescrIndex += 1;
+ *CurrentLbaCount += (ULONG)sectorCount;
+
+ TracePrint((TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_IOCTL,
+ "ConvertDataSetRangeToUnmapBlockDescr (%p): Generated UNMAP Block Descriptor: \
+ \n\t\t\tStartingLBA = %I64u \
+ \n\t\t\tLBACount = %I64u\n",
+ FdoExtension->DeviceObject,
+ startingSector,
+ sectorCount));
+ }
+
+ return;
+}
+
+
+NTSTATUS
+DeviceProcessDsmTrimRequest(
+ _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ _In_ PDEVICE_DATA_SET_RANGE DataSetRanges,
+ _In_ ULONG DataSetRangesCount,
+ _In_ ULONG UnmapGranularity,
+ _In_ ULONG SrbFlags,
+ _Inout_ PSCSI_REQUEST_BLOCK Srb
+)
+/*++
+
+Routine Description:
+
+ Process TRIM request that received from upper layer.
+
+Arguments:
+
+ FdoExtension
+ DataSetRanges - this parameter must be already validated in caller.
+ DataSetRangesCount - this parameter must be already validated in caller.
+ UnmapGranularity - The unmap granularity in blocks. This is used to split
+ up the unmap command into chunks that are granularity-aligned.
+ Srb - The SRB to use for the unmap command. The caller must allocate it,
+ but this function will take care of initialzing it.
+
+Return Value:
+
+ status of the operation
+
+--*/
+{
+ NTSTATUS status = STATUS_SUCCESS;
+
+ PUNMAP_LIST_HEADER buffer = NULL;
+ PUNMAP_BLOCK_DESCRIPTOR blockDescrPointer;
+ ULONG bufferLength;
+ ULONG maxBlockDescrCount;
+ ULONG neededBlockDescrCount;
+ ULONG i;
+
+ BOOLEAN allDataSetRangeFullyConverted;
+ BOOLEAN needToSendCommand;
+ BOOLEAN tempDataSetRangeFullyConverted;
+
+ ULONG dataSetRangeIndex;
+ DEVICE_DATA_SET_RANGE tempDataSetRange;
+
+ ULONG blockDescrIndex;
+ ULONGLONG lbaCount;
+ ULONGLONG maxLbaCount;
+ ULONGLONG maxParameterListLength;
+
+ UNREFERENCED_PARAMETER(UnmapGranularity);
+
+ //
+ // The given LBA ranges are in DEVICE_DATA_SET_RANGE format and need to be converted into UNMAP Block Descriptors.
+ // The UNMAP command is able to carry 0xFFFF bytes (0xFFF8 in reality as there are 8 bytes of header plus n*16 bytes of Block Descriptors) of data.
+ // The actual size will also be constrained by the Maximum LBA Count and Maximum Transfer Length.
+ //
+
+ //
+ // 1.1 Calculate how many Block Descriptors are needed to complete this request.
+ //
+ neededBlockDescrCount = 0;
+ for (i = 0; i < DataSetRangesCount; i++) {
+ lbaCount = DataSetRanges[i].LengthInBytes / FdoExtension->DiskGeometry.BytesPerSector;
+
+ //
+ // 1.1.1 the UNMAP_BLOCK_DESCRIPTOR LbaCount is 32 bits, the max value is 0xFFFFFFFF
+ //
+ if (lbaCount > 0) {
+ neededBlockDescrCount += (ULONG)((lbaCount - 1) / MAXULONG + 1);
+ }
+ }
+
+ //
+ // Honor Max Unmap Block Descriptor Count if it has been specified. Otherwise,
+ // use the maximum value that the Parameter List Length field will allow (0xFFFF).
+ // If the count is 0xFFFFFFFF, then no maximum is specified.
+ //
+ if (FdoExtension->FunctionSupportInfo->BlockLimitsData.MaxUnmapBlockDescrCount != 0 &&
+ FdoExtension->FunctionSupportInfo->BlockLimitsData.MaxUnmapBlockDescrCount != MAXULONG)
+ {
+ maxParameterListLength = (ULONGLONG)(FdoExtension->FunctionSupportInfo->BlockLimitsData.MaxUnmapBlockDescrCount * sizeof(UNMAP_BLOCK_DESCRIPTOR))
+ + sizeof(UNMAP_LIST_HEADER);
+
+ //
+ // In the SBC-3, the Max Unmap Block Descriptor Count field in the 0xB0
+ // page is 4 bytes and the Parameter List Length in the UNMAP command is
+ // 2 bytes, therefore it is possible that the Max Unmap Block Descriptor
+ // Count could imply more bytes than can be specified in the Parameter
+ // List Length field. Adjust for that here.
+ //
+ maxParameterListLength = min(maxParameterListLength, MAXUSHORT);
+ }
+ else
+ {
+ maxParameterListLength = MAXUSHORT;
+ }
+
+ //
+ // 1.2 Calculate the buffer size needed, capped by the device's limitations.
+ //
+ bufferLength = min(FdoExtension->PrivateFdoData->HwMaxXferLen, (ULONG)maxParameterListLength);
+ bufferLength = min(bufferLength, (neededBlockDescrCount * sizeof(UNMAP_BLOCK_DESCRIPTOR) + sizeof(UNMAP_LIST_HEADER)));
+
+ maxBlockDescrCount = (bufferLength - sizeof(UNMAP_LIST_HEADER)) / sizeof(UNMAP_BLOCK_DESCRIPTOR);
+
+ if (maxBlockDescrCount == 0) {
+ //
+ // This shouldn't happen since we've already done validation.
+ //
+ TracePrint((TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_IOCTL,
+ "DeviceProcessDsmTrimRequest (%p): Max Block Descriptor count is Zero\n",
+ FdoExtension->DeviceObject));
+
+ NT_ASSERT(maxBlockDescrCount != 0);
+ status = STATUS_DATA_ERROR;
+ goto Exit;
+ }
+
+ //
+ // The Maximum LBA Count is set during device initialization.
+ //
+ maxLbaCount = (ULONGLONG)FdoExtension->FunctionSupportInfo->BlockLimitsData.MaxUnmapLbaCount;
+ if (maxLbaCount == 0) {
+ //
+ // This shouldn't happen since we've already done validation.
+ //
+ TracePrint((TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_IOCTL,
+ "DeviceProcessDsmTrimRequest (%p): Max LBA count is Zero\n",
+ FdoExtension->DeviceObject));
+
+ NT_ASSERT(maxLbaCount != 0);
+ status = STATUS_DATA_ERROR;
+ goto Exit;
+ }
+
+ //
+ // Finally, allocate the buffer we'll use to send the UNMAP command.
+ //
+
+#if defined(_ARM_) || defined(_ARM64_)
+ //
+ // ARM has specific alignment requirements, although this will not have a functional impact on x86 or amd64
+ // based platforms. We are taking the conservative approach here.
+ //
+ bufferLength = ALIGN_UP_BY(bufferLength,KeGetRecommendedSharedDataAlignment());
+ buffer = (PUNMAP_LIST_HEADER)ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned, bufferLength, CLASS_TAG_LB_PROVISIONING);
+#else
+ buffer = (PUNMAP_LIST_HEADER)ExAllocatePoolWithTag(NonPagedPoolNx, bufferLength, CLASS_TAG_LB_PROVISIONING);
+#endif
+
+ if (buffer == NULL) {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Exit;
+ }
+
+ RtlZeroMemory(buffer, bufferLength);
+
+ blockDescrPointer = &buffer->Descriptors[0];
+
+ allDataSetRangeFullyConverted = FALSE;
+ needToSendCommand = FALSE;
+ tempDataSetRangeFullyConverted = TRUE;
+ dataSetRangeIndex = 0;
+ RtlZeroMemory(&tempDataSetRange, sizeof(tempDataSetRange));
+
+ blockDescrIndex = 0;
+ lbaCount = 0;
+
+ while (!allDataSetRangeFullyConverted) {
+
+ //
+ // If the previous entry conversion completed, go on to the next one;
+ // otherwise, continue processing the current entry.
+ //
+ if (tempDataSetRangeFullyConverted) {
+ tempDataSetRange.StartingOffset = DataSetRanges[dataSetRangeIndex].StartingOffset;
+ tempDataSetRange.LengthInBytes = DataSetRanges[dataSetRangeIndex].LengthInBytes;
+ dataSetRangeIndex++;
+ }
+
+ ConvertDataSetRangeToUnmapBlockDescr(FdoExtension,
+ blockDescrPointer,
+ &blockDescrIndex,
+ maxBlockDescrCount,
+ &lbaCount,
+ maxLbaCount,
+ &tempDataSetRange
+ );
+
+ tempDataSetRangeFullyConverted = (tempDataSetRange.LengthInBytes == 0) ? TRUE : FALSE;
+
+ allDataSetRangeFullyConverted = tempDataSetRangeFullyConverted && (dataSetRangeIndex == DataSetRangesCount);
+
+ //
+ // Send the UNMAP command when the buffer is full or when all input entries are converted.
+ //
+ if ( (blockDescrIndex == maxBlockDescrCount) || // Buffer full or block descriptor count reached
+ (lbaCount == maxLbaCount) || // Block LBA count reached
+ allDataSetRangeFullyConverted) { // All DataSetRanges have been converted
+
+ USHORT transferSize;
+ USHORT tempSize;
+ PCDB cdb;
+
+ //
+ // Get the transfer size, including the header.
+ //
+ transferSize = (USHORT)(blockDescrIndex * sizeof(UNMAP_BLOCK_DESCRIPTOR) + sizeof(UNMAP_LIST_HEADER));
+ if (transferSize > bufferLength)
+ {
+ //
+ // This should never happen.
+ //
+ NT_ASSERT(transferSize <= bufferLength);
+ status = STATUS_BUFFER_TOO_SMALL;
+ break;
+ }
+
+ tempSize = transferSize - (USHORT)FIELD_OFFSET(UNMAP_LIST_HEADER, BlockDescrDataLength);
+ REVERSE_BYTES_SHORT(buffer->DataLength, &tempSize);
+ tempSize = transferSize - (USHORT)FIELD_OFFSET(UNMAP_LIST_HEADER, Descriptors[0]);
+ REVERSE_BYTES_SHORT(buffer->BlockDescrDataLength, &tempSize);
+
+ //
+ // Initialize the SRB.
+ //
+ if (FdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ status = InitializeStorageRequestBlock((PSTORAGE_REQUEST_BLOCK)Srb,
+ STORAGE_ADDRESS_TYPE_BTL8,
+ CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE,
+ 1,
+ SrbExDataTypeScsiCdb16);
+ if (NT_SUCCESS(status)) {
+ ((PSTORAGE_REQUEST_BLOCK)Srb)->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
+ } else {
+ //
+ // Should not occur.
+ //
+ NT_ASSERT(FALSE);
+ break;
+ }
+
+ } else {
+ RtlZeroMemory(Srb, sizeof(SCSI_REQUEST_BLOCK));
+ Srb->Length = sizeof(SCSI_REQUEST_BLOCK);
+ Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
+ }
+
+ //
+ // Prepare the Srb
+ //
+ SrbSetTimeOutValue(Srb, FdoExtension->TimeOutValue);
+ SrbSetRequestTag(Srb, SP_UNTAGGED);
+ SrbSetRequestAttribute(Srb, SRB_SIMPLE_TAG_REQUEST);
+
+ //
+ // Set the SrbFlags to indicate that it's a data-out operation.
+ // Also set any passed-in SrbFlags.
+ //
+ SrbAssignSrbFlags(Srb, FdoExtension->SrbFlags);
+ SrbClearSrbFlags(Srb, SRB_FLAGS_DATA_IN);
+ SrbSetSrbFlags(Srb, SRB_FLAGS_DATA_OUT);
+ SrbSetSrbFlags(Srb, SrbFlags);
+
+ SrbSetCdbLength(Srb, 10);
+
+ cdb = SrbGetCdb(Srb);
+ cdb->UNMAP.OperationCode = SCSIOP_UNMAP;
+ cdb->UNMAP.Anchor = 0;
+ cdb->UNMAP.GroupNumber = 0;
+ cdb->UNMAP.AllocationLength[0] = (UCHAR)(transferSize >> 8);
+ cdb->UNMAP.AllocationLength[1] = (UCHAR)transferSize;
+
+ status = ClassSendSrbSynchronous(FdoExtension->DeviceObject,
+ Srb,
+ buffer,
+ transferSize,
+ TRUE);
+
+ TracePrint((TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_IOCTL,
+ "DeviceProcessDsmTrimRequest (%p): UNMAP command issued. Returned NTSTATUS: %!STATUS!.\n",
+ FdoExtension->DeviceObject,
+ status
+ ));
+
+ //
+ // Clear the buffer so we can re-use it.
+ //
+ blockDescrIndex = 0;
+ lbaCount = 0;
+ RtlZeroMemory(buffer, bufferLength);
+ }
+ }
+
+Exit:
+
+ FREE_POOL(buffer);
+
+ return status;
+}
+
+NTSTATUS ClasspDeviceTrimProcess(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _In_ PIRP Irp,
+ _Inout_ PSCSI_REQUEST_BLOCK Srb
+ )
+/*
+ This function is to process IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES with DeviceDsmAction_Trim.
+ At first time of receiving the request, this function will forward it to lower stack to determine if it's supportted.
+ If it's not supported, UNMAP (with anchor attribute set) will be sent down to process the request.
+*/
+{
+ NTSTATUS status = STATUS_SUCCESS;
+
+ PCOMMON_DEVICE_EXTENSION commonExtension = (PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+
+ PDEVICE_MANAGE_DATA_SET_ATTRIBUTES dsmAttributes = Irp->AssociatedIrp.SystemBuffer;
+
+ PDEVICE_DATA_SET_RANGE dataSetRanges;
+ ULONG dataSetRangesCount;
+ DEVICE_DATA_SET_RANGE entireDataSetRange = {0};
+ ULONG i;
+ ULONGLONG granularityAlignmentInBytes;
+ ULONG granularityInBlocks;
+ ULONG srbFlags = 0;
+
+ CLASS_VPD_B0_DATA blockLimitsData;
+ ULONG generationCount;
+
+ if ( (DeviceObject->DeviceType != FILE_DEVICE_DISK) ||
+ (TEST_FLAG(DeviceObject->Characteristics, FILE_FLOPPY_DISKETTE)) ||
+ (fdoExtension->FunctionSupportInfo->LowerLayerSupport.TrimProcess == Supported) ) {
+ // if it's not disk, forward the request to lower layer,
+ // if the IOCTL is supported by lower stack, forward it down.
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+
+ TracePrint((TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_GENERAL,
+ "ClasspDeviceTrimProcess (%p): Lower layer supports Trim DSM IOCTL, forwarding IOCTL.\n",
+ DeviceObject));
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+ return status;
+ }
+
+ //
+ // Request validation.
+ // Note that InputBufferLength and IsFdo have been validated beforing entering this routine.
+ //
+
+ if (KeGetCurrentIrql() >= DISPATCH_LEVEL) {
+ NT_ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
+ status = STATUS_INVALID_LEVEL;
+ goto Exit;
+ }
+
+ //
+ // If the caller has not set the "entire dataset range" flag then at least
+ // one dataset range should be specified. However, if the caller *has* set
+ // the flag, then there should not be any dataset ranges specified.
+ //
+ if ((!TEST_FLAG(dsmAttributes->Flags, DEVICE_DSM_FLAG_ENTIRE_DATA_SET_RANGE) &&
+ (dsmAttributes->DataSetRangesOffset == 0 ||
+ dsmAttributes->DataSetRangesLength == 0)) ||
+ (TEST_FLAG(dsmAttributes->Flags, DEVICE_DSM_FLAG_ENTIRE_DATA_SET_RANGE) &&
+ (dsmAttributes->DataSetRangesOffset != 0 ||
+ dsmAttributes->DataSetRangesLength != 0))) {
+
+ status = STATUS_INVALID_PARAMETER;
+ goto Exit;
+ }
+
+ //
+ // note that 'Supported' case has been handled at the beginning of this function.
+ //
+ switch (fdoExtension->FunctionSupportInfo->LowerLayerSupport.TrimProcess) {
+ case SupportUnknown: {
+ // send down request and wait for the request to complete.
+ status = ClassForwardIrpSynchronous(commonExtension, Irp);
+
+ TracePrint((TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_GENERAL,
+ "ClasspDeviceTrimProcess (%p): Trim DSM IOCTL support unknown. Forwarded IOCTL and received NTSTATUS %!STATUS!.\n",
+ DeviceObject,
+ status));
+
+ if (ClasspLowerLayerNotSupport(status)) {
+ // case 1: the request is not supported by lower layer, sends down command
+ // some port drivers (or filter drivers) return STATUS_INVALID_DEVICE_REQUEST if a request is not supported.
+ // In this case we'll just fall through to the NotSupported case so that we can handle it ourselves.
+
+ //
+ // VPD pages 0xB2 and 0xB0 should have been cached in Start Device phase - ClassPnpStartDevice.
+ // 0xB2 page: fdoExtension->FunctionSupportInfo->LBProvisioningData;
+ // 0xB0 page: fdoExtension->FunctionSupportInfo->BlockLimitsData
+ //
+ if (fdoExtension->FunctionSupportInfo->ValidInquiryPages.LBProvisioning == TRUE) {
+ NT_ASSERT(fdoExtension->FunctionSupportInfo->LBProvisioningData.CommandStatus != -1);
+ }
+
+ if (fdoExtension->FunctionSupportInfo->ValidInquiryPages.BlockLimits == TRUE) {
+ NT_ASSERT(fdoExtension->FunctionSupportInfo->BlockLimitsData.CommandStatus != -1);
+ }
+
+ } else {
+
+ // case 2: the request is supported and it completes successfully
+ // case 3: the request is supported by lower stack but other failure status is returned.
+ // from now on, the same request will be send down to lower stack directly.
+ fdoExtension->FunctionSupportInfo->LowerLayerSupport.TrimProcess = Supported;
+ goto Exit;
+ }
+ }
+
+ case NotSupported: {
+
+ // send UNMAP command if it is supported. don't need to check 'status' value.
+ if (ClasspSupportsUnmap(fdoExtension->FunctionSupportInfo))
+ {
+ //
+ // Make sure that we know the bytes per sector (logical block) as it's
+ // necessary for calculations involving granularity and alignment.
+ //
+ if (fdoExtension->DiskGeometry.BytesPerSector == 0) {
+ status = ClassReadDriveCapacity(fdoExtension->DeviceObject);
+ if(!NT_SUCCESS(status) || fdoExtension->DiskGeometry.BytesPerSector == 0) {
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ goto Exit;
+ }
+ }
+
+ //
+ // Take a snapshot of the block limits data since it can change.
+ // It's acceptable if the block limits data is outdated since
+ // there isn't a hard requirement on the unmap granularity.
+ //
+ ClasspBlockLimitsDataSnapshot(fdoExtension,
+ FALSE,
+ &blockLimitsData,
+ &generationCount);
+
+ //
+ // Check to see if the Optimal Unmap Granularity and Unmap Granularity
+ // Alignment have been specified. If not, default the granularity to
+ // one block and the alignment to zero.
+ //
+ if (blockLimitsData.OptimalUnmapGranularity != 0)
+ {
+ granularityInBlocks = blockLimitsData.OptimalUnmapGranularity;
+ }
+ else
+ {
+ granularityInBlocks = 1;
+
+ TracePrint((TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_GENERAL,
+ "ClasspDeviceTrimProcess (%p): Optimal Unmap Granularity not provided, defaulted to 1.\n",
+ DeviceObject));
+ }
+
+ if (blockLimitsData.UGAVALID == TRUE)
+ {
+ granularityAlignmentInBytes = (ULONGLONG)blockLimitsData.UnmapGranularityAlignment * fdoExtension->DiskGeometry.BytesPerSector;
+ }
+ else
+ {
+ granularityAlignmentInBytes = 0;
+
+ TracePrint((TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_GENERAL,
+ "ClasspDeviceTrimProcess (%p): Unmap Granularity Alignment not provided, defaulted to 0.\n",
+ DeviceObject));
+ }
+
+ if (TEST_FLAG(dsmAttributes->Flags, DEVICE_DSM_FLAG_ENTIRE_DATA_SET_RANGE))
+ {
+ //
+ // The caller wants to UNMAP the entire disk so we need to build a single
+ // dataset range that represents the entire disk.
+ //
+ entireDataSetRange.StartingOffset = granularityAlignmentInBytes;
+ entireDataSetRange.LengthInBytes = (ULONGLONG)fdoExtension->CommonExtension.PartitionLength.QuadPart - (ULONGLONG)entireDataSetRange.StartingOffset;
+
+ dataSetRanges = &entireDataSetRange;
+ dataSetRangesCount = 1;
+ }
+ else
+ {
+
+ dataSetRanges = (PDEVICE_DATA_SET_RANGE)((PUCHAR)dsmAttributes + dsmAttributes->DataSetRangesOffset);
+ dataSetRangesCount = dsmAttributes->DataSetRangesLength / sizeof(DEVICE_DATA_SET_RANGE);
+
+ //
+ // Validate the data ranges. Make sure the range is block-aligned,
+ // falls in a valid portion of the disk, and is non-zero.
+ //
+ for (i = 0; i < dataSetRangesCount; i++)
+ {
+ if ((dataSetRanges[i].StartingOffset % fdoExtension->DiskGeometry.BytesPerSector != 0) ||
+ (dataSetRanges[i].LengthInBytes % fdoExtension->DiskGeometry.BytesPerSector != 0) ||
+ (dataSetRanges[i].StartingOffset < (LONGLONG)granularityAlignmentInBytes) ||
+ (dataSetRanges[i].LengthInBytes == 0) ||
+ ((ULONGLONG)dataSetRanges[i].StartingOffset + dataSetRanges[i].LengthInBytes > (ULONGLONG)fdoExtension->CommonExtension.PartitionLength.QuadPart))
+ {
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClasspDeviceTrimProcess (%p): Invalid dataset range. StartingOffset = %I64x, LengthInBytes = %I64x\n",
+ DeviceObject,
+ dataSetRanges[i].StartingOffset,
+ dataSetRanges[i].LengthInBytes));
+
+ status = STATUS_INVALID_PARAMETER;
+ goto Exit;
+ }
+ }
+ }
+
+
+ if (!TEST_FLAG(dsmAttributes->Flags, DEVICE_DSM_FLAG_TRIM_NOT_FS_ALLOCATED))
+ {
+ {
+ //
+ // For security reasons, file-level TRIM must be forwarded on only
+ // if reading the unmapped blocks' contents will return back zeros.
+ // This is because if LBPRZ bit is not set, it indicates that a read
+ // of unmapped blocks may return "any" data thus potentially leaking
+ // in data (into the read buffer) from other blocks.
+ //
+ if (fdoExtension->FunctionSupportInfo->ValidInquiryPages.LBProvisioning &&
+ !fdoExtension->FunctionSupportInfo->LBProvisioningData.LBPRZ) {
+
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClasspDeviceTrimProcess (%p): Device does not support file level TRIM.\n",
+ DeviceObject));
+
+ status = STATUS_TRIM_READ_ZERO_NOT_SUPPORTED;
+ goto Exit;
+ }
+ }
+ }
+
+ // process DSM IOCTL
+ status = DeviceProcessDsmTrimRequest(fdoExtension,
+ dataSetRanges,
+ dataSetRangesCount,
+ granularityInBlocks,
+ srbFlags,
+ Srb);
+ } else {
+ // DSM IOCTL should be completed as not supported
+
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClasspDeviceTrimProcess (%p): Device does not support UNMAP.\n",
+ DeviceObject));
+
+ status = STATUS_NOT_SUPPORTED;
+ }
+
+ // set the support status after the SCSI command is executed to avoid racing condition between multiple same type of requests.
+ fdoExtension->FunctionSupportInfo->LowerLayerSupport.TrimProcess = NotSupported;
+
+ break;
+ }
+
+ case Supported: {
+ NT_ASSERT(FALSE); // this case is handled at the begining of the function.
+ break;
+ }
+
+ } // end of switch (fdoExtension->FunctionSupportInfo->LowerLayerSupport.TrimProcess)
+
+Exit:
+
+ //
+ // Set the size and status in IRP
+ //
+ Irp->IoStatus.Information = 0;
+ Irp->IoStatus.Status = status;
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+
+ return status;
+}
+
+NTSTATUS
+GetLBAStatus(
+ _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ _In_ PSCSI_REQUEST_BLOCK Srb,
+ _In_ ULONGLONG StartingLBA,
+ _Inout_ PLBA_STATUS_LIST_HEADER LBAStatusHeader,
+ _In_ ULONG LBAStatusSize,
+ _In_ BOOLEAN ConsolidateableBlocksOnly
+ )
+/*++
+
+Routine Description:
+
+ Send down a Get LBA Status command for the given range.
+
+Arguments:
+ FdoExtension: The FDO extension of the device to which Get LBA Status will
+ be sent.
+ Srb: This should be allocated and initialized before it's passed in. It
+ will be used for the Get LBA Status command.
+ StartingLBA: The LBA that is at the beginning of the requested range.
+ LBAStatusHeader: Caller-allocated output buffer.
+ LBASTatusSize: Size of the caller-allocated output buffer.
+
+Return Value:
+
+ Status of the operation.
+
+--*/
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PCDB cdb;
+
+ if (LBAStatusHeader == NULL || LBAStatusSize == 0)
+ {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // Build and send down the Get LBA Status command.
+ //
+ SrbSetTimeOutValue(Srb, FdoExtension->TimeOutValue);
+ SrbSetRequestTag(Srb, SP_UNTAGGED);
+ SrbSetRequestAttribute(Srb, SRB_SIMPLE_TAG_REQUEST);
+ SrbAssignSrbFlags(Srb, FdoExtension->SrbFlags);
+ SrbSetCdbLength(Srb, sizeof(cdb->GET_LBA_STATUS));
+
+
+ cdb = SrbGetCdb(Srb);
+ cdb->GET_LBA_STATUS.OperationCode = SCSIOP_GET_LBA_STATUS;
+ cdb->GET_LBA_STATUS.ServiceAction = SERVICE_ACTION_GET_LBA_STATUS;
+ REVERSE_BYTES_QUAD(&(cdb->GET_LBA_STATUS.StartingLBA), &StartingLBA);
+ REVERSE_BYTES(&(cdb->GET_LBA_STATUS.AllocationLength), &LBAStatusSize);
+
+ TracePrint((TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_IOCTL,
+ "GetLBAStatus (%p): sending command with StartingLBA = 0x%I64x, AllocationLength = 0x%I64x, ConsolidateableBlocksOnly = %u\n",
+ FdoExtension->DeviceObject,
+ StartingLBA,
+ LBAStatusSize,
+ ConsolidateableBlocksOnly));
+
+ status = ClassSendSrbSynchronous(FdoExtension->DeviceObject,
+ Srb,
+ LBAStatusHeader,
+ LBAStatusSize,
+ FALSE);
+
+ //
+ // Handle the case where we get back STATUS_DATA_OVERRUN b/c the input
+ // buffer was larger than necessary.
+ //
+ if (status == STATUS_DATA_OVERRUN &&
+ SrbGetDataTransferLength(Srb) < LBAStatusSize)
+ {
+ status = STATUS_SUCCESS;
+ }
+
+ // log command.
+ TracePrint((TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_IOCTL,
+ "GetLBAStatus (%p): command returned NT Status: %!STATUS!\n",
+ FdoExtension->DeviceObject,
+ status
+ ));
+
+ return status;
+}
+
+
+NTSTATUS ClasspDeviceGetLBAStatus(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _Inout_ PIRP Irp,
+ _Inout_ PSCSI_REQUEST_BLOCK Srb
+ )
+/*
+Routine Description:
+
+ This function is to process IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES with DeviceDsmAction_Allocation.
+
+ 1. This function will only handle the first dataset range.
+ 2. This function will not handle dataset ranges whose LengthInBytes is greater than:
+ ((MAXULONG - sizeof(LBA_STATUS_LIST_HEADER)) / sizeof(LBA_STATUS_DESCRIPTOR)) * BytesPerSlab
+
+ The input buffer should consist of a DEVICE_MANAGE_DATA_SET_ATTRIBUTES followed
+ in memory by a single DEVICE_DATA_SET_RANGE that specifies the requested range
+ of slabs for which mapping status is desired.
+
+ The output buffer will consist of a DEVICE_MANAGE_DATA_SET_ATTRIBUTES_OUTPUT
+ followed in memory by a single DEVICE_DATA_SET_LB_PROVISIONING_STATE that
+ contains a bitmap that represents the mapped status of the slabs in the requested
+ range. Note that the number of slabs returned may be less than the number
+ requested.
+
+ Thus function will automatically re-align the given range offset if it was
+ not slab-aligned. The delta between the given range offset and the properly
+ aligned offset will be given in returned DEVICE_DATA_SET_LB_PROVISIONING_STATE.
+
+Arguments:
+ DeviceObject: The FDO of the device to which Get LBA Status will be sent.
+ Irp: The IRP for the request. This function will read the input buffer and
+ write to the output buffer at the current IRP stack location.
+ Srb: This should be allocated and initialized before it's passed in. It
+ will be used for the Get LBA Status command.
+
+Return Value:
+
+ STATUS_INVALID_PARAMETER: May be returned under the following conditions:
+ - If the requested range was too large. The caller should try again with a
+ smaller range. See above for how to calculate the maximum range.
+ - If the given starting offset was not within the valid range of the device.
+ STATUS_NOT_SUPPORTED: The storage did not report some information critical to
+ the execution of this function (e.g. Optimal Unmap Granularity).
+ STATUS_BUFFER_TOO_SMALL: The output buffer is not large enough to hold the max
+ data that could be returned from this function. If the output buffer is
+ at least the size of a ULONG, we will write the required output buffer size
+ to the first ULONG bytes of the output buffer.
+ STATUS_UNSUCCESSFUL: The Get LBA Status command succeeded but did not
+ return data as expected.
+--*/
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
+ PDEVICE_MANAGE_DATA_SET_ATTRIBUTES dsmAttributes = (PDEVICE_MANAGE_DATA_SET_ATTRIBUTES)Irp->AssociatedIrp.SystemBuffer;
+ PDEVICE_DATA_SET_RANGE dataSetRanges = NULL;
+ PDEVICE_MANAGE_DATA_SET_ATTRIBUTES_OUTPUT dsmOutput = (PDEVICE_MANAGE_DATA_SET_ATTRIBUTES_OUTPUT)Irp->AssociatedIrp.SystemBuffer;
+ ULONG dsmOutputLength;
+ NTSTATUS finalStatus;
+ NTSTATUS getLBAWorkerStatus;
+ ULONG retryCount;
+ ULONG retryCountMax;
+ CLASS_VPD_B0_DATA blockLimitsData;
+ ULONG generationCount1;
+ ULONG generationCount2;
+ BOOLEAN blockLimitsDataMayHaveChanged;
+ ULONG_PTR information = 0;
+ LONGLONG startingOffset;
+ ULONGLONG lengthInBytes;
+ BOOLEAN consolidateableBlocksOnly = FALSE;
+ ULONG outputVersion;
+
+ //
+ // Basic parameter validation.
+ // Note that InputBufferLength and IsFdo have been validated beforing entering this routine.
+ //
+ if (dsmOutput == NULL ||
+ dsmAttributes == NULL)
+ {
+ finalStatus = STATUS_INVALID_PARAMETER;
+ goto Exit;
+ }
+
+ if (TEST_FLAG(dsmAttributes->Flags, DEVICE_DSM_FLAG_ENTIRE_DATA_SET_RANGE)) {
+ //
+ // The caller wants the mapping status of the entire disk.
+ //
+ ULONG unmapGranularityAlignment = 0;
+ if (fdoExtension->FunctionSupportInfo->BlockLimitsData.UGAVALID) {
+ unmapGranularityAlignment = fdoExtension->FunctionSupportInfo->BlockLimitsData.UnmapGranularityAlignment;
+ }
+ startingOffset = unmapGranularityAlignment;
+ lengthInBytes = (ULONGLONG)fdoExtension->CommonExtension.PartitionLength.QuadPart - (ULONGLONG)startingOffset;
+ } else {
+ if (dsmAttributes->DataSetRangesOffset == 0 ||
+ dsmAttributes->DataSetRangesLength == 0) {
+ finalStatus = STATUS_INVALID_PARAMETER;
+ goto Exit;
+ }
+
+ //
+ // We only service the first dataset range specified.
+ //
+ dataSetRanges = (PDEVICE_DATA_SET_RANGE)((PUCHAR)dsmAttributes + dsmAttributes->DataSetRangesOffset);
+ startingOffset = dataSetRanges[0].StartingOffset;
+ lengthInBytes = dataSetRanges[0].LengthInBytes;
+ }
+
+
+ //
+ // See if the sender is requesting a specific version of the output data
+ // structure. Othwerwise, default to V1.
+ //
+ outputVersion = DEVICE_DATA_SET_LB_PROVISIONING_STATE_VERSION_V1;
+#if (NTDDI_VERSION >= NTDDI_WINBLUE)
+ if ((dsmAttributes->ParameterBlockOffset >= sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES)) &&
+ (dsmAttributes->ParameterBlockLength >= sizeof(DEVICE_DATA_SET_LBP_STATE_PARAMETERS))) {
+ PDEVICE_DATA_SET_LBP_STATE_PARAMETERS parameters = Add2Ptr(dsmAttributes, dsmAttributes->ParameterBlockOffset);
+ if ((parameters->Version == DEVICE_DATA_SET_LBP_STATE_PARAMETERS_VERSION_V1) &&
+ (parameters->Size >= sizeof(DEVICE_DATA_SET_LBP_STATE_PARAMETERS))) {
+
+ outputVersion = parameters->OutputVersion;
+
+ if ((outputVersion != DEVICE_DATA_SET_LB_PROVISIONING_STATE_VERSION_V1) &&
+ (outputVersion != DEVICE_DATA_SET_LB_PROVISIONING_STATE_VERSION_V2)) {
+ finalStatus = STATUS_INVALID_PARAMETER;
+ goto Exit;
+ }
+ }
+ }
+#endif
+
+ //
+ // Take a snapshot of the block limits data for the worker function to use.
+ // We need to fail the request if we fail to get updated block limits data
+ // since we need an accurate Optimal Unmap Granularity value to properly
+ // convert the returned mapping descriptors into a bitmap.
+ //
+ finalStatus = ClasspBlockLimitsDataSnapshot(fdoExtension,
+ TRUE,
+ &blockLimitsData,
+ &generationCount1);
+
+ if (!NT_SUCCESS(finalStatus)) {
+ information = 0;
+ goto Exit;
+ }
+
+ if (dsmAttributes->Flags & DEVICE_DSM_FLAG_ALLOCATION_CONSOLIDATEABLE_ONLY) {
+ consolidateableBlocksOnly = TRUE;
+ }
+
+ //
+ // The retry logic is to handle the case when block limits data changes during rare occasions
+ // (e.g. diff-VHD fork or merge).
+ //
+ retryCountMax = GET_LBA_STATUS_RETRY_COUNT_MAX;
+ for (retryCount = 0; retryCount < retryCountMax; retryCount++) {
+
+ dsmOutputLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
+ getLBAWorkerStatus = ClasspDeviceGetLBAStatusWorker(DeviceObject,
+ &blockLimitsData,
+ startingOffset,
+ lengthInBytes,
+ dsmOutput,
+ &dsmOutputLength,
+ Srb,
+ consolidateableBlocksOnly,
+ outputVersion,
+ &blockLimitsDataMayHaveChanged);
+
+ if (!NT_SUCCESS(getLBAWorkerStatus) && !blockLimitsDataMayHaveChanged) {
+ information = 0;
+ finalStatus = getLBAWorkerStatus;
+ break;
+ }
+
+ //
+ // Again, we need to fail the request if we fail to get updated block
+ // limits data since we need an accurate Optimal Unmap Granularity value.
+ //
+ finalStatus = ClasspBlockLimitsDataSnapshot(fdoExtension,
+ TRUE,
+ &blockLimitsData,
+ &generationCount2);
+ if (!NT_SUCCESS(finalStatus)) {
+ information = 0;
+ goto Exit;
+ }
+
+ if (generationCount1 == generationCount2) {
+ //
+ // Block limits data stays the same during the call to ClasspDeviceGetLBAStatusWorker()
+ // The result from ClasspDeviceGetLBAStatusWorker() is valid.
+ //
+ finalStatus = getLBAWorkerStatus;
+ if (NT_SUCCESS(finalStatus)) {
+ information = dsmOutputLength;
+ }
+ break;
+ }
+
+ //
+ // Try again with the latest block limits data
+ //
+ generationCount1 = generationCount2;
+ information = 0;
+ finalStatus = STATUS_DEVICE_DATA_ERROR;
+ }
+
+Exit:
+ Irp->IoStatus.Information = information;
+ Irp->IoStatus.Status = finalStatus;
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ return finalStatus;
+}
+
+NTSTATUS
+ClasspDeviceGetLBAStatusWorker(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _In_ PCLASS_VPD_B0_DATA BlockLimitsData,
+ _In_ ULONGLONG StartingOffset,
+ _In_ ULONGLONG LengthInBytes,
+ _Out_ PDEVICE_MANAGE_DATA_SET_ATTRIBUTES_OUTPUT DsmOutput,
+ _Inout_ PULONG DsmOutputLength,
+ _Inout_ PSCSI_REQUEST_BLOCK Srb,
+ _In_ BOOLEAN ConsolidateableBlocksOnly,
+ _In_ ULONG OutputVersion,
+ _Out_ PBOOLEAN BlockLimitsDataMayHaveChanged
+ )
+/*
+Routine Description:
+
+ This function is to process IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES with DeviceDsmAction_Allocation.
+
+ 1. This function will only handle the first dataset range.
+ 2. This function will not handle dataset ranges whose LengthInBytes is greater than:
+ ((MAXULONG - sizeof(LBA_STATUS_LIST_HEADER)) / sizeof(LBA_STATUS_DESCRIPTOR)) * BytesPerSlab
+
+ The input buffer should consist of a DEVICE_MANAGE_DATA_SET_ATTRIBUTES followed
+ in memory by a single DEVICE_DATA_SET_RANGE that specifies the requested range
+ of slabs for which mapping status is desired.
+
+ The output buffer will consist of a DEVICE_MANAGE_DATA_SET_ATTRIBUTES_OUTPUT
+ followed in memory by a single DEVICE_DATA_SET_LB_PROVISIONING_STATE that
+ contains a bitmap that represents the mapped status of the slabs in the requested
+ range. Note that the number of slabs returned may be less than the number
+ requested.
+
+ Thus function will automatically re-align the given range offset if it was
+ not slab-aligned. The delta between the given range offset and the properly
+ aligned offset will be given in returned DEVICE_DATA_SET_LB_PROVISIONING_STATE.
+
+Arguments:
+ DeviceObject: The FDO of the device to which Get LBA Status will be sent.
+ BlockLimitsData: Block limits data of the device
+ StartingOffset: Starting byte offset of byte range to query LBA status (must be sector aligned)
+ LengthInBytes: Length of byte range to query LBA status (multiple of sector size)
+ DsmOutput: Output data buffer
+ DsmOutputLength: output data buffer size. It will be updated with actual bytes used.
+ Srb: This should be allocated and initialized before it's passed in. It
+ will be used for the Get LBA Status command.
+ ConsolidateableBlocksOnly: Only blocks that are eligible for consolidation
+ should be returned.
+ OutputVersion: The version of the DEVICE_DATA_SET_LB_PROVISIONING_STATE
+ structure to return. This should be one of:
+ - DEVICE_DATA_SET_LB_PROVISIONING_STATE_VERSION_V1
+ - DEVICE_DATA_SET_LB_PROVISIONING_STATE_VERSION_V2
+ BlockLimitsDataMayHaveChanged: if this function fails, this flag indicates
+ if the failure can be caused by changes in device's block limit data.
+
+Return Value:
+
+ STATUS_INVALID_PARAMETER: May be returned under the following conditions:
+ - If the requested range was too large. The caller should try again with a
+ smaller range. See above for how to calculate the maximum range.
+ - If the given starting offset was not within the valid range of the device.
+ STATUS_NOT_SUPPORTED: The storage did not report some information critical to
+ the execution of this function (e.g. Optimal Unmap Granularity).
+ STATUS_BUFFER_TOO_SMALL: The output buffer is not large enough to hold the max
+ data that could be returned from this function. If the output buffer is
+ at least the size of a ULONG, we will write the required output buffer size
+ to the first ULONG bytes of the output buffer.
+ STATUS_DEVICE_DATA_ERROR: The Get LBA Status command succeeded but did not
+ return data as expected.
+--*/
+{
+ NTSTATUS status = STATUS_SUCCESS;
+
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+
+ PDEVICE_DATA_SET_LB_PROVISIONING_STATE lbpState;
+ ULONG bitMapGranularityInBits = FIELD_SIZE(DEVICE_DATA_SET_LB_PROVISIONING_STATE,SlabAllocationBitMap[0]) * 8;
+ ULONG requiredOutputLength;
+ ULONG outputLength = *DsmOutputLength;
+
+ ULONG blocksPerSlab;
+ ULONGLONG bytesPerSlab;
+ ULONGLONG alignmentInBytes = 0;
+ ULONG alignmentInBlocks = 0;
+ ULONG maxBufferSize;
+ ULONG maxSlabs;
+ ULONGLONG requestedSlabs; // Total number of slabs requested by the caller.
+ ULONGLONG startingLBA;
+ ULONGLONG startingOffsetDelta;
+ ULONG totalProcessedSlabs = 0; // Total number of slabs we processed.
+ ULONGLONG slabsPerCommand; // Number of slabs we can ask for in one Get LBA Status command.
+ BOOLEAN doneProcessing = FALSE; // Indicates we should break out of the Get LBA Status loop.
+
+ ULONG lbaStatusSize;
+ PLBA_STATUS_LIST_HEADER lbaStatusListHeader = NULL;
+
+ //
+ // This function can fail if the block limits data on the device changes.
+ // This flag tells the caller if it should retry with a newer block limits data
+ //
+ *BlockLimitsDataMayHaveChanged = FALSE;
+
+ //
+ // Make sure we're running at PASSIVE_LEVEL
+ //
+ if (KeGetCurrentIrql() >= DISPATCH_LEVEL)
+ {
+ NT_ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
+ status = STATUS_INVALID_LEVEL;
+ goto Exit;
+ }
+
+ //
+ // Don't send down a Get LBA Status command if UNMAP isn't supported.
+ //
+ if (!fdoExtension->FunctionSupportInfo->LBProvisioningData.LBPU)
+ {
+ return STATUS_NOT_SUPPORTED;
+ goto Exit;
+ }
+
+ //
+ // Make sure we have a non-zero value for the number of bytes per block.
+ // Otherwise we will end up dividing by zero later on.
+ //
+ if (fdoExtension->DiskGeometry.BytesPerSector == 0)
+ {
+ status = ClassReadDriveCapacity(fdoExtension->DeviceObject);
+ if(!NT_SUCCESS(status) || fdoExtension->DiskGeometry.BytesPerSector == 0)
+ {
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ goto Exit;
+ }
+ }
+
+ //
+ // We only service the first dataset range specified.
+ //
+ if (BlockLimitsData->UGAVALID == TRUE) {
+ alignmentInBlocks = BlockLimitsData->UnmapGranularityAlignment;
+ alignmentInBytes = (ULONGLONG)alignmentInBlocks * (ULONGLONG)fdoExtension->DiskGeometry.BytesPerSector;
+ }
+
+ //
+ // Make sure the specified range is valid. The Unmap Granularity Alignment
+ // defines a region at the beginning of the disk that cannot be
+ // mapped/unmapped so the specified range should not include any part of that
+ // region.
+ //
+ if (LengthInBytes == 0 ||
+ StartingOffset < alignmentInBytes ||
+ StartingOffset + LengthInBytes > (ULONGLONG)fdoExtension->CommonExtension.PartitionLength.QuadPart)
+ {
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClasspDeviceGetLBAStatusWorker (%p): Invalid range, length is %I64u bytes, starting offset is %I64u bytes, Unmap alignment is %I64u bytes, and disk size is %I64u bytes\n",
+ DeviceObject,
+ LengthInBytes,
+ StartingOffset,
+ alignmentInBytes,
+ (ULONGLONG)fdoExtension->CommonExtension.PartitionLength.QuadPart));
+
+ status = STATUS_INVALID_PARAMETER;
+ goto Exit;
+ }
+
+ //
+ // Calculate the number of bytes per slab so that we can convert (and
+ // possibly align) the given offset (given in bytes) to slabs.
+ //
+ blocksPerSlab = BlockLimitsData->OptimalUnmapGranularity;
+ bytesPerSlab = (ULONGLONG)blocksPerSlab * (ULONGLONG)fdoExtension->DiskGeometry.BytesPerSector;
+
+ //
+ // If the starting offset is not slab-aligned, we need to adjust it to
+ // be aligned with the next highest slab. We also need to save the delta
+ // to return to the user later.
+ //
+ if (((StartingOffset - alignmentInBytes) % bytesPerSlab) != 0)
+ {
+ startingLBA = (((StartingOffset - alignmentInBytes) / bytesPerSlab) + 1) * (ULONGLONG)blocksPerSlab + alignmentInBlocks;
+ startingOffsetDelta = (startingLBA * fdoExtension->DiskGeometry.BytesPerSector) - StartingOffset;
+ }
+ else
+ {
+ startingLBA = ((StartingOffset - alignmentInBytes) / bytesPerSlab) * (ULONGLONG)blocksPerSlab + alignmentInBlocks;
+ startingOffsetDelta = 0;
+ }
+
+ //
+ // Caclulate the number of slabs the caller requested.
+ //
+ if ((LengthInBytes % bytesPerSlab) == 0) {
+ requestedSlabs = (LengthInBytes / bytesPerSlab);
+ } else {
+ //
+ // Round up the number of requested slabs if the length indicates a
+ // partial slab. This should cover the case where the user specifies
+ // a dataset range for the whole disk, but the size of the disk is not
+ // a slab-multiple. Rounding up allows us to return the status of the
+ // partial slab
+ //
+ requestedSlabs = (LengthInBytes / bytesPerSlab) + 1;
+ }
+
+ //
+ // If the caller asked for no slabs then return STATUS_INVALID_PARAMETER.
+ //
+ if (requestedSlabs == 0)
+ {
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClasspDeviceGetLBAStatusWorker (%p): Invalid number (%I64u) of slabs requested\n",
+ DeviceObject,
+ requestedSlabs));
+
+ status = STATUS_INVALID_PARAMETER;
+ goto Exit;
+ }
+
+ //
+ // Cap requested slabs at MAXULONG since SlabAllocationBitMapBitCount
+ // is a 4-byte field. We may return less data than requested, but the
+ // caller can simply re-query for the omitted portion(s).
+ //
+ requestedSlabs = min(requestedSlabs, MAXULONG);
+
+ //
+ // Calculate the required size of the output buffer based upon the desired
+ // version of the output structure.
+ // In the worst case, Get LBA Status returns a descriptor for each slab
+ // requested, thus the required output buffer length is equal to:
+ // 1. The size of DEVICE_MANAGE_DATA_SET_ATTRIBUTES_OUTPUT; plus
+ // 2. The size of DEVICE_DATA_SET_LB_PROVISIONING_STATE(_V2); plus
+ // 3. The size of a ULONG array large enough to hold a bit for each slab requested.
+ // (The first element is already allocated in DEVICE_DATA_SET_LB_PROVISIONING_STATE(_V2).)
+ //
+#if (NTDDI_VERSION >= NTDDI_WINBLUE)
+ if (OutputVersion == DEVICE_DATA_SET_LB_PROVISIONING_STATE_VERSION_V2) {
+
+ requiredOutputLength = (ULONG)(sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES_OUTPUT)
+ + sizeof(DEVICE_DATA_SET_LB_PROVISIONING_STATE_V2)
+ + (((requestedSlabs - 1) / bitMapGranularityInBits))
+ * FIELD_SIZE(DEVICE_DATA_SET_LB_PROVISIONING_STATE_V2, SlabAllocationBitMap[0]));
+
+ } else
+#else
+ UNREFERENCED_PARAMETER(OutputVersion);
+#endif
+ {
+
+ requiredOutputLength = (ULONG)(sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES_OUTPUT)
+ + sizeof(DEVICE_DATA_SET_LB_PROVISIONING_STATE)
+ + (((requestedSlabs - 1) / bitMapGranularityInBits))
+ * FIELD_SIZE(DEVICE_DATA_SET_LB_PROVISIONING_STATE, SlabAllocationBitMap[0]));
+ }
+
+ //
+ // The output buffer is not big enough to hold the requested data.
+ // Inform the caller of the correct buffer size.
+ //
+ if (outputLength < requiredOutputLength)
+ {
+ status = STATUS_BUFFER_TOO_SMALL;
+
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClasspDeviceGetLBAStatusWorker (%p): Given output buffer is %u bytes, needs to be %u bytes\n",
+ DeviceObject,
+ outputLength,
+ requiredOutputLength));
+
+ //
+ // If the output buffer is big enough, write the required buffer
+ // length to the first ULONG bytes of the output buffer.
+ //
+ if (outputLength >= sizeof(ULONG))
+ {
+ *((PULONG)DsmOutput) = requiredOutputLength;
+ }
+
+ goto Exit;
+ }
+
+ //
+ // Calculate the maximum number of slabs that could be returned by a single
+ // Get LBA Status command. The max buffer size could either be capped by
+ // the Parameter Data Length field or the Max Transfer Length of the
+ // adapter.
+ // The number of slabs we actually ask for in a single command is the
+ // smaller of the number of slabs requested by the user or the max number
+ // of slabs we can theoretically ask for in a single command.
+ //
+ maxBufferSize = MIN(MAXULONG, fdoExtension->PrivateFdoData->HwMaxXferLen);
+ maxSlabs = (maxBufferSize - sizeof(LBA_STATUS_LIST_HEADER)) / sizeof(LBA_STATUS_DESCRIPTOR);
+ slabsPerCommand = min(requestedSlabs, maxSlabs);
+
+ //
+ // Allocate the buffer that will contain the returned LBA Status Descriptors.
+ // Assume that in the worst case every other slab has a different mapping
+ // status. That means that there may be a descriptor for every slab requested.
+ //
+ lbaStatusSize = (ULONG)(sizeof(LBA_STATUS_LIST_HEADER) + (slabsPerCommand * sizeof(LBA_STATUS_DESCRIPTOR)));
+#if defined(_ARM_) || defined(_ARM64_)
+ //
+ // ARM has specific alignment requirements, although this will not have a functional impact on x86 or amd64
+ // based platforms. We are taking the conservative approach here.
+ //
+ lbaStatusSize = ALIGN_UP_BY(lbaStatusSize,KeGetRecommendedSharedDataAlignment());
+ lbaStatusListHeader = (PLBA_STATUS_LIST_HEADER)ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned, lbaStatusSize, CLASS_TAG_LB_PROVISIONING);
+#else
+ lbaStatusListHeader = (PLBA_STATUS_LIST_HEADER)ExAllocatePoolWithTag(NonPagedPoolNx, lbaStatusSize, CLASS_TAG_LB_PROVISIONING);
+#endif
+
+ if (lbaStatusListHeader == NULL)
+ {
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClasspDeviceGetLBAStatusWorker (%p): Failed to allocate %u bytes for descriptors\n",
+ DeviceObject,
+ lbaStatusSize));
+
+ NT_ASSERT(lbaStatusListHeader != NULL);
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Exit;
+ }
+
+ //
+ // Set default values for the output buffer.
+ // If we process at least one slab from the device we will update the
+ // offset and lengths accordingly.
+ //
+ DsmOutput->Action = DeviceDsmAction_Allocation;
+ DsmOutput->Size = sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES_OUTPUT);
+ DsmOutput->OutputBlockOffset = 0;
+ DsmOutput->OutputBlockLength = 0;
+ *DsmOutputLength = DsmOutput->Size;
+
+ //
+ // The returned DEVICE_DATA_SET_LB_PROVISIONING_STATE is at the end of the
+ // DSM output structure. Zero it out before we start to fill it in.
+ //
+ lbpState = Add2Ptr(DsmOutput, sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES_OUTPUT));
+ RtlZeroMemory(lbpState, requiredOutputLength - sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES_OUTPUT));
+
+ do {
+ //
+ // Send down GetLBAStatus for the current range.
+ //
+ status = GetLBAStatus(fdoExtension,
+ Srb,
+ startingLBA,
+ lbaStatusListHeader,
+ lbaStatusSize,
+ ConsolidateableBlocksOnly);
+
+ if (NT_SUCCESS(status))
+ {
+ ULONG descrIndex = 0;
+ ULONG descrSize = 0;
+ ULONG descrSizeOverhead;
+ ULONG descrCount = 0;
+ ULONGLONG expectedStartingLBA;
+ BOOLEAN processCurrentDescriptor = TRUE;
+ ULONG commandProcessedSlabs = 0; // Number of slabs processed for this command.
+
+ descrSizeOverhead = FIELD_OFFSET(LBA_STATUS_LIST_HEADER, Descriptors[0]) -
+ RTL_SIZEOF_THROUGH_FIELD(LBA_STATUS_LIST_HEADER, ParameterLength);
+ REVERSE_BYTES(&descrSize, &(lbaStatusListHeader->ParameterLength));
+
+ //
+ // If the returned Parameter Data Length field describes more
+ // descriptors than we allocated space for then make sure we don't
+ // try to process more descriptors than are actually present.
+ //
+ if (descrSize > (lbaStatusSize - RTL_SIZEOF_THROUGH_FIELD(LBA_STATUS_LIST_HEADER, ParameterLength))) {
+ descrSize = (lbaStatusSize - RTL_SIZEOF_THROUGH_FIELD(LBA_STATUS_LIST_HEADER, ParameterLength));
+ }
+
+ if (descrSize >= descrSizeOverhead) {
+ descrSize -= descrSizeOverhead;
+ descrCount = descrSize / sizeof(LBA_STATUS_DESCRIPTOR);
+
+ //
+ // Make sure at least one descriptor was returned.
+ //
+ if (descrCount > 0) {
+ //
+ // We expect the first starting LBA returned by the device to be the
+ // same starting LBA we specified in the command.
+ //
+ expectedStartingLBA = startingLBA;
+
+ //
+ // Translate the returned LBA status descriptors into a bitmap where each bit represents
+ // a slab. The slab size is represented by the Optimal Unmap Granularity.
+ // 1 = The slab is mapped.
+ // 0 = The slab is unmapped (deallocated or anchored).
+ //
+ for (descrIndex = 0; descrIndex < descrCount && totalProcessedSlabs < requestedSlabs && !doneProcessing; descrIndex++)
+ {
+ PLBA_STATUS_DESCRIPTOR lbaStatusDescr = &(lbaStatusListHeader->Descriptors[descrIndex]);
+ ULONGLONG returnedStartingLBA;
+ ULONG mapped = (lbaStatusDescr->ProvisioningStatus != LBA_STATUS_MAPPED) ? 0x0 : 0x1;
+ ULONG lbaCount = 0;
+
+ REVERSE_BYTES_QUAD(&returnedStartingLBA, &(lbaStatusDescr->StartingLBA));
+ REVERSE_BYTES(&lbaCount, &(lbaStatusDescr->LogicalBlockCount));
+
+ if (returnedStartingLBA != expectedStartingLBA)
+ {
+ //
+ // We expect the descriptors will express a contiguous range of LBAs.
+ // If the starting LBA is not contiguous with the LBA range from the
+ // previous descriptor then we should not process any more descriptors,
+ // including the current one.
+ //
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClasspDeviceGetLBAStatusWorker (%p): Device returned starting LBA = %I64x when %I64x was expected.\n",
+ DeviceObject,
+ returnedStartingLBA,
+ startingLBA));
+
+ doneProcessing = TRUE;
+ processCurrentDescriptor = FALSE;
+ *BlockLimitsDataMayHaveChanged = TRUE;
+ }
+ else if (lbaCount > 0 && lbaCount % blocksPerSlab != 0)
+ {
+ //
+ // If the device returned an LBA count with a partial slab, round
+ // the LBA count up to the nearest slab and set a flag to stop
+ // processing further descriptors. This is mainly to handle the
+ // case where disk size may not be slab-aligned and thus the last
+ // "slab" is actually a partial slab.
+ //
+ TracePrint((TRACE_LEVEL_WARNING,
+ TRACE_FLAG_IOCTL,
+ "ClasspDeviceGetLBAStatusWorker (%p): Device returned an LBA count (%u) that is not a multiple of the slab size (%u)\n",
+ DeviceObject,
+ lbaCount,
+ blocksPerSlab));
+
+ lbaCount = ((lbaCount / blocksPerSlab) + 1) * blocksPerSlab;
+
+ doneProcessing = TRUE;
+ processCurrentDescriptor = TRUE;
+ }
+ else if (lbaCount == 0)
+ {
+ //
+ // If the LBA count is 0, just skip this descriptor.
+ //
+ TracePrint((TRACE_LEVEL_WARNING,
+ TRACE_FLAG_IOCTL,
+ "ClasspDeviceGetLBAStatusWorker (%p): Device returned a zero LBA count\n",
+ DeviceObject));
+
+ processCurrentDescriptor = FALSE;
+ }
+
+ //
+ // Generate bits for the slabs described in the current descriptor.
+ // It's possible the device may have returned more slabs than requested
+ // so we make sure to stop once we've processed all we need.
+ //
+ if (processCurrentDescriptor)
+ {
+ ULONG descrSlabs = lbaCount / blocksPerSlab; // Number of slabs in this descriptor.
+
+ for(; 0 < descrSlabs && totalProcessedSlabs < requestedSlabs; descrSlabs--, commandProcessedSlabs++, totalProcessedSlabs++)
+ {
+ ULONG bitMapIndex = totalProcessedSlabs / bitMapGranularityInBits;
+ ULONG bitPos = totalProcessedSlabs % bitMapGranularityInBits;
+
+ #if (NTDDI_VERSION >= NTDDI_WINBLUE)
+ if (OutputVersion == DEVICE_DATA_SET_LB_PROVISIONING_STATE_VERSION_V2) {
+ ((PDEVICE_DATA_SET_LB_PROVISIONING_STATE_V2)lbpState)->SlabAllocationBitMap[bitMapIndex] |= (mapped << bitPos);
+ } else
+ #endif
+ {
+ lbpState->SlabAllocationBitMap[bitMapIndex] |= (mapped << bitPos);
+ }
+ }
+ }
+
+ //
+ // Calculate the next expected starting LBA.
+ //
+ expectedStartingLBA = returnedStartingLBA + lbaCount;
+ }
+
+ if (commandProcessedSlabs > 0) {
+
+ //
+ // Calculate the starting LBA we'll use for the next command.
+ //
+ startingLBA += ((ULONGLONG)commandProcessedSlabs * (ULONGLONG)blocksPerSlab);
+
+ } else {
+ //
+ // This should never happen, but we should handle it gracefully anyway.
+ //
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClasspDeviceGetLBAStatusWorker (%p): The slab allocation bitmap has zero length.\n",
+ DeviceObject));
+
+ NT_ASSERT(commandProcessedSlabs != 0);
+ doneProcessing = TRUE;
+ status = STATUS_UNSUCCESSFUL;
+ }
+ } else {
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClasspDeviceGetLBAStatusWorker (%p): Device returned no LBA Status Descriptors.\n",
+ DeviceObject));
+
+ doneProcessing = TRUE;
+ status = STATUS_UNSUCCESSFUL;
+ }
+ } else {
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClasspDeviceGetLBAStatusWorker (%p): not enough bytes returned\n",
+ DeviceObject));
+
+ doneProcessing = TRUE;
+ status = STATUS_DEVICE_DATA_ERROR;
+ }
+ }
+
+ //
+ // Loop until we encounter some error or we've processed all the requested slabs.
+ //
+ } while (NT_SUCCESS(status) &&
+ !doneProcessing &&
+ (totalProcessedSlabs < requestedSlabs));
+
+ //
+ // At least one slab was returned by the device and processed, which we
+ // consider success. It's up to the caller to detect truncation.
+ // Update the output buffer sizes, offsets, etc. accordingly.
+ //
+ if (totalProcessedSlabs > 0) {
+
+ #if (NTDDI_VERSION >= NTDDI_WINBLUE)
+ if (OutputVersion == DEVICE_DATA_SET_LB_PROVISIONING_STATE_VERSION_V2) {
+ PDEVICE_DATA_SET_LB_PROVISIONING_STATE_V2 lbpStateV2 = (PDEVICE_DATA_SET_LB_PROVISIONING_STATE_V2)lbpState;
+
+ lbpStateV2->SlabSizeInBytes = bytesPerSlab;
+ lbpStateV2->SlabOffsetDeltaInBytes = startingOffsetDelta;
+ lbpStateV2->SlabAllocationBitMapBitCount = totalProcessedSlabs;
+ lbpStateV2->SlabAllocationBitMapLength = ((totalProcessedSlabs - 1) / (ULONGLONG)bitMapGranularityInBits) + 1;
+ lbpStateV2->Version = DEVICE_DATA_SET_LB_PROVISIONING_STATE_VERSION_V2;
+
+ //
+ // Note that there is already one element of the bitmap array allocated
+ // in the DEVICE_DATA_SET_LB_PROVISIONING_STATE_V2 structure itself, which
+ // is why we subtract 1 from SlabAllocationBitMapLength.
+ //
+ lbpStateV2->Size = sizeof(DEVICE_DATA_SET_LB_PROVISIONING_STATE_V2)
+ + ((lbpStateV2->SlabAllocationBitMapLength - 1) * sizeof(lbpStateV2->SlabAllocationBitMap[0]));
+
+ } else
+ #endif
+ {
+
+ lbpState->SlabSizeInBytes = bytesPerSlab;
+ lbpState->SlabOffsetDeltaInBytes = (ULONG)startingOffsetDelta;
+ lbpState->SlabAllocationBitMapBitCount = totalProcessedSlabs;
+ lbpState->SlabAllocationBitMapLength = ((totalProcessedSlabs - 1) / bitMapGranularityInBits) + 1;
+ lbpState->Version = DEVICE_DATA_SET_LB_PROVISIONING_STATE_VERSION_V1;
+
+ //
+ // Note that there is already one element of the bitmap array allocated
+ // in the DEVICE_DATA_SET_LB_PROVISIONING_STATE structure itself, which
+ // is why we subtract 1 from SlabAllocationBitMapLength.
+ //
+ lbpState->Size = sizeof(DEVICE_DATA_SET_LB_PROVISIONING_STATE)
+ + ((lbpState->SlabAllocationBitMapLength - 1) * sizeof(lbpState->SlabAllocationBitMap[0]));
+ }
+
+ DsmOutput->OutputBlockLength = lbpState->Size; // Size is at the same offset in all versions of the structure.
+ DsmOutput->OutputBlockOffset = sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES_OUTPUT);
+ *DsmOutputLength = DsmOutput->Size + DsmOutput->OutputBlockLength;
+
+ status = STATUS_SUCCESS;
+ }
+
+ TracePrint((TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_IOCTL,
+ "ClasspDeviceGetLBAStatusWorker (%p): Processed a total of %u slabs\n",
+ DeviceObject,
+ totalProcessedSlabs));
+Exit:
+
+ FREE_POOL(lbaStatusListHeader);
+ return status;
+}
+
+NTSTATUS ClassGetLBProvisioningLogPage(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _In_ PSCSI_REQUEST_BLOCK Srb,
+ _In_ ULONG LogPageSize,
+ _Inout_ PLOG_PAGE_LOGICAL_BLOCK_PROVISIONING LogPage
+ )
+/*
+Routine Description:
+
+ This function sends a LOG SENSE command to the given device and returns the
+ Logical Block Provisioning Log Page, if available.
+
+Arguments:
+ DeviceObject: The FDO of the device to which the Log Sense command will be sent.
+ Srb: This should be allocated before it is passed in, but it does not have
+ to be initialized. This function will initialize it.
+ LogPageSize: The size of the LogPage buffer in bytes.
+ LogPage: A pointer to an already allocated output buffer that may contain
+ the LBP log page when this function returns.
+
+Return Value:
+
+ STATUS_INVALID_PARAMETER: May be returned if the LogPage buffer is NULL or
+ not large enough.
+ STATUS_SUCCESS: The log page was obtained and placed in the LogPage buffer.
+
+ This function may return other NTSTATUS codes from internal function calls.
+--*/
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+ NTSTATUS status = STATUS_SUCCESS;
+ PCDB cdb = NULL;
+
+ //
+ // Make sure the caller passed in an adequate output buffer. The Allocation
+ // Length field in the Log Sense command is only 2 bytes so we need to also
+ // make sure that the given log page size isn't larger than MAXUSHORT.
+ //
+ if (LogPage == NULL ||
+ LogPageSize < sizeof(LOG_PAGE_LOGICAL_BLOCK_PROVISIONING) ||
+ LogPageSize > MAXUSHORT)
+ {
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_GENERAL,
+ "ClassGetLBProvisioningLogPage: DO (%p), Invalid parameter, LogPage = %p, LogPageSize = %u.\n",
+ DeviceObject,
+ LogPage,
+ LogPageSize));
+
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // Initialize the SRB.
+ //
+ if (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ status = InitializeStorageRequestBlock((PSTORAGE_REQUEST_BLOCK)Srb,
+ STORAGE_ADDRESS_TYPE_BTL8,
+ CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE,
+ 1,
+ SrbExDataTypeScsiCdb16);
+ if (NT_SUCCESS(status)) {
+ ((PSTORAGE_REQUEST_BLOCK)Srb)->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
+ } else {
+ //
+ // Should not occur.
+ //
+ NT_ASSERT(FALSE);
+ }
+ } else {
+ RtlZeroMemory(Srb, sizeof(SCSI_REQUEST_BLOCK));
+ Srb->Length = sizeof(SCSI_REQUEST_BLOCK);
+ Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
+ }
+
+ //
+ // Build and send down the Log Sense command.
+ //
+ SrbSetTimeOutValue(Srb, fdoExtension->TimeOutValue);
+ SrbSetRequestTag(Srb, SP_UNTAGGED);
+ SrbSetRequestAttribute(Srb, SRB_SIMPLE_TAG_REQUEST);
+ SrbAssignSrbFlags(Srb, fdoExtension->SrbFlags);
+ SrbSetCdbLength(Srb, sizeof(cdb->LOGSENSE));
+
+ cdb = SrbGetCdb(Srb);
+ cdb->LOGSENSE.OperationCode = SCSIOP_LOG_SENSE;
+ cdb->LOGSENSE.PageCode = LOG_PAGE_CODE_LOGICAL_BLOCK_PROVISIONING;
+ cdb->LOGSENSE.PCBit = 0;
+ cdb->LOGSENSE.ParameterPointer[0] = 0;
+ cdb->LOGSENSE.ParameterPointer[1] = 0;
+ REVERSE_BYTES_SHORT(&(cdb->LOGSENSE.AllocationLength), &LogPageSize);
+
+ status = ClassSendSrbSynchronous(fdoExtension->DeviceObject,
+ Srb,
+ LogPage,
+ LogPageSize,
+ FALSE);
+
+ //
+ // Handle the case where we get back STATUS_DATA_OVERRUN b/c the input
+ // buffer was larger than necessary.
+ //
+ if (status == STATUS_DATA_OVERRUN &&
+ SrbGetDataTransferLength(Srb) < LogPageSize)
+ {
+ status = STATUS_SUCCESS;
+ }
+
+ //
+ // Log the command.
+ //
+ TracePrint((TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_IOCTL,
+ "ClassGetLBProvisioningLogPage: DO (%p), LogSense command issued for LBP log page. NT Status: %!STATUS!.\n",
+ DeviceObject,
+ status
+ ));
+
+ return status;
+}
+
+NTSTATUS ClassInterpretLBProvisioningLogPage(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _In_ ULONG LogPageSize,
+ _In_ PLOG_PAGE_LOGICAL_BLOCK_PROVISIONING LogPage,
+ _In_ ULONG ResourcesSize,
+ _Out_ PSTORAGE_LB_PROVISIONING_MAP_RESOURCES Resources
+ )
+/*
+Routine Description:
+
+ This function takes a Logical Block Provisioning log page (returned by
+ ClassGetLBProvisioningLogPage(), for example), interprets its contents,
+ and returns the interpreted data in a STORAGE_LB_PROVISIONING_MAP_RESOURCES
+ structure.
+
+ None, some, or all of the data in the output buffer may be valid. The
+ caller must look at the individual "Valid" fields to see which fields have
+ valid data.
+
+Arguments:
+ DeviceObject: The FDO of the device from which the log page was obtained.
+ LogPageSize: The size of the LogPage buffer in bytes.
+ LogPage: A pointer to a valid LBP log page structure.
+ ResourcesSize: The size of the Resources buffer in bytes.
+ Resources: A pointer to an already allocated output buffer that may contain
+ the interpreted log page data when this function returns.
+
+Return Value:
+
+ STATUS_NOT_SUPPORTED: May be returned if the threshold exponent from the
+ 0xB2 page is invalid.
+ STATUS_INVALID_PARAMETER: May be returned if either the LogPage or Resources
+ buffers are NULL or too small.
+ STATUS_SUCCESS: The log page data was interpreted and the Resources output
+ buffer has data in it.
+
+ This function may return other NTSTATUS codes from internal function calls.
+--*/
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+ USHORT pageLength;
+ PLOG_PARAMETER_HEADER parameter;
+ PVOID endOfPage;
+ USHORT parameterCode;
+ ULONG resourceCount;
+ UCHAR thresholdExponent = fdoExtension->FunctionSupportInfo->LBProvisioningData.ThresholdExponent;
+ ULONGLONG thresholdSetSize;
+
+ //
+ // SBC-3 states that the threshold exponent (from the 0xB2 VPD page), must
+ // be non-zero and less than or equal to 32.
+ //
+ if (thresholdExponent < 0 || thresholdExponent > 32)
+ {
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_GENERAL,
+ "ClassInterpretLBProvisioningLogPage: DO (%p), Threshold Exponent (%u) is invalid.\n",
+ DeviceObject,
+ thresholdExponent));
+
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ if (Resources == NULL ||
+ ResourcesSize < sizeof(STORAGE_LB_PROVISIONING_MAP_RESOURCES) ||
+ LogPage == NULL ||
+ LogPageSize < sizeof(LOG_PAGE_LOGICAL_BLOCK_PROVISIONING))
+ {
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_GENERAL,
+ "ClassInterpretLBProvisioningLogPage: DO (%p), Invalid parameter, Resources = %p, ResourcesSize = %u, LogPage = %p, LogPageSize = %u.\n",
+ DeviceObject,
+ Resources,
+ ResourcesSize,
+ LogPage,
+ LogPageSize));
+
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // Calculate the threshold set size (in LBAs).
+ //
+ thresholdSetSize = 1I64 << thresholdExponent;
+
+ REVERSE_BYTES_SHORT(&pageLength, &(LogPage->PageLength));
+
+ //
+ // Initialize the output buffer.
+ //
+ RtlZeroMemory(Resources, sizeof(STORAGE_LB_PROVISIONING_MAP_RESOURCES));
+ Resources->Size = sizeof(STORAGE_LB_PROVISIONING_MAP_RESOURCES);
+ Resources->Version = sizeof(STORAGE_LB_PROVISIONING_MAP_RESOURCES);
+
+ //
+ // Make sure we don't walk off the end of the log page buffer
+ // if pageLength is somehow longer than the buffer itself.
+ //
+ pageLength = (USHORT)min(pageLength, (LogPageSize - FIELD_OFFSET(LOG_PAGE_LOGICAL_BLOCK_PROVISIONING, Parameters)));
+
+ parameter = (PLOG_PARAMETER_HEADER)((PUCHAR)LogPage + FIELD_OFFSET(LOG_PAGE_LOGICAL_BLOCK_PROVISIONING, Parameters));
+ endOfPage = (PVOID)((PUCHAR)parameter + pageLength);
+
+ //
+ // Walk the parameters.
+ //
+ while ((PVOID)parameter < endOfPage)
+ {
+ if (parameter->ParameterLength > 0)
+ {
+ REVERSE_BYTES_SHORT(&parameterCode, &(parameter->ParameterCode));
+ switch(parameterCode)
+ {
+ case LOG_PAGE_LBP_PARAMETER_CODE_AVAILABLE:
+ {
+ REVERSE_BYTES(&resourceCount, &(((PLOG_PARAMETER_THRESHOLD_RESOURCE_COUNT)parameter)->ResourceCount));
+ Resources->AvailableMappingResources = (ULONGLONG)resourceCount * thresholdSetSize * (ULONGLONG)fdoExtension->DiskGeometry.BytesPerSector;
+ Resources->AvailableMappingResourcesValid = TRUE;
+
+ //
+ // Devices that implement SBC-3 revisions older than r27 will not specify
+ // an LBP log page parameter that has fields beyond ResourceCount.
+ //
+ if (parameter->ParameterLength > FIELD_OFFSET(LOG_PARAMETER_THRESHOLD_RESOURCE_COUNT, ResourceCount[3])) {
+ Resources->AvailableMappingResourcesScope = ((PLOG_PARAMETER_THRESHOLD_RESOURCE_COUNT)parameter)->Scope;
+ }
+
+ break;
+ }
+
+ case LOG_PAGE_LBP_PARAMETER_CODE_USED:
+ {
+ REVERSE_BYTES(&resourceCount, &(((PLOG_PARAMETER_THRESHOLD_RESOURCE_COUNT)parameter)->ResourceCount));
+ Resources->UsedMappingResources = (ULONGLONG)resourceCount * thresholdSetSize * (ULONGLONG)fdoExtension->DiskGeometry.BytesPerSector;
+ Resources->UsedMappingResourcesValid = TRUE;
+
+ //
+ // Devices that implement SBC-3 revisions older than r27 will not specify
+ // an LBP log page parameter that has fields beyond ResourceCount.
+ //
+ if (parameter->ParameterLength > FIELD_OFFSET(LOG_PARAMETER_THRESHOLD_RESOURCE_COUNT, ResourceCount[3])) {
+ Resources->UsedMappingResourcesScope = ((PLOG_PARAMETER_THRESHOLD_RESOURCE_COUNT)parameter)->Scope;
+ }
+
+ break;
+ }
+ }
+ }
+
+ //
+ // Move to the next parameter.
+ //
+ parameter = (PLOG_PARAMETER_HEADER)((PUCHAR)parameter + sizeof(LOG_PARAMETER_HEADER) + parameter->ParameterLength);
+ }
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS ClassGetLBProvisioningResources(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _Inout_ PSCSI_REQUEST_BLOCK Srb,
+ _In_ ULONG ResourcesSize,
+ _Inout_ PSTORAGE_LB_PROVISIONING_MAP_RESOURCES Resources
+ )
+/*
+Routine Description:
+
+ This function obtains the Logical Block Provisioning log page, interprets
+ its contents, and returns the interpreted data in a
+ STORAGE_LB_PROVISIONING_MAP_RESOURCES structure.
+
+ None, some, or all of the data in the output buffer may be valid. The
+ caller must look at the individual "Valid" fields to see which fields have
+ valid data.
+
+Arguments:
+ DeviceObject: The target FDO.
+ Srb: This should be allocated before it is passed in, but it does not have
+ to be initialized.
+ ResourcesSize: The size of the Resources buffer in bytes.
+ Resources: A pointer to an already allocated output buffer that may contain
+ the interpreted log page data when this function returns.
+
+Return Value:
+
+ STATUS_NOT_SUPPORTED: May be returned if the device does not have LBP enabled.
+ STATUS_INVALID_PARAMETER: May be returned if either the Resources buffer is
+ NULL or too small.
+ STATUS_INSUFFICIENT_RESOURCES: May be returned if a log page buffer could not
+ be allocated.
+ STATUS_SUCCESS: The log page data was obtained and the Resources output
+ buffer has data in it.
+
+ This function may return other NTSTATUS codes from internal function calls.
+--*/
+{
+ NTSTATUS status;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+ ULONG logPageSize;
+ PLOG_PAGE_LOGICAL_BLOCK_PROVISIONING logPage = NULL;
+
+ //
+ // This functionality is only supported for devices that support logical
+ // block provisioning.
+ //
+ if (fdoExtension->FunctionSupportInfo->ValidInquiryPages.LBProvisioning == FALSE)
+ {
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_GENERAL,
+ "ClassGetLBProvisioningResources: DO (%p), Device does not support logical block provisioning.\n",
+ DeviceObject));
+
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ //
+ // Validate the output buffer.
+ //
+ if (Resources == NULL ||
+ ResourcesSize < sizeof(STORAGE_LB_PROVISIONING_MAP_RESOURCES))
+ {
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_GENERAL,
+ "ClassGetLBProvisioningResources: DO (%p), Invalid parameter, Resources = %p, ResourcesSize = %u.\n",
+ DeviceObject,
+ Resources,
+ ResourcesSize));
+
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // Allocate a buffer for the log page. Currently the log page contains:
+ // 1. Log page header
+ // 2. Log page parameter for used resources
+ // 3. Log page parameter for available resources
+ //
+ logPageSize = sizeof(LOG_PAGE_LOGICAL_BLOCK_PROVISIONING) + (2 * sizeof(LOG_PARAMETER_THRESHOLD_RESOURCE_COUNT));
+
+#if defined(_ARM_) || defined(_ARM64_)
+ //
+ // ARM has specific alignment requirements, although this will not have a functional impact on x86 or amd64
+ // based platforms. We are taking the conservative approach here.
+ //
+ logPageSize = ALIGN_UP_BY(logPageSize, KeGetRecommendedSharedDataAlignment());
+ logPage = (PLOG_PAGE_LOGICAL_BLOCK_PROVISIONING)ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned, logPageSize, CLASS_TAG_LB_PROVISIONING);
+#else
+ logPage = (PLOG_PAGE_LOGICAL_BLOCK_PROVISIONING)ExAllocatePoolWithTag(NonPagedPoolNx, logPageSize, CLASS_TAG_LB_PROVISIONING);
+#endif
+ if (logPage != NULL)
+ {
+ //
+ // Get the LBP log page from the device.
+ //
+ status = ClassGetLBProvisioningLogPage(DeviceObject,
+ Srb,
+ logPageSize,
+ logPage);
+
+ if (NT_SUCCESS(status))
+ {
+ //
+ // Interpret the log page and fill in the output buffer.
+ //
+ status = ClassInterpretLBProvisioningLogPage(DeviceObject,
+ logPageSize,
+ logPage,
+ ResourcesSize,
+ Resources);
+ }
+
+ ExFreePool(logPage);
+ }
+ else
+ {
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_GENERAL,
+ "ClassGetLBProvisioningResources: DO (%p), Failed to allocate memory for LBP log page.\n",
+ DeviceObject));
+
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ return status;
+}
+
+NTSTATUS
+ClassDeviceGetLBProvisioningResources(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _Inout_ PIRP Irp,
+ _Inout_ PSCSI_REQUEST_BLOCK Srb
+ )
+/*
+Routine Description:
+
+ This function returns the LBP resource counts in a
+ STORAGE_LB_PROVISIONING_MAP_RESOURCES structure in the IRP.
+
+ None, some, or all of the data in the output buffer may be valid. The
+ caller must look at the individual "Valid" fields to see which fields have
+ valid data.
+
+Arguments:
+ DeviceObject: The target FDO.
+ Irp: The IRP which will contain the output buffer upon completion.
+ Srb: This should be allocated before it is passed in, but it does not have
+ to be initialized.
+
+Return Value:
+
+ Some NTSTATUS code.
+
+--*/
+{
+ NTSTATUS status;
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
+ PSTORAGE_LB_PROVISIONING_MAP_RESOURCES mapResources = (PSTORAGE_LB_PROVISIONING_MAP_RESOURCES)Irp->AssociatedIrp.SystemBuffer;
+
+ status = ClassGetLBProvisioningResources(DeviceObject,
+ Srb,
+ irpStack->Parameters.DeviceIoControl.OutputBufferLength,
+ mapResources);
+
+ if (NT_SUCCESS(status)) {
+ Irp->IoStatus.Information = mapResources->Size;
+ } else {
+ Irp->IoStatus.Information = 0;
+ }
+
+ Irp->IoStatus.Status = status;
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+
+ return status;
+}
+
+_Function_class_(IO_WORKITEM_ROUTINE)
+_IRQL_requires_(PASSIVE_LEVEL)
+_IRQL_requires_same_
+VOID
+ClassLogThresholdEvent(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _In_opt_ PVOID Context
+ )
+/*
+ Routine Description:
+
+ This function logs a logical block provisioning soft threshold event to the
+ system event log.
+
+Arguments:
+ DeviceObject: The FDO that represents the device that reported the soft
+ threshold.
+ Context: A pointer to the IO_WORKITEM in which this function is running.
+
+--*/
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PIO_WORKITEM workItem = (PIO_WORKITEM)Context;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+ PSCSI_REQUEST_BLOCK srb = NULL;
+ STORAGE_LB_PROVISIONING_MAP_RESOURCES resources = {0};
+ ULONG resourcesSize = sizeof(STORAGE_LB_PROVISIONING_MAP_RESOURCES);
+ PIO_ERROR_LOG_PACKET errorLogEntry = NULL;
+ ULONG logEntrySize = sizeof(IO_ERROR_LOG_PACKET);
+ PWCHAR stringIndex = NULL;
+ LONG stringSize = 0;
+ ULONG srbSize;
+
+ //
+ // Allocate an SRB for getting the LBP log page.
+ //
+ if ((fdoExtension->AdapterDescriptor != NULL) &&
+ (fdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK)) {
+ srbSize = CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE;
+ } else {
+ srbSize = sizeof(SCSI_REQUEST_BLOCK);
+ }
+
+ srb = ExAllocatePoolWithTag(NonPagedPoolNx,
+ srbSize,
+ 'ACcS');
+ if (srb != NULL) {
+
+ //
+ // Try to get the LBP resources from the device so we can report them in
+ // the system event log.
+ //
+ ClassGetLBProvisioningResources(DeviceObject,
+ srb,
+ resourcesSize,
+ &resources);
+
+ //
+ // We need to allocate enough space for 3 insertion strings:
+ // The first is a ULONG representing the disk number in decimal, which means
+ // a max of 10 digits, plus one for the NULL character.
+ // The second and third are ULONGLONGs representing the used and available
+ // bytes, which means a max of 20 digits, plus one for the NULL character.
+ // Make sure we do not exceed the max error log size or the max size of a
+ // UCHAR since the size gets truncated to a UCHAR when we pass it to
+ // IoAllocateErrorLogEntry().
+ //
+ logEntrySize = sizeof(IO_ERROR_LOG_PACKET) + (11 * sizeof(WCHAR)) + (2 * (21 * sizeof(WCHAR)));
+ logEntrySize = min(logEntrySize, ERROR_LOG_MAXIMUM_SIZE);
+ logEntrySize = min(logEntrySize, MAXUCHAR);
+
+ errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry(DeviceObject, (UCHAR)logEntrySize);
+ if (errorLogEntry != NULL)
+ {
+ //
+ // There are two event IDs we can use here. Both use the disk number,
+ // but one reports the available and used bytes while the other does not.
+ // We fall back on the latter if we failed to obtain the available and
+ // used byte counts from the LBP log page.
+ //
+ // The event insertion strings need to be in this order:
+ // 1. The disk number. (Both event IDs use this.)
+ // 2. Bytes used.
+ // 3. Bytes available.
+ //
+
+ RtlZeroMemory(errorLogEntry, logEntrySize);
+ errorLogEntry->StringOffset = sizeof(IO_ERROR_LOG_PACKET);
+
+ stringIndex = (PWCHAR)((ULONG_PTR)errorLogEntry + sizeof(IO_ERROR_LOG_PACKET));
+ stringSize = logEntrySize - sizeof(IO_ERROR_LOG_PACKET);
+
+ //
+ // Add the disk number to the insertion strings.
+ //
+ status = RtlStringCbPrintfW(stringIndex, stringSize, L"%d", fdoExtension->DeviceNumber);
+
+ if (NT_SUCCESS(status) )
+ {
+ errorLogEntry->NumberOfStrings++;
+
+ if (resources.UsedMappingResourcesValid &&
+ resources.AvailableMappingResourcesValid)
+ {
+ //
+ // Add the used mapping resources to the insertion strings.
+ //
+ stringIndex += (wcslen(stringIndex) + 1);
+ stringSize -= (LONG)(wcslen(stringIndex) + 1) * sizeof(WCHAR);
+
+ status = RtlStringCbPrintfW(stringIndex, stringSize, L"%I64u", resources.UsedMappingResources);
+
+ if (NT_SUCCESS(status))
+ {
+ errorLogEntry->NumberOfStrings++;
+
+ //
+ // Add the available mapping resources to the insertion strings.
+ //
+ stringIndex += (wcslen(stringIndex) + 1);
+ stringSize -= (LONG)(wcslen(stringIndex) + 1) * sizeof(WCHAR);
+
+ status = RtlStringCbPrintfW(stringIndex, stringSize, L"%I64u", resources.AvailableMappingResources);
+
+ if (NT_SUCCESS(status))
+ {
+ errorLogEntry->NumberOfStrings++;
+ }
+ }
+ }
+ else
+ {
+ TracePrint((TRACE_LEVEL_WARNING,
+ TRACE_FLAG_GENERAL,
+ "ClassLogThresholdEvent: DO (%p), Used and available mapping resources were unavailable.\n",
+ DeviceObject));
+ }
+ }
+
+ //
+ // If we were able to successfully assemble all 3 insertion strings,
+ // then we can use one of the "extended" event IDs. Otherwise, use the basic
+ // event ID, which only requires the disk number.
+ //
+ if (errorLogEntry->NumberOfStrings == 3)
+ {
+ if (resources.UsedMappingResourcesScope == LOG_PAGE_LBP_RESOURCE_SCOPE_DEDICATED_TO_LUN &&
+ resources.AvailableMappingResourcesScope == LOG_PAGE_LBP_RESOURCE_SCOPE_DEDICATED_TO_LUN) {
+
+ errorLogEntry->ErrorCode = IO_WARNING_SOFT_THRESHOLD_REACHED_EX_LUN_LUN;
+
+ } else if (resources.UsedMappingResourcesScope == LOG_PAGE_LBP_RESOURCE_SCOPE_DEDICATED_TO_LUN &&
+ resources.AvailableMappingResourcesScope == LOG_PAGE_LBP_RESOURCE_SCOPE_NOT_DEDICATED_TO_LUN) {
+
+ errorLogEntry->ErrorCode = IO_WARNING_SOFT_THRESHOLD_REACHED_EX_LUN_POOL;
+
+ } else if (resources.UsedMappingResourcesScope == LOG_PAGE_LBP_RESOURCE_SCOPE_NOT_DEDICATED_TO_LUN &&
+ resources.AvailableMappingResourcesScope == LOG_PAGE_LBP_RESOURCE_SCOPE_DEDICATED_TO_LUN) {
+
+ errorLogEntry->ErrorCode = IO_WARNING_SOFT_THRESHOLD_REACHED_EX_POOL_LUN;
+
+ } else if (resources.UsedMappingResourcesScope == LOG_PAGE_LBP_RESOURCE_SCOPE_NOT_DEDICATED_TO_LUN &&
+ resources.AvailableMappingResourcesScope == LOG_PAGE_LBP_RESOURCE_SCOPE_NOT_DEDICATED_TO_LUN) {
+
+ errorLogEntry->ErrorCode = IO_WARNING_SOFT_THRESHOLD_REACHED_EX_POOL_POOL;
+
+ } else {
+
+ errorLogEntry->ErrorCode = IO_WARNING_SOFT_THRESHOLD_REACHED_EX;
+ }
+ }
+ else
+ {
+ errorLogEntry->ErrorCode = IO_WARNING_SOFT_THRESHOLD_REACHED;
+ }
+
+ //
+ // Write the error log packet to the system error logging thread.
+ // It will be freed automatically.
+ //
+ IoWriteErrorLogEntry(errorLogEntry);
+
+ TracePrint((TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_GENERAL,
+ "ClassLogThresholdEvent: DO (%p), Soft threshold notification logged.\n",
+ DeviceObject));
+ }
+ else
+ {
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_GENERAL,
+ "ClassLogThresholdEvent: DO (%p), Failed to allocate memory for error log entry.\n",
+ DeviceObject));
+ }
+ } else {
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_GENERAL,
+ "ClassLogThresholdEvent: DO (%p), Failed to allocate memory for SRB.\n",
+ DeviceObject));
+ }
+
+
+ //
+ // Clear the soft threshold event pending flag so that another can be queued.
+ //
+ InterlockedExchange((PLONG)&(fdoExtension->FunctionSupportInfo->LBProvisioningData.SoftThresholdEventPending), 0);
+
+ ClassReleaseRemoveLock(DeviceObject, (PIRP)workItem);
+
+ FREE_POOL(srb);
+
+ if (workItem != NULL) {
+ IoFreeWorkItem(workItem);
+ }
+}
+
+NTSTATUS
+ClasspLogSystemEventWithDeviceNumber(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _In_ NTSTATUS IoErrorCode
+ )
+/*
+ Routine Description:
+
+ This function is a helper routine to log any system events that require
+ the DeviceNumber (e.g. disk number). It is basically a wrapper for the
+ IoWriteErrorLogEntry call.
+
+Arguments:
+ DeviceObject: The FDO that represents the device for which the event needs to be logged.
+ IoErrorCode: The IO error code for the event.
+
+Return Value:
+ STATUS_SUCCESS - if the event was logged
+ STATUS_INSUFFICIENT_RESOURCES - otherwise
+
+--*/
+{
+ NTSTATUS status = STATUS_INSUFFICIENT_RESOURCES;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+ PIO_ERROR_LOG_PACKET errorLogEntry = NULL;
+ ULONG logEntrySize = sizeof(IO_ERROR_LOG_PACKET);
+ PWCHAR stringIndex = NULL;
+ LONG stringSize = 0;
+
+ //
+ // We need to allocate enough space for one insertion string: a ULONG
+ // representing the disk number in decimal, which means a max of 10 digits,
+ // plus one for the NULL character.
+ // Make sure we do not exceed the max error log size or the max size of a
+ // UCHAR since the size gets truncated to a UCHAR when we pass it to
+ // IoAllocateErrorLogEntry().
+ //
+ logEntrySize = sizeof(IO_ERROR_LOG_PACKET) + (11 * sizeof(WCHAR));
+ logEntrySize = min(logEntrySize, ERROR_LOG_MAXIMUM_SIZE);
+ logEntrySize = min(logEntrySize, MAXUCHAR);
+
+ errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry(DeviceObject, (UCHAR)logEntrySize);
+ if (errorLogEntry) {
+
+ RtlZeroMemory(errorLogEntry, logEntrySize);
+ errorLogEntry->StringOffset = sizeof(IO_ERROR_LOG_PACKET);
+ errorLogEntry->ErrorCode = IoErrorCode;
+
+ stringIndex = (PWCHAR)((ULONG_PTR)errorLogEntry + sizeof(IO_ERROR_LOG_PACKET));
+ stringSize = logEntrySize - sizeof(IO_ERROR_LOG_PACKET);
+
+ //
+ // Add the disk number to the insertion strings.
+ //
+ status = RtlStringCbPrintfW(stringIndex, stringSize, L"%d", fdoExtension->DeviceNumber);
+
+ if (NT_SUCCESS(status)) {
+ errorLogEntry->NumberOfStrings++;
+ }
+
+ //
+ // Write the error log packet to the system error logging thread.
+ // It will be freed automatically.
+ //
+ IoWriteErrorLogEntry(errorLogEntry);
+
+ status = STATUS_SUCCESS;
+ }
+
+ return status;
+}
+
+_Function_class_(IO_WORKITEM_ROUTINE)
+_IRQL_requires_(PASSIVE_LEVEL)
+_IRQL_requires_same_
+VOID
+ClassLogResourceExhaustionEvent(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _In_opt_ PVOID Context
+ )
+/*
+ Routine Description:
+
+ This function logs a logical block provisioning permanent resource exhaustion
+ event to the system event log.
+
+Arguments:
+ DeviceObject: The FDO that represents the device that reported the permanent
+ resource exhaustion.
+ Context: A pointer to the IO_WORKITEM in which this function is running.
+
+--*/
+{
+ PIO_WORKITEM workItem = (PIO_WORKITEM)Context;
+
+ if (NT_SUCCESS(ClasspLogSystemEventWithDeviceNumber(DeviceObject, IO_ERROR_DISK_RESOURCES_EXHAUSTED))) {
+
+ TracePrint((TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_GENERAL,
+ "ClassLogResourceExhaustionEvent: DO (%p), Permanent resource exhaustion logged.\n",
+ DeviceObject));
+ } else {
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_GENERAL,
+ "ClassLogResourceExhaustionEvent: DO (%p), Failed to allocate memory for error log entry.\n",
+ DeviceObject));
+ }
+
+
+ ClassReleaseRemoveLock(DeviceObject, (PIRP)workItem);
+
+ if (workItem != NULL) {
+ IoFreeWorkItem(workItem);
+ }
+}
+
+
+VOID ClassQueueThresholdEventWorker(
+ _In_ PDEVICE_OBJECT DeviceObject
+ )
+/*
+Routine Description:
+
+ This function queues a delayed work item that will eventually log a
+ logical block provisioning soft threshold event to the system event log.
+
+Arguments:
+ DeviceObject: The FDO that represents the device that reported the soft
+ threshold.
+
+--*/
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = (PCOMMON_DEVICE_EXTENSION)(DeviceObject->DeviceExtension);
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)(DeviceObject->DeviceExtension);
+ PIO_WORKITEM workItem = NULL;
+
+ if (commonExtension->IsFdo &&
+ InterlockedCompareExchange((PLONG)&(fdoExtension->FunctionSupportInfo->LBProvisioningData.SoftThresholdEventPending), 1, 0) == 0)
+ {
+ workItem = IoAllocateWorkItem(DeviceObject);
+
+ if (workItem)
+ {
+
+ TracePrint((TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_GENERAL,
+ "ClassQueueThresholdEventWorker: DO (%p), Queueing soft threshold notification work item.\n",
+ DeviceObject));
+
+
+ ClassAcquireRemoveLock(DeviceObject, (PIRP)(workItem));
+
+ //
+ // Queue a work item to write the threshold notification to the
+ // system event log.
+ //
+ IoQueueWorkItem(workItem, ClassLogThresholdEvent, DelayedWorkQueue, workItem);
+ }
+ else
+ {
+ //
+ // Clear the soft threshold event pending flag since this is normally
+ // done when the work item completes.
+ //
+ InterlockedExchange((PLONG)&(fdoExtension->FunctionSupportInfo->LBProvisioningData.SoftThresholdEventPending), 0);
+
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_GENERAL,
+ "ClassQueueThresholdEventWorker: DO (%p), Failed to allocate memory for the work item.\n",
+ DeviceObject));
+ }
+ }
+}
+
+VOID ClassQueueResourceExhaustionEventWorker(
+ _In_ PDEVICE_OBJECT DeviceObject
+ )
+/*
+Routine Description:
+
+ This function queues a delayed work item that will eventually log a
+ logical block provisioning permanent resource exhaustion event to the
+ system event log.
+
+Arguments:
+ DeviceObject: The FDO that represents the device that reported the resource
+ exhaustion.
+
+--*/
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = (PCOMMON_DEVICE_EXTENSION)(DeviceObject->DeviceExtension);
+ PIO_WORKITEM workItem = NULL;
+
+ if (commonExtension->IsFdo)
+ {
+ workItem = IoAllocateWorkItem(DeviceObject);
+
+ if (workItem)
+ {
+
+ TracePrint((TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_GENERAL,
+ "ClassQueueResourceExhaustionEventWorker: DO (%p), Queueing permanent resource exhaustion event work item.\n",
+ DeviceObject));
+
+ ClassAcquireRemoveLock(DeviceObject, (PIRP)(workItem));
+
+ //
+ // Queue a work item to write the threshold notification to the
+ // system event log.
+ //
+ IoQueueWorkItem(workItem, ClassLogResourceExhaustionEvent, DelayedWorkQueue, workItem);
+ }
+ else
+ {
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_GENERAL,
+ "ClassQueueResourceExhaustionEventWorker: DO (%p), Failed to allocate memory for the work item.\n",
+ DeviceObject));
+ }
+ }
+}
+
+_Function_class_(IO_WORKITEM_ROUTINE)
+_IRQL_requires_(PASSIVE_LEVEL)
+_IRQL_requires_same_
+VOID
+ClassLogCapacityChangedProcess(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _In_opt_ PVOID Context
+ )
+/*
+ Routine Description:
+
+ This function logs a capacity changed event to the system event log.
+
+Arguments:
+ DeviceObject: The FDO that represents the device that reported the capacity change.
+ Context: A pointer to the IO_WORKITEM in which this function is running.
+
+--*/
+{
+ NTSTATUS status;
+ PIO_WORKITEM workItem = (PIO_WORKITEM)Context;
+
+ status = ClasspLogSystemEventWithDeviceNumber(DeviceObject, IO_WARNING_DISK_CAPACITY_CHANGED);
+
+ if (NT_SUCCESS(status)) {
+
+ TracePrint((TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_GENERAL,
+ "ClassLogCapacityChangedEvent: DO (%p), Capacity changed logged.\n",
+ DeviceObject));
+ } else {
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_GENERAL,
+ "ClassLogCapacityChangedEvent: DO (%p), Failed to allocate memory for error log entry.\n",
+ DeviceObject));
+ }
+
+ //
+ // Get disk capacity and notify upper layer if capacity is changed.
+ //
+ status = ClassReadDriveCapacity(DeviceObject);
+
+ if (!NT_SUCCESS(status)) {
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_GENERAL,
+ "ClassLogCapacityChangedEvent: DO (%p), ClassReadDriveCapacity returned %!STATUS!.\n",
+ DeviceObject,
+ status));
+ }
+
+ ClassReleaseRemoveLock(DeviceObject, (PIRP)workItem);
+
+ if (workItem != NULL) {
+ IoFreeWorkItem(workItem);
+ }
+}
+
+
+VOID
+ClassQueueCapacityChangedEventWorker(
+ _In_ PDEVICE_OBJECT DeviceObject
+ )
+/*
+Routine Description:
+
+ This function queues a delayed work item that will eventually log a
+ disk capacity changed event to the system event log.
+
+Arguments:
+ DeviceObject: The FDO that represents the device that reported the capacity change.
+
+--*/
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = (PCOMMON_DEVICE_EXTENSION)(DeviceObject->DeviceExtension);
+ PIO_WORKITEM workItem = NULL;
+
+ if (commonExtension->IsFdo)
+ {
+ workItem = IoAllocateWorkItem(DeviceObject);
+
+ if (workItem)
+ {
+
+ TracePrint((TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_GENERAL,
+ "ClassQueueCapacityChangedEventWorker: DO (%p), Queueing capacity changed event work item.\n",
+ DeviceObject));
+
+ ClassAcquireRemoveLock(DeviceObject, (PIRP)(workItem));
+
+ //
+ // Queue a work item to write the threshold notification to the
+ // system event log.
+ //
+ IoQueueWorkItem(workItem, ClassLogCapacityChangedProcess, DelayedWorkQueue, workItem);
+ }
+ else
+ {
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_GENERAL,
+ "ClassQueueCapacityChangedEventWorker: DO (%p), Failed to allocate memory for the work item.\n",
+ DeviceObject));
+ }
+ }
+}
+
+_Function_class_(IO_WORKITEM_ROUTINE)
+_IRQL_requires_(PASSIVE_LEVEL)
+_IRQL_requires_same_
+VOID
+ClassLogProvisioningTypeChangedEvent(
+ PDEVICE_OBJECT DeviceObject,
+ PVOID Context
+ )
+/*
+ Routine Description:
+
+ This function logs a provisioning type changed event to the system event log.
+
+Arguments:
+ DeviceObject: The FDO that represents the device that reported the provisioning type change.
+ Context: A pointer to the IO_WORKITEM in which this function is running.
+
+--*/
+{
+ PIO_WORKITEM workItem = (PIO_WORKITEM)Context;
+
+ if (NT_SUCCESS(ClasspLogSystemEventWithDeviceNumber(DeviceObject, IO_WARNING_DISK_PROVISIONING_TYPE_CHANGED))) {
+
+ TracePrint((TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_GENERAL,
+ "ClassLogProvisioningTypeChangedEvent: DO (%p), LB Provisioning Type changed logged.\n",
+ DeviceObject));
+ } else {
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_GENERAL,
+ "ClassLogProvisioningTypeChangedEvent: DO (%p), Failed to allocate memory for error log entry.\n",
+ DeviceObject));
+ }
+
+ ClassReleaseRemoveLock(DeviceObject, (PIRP)workItem);
+
+ IoFreeWorkItem(workItem);
+}
+
+
+VOID
+ClassQueueProvisioningTypeChangedEventWorker(
+ _In_ PDEVICE_OBJECT DeviceObject
+ )
+/*
+Routine Description:
+
+ This function queues a delayed work item that will eventually log a
+ provisioning type changed event to the system event log.
+
+Arguments:
+ DeviceObject: The FDO that represents the device that reported the provisioning type change.
+
+--*/
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = (PCOMMON_DEVICE_EXTENSION)(DeviceObject->DeviceExtension);
+ PIO_WORKITEM workItem = NULL;
+
+ if (commonExtension->IsFdo)
+ {
+ workItem = IoAllocateWorkItem(DeviceObject);
+
+ if (workItem)
+ {
+
+ TracePrint((TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_GENERAL,
+ "ClassQueueProvisioningTypeChangedEventWorker: DO (%p), Queueing LB provisioning type changed event work item.\n",
+ DeviceObject));
+
+ ClassAcquireRemoveLock(DeviceObject, (PIRP)(workItem));
+
+ //
+ // Queue a work item to write the threshold notification to the
+ // system event log.
+ //
+ IoQueueWorkItem(workItem, ClassLogProvisioningTypeChangedEvent, DelayedWorkQueue, workItem);
+ }
+ else
+ {
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_GENERAL,
+ "ClassQueueProvisioningTypeChangedEventWorker: DO (%p), Failed to allocate memory for the work item.\n",
+ DeviceObject));
+ }
+ }
+}
+
+_Function_class_(IO_WORKITEM_ROUTINE)
+_IRQL_requires_(PASSIVE_LEVEL)
+_IRQL_requires_same_
+VOID
+ClasspLogIOEventWithContext(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _In_opt_ PVOID Context
+ )
+/*
+ Routine Description:
+
+ This function logs an event to the system event log with dumpdata containing opcode and
+ sense data.
+
+Arguments:
+ DeviceObject: The FDO that represents the device that retried the IO.
+ Context: A pointer to the OPCODE_SENSE_DATA_IO_LOG_MESSAGE_CONTEXT that has data to be logged as part of the message.
+
+--*/
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+ POPCODE_SENSE_DATA_IO_LOG_MESSAGE_CONTEXT_HEADER ioLogMessageContextHeader = (POPCODE_SENSE_DATA_IO_LOG_MESSAGE_CONTEXT_HEADER)Context;
+ PIO_WORKITEM workItem;
+ PIO_ERROR_LOG_PACKET errorLogEntry = NULL;
+ ULONG logEntrySize;
+ PWCHAR stringIndex = NULL;
+ LONG stringSize = 0;
+ ULONG senseBufferSize;
+ ULONG stringsBufferLength = 0;
+ ULONG pdoNameLength = 0;
+
+ NT_ASSERT(ioLogMessageContextHeader != NULL);
+ _Analysis_assume_(ioLogMessageContextHeader != NULL);
+
+ switch (ioLogMessageContextHeader->ErrorCode) {
+
+ case IO_ERROR_IO_HARDWARE_ERROR:
+ case IO_WARNING_IO_OPERATION_RETRIED: {
+
+ //
+ // We need to allocate enough space for 3 insertion strings:
+ // 1. A ULONGLONG in Hex representing the LBA which means a max of 16 digits,
+ // plus two for "0x" plus one for the NULL character.
+ // 2. A ULONG representing the disk number in decimal, which means
+ // a max of 10 digits, plus one for the NULL character.
+ // 3. The PDO name, so that if the disk number is hidden from the
+ // user for some reason, there is still a way to associate the
+ // event with the correct device.
+ //
+ stringsBufferLength = (19 + 11) * sizeof(WCHAR);
+
+ //
+ // Query for the size of the PDO name.
+ //
+ status = IoGetDeviceProperty(fdoExtension->LowerPdo,
+ DevicePropertyPhysicalDeviceObjectName,
+ 0,
+ NULL,
+ &pdoNameLength);
+
+ if (status == STATUS_BUFFER_TOO_SMALL && pdoNameLength > 0) {
+ stringsBufferLength += pdoNameLength;
+ } else {
+ pdoNameLength = 0;
+ }
+
+ break;
+ }
+
+ }
+
+ workItem = ioLogMessageContextHeader->WorkItem;
+
+ //
+ // DumpData[0] which is of ULONG size and will contain opcode|srbstatus|scsistatus.
+ // Then we will have sensebuffer, hence
+ // DumpDataSize = senseBufferSize + sizeof(ULONG)
+ // and DumpDataSize must be multiple of sizeof(ULONG)
+ // which means senseBufferSize needs to ULONG aligned
+ // Please note we will use original buffersize for padding later
+ //
+ senseBufferSize = ALIGN_UP_BY(ioLogMessageContextHeader->SenseDataSize, sizeof(ULONG));
+
+ logEntrySize = FIELD_OFFSET( IO_ERROR_LOG_PACKET, DumpData ) + sizeof(ULONG) + senseBufferSize;
+
+ //
+ // We need to make sure the string offset is WCHAR-aligned (the insertion strings
+ // come after the sense buffer in the dump data, if any).
+ // But we don't need to do anything special for it,
+ // since FIELD_OFFSET( IO_ERROR_LOG_PACKET, DumpData) is currently ULONG aligned
+ // and SenseBufferSize is also ULONG aligned. This means buffer that precedes the insertion string is ULONG aligned
+ // note stringoffset = FIELD_OFFSET( IO_ERROR_LOG_PACKET, DumpData ) + DumpDataSize
+ // This leads us to fact that stringoffset will always be ULONG aligned and effectively WCHAR aligned
+ //
+
+ //
+ // We need to allocate enough space for the insertion strings provided in the passed in Context
+ // as well as the opcode and the sense data, while making sure we cap at max error log size.
+ // The log packet is followed by the opcode, then the sense data, and then the
+ // insertion strings.
+ //
+ logEntrySize = logEntrySize + stringsBufferLength;
+
+ if (logEntrySize > ERROR_LOG_MAXIMUM_SIZE) {
+ if (senseBufferSize) {
+ if (logEntrySize - ERROR_LOG_MAXIMUM_SIZE < senseBufferSize) {
+ //
+ // In below steps, senseBufferSize will become same or less than as ioLogMessageContextHeader->SenseDataSize
+ // it can't be more than that.
+ //
+ senseBufferSize -= logEntrySize - ERROR_LOG_MAXIMUM_SIZE;
+
+ //
+ // Decrease the sensebuffersize further, if needed, to keep senseBufferSize ULONG aligned
+ //
+ senseBufferSize = ALIGN_DOWN_BY(senseBufferSize, sizeof(ULONG));
+
+ } else {
+ senseBufferSize = 0;
+ }
+ }
+ logEntrySize = ERROR_LOG_MAXIMUM_SIZE;
+ }
+
+ errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry(DeviceObject, (UCHAR)logEntrySize);
+
+ if (errorLogEntry) {
+
+ RtlZeroMemory(errorLogEntry, logEntrySize);
+ errorLogEntry->MajorFunctionCode = IRP_MJ_SCSI;
+ errorLogEntry->RetryCount = 1;
+ errorLogEntry->DumpDataSize = (USHORT)(sizeof(ULONG) + senseBufferSize);
+ errorLogEntry->StringOffset = (USHORT)(FIELD_OFFSET( IO_ERROR_LOG_PACKET, DumpData ) + errorLogEntry->DumpDataSize);
+ errorLogEntry->ErrorCode = ioLogMessageContextHeader->ErrorCode;
+ errorLogEntry->DumpData[0] = (((ULONG)(ioLogMessageContextHeader->OpCode)) << 24) |
+ (((ULONG)(ioLogMessageContextHeader->SrbStatus)) << 16) |
+ (((ULONG)(ioLogMessageContextHeader->ScsiStatus)) << 8);
+
+ //
+ // Copy sense data and do padding for sense data if needed, with '-'
+ //
+ if (senseBufferSize > ioLogMessageContextHeader->SenseDataSize) {
+ RtlCopyMemory(&errorLogEntry->DumpData[1], ioLogMessageContextHeader->SenseData, ioLogMessageContextHeader->SenseDataSize);
+ RtlFillMemory( (PCHAR)&errorLogEntry->DumpData[1] + ioLogMessageContextHeader->SenseDataSize , (senseBufferSize - ioLogMessageContextHeader->SenseDataSize) , '-' );
+ } else {
+ RtlCopyMemory(&errorLogEntry->DumpData[1], ioLogMessageContextHeader->SenseData, senseBufferSize);
+ }
+
+ stringIndex = (PWCHAR)((PCHAR)errorLogEntry->DumpData + errorLogEntry->DumpDataSize);
+ stringSize = logEntrySize - errorLogEntry->StringOffset;
+
+ //
+ // Add the strings
+ //
+ switch (ioLogMessageContextHeader->ErrorCode) {
+ case IO_ERROR_IO_HARDWARE_ERROR:
+ case IO_WARNING_IO_OPERATION_RETRIED: {
+
+ PIO_RETRIED_LOG_MESSAGE_CONTEXT ioLogMessageContext = (PIO_RETRIED_LOG_MESSAGE_CONTEXT)Context;
+
+ //
+ // The first is a "0x" plus ULONGLONG in hex representing the LBA plus the NULL character.
+ // The second is a ULONG representing the disk number plus the NULL character.
+ //
+ status = RtlStringCbPrintfW(stringIndex, stringSize, L"0x%I64x", ioLogMessageContext->Lba.QuadPart);
+ if (NT_SUCCESS(status)) {
+ errorLogEntry->NumberOfStrings++;
+
+ //
+ // Add the disk number to the insertion strings.
+ //
+ stringSize -= (ULONG)(wcslen(stringIndex) + 1) * sizeof(WCHAR);
+ stringIndex += (wcslen(stringIndex) + 1);
+
+ if (stringSize > 0) {
+
+ status = RtlStringCbPrintfW(stringIndex, stringSize, L"%d", ioLogMessageContext->DeviceNumber);
+
+ if (NT_SUCCESS(status)) {
+
+ errorLogEntry->NumberOfStrings++;
+
+ stringSize -= (ULONG)(wcslen(stringIndex) + 1) * sizeof(WCHAR);
+ stringIndex += (wcslen(stringIndex) + 1);
+
+ if (stringSize >= (LONG)pdoNameLength && pdoNameLength > 0) {
+ ULONG resultLength;
+
+ //
+ // Get the PDO name and place it in the insertion string buffer.
+ //
+ status = IoGetDeviceProperty(fdoExtension->LowerPdo,
+ DevicePropertyPhysicalDeviceObjectName,
+ pdoNameLength,
+ stringIndex,
+ &resultLength);
+
+ if (NT_SUCCESS(status) && resultLength > 0) {
+ errorLogEntry->NumberOfStrings++;
+ }
+ }
+ }
+ }
+ }
+
+ break;
+ }
+
+ }
+
+ //
+ // Write the error log packet to the system error logging thread.
+ // It will be freed automatically.
+ //
+ IoWriteErrorLogEntry(errorLogEntry);
+
+ TracePrint((TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_GENERAL,
+ "ClasspLogIORetriedEvent: DO (%p), Soft threshold notification logged.\n",
+ DeviceObject));
+ }
+
+ ClassReleaseRemoveLock(DeviceObject, (PIRP)workItem);
+
+ if (ioLogMessageContextHeader->SenseData) {
+ ExFreePool(ioLogMessageContextHeader->SenseData);
+ }
+ if (workItem) {
+ IoFreeWorkItem(workItem);
+ }
+ ExFreePool(ioLogMessageContextHeader);
+}
+
+
+VOID
+ClasspQueueLogIOEventWithContextWorker(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _In_ ULONG SenseBufferSize,
+ _In_ PVOID SenseData,
+ _In_ UCHAR SrbStatus,
+ _In_ UCHAR ScsiStatus,
+ _In_ ULONG ErrorCode,
+ _In_ ULONG CdbLength,
+ _In_opt_ PCDB Cdb,
+ _In_opt_ PTRANSFER_PACKET Pkt
+ )
+/*
+Routine Description:
+
+ Helper function that queues a delayed work item that will eventually
+ log an event to the system event log corresponding to passed in ErrorCode.
+ The dumpdata is fixed to include the opcode and the sense information.
+ But the number of insertion strings varies based on the passed in ErrorCode.
+
+Arguments:
+ DeviceObject: The FDO that represents the device that was the target of the IO.
+ SesneBufferSize: Size of the SenseData buffer.
+ SenseData: Error information from the target (to be included in the dump data).
+ SrbStatus: Srb status returned by the miniport.
+ ScsiStatus: SCSI status associated with the request upon completion from lower layers.
+ ErrorCode: Numerical value of the error code.
+ CdbLength: Number of bytes of Cdb.
+ Cdb: Pointer to the CDB.
+ Pkt: The tranfer packet representing the IO of interest. This may be NULL.
+
+--*/
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = (PCOMMON_DEVICE_EXTENSION)(DeviceObject->DeviceExtension);
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+ POPCODE_SENSE_DATA_IO_LOG_MESSAGE_CONTEXT_HEADER ioLogMessageContextHeader = NULL;
+ PVOID senseData = NULL;
+ PIO_WORKITEM workItem = NULL;
+ ULONG senseBufferSize = 0;
+ LARGE_INTEGER lba = {0};
+
+ if (!commonExtension->IsFdo) {
+ return;
+ }
+
+ if (!Cdb) {
+ return;
+ }
+
+ workItem = IoAllocateWorkItem(DeviceObject);
+ if (!workItem) {
+ goto __ClasspQueueLogIOEventWithContextWorker_ExitWithMessage;
+ }
+
+ if (SenseBufferSize) {
+ senseData = ExAllocatePoolWithTag(NonPagedPoolNx, SenseBufferSize, CLASSPNP_POOL_TAG_LOG_MESSAGE);
+ if (senseData) {
+ senseBufferSize = SenseBufferSize;
+ }
+ }
+
+ if (CdbLength == 16) {
+ REVERSE_BYTES_QUAD(&lba, Cdb->CDB16.LogicalBlock);
+ } else {
+ ((PFOUR_BYTE)&lba.LowPart)->Byte3 = Cdb->CDB10.LogicalBlockByte0;
+ ((PFOUR_BYTE)&lba.LowPart)->Byte2 = Cdb->CDB10.LogicalBlockByte1;
+ ((PFOUR_BYTE)&lba.LowPart)->Byte1 = Cdb->CDB10.LogicalBlockByte2;
+ ((PFOUR_BYTE)&lba.LowPart)->Byte0 = Cdb->CDB10.LogicalBlockByte3;
+ }
+
+ //
+ // Calculate the amount of buffer required for the insertion strings.
+ //
+ switch (ErrorCode) {
+ case IO_ERROR_IO_HARDWARE_ERROR:
+ case IO_WARNING_IO_OPERATION_RETRIED: {
+
+ PIO_RETRIED_LOG_MESSAGE_CONTEXT ioLogMessageContext = NULL;
+
+ ioLogMessageContext = ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(IO_RETRIED_LOG_MESSAGE_CONTEXT), CLASSPNP_POOL_TAG_LOG_MESSAGE);
+ if (!ioLogMessageContext) {
+ goto __ClasspQueueLogIOEventWithContextWorker_ExitWithMessage;
+ }
+
+ ioLogMessageContext->Lba.QuadPart = lba.QuadPart;
+ ioLogMessageContext->DeviceNumber = fdoExtension->DeviceNumber;
+
+ ioLogMessageContextHeader = (POPCODE_SENSE_DATA_IO_LOG_MESSAGE_CONTEXT_HEADER)ioLogMessageContext;
+
+ break;
+ }
+
+ default: goto __ClasspQueueLogIOEventWithContextWorker_Exit;
+ }
+
+ TracePrint((TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_GENERAL,
+ "ClasspQueueLogIOEventWithContextWorker: DO (%p), Pkt (%p), Queueing IO retried event log message work item.\n",
+ DeviceObject,
+ Pkt));
+
+ ioLogMessageContextHeader->WorkItem = workItem;
+ if (senseData) {
+ RtlCopyMemory(senseData, SenseData, SenseBufferSize);
+ }
+ ioLogMessageContextHeader->SenseData = senseData;
+ ioLogMessageContextHeader->SenseDataSize = senseBufferSize;
+ ioLogMessageContextHeader->SrbStatus = SrbStatus;
+ ioLogMessageContextHeader->ScsiStatus = ScsiStatus;
+ ioLogMessageContextHeader->OpCode = Cdb->CDB6GENERIC.OperationCode;
+ ioLogMessageContextHeader->Reserved = 0;
+ ioLogMessageContextHeader->ErrorCode = ErrorCode;
+
+ ClassAcquireRemoveLock(DeviceObject, (PIRP)(workItem));
+
+ //
+ // Queue a work item to write the system event log.
+ //
+ IoQueueWorkItem(workItem, ClasspLogIOEventWithContext, DelayedWorkQueue, ioLogMessageContextHeader);
+
+ return;
+
+__ClasspQueueLogIOEventWithContextWorker_ExitWithMessage:
+
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_GENERAL,
+ "ClasspQueueLogIOEventWithContextWorker: DO (%p), Failed to allocate memory for the log message.\n",
+ DeviceObject));
+
+__ClasspQueueLogIOEventWithContextWorker_Exit:
+ if (senseData) {
+ ExFreePool(senseData);
+ }
+ if (workItem) {
+ IoFreeWorkItem(workItem);
+ }
+ if (ioLogMessageContextHeader) {
+ ExFreePool(ioLogMessageContextHeader);
+ }
+}
+
+__inline
+BOOLEAN
+ValidPersistentReserveScope(
+ UCHAR Scope)
+{
+ switch (Scope) {
+ case RESERVATION_SCOPE_LU:
+ case RESERVATION_SCOPE_ELEMENT:
+
+ return TRUE;
+
+ default:
+
+ break;
+ }
+
+ return FALSE;
+}
+
+__inline
+ValidPersistentReserveType(
+ UCHAR Type)
+{
+ switch (Type) {
+ case RESERVATION_TYPE_WRITE_EXCLUSIVE:
+ case RESERVATION_TYPE_EXCLUSIVE:
+ case RESERVATION_TYPE_WRITE_EXCLUSIVE_REGISTRANTS:
+ case RESERVATION_TYPE_EXCLUSIVE_REGISTRANTS:
+
+ return TRUE;
+
+ default:
+
+ break;
+ }
+
+ return FALSE;
+}
+
+
+/*++
+
+ClasspPersistentReserve
+
+Routine Description:
+
+ Handles IOCTL_STORAGE_PERSISTENT_RESERVE_IN and IOCTL_STORAGE_PERSISTENT_RESERVE_OUT.
+
+Arguments:
+
+ DeviceObject - a pointer to the device object
+ Irp - a pointer to the I/O request packet
+ Srb - pointer to preallocated SCSI_REQUEST_BLOCK.
+
+Return Value:
+
+ Status Code
+
+--*/
+NTSTATUS
+ClasspPersistentReserve(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _In_ PIRP Irp,
+ _Inout_ PSCSI_REQUEST_BLOCK Srb
+ )
+{
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
+ PCDB cdb = NULL;
+ PPERSISTENT_RESERVE_COMMAND prCommand = Irp->AssociatedIrp.SystemBuffer;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+
+ NTSTATUS status;
+
+ ULONG dataBufLen;
+ ULONG controlCode = irpStack->Parameters.DeviceIoControl.IoControlCode;
+
+ BOOLEAN writeToDevice;
+
+ //
+ // Check common input buffer parameters.
+ //
+
+ if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
+ sizeof(PERSISTENT_RESERVE_COMMAND) ||
+ prCommand->Size < sizeof(PERSISTENT_RESERVE_COMMAND)) {
+
+ status = STATUS_INFO_LENGTH_MISMATCH;
+ Irp->IoStatus.Status = status;
+ Irp->IoStatus.Information = 0;
+
+ FREE_POOL(Srb);
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ goto ClasspPersistentReserve_Exit;
+ }
+
+ //
+ // Check buffer alignment. Only an issue if another kernel mode component
+ // (not the I/O manager) allocates the buffer.
+ //
+
+ if ((ULONG_PTR)prCommand & fdoExtension->AdapterDescriptor->AlignmentMask) {
+
+ status = STATUS_INVALID_USER_BUFFER;
+ Irp->IoStatus.Status = status;
+ Irp->IoStatus.Information = 0;
+
+ FREE_POOL(Srb);
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ goto ClasspPersistentReserve_Exit;
+ }
+
+ //
+ // Check additional parameters.
+ //
+
+ status = STATUS_SUCCESS;
+
+ SrbSetCdbLength(Srb, 10);
+ cdb = SrbGetCdb(Srb);
+
+ if (controlCode == IOCTL_STORAGE_PERSISTENT_RESERVE_IN) {
+
+ //
+ // Check output buffer for PR In.
+ //
+
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
+ prCommand->PR_IN.AllocationLength) {
+
+ status = STATUS_INVALID_PARAMETER;
+ }
+
+ switch (prCommand->PR_IN.ServiceAction) {
+
+ case RESERVATION_ACTION_READ_KEYS:
+
+ if (prCommand->PR_IN.AllocationLength < sizeof(PRI_REGISTRATION_LIST)) {
+
+ status = STATUS_INVALID_PARAMETER;
+ }
+
+ break;
+
+ case RESERVATION_ACTION_READ_RESERVATIONS:
+
+ if (prCommand->PR_IN.AllocationLength < sizeof(PRI_RESERVATION_LIST)) {
+
+ status = STATUS_INVALID_PARAMETER;
+ }
+
+ break;
+
+ default:
+
+ status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ if (!NT_SUCCESS(status)) {
+
+ Irp->IoStatus.Status = status;
+ Irp->IoStatus.Information = 0;
+
+ FREE_POOL(Srb);
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ goto ClasspPersistentReserve_Exit;
+ }
+
+ //
+ // Fill in the CDB.
+ //
+
+ cdb->PERSISTENT_RESERVE_IN.OperationCode = SCSIOP_PERSISTENT_RESERVE_IN;
+ cdb->PERSISTENT_RESERVE_IN.ServiceAction = prCommand->PR_IN.ServiceAction;
+
+ REVERSE_BYTES_SHORT(&(cdb->PERSISTENT_RESERVE_IN.AllocationLength),
+ &(prCommand->PR_IN.AllocationLength));
+
+ dataBufLen = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
+ writeToDevice = FALSE;
+
+
+ } else {
+
+ //
+ // Verify ServiceAction, Scope, and Type
+ //
+
+ switch (prCommand->PR_OUT.ServiceAction) {
+
+ case RESERVATION_ACTION_REGISTER:
+ case RESERVATION_ACTION_REGISTER_IGNORE_EXISTING:
+ case RESERVATION_ACTION_CLEAR:
+
+ // Scope and type ignored.
+
+ break;
+
+ case RESERVATION_ACTION_RESERVE:
+ case RESERVATION_ACTION_RELEASE:
+ case RESERVATION_ACTION_PREEMPT:
+ case RESERVATION_ACTION_PREEMPT_ABORT:
+
+ if (!ValidPersistentReserveScope(prCommand->PR_OUT.Scope) ||
+ !ValidPersistentReserveType(prCommand->PR_OUT.Type)) {
+
+ status = STATUS_INVALID_PARAMETER;
+
+ }
+
+ break;
+
+ default:
+
+ status = STATUS_INVALID_PARAMETER;
+
+ break;
+ }
+
+ //
+ // Check input buffer for PR Out.
+ // Caller must include the PR parameter list.
+ //
+
+ if (NT_SUCCESS(status)) {
+
+ if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
+ (sizeof(PERSISTENT_RESERVE_COMMAND) +
+ sizeof(PRO_PARAMETER_LIST)) ||
+ prCommand->Size <
+ irpStack->Parameters.DeviceIoControl.InputBufferLength) {
+
+ status = STATUS_INVALID_PARAMETER;
+
+ }
+ }
+
+
+ if (!NT_SUCCESS(status)) {
+
+ Irp->IoStatus.Status = status;
+ Irp->IoStatus.Information = 0;
+
+ FREE_POOL(Srb);
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ goto ClasspPersistentReserve_Exit;
+ }
+
+ //
+ // Fill in the CDB.
+ //
+
+ cdb->PERSISTENT_RESERVE_OUT.OperationCode = SCSIOP_PERSISTENT_RESERVE_OUT;
+ cdb->PERSISTENT_RESERVE_OUT.ServiceAction = prCommand->PR_OUT.ServiceAction;
+ cdb->PERSISTENT_RESERVE_OUT.Scope = prCommand->PR_OUT.Scope;
+ cdb->PERSISTENT_RESERVE_OUT.Type = prCommand->PR_OUT.Type;
+
+ cdb->PERSISTENT_RESERVE_OUT.ParameterListLength[1] = (UCHAR)sizeof(PRO_PARAMETER_LIST);
+
+ //
+ // Move the parameter list to the beginning of the data buffer (so it is aligned
+ // correctly and that the MDL describes it correctly).
+ //
+
+ RtlMoveMemory(prCommand,
+ prCommand->PR_OUT.ParameterList,
+ sizeof(PRO_PARAMETER_LIST));
+
+ dataBufLen = sizeof(PRO_PARAMETER_LIST);
+ writeToDevice = TRUE;
+ }
+
+ //
+ // Fill in the SRB
+ //
+
+ //
+ // Set timeout value.
+ //
+
+ SrbSetTimeOutValue(Srb, fdoExtension->TimeOutValue);
+
+ //
+ // Send as a tagged request.
+ //
+
+ SrbSetRequestAttribute(Srb, SRB_HEAD_OF_QUEUE_TAG_REQUEST);
+ SrbSetSrbFlags(Srb, SRB_FLAGS_NO_QUEUE_FREEZE | SRB_FLAGS_QUEUE_ACTION_ENABLE);
+
+ status = ClassSendSrbAsynchronous(DeviceObject,
+ Srb,
+ Irp,
+ prCommand,
+ dataBufLen,
+ writeToDevice);
+
+ClasspPersistentReserve_Exit:
+
+ return status;
+
+}
+
+/*++
+
+ClasspPriorityHint
+
+Routine Description:
+
+ Handles IOCTL_STORAGE_CHECK_PRIORITY_HINT_SUPPORT.
+
+Arguments:
+
+ DeviceObject - a pointer to the device object
+ Irp - a pointer to the I/O request packet
+
+Return Value:
+
+ Status Code
+
+--*/
+NTSTATUS
+ClasspPriorityHint(
+ PDEVICE_OBJECT DeviceObject,
+ PIRP Irp
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
+ PSTORAGE_PRIORITY_HINT_SUPPORT priSupport = Irp->AssociatedIrp.SystemBuffer;
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
+ NTSTATUS status = STATUS_SUCCESS;
+
+ Irp->IoStatus.Information = 0;
+
+ //
+ // Check whether this device supports idle priority.
+ //
+ if (!fdoData->IdlePrioritySupported) {
+ status = STATUS_NOT_SUPPORTED;
+ goto PriorityHintExit;
+ }
+
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
+ sizeof(STORAGE_PRIORITY_HINT_SUPPORT)) {
+
+ status = STATUS_BUFFER_TOO_SMALL;
+ goto PriorityHintExit;
+ }
+
+ RtlZeroMemory(priSupport, sizeof(STORAGE_PRIORITY_HINT_SUPPORT));
+
+ status = ClassForwardIrpSynchronous(commonExtension, Irp);
+ if (!NT_SUCCESS(status)) {
+ //
+ // If I/O priority is not supported by lower drivers, just set the
+ // priorities supported by class driver.
+ //
+ TracePrint((TRACE_LEVEL_FATAL, TRACE_FLAG_IOCTL, "ClasspPriorityHint: I/O priority not supported by port driver.\n"));
+ priSupport->SupportFlags = 0;
+ status = STATUS_SUCCESS;
+ }
+
+ TracePrint((TRACE_LEVEL_FATAL, TRACE_FLAG_IOCTL, "ClasspPriorityHint: I/O priorities supported by port driver: %X\n", priSupport->SupportFlags));
+
+ priSupport->SupportFlags |= (1 << IoPriorityVeryLow) |
+ (1 << IoPriorityLow) |
+ (1 << IoPriorityNormal) ;
+
+ TracePrint((TRACE_LEVEL_FATAL, TRACE_FLAG_IOCTL, "ClasspPriorityHint: I/O priorities supported: %X\n", priSupport->SupportFlags));
+ Irp->IoStatus.Information = sizeof(STORAGE_PRIORITY_HINT_SUPPORT);
+
+PriorityHintExit:
+
+ Irp->IoStatus.Status = status;
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ return status;
+}
+
+/*++
+
+ClasspConvertToScsiRequestBlock
+
+Routine Description:
+
+ Convert an extended SRB to a SCSI_REQUEST_BLOCK. This function handles only
+ a single SRB and will not converted SRBs that are linked.
+
+Arguments:
+
+ Srb - a pointer to a SCSI_REQUEST_BLOCK
+ SrbEx - a pointer to an extended SRB
+
+Return Value:
+
+ None
+
+--*/
+VOID
+ClasspConvertToScsiRequestBlock(
+ _Out_ PSCSI_REQUEST_BLOCK Srb,
+ _In_ PSTORAGE_REQUEST_BLOCK SrbEx
+ )
+{
+ PSTOR_ADDR_BTL8 storAddrBtl8;
+ ULONG i;
+ BOOLEAN foundEntry = FALSE;
+ PSRBEX_DATA srbExData;
+
+ if ((Srb == NULL) || (SrbEx == NULL)) {
+ return;
+ }
+
+ RtlZeroMemory(Srb, sizeof(SCSI_REQUEST_BLOCK));
+
+ Srb->Length = sizeof(SCSI_REQUEST_BLOCK);
+ Srb->Function = (UCHAR)SrbEx->SrbFunction;
+ Srb->SrbStatus = SrbEx->SrbStatus;
+ Srb->QueueTag = (UCHAR)SrbEx->RequestTag;
+ Srb->QueueAction = (UCHAR)SrbEx->RequestAttribute;
+ Srb->SrbFlags = SrbEx->SrbFlags;
+ Srb->DataTransferLength = SrbEx->DataTransferLength;
+ Srb->TimeOutValue = SrbEx->TimeOutValue;
+ Srb->DataBuffer = SrbEx->DataBuffer;
+ Srb->OriginalRequest = SrbEx->OriginalRequest;
+ Srb->SrbExtension = SrbEx->MiniportContext;
+ Srb->InternalStatus = SrbEx->SystemStatus;
+
+ //
+ // Handle address fields
+ //
+ if (SrbEx->AddressOffset >= sizeof(STORAGE_REQUEST_BLOCK)) {
+ storAddrBtl8 = (PSTOR_ADDR_BTL8)((PCHAR)SrbEx + SrbEx->AddressOffset);
+
+ if (storAddrBtl8->Type == STOR_ADDRESS_TYPE_BTL8) {
+ Srb->PathId = storAddrBtl8->Path;
+ Srb->TargetId = storAddrBtl8->Target;
+ Srb->Lun = storAddrBtl8->Lun;
+ } else {
+ // Catch unsupported address types
+ NT_ASSERT(FALSE);
+ }
+ }
+
+ //
+ // Handle SRB function specific fields
+ //
+ if (SrbEx->NumSrbExData > 0) {
+
+ for (i = 0; i < SrbEx->NumSrbExData; i++) {
+
+ if ((SrbEx->SrbExDataOffset[i] == 0) ||
+ (SrbEx->SrbExDataOffset[i] < sizeof(STORAGE_REQUEST_BLOCK))) {
+ // Catch invalid offsets
+ NT_ASSERT(FALSE);
+ continue;
+ }
+
+ srbExData = (PSRBEX_DATA)((PCHAR)SrbEx + SrbEx->SrbExDataOffset[i]);
+
+ switch (SrbEx->SrbFunction) {
+
+ case SRB_FUNCTION_EXECUTE_SCSI:
+
+ switch (srbExData->Type) {
+
+ case SrbExDataTypeScsiCdb16:
+ Srb->ScsiStatus = ((PSRBEX_DATA_SCSI_CDB16)srbExData)->ScsiStatus;
+ Srb->CdbLength = ((PSRBEX_DATA_SCSI_CDB16)srbExData)->CdbLength;
+ Srb->SenseInfoBufferLength = ((PSRBEX_DATA_SCSI_CDB16)srbExData)->SenseInfoBufferLength;
+ Srb->SenseInfoBuffer = ((PSRBEX_DATA_SCSI_CDB16)srbExData)->SenseInfoBuffer;
+ RtlCopyMemory(Srb->Cdb, ((PSRBEX_DATA_SCSI_CDB16)srbExData)->Cdb, sizeof(Srb->Cdb));
+ foundEntry = TRUE;
+ break;
+
+ case SrbExDataTypeScsiCdb32:
+ Srb->ScsiStatus = ((PSRBEX_DATA_SCSI_CDB32)srbExData)->ScsiStatus;
+ Srb->CdbLength = ((PSRBEX_DATA_SCSI_CDB32)srbExData)->CdbLength;
+ Srb->SenseInfoBufferLength = ((PSRBEX_DATA_SCSI_CDB32)srbExData)->SenseInfoBufferLength;
+ Srb->SenseInfoBuffer = ((PSRBEX_DATA_SCSI_CDB32)srbExData)->SenseInfoBuffer;
+
+ // Copy only the first 16 bytes
+ RtlCopyMemory(Srb->Cdb, ((PSRBEX_DATA_SCSI_CDB32)srbExData)->Cdb, sizeof(Srb->Cdb));
+ foundEntry = TRUE;
+ break;
+
+ case SrbExDataTypeScsiCdbVar:
+ Srb->ScsiStatus = ((PSRBEX_DATA_SCSI_CDB_VAR)srbExData)->ScsiStatus;
+ Srb->CdbLength = (UCHAR)((PSRBEX_DATA_SCSI_CDB_VAR)srbExData)->CdbLength;
+ Srb->SenseInfoBufferLength = ((PSRBEX_DATA_SCSI_CDB_VAR)srbExData)->SenseInfoBufferLength;
+ Srb->SenseInfoBuffer = ((PSRBEX_DATA_SCSI_CDB_VAR)srbExData)->SenseInfoBuffer;
+
+ // Copy only the first 16 bytes
+ RtlCopyMemory(Srb->Cdb, ((PSRBEX_DATA_SCSI_CDB_VAR)srbExData)->Cdb, sizeof(Srb->Cdb));
+ foundEntry = TRUE;
+ break;
+
+ default:
+ break;
+
+ }
+ break;
+
+ case SRB_FUNCTION_WMI:
+
+ if (srbExData->Type == SrbExDataTypeWmi) {
+ ((PSCSI_WMI_REQUEST_BLOCK)Srb)->WMISubFunction = ((PSRBEX_DATA_WMI)srbExData)->WMISubFunction;
+ ((PSCSI_WMI_REQUEST_BLOCK)Srb)->WMIFlags = ((PSRBEX_DATA_WMI)srbExData)->WMIFlags;
+ ((PSCSI_WMI_REQUEST_BLOCK)Srb)->DataPath = ((PSRBEX_DATA_WMI)srbExData)->DataPath;
+ foundEntry = TRUE;
+ }
+ break;
+
+ case SRB_FUNCTION_PNP:
+
+ if (srbExData->Type == SrbExDataTypePnP) {
+ ((PSCSI_PNP_REQUEST_BLOCK)Srb)->PnPAction = ((PSRBEX_DATA_PNP)srbExData)->PnPAction;
+ ((PSCSI_PNP_REQUEST_BLOCK)Srb)->PnPSubFunction = ((PSRBEX_DATA_PNP)srbExData)->PnPSubFunction;
+ ((PSCSI_PNP_REQUEST_BLOCK)Srb)->SrbPnPFlags = ((PSRBEX_DATA_PNP)srbExData)->SrbPnPFlags;
+ foundEntry = TRUE;
+ }
+ break;
+
+ case SRB_FUNCTION_POWER:
+
+ if (srbExData->Type == SrbExDataTypePower) {
+ ((PSCSI_POWER_REQUEST_BLOCK)Srb)->DevicePowerState = ((PSRBEX_DATA_POWER)srbExData)->DevicePowerState;
+ ((PSCSI_POWER_REQUEST_BLOCK)Srb)->PowerAction = ((PSRBEX_DATA_POWER)srbExData)->PowerAction;
+ ((PSCSI_POWER_REQUEST_BLOCK)Srb)->SrbPowerFlags = ((PSRBEX_DATA_POWER)srbExData)->SrbPowerFlags;
+ foundEntry = TRUE;
+ }
+ break;
+
+ default:
+ break;
+
+ }
+
+ //
+ // Quit on first match
+ //
+ if (foundEntry) {
+ break;
+ }
+ }
+ }
+
+ return;
+}
+
+
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+ClasspGetMaximumTokenListIdentifier(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _In_z_ PWSTR RegistryPath,
+ _Out_ PULONG MaximumListIdentifier
+ )
+
+/*++
+
+Routine Description:
+
+ This routine returns the maximum ListIdentifier (to be used when building TokenOperation
+ requests) by querying the value MaximumListIdentifier under the key 'RegistryPath'.
+
+Arguments:
+
+ DeviceObject - The device handling the request.
+ RegistryPath - The absolute registry path under which MaximumListIdentifier resides.
+ MaximumListIdentifier - Returns the value being queried.
+
+Return Value:
+
+ STATUS_SUCCESS or appropriate error status returned by Registry API.
+
+--*/
+
+{
+ RTL_QUERY_REGISTRY_TABLE queryTable[2];
+ ULONG value = 0;
+ NTSTATUS status;
+
+ TracePrint((TRACE_LEVEL_VERBOSE,
+ TRACE_FLAG_PNP,
+ "ClasspGetMaximumTokenListIdentifier (%p): Entering function.\n",
+ DeviceObject));
+
+ //
+ // Zero the table entries.
+ //
+ RtlZeroMemory(queryTable, sizeof(queryTable));
+
+ //
+ // The query table has two entries. One for the MaximumListIdentifier and
+ // the second which is the 'NULL' terminator.
+ //
+ // Indicate that there is NO call-back routine.
+ //
+ queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
+
+ //
+ // The value to query.
+ //
+ queryTable[0].Name = REG_MAX_LIST_IDENTIFIER_VALUE;
+
+ //
+ // Where to put the value, the type of the value, default value and length.
+ //
+ queryTable[0].EntryContext = &value;
+ queryTable[0].DefaultType = REG_DWORD;
+ queryTable[0].DefaultData = &value;
+ queryTable[0].DefaultLength = sizeof(value);
+
+ //
+ // Try to get the maximum listIdentifier.
+ //
+ status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
+ RegistryPath,
+ queryTable,
+ NULL,
+ NULL);
+
+ if (NT_SUCCESS(status)) {
+ *MaximumListIdentifier = value;
+ } else {
+ *MaximumListIdentifier = 0;
+ }
+
+ TracePrint((TRACE_LEVEL_VERBOSE,
+ TRACE_FLAG_PNP,
+ "ClasspGetMaximumTokenListIdentifier (%p): Exiting function with status %x (maxListId %u).\n",
+ DeviceObject,
+ status,
+ *MaximumListIdentifier));
+
+ return status;
+}
+
+
+_IRQL_requires_max_(APC_LEVEL)
+_IRQL_requires_min_(PASSIVE_LEVEL)
+_IRQL_requires_same_
+NTSTATUS
+ClasspDeviceCopyOffloadProperty(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _Inout_ PIRP Irp,
+ _Inout_ PSCSI_REQUEST_BLOCK Srb
+ )
+
+/*++
+
+Routine Description:
+
+ This routine returns the copy offload parameters associated with the device.
+
+ This function must be called at IRQL < DISPATCH_LEVEL.
+
+Arguments:
+
+ DeviceObject - Supplies the device object associated with this request
+ Irp - The IRP to be processed
+ Srb - The SRB associated with the request
+
+Return Value:
+
+ NTSTATUS code
+
+--*/
+
+{
+ NTSTATUS status;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
+ PSTORAGE_PROPERTY_QUERY query;
+ PIO_STACK_LOCATION irpStack;
+ ULONG length;
+ ULONG information;
+ PDEVICE_COPY_OFFLOAD_DESCRIPTOR copyOffloadDescr = (PDEVICE_COPY_OFFLOAD_DESCRIPTOR)Irp->AssociatedIrp.SystemBuffer;
+
+ UNREFERENCED_PARAMETER(Srb);
+
+ PAGED_CODE();
+
+ fdoExtension = DeviceObject->DeviceExtension;
+ query = (PSTORAGE_PROPERTY_QUERY)Irp->AssociatedIrp.SystemBuffer;
+ irpStack = IoGetCurrentIrpStackLocation(Irp);
+ length = 0;
+ information = 0;
+
+ TracePrint((TRACE_LEVEL_VERBOSE,
+ TRACE_FLAG_IOCTL,
+ "ClasspDeviceCopyOffloadProperty (%p): Entering function.\n",
+ DeviceObject));
+
+ //
+ // Check proper query type.
+ //
+ if (query->QueryType == PropertyExistsQuery) {
+
+ //
+ // In order to maintain consistency with the how the rest of the properties
+ // are handled, we shall always return success for PropertyExistsQuery.
+ //
+ status = STATUS_SUCCESS;
+ goto __ClasspDeviceCopyOffloadProperty_Exit;
+
+ } else if (query->QueryType != PropertyStandardQuery) {
+
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClasspDeviceCopyOffloadProperty (%p): Unsupported query type %x for Copy Offload property.\n",
+ DeviceObject,
+ query->QueryType));
+
+ status = STATUS_NOT_SUPPORTED;
+ goto __ClasspDeviceCopyOffloadProperty_Exit;
+ }
+
+ //
+ // Request validation.
+ // Note that InputBufferLength and IsFdo have been validated beforing entering this routine.
+ //
+
+ if (KeGetCurrentIrql() >= DISPATCH_LEVEL) {
+
+ NT_ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
+
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClasspDeviceCopyOffloadProperty (%p): Query property for Copy Offload called at incorrect IRQL.\n",
+ DeviceObject));
+
+ status = STATUS_INVALID_LEVEL;
+ goto __ClasspDeviceCopyOffloadProperty_Exit;
+ }
+
+ length = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
+
+ if (length < sizeof(DEVICE_COPY_OFFLOAD_DESCRIPTOR)) {
+
+ if (length >= sizeof(STORAGE_DESCRIPTOR_HEADER)) {
+
+ TracePrint((TRACE_LEVEL_WARNING,
+ TRACE_FLAG_IOCTL,
+ "ClasspDeviceCopyOffloadProperty (%p): Length %u specified for Copy Offload property enough only for header.\n",
+ DeviceObject,
+ length));
+
+ information = sizeof(STORAGE_DESCRIPTOR_HEADER);
+ copyOffloadDescr->Version = sizeof(DEVICE_COPY_OFFLOAD_DESCRIPTOR);
+ copyOffloadDescr->Size = sizeof(DEVICE_COPY_OFFLOAD_DESCRIPTOR);
+
+ status = STATUS_SUCCESS;
+ goto __ClasspDeviceCopyOffloadProperty_Exit;
+ }
+
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClasspDeviceCopyOffloadProperty (%p): Incorrect length %u specified for Copy Offload property.\n",
+ DeviceObject,
+ length));
+
+ status = STATUS_BUFFER_TOO_SMALL;
+ goto __ClasspDeviceCopyOffloadProperty_Exit;
+ }
+
+ if (!fdoExtension->FunctionSupportInfo->ValidInquiryPages.BlockDeviceRODLimits) {
+
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClasspDeviceCopyOffloadProperty (%p): Command not supported on this device.\n",
+ DeviceObject));
+
+ status = STATUS_DEVICE_FEATURE_NOT_SUPPORTED;
+ goto __ClasspDeviceCopyOffloadProperty_Exit;
+ }
+
+ if (!NT_SUCCESS(fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.CommandStatus)) {
+
+ status = fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.CommandStatus;
+
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClasspDeviceCopyOffloadProperty (%p): VPD retrieval had failed with %x.\n",
+ DeviceObject,
+ status));
+
+ goto __ClasspDeviceCopyOffloadProperty_Exit;
+ }
+
+ //
+ // Fill in the output buffer. All data is copied from the FDO extension where we
+ // cached Block Limits and Block Device Token Limits info when the device was first initialized.
+ //
+ RtlZeroMemory(copyOffloadDescr, length);
+ copyOffloadDescr->Version = 1;
+ copyOffloadDescr->Size = sizeof(DEVICE_COPY_OFFLOAD_DESCRIPTOR);
+ copyOffloadDescr->MaximumTokenLifetime = fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.MaximumInactivityTimer;
+ copyOffloadDescr->DefaultTokenLifetime = fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.DefaultInactivityTimer;
+ copyOffloadDescr->MaximumTransferSize = fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.MaximumTokenTransferSize;
+ copyOffloadDescr->OptimalTransferCount = fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.OptimalTransferCount;
+ copyOffloadDescr->MaximumDataDescriptors = fdoExtension->FunctionSupportInfo->BlockDeviceRODLimitsData.MaximumRangeDescriptors;
+
+ if (NT_SUCCESS(fdoExtension->FunctionSupportInfo->BlockLimitsData.CommandStatus)) {
+
+ copyOffloadDescr->MaximumTransferLengthPerDescriptor = fdoExtension->FunctionSupportInfo->BlockLimitsData.MaximumTransferLength;
+ copyOffloadDescr->OptimalTransferLengthPerDescriptor = fdoExtension->FunctionSupportInfo->BlockLimitsData.OptimalTransferLength;
+ copyOffloadDescr->OptimalTransferLengthGranularity = fdoExtension->FunctionSupportInfo->BlockLimitsData.OptimalTransferLengthGranularity;
+ }
+
+ information = sizeof(DEVICE_COPY_OFFLOAD_DESCRIPTOR);
+ status = STATUS_SUCCESS;
+
+__ClasspDeviceCopyOffloadProperty_Exit:
+
+ //
+ // Set the size and status in IRP
+ //
+ Irp->IoStatus.Information = information;
+ Irp->IoStatus.Status = status;
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+
+ TracePrint((TRACE_LEVEL_VERBOSE,
+ TRACE_FLAG_IOCTL,
+ "ClasspDeviceCopyOffloadProperty (%p): Exiting function with status %x.\n",
+ DeviceObject,
+ status));
+
+ return status;
+}
+
+
+_IRQL_requires_max_(APC_LEVEL)
+_IRQL_requires_min_(PASSIVE_LEVEL)
+_IRQL_requires_same_
+NTSTATUS
+ClasspValidateOffloadSupported(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _In_ PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine validates if this device supports offload requests.
+
+ This function must be called at IRQL < DISPATCH_LEVEL.
+
+Arguments:
+
+ DeviceObject - Supplies the device object associated with this request
+ Irp - The IRP to be processed
+
+Return Value:
+
+ NTSTATUS code
+
+--*/
+
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt;
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ TracePrint((TRACE_LEVEL_VERBOSE,
+ TRACE_FLAG_IOCTL,
+ "ClasspValidateOffloadSupported (%p): Entering function. Irp %p\n",
+ DeviceObject,
+ Irp));
+
+ fdoExt = DeviceObject->DeviceExtension;
+ status = STATUS_SUCCESS;
+
+ //
+ // For now this command is only supported by disk devices
+ //
+ if ((DeviceObject->DeviceType == FILE_DEVICE_DISK) &&
+ (!TEST_FLAG(DeviceObject->Characteristics, FILE_FLOPPY_DISKETTE))) {
+
+ if (!fdoExt->FunctionSupportInfo->ValidInquiryPages.BlockDeviceRODLimits) {
+
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClasspValidateOffloadSupported (%p): Command not supported on this disk device.\n",
+ DeviceObject));
+
+ status = STATUS_DEVICE_FEATURE_NOT_SUPPORTED;
+ goto __ClasspValidateOffloadSupported_Exit;
+ }
+
+ if (!NT_SUCCESS(fdoExt->FunctionSupportInfo->BlockDeviceRODLimitsData.CommandStatus)) {
+
+ status = fdoExt->FunctionSupportInfo->BlockDeviceRODLimitsData.CommandStatus;
+
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClasspValidateOffloadSupported (%p): VPD retrieval failed with %x.\n",
+ DeviceObject,
+ status));
+
+ goto __ClasspValidateOffloadSupported_Exit;
+ }
+ } else {
+
+ TracePrint((TRACE_LEVEL_WARNING,
+ TRACE_FLAG_IOCTL,
+ "ClasspValidateOffloadSupported (%p): Suported only on Disk devices.\n",
+ DeviceObject));
+
+ status = STATUS_DEVICE_FEATURE_NOT_SUPPORTED;
+ goto __ClasspValidateOffloadSupported_Exit;
+ }
+
+__ClasspValidateOffloadSupported_Exit:
+ TracePrint((TRACE_LEVEL_VERBOSE,
+ TRACE_FLAG_IOCTL,
+ "ClasspValidateOffloadSupported (%p): Exiting function Irp %p with status %x.\n",
+ DeviceObject,
+ Irp,
+ status));
+
+ return status;
+}
+
+
+_IRQL_requires_max_(APC_LEVEL)
+_IRQL_requires_min_(PASSIVE_LEVEL)
+_IRQL_requires_same_
+NTSTATUS
+ClasspValidateOffloadInputParameters(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _In_ PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine does some basic validation of the input parameters of the offload request.
+
+ This function must be called at IRQL < DISPATCH_LEVEL.
+
+Arguments:
+
+ DeviceObject - Supplies the device object associated with this request
+ Irp - The IRP to be processed
+
+Return Value:
+
+ NTSTATUS code
+
+--*/
+
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
+ PIO_STACK_LOCATION irpStack;
+ PDEVICE_MANAGE_DATA_SET_ATTRIBUTES dsmAttributes;
+ PDEVICE_DATA_SET_RANGE dataSetRanges;
+ ULONG dataSetRangesCount;
+ ULONG i;
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ TracePrint((TRACE_LEVEL_VERBOSE,
+ TRACE_FLAG_IOCTL,
+ "ClasspValidateOffloadInputParameters (%p): Entering function Irp %p.\n",
+ DeviceObject,
+ Irp));
+
+ fdoExtension = DeviceObject->DeviceExtension;
+ irpStack = IoGetCurrentIrpStackLocation (Irp);
+ dsmAttributes = Irp->AssociatedIrp.SystemBuffer;
+ status = STATUS_SUCCESS;
+
+ if (!dsmAttributes) {
+
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClasspValidateOffloadInputParameters (%p): NULL DsmAttributes passed in.\n",
+ DeviceObject));
+
+ status = STATUS_INVALID_PARAMETER;
+ goto __ClasspValidateOffloadInputParameters_Exit;
+ }
+
+ if ((irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES)) ||
+ (irpStack->Parameters.DeviceIoControl.InputBufferLength <
+ (sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES) + dsmAttributes->ParameterBlockLength + dsmAttributes->DataSetRangesLength))) {
+
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClasspValidateOffloadInputParameters (%p): Input buffer size (%u) too small.\n",
+ DeviceObject,
+ irpStack->Parameters.DeviceIoControl.InputBufferLength));
+
+ status = STATUS_INVALID_PARAMETER;
+ goto __ClasspValidateOffloadInputParameters_Exit;
+ }
+
+ if ((dsmAttributes->DataSetRangesOffset == 0) ||
+ (dsmAttributes->DataSetRangesLength == 0)) {
+
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClasspValidateOffloadInputParameters (%p): Incorrect DataSetRanges [offset %u, length %u].\n",
+ DeviceObject,
+ dsmAttributes->DataSetRangesOffset,
+ dsmAttributes->DataSetRangesLength));
+
+ status = STATUS_INVALID_PARAMETER;
+ goto __ClasspValidateOffloadInputParameters_Exit;
+ }
+
+ dataSetRanges = Add2Ptr(dsmAttributes, dsmAttributes->DataSetRangesOffset);
+ dataSetRangesCount = dsmAttributes->DataSetRangesLength / sizeof(DEVICE_DATA_SET_RANGE);
+
+ if (dataSetRangesCount == 0) {
+
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClasspValidateOffloadInputParameters (%p): DataSetRanges specifies no extents.\n",
+ DeviceObject));
+
+ status = STATUS_INVALID_PARAMETER;
+ goto __ClasspValidateOffloadInputParameters_Exit;
+ }
+
+ //
+ // Some third party disk class drivers do not query the geometry at initialization time,
+ // so this information may not be available at this time. If that is the case, we'll
+ // first query that information before proceeding with the rest of our validations.
+ //
+ if (fdoExtension->DiskGeometry.BytesPerSector == 0) {
+ status = ClassReadDriveCapacity(fdoExtension->DeviceObject);
+ if ((!NT_SUCCESS(status)) || (fdoExtension->DiskGeometry.BytesPerSector == 0)) {
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClasspValidateOffloadInputParameters (%p): Couldn't retrieve disk geometry, status: %x, bytes/sector: %u.\n",
+ DeviceObject,
+ status,
+ fdoExtension->DiskGeometry.BytesPerSector));
+
+ status = STATUS_INVALID_PARAMETER;
+ goto __ClasspValidateOffloadInputParameters_Exit;
+ }
+ }
+
+ //
+ // Data must be aligned to sector boundary and
+ // LengthInBytes must be > 0 for it to be a valid LBA entry
+ //
+ for (i = 0; i < dataSetRangesCount; i++) {
+ if ((dataSetRanges[i].StartingOffset % fdoExtension->DiskGeometry.BytesPerSector != 0) ||
+ (dataSetRanges[i].LengthInBytes % fdoExtension->DiskGeometry.BytesPerSector != 0) ||
+ (dataSetRanges[i].LengthInBytes == 0) ) {
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClasspValidateOffloadInputParameters (%p): Incorrect DataSetRanges entry %u [offset %I64x, length %I64x].\n",
+ DeviceObject,
+ i,
+ dataSetRanges[i].StartingOffset,
+ dataSetRanges[i].LengthInBytes));
+
+ status = STATUS_INVALID_PARAMETER;
+ goto __ClasspValidateOffloadInputParameters_Exit;
+ }
+
+ if ((ULONGLONG)dataSetRanges[i].StartingOffset + dataSetRanges[i].LengthInBytes > (ULONGLONG)fdoExtension->CommonExtension.PartitionLength.QuadPart) {
+
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClasspValidateOffloadInputParameters (%p): Error! DataSetRange %u (starting LBA %I64x) specified length %I64x exceeds the medium's capacity (%I64x).\n",
+ DeviceObject,
+ i,
+ dataSetRanges[i].StartingOffset,
+ dataSetRanges[i].LengthInBytes,
+ fdoExtension->CommonExtension.PartitionLength.QuadPart));
+
+ status = STATUS_NONEXISTENT_SECTOR;
+ goto __ClasspValidateOffloadInputParameters_Exit;
+ }
+ }
+
+__ClasspValidateOffloadInputParameters_Exit:
+ TracePrint((TRACE_LEVEL_VERBOSE,
+ TRACE_FLAG_IOCTL,
+ "ClasspValidateOffloadInputParameters (%p): Exiting function Irp %p with status %x.\n",
+ DeviceObject,
+ Irp,
+ status));
+
+ return status;
+}
+
+
+_IRQL_requires_same_
+NTSTATUS
+ClasspGetTokenOperationCommandBufferLength(
+ _In_ PDEVICE_OBJECT Fdo,
+ _In_ ULONG ServiceAction,
+ _Inout_ PULONG CommandBufferLength,
+ _Out_opt_ PULONG TokenOperationBufferLength,
+ _Out_opt_ PULONG ReceiveTokenInformationBufferLength
+ )
+
+/*++
+
+Routine description:
+
+ This routine calculates the buffer length required to service a TokenOperation and its
+ corresponding ReceiveTokenInformation command.
+
+Arguments:
+
+ Fdo - The functional device object processing the PopulateToken/WriteUsingToken request
+ ServiceAction - Used to distinguish between a PopulateToken and a WriteUsingToken operation
+ CommandBufferLength - Returns the length of the buffer needed to service the token request (i.e. TokenOperation and its corresponding ReceiveTokenInformation command)
+ TokenOperationBufferLength - Optional parameter, which returns the length of the buffer needed to service just the TokenOperation command.
+ ReceiveTokenInformationBufferLength - Optional parameter, which returns the length of the buffer needed to service just the ReceiveTokenInformation command.
+
+Return Value:
+
+ STATUS_SUCCESS
+
+--*/
+
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
+ ULONG tokenOperationBufferLength;
+ ULONG receiveTokenInformationBufferLength;
+ PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension;
+ PSTORAGE_ADAPTER_DESCRIPTOR adapterDesc = commonExtension->PartitionZeroExtension->AdapterDescriptor;
+ ULONG hwMaxXferLen;
+ ULONG bufferLength = 0;
+ ULONG tokenOperationHeaderSize;
+ ULONG responseSize;
+
+ TracePrint((TRACE_LEVEL_VERBOSE,
+ TRACE_FLAG_IOCTL,
+ "ClasspGetTokenOperationCommandBufferLengths (%p): Entering function.\n",
+ Fdo));
+
+ NT_ASSERT(fdoExt->FunctionSupportInfo->ValidInquiryPages.BlockDeviceRODLimits &&
+ NT_SUCCESS(fdoExt->FunctionSupportInfo->BlockDeviceRODLimitsData.CommandStatus));
+
+ if (ServiceAction == SERVICE_ACTION_POPULATE_TOKEN) {
+ tokenOperationHeaderSize = FIELD_OFFSET(POPULATE_TOKEN_HEADER, BlockDeviceRangeDescriptor);
+ responseSize = FIELD_OFFSET(RECEIVE_TOKEN_INFORMATION_RESPONSE_HEADER, TokenDescriptor) + sizeof(BLOCK_DEVICE_TOKEN_DESCRIPTOR);
+ } else {
+ tokenOperationHeaderSize = FIELD_OFFSET(WRITE_USING_TOKEN_HEADER, BlockDeviceRangeDescriptor);
+ responseSize = 0;
+ }
+
+ //
+ // The TokenOperation command can specify a parameter length of max 2^16.
+ // If the device has a max limit on the number of range descriptors that can be specified in
+ // the TokenOperation command, we are limited to the lesser of these two values.
+ //
+ if (fdoExt->FunctionSupportInfo->BlockDeviceRODLimitsData.MaximumRangeDescriptors == 0) {
+
+ tokenOperationBufferLength = MAX_TOKEN_OPERATION_PARAMETER_DATA_LENGTH;
+
+ } else {
+
+ tokenOperationBufferLength = MIN(tokenOperationHeaderSize + fdoExt->FunctionSupportInfo->BlockDeviceRODLimitsData.MaximumRangeDescriptors * sizeof(BLOCK_DEVICE_RANGE_DESCRIPTOR),
+ MAX_TOKEN_OPERATION_PARAMETER_DATA_LENGTH);
+ }
+
+
+ //
+ // The ReceiveTokenInformation command can specify a parameter length of max 2 ^ 32
+ // Also, since the sense data can be of variable size, we'll use MAX_SENSE_BUFFER_SIZE.
+ //
+ receiveTokenInformationBufferLength = MIN(FIELD_OFFSET(RECEIVE_TOKEN_INFORMATION_HEADER, SenseData) + MAX_SENSE_BUFFER_SIZE + responseSize,
+ MAX_RECEIVE_TOKEN_INFORMATION_PARAMETER_DATA_LENGTH);
+
+ //
+ // Since we're going to reuse the buffer for both the TokenOperation and the ReceiveTokenInformation
+ // commands, the buffer length needs to handle both operations.
+ //
+ bufferLength = MAX(tokenOperationBufferLength, receiveTokenInformationBufferLength);
+
+ //
+ // The buffer length needs to be further limited to the adapter's capability though.
+ //
+ hwMaxXferLen = MIN(fdoData->HwMaxXferLen, adapterDesc->MaximumTransferLength);
+ bufferLength = MIN(bufferLength, hwMaxXferLen);
+
+ *CommandBufferLength = bufferLength;
+
+ if (TokenOperationBufferLength) {
+ *TokenOperationBufferLength = tokenOperationBufferLength;
+ }
+
+ if (ReceiveTokenInformationBufferLength) {
+ *ReceiveTokenInformationBufferLength = receiveTokenInformationBufferLength;
+ }
+
+ TracePrint((TRACE_LEVEL_VERBOSE,
+ TRACE_FLAG_IOCTL,
+ "ClasspGetTokenOperationCommandBufferLengths (%p): Exiting function with bufferLength %u (tokenOpBufLen %u, recTokenInfoBufLen %u).\n",
+ Fdo,
+ bufferLength,
+ tokenOperationBufferLength,
+ receiveTokenInformationBufferLength));
+
+ return STATUS_SUCCESS;
+}
+
+
+_IRQL_requires_same_
+NTSTATUS
+ClasspGetTokenOperationDescriptorLimits(
+ _In_ PDEVICE_OBJECT Fdo,
+ _In_ ULONG ServiceAction,
+ _In_ ULONG MaxParameterBufferLength,
+ _Out_ PULONG MaxBlockDescriptorsCount,
+ _Out_ PULONGLONG MaxBlockDescriptorsLength
+ )
+
+/*++
+
+Routine description:
+
+ This routine calculates the maximum block descriptors and the maximum token transfer size
+ that can be accomodated in a single TokenOperation command.
+
+Arguments:
+
+ Fdo - The functional device object processing the PopulateToken/WriteUsingToken request
+ ServiceAction - Used to distinguish between a PopulateToken and a WriteUsingToken operation
+ MaxParameterBufferLength - The length constraint of the entire buffer for the parameter list based on other limitations (e.g. adapter max transfer length)
+ MaxBlockDescriptorsCount - Returns the maximum number of the block range descriptors that can be passed in a single TokenOperation command.
+ MaxBlockDescriptorsLength - Returns the maximum cumulative number of blocks across all the descriptors that must not be exceeded in a single TokenOperation command.
+
+Return Value:
+
+ STATUS_SUCCESS
+
+--*/
+
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
+ ULONG tokenOperationHeaderSize = (ServiceAction == SERVICE_ACTION_POPULATE_TOKEN) ?
+ FIELD_OFFSET(POPULATE_TOKEN_HEADER, BlockDeviceRangeDescriptor) :
+ FIELD_OFFSET(WRITE_USING_TOKEN_HEADER, BlockDeviceRangeDescriptor);
+
+ TracePrint((TRACE_LEVEL_VERBOSE,
+ TRACE_FLAG_IOCTL,
+ "ClasspGetTokenOperationDescriptorLimits (%p): Entering function.\n",
+ Fdo));
+
+ NT_ASSERT(fdoExt->FunctionSupportInfo->ValidInquiryPages.BlockDeviceRODLimits &&
+ NT_SUCCESS(fdoExt->FunctionSupportInfo->BlockDeviceRODLimitsData.CommandStatus));
+
+ *MaxBlockDescriptorsCount = (MaxParameterBufferLength - tokenOperationHeaderSize) / sizeof(BLOCK_DEVICE_RANGE_DESCRIPTOR);
+ *MaxBlockDescriptorsLength = (fdoExt->FunctionSupportInfo->BlockDeviceRODLimitsData.MaximumTokenTransferSize == 0) ?
+ MAX_TOKEN_TRANSFER_SIZE : fdoExt->FunctionSupportInfo->BlockDeviceRODLimitsData.MaximumTokenTransferSize;
+
+ TracePrint((TRACE_LEVEL_VERBOSE,
+ TRACE_FLAG_IOCTL,
+ "ClasspGetTokenOperationDescriptorLimits (%p): Exiting function with MaxDescr %u, MaxXferBlocks %I64u.\n",
+ Fdo,
+ *MaxBlockDescriptorsCount,
+ *MaxBlockDescriptorsLength));
+
+ return STATUS_SUCCESS;
+}
+
+
+
+_IRQL_requires_max_(APC_LEVEL)
+_IRQL_requires_min_(PASSIVE_LEVEL)
+_IRQL_requires_same_
+VOID
+ClasspConvertDataSetRangeToBlockDescr(
+ _In_ PDEVICE_OBJECT Fdo,
+ _In_ PVOID BlockDescr,
+ _Inout_ PULONG CurrentBlockDescrIndex,
+ _In_ ULONG MaxBlockDescrCount,
+ _Inout_ PULONG CurrentLbaCount,
+ _In_ ULONGLONG MaxLbaCount,
+ _Inout_ PDEVICE_DATA_SET_RANGE DataSetRange,
+ _Inout_ PULONGLONG TotalSectorsProcessed
+ )
+
+/*++
+
+Routine Description:
+
+ Convert DEVICE_DATA_SET_RANGE entry to be WINDOWS_BLOCK_DEVICE_RANGE_DESCRIPTOR entries.
+
+ As LengthInBytes field in DEVICE_DATA_SET_RANGE structure is 64 bits (bytes)
+ and LbaCount field in WINDOWS_BLOCK_DEVICE_RANGE_DESCRIPTOR structure is 32 bits (sectors),
+ it's possible that one DEVICE_DATA_SET_RANGE entry needs multiple
+ WINDOWS_BLOCK_DEVICE_RANGE_DESCRIPTOR entries. This routine handles the need for that
+ potential split.
+
+Arguments:
+
+ Fdo - The functional device object
+ BlockDescr - Pointer to the start of the Token Operation command's block descriptor
+ CurrentBlockDescrIndex - Index into the block descriptor at which to update the DataSetRange info
+ It also gets updated to return the index to the next empty one.
+ MaxBlockDescrCount - Maximum number of block descriptors that the device can handle in a single TokenOperation command
+ CurrentLbaCount - Returns the LBA of the last successfully processed DataSetRange
+ MaxLbaCount - Maximum transfer size that the device is capable of handling in a single TokenOperation command
+ DataSetRange - Contains information about one range extent that needs to be converted into a block descriptor
+ TotalSectorsProcessed - Returns the number of sectors corresponding to the DataSetRange that were succesfully mapped into block descriptors
+
+Return Value:
+
+ Nothing.
+
+ NOTE: if LengthInBytes does not reach to 0, the conversion for DEVICE_DATA_SET_RANGE entry
+ is not completed. Further conversion is needed by calling this function again.
+
+--*/
+
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
+ PBLOCK_DEVICE_RANGE_DESCRIPTOR blockDescr;
+ ULONGLONG startingSector;
+ ULONGLONG sectorCount;
+ ULONGLONG totalSectorCount;
+ ULONGLONG numberOfOptimalChunks;
+ USHORT optimalLbaPerDescrGranularity;
+ ULONG optimalLbaPerDescr;
+ ULONG maxLbaPerDescr;
+
+ TracePrint((TRACE_LEVEL_VERBOSE,
+ TRACE_FLAG_IOCTL,
+ "ClasspConvertDataSetRangeToBlockDescr (%p): Entering function. Starting offset %I64x.\n",
+ Fdo,
+ DataSetRange->StartingOffset));
+
+ fdoExtension = Fdo->DeviceExtension;
+ blockDescr = (PBLOCK_DEVICE_RANGE_DESCRIPTOR)BlockDescr;
+ totalSectorCount = 0;
+
+
+ //
+ // Since the OptimalTransferLength and the MaximumTransferLength are overloaded parameters for
+ // offloaded data transfers and regular read/write requests, it is not safe to use these values
+ // as they may report back what is used by regular read/write, which will cause a perf degradation
+ // in the offloaded case, since we may end up limiting the per block range descriptor length
+ // specified as opposed to what the target can actually handle in a single request.
+ // So until the SPC spec introduces these values specific to offloaded data transfers, we shall
+ // ignore them completely. The expectation we have from the target is as follows:
+ // 1. If the length specified in any of the block range descriptors is greater than the OTL that
+ // applies to ODX, the target will internally split into additional descriptors.
+ // 2. If the above causes it to run out of descriptors, or if the length specified in any of the
+ // descriptors is greater than the MTL that applies to ODX, the target will operate on as much
+ // data as possible and truncate the request to that point.
+ //
+ optimalLbaPerDescrGranularity = 0;
+ optimalLbaPerDescr = 0;
+ maxLbaPerDescr = 0;
+
+ if (optimalLbaPerDescr && maxLbaPerDescr) {
+
+ NT_ASSERT(optimalLbaPerDescr <= maxLbaPerDescr);
+ }
+
+ while ((DataSetRange->LengthInBytes > 0) &&
+ (*CurrentBlockDescrIndex < MaxBlockDescrCount) &&
+ (*CurrentLbaCount < MaxLbaCount)) {
+
+ startingSector = (ULONGLONG)(DataSetRange->StartingOffset / fdoExtension->DiskGeometry.BytesPerSector);
+
+ //
+ // Since the block descriptor has only 4 bytes for the number of logical blocks, we are
+ // constrained by that theoretical maximum.
+ //
+ sectorCount = MIN(DataSetRange->LengthInBytes / fdoExtension->DiskGeometry.BytesPerSector,
+ MAX_NUMBER_BLOCKS_PER_BLOCK_DEVICE_RANGE_DESCRIPTOR);
+
+ //
+ // We are constrained by MaxLbaCount.
+ //
+ if (((ULONGLONG)*CurrentLbaCount + sectorCount) >= MaxLbaCount) {
+
+ sectorCount = MaxLbaCount - *CurrentLbaCount;
+ }
+
+ //
+ // For each descriptor, the block count should be lesser than the MaximumTransferSize
+ //
+ if (maxLbaPerDescr > 0) {
+
+ //
+ // Each block device range descriptor can specify a max number of LBAs
+ //
+ sectorCount = MIN(sectorCount, maxLbaPerDescr);
+ }
+
+ //
+ // If the number of LBAs specified in the descriptor is greater than the OptimalTransferLength,
+ // processing of this descriptor by the target may incur a significant delay.
+ // So in order to allow the target to perform optimally, we'll further limit the number
+ // of blocks specified in any descriptor to be maximum OptimalTranferLength.
+ //
+ if (optimalLbaPerDescr > 0) {
+
+ sectorCount = MIN(sectorCount, optimalLbaPerDescr);
+ }
+
+ //
+ // In addition, it should either be an exact multiple of the OptimalTransferLengthGranularity,
+ // or be lesser than the OptimalTransferLengthGranularity (taken care of here).
+ //
+ if (optimalLbaPerDescrGranularity > 0) {
+
+ numberOfOptimalChunks = sectorCount / optimalLbaPerDescrGranularity;
+
+ if (numberOfOptimalChunks > 0) {
+ sectorCount = numberOfOptimalChunks * optimalLbaPerDescrGranularity;
+ }
+ }
+
+ NT_ASSERT(sectorCount <= MAX_NUMBER_BLOCKS_PER_BLOCK_DEVICE_RANGE_DESCRIPTOR);
+
+ REVERSE_BYTES_QUAD(blockDescr[*CurrentBlockDescrIndex].LogicalBlockAddress, &startingSector);
+ REVERSE_BYTES(blockDescr[*CurrentBlockDescrIndex].TransferLength, &sectorCount);
+
+ totalSectorCount += sectorCount;
+
+ DataSetRange->StartingOffset += sectorCount * fdoExtension->DiskGeometry.BytesPerSector;
+ DataSetRange->LengthInBytes -= sectorCount * fdoExtension->DiskGeometry.BytesPerSector;
+
+ *CurrentBlockDescrIndex += 1;
+ *CurrentLbaCount += (ULONG)sectorCount;
+
+ TracePrint((TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_IOCTL,
+ "ClasspConvertDataSetRangeToBlockDescr (%p): Descriptor: %u, starting LBA: %I64x, length: %I64x bytes, media size: %I64x.\n",
+ Fdo,
+ *CurrentBlockDescrIndex - 1,
+ startingSector,
+ sectorCount * fdoExtension->DiskGeometry.BytesPerSector,
+ (ULONGLONG)fdoExtension->CommonExtension.PartitionLength.QuadPart));
+ }
+
+ *TotalSectorsProcessed = totalSectorCount;
+
+ TracePrint((TRACE_LEVEL_VERBOSE,
+ TRACE_FLAG_IOCTL,
+ "ClasspConvertDataSetRangeToBlockDescr (%p): Exiting function (starting offset %I64x). Total sectors processed %I64u.\n",
+ Fdo,
+ DataSetRange->StartingOffset,
+ totalSectorCount));
+
+ return;
+}
+
+_IRQL_requires_same_
+PUCHAR
+ClasspBinaryToAscii(
+ _In_reads_(Length) PUCHAR HexBuffer,
+ _In_ ULONG Length,
+ _Inout_ PULONG UpdateLength
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will convert HexBuffer into an ascii NULL-terminated string.
+
+ Note: This routine will allocate memory for storing the ascii string. It is
+ the responsibility of the caller to free this buffer.
+
+Arguments:
+
+ HexBuffer - Pointer to the binary data.
+ Length - Length, in bytes, of HexBuffer.
+ UpdateLength - Storage to place the actual length of the returned string.
+
+Return Value:
+
+ ASCII string equivalent of the hex buffer, or NULL if an error occurred.
+
+--*/
+
+{
+ static const UCHAR integerTable[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
+ ULONG i;
+ ULONG j;
+ ULONG actualLength;
+ PUCHAR buffer = NULL;
+ UCHAR highWord;
+ UCHAR lowWord;
+
+ TracePrint((TRACE_LEVEL_VERBOSE,
+ TRACE_FLAG_IOCTL,
+ "ClasspBinaryToAscii (HexBuff %p): Entering function.\n",
+ HexBuffer));
+
+ if (!HexBuffer || Length == 0) {
+ *UpdateLength = 0;
+ goto __ClasspBinaryToAscii_Exit;
+ }
+
+ //
+ // Each byte converts into 2 chars:
+ // e.g. 0x05 => '0' '5'
+ // 0x0C => '0' 'C'
+ // 0x12 => '1' '2'
+ // And we need a terminating NULL for the string.
+ //
+ actualLength = (Length * 2) + 1;
+
+ //
+ // Allocate the buffer.
+ //
+ buffer = ExAllocatePoolWithTag(NonPagedPoolNx, actualLength, CLASSPNP_POOL_TAG_TOKEN_OPERATION);
+ if (!buffer) {
+
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClasspBinaryToAscii (HexBuff %p): Failed to allocate buffer for ASCII equivalent.\n",
+ HexBuffer));
+
+ *UpdateLength = 0;
+ goto __ClasspBinaryToAscii_Exit;
+ }
+
+ RtlZeroMemory(buffer, actualLength);
+
+ for (i = 0, j = 0; i < Length; i++) {
+
+ //
+ // Split out each nibble from the binary byte.
+ //
+ highWord = HexBuffer[i] >> 4;
+ lowWord = HexBuffer[i] & 0x0F;
+
+ //
+ // Using the lookup table, convert and stuff into
+ // the ascii buffer.
+ //
+ buffer[j++] = integerTable[highWord];
+#pragma warning(suppress: 6386) // PREFast bug means it doesn't see that Length < actualLength
+ buffer[j++] = integerTable[lowWord];
+ }
+
+ //
+ // Update the caller's length field.
+ //
+ *UpdateLength = actualLength;
+
+__ClasspBinaryToAscii_Exit:
+
+ TracePrint((TRACE_LEVEL_VERBOSE,
+ TRACE_FLAG_IOCTL,
+ "ClasspBinaryToAscii (HexBuff %p): Exiting function with buffer %s.\n",
+ HexBuffer,
+ (buffer == NULL) ? "" : (const char*)buffer));
+
+ return buffer;
+}
+
+_IRQL_requires_same_
+NTSTATUS
+ClasspStorageEventNotification(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _In_ PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine handles an asynchronous event notification (most likely from
+ port drivers). Currently, we only care about media status change events.
+
+Arguments:
+
+ DeviceObject - Supplies the device object associated with this request
+ Irp - The IRP to be processed
+
+Return Value:
+
+ NTSTATUS code
+
+--*/
+
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
+ PIO_STACK_LOCATION irpStack;
+ PSTORAGE_EVENT_NOTIFICATION storageEvents;
+ NTSTATUS status;
+
+ TracePrint((TRACE_LEVEL_VERBOSE,
+ TRACE_FLAG_IOCTL,
+ "ClasspStorageEventNotification (%p): Entering function Irp %p.\n",
+ DeviceObject,
+ Irp));
+
+ fdoExtension = DeviceObject->DeviceExtension;
+ irpStack = IoGetCurrentIrpStackLocation (Irp);
+ storageEvents = Irp->AssociatedIrp.SystemBuffer;
+ status = STATUS_SUCCESS;
+
+ if (!storageEvents) {
+
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClasspStorageEventNotification (%p): NULL storage events passed in.\n",
+ DeviceObject));
+
+ status = STATUS_INVALID_PARAMETER;
+ goto __ClasspStorageEventNotification_Exit;
+ }
+
+ if (irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(STORAGE_EVENT_NOTIFICATION)) {
+
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClasspStorageEventNotification (%p): Input buffer size (%u) too small.\n",
+ DeviceObject,
+ irpStack->Parameters.DeviceIoControl.InputBufferLength));
+
+ status = STATUS_INFO_LENGTH_MISMATCH;
+ goto __ClasspStorageEventNotification_Exit;
+ }
+
+ if ((storageEvents->Version != STORAGE_EVENT_NOTIFICATION_VERSION_V1) ||
+ (storageEvents->Size != sizeof(STORAGE_EVENT_NOTIFICATION))) {
+
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_IOCTL,
+ "ClasspStorageEventNotification (%p): Invalid version/size [version %u, size %u].\n",
+ DeviceObject,
+ storageEvents->Version,
+ storageEvents->Size));
+
+ status = STATUS_INVALID_PARAMETER;
+ goto __ClasspStorageEventNotification_Exit;
+ }
+
+ //
+ // Handle a media status event.
+ //
+ if (storageEvents->Events & STORAGE_EVENT_MEDIA_STATUS) {
+
+ //
+ // Only initiate operation if underlying port driver supports asynchronous notification
+ // and this is the FDO.
+ //
+ if ((fdoExtension->CommonExtension.IsFdo == TRUE) &&
+ (fdoExtension->FunctionSupportInfo->AsynchronousNotificationSupported)) {
+ ClassCheckMediaState(fdoExtension);
+ } else {
+ status = STATUS_NOT_SUPPORTED;
+ }
+
+ }
+
+__ClasspStorageEventNotification_Exit:
+ TracePrint((TRACE_LEVEL_VERBOSE,
+ TRACE_FLAG_IOCTL,
+ "ClasspStorageEventNotification (%p): Exiting function Irp %p with status %x.\n",
+ DeviceObject,
+ Irp,
+ status));
+
+ Irp->IoStatus.Information = 0;
+ Irp->IoStatus.Status = status;
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+
+ return status;
+}
+
+VOID
+ClasspZeroQERR(
+ _In_ PDEVICE_OBJECT DeviceObject
+ )
+/*++
+
+Routine Description:
+
+ This routine will attempt to set the QERR bit of the mode Control page to
+ zero.
+
+Arguments:
+
+ DeviceObject - Supplies the device object associated with this request
+
+Return Value:
+
+ None
+
+--*/
+{
+ PMODE_PARAMETER_HEADER modeData = NULL;
+ PMODE_CONTROL_PAGE pageData = NULL;
+ ULONG size = 0;
+
+ modeData = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
+ MODE_PAGE_DATA_SIZE,
+ CLASS_TAG_MODE_DATA);
+
+ if (modeData == NULL) {
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_SCSI, "ClasspZeroQERR: Unable to allocate mode data buffer\n"));
+ goto ClasspZeroQERR_Exit;
+ }
+
+ RtlZeroMemory(modeData, MODE_PAGE_DATA_SIZE);
+
+ size = ClassModeSense(DeviceObject,
+ (PCHAR) modeData,
+ MODE_PAGE_DATA_SIZE,
+ MODE_PAGE_CONTROL);
+
+ if (size < sizeof(MODE_PARAMETER_HEADER)) {
+
+ //
+ // Retry the request in case of a check condition.
+ //
+
+ size = ClassModeSense(DeviceObject,
+ (PCHAR) modeData,
+ MODE_PAGE_DATA_SIZE,
+ MODE_PAGE_CONTROL);
+
+ if (size < sizeof(MODE_PARAMETER_HEADER)) {
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_SCSI, "ClasspZeroQERR: Mode Sense failed\n"));
+ goto ClasspZeroQERR_Exit;
+ }
+ }
+
+ //
+ // If the size is greater than size indicated by the mode data reset
+ // the data to the mode data.
+ //
+
+ if (size > (ULONG) (modeData->ModeDataLength + 1)) {
+ size = modeData->ModeDataLength + 1;
+ }
+
+ //
+ // Look for control page in the returned mode page data.
+ //
+
+ pageData = ClassFindModePage((PCHAR) modeData,
+ size,
+ MODE_PAGE_CONTROL,
+ TRUE);
+
+ if (pageData) {
+ TracePrint((TRACE_LEVEL_VERBOSE,
+ TRACE_FLAG_SCSI,
+ "ClasspZeroQERR (%p): Current settings: QERR = %u, TST = %u, TAS = %u.\n",
+ DeviceObject,
+ pageData->QERR,
+ pageData->TST,
+ pageData->TAS));
+
+ if (pageData->QERR != 0) {
+ NTSTATUS status;
+ UCHAR pageSavable = 0;
+
+ //
+ // Set QERR to 0 with a Mode Select command. Re-use the modeData
+ // and pageData structures.
+ //
+ pageData->QERR = 0;
+
+ //
+ // We use the original Page Savable (PS) value for the Save Pages
+ // (SP) bit due to behavior described under the MODE SELECT(6)
+ // section of SPC-4.
+ //
+ pageSavable = pageData->PageSavable;
+
+ status = ClasspModeSelect(DeviceObject,
+ (PCHAR)modeData,
+ size,
+ pageSavable);
+
+ if (!NT_SUCCESS(status)) {
+ TracePrint((TRACE_LEVEL_WARNING,
+ TRACE_FLAG_SCSI,
+ "ClasspZeroQERR (%p): Failed to set QERR = 0 with status %x\n",
+ DeviceObject,
+ status));
+ }
+ }
+ }
+
+ClasspZeroQERR_Exit:
+
+ if (modeData != NULL) {
+ ExFreePool(modeData);
+ }
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+ClasspPowerActivateDevice(
+ _In_ PDEVICE_OBJECT DeviceObject
+ )
+/*++
+
+Routine Description:
+
+ This routine synchronously sends an IOCTL_STORAGE_POWER_ACTIVE to the port
+ PDO in order to take an active reference on the given device. The device
+ will remain powered up and active for as long as this active reference is
+ taken.
+
+ The caller should ensure idle power management is enabled for the device
+ before calling this function.
+
+ Call ClasspPowerIdleDevice to release the active reference.
+
+Arguments:
+
+ DeviceObject - Supplies the FDO associated with this request.
+
+Return Value:
+
+ STATUS_SUCCESS if the active reference was successfully taken.
+
+--*/
+{
+ NTSTATUS status = STATUS_UNSUCCESSFUL;
+ PIRP irp;
+ KEVENT event;
+ IO_STATUS_BLOCK ioStatus;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+
+ NT_ASSERT(fdoExtension->CommonExtension.IsFdo);
+ NT_ASSERT(fdoExtension->FunctionSupportInfo->IdlePower.IdlePowerEnabled);
+
+ KeInitializeEvent(&event, SynchronizationEvent, FALSE);
+
+ irp = IoBuildDeviceIoControlRequest(IOCTL_STORAGE_POWER_ACTIVE,
+ fdoExtension->LowerPdo,
+ NULL,
+ 0,
+ NULL,
+ 0,
+ FALSE,
+ &event,
+ &ioStatus);
+
+ if (irp != NULL) {
+ status = IoCallDriver(fdoExtension->LowerPdo, irp);
+ if (status == STATUS_PENDING) {
+ (VOID)KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
+ status = ioStatus.Status;
+ }
+ } else {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ return status;
+}
+
+_IRQL_requires_max_(PASSIVE_LEVEL)
+NTSTATUS
+ClasspPowerIdleDevice(
+ _In_ PDEVICE_OBJECT DeviceObject
+ )
+/*++
+
+Routine Description:
+
+ This routine synchronously sends an IOCTL_STORAGE_POWER_IDLE to the port
+ PDO in order to release an active reference on the given device.
+
+ A call to ClasspPowerActivateDevice *must* have preceded a call to this
+ function.
+
+ The caller should ensure idle power management is enabled for the device
+ before calling this function.
+
+Arguments:
+
+ DeviceObject - Supplies the FDO associated with this request.
+
+Return Value:
+
+ STATUS_SUCCESS if the active reference was successfully released.
+
+--*/
+{
+ NTSTATUS status = STATUS_UNSUCCESSFUL;
+ PIRP irp;
+ KEVENT event;
+ IO_STATUS_BLOCK ioStatus;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+
+ NT_ASSERT(fdoExtension->CommonExtension.IsFdo);
+ NT_ASSERT(fdoExtension->FunctionSupportInfo->IdlePower.IdlePowerEnabled);
+
+ KeInitializeEvent(&event, SynchronizationEvent, FALSE);
+
+ irp = IoBuildDeviceIoControlRequest(IOCTL_STORAGE_POWER_IDLE,
+ fdoExtension->LowerPdo,
+ NULL,
+ 0,
+ NULL,
+ 0,
+ FALSE,
+ &event,
+ &ioStatus);
+
+ if (irp != NULL) {
+ status = IoCallDriver(fdoExtension->LowerPdo, irp);
+ if (status == STATUS_PENDING) {
+ (VOID)KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
+ status = ioStatus.Status;
+ }
+ } else {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ return status;
+}
+
+
+#if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD)
+
+NTSTATUS
+ClasspGetHwFirmwareInfo(
+ _In_ PDEVICE_OBJECT DeviceObject
+ )
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+
+ PSTORAGE_HW_FIRMWARE_INFO firmwareInfo = NULL;
+ PSTORAGE_HW_FIRMWARE_INFO_QUERY query = NULL;
+
+ IO_STATUS_BLOCK ioStatus = {0};
+ ULONG dataLength = sizeof(STORAGE_HW_FIRMWARE_INFO);
+ ULONG iteration = 1;
+
+ CLASS_FUNCTION_SUPPORT oldState;
+ KLOCK_QUEUE_HANDLE lockHandle;
+
+ //
+ // Try to get firmware information that contains only one slot.
+ // We will retry the query if the required buffer size is bigger than that.
+ //
+retry:
+
+ firmwareInfo = ExAllocatePoolWithTag(NonPagedPoolNx, dataLength, CLASSPNP_POOL_TAG_FIRMWARE);
+
+ if (firmwareInfo == NULL) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT, "ClasspGetHwFirmwareInfo: cannot allocate memory to hold data. \n"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlZeroMemory(firmwareInfo, dataLength);
+
+ //
+ // Set up query data, making sure the "Flags" field indicating the request is for device itself.
+ //
+ query = (PSTORAGE_HW_FIRMWARE_INFO_QUERY)firmwareInfo;
+
+ query->Version = sizeof(STORAGE_HW_FIRMWARE_INFO_QUERY);
+ query->Size = sizeof(STORAGE_HW_FIRMWARE_INFO_QUERY);
+ query->Flags = 0;
+
+ //
+ // On the first pass we just want to get the first few
+ // bytes of the descriptor so we can read it's size
+ //
+ ClassSendDeviceIoControlSynchronous(IOCTL_STORAGE_FIRMWARE_GET_INFO,
+ commonExtension->LowerDeviceObject,
+ query,
+ sizeof(STORAGE_HW_FIRMWARE_INFO_QUERY),
+ dataLength,
+ FALSE,
+ &ioStatus
+ );
+
+ if (!NT_SUCCESS(ioStatus.Status) &&
+ (ioStatus.Status != STATUS_BUFFER_OVERFLOW)) {
+ if (ClasspLowerLayerNotSupport(ioStatus.Status)) {
+ oldState = InterlockedCompareExchange((PLONG)(&fdoExtension->FunctionSupportInfo->HwFirmwareGetInfoSupport), (LONG)NotSupported, (ULONG)SupportUnknown);
+ }
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT, "ClasspGetHwFirmwareInfo: error %lx trying to "
+ "query hardware firmware information #%d \n", ioStatus.Status, iteration));
+ FREE_POOL(firmwareInfo);
+ return ioStatus.Status;
+ }
+
+ //
+ // Catch implementation issues from lower level driver.
+ //
+ if ((firmwareInfo->Version < sizeof(STORAGE_HW_FIRMWARE_INFO)) ||
+ (firmwareInfo->Size < sizeof(STORAGE_HW_FIRMWARE_INFO)) ||
+ (firmwareInfo->SlotCount == 0) ||
+ (firmwareInfo->ActiveSlot >= firmwareInfo->SlotCount) ||
+ ((firmwareInfo->PendingActivateSlot >= firmwareInfo->SlotCount) && (firmwareInfo->PendingActivateSlot != STORAGE_HW_FIRMWARE_INVALID_SLOT)) ||
+ (firmwareInfo->ImagePayloadMaxSize > fdoExtension->AdapterDescriptor->MaximumTransferLength)) {
+
+ oldState = InterlockedCompareExchange((PLONG)(&fdoExtension->FunctionSupportInfo->HwFirmwareGetInfoSupport), (LONG)NotSupported, (ULONG)SupportUnknown);
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT, "ClasspGetHwFirmwareInfo: error in returned data! "
+ "Version: 0x%X, Size: 0x%X, SlotCount: 0x%X, ActiveSlot: 0x%X, PendingActiveSlot: 0x%X, ImagePayloadMaxSize: 0x%X \n",
+ firmwareInfo->Version,
+ firmwareInfo->Size,
+ firmwareInfo->SlotCount,
+ firmwareInfo->ActiveSlot,
+ firmwareInfo->PendingActivateSlot,
+ firmwareInfo->ImagePayloadMaxSize));
+
+ FREE_POOL(firmwareInfo);
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ //
+ // If the data size is bigger than sizeof(STORAGE_HW_FIRMWARE_INFO), e.g. device has more than one firmware slot,
+ // allocate a buffer to get all the data.
+ //
+ if ((firmwareInfo->Size > sizeof(STORAGE_HW_FIRMWARE_INFO)) &&
+ (iteration < 2)) {
+
+ dataLength = max(firmwareInfo->Size, sizeof(STORAGE_HW_FIRMWARE_INFO) + sizeof(STORAGE_HW_FIRMWARE_SLOT_INFO) * (firmwareInfo->SlotCount - 1));
+
+ //
+ // Retry the query with required buffer length.
+ //
+ FREE_POOL(firmwareInfo);
+ iteration++;
+ goto retry;
+ }
+
+
+ //
+ // Set the support status and use the memory we've allocated as caching buffer.
+ // In case of a competing thread already set the state, it will assign the caching buffer so release the current allocated one.
+ //
+ KeAcquireInStackQueuedSpinLock(&fdoExtension->FunctionSupportInfo->SyncLock, &lockHandle);
+
+ oldState = InterlockedCompareExchange((PLONG)(&fdoExtension->FunctionSupportInfo->HwFirmwareGetInfoSupport), (LONG)Supported, (ULONG)SupportUnknown);
+
+ if (oldState == SupportUnknown) {
+ fdoExtension->FunctionSupportInfo->HwFirmwareInfo = firmwareInfo;
+ } else if (oldState == Supported) {
+ //
+ // swap the buffers to keep the latest version.
+ //
+ PSTORAGE_HW_FIRMWARE_INFO cachedInfo = fdoExtension->FunctionSupportInfo->HwFirmwareInfo;
+
+ fdoExtension->FunctionSupportInfo->HwFirmwareInfo = firmwareInfo;
+
+ FREE_POOL(cachedInfo);
+ } else {
+ FREE_POOL(firmwareInfo);
+ }
+
+ KeReleaseInStackQueuedSpinLock(&lockHandle);
+
+ return ioStatus.Status;
+} // end ClasspGetHwFirmwareInfo()
+
+#endif // #if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD)
+
+NTSTATUS
+ClassDeviceHwFirmwareGetInfoProcess(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _Inout_ PIRP Irp
+ )
+/*
+Routine Description:
+
+ This function processes the Storage Hardware Firmware Get Information request.
+ If the information is not cached yet, it gets from lower level driver.
+
+Arguments:
+ DeviceObject: The target FDO.
+ Irp: The IRP which will contain the output buffer upon completion.
+
+Return Value:
+
+ NTSTATUS code.
+
+--*/
+{
+ NTSTATUS status = STATUS_SUCCESS;
+
+#if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD)
+
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
+ PSTORAGE_HW_FIRMWARE_INFO_QUERY query = (PSTORAGE_HW_FIRMWARE_INFO_QUERY)Irp->AssociatedIrp.SystemBuffer;
+ BOOLEAN passDown = FALSE;
+ BOOLEAN copyData = FALSE;
+
+
+ //
+ // Input buffer is not big enough to contain required input information.
+ //
+ if (irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(STORAGE_HW_FIRMWARE_INFO_QUERY)) {
+
+ status = STATUS_INFO_LENGTH_MISMATCH;
+ goto Exit_Firmware_Get_Info;
+ }
+
+ //
+ // Output buffer is too small to contain return data.
+ //
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(STORAGE_HW_FIRMWARE_INFO)) {
+
+ status = STATUS_BUFFER_TOO_SMALL;
+ goto Exit_Firmware_Get_Info;
+ }
+
+ //
+ // If the request is for a FDO, process the request for Storport, SDstor and Spaceport only.
+ //
+ if (commonExtension->IsFdo &&
+ (fdoExtension->MiniportDescriptor->Portdriver != StoragePortCodeSetStorport) &&
+ (fdoExtension->MiniportDescriptor->Portdriver != StoragePortCodeSetSpaceport) &&
+ (fdoExtension->MiniportDescriptor->Portdriver != StoragePortCodeSetSDport)) {
+
+ status = STATUS_NOT_IMPLEMENTED;
+ goto Exit_Firmware_Get_Info;
+ }
+
+ //
+ // Buffer "FunctionSupportInfo" is allocated during start device process. Following check defenses the situation
+ // of receiving this IOCTL when the device is created but not started, or device start failed but not get removed yet.
+ //
+ if (commonExtension->IsFdo && (fdoExtension->FunctionSupportInfo == NULL)) {
+
+ status = STATUS_UNSUCCESSFUL;
+ goto Exit_Firmware_Get_Info;
+ }
+
+ //
+ // Process the situation that request should be forwarded to lower level.
+ //
+ if (!commonExtension->IsFdo) {
+ passDown = TRUE;
+ }
+
+ if ((query->Flags & STORAGE_HW_FIRMWARE_REQUEST_FLAG_CONTROLLER) != 0) {
+ passDown = TRUE;
+ }
+
+ if (passDown) {
+
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+ return status;
+ }
+
+ //
+ // The request is for a FDO. Process the request.
+ //
+ if (fdoExtension->FunctionSupportInfo->HwFirmwareGetInfoSupport == NotSupported) {
+ status = STATUS_NOT_IMPLEMENTED;
+ goto Exit_Firmware_Get_Info;
+ } else {
+ //
+ // Retrieve information from lower layer for the request. The cached information is not used
+ // in case device firmware information changed.
+ //
+ status = ClasspGetHwFirmwareInfo(DeviceObject);
+ copyData = NT_SUCCESS(status);
+ }
+
+Exit_Firmware_Get_Info:
+
+ if (copyData) {
+ //
+ // Firmware information is already cached in classpnp. Return a copy.
+ //
+ ULONG dataLength = min(irpStack->Parameters.DeviceIoControl.OutputBufferLength, fdoExtension->FunctionSupportInfo->HwFirmwareInfo->Size);
+
+ memcpy(Irp->AssociatedIrp.SystemBuffer, fdoExtension->FunctionSupportInfo->HwFirmwareInfo, dataLength);
+
+ Irp->IoStatus.Information = dataLength;
+ }
+
+ Irp->IoStatus.Status = status;
+
+#else
+ status = STATUS_NOT_IMPLEMENTED;
+ Irp->IoStatus.Status = status;
+#endif // #if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD)
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+
+ return status;
+}
+
+
+#if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD)
+_IRQL_requires_same_
+_IRQL_requires_max_(DISPATCH_LEVEL)
+NTSTATUS
+ClassHwFirmwareDownloadComplete (
+ _In_ PDEVICE_OBJECT Fdo,
+ _In_ PIRP Irp,
+ _In_reads_opt_(_Inexpressible_("varies")) PVOID Context
+ )
+{
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
+
+ PIRP originalIrp;
+
+ //
+ // Free the allocated buffer for firmware image.
+ //
+ if (Context != NULL) {
+ FREE_POOL(Context);
+ }
+
+ originalIrp = irpStack->Parameters.Others.Argument1;
+
+ NT_ASSERT(originalIrp != NULL);
+
+ originalIrp->IoStatus.Status = Irp->IoStatus.Status;
+ originalIrp->IoStatus.Information = Irp->IoStatus.Information;
+
+ ClassReleaseRemoveLock(Fdo, originalIrp);
+ ClassCompleteRequest(Fdo, originalIrp, IO_DISK_INCREMENT);
+
+ IoFreeIrp(Irp);
+
+ return STATUS_MORE_PROCESSING_REQUIRED;
+
+} // end ClassHwFirmwareDownloadComplete()
+#endif // #if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD)
+
+
+NTSTATUS
+ClassDeviceHwFirmwareDownloadProcess(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _Inout_ PIRP Irp,
+ _Inout_ PSCSI_REQUEST_BLOCK Srb
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+
+#if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD)
+
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
+ PSTORAGE_HW_FIRMWARE_DOWNLOAD firmwareDownload = (PSTORAGE_HW_FIRMWARE_DOWNLOAD)Irp->AssociatedIrp.SystemBuffer;
+ BOOLEAN passDown = FALSE;
+ ULONG i;
+ ULONG bufferSize = 0;
+ PUCHAR firmwareImageBuffer = NULL;
+ PIRP irp2 = NULL;
+ PIO_STACK_LOCATION newStack = NULL;
+ PCDB cdb = NULL;
+
+
+ //
+ // Input buffer is not big enough to contain required input information.
+ //
+ if (irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(STORAGE_HW_FIRMWARE_DOWNLOAD)) {
+
+ status = STATUS_INFO_LENGTH_MISMATCH;
+ goto Exit_Firmware_Download;
+ }
+
+ //
+ // Input buffer basic validation.
+ //
+ if ((firmwareDownload->Version < sizeof(STORAGE_HW_FIRMWARE_DOWNLOAD)) ||
+ (firmwareDownload->Size > irpStack->Parameters.DeviceIoControl.InputBufferLength) ||
+ ((firmwareDownload->BufferSize + FIELD_OFFSET(STORAGE_HW_FIRMWARE_DOWNLOAD, ImageBuffer)) > firmwareDownload->Size)) {
+
+ status = STATUS_INVALID_PARAMETER;
+ goto Exit_Firmware_Download;
+ }
+
+ //
+ // If the request is for a FDO, process the request for Storport, SDstor and Spaceport only.
+ //
+ if (commonExtension->IsFdo &&
+ (fdoExtension->MiniportDescriptor->Portdriver != StoragePortCodeSetStorport) &&
+ (fdoExtension->MiniportDescriptor->Portdriver != StoragePortCodeSetSpaceport) &&
+ (fdoExtension->MiniportDescriptor->Portdriver != StoragePortCodeSetSDport)) {
+
+ status = STATUS_NOT_IMPLEMENTED;
+ goto Exit_Firmware_Download;
+ }
+
+ //
+ // Buffer "FunctionSupportInfo" is allocated during start device process. Following check defenses the situation
+ // of receiving this IOCTL when the device is created but not started, or device start failed but not get removed yet.
+ //
+ if (commonExtension->IsFdo && (fdoExtension->FunctionSupportInfo == NULL)) {
+
+ status = STATUS_UNSUCCESSFUL;
+ goto Exit_Firmware_Download;
+ }
+
+ //
+ // Process the situation that request should be forwarded to lower level.
+ //
+ if (!commonExtension->IsFdo) {
+ passDown = TRUE;
+ }
+
+ if ((firmwareDownload->Flags & STORAGE_HW_FIRMWARE_REQUEST_FLAG_CONTROLLER) != 0) {
+ passDown = TRUE;
+ }
+
+ if (passDown) {
+
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+ FREE_POOL(Srb);
+ return status;
+ }
+
+ //
+ // If firmware information hasn't been cached in classpnp, retrieve it.
+ //
+ if (fdoExtension->FunctionSupportInfo->HwFirmwareInfo == NULL) {
+ if (fdoExtension->FunctionSupportInfo->HwFirmwareGetInfoSupport == NotSupported) {
+ status = STATUS_NOT_IMPLEMENTED;
+ goto Exit_Firmware_Download;
+ } else {
+ //
+ // If this is the first time of retrieving firmware information,
+ // send request to lower level to get it.
+ //
+ status = ClasspGetHwFirmwareInfo(DeviceObject);
+
+ if (!NT_SUCCESS(status)) {
+ goto Exit_Firmware_Download;
+ }
+ }
+ }
+
+ //
+ // Fail the request if the firmware information cannot be retrieved.
+ //
+ if (fdoExtension->FunctionSupportInfo->HwFirmwareInfo == NULL) {
+ if (fdoExtension->FunctionSupportInfo->HwFirmwareGetInfoSupport == NotSupported) {
+ status = STATUS_NOT_IMPLEMENTED;
+ } else {
+ status = STATUS_UNSUCCESSFUL;
+ }
+
+ goto Exit_Firmware_Download;
+ }
+
+ //
+ // Validate the device support
+ //
+ if (fdoExtension->FunctionSupportInfo->HwFirmwareInfo->SupportUpgrade == FALSE) {
+ status = STATUS_NOT_SUPPORTED;
+ goto Exit_Firmware_Download;
+ }
+
+ //
+ // Check if the slot can be used to hold firmware image.
+ //
+ for (i = 0; i < fdoExtension->FunctionSupportInfo->HwFirmwareInfo->SlotCount; i++) {
+ if (fdoExtension->FunctionSupportInfo->HwFirmwareInfo->Slot[i].SlotNumber == firmwareDownload->Slot) {
+ break;
+ }
+ }
+
+ if ((i >= fdoExtension->FunctionSupportInfo->HwFirmwareInfo->SlotCount) ||
+ (fdoExtension->FunctionSupportInfo->HwFirmwareInfo->Slot[i].ReadOnly == TRUE)) {
+ //
+ // Either the slot number is out of scope or the slot is read-only.
+ //
+ status = STATUS_INVALID_PARAMETER;
+ goto Exit_Firmware_Download;
+ }
+
+ //
+ // Buffer size and alignment validation.
+ // Max Offset and Buffer Size can be represented by SCSI command is max value for 3 bytes.
+ //
+ if ((firmwareDownload->BufferSize == 0) ||
+ ((firmwareDownload->BufferSize % fdoExtension->FunctionSupportInfo->HwFirmwareInfo->ImagePayloadAlignment) != 0) ||
+ (firmwareDownload->BufferSize > fdoExtension->FunctionSupportInfo->HwFirmwareInfo->ImagePayloadMaxSize) ||
+ (firmwareDownload->BufferSize > fdoExtension->AdapterDescriptor->MaximumTransferLength) ||
+ ((firmwareDownload->Offset % fdoExtension->FunctionSupportInfo->HwFirmwareInfo->ImagePayloadAlignment) != 0) ||
+ (firmwareDownload->Offset > 0xFFFFFF) ||
+ (firmwareDownload->BufferSize > 0xFFFFFF)) {
+
+ status = STATUS_INVALID_PARAMETER;
+ goto Exit_Firmware_Download;
+ }
+
+
+ //
+ // Process the request by translating it into WRITE BUFFER command.
+ //
+ if (((ULONG_PTR)firmwareDownload->ImageBuffer % fdoExtension->FunctionSupportInfo->HwFirmwareInfo->ImagePayloadAlignment) != 0) {
+ //
+ // Allocate buffer aligns to PAGE_SIZE to accommodate any alignment requirement.
+ //
+ bufferSize = ALIGN_UP_BY(firmwareDownload->BufferSize, PAGE_SIZE);
+
+#pragma prefast(suppress:6014, "The allocated memory that firmwareImageBuffer points to will be freed in ClassHwFirmwareDownloadComplete().")
+ firmwareImageBuffer = ExAllocatePoolWithTag(NonPagedPoolNx, bufferSize, CLASSPNP_POOL_TAG_FIRMWARE);
+
+ if (firmwareImageBuffer == NULL) {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Exit_Firmware_Download;
+ }
+
+ RtlZeroMemory(firmwareImageBuffer, bufferSize);
+
+ RtlCopyMemory(firmwareImageBuffer, firmwareDownload->ImageBuffer, (ULONG)firmwareDownload->BufferSize);
+
+ } else {
+ firmwareImageBuffer = firmwareDownload->ImageBuffer;
+ bufferSize = (ULONG)firmwareDownload->BufferSize;
+ }
+
+ //
+ // Allocate a new irp to send the WRITE BUFFER command down.
+ // Similar process as IOCTL_STORAGE_CHECK_VERIFY.
+ //
+ irp2 = IoAllocateIrp((CCHAR)(DeviceObject->StackSize + 3), FALSE);
+
+ if (irp2 == NULL) {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+
+ if (firmwareImageBuffer != firmwareDownload->ImageBuffer) {
+ FREE_POOL(firmwareImageBuffer);
+ }
+
+ goto Exit_Firmware_Download;
+ }
+
+ //
+ // Make sure to acquire the lock for the new irp.
+ //
+ ClassAcquireRemoveLock(DeviceObject, irp2);
+
+ irp2->Tail.Overlay.Thread = Irp->Tail.Overlay.Thread;
+ IoSetNextIrpStackLocation(irp2);
+
+ //
+ // Set the top stack location and shove the master Irp into the
+ // top location
+ //
+ newStack = IoGetCurrentIrpStackLocation(irp2);
+ newStack->Parameters.Others.Argument1 = Irp;
+ newStack->DeviceObject = DeviceObject;
+
+ //
+ // Stick the firmware download completion routine onto the stack
+ // and prepare the irp for the port driver
+ //
+ IoSetCompletionRoutine(irp2,
+ ClassHwFirmwareDownloadComplete,
+ (firmwareImageBuffer != firmwareDownload->ImageBuffer) ? firmwareImageBuffer : NULL,
+ TRUE,
+ TRUE,
+ TRUE);
+
+ IoSetNextIrpStackLocation(irp2);
+ newStack = IoGetCurrentIrpStackLocation(irp2);
+ newStack->DeviceObject = DeviceObject;
+ newStack->MajorFunction = irpStack->MajorFunction;
+ newStack->MinorFunction = irpStack->MinorFunction;
+ newStack->Flags = irpStack->Flags;
+
+
+ //
+ // Mark the master irp as pending - whether the lower level
+ // driver completes it immediately or not this should allow it
+ // to go all the way back up.
+ //
+ IoMarkIrpPending(Irp);
+
+ //
+ // Setup the CDB.
+ //
+ SrbSetCdbLength(Srb, CDB10GENERIC_LENGTH);
+ cdb = SrbGetCdb(Srb);
+ cdb->WRITE_BUFFER.OperationCode = SCSIOP_WRITE_DATA_BUFF;
+ cdb->WRITE_BUFFER.Mode = 0x0E;
+ cdb->WRITE_BUFFER.ModeSpecific = 0; //Reserved for Mode 0x0E
+ cdb->WRITE_BUFFER.BufferID = firmwareDownload->Slot;
+
+ cdb->WRITE_BUFFER.BufferOffset[0] = *((PCHAR)&firmwareDownload->Offset + 2);
+ cdb->WRITE_BUFFER.BufferOffset[1] = *((PCHAR)&firmwareDownload->Offset + 1);
+ cdb->WRITE_BUFFER.BufferOffset[2] = *((PCHAR)&firmwareDownload->Offset);
+
+ cdb->WRITE_BUFFER.ParameterListLength[0] = *((PCHAR)&firmwareDownload->BufferSize + 2);
+ cdb->WRITE_BUFFER.ParameterListLength[1] = *((PCHAR)&firmwareDownload->BufferSize + 1);
+ cdb->WRITE_BUFFER.ParameterListLength[2] = *((PCHAR)&firmwareDownload->BufferSize);
+
+ //
+ // Send as a tagged command.
+ //
+ SrbSetRequestAttribute(Srb, SRB_HEAD_OF_QUEUE_TAG_REQUEST);
+ SrbSetSrbFlags(Srb, SRB_FLAGS_NO_QUEUE_FREEZE | SRB_FLAGS_QUEUE_ACTION_ENABLE);
+
+ //
+ // Set timeout value.
+ //
+ SrbSetTimeOutValue(Srb, fdoExtension->TimeOutValue);
+
+ //
+ // This routine uses a completion routine so we don't want to release
+ // the remove lock until then.
+ //
+ status = ClassSendSrbAsynchronous(DeviceObject,
+ Srb,
+ irp2,
+ firmwareImageBuffer,
+ bufferSize,
+ TRUE);
+
+ if (status != STATUS_PENDING) {
+ //
+ // If the new irp cannot be sent down, free allocated memory and bail out.
+ //
+ if (firmwareImageBuffer != firmwareDownload->ImageBuffer) {
+ FREE_POOL(firmwareImageBuffer);
+ }
+
+ //
+ // If the irp cannot be sent down, the Srb has been freed. NULL it to prevent from freeing it again.
+ //
+ Srb = NULL;
+
+ ClassReleaseRemoveLock(DeviceObject, irp2);
+
+ IoFreeIrp(irp2);
+
+ goto Exit_Firmware_Download;
+ }
+
+ return status;
+
+Exit_Firmware_Download:
+
+ //
+ // Firmware Download request will be failed.
+ //
+ NT_ASSERT(!NT_SUCCESS(status));
+
+ Irp->IoStatus.Status = status;
+
+#else
+ status = STATUS_NOT_IMPLEMENTED;
+ Irp->IoStatus.Status = status;
+#endif // #if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD)
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+
+ FREE_POOL(Srb);
+
+ return status;
+}
+
+NTSTATUS
+ClassDeviceHwFirmwareActivateProcess(
+ _In_ PDEVICE_OBJECT DeviceObject,
+ _Inout_ PIRP Irp,
+ _Inout_ PSCSI_REQUEST_BLOCK Srb
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+
+#if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD)
+
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
+ PSTORAGE_HW_FIRMWARE_ACTIVATE firmwareActivate = (PSTORAGE_HW_FIRMWARE_ACTIVATE)Irp->AssociatedIrp.SystemBuffer;
+ BOOLEAN passDown = FALSE;
+ PCDB cdb = NULL;
+ ULONG i;
+
+
+ //
+ // Input buffer is not big enough to contain required input information.
+ //
+ if (irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(STORAGE_HW_FIRMWARE_ACTIVATE)) {
+
+ status = STATUS_INFO_LENGTH_MISMATCH;
+ goto Exit_Firmware_Activate;
+ }
+
+ //
+ // Input buffer basic validation.
+ //
+ if ((firmwareActivate->Version < sizeof(STORAGE_HW_FIRMWARE_ACTIVATE)) ||
+ (firmwareActivate->Size > irpStack->Parameters.DeviceIoControl.InputBufferLength)) {
+
+ status = STATUS_INVALID_PARAMETER;
+ goto Exit_Firmware_Activate;
+ }
+
+ //
+ // If the request is for a FDO, process the request for Storport, SDstor and Spaceport only.
+ //
+ if (commonExtension->IsFdo &&
+ (fdoExtension->MiniportDescriptor->Portdriver != StoragePortCodeSetStorport) &&
+ (fdoExtension->MiniportDescriptor->Portdriver != StoragePortCodeSetSpaceport) &&
+ (fdoExtension->MiniportDescriptor->Portdriver != StoragePortCodeSetSDport)) {
+
+ status = STATUS_NOT_IMPLEMENTED;
+ goto Exit_Firmware_Activate;
+ }
+
+ //
+ // Buffer "FunctionSupportInfo" is allocated during start device process. Following check defenses the situation
+ // of receiving this IOCTL when the device is created but not started, or device start failed but not get removed yet.
+ //
+ if (commonExtension->IsFdo && (fdoExtension->FunctionSupportInfo == NULL)) {
+
+ status = STATUS_UNSUCCESSFUL;
+ goto Exit_Firmware_Activate;
+ }
+
+ //
+ // Process the situation that request should be forwarded to lower level.
+ //
+ if (!commonExtension->IsFdo) {
+ passDown = TRUE;
+ }
+
+ if ((firmwareActivate->Flags & STORAGE_HW_FIRMWARE_REQUEST_FLAG_CONTROLLER) != 0) {
+ passDown = TRUE;
+ }
+
+ if (passDown) {
+
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+ FREE_POOL(Srb);
+ return status;
+ }
+
+ //
+ // If firmware information hasn't been cached in classpnp, retrieve it.
+ //
+ if (fdoExtension->FunctionSupportInfo->HwFirmwareInfo == NULL) {
+ if (fdoExtension->FunctionSupportInfo->HwFirmwareGetInfoSupport == NotSupported) {
+ status = STATUS_NOT_IMPLEMENTED;
+ goto Exit_Firmware_Activate;
+ } else {
+ //
+ // If this is the first time of retrieving firmware information,
+ // send request to lower level to get it.
+ //
+ status = ClasspGetHwFirmwareInfo(DeviceObject);
+
+ if (!NT_SUCCESS(status)) {
+ goto Exit_Firmware_Activate;
+ }
+ }
+ }
+
+ //
+ // Fail the request if the firmware information cannot be retrieved.
+ //
+ if (fdoExtension->FunctionSupportInfo->HwFirmwareInfo == NULL) {
+ if (fdoExtension->FunctionSupportInfo->HwFirmwareGetInfoSupport == NotSupported) {
+ status = STATUS_NOT_IMPLEMENTED;
+ } else {
+ status = STATUS_UNSUCCESSFUL;
+ }
+
+ goto Exit_Firmware_Activate;
+ }
+
+ //
+ // Validate the device support
+ //
+ if (fdoExtension->FunctionSupportInfo->HwFirmwareInfo->SupportUpgrade == FALSE) {
+ status = STATUS_NOT_SUPPORTED;
+ goto Exit_Firmware_Activate;
+ }
+
+ //
+ // Check if the slot number is valid.
+ //
+ for (i = 0; i < fdoExtension->FunctionSupportInfo->HwFirmwareInfo->SlotCount; i++) {
+ if (fdoExtension->FunctionSupportInfo->HwFirmwareInfo->Slot[i].SlotNumber == firmwareActivate->Slot) {
+ break;
+ }
+ }
+
+ if (i >= fdoExtension->FunctionSupportInfo->HwFirmwareInfo->SlotCount) {
+ //
+ // Either the slot number is out of scope or the slot is read-only.
+ //
+ status = STATUS_INVALID_PARAMETER;
+ goto Exit_Firmware_Activate;
+ }
+
+ //
+ // Process the request by translating it into WRITE BUFFER command.
+ //
+ //
+ // Setup the CDB. This should be an untagged request.
+ //
+ SrbSetCdbLength(Srb, CDB10GENERIC_LENGTH);
+ cdb = SrbGetCdb(Srb);
+ cdb->WRITE_BUFFER.OperationCode = SCSIOP_WRITE_DATA_BUFF;
+ cdb->WRITE_BUFFER.Mode = 0x0F;
+ cdb->WRITE_BUFFER.ModeSpecific = 0; //Reserved for Mode 0x0F
+ cdb->WRITE_BUFFER.BufferID = firmwareActivate->Slot; //NOTE: this field will be ignored by SCSI device.
+
+ //
+ // Set timeout value.
+ //
+ SrbSetTimeOutValue(Srb, FIRMWARE_ACTIVATE_TIMEOUT_VALUE);
+
+ //
+ // This routine uses a completion routine - ClassIoComplete() so we don't want to release
+ // the remove lock until then.
+ //
+ status = ClassSendSrbAsynchronous(DeviceObject,
+ Srb,
+ Irp,
+ NULL,
+ 0,
+ FALSE);
+
+ if (status != STATUS_PENDING) {
+ //
+ // If the irp cannot be sent down, the Srb has been freed. NULL it to prevent from freeing it again.
+ //
+ Srb = NULL;
+
+ goto Exit_Firmware_Activate;
+ }
+
+ return status;
+
+Exit_Firmware_Activate:
+
+ //
+ // Firmware Activate request will be failed.
+ //
+ NT_ASSERT(!NT_SUCCESS(status));
+
+ Irp->IoStatus.Status = status;
+
+#else
+ status = STATUS_NOT_IMPLEMENTED;
+ Irp->IoStatus.Status = status;
+#endif // #if (NTDDI_VERSION >= NTDDI_WINTHRESHOLD)
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+
+ FREE_POOL(Srb);
+ return status;
+}
+
+
diff --git a/storage/class/classpnp/src/xferpkt.c b/storage/class/classpnp/src/xferpkt.c
new file mode 100644
index 00000000..e4a051ed
--- /dev/null
+++ b/storage/class/classpnp/src/xferpkt.c
@@ -0,0 +1,2047 @@
+/*++
+
+Copyright (C) Microsoft Corporation, 1991 - 2010
+
+Module Name:
+
+ xferpkt.c
+
+Abstract:
+
+ Packet routines for CLASSPNP
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+
+Revision History:
+
+--*/
+
+#include "classp.h"
+#include "debug.h"
+
+#ifdef DEBUG_USE_WPP
+#include "xferpkt.tmh"
+#endif
+
+#ifdef ALLOC_PRAGMA
+ #pragma alloc_text(PAGE, InitializeTransferPackets)
+ #pragma alloc_text(PAGE, DestroyAllTransferPackets)
+ #pragma alloc_text(PAGE, SetupEjectionTransferPacket)
+ #pragma alloc_text(PAGE, SetupModeSenseTransferPacket)
+ #pragma alloc_text(PAGE, CleanupTransferPacketToWorkingSetSizeWorker)
+ #pragma alloc_text(PAGE, ClasspSetupPopulateTokenTransferPacket)
+#endif
+
+/*
+ * InitializeTransferPackets
+ *
+ * Allocate/initialize TRANSFER_PACKETs and related resources.
+ */
+NTSTATUS InitializeTransferPackets(PDEVICE_OBJECT Fdo)
+{
+ PCOMMON_DEVICE_EXTENSION commonExt = Fdo->DeviceExtension;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
+ PSTORAGE_ADAPTER_DESCRIPTOR adapterDesc = commonExt->PartitionZeroExtension->AdapterDescriptor;
+ PSTORAGE_DEVICE_IO_CAPABILITY_DESCRIPTOR devIoCapabilityDesc = NULL;
+ STORAGE_PROPERTY_ID propertyId;
+ OSVERSIONINFOEXW osVersionInfo;
+ ULONG hwMaxPages;
+ ULONG arraySize;
+ ULONG index;
+ ULONG maxOutstandingIOPerLUN;
+ ULONG minWorkingSetTransferPackets;
+ ULONG maxWorkingSetTransferPackets;
+
+ NTSTATUS status = STATUS_SUCCESS;
+
+ PAGED_CODE();
+
+ //
+ // Precompute the maximum transfer length
+ //
+ NT_ASSERT(adapterDesc->MaximumTransferLength);
+
+ hwMaxPages = adapterDesc->MaximumPhysicalPages ? adapterDesc->MaximumPhysicalPages-1 : 0;
+
+ fdoData->HwMaxXferLen = MIN(adapterDesc->MaximumTransferLength, hwMaxPages << PAGE_SHIFT);
+ fdoData->HwMaxXferLen = MAX(fdoData->HwMaxXferLen, PAGE_SIZE);
+
+ //
+ // Allocate per-node free packet lists
+ //
+ arraySize = KeQueryHighestNodeNumber() + 1;
+ fdoData->FreeTransferPacketsLists =
+ ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
+ sizeof(PNL_SLIST_HEADER) * arraySize,
+ CLASS_TAG_PRIVATE_DATA);
+
+ if (fdoData->FreeTransferPacketsLists == NULL) {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ return status;
+ }
+
+ for (index = 0; index < arraySize; index++) {
+ InitializeSListHead(&(fdoData->FreeTransferPacketsLists[index].SListHeader));
+ fdoData->FreeTransferPacketsLists[index].NumTotalTransferPackets = 0;
+ fdoData->FreeTransferPacketsLists[index].NumFreeTransferPackets = 0;
+ }
+
+ InitializeListHead(&fdoData->AllTransferPacketsList);
+
+ //
+ // Set the packet threshold numbers based on the Windows Client or Server SKU.
+ //
+
+ osVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
+ status = RtlGetVersion((POSVERSIONINFOW) &osVersionInfo);
+
+ NT_ASSERT( NT_SUCCESS(status));
+
+ //
+ // Retrieve info on IO capability supported by port drivers
+ //
+
+ propertyId = StorageDeviceIoCapabilityProperty;
+ status = ClassGetDescriptor(fdoExt->CommonExtension.LowerDeviceObject,
+ &propertyId,
+ &devIoCapabilityDesc);
+
+ if (NT_SUCCESS(status) && (devIoCapabilityDesc != NULL)) {
+ maxOutstandingIOPerLUN = devIoCapabilityDesc->LunMaxIoCount;
+ FREE_POOL(devIoCapabilityDesc);
+
+#if DBG
+ fdoData->MaxOutstandingIOPerLUN = maxOutstandingIOPerLUN;
+#endif
+
+ } else {
+ maxOutstandingIOPerLUN = MAX_OUTSTANDING_IO_PER_LUN_DEFAULT;
+
+#if DBG
+ fdoData->MaxOutstandingIOPerLUN = 0;
+#endif
+
+ }
+
+ //
+ // StorageDeviceIoCapabilityProperty support is optional so
+ // ignore any failures.
+ //
+
+ status = STATUS_SUCCESS;
+
+
+ if ((osVersionInfo.wProductType != VER_NT_DOMAIN_CONTROLLER) &&
+ (osVersionInfo.wProductType != VER_NT_SERVER)) {
+
+ // this is Client SKU
+
+ minWorkingSetTransferPackets = MIN_WORKINGSET_TRANSFER_PACKETS_Client;
+
+ // Note: the reason we use max here is to guarantee a reasonable large max number
+ // in the case where the port driver may return a very small supported outstanding
+ // IOs. For example, even EMMC drive only reports 1 outstanding IO supported, we
+ // may still want to set this value to be at least
+ // MAX_WORKINGSET_TRANSFER_PACKETS_Client.
+ maxWorkingSetTransferPackets = max(MAX_WORKINGSET_TRANSFER_PACKETS_Client,
+ 2 * maxOutstandingIOPerLUN);
+
+ } else {
+
+ // this is Server SKU
+ // Note: the addition max here to make sure we set the min to be at least
+ // MIN_WORKINGSET_TRANSFER_PACKETS_Server_LowerBound no matter what maxOutstandingIOPerLUN
+ // reported. We shouldn�t set this value to be smaller than client system.
+ // In other words, the minWorkingSetTransferPackets for server will always between
+ // MIN_WORKINGSET_TRANSFER_PACKETS_Server_LowerBound and MIN_WORKINGSET_TRANSFER_PACKETS_Server_UpperBound
+
+ minWorkingSetTransferPackets =
+ max(MIN_WORKINGSET_TRANSFER_PACKETS_Server_LowerBound,
+ min(MIN_WORKINGSET_TRANSFER_PACKETS_Server_UpperBound,
+ maxOutstandingIOPerLUN));
+
+ maxWorkingSetTransferPackets = max(MAX_WORKINGSET_TRANSFER_PACKETS_Server,
+ 2 * maxOutstandingIOPerLUN);
+ }
+
+
+ fdoData->LocalMinWorkingSetTransferPackets = minWorkingSetTransferPackets;
+ fdoData->LocalMaxWorkingSetTransferPackets = maxWorkingSetTransferPackets;
+
+ //
+ // Allow class driver to override the settings
+ //
+ if (commonExt->DriverExtension->WorkingSet != NULL) {
+ PCLASS_WORKING_SET workingSet = commonExt->DriverExtension->WorkingSet;
+
+ // override only if non-zero
+ if (workingSet->XferPacketsWorkingSetMinimum != 0)
+ {
+ fdoData->LocalMinWorkingSetTransferPackets = workingSet->XferPacketsWorkingSetMinimum;
+ // adjust maximum upwards if needed
+ if (fdoData->LocalMaxWorkingSetTransferPackets < fdoData->LocalMinWorkingSetTransferPackets)
+ {
+ fdoData->LocalMaxWorkingSetTransferPackets = fdoData->LocalMinWorkingSetTransferPackets;
+ }
+ }
+ // override only if non-zero
+ if (workingSet->XferPacketsWorkingSetMaximum != 0)
+ {
+ fdoData->LocalMaxWorkingSetTransferPackets = workingSet->XferPacketsWorkingSetMaximum;
+ // adjust minimum downwards if needed
+ if (fdoData->LocalMinWorkingSetTransferPackets > fdoData->LocalMaxWorkingSetTransferPackets)
+ {
+ fdoData->LocalMinWorkingSetTransferPackets = fdoData->LocalMaxWorkingSetTransferPackets;
+ }
+ }
+ // that's all the adjustments required/allowed
+ } // end working set size special code
+
+ for (index = 0; index < arraySize; index++) {
+ while (fdoData->FreeTransferPacketsLists[index].NumFreeTransferPackets < MIN_INITIAL_TRANSFER_PACKETS){
+ PTRANSFER_PACKET pkt = NewTransferPacket(Fdo);
+ if (pkt) {
+ InterlockedIncrement((volatile LONG *)&(fdoData->FreeTransferPacketsLists[index].NumTotalTransferPackets));
+ pkt->AllocateNode = index;
+ EnqueueFreeTransferPacket(Fdo, pkt);
+ } else {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ break;
+ }
+ }
+ fdoData->FreeTransferPacketsLists[index].DbgPeakNumTransferPackets = fdoData->FreeTransferPacketsLists[index].NumTotalTransferPackets;
+ }
+
+ //
+ // Pre-initialize our SCSI_REQUEST_BLOCK template with all
+ // the constant fields. This will save a little time for each xfer.
+ // NOTE: a CdbLength field of 10 may not always be appropriate
+ //
+
+ if (NT_SUCCESS(status)) {
+ if (fdoExt->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ ULONG ByteSize = 0;
+
+ #if (NTDDI_VERSION >= NTDDI_WINBLUE)
+ if ((fdoExt->MiniportDescriptor != NULL) &&
+ (fdoExt->MiniportDescriptor->Size >= RTL_SIZEOF_THROUGH_FIELD(STORAGE_MINIPORT_DESCRIPTOR, ExtraIoInfoSupported)) &&
+ (fdoExt->MiniportDescriptor->ExtraIoInfoSupported == TRUE)) {
+ status = CreateStorageRequestBlock((PSTORAGE_REQUEST_BLOCK *)&fdoData->SrbTemplate,
+ fdoExt->AdapterDescriptor->AddressType,
+ DefaultStorageRequestBlockAllocateRoutine,
+ &ByteSize,
+ 2,
+ SrbExDataTypeScsiCdb16,
+ SrbExDataTypeIoInfo
+ );
+ } else {
+ status = CreateStorageRequestBlock((PSTORAGE_REQUEST_BLOCK *)&fdoData->SrbTemplate,
+ fdoExt->AdapterDescriptor->AddressType,
+ DefaultStorageRequestBlockAllocateRoutine,
+ &ByteSize,
+ 1,
+ SrbExDataTypeScsiCdb16
+ );
+ }
+ #else
+ status = CreateStorageRequestBlock((PSTORAGE_REQUEST_BLOCK *)&fdoData->SrbTemplate,
+ fdoExt->AdapterDescriptor->AddressType,
+ DefaultStorageRequestBlockAllocateRoutine,
+ &ByteSize,
+ 1,
+ SrbExDataTypeScsiCdb16
+ );
+ #endif
+ if (NT_SUCCESS(status)) {
+ ((PSTORAGE_REQUEST_BLOCK) fdoData->SrbTemplate)->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
+ } else {
+ NT_ASSERT(FALSE);
+ }
+ } else {
+ fdoData->SrbTemplate = ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(SCSI_REQUEST_BLOCK), '-brs');
+ if (fdoData->SrbTemplate == NULL) {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ } else {
+ RtlZeroMemory(fdoData->SrbTemplate, sizeof(SCSI_REQUEST_BLOCK));
+ fdoData->SrbTemplate->Length = sizeof(SCSI_REQUEST_BLOCK);
+ fdoData->SrbTemplate->Function = SRB_FUNCTION_EXECUTE_SCSI;
+ }
+ }
+ }
+
+ if (status == STATUS_SUCCESS) {
+ SrbSetRequestAttribute(fdoData->SrbTemplate, SRB_SIMPLE_TAG_REQUEST);
+ SrbSetSenseInfoBufferLength(fdoData->SrbTemplate, SENSE_BUFFER_SIZE_EX);
+ SrbSetCdbLength(fdoData->SrbTemplate, 10);
+ }
+
+ return status;
+}
+
+
+VOID DestroyAllTransferPackets(PDEVICE_OBJECT Fdo)
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
+ TRANSFER_PACKET *pkt;
+ ULONG index;
+ ULONG arraySize;
+
+ PAGED_CODE();
+
+ //
+ // fdoData->FreeTransferPacketsLists could be NULL if
+ // there was an error during start device.
+ //
+ if (fdoData->FreeTransferPacketsLists != NULL) {
+
+ NT_ASSERT(IsListEmpty(&fdoData->DeferredClientIrpList));
+
+ arraySize = KeQueryHighestNodeNumber() + 1;
+ for (index = 0; index < arraySize; index++) {
+ pkt = DequeueFreeTransferPacketEx(Fdo, FALSE, index);
+ while (pkt) {
+ DestroyTransferPacket(pkt);
+ InterlockedDecrement((volatile LONG *)&(fdoData->FreeTransferPacketsLists[index].NumTotalTransferPackets));
+ pkt = DequeueFreeTransferPacketEx(Fdo, FALSE, index);
+ }
+
+ NT_ASSERT(fdoData->FreeTransferPacketsLists[index].NumTotalTransferPackets == 0);
+ }
+ }
+
+ FREE_POOL(fdoData->SrbTemplate);
+}
+
+__drv_allocatesMem(Mem)
+#pragma warning(suppress:28195) // This function may not allocate memory in some error cases.
+PTRANSFER_PACKET NewTransferPacket(PDEVICE_OBJECT Fdo)
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
+ PTRANSFER_PACKET newPkt = NULL;
+ ULONG transferLength = (ULONG)-1;
+ NTSTATUS status = STATUS_SUCCESS;
+
+ if (NT_SUCCESS(status)) {
+ status = RtlULongAdd(fdoData->HwMaxXferLen, PAGE_SIZE, &transferLength);
+ if (!NT_SUCCESS(status)) {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_RW, "Integer overflow in calculating transfer packet size."));
+ status = STATUS_INTEGER_OVERFLOW;
+ }
+ }
+
+ /*
+ * Allocate the actual packet.
+ */
+ if (NT_SUCCESS(status)) {
+ newPkt = ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(TRANSFER_PACKET), 'pnPC');
+ if (newPkt == NULL) {
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_RW, "Failed to allocate transfer packet."));
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ } else {
+ RtlZeroMemory(newPkt, sizeof(TRANSFER_PACKET));
+ newPkt->AllocateNode = KeGetCurrentNodeNumber();
+ if (fdoExt->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+#if (NTDDI_VERSION >= NTDDI_WINBLUE)
+ if ((fdoExt->MiniportDescriptor != NULL) &&
+ (fdoExt->MiniportDescriptor->Size >= RTL_SIZEOF_THROUGH_FIELD(STORAGE_MINIPORT_DESCRIPTOR, ExtraIoInfoSupported)) &&
+ (fdoExt->MiniportDescriptor->ExtraIoInfoSupported == TRUE)) {
+ status = CreateStorageRequestBlock((PSTORAGE_REQUEST_BLOCK *)&newPkt->Srb,
+ fdoExt->AdapterDescriptor->AddressType,
+ DefaultStorageRequestBlockAllocateRoutine,
+ NULL,
+ 2,
+ SrbExDataTypeScsiCdb16,
+ SrbExDataTypeIoInfo
+ );
+ } else {
+ status = CreateStorageRequestBlock((PSTORAGE_REQUEST_BLOCK *)&newPkt->Srb,
+ fdoExt->AdapterDescriptor->AddressType,
+ DefaultStorageRequestBlockAllocateRoutine,
+ NULL,
+ 1,
+ SrbExDataTypeScsiCdb16
+ );
+ }
+#else
+ status = CreateStorageRequestBlock((PSTORAGE_REQUEST_BLOCK *)&newPkt->Srb,
+ fdoExt->AdapterDescriptor->AddressType,
+ DefaultStorageRequestBlockAllocateRoutine,
+ NULL,
+ 1,
+ SrbExDataTypeScsiCdb16
+ );
+#endif
+ } else {
+#pragma prefast(suppress:6014, "The allocated memory that Pkt->Srb points to will be freed in DestroyTransferPacket().")
+ newPkt->Srb = ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(SCSI_REQUEST_BLOCK), '-brs');
+ if (newPkt->Srb == NULL) {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ }
+
+ if (status != STATUS_SUCCESS)
+ {
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_RW, "Failed to allocate SRB."));
+ FREE_POOL(newPkt);
+ }
+ }
+ }
+
+ /*
+ * Allocate Irp for the packet.
+ */
+ if (NT_SUCCESS(status) && newPkt != NULL) {
+ newPkt->Irp = IoAllocateIrp(Fdo->StackSize, FALSE);
+ if (newPkt->Irp == NULL) {
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_RW, "Failed to allocate IRP for transfer packet."));
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+ }
+
+ /*
+ * Allocate a MDL. Add one page to the length to insure an extra page
+ * entry is allocated if the buffer does not start on page boundaries.
+ */
+ if (NT_SUCCESS(status) && newPkt != NULL) {
+
+ NT_ASSERT(transferLength != (ULONG)-1);
+
+ newPkt->PartialMdl = IoAllocateMdl(NULL,
+ transferLength,
+ FALSE,
+ FALSE,
+ NULL);
+ if (newPkt->PartialMdl == NULL) {
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_RW, "Failed to allocate MDL for transfer packet."));
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ } else {
+ NT_ASSERT(newPkt->PartialMdl->Size >= (CSHORT)(sizeof(MDL) + BYTES_TO_PAGES(fdoData->HwMaxXferLen) * sizeof(PFN_NUMBER)));
+ }
+
+ }
+
+ /*
+ * Allocate per-packet retry history, if required
+ */
+ if (NT_SUCCESS(status) &&
+ (fdoData->InterpretSenseInfo != NULL) &&
+ (newPkt != NULL)
+ ) {
+ // attempt to allocate also the history
+ ULONG historyByteCount = 0;
+
+ // SAL annotation and ClassInitializeEx() should both catch this case
+ NT_ASSERT(fdoData->InterpretSenseInfo->HistoryCount != 0);
+ _Analysis_assume_(fdoData->InterpretSenseInfo->HistoryCount != 0);
+
+ historyByteCount = sizeof(SRB_HISTORY_ITEM) * fdoData->InterpretSenseInfo->HistoryCount;
+ historyByteCount += sizeof(SRB_HISTORY) - sizeof(SRB_HISTORY_ITEM);
+
+ newPkt->RetryHistory = (PSRB_HISTORY)ExAllocatePoolWithTag(NonPagedPoolNx, historyByteCount, 'hrPC');
+
+ if (newPkt->RetryHistory == NULL) {
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_RW, "Failed to allocate MDL for transfer packet."));
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ } else {
+ // call this routine directly once since it's the first initialization of
+ // the structure and the internal maximum count field is not yet setup.
+ HistoryInitializeRetryLogs(newPkt->RetryHistory, fdoData->InterpretSenseInfo->HistoryCount);
+ }
+ }
+
+ /*
+ * Enqueue the packet in our static AllTransferPacketsList
+ * (just so we can find it during debugging if its stuck somewhere).
+ */
+ if (NT_SUCCESS(status) && newPkt != NULL)
+ {
+ KIRQL oldIrql;
+ newPkt->Fdo = Fdo;
+#if DBG
+ newPkt->DbgPktId = InterlockedIncrement((volatile LONG *)&fdoData->DbgMaxPktId);
+#endif
+ KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
+ InsertTailList(&fdoData->AllTransferPacketsList, &newPkt->AllPktsListEntry);
+ KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
+
+ } else {
+ // free any resources acquired above (in reverse order)
+ if (newPkt != NULL) {
+ FREE_POOL(newPkt->RetryHistory);
+ if (newPkt->PartialMdl != NULL) { IoFreeMdl(newPkt->PartialMdl); }
+ if (newPkt->Irp != NULL) { IoFreeIrp(newPkt->Irp); }
+ if (newPkt->Srb != NULL) { FREE_POOL(newPkt->Srb); }
+ FREE_POOL(newPkt);
+ }
+ }
+
+ return newPkt;
+}
+
+
+/*
+ * DestroyTransferPacket
+ *
+ */
+VOID DestroyTransferPacket(_In_ __drv_freesMem(mem) PTRANSFER_PACKET Pkt)
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Pkt->Fdo->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
+ KIRQL oldIrql;
+
+ NT_ASSERT(!Pkt->SlistEntry.Next);
+// NT_ASSERT(!Pkt->OriginalIrp);
+
+ KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
+
+ /*
+ * Delete the packet from our all-packets queue.
+ */
+ NT_ASSERT(!IsListEmpty(&Pkt->AllPktsListEntry));
+ NT_ASSERT(!IsListEmpty(&fdoData->AllTransferPacketsList));
+ RemoveEntryList(&Pkt->AllPktsListEntry);
+ InitializeListHead(&Pkt->AllPktsListEntry);
+
+ KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
+
+ IoFreeMdl(Pkt->PartialMdl);
+ IoFreeIrp(Pkt->Irp);
+ FREE_POOL(Pkt->RetryHistory);
+ FREE_POOL(Pkt->Srb);
+ FREE_POOL(Pkt);
+}
+
+
+VOID EnqueueFreeTransferPacket(PDEVICE_OBJECT Fdo, __drv_aliasesMem PTRANSFER_PACKET Pkt)
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
+ ULONG allocateNode;
+ KIRQL oldIrql;
+
+ NT_ASSERT(!Pkt->SlistEntry.Next);
+
+ allocateNode = Pkt->AllocateNode;
+ InterlockedPushEntrySList(&(fdoData->FreeTransferPacketsLists[allocateNode].SListHeader), &Pkt->SlistEntry);
+ InterlockedIncrement((volatile LONG *)&(fdoData->FreeTransferPacketsLists[allocateNode].NumFreeTransferPackets));
+
+ /*
+ * If the total number of packets is larger than LocalMinWorkingSetTransferPackets,
+ * that means that we've been in stress. If all those packets are now
+ * free, then we are now out of stress and can free the extra packets.
+ * Attempt to free down to LocalMaxWorkingSetTransferPackets immediately, and
+ * down to LocalMinWorkingSetTransferPackets lazily (one at a time).
+ * However, since we're at DPC, do this is a work item. If the device is removed
+ * or we are unable to allocate the work item, do NOT free more than
+ * MAX_CLEANUP_TRANSFER_PACKETS_AT_ONCE. Subsequent IO completions will end up freeing
+ * up the rest, even if it is MAX_CLEANUP_TRANSFER_PACKETS_AT_ONCE at a time.
+ */
+ if (fdoData->FreeTransferPacketsLists[allocateNode].NumFreeTransferPackets >=
+ fdoData->FreeTransferPacketsLists[allocateNode].NumTotalTransferPackets) {
+
+ /*
+ * 1. Immediately snap down to our UPPER threshold.
+ */
+ if (fdoData->FreeTransferPacketsLists[allocateNode].NumTotalTransferPackets >
+ fdoData->LocalMaxWorkingSetTransferPackets) {
+
+ ULONG isRemoved;
+ PIO_WORKITEM workItem = NULL;
+
+ workItem = IoAllocateWorkItem(Fdo);
+
+ //
+ // Acquire a remove lock in order to make sure the device object and its
+ // private data structures will exist when the workitem fires.
+ // The remove lock will be released by the workitem (CleanupTransferPacketToWorkingSetSize).
+ //
+ isRemoved = ClassAcquireRemoveLock(Fdo, (PIRP)workItem);
+
+ if (workItem && !isRemoved) {
+
+ TracePrint((TRACE_LEVEL_INFORMATION,
+ TRACE_FLAG_GENERAL,
+ "EnqueueFreeTransferPacket: Device (%p), queuing work item to clean up free transfer packets.\n",
+ Fdo));
+
+ //
+ // Queue a work item to trim down the total number of transfer packets to with the
+ // working size.
+ //
+ IoQueueWorkItemEx(workItem, CleanupTransferPacketToWorkingSetSizeWorker, DelayedWorkQueue, (PVOID) allocateNode);
+
+ } else {
+
+ if (workItem) {
+ IoFreeWorkItem(workItem);
+ }
+
+ if (isRemoved != REMOVE_COMPLETE) {
+ ClassReleaseRemoveLock(Fdo, (PIRP)workItem);
+ }
+
+ TracePrint((TRACE_LEVEL_ERROR,
+ TRACE_FLAG_GENERAL,
+ "EnqueueFreeTransferPacket: Device (%p), Failed to allocate memory for the work item.\n",
+ Fdo));
+
+ CleanupTransferPacketToWorkingSetSize(Fdo, TRUE, allocateNode);
+ }
+ }
+
+ /*
+ * 2. Lazily work down to our LOWER threshold (by only freeing one packet at a time).
+ */
+ if (fdoData->FreeTransferPacketsLists[allocateNode].NumTotalTransferPackets >
+ fdoData->LocalMinWorkingSetTransferPackets){
+ /*
+ * Check the counter again with lock held. This eliminates a race condition
+ * while still allowing us to not grab the spinlock in the common codepath.
+ *
+ * Note that the spinlock does not synchronize with threads dequeuing free
+ * packets to send (DequeueFreeTransferPacket does that with a lightweight
+ * interlocked exchange); the spinlock prevents multiple threads in this function
+ * from deciding to free too many extra packets at once.
+ */
+ PTRANSFER_PACKET pktToDelete = NULL;
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_RW, "Exiting stress, lazily freeing one of %d/%d packets from node %d.",
+ fdoData->FreeTransferPacketsLists[allocateNode].NumTotalTransferPackets,
+ fdoData->LocalMinWorkingSetTransferPackets,
+ allocateNode));
+
+ KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
+ if ((fdoData->FreeTransferPacketsLists[allocateNode].NumFreeTransferPackets >=
+ fdoData->FreeTransferPacketsLists[allocateNode].NumTotalTransferPackets) &&
+ (fdoData->FreeTransferPacketsLists[allocateNode].NumTotalTransferPackets >
+ fdoData->LocalMinWorkingSetTransferPackets)){
+
+ pktToDelete = DequeueFreeTransferPacketEx(Fdo, FALSE, allocateNode);
+ if (pktToDelete) {
+ InterlockedDecrement((volatile LONG *)&(fdoData->FreeTransferPacketsLists[allocateNode].NumTotalTransferPackets));
+ } else {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_RW,
+ "Extremely unlikely condition (non-fatal): %d packets dequeued at once for Fdo %p. NumTotalTransferPackets=%d (2). Node=%d",
+ fdoData->LocalMinWorkingSetTransferPackets,
+ Fdo,
+ fdoData->FreeTransferPacketsLists[allocateNode].NumTotalTransferPackets,
+ allocateNode));
+ }
+ }
+ KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
+
+ if (pktToDelete) {
+ DestroyTransferPacket(pktToDelete);
+ }
+ }
+
+ }
+
+}
+
+PTRANSFER_PACKET DequeueFreeTransferPacket(PDEVICE_OBJECT Fdo, BOOLEAN AllocIfNeeded)
+{
+ return DequeueFreeTransferPacketEx(Fdo, AllocIfNeeded, KeGetCurrentNodeNumber());
+}
+
+PTRANSFER_PACKET DequeueFreeTransferPacketEx(
+ _In_ PDEVICE_OBJECT Fdo,
+ _In_ BOOLEAN AllocIfNeeded,
+ _In_ ULONG Node)
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
+ PTRANSFER_PACKET pkt;
+ PSLIST_ENTRY slistEntry;
+
+ slistEntry = InterlockedPopEntrySList(&(fdoData->FreeTransferPacketsLists[Node].SListHeader));
+
+ if (slistEntry) {
+ slistEntry->Next = NULL;
+ pkt = CONTAINING_RECORD(slistEntry, TRANSFER_PACKET, SlistEntry);
+ InterlockedDecrement((volatile LONG *)&(fdoData->FreeTransferPacketsLists[Node].NumFreeTransferPackets));
+
+ // when dequeuing the packet, also reset the history data
+ HISTORYINITIALIZERETRYLOGS(pkt);
+
+ } else {
+ if (AllocIfNeeded) {
+ /*
+ * We are in stress and have run out of lookaside packets.
+ * In order to service the current transfer,
+ * allocate an extra packet.
+ * We will free it lazily when we are out of stress.
+ */
+ pkt = NewTransferPacket(Fdo);
+ if (pkt) {
+ InterlockedIncrement((volatile LONG *)&fdoData->FreeTransferPacketsLists[Node].NumTotalTransferPackets);
+ fdoData->FreeTransferPacketsLists[Node].DbgPeakNumTransferPackets =
+ max(fdoData->FreeTransferPacketsLists[Node].DbgPeakNumTransferPackets,
+ fdoData->FreeTransferPacketsLists[Node].NumTotalTransferPackets);
+ } else {
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_RW, "DequeueFreeTransferPacket: packet allocation failed"));
+ }
+ } else {
+ pkt = NULL;
+ }
+ }
+
+ return pkt;
+}
+
+
+/*
+ * SetupReadWriteTransferPacket
+ *
+ * This function is called once to set up the first attempt to send a packet.
+ * It is not called before a retry, as SRB fields may be modified for the retry.
+ *
+ * Set up the Srb of the TRANSFER_PACKET for the transfer.
+ * The Irp is set up in SubmitTransferPacket because it must be reset
+ * for each packet submission.
+ */
+VOID SetupReadWriteTransferPacket( PTRANSFER_PACKET Pkt,
+ PVOID Buf,
+ ULONG Len,
+ LARGE_INTEGER DiskLocation,
+ PIRP OriginalIrp)
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Pkt->Fdo->DeviceExtension;
+ PCOMMON_DEVICE_EXTENSION commonExtension = Pkt->Fdo->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
+ PIO_STACK_LOCATION origCurSp = IoGetCurrentIrpStackLocation(OriginalIrp);
+ UCHAR majorFunc = origCurSp->MajorFunction;
+ LARGE_INTEGER logicalBlockAddr;
+ ULONG numTransferBlocks;
+ PCDB pCdb;
+ ULONG srbLength;
+ ULONG timeoutValue = fdoExt->TimeOutValue;
+
+ logicalBlockAddr.QuadPart = Int64ShrlMod32(DiskLocation.QuadPart, fdoExt->SectorShift);
+ numTransferBlocks = Len >> fdoExt->SectorShift;
+
+ /*
+ * This field is useful when debugging, since low-memory conditions are
+ * handled differently for CDROM (which is the only driver using StartIO)
+ */
+ Pkt->DriverUsesStartIO = (commonExtension->DriverExtension->InitData.ClassStartIo != NULL);
+
+ /*
+ * Slap the constant SRB fields in from our pre-initialized template.
+ * We'll then only have to fill in the unique fields for this transfer.
+ * Tell lower drivers to sort the SRBs by the logical block address
+ * so that disk seeks are minimized.
+ */
+ if (fdoExt->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ srbLength = ((PSTORAGE_REQUEST_BLOCK) fdoData->SrbTemplate)->SrbLength;
+ NT_ASSERT(((PSTORAGE_REQUEST_BLOCK) Pkt->Srb)->SrbLength >= srbLength);
+ } else {
+ srbLength = fdoData->SrbTemplate->Length;
+ }
+ RtlCopyMemory(Pkt->Srb, fdoData->SrbTemplate, srbLength); // copies _contents_ of SRB blocks
+ SrbSetDataBuffer(Pkt->Srb, Buf);
+ SrbSetDataTransferLength(Pkt->Srb, Len);
+ SrbSetQueueSortKey(Pkt->Srb, logicalBlockAddr.LowPart);
+ if (logicalBlockAddr.QuadPart > 0xFFFFFFFF) {
+ //
+ // If the requested LBA is more than max ULONG set the
+ // QueueSortKey to the maximum value, so that these
+ // requests can be added towards the end of the queue.
+ //
+
+ SrbSetQueueSortKey(Pkt->Srb, 0xFFFFFFFF);
+ }
+ SrbSetOriginalRequest(Pkt->Srb, Pkt->Irp);
+ SrbSetSenseInfoBuffer(Pkt->Srb, &Pkt->SrbErrorSenseData);
+
+
+ SrbSetTimeOutValue(Pkt->Srb, timeoutValue);
+
+ /*
+ * Arrange values in CDB in big-endian format.
+ */
+ pCdb = SrbGetCdb(Pkt->Srb);
+ if (pCdb) {
+ if (TEST_FLAG(fdoExt->DeviceFlags, DEV_USE_16BYTE_CDB)) {
+ REVERSE_BYTES_QUAD(&pCdb->CDB16.LogicalBlock, &logicalBlockAddr);
+ REVERSE_BYTES(&pCdb->CDB16.TransferLength, &numTransferBlocks);
+ pCdb->CDB16.OperationCode = (majorFunc==IRP_MJ_READ) ? SCSIOP_READ16 : SCSIOP_WRITE16;
+ SrbSetCdbLength(Pkt->Srb, 16);
+ } else {
+ pCdb->CDB10.LogicalBlockByte0 = ((PFOUR_BYTE)&logicalBlockAddr.LowPart)->Byte3;
+ pCdb->CDB10.LogicalBlockByte1 = ((PFOUR_BYTE)&logicalBlockAddr.LowPart)->Byte2;
+ pCdb->CDB10.LogicalBlockByte2 = ((PFOUR_BYTE)&logicalBlockAddr.LowPart)->Byte1;
+ pCdb->CDB10.LogicalBlockByte3 = ((PFOUR_BYTE)&logicalBlockAddr.LowPart)->Byte0;
+ pCdb->CDB10.TransferBlocksMsb = ((PFOUR_BYTE)&numTransferBlocks)->Byte1;
+ pCdb->CDB10.TransferBlocksLsb = ((PFOUR_BYTE)&numTransferBlocks)->Byte0;
+ pCdb->CDB10.OperationCode = (majorFunc==IRP_MJ_READ) ? SCSIOP_READ : SCSIOP_WRITE;
+ }
+ }
+
+ /*
+ * Set SRB and IRP flags
+ */
+ SrbAssignSrbFlags(Pkt->Srb, fdoExt->SrbFlags);
+ if (TEST_FLAG(OriginalIrp->Flags, IRP_PAGING_IO) ||
+ TEST_FLAG(OriginalIrp->Flags, IRP_SYNCHRONOUS_PAGING_IO)){
+ SrbSetSrbFlags(Pkt->Srb, SRB_CLASS_FLAGS_PAGING);
+ }
+ SrbSetSrbFlags(Pkt->Srb, (majorFunc==IRP_MJ_READ) ? SRB_FLAGS_DATA_IN : SRB_FLAGS_DATA_OUT);
+
+ /*
+ * Allow caching only if this is not a write-through request.
+ * If write-through and caching is enabled on the device, force
+ * media access.
+ * Ignore SL_WRITE_THROUGH for reads; it's only set because the file handle was opened with WRITE_THROUGH.
+ */
+ if ((majorFunc == IRP_MJ_WRITE) && TEST_FLAG(origCurSp->Flags, SL_WRITE_THROUGH) && pCdb) {
+ pCdb->CDB10.ForceUnitAccess = fdoExt->CdbForceUnitAccess;
+ } else {
+ SrbSetSrbFlags(Pkt->Srb, SRB_FLAGS_ADAPTER_CACHE_ENABLE);
+ }
+
+ /*
+ * Remember the buf and len in the SRB because miniports
+ * can overwrite SRB.DataTransferLength and we may need it again
+ * for the retry.
+ */
+ Pkt->BufPtrCopy = Buf;
+ Pkt->BufLenCopy = Len;
+ Pkt->TargetLocationCopy = DiskLocation;
+
+ Pkt->OriginalIrp = OriginalIrp;
+ Pkt->NumRetries = fdoData->MaxNumberOfIoRetries;
+ Pkt->SyncEventPtr = NULL;
+ Pkt->CompleteOriginalIrpWhenLastPacketCompletes = TRUE;
+
+
+ if (pCdb) {
+ DBGLOGFLUSHINFO(fdoData, TRUE, (BOOLEAN)(pCdb->CDB10.ForceUnitAccess), FALSE);
+ } else {
+ DBGLOGFLUSHINFO(fdoData, TRUE, FALSE, FALSE);
+ }
+}
+
+
+/*
+ * SubmitTransferPacket
+ *
+ * Set up the IRP for the TRANSFER_PACKET submission and send it down.
+ */
+NTSTATUS SubmitTransferPacket(PTRANSFER_PACKET Pkt)
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = Pkt->Fdo->DeviceExtension;
+ PDEVICE_OBJECT nextDevObj = commonExtension->LowerDeviceObject;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Pkt->Fdo->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
+ BOOLEAN idleRequest = FALSE;
+ PIO_STACK_LOCATION nextSp;
+
+ NT_ASSERT(Pkt->Irp->CurrentLocation == Pkt->Irp->StackCount+1);
+
+ /*
+ * Attach the SRB to the IRP.
+ * The reused IRP's stack location has to be rewritten for each retry
+ * call because IoCompleteRequest clears the stack locations.
+ */
+ IoReuseIrp(Pkt->Irp, STATUS_NOT_SUPPORTED);
+
+
+ nextSp = IoGetNextIrpStackLocation(Pkt->Irp);
+ nextSp->MajorFunction = IRP_MJ_SCSI;
+ nextSp->Parameters.Scsi.Srb = (PSCSI_REQUEST_BLOCK)Pkt->Srb;
+
+ SrbSetScsiStatus(Pkt->Srb, 0);
+ Pkt->Srb->SrbStatus = 0;
+ SrbSetSenseInfoBufferLength(Pkt->Srb, SENSE_BUFFER_SIZE_EX);
+
+ if (Pkt->CompleteOriginalIrpWhenLastPacketCompletes) {
+ /*
+ * Only dereference the "original IRP"'s stack location
+ * if its a real client irp (as opposed to a static irp
+ * we're using just for result status for one of the non-IO scsi commands).
+ *
+ * For read/write, propagate the storage-specific IRP stack location flags
+ * (e.g. SL_OVERRIDE_VERIFY_VOLUME, SL_WRITE_THROUGH).
+ */
+ PIO_STACK_LOCATION origCurSp = IoGetCurrentIrpStackLocation(Pkt->OriginalIrp);
+ nextSp->Flags = origCurSp->Flags;
+ }
+
+ //
+ // If the request is not split, we can use the original IRP MDL. If the
+ // request needs to be split, we need to use a partial MDL. The partial MDL
+ // is needed because more than one driver might be mapping the same MDL
+ // and this causes problems.
+ //
+ if (Pkt->UsePartialMdl == FALSE) {
+ Pkt->Irp->MdlAddress = Pkt->OriginalIrp->MdlAddress;
+ } else {
+ IoBuildPartialMdl(Pkt->OriginalIrp->MdlAddress, Pkt->PartialMdl, SrbGetDataBuffer(Pkt->Srb), SrbGetDataTransferLength(Pkt->Srb));
+ Pkt->Irp->MdlAddress = Pkt->PartialMdl;
+ }
+
+
+ DBGLOGSENDPACKET(Pkt);
+ HISTORYLOGSENDPACKET(Pkt);
+
+ //
+ // Set the original irp here for SFIO.
+ //
+ ClasspSrbSetOriginalIrp(Pkt->Srb, (PVOID) (Pkt->OriginalIrp));
+
+ //
+ // No need to lock for IdlePrioritySupported, since it will
+ // be modified only at initialization time.
+ //
+ if (fdoData->IdlePrioritySupported == TRUE) {
+ idleRequest = ClasspIsIdleRequest(Pkt->OriginalIrp);
+ if (idleRequest) {
+ InterlockedIncrement(&fdoData->ActiveIdleIoCount);
+ } else {
+ InterlockedIncrement(&fdoData->ActiveIoCount);
+ }
+ }
+
+ IoSetCompletionRoutine(Pkt->Irp, TransferPktComplete, Pkt, TRUE, TRUE, TRUE);
+ return IoCallDriver(nextDevObj, Pkt->Irp);
+}
+
+
+NTSTATUS TransferPktComplete(IN PDEVICE_OBJECT NullFdo, IN PIRP Irp, IN PVOID Context)
+{
+ PTRANSFER_PACKET pkt = (PTRANSFER_PACKET)Context;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt = pkt->Fdo->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
+ BOOLEAN packetDone = FALSE;
+ BOOLEAN idleRequest = FALSE;
+ ULONG transferLength;
+
+ UNREFERENCED_PARAMETER(NullFdo);
+
+ /*
+ * Put all the assertions and spew in here so we don't have to look at them.
+ */
+ DBGLOGRETURNPACKET(pkt);
+ DBGCHECKRETURNEDPKT(pkt);
+ HISTORYLOGRETURNEDPACKET(pkt);
+
+
+ if (fdoData->IdlePrioritySupported == TRUE) {
+ idleRequest = ClasspIsIdleRequest(pkt->OriginalIrp);
+ if (idleRequest) {
+ InterlockedDecrement(&fdoData->ActiveIdleIoCount);
+ NT_ASSERT(fdoData->ActiveIdleIoCount >= 0);
+ } else {
+ fdoData->LastIoTime = ClasspGetCurrentTime(NULL);
+ fdoData->IdleTicks = 0;
+ InterlockedDecrement(&fdoData->ActiveIoCount);
+ NT_ASSERT(fdoData->ActiveIoCount >= 0);
+ }
+ }
+
+ //
+ // If partial MDL was used, unmap the pages. When the packet is retried, the
+ // MDL will be recreated. If the packet is done, the MDL will be ready to be reused.
+ //
+ if (pkt->UsePartialMdl) {
+ MmPrepareMdlForReuse(pkt->PartialMdl);
+ }
+
+ if (SRB_STATUS(pkt->Srb->SrbStatus) == SRB_STATUS_SUCCESS) {
+
+ NT_ASSERT(NT_SUCCESS(Irp->IoStatus.Status));
+
+ transferLength = SrbGetDataTransferLength(pkt->Srb);
+
+ fdoData->LoggedTURFailureSinceLastIO = FALSE;
+
+ /*
+ * The port driver should not have allocated a sense buffer
+ * if the SRB succeeded.
+ */
+ NT_ASSERT(!PORT_ALLOCATED_SENSE_EX(fdoExt, pkt->Srb));
+
+ /*
+ * Add this packet's transferred length to the original IRP's.
+ */
+ InterlockedExchangeAdd((PLONG)&pkt->OriginalIrp->IoStatus.Information,
+ (LONG)transferLength);
+
+ if ((pkt->InLowMemRetry) ||
+ (pkt->DriverUsesStartIO && pkt->LowMemRetry_remainingBufLen > 0)) {
+ packetDone = StepLowMemRetry(pkt);
+ } else {
+ packetDone = TRUE;
+ }
+
+ }
+ else {
+ /*
+ * The packet failed. We may retry it if possible.
+ */
+ BOOLEAN shouldRetry;
+
+ /*
+ * Make sure IRP status matches SRB error status (since we propagate it).
+ */
+ if (NT_SUCCESS(Irp->IoStatus.Status)){
+ Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
+ }
+
+ /*
+ * The packet failed.
+ * So when sending the packet down we either saw either an error or STATUS_PENDING,
+ * and so we returned STATUS_PENDING for the original IRP.
+ * So now we must mark the original irp pending to match that, _regardless_ of
+ * whether we actually switch threads here by retrying.
+ * (We also have to mark the irp pending if the lower driver marked the irp pending;
+ * that is dealt with farther down).
+ */
+ if (pkt->CompleteOriginalIrpWhenLastPacketCompletes){
+ IoMarkIrpPending(pkt->OriginalIrp);
+ }
+
+ /*
+ * Interpret the SRB error (to a meaningful IRP status)
+ * and determine if we should retry this packet.
+ * This call looks at the returned SENSE info to figure out what to do.
+ */
+ shouldRetry = InterpretTransferPacketError(pkt);
+
+ /*
+ * If the SRB queue is locked-up, release it.
+ * Do this after calling the error handler.
+ */
+ if (pkt->Srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN){
+ ClassReleaseQueue(pkt->Fdo);
+ }
+
+ if (NT_SUCCESS(Irp->IoStatus.Status)){
+ /*
+ * The error was recovered above in the InterpretTransferPacketError() call.
+ */
+
+ NT_ASSERT(!shouldRetry);
+
+ /*
+ * In the case of a recovered error,
+ * add the transfer length to the original Irp as we would in the success case.
+ */
+ InterlockedExchangeAdd((PLONG)&pkt->OriginalIrp->IoStatus.Information,
+ (LONG)SrbGetDataTransferLength(pkt->Srb));
+
+ if ((pkt->InLowMemRetry) ||
+ (pkt->DriverUsesStartIO && pkt->LowMemRetry_remainingBufLen > 0)) {
+ packetDone = StepLowMemRetry(pkt);
+ } else {
+ packetDone = TRUE;
+ }
+ } else {
+ if (shouldRetry && (pkt->NumRetries > 0)){
+ packetDone = RetryTransferPacket(pkt);
+ } else if (shouldRetry && (pkt->RetryHistory != NULL)){
+ // don't limit retries if class driver has custom interpretation routines
+ packetDone = RetryTransferPacket(pkt);
+ } else {
+ packetDone = TRUE;
+ }
+ }
+ }
+
+ /*
+ * If the packet is completed, put it back in the free list.
+ * If it is the last packet servicing the original request, complete the original irp.
+ */
+ if (packetDone){
+ LONG numPacketsRemaining;
+ PIRP deferredIrp;
+ PDEVICE_OBJECT Fdo = pkt->Fdo;
+ UCHAR uniqueAddr = 0;
+
+ /*
+ * In case a remove is pending, bump the lock count so we don't get freed
+ * right after we complete the original irp.
+ */
+ ClassAcquireRemoveLock(Fdo, (PVOID)&uniqueAddr);
+
+
+ /*
+ * Sometimes the port driver can allocates a new 'sense' buffer
+ * to report transfer errors, e.g. when the default sense buffer
+ * is too small. If so, it is up to us to free it.
+ * Now that we're done using the sense info, free it if appropriate.
+ * Then clear the sense buffer so it doesn't pollute future errors returned in this packet.
+ */
+ if (PORT_ALLOCATED_SENSE_EX(fdoExt, pkt->Srb)) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_RW, "Freeing port-allocated sense buffer for pkt %ph.", pkt));
+ FREE_PORT_ALLOCATED_SENSE_BUFFER_EX(fdoExt, pkt->Srb);
+ SrbSetSenseInfoBuffer(pkt->Srb, &pkt->SrbErrorSenseData);
+ SrbSetSenseInfoBufferLength(pkt->Srb, sizeof(pkt->SrbErrorSenseData));
+ } else {
+ NT_ASSERT(SrbGetSenseInfoBuffer(pkt->Srb) == &pkt->SrbErrorSenseData);
+ NT_ASSERT(SrbGetSenseInfoBufferLength(pkt->Srb) <= sizeof(pkt->SrbErrorSenseData));
+ }
+
+ RtlZeroMemory(&pkt->SrbErrorSenseData, sizeof(pkt->SrbErrorSenseData));
+
+ /*
+ * Call IoSetMasterIrpStatus to set appropriate status
+ * for the Master IRP.
+ */
+ IoSetMasterIrpStatus(pkt->OriginalIrp, Irp->IoStatus.Status);
+
+ if (!NT_SUCCESS(Irp->IoStatus.Status)){
+ /*
+ * If the original I/O originated in user space (i.e. it is thread-queued),
+ * and the error is user-correctable (e.g. media is missing, for removable media),
+ * alert the user.
+ * Since this is only one of possibly several packets completing for the original IRP,
+ * we may do this more than once for a single request. That's ok; this allows
+ * us to test each returned status with IoIsErrorUserInduced().
+ */
+ if (IoIsErrorUserInduced(Irp->IoStatus.Status) &&
+ pkt->CompleteOriginalIrpWhenLastPacketCompletes &&
+ pkt->OriginalIrp->Tail.Overlay.Thread){
+
+ IoSetHardErrorOrVerifyDevice(pkt->OriginalIrp, Fdo);
+ }
+ }
+
+ /*
+ * We use a field in the original IRP to count
+ * down the transfer pieces as they complete.
+ */
+ numPacketsRemaining = InterlockedDecrement(
+ (PLONG)&pkt->OriginalIrp->Tail.Overlay.DriverContext[0]);
+
+ if (numPacketsRemaining > 0){
+ /*
+ * More transfer pieces remain for the original request.
+ * Wait for them to complete before completing the original irp.
+ */
+ } else {
+
+ /*
+ * All the transfer pieces are done.
+ * Complete the original irp if appropriate.
+ */
+ NT_ASSERT(numPacketsRemaining == 0);
+ if (pkt->CompleteOriginalIrpWhenLastPacketCompletes){
+
+ IO_PAGING_PRIORITY priority = (TEST_FLAG(pkt->OriginalIrp->Flags, IRP_PAGING_IO)) ? IoGetPagingIoPriority(pkt->OriginalIrp) : IoPagingPriorityInvalid;
+ KIRQL oldIrql;
+
+ if (NT_SUCCESS(pkt->OriginalIrp->IoStatus.Status)){
+ NT_ASSERT((ULONG)pkt->OriginalIrp->IoStatus.Information == IoGetCurrentIrpStackLocation(pkt->OriginalIrp)->Parameters.Read.Length);
+ ClasspPerfIncrementSuccessfulIo(fdoExt);
+ }
+ ClassReleaseRemoveLock(Fdo, pkt->OriginalIrp);
+
+ /*
+ * We submitted all the downward irps, including this last one, on the thread
+ * that the OriginalIrp came in on. So the OriginalIrp is completing on a
+ * different thread iff this last downward irp is completing on a different thread.
+ * If BlkCache is loaded, for example, it will often complete
+ * requests out of the cache on the same thread, therefore not marking the downward
+ * irp pending and not requiring us to do so here. If the downward request is completing
+ * on the same thread, then by not marking the OriginalIrp pending we can save an APC
+ * and get extra perf benefit out of BlkCache.
+ * Note that if the packet ever cycled due to retry or LowMemRetry,
+ * we set the pending bit in those codepaths.
+ */
+ if (pkt->Irp->PendingReturned){
+ IoMarkIrpPending(pkt->OriginalIrp);
+ }
+
+
+ ClassCompleteRequest(Fdo, pkt->OriginalIrp, IO_DISK_INCREMENT);
+
+ //
+ // Drop the count only after completing the request, to give
+ // Mm some amount of time to issue its next critical request
+ //
+
+ if (priority == IoPagingPriorityHigh)
+ {
+ KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
+
+ if (fdoData->MaxInterleavedNormalIo < ClassMaxInterleavePerCriticalIo)
+ {
+ fdoData->MaxInterleavedNormalIo = 0;
+ } else {
+ fdoData->MaxInterleavedNormalIo -= ClassMaxInterleavePerCriticalIo;
+ }
+
+ fdoData->NumHighPriorityPagingIo--;
+
+ if (fdoData->NumHighPriorityPagingIo == 0)
+ {
+ LARGE_INTEGER period;
+
+ //
+ // Exiting throttle mode
+ //
+
+ KeQuerySystemTime(&fdoData->ThrottleStopTime);
+
+ period.QuadPart = fdoData->ThrottleStopTime.QuadPart - fdoData->ThrottleStartTime.QuadPart;
+ fdoData->LongestThrottlePeriod.QuadPart = max(fdoData->LongestThrottlePeriod.QuadPart, period.QuadPart);
+ }
+
+ KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
+ }
+
+ if (idleRequest) {
+ ClasspCompleteIdleRequest(fdoExt);
+ }
+
+ /*
+ * We may have been called by one of the class drivers (e.g. cdrom)
+ * via the legacy API ClassSplitRequest.
+ * This is the only case for which the packet engine is called for an FDO
+ * with a StartIo routine; in that case, we have to call IoStartNextPacket
+ * now that the original irp has been completed.
+ */
+ if (fdoExt->CommonExtension.DriverExtension->InitData.ClassStartIo) {
+ if (TEST_FLAG(SrbGetSrbFlags(pkt->Srb), SRB_FLAGS_DONT_START_NEXT_PACKET)){
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_RW, "SRB_FLAGS_DONT_START_NEXT_PACKET should never be set here (??)"));
+ } else {
+ KeRaiseIrql(DISPATCH_LEVEL, &oldIrql);
+ IoStartNextPacket(Fdo, TRUE); // yes, some IO is now cancellable
+ KeLowerIrql(oldIrql);
+ }
+ }
+ }
+ }
+
+ /*
+ * If the packet was synchronous, write the final result back to the issuer's status buffer
+ * and signal his event.
+ */
+ if (pkt->SyncEventPtr){
+ KeSetEvent(pkt->SyncEventPtr, 0, FALSE);
+ pkt->SyncEventPtr = NULL;
+ }
+
+ /*
+ * If the operation isn't a normal read/write, but needs to do more
+ * operation-specific processing, call the operation's continuation
+ * routine. The operation may create and queue another transfer packet
+ * within this routine, but pkt is still freed after returning from the
+ * continuation routine.
+ */
+ if (pkt->ContinuationRoutine != NULL){
+ pkt->ContinuationRoutine(pkt->ContinuationContext);
+ pkt->ContinuationRoutine = NULL;
+ }
+
+ /*
+ * Free the completed packet.
+ */
+ pkt->UsePartialMdl = FALSE;
+// pkt->OriginalIrp = NULL;
+ pkt->InLowMemRetry = FALSE;
+ EnqueueFreeTransferPacket(Fdo, pkt);
+
+ /*
+ * Now that we have freed some resources,
+ * try again to send one of the previously deferred irps.
+ */
+ deferredIrp = DequeueDeferredClientIrp(Fdo);
+ if (deferredIrp){
+ ServiceTransferRequest(Fdo, deferredIrp, TRUE);
+ }
+
+ ClassReleaseRemoveLock(Fdo, (PVOID)&uniqueAddr);
+ }
+
+ return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+
+/*
+ * SetupEjectionTransferPacket
+ *
+ * Set up a transferPacket for a synchronous Ejection Control transfer.
+ */
+VOID SetupEjectionTransferPacket( TRANSFER_PACKET *Pkt,
+ BOOLEAN PreventMediaRemoval,
+ PKEVENT SyncEventPtr,
+ PIRP OriginalIrp)
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Pkt->Fdo->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
+ PCDB pCdb;
+ ULONG srbLength;
+
+ PAGED_CODE();
+
+ if (fdoExt->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ srbLength = ((PSTORAGE_REQUEST_BLOCK) fdoData->SrbTemplate)->SrbLength;
+ NT_ASSERT(((PSTORAGE_REQUEST_BLOCK) Pkt->Srb)->SrbLength >= srbLength);
+ } else {
+ srbLength = fdoData->SrbTemplate->Length;
+ }
+ RtlCopyMemory(Pkt->Srb, fdoData->SrbTemplate, srbLength); // copies _contents_ of SRB blocks
+
+ SrbSetRequestAttribute(Pkt->Srb, SRB_SIMPLE_TAG_REQUEST);
+ SrbSetCdbLength(Pkt->Srb, 6);
+ SrbSetOriginalRequest(Pkt->Srb, Pkt->Irp);
+ SrbSetSenseInfoBuffer(Pkt->Srb, &Pkt->SrbErrorSenseData);
+ SrbSetSenseInfoBufferLength(Pkt->Srb, sizeof(Pkt->SrbErrorSenseData));
+ SrbSetTimeOutValue(Pkt->Srb, fdoExt->TimeOutValue);
+
+ SrbAssignSrbFlags(Pkt->Srb, fdoExt->SrbFlags | SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_NO_QUEUE_FREEZE);
+
+ pCdb = SrbGetCdb(Pkt->Srb);
+ if (pCdb) {
+ pCdb->MEDIA_REMOVAL.OperationCode = SCSIOP_MEDIUM_REMOVAL;
+ pCdb->MEDIA_REMOVAL.Prevent = PreventMediaRemoval;
+ }
+
+ Pkt->BufPtrCopy = NULL;
+ Pkt->BufLenCopy = 0;
+
+ Pkt->OriginalIrp = OriginalIrp;
+ Pkt->NumRetries = NUM_LOCKMEDIAREMOVAL_RETRIES;
+ Pkt->SyncEventPtr = SyncEventPtr;
+ Pkt->CompleteOriginalIrpWhenLastPacketCompletes = FALSE;
+}
+
+
+/*
+ * SetupModeSenseTransferPacket
+ *
+ * Set up a transferPacket for a synchronous Mode Sense transfer.
+ */
+VOID SetupModeSenseTransferPacket(TRANSFER_PACKET *Pkt,
+ PKEVENT SyncEventPtr,
+ PVOID ModeSenseBuffer,
+ UCHAR ModeSenseBufferLen,
+ UCHAR PageMode,
+ UCHAR SubPage,
+ PIRP OriginalIrp,
+ UCHAR PageControl)
+
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Pkt->Fdo->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
+ PCDB pCdb;
+ ULONG srbLength;
+
+ PAGED_CODE();
+
+ if (fdoExt->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ srbLength = ((PSTORAGE_REQUEST_BLOCK) fdoData->SrbTemplate)->SrbLength;
+ NT_ASSERT(((PSTORAGE_REQUEST_BLOCK) Pkt->Srb)->SrbLength >= srbLength);
+ } else {
+ srbLength = fdoData->SrbTemplate->Length;
+ }
+ RtlCopyMemory(Pkt->Srb, fdoData->SrbTemplate, srbLength); // copies _contents_ of SRB blocks
+
+ SrbSetRequestAttribute(Pkt->Srb, SRB_SIMPLE_TAG_REQUEST);
+ SrbSetCdbLength(Pkt->Srb, 6);
+ SrbSetOriginalRequest(Pkt->Srb, Pkt->Irp);
+ SrbSetSenseInfoBuffer(Pkt->Srb, &Pkt->SrbErrorSenseData);
+ SrbSetSenseInfoBufferLength(Pkt->Srb, sizeof(Pkt->SrbErrorSenseData));
+ SrbSetTimeOutValue(Pkt->Srb, fdoExt->TimeOutValue);
+ SrbSetDataBuffer(Pkt->Srb, ModeSenseBuffer);
+ SrbSetDataTransferLength(Pkt->Srb, ModeSenseBufferLen);
+
+
+ SrbAssignSrbFlags(Pkt->Srb, fdoExt->SrbFlags | SRB_FLAGS_DATA_IN | SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_NO_QUEUE_FREEZE);
+
+ pCdb = SrbGetCdb(Pkt->Srb);
+ if (pCdb) {
+ pCdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE;
+ pCdb->MODE_SENSE.PageCode = PageMode;
+ pCdb->MODE_SENSE.SubPageCode = SubPage;
+ pCdb->MODE_SENSE.Pc = PageControl;
+ pCdb->MODE_SENSE.AllocationLength = (UCHAR)ModeSenseBufferLen;
+ }
+
+ Pkt->BufPtrCopy = ModeSenseBuffer;
+ Pkt->BufLenCopy = ModeSenseBufferLen;
+
+ Pkt->OriginalIrp = OriginalIrp;
+ Pkt->NumRetries = NUM_MODESENSE_RETRIES;
+ Pkt->SyncEventPtr = SyncEventPtr;
+ Pkt->CompleteOriginalIrpWhenLastPacketCompletes = FALSE;
+}
+
+/*
+ * SetupModeSelectTransferPacket
+ *
+ * Set up a transferPacket for a synchronous Mode Select transfer.
+ */
+VOID SetupModeSelectTransferPacket(TRANSFER_PACKET *Pkt,
+ PKEVENT SyncEventPtr,
+ PVOID ModeSelectBuffer,
+ UCHAR ModeSelectBufferLen,
+ BOOLEAN SavePages,
+ PIRP OriginalIrp)
+
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Pkt->Fdo->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
+ PCDB pCdb;
+ ULONG srbLength;
+
+ if (fdoExt->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ srbLength = ((PSTORAGE_REQUEST_BLOCK) fdoData->SrbTemplate)->SrbLength;
+ NT_ASSERT(((PSTORAGE_REQUEST_BLOCK) Pkt->Srb)->SrbLength >= srbLength);
+ } else {
+ srbLength = fdoData->SrbTemplate->Length;
+ }
+ RtlCopyMemory(Pkt->Srb, fdoData->SrbTemplate, srbLength); // copies _contents_ of SRB blocks
+
+ SrbSetRequestAttribute(Pkt->Srb, SRB_SIMPLE_TAG_REQUEST);
+ SrbSetCdbLength(Pkt->Srb, 6);
+ SrbSetOriginalRequest(Pkt->Srb, Pkt->Irp);
+ SrbSetSenseInfoBuffer(Pkt->Srb, &Pkt->SrbErrorSenseData);
+ SrbSetSenseInfoBufferLength(Pkt->Srb, sizeof(Pkt->SrbErrorSenseData));
+ SrbSetTimeOutValue(Pkt->Srb, fdoExt->TimeOutValue);
+ SrbSetDataBuffer(Pkt->Srb, ModeSelectBuffer);
+ SrbSetDataTransferLength(Pkt->Srb, ModeSelectBufferLen);
+
+ SrbAssignSrbFlags(Pkt->Srb, fdoExt->SrbFlags | SRB_FLAGS_DATA_OUT | SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_NO_QUEUE_FREEZE);
+
+ pCdb = SrbGetCdb(Pkt->Srb);
+ if (pCdb) {
+ pCdb->MODE_SELECT.OperationCode = SCSIOP_MODE_SELECT;
+ pCdb->MODE_SELECT.SPBit = SavePages;
+ pCdb->MODE_SELECT.PFBit = 1;
+ pCdb->MODE_SELECT.ParameterListLength = (UCHAR)ModeSelectBufferLen;
+ }
+
+ Pkt->BufPtrCopy = ModeSelectBuffer;
+ Pkt->BufLenCopy = ModeSelectBufferLen;
+
+ Pkt->OriginalIrp = OriginalIrp;
+ Pkt->NumRetries = NUM_MODESELECT_RETRIES;
+ Pkt->SyncEventPtr = SyncEventPtr;
+ Pkt->CompleteOriginalIrpWhenLastPacketCompletes = FALSE;
+}
+
+
+/*
+ * SetupDriveCapacityTransferPacket
+ *
+ * Set up a transferPacket for a synchronous Drive Capacity transfer.
+ */
+VOID SetupDriveCapacityTransferPacket( TRANSFER_PACKET *Pkt,
+ PVOID ReadCapacityBuffer,
+ ULONG ReadCapacityBufferLen,
+ PKEVENT SyncEventPtr,
+ PIRP OriginalIrp,
+ BOOLEAN Use16ByteCdb)
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Pkt->Fdo->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
+ PCDB pCdb;
+ ULONG srbLength;
+
+ if (fdoExt->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ srbLength = ((PSTORAGE_REQUEST_BLOCK) fdoData->SrbTemplate)->SrbLength;
+ NT_ASSERT(((PSTORAGE_REQUEST_BLOCK) Pkt->Srb)->SrbLength >= srbLength);
+ } else {
+ srbLength = fdoData->SrbTemplate->Length;
+ }
+ RtlCopyMemory(Pkt->Srb, fdoData->SrbTemplate, srbLength); // copies _contents_ of SRB blocks
+
+ SrbSetRequestAttribute(Pkt->Srb, SRB_SIMPLE_TAG_REQUEST);
+ SrbSetOriginalRequest(Pkt->Srb, Pkt->Irp);
+ SrbSetSenseInfoBuffer(Pkt->Srb, &Pkt->SrbErrorSenseData);
+ SrbSetSenseInfoBufferLength(Pkt->Srb, sizeof(Pkt->SrbErrorSenseData));
+ SrbSetTimeOutValue(Pkt->Srb, fdoExt->TimeOutValue);
+ SrbSetDataBuffer(Pkt->Srb, ReadCapacityBuffer);
+ SrbSetDataTransferLength(Pkt->Srb, ReadCapacityBufferLen);
+
+ SrbAssignSrbFlags(Pkt->Srb, fdoExt->SrbFlags | SRB_FLAGS_DATA_IN | SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_NO_QUEUE_FREEZE);
+
+ pCdb = SrbGetCdb(Pkt->Srb);
+ if (pCdb) {
+ if (Use16ByteCdb == TRUE) {
+ NT_ASSERT(ReadCapacityBufferLen >= sizeof(READ_CAPACITY_DATA_EX));
+ SrbSetCdbLength(Pkt->Srb, 16);
+ pCdb->CDB16.OperationCode = SCSIOP_READ_CAPACITY16;
+ REVERSE_BYTES(&pCdb->CDB16.TransferLength, &ReadCapacityBufferLen);
+ pCdb->AsByte[1] = 0x10; // Service Action
+ } else {
+ SrbSetCdbLength(Pkt->Srb, 10);
+ pCdb->CDB10.OperationCode = SCSIOP_READ_CAPACITY;
+ }
+ }
+
+ Pkt->BufPtrCopy = ReadCapacityBuffer;
+ Pkt->BufLenCopy = ReadCapacityBufferLen;
+
+ Pkt->OriginalIrp = OriginalIrp;
+ Pkt->NumRetries = NUM_DRIVECAPACITY_RETRIES;
+ Pkt->SyncEventPtr = SyncEventPtr;
+ Pkt->CompleteOriginalIrpWhenLastPacketCompletes = FALSE;
+}
+
+
+#if 0
+ /*
+ * SetupSendStartUnitTransferPacket
+ *
+ * Set up a transferPacket for a synchronous Send Start Unit transfer.
+ */
+ VOID SetupSendStartUnitTransferPacket( TRANSFER_PACKET *Pkt,
+ PIRP OriginalIrp)
+ {
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Pkt->Fdo->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
+ PCDB pCdb;
+
+ PAGED_CODE();
+
+ RtlZeroMemory(&Pkt->Srb, sizeof(SCSI_REQUEST_BLOCK));
+
+ /*
+ * Initialize the SRB.
+ * Use a very long timeout value to give the drive time to spin up.
+ */
+ Pkt->Srb->Length = sizeof(SCSI_REQUEST_BLOCK);
+ Pkt->Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
+ Pkt->Srb->TimeOutValue = START_UNIT_TIMEOUT;
+ Pkt->Srb->CdbLength = 6;
+ Pkt->Srb->OriginalRequest = Pkt->Irp;
+ Pkt->Srb->SenseInfoBuffer = &Pkt->SrbErrorSenseData;
+ Pkt->Srb->SenseInfoBufferLength = sizeof(Pkt->SrbErrorSenseData);
+ Pkt->Srb->Lun = 0;
+
+ SET_FLAG(Pkt->Srb->SrbFlags, SRB_FLAGS_NO_DATA_TRANSFER);
+ SET_FLAG(Pkt->Srb->SrbFlags, SRB_FLAGS_DISABLE_AUTOSENSE);
+ SET_FLAG(Pkt->Srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
+
+ pCdb = (PCDB)Pkt->Srb->Cdb;
+ pCdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;
+ pCdb->START_STOP.Start = 1;
+ pCdb->START_STOP.Immediate = 0;
+ pCdb->START_STOP.LogicalUnitNumber = 0;
+
+ Pkt->OriginalIrp = OriginalIrp;
+ Pkt->NumRetries = 0;
+ Pkt->SyncEventPtr = NULL;
+ Pkt->CompleteOriginalIrpWhenLastPacketCompletes = FALSE;
+ }
+#endif
+
+
+VOID
+CleanupTransferPacketToWorkingSetSizeWorker(
+ _In_ PVOID Fdo,
+ _In_opt_ PVOID Context,
+ _In_ PIO_WORKITEM IoWorkItem
+ )
+{
+ ULONG node = (ULONG) Context;
+
+ PAGED_CODE();
+
+ CleanupTransferPacketToWorkingSetSize((PDEVICE_OBJECT)Fdo, FALSE, node);
+
+ //
+ // Release the remove lock acquired in EnqueueFreeTransferPacket
+ //
+ ClassReleaseRemoveLock((PDEVICE_OBJECT)Fdo, (PIRP)IoWorkItem);
+
+ if (IoWorkItem != NULL) {
+ IoFreeWorkItem(IoWorkItem);
+ }
+}
+
+
+VOID
+CleanupTransferPacketToWorkingSetSize(
+ _In_ PDEVICE_OBJECT Fdo,
+ _In_ BOOLEAN LimitNumPktToDelete,
+ _In_ ULONG Node
+ )
+
+/*
+Routine Description:
+
+ This function frees the resources for the free transfer packets attempting
+ to bring them down within the working set size.
+
+Arguments:
+ Fdo: The FDO that represents the device whose transfer packet size needs to be trimmed.
+ LimitNumPktToDelete: Flag to indicate if the number of packets freed in one call should be capped.
+ Node: NUMA node transfer packet is associated with.
+
+--*/
+
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
+ KIRQL oldIrql;
+ SINGLE_LIST_ENTRY pktList;
+ PSINGLE_LIST_ENTRY slistEntry;
+ PTRANSFER_PACKET pktToDelete;
+ ULONG requiredNumPktToDelete = fdoData->FreeTransferPacketsLists[Node].NumTotalTransferPackets -
+ fdoData->LocalMaxWorkingSetTransferPackets;
+
+ if (LimitNumPktToDelete) {
+ requiredNumPktToDelete = MIN(requiredNumPktToDelete, MAX_CLEANUP_TRANSFER_PACKETS_AT_ONCE);
+ }
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_RW, "CleanupTransferPacketToWorkingSetSize (%p): Exiting stress, block freeing %d packets.", Fdo, requiredNumPktToDelete));
+
+ /*
+ * Check the counter again with lock held. This eliminates a race condition
+ * while still allowing us to not grab the spinlock in the common codepath.
+ *
+ * Note that the spinlock does not synchronize with threads dequeuing free
+ * packets to send (DequeueFreeTransferPacket does that with a lightweight
+ * interlocked exchange); the spinlock prevents multiple threads in this function
+ * from deciding to free too many extra packets at once.
+ */
+ SimpleInitSlistHdr(&pktList);
+ KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
+ while ((fdoData->FreeTransferPacketsLists[Node].NumFreeTransferPackets >= fdoData->FreeTransferPacketsLists[Node].NumTotalTransferPackets) &&
+ (fdoData->FreeTransferPacketsLists[Node].NumTotalTransferPackets > fdoData->LocalMaxWorkingSetTransferPackets) &&
+ (requiredNumPktToDelete--)){
+
+ pktToDelete = DequeueFreeTransferPacketEx(Fdo, FALSE, Node);
+ if (pktToDelete){
+ SimplePushSlist(&pktList,
+ (PSINGLE_LIST_ENTRY)&pktToDelete->SlistEntry);
+ InterlockedDecrement((volatile LONG *)&fdoData->FreeTransferPacketsLists[Node].NumTotalTransferPackets);
+ } else {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_RW,
+ "Extremely unlikely condition (non-fatal): %d packets dequeued at once for Fdo %p. NumTotalTransferPackets=%d (1). Node=%d",
+ fdoData->LocalMaxWorkingSetTransferPackets,
+ Fdo,
+ fdoData->FreeTransferPacketsLists[Node].NumTotalTransferPackets,
+ Node));
+ break;
+ }
+ }
+ KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
+
+ slistEntry = SimplePopSlist(&pktList);
+ while (slistEntry) {
+ pktToDelete = CONTAINING_RECORD(slistEntry, TRANSFER_PACKET, SlistEntry);
+ DestroyTransferPacket(pktToDelete);
+ slistEntry = SimplePopSlist(&pktList);
+ }
+
+ return;
+}
+
+
+_IRQL_requires_max_(APC_LEVEL)
+_IRQL_requires_min_(PASSIVE_LEVEL)
+_IRQL_requires_same_
+VOID
+ClasspSetupPopulateTokenTransferPacket(
+ _In_ __drv_aliasesMem POFFLOAD_READ_CONTEXT OffloadReadContext,
+ _In_ PTRANSFER_PACKET Pkt,
+ _In_ ULONG Length,
+ _In_reads_bytes_(Length) PUCHAR PopulateTokenBuffer,
+ _In_ PIRP OriginalIrp,
+ _In_ ULONG ListIdentifier
+ )
+
+/*++
+
+Routine description:
+
+ This routine is called once to set up a packet for PopulateToken.
+ It builds up the SRB by setting the appropriate fields.
+
+Arguments:
+
+ Pkt - The transfer packet to be sent down to the lower driver
+ SyncEventPtr - The event that gets signaled once the IRP contained in the packet completes
+ Length - Length of the buffer being sent as part of the command
+ PopulateTokenBuffer - The buffer that contains the LBA ranges information for the PopulateToken operation
+ OriginalIrp - The Io request to be processed
+ ListIdentifier - The identifier that will be used to correlate a subsequent command to retrieve the token
+
+Return Value:
+
+ Nothing
+
+--*/
+
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt;
+ PCLASS_PRIVATE_FDO_DATA fdoData;
+ PCDB pCdb;
+ ULONG srbLength;
+
+ PAGED_CODE();
+
+ TracePrint((TRACE_LEVEL_VERBOSE,
+ TRACE_FLAG_IOCTL,
+ "ClasspSetupPopulateTokenTransferPacket (%p): Entering function. Irp %p\n",
+ Pkt->Fdo,
+ OriginalIrp));
+
+ fdoExt = Pkt->Fdo->DeviceExtension;
+ fdoData = fdoExt->PrivateFdoData;
+
+ if (fdoExt->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ srbLength = ((PSTORAGE_REQUEST_BLOCK) fdoData->SrbTemplate)->SrbLength;
+ NT_ASSERT(((PSTORAGE_REQUEST_BLOCK) Pkt->Srb)->SrbLength >= srbLength);
+ } else {
+ srbLength = fdoData->SrbTemplate->Length;
+ }
+ RtlCopyMemory(Pkt->Srb, fdoData->SrbTemplate, srbLength); // copies _contents_ of SRB blocks
+
+ SrbSetRequestAttribute(Pkt->Srb, SRB_SIMPLE_TAG_REQUEST);
+ SrbSetCdbLength(Pkt->Srb, 16);
+ SrbSetOriginalRequest(Pkt->Srb, Pkt->Irp);
+ SrbSetSenseInfoBuffer(Pkt->Srb, &Pkt->SrbErrorSenseData);
+ SrbSetSenseInfoBufferLength(Pkt->Srb, sizeof(Pkt->SrbErrorSenseData));
+ SrbSetTimeOutValue(Pkt->Srb, fdoExt->TimeOutValue);
+ SrbSetDataBuffer(Pkt->Srb, PopulateTokenBuffer);
+ SrbSetDataTransferLength(Pkt->Srb, Length);
+
+ SrbAssignSrbFlags(Pkt->Srb, fdoExt->SrbFlags | SRB_FLAGS_DATA_OUT | SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_NO_QUEUE_FREEZE);
+
+ pCdb = SrbGetCdb(Pkt->Srb);
+ if (pCdb) {
+ pCdb->TOKEN_OPERATION.OperationCode = SCSIOP_POPULATE_TOKEN;
+ pCdb->TOKEN_OPERATION.ServiceAction = SERVICE_ACTION_POPULATE_TOKEN;
+
+ REVERSE_BYTES(&pCdb->TOKEN_OPERATION.ListIdentifier, &ListIdentifier);
+ REVERSE_BYTES(&pCdb->TOKEN_OPERATION.ParameterListLength, &Length);
+ }
+
+ Pkt->BufPtrCopy = PopulateTokenBuffer;
+ Pkt->BufLenCopy = Length;
+
+ Pkt->OriginalIrp = OriginalIrp;
+ Pkt->NumRetries = NUM_POPULATE_TOKEN_RETRIES;
+ Pkt->CompleteOriginalIrpWhenLastPacketCompletes = FALSE;
+
+ Pkt->ContinuationRoutine = ClasspPopulateTokenTransferPacketDone;
+ Pkt->ContinuationContext = OffloadReadContext;
+
+
+ TracePrint((TRACE_LEVEL_VERBOSE,
+ TRACE_FLAG_IOCTL,
+ "ClasspSetupPopulateTokenTransferPacket (%p): Exiting function with Irp %p\n",
+ Pkt->Fdo,
+ OriginalIrp));
+
+ return;
+}
+
+
+_IRQL_requires_max_(APC_LEVEL)
+_IRQL_requires_min_(PASSIVE_LEVEL)
+_IRQL_requires_same_
+VOID
+ClasspSetupReceivePopulateTokenInformationTransferPacket(
+ _In_ POFFLOAD_READ_CONTEXT OffloadReadContext,
+ _In_ PTRANSFER_PACKET Pkt,
+ _In_ ULONG Length,
+ _In_reads_bytes_(Length) PUCHAR ReceivePopulateTokenInformationBuffer,
+ _In_ PIRP OriginalIrp,
+ _In_ ULONG ListIdentifier
+ )
+
+/*++
+
+Routine description:
+
+ This routine is called once to set up a packet for read token retrieval.
+ It builds up the SRB by setting the appropriate fields.
+
+Arguments:
+
+ Pkt - The transfer packet to be sent down to the lower driver
+ Length - Length of the buffer being sent as part of the command
+ ReceivePopulateTokenInformationBuffer - The buffer into which the target will pass back the token
+ OriginalIrp - The Io request to be processed
+ ListIdentifier - The identifier that will be used to correlate this command with its corresponding previous populate token operation
+
+Return Value:
+
+ Nothing
+
+--*/
+
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt;
+ PCLASS_PRIVATE_FDO_DATA fdoData;
+ PCDB pCdb;
+ ULONG srbLength;
+
+ TracePrint((TRACE_LEVEL_VERBOSE,
+ TRACE_FLAG_IOCTL,
+ "ClasspSetupReceivePopulateTokenInformationTransferPacket (%p): Entering function. Irp %p\n",
+ Pkt->Fdo,
+ OriginalIrp));
+
+ fdoExt = Pkt->Fdo->DeviceExtension;
+ fdoData = fdoExt->PrivateFdoData;
+
+ if (fdoExt->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ srbLength = ((PSTORAGE_REQUEST_BLOCK) fdoData->SrbTemplate)->SrbLength;
+ NT_ASSERT(((PSTORAGE_REQUEST_BLOCK) Pkt->Srb)->SrbLength >= srbLength);
+ } else {
+ srbLength = fdoData->SrbTemplate->Length;
+ }
+ RtlCopyMemory(Pkt->Srb, fdoData->SrbTemplate, srbLength); // copies _contents_ of SRB blocks
+
+ SrbSetRequestAttribute(Pkt->Srb, SRB_SIMPLE_TAG_REQUEST);
+ SrbSetCdbLength(Pkt->Srb, 16);
+ SrbSetOriginalRequest(Pkt->Srb, Pkt->Irp);
+ SrbSetSenseInfoBuffer(Pkt->Srb, &Pkt->SrbErrorSenseData);
+ SrbSetSenseInfoBufferLength(Pkt->Srb, sizeof(Pkt->SrbErrorSenseData));
+ SrbSetTimeOutValue(Pkt->Srb, fdoExt->TimeOutValue);
+ SrbSetDataBuffer(Pkt->Srb, ReceivePopulateTokenInformationBuffer);
+ SrbSetDataTransferLength(Pkt->Srb, Length);
+
+ SrbAssignSrbFlags(Pkt->Srb, fdoExt->SrbFlags | SRB_FLAGS_DATA_IN | SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_NO_QUEUE_FREEZE);
+
+ pCdb = SrbGetCdb(Pkt->Srb);
+ if (pCdb) {
+ pCdb->RECEIVE_TOKEN_INFORMATION.OperationCode = SCSIOP_RECEIVE_ROD_TOKEN_INFORMATION;
+ pCdb->RECEIVE_TOKEN_INFORMATION.ServiceAction = SERVICE_ACTION_RECEIVE_TOKEN_INFORMATION;
+
+ REVERSE_BYTES(&pCdb->RECEIVE_TOKEN_INFORMATION.ListIdentifier, &ListIdentifier);
+ REVERSE_BYTES(&pCdb->RECEIVE_TOKEN_INFORMATION.AllocationLength, &Length);
+ }
+
+ Pkt->BufPtrCopy = ReceivePopulateTokenInformationBuffer;
+ Pkt->BufLenCopy = Length;
+
+ Pkt->OriginalIrp = OriginalIrp;
+ Pkt->NumRetries = NUM_POPULATE_TOKEN_RETRIES;
+ Pkt->CompleteOriginalIrpWhenLastPacketCompletes = FALSE;
+
+ Pkt->ContinuationRoutine = ClasspReceivePopulateTokenInformationTransferPacketDone;
+ Pkt->ContinuationContext = OffloadReadContext;
+
+
+ TracePrint((TRACE_LEVEL_VERBOSE,
+ TRACE_FLAG_IOCTL,
+ "ClasspSetupReceivePopulateTokenInformationTransferPacket (%p): Exiting function with Irp %p\n",
+ Pkt->Fdo,
+ OriginalIrp));
+
+ return;
+}
+
+
+_IRQL_requires_max_(APC_LEVEL)
+_IRQL_requires_min_(PASSIVE_LEVEL)
+_IRQL_requires_same_
+VOID
+ClasspSetupWriteUsingTokenTransferPacket(
+ _In_ __drv_aliasesMem POFFLOAD_WRITE_CONTEXT OffloadWriteContext,
+ _In_ PTRANSFER_PACKET Pkt,
+ _In_ ULONG Length,
+ _In_reads_bytes_ (Length) PUCHAR WriteUsingTokenBuffer,
+ _In_ PIRP OriginalIrp,
+ _In_ ULONG ListIdentifier
+ )
+
+/*++
+
+Routine description:
+
+ This routine is called once to set up a packet for WriteUsingToken.
+ It builds up the SRB by setting the appropriate fields. It is not called
+ before a retry as the SRB fields may be modified for the retry.
+
+ The IRP is set up in SubmitTransferPacket because it must be reset for
+ each packet submission.
+
+Arguments:
+
+ Pkt - The transfer packet to be sent down to the lower driver
+ Length - Length of the buffer being sent as part of the command
+ WriteUsingTokenBuffer - The buffer that contains the read token and the write LBA ranges information for the WriteUsingToken operation
+ OriginalIrp - The Io request to be processed
+ ListIdentifier - The identifier that will be used to correlate a subsequent command to retrieve extended results in case of command failure
+
+Return Value:
+
+ Nothing
+
+--*/
+
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt;
+ PCLASS_PRIVATE_FDO_DATA fdoData;
+ PCDB pCdb;
+ ULONG srbLength;
+
+ TracePrint((TRACE_LEVEL_VERBOSE,
+ TRACE_FLAG_IOCTL,
+ "ClasspSetupWriteUsingTokenTransferPacket (%p): Entering function. Irp %p\n",
+ Pkt->Fdo,
+ OriginalIrp));
+
+ fdoExt = Pkt->Fdo->DeviceExtension;
+ fdoData = fdoExt->PrivateFdoData;
+
+ if (fdoExt->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ srbLength = ((PSTORAGE_REQUEST_BLOCK) fdoData->SrbTemplate)->SrbLength;
+ NT_ASSERT(((PSTORAGE_REQUEST_BLOCK) Pkt->Srb)->SrbLength >= srbLength);
+ } else {
+ srbLength = fdoData->SrbTemplate->Length;
+ }
+ RtlCopyMemory(Pkt->Srb, fdoData->SrbTemplate, srbLength); // copies _contents_ of SRB blocks
+
+ SrbSetRequestAttribute(Pkt->Srb, SRB_SIMPLE_TAG_REQUEST);
+ SrbSetCdbLength(Pkt->Srb, 16);
+ SrbSetOriginalRequest(Pkt->Srb, Pkt->Irp);
+ SrbSetSenseInfoBuffer(Pkt->Srb, &Pkt->SrbErrorSenseData);
+ SrbSetSenseInfoBufferLength(Pkt->Srb, sizeof(Pkt->SrbErrorSenseData));
+ SrbSetTimeOutValue(Pkt->Srb, fdoExt->TimeOutValue);
+ SrbSetDataBuffer(Pkt->Srb, WriteUsingTokenBuffer);
+ SrbSetDataTransferLength(Pkt->Srb, Length);
+
+ SrbAssignSrbFlags(Pkt->Srb, fdoExt->SrbFlags | SRB_FLAGS_DATA_OUT | SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_NO_QUEUE_FREEZE);
+
+ pCdb = SrbGetCdb(Pkt->Srb);
+ if (pCdb) {
+ pCdb->TOKEN_OPERATION.OperationCode = SCSIOP_WRITE_USING_TOKEN;
+ pCdb->TOKEN_OPERATION.ServiceAction = SERVICE_ACTION_WRITE_USING_TOKEN;
+
+ REVERSE_BYTES(&pCdb->TOKEN_OPERATION.ParameterListLength, &Length);
+ REVERSE_BYTES(&pCdb->TOKEN_OPERATION.ListIdentifier, &ListIdentifier);
+ }
+
+ Pkt->BufPtrCopy = WriteUsingTokenBuffer;
+ Pkt->BufLenCopy = Length;
+
+ Pkt->OriginalIrp = OriginalIrp;
+ Pkt->NumRetries = NUM_WRITE_USING_TOKEN_RETRIES;
+ Pkt->CompleteOriginalIrpWhenLastPacketCompletes = FALSE;
+
+ Pkt->ContinuationRoutine = ClasspWriteUsingTokenTransferPacketDone;
+ Pkt->ContinuationContext = OffloadWriteContext;
+
+
+ TracePrint((TRACE_LEVEL_VERBOSE,
+ TRACE_FLAG_IOCTL,
+ "ClasspSetupWriteUsingTokenTransferPacket (%p): Exiting function with Irp %p\n",
+ Pkt->Fdo,
+ OriginalIrp));
+
+ return;
+}
+
+
+_IRQL_requires_max_(APC_LEVEL)
+_IRQL_requires_min_(PASSIVE_LEVEL)
+_IRQL_requires_same_
+VOID
+ClasspSetupReceiveWriteUsingTokenInformationTransferPacket(
+ _In_ POFFLOAD_WRITE_CONTEXT OffloadWriteContext,
+ _In_ PTRANSFER_PACKET Pkt,
+ _In_ ULONG Length,
+ _In_reads_bytes_ (Length) PUCHAR ReceiveWriteUsingTokenInformationBuffer,
+ _In_ PIRP OriginalIrp,
+ _In_ ULONG ListIdentifier
+ )
+
+/*++
+
+Routine description:
+
+ This routine is called once to set up a packet for extended results for
+ WriteUsingToken operation. It builds up the SRB by setting the appropriate fields.
+
+Arguments:
+
+ Pkt - The transfer packet to be sent down to the lower driver
+ SyncEventPtr - The event that gets signaled once the IRP contained in the packet completes
+ Length - Length of the buffer being sent as part of the command
+ ReceiveWriteUsingTokenInformationBuffer - The buffer into which the target will pass back the extended results
+ OriginalIrp - The Io request to be processed
+ ListIdentifier - The identifier that will be used to correlate this command with its corresponding previous write using token operation
+
+Return Value:
+
+ Nothing
+
+--*/
+
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt;
+ PCLASS_PRIVATE_FDO_DATA fdoData;
+ PCDB pCdb;
+ ULONG srbLength;
+
+ TracePrint((TRACE_LEVEL_VERBOSE,
+ TRACE_FLAG_IOCTL,
+ "ClasspSetupReceiveWriteUsingTokenInformationTransferPacket (%p): Entering function. Irp %p\n",
+ Pkt->Fdo,
+ OriginalIrp));
+
+ fdoExt = Pkt->Fdo->DeviceExtension;
+ fdoData = fdoExt->PrivateFdoData;
+
+ if (fdoExt->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ srbLength = ((PSTORAGE_REQUEST_BLOCK) fdoData->SrbTemplate)->SrbLength;
+ NT_ASSERT(((PSTORAGE_REQUEST_BLOCK) Pkt->Srb)->SrbLength >= srbLength);
+ } else {
+ srbLength = fdoData->SrbTemplate->Length;
+ }
+ RtlCopyMemory(Pkt->Srb, fdoData->SrbTemplate, srbLength); // copies _contents_ of SRB blocks
+
+ SrbSetRequestAttribute(Pkt->Srb, SRB_SIMPLE_TAG_REQUEST);
+ SrbSetCdbLength(Pkt->Srb, 16);
+ SrbSetOriginalRequest(Pkt->Srb, Pkt->Irp);
+ SrbSetSenseInfoBuffer(Pkt->Srb, &Pkt->SrbErrorSenseData);
+ SrbSetSenseInfoBufferLength(Pkt->Srb, sizeof(Pkt->SrbErrorSenseData));
+ SrbSetTimeOutValue(Pkt->Srb, fdoExt->TimeOutValue);
+ SrbSetDataBuffer(Pkt->Srb, ReceiveWriteUsingTokenInformationBuffer);
+ SrbSetDataTransferLength(Pkt->Srb, Length);
+
+ SrbAssignSrbFlags(Pkt->Srb, fdoExt->SrbFlags | SRB_FLAGS_DATA_IN | SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_NO_QUEUE_FREEZE);
+
+ pCdb = SrbGetCdb(Pkt->Srb);
+ if (pCdb) {
+ pCdb->RECEIVE_TOKEN_INFORMATION.OperationCode = SCSIOP_RECEIVE_ROD_TOKEN_INFORMATION;
+ pCdb->RECEIVE_TOKEN_INFORMATION.ServiceAction = SERVICE_ACTION_RECEIVE_TOKEN_INFORMATION;
+
+ REVERSE_BYTES(&pCdb->RECEIVE_TOKEN_INFORMATION.AllocationLength, &Length);
+ REVERSE_BYTES(&pCdb->RECEIVE_TOKEN_INFORMATION.ListIdentifier, &ListIdentifier);
+ }
+
+ Pkt->BufPtrCopy = ReceiveWriteUsingTokenInformationBuffer;
+ Pkt->BufLenCopy = Length;
+
+ Pkt->OriginalIrp = OriginalIrp;
+ Pkt->NumRetries = NUM_WRITE_USING_TOKEN_RETRIES;
+ Pkt->CompleteOriginalIrpWhenLastPacketCompletes = FALSE;
+
+ Pkt->ContinuationRoutine = ClasspReceiveWriteUsingTokenInformationTransferPacketDone;
+ Pkt->ContinuationContext = OffloadWriteContext;
+
+
+ TracePrint((TRACE_LEVEL_VERBOSE,
+ TRACE_FLAG_IOCTL,
+ "ClasspSetupReceiveWriteUsingTokenInformationTransferPacket (%p): Exiting function with Irp %p\n",
+ Pkt->Fdo,
+ OriginalIrp));
+
+ return;
+}
+
+