summaryrefslogtreecommitdiff
path: root/storage/class/disk/src/diskwmi.c
diff options
context:
space:
mode:
authorDave Wilson <[email protected]>2015-04-29 09:48:49 -0700
committerDave Wilson <[email protected]>2015-04-29 10:47:29 -0700
commitd40232638faaab19de557e70a3ee938da7a35295 (patch)
tree14e61c973745949af186b7940e3a53d25322f5de /storage/class/disk/src/diskwmi.c
parent02e5b090a6131a7cc1b90f4a9373117c9e3c7088 (diff)
samples update for //build
Diffstat (limited to 'storage/class/disk/src/diskwmi.c')
-rw-r--r--storage/class/disk/src/diskwmi.c3501
1 files changed, 3501 insertions, 0 deletions
diff --git a/storage/class/disk/src/diskwmi.c b/storage/class/disk/src/diskwmi.c
new file mode 100644
index 00000000..c1a0945e
--- /dev/null
+++ b/storage/class/disk/src/diskwmi.c
@@ -0,0 +1,3501 @@
+/*++
+
+Copyright (C) Microsoft Corporation, 1991 - 2010
+
+Module Name:
+
+ diskwmi.c
+
+Abstract:
+
+ SCSI disk class driver - WMI support routines
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+Revision History:
+
+--*/
+
+#include "disk.h"
+
+#ifdef DEBUG_USE_WPP
+#include "diskwmi.tmh"
+#endif
+
+NTSTATUS
+DiskSendFailurePredictIoctl(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ PSTORAGE_PREDICT_FAILURE checkFailure
+ );
+
+NTSTATUS
+DiskGetIdentifyInfo(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ PBOOLEAN SupportSmart
+ );
+
+NTSTATUS
+DiskDetectFailurePrediction(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ PFAILURE_PREDICTION_METHOD FailurePredictCapability,
+ BOOLEAN ScsiAddressAvailable
+ );
+
+NTSTATUS
+DiskReadFailurePredictThresholds(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ PSTORAGE_FAILURE_PREDICT_THRESHOLDS DiskSmartThresholds
+ );
+
+NTSTATUS
+DiskReadSmartLog(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN UCHAR SectorCount,
+ IN UCHAR LogAddress,
+ OUT PUCHAR Buffer
+ );
+
+NTSTATUS
+DiskWriteSmartLog(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN UCHAR SectorCount,
+ IN UCHAR LogAddress,
+ IN PUCHAR Buffer
+ );
+
+IO_WORKITEM_ROUTINE DiskReregWorker;
+
+IO_COMPLETION_ROUTINE DiskInfoExceptionComplete;
+
+//
+// WMI reregistration globals
+//
+// Since it will take too long to do a mode sense on some drive, we
+// need a good way to effect the mode sense for the info exceptions
+// mode page so that we can determine if SMART is supported and enabled
+// for the drive. So the strategy is to do an asynchronous mode sense
+// when the device starts and then look at the info exceptions mode
+// page within the completion routine. Now within the completion
+// routine we cannot call IoWMIRegistrationControl since we are at DPC
+// level, so we create a stack of device objects that will be processed
+// by a single work item that is fired off only when the stack
+// transitions from empty to non empty.
+//
+SINGLE_LIST_ENTRY DiskReregHead;
+KSPIN_LOCK DiskReregSpinlock;
+LONG DiskReregWorkItems;
+
+GUIDREGINFO DiskWmiFdoGuidList[] =
+{
+ {
+ WMI_DISK_GEOMETRY_GUID,
+ 1,
+ 0
+ },
+
+ {
+ WMI_STORAGE_FAILURE_PREDICT_STATUS_GUID,
+ 1,
+ WMIREG_FLAG_EXPENSIVE
+ },
+
+ {
+ WMI_STORAGE_FAILURE_PREDICT_DATA_GUID,
+ 1,
+ WMIREG_FLAG_EXPENSIVE
+ },
+
+ {
+ WMI_STORAGE_FAILURE_PREDICT_FUNCTION_GUID,
+ 1,
+ WMIREG_FLAG_EXPENSIVE
+ },
+
+ {
+ WMI_STORAGE_PREDICT_FAILURE_EVENT_GUID,
+ 1,
+ WMIREG_FLAG_EVENT_ONLY_GUID
+ },
+
+ {
+ WMI_STORAGE_FAILURE_PREDICT_THRESHOLDS_GUID,
+ 1,
+ WMIREG_FLAG_EXPENSIVE
+ },
+
+ {
+ WMI_STORAGE_SCSI_INFO_EXCEPTIONS_GUID,
+ 1,
+ 0
+ }
+};
+
+
+GUID DiskPredictFailureEventGuid = WMI_STORAGE_PREDICT_FAILURE_EVENT_GUID;
+
+#define DiskGeometryGuid 0
+#define SmartStatusGuid 1
+#define SmartDataGuid 2
+#define SmartPerformFunction 3
+ #define AllowDisallowPerformanceHit 1
+ #define EnableDisableHardwareFailurePrediction 2
+ #define EnableDisableFailurePredictionPolling 3
+ #define GetFailurePredictionCapability 4
+ #define EnableOfflineDiags 5
+
+#define SmartEventGuid 4
+#define SmartThresholdsGuid 5
+#define ScsiInfoExceptionsGuid 6
+
+#ifdef ALLOC_PRAGMA
+
+#pragma alloc_text(PAGE, DiskWmiFunctionControl)
+#pragma alloc_text(PAGE, DiskFdoQueryWmiRegInfo)
+#pragma alloc_text(PAGE, DiskFdoQueryWmiDataBlock)
+#pragma alloc_text(PAGE, DiskFdoSetWmiDataBlock)
+#pragma alloc_text(PAGE, DiskFdoSetWmiDataItem)
+#pragma alloc_text(PAGE, DiskFdoExecuteWmiMethod)
+
+#pragma alloc_text(PAGE, DiskDetectFailurePrediction)
+#pragma alloc_text(PAGE, DiskEnableDisableFailurePrediction)
+#pragma alloc_text(PAGE, DiskEnableDisableFailurePredictPolling)
+#pragma alloc_text(PAGE, DiskReadFailurePredictStatus)
+#pragma alloc_text(PAGE, DiskReadFailurePredictData)
+#pragma alloc_text(PAGE, DiskReadFailurePredictThresholds)
+#pragma alloc_text(PAGE, DiskGetIdentifyInfo)
+#pragma alloc_text(PAGE, DiskReadSmartLog)
+#pragma alloc_text(PAGE, DiskWriteSmartLog)
+#pragma alloc_text(PAGE, DiskPerformSmartCommand)
+#pragma alloc_text(PAGE, DiskSendFailurePredictIoctl)
+#pragma alloc_text(PAGE, DiskReregWorker)
+#pragma alloc_text(PAGE, DiskInitializeReregistration)
+
+#if (NTDDI_VERSION >= NTDDI_WINBLUE)
+#pragma alloc_text(PAGE, DiskGetModePage)
+#pragma alloc_text(PAGE, DiskEnableInfoExceptions)
+#endif // (NTDDI_VERSION >= NTDDI_WINBLUE)
+
+#endif
+
+
+//
+// Note:
+// Some port drivers assume that the SENDCMDINPARAMS structure will always be atleast
+// sizeof(SENDCMDINPARAMS). So do not adjust for the [pBuffer] if it isn't being used
+//
+
+//
+// SMART/IDE specific routines
+//
+
+//
+// Read SMART data attributes.
+// SrbControl should be : sizeof(SRB_IO_CONTROL) + MAX[ sizeof(SENDCMDINPARAMS), sizeof(SENDCMDOUTPARAMS) - 1 + READ_ATTRIBUTE_BUFFER_SIZE ]
+// Attribute data returned at &SendCmdOutParams->bBuffer[0]
+//
+#define DiskReadSmartData(FdoExtension, \
+ SrbControl, \
+ BufferSize) \
+ DiskPerformSmartCommand(FdoExtension, \
+ IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS, \
+ SMART_CMD, \
+ READ_ATTRIBUTES, \
+ 0, \
+ 0, \
+ (SrbControl), \
+ (BufferSize))
+
+
+//
+// Read SMART data thresholds.
+// SrbControl should be : sizeof(SRB_IO_CONTROL) + MAX[ sizeof(SENDCMDINPARAMS), sizeof(SENDCMDOUTPARAMS) - 1 + READ_THRESHOLD_BUFFER_SIZE ]
+// Attribute data returned at &SendCmdOutParams->bBuffer[0]
+//
+#define DiskReadSmartThresholds(FdoExtension, \
+ SrbControl, \
+ BufferSize) \
+ DiskPerformSmartCommand(FdoExtension, \
+ IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS, \
+ SMART_CMD, \
+ READ_THRESHOLDS, \
+ 0, \
+ 0, \
+ (SrbControl), \
+ (BufferSize))
+
+
+//
+// Read SMART status
+// SrbControl should be : sizeof(SRB_IO_CONTROL) + MAX[ sizeof(SENDCMDINPARAMS), sizeof(SENDCMDOUTPARAMS) - 1 + sizeof(IDEREGS) ]
+// Failure predicted if SendCmdOutParams->bBuffer[3] == 0xf4 and SendCmdOutParams->bBuffer[4] == 0x2c
+//
+#define DiskReadSmartStatus(FdoExtension, \
+ SrbControl, \
+ BufferSize) \
+ DiskPerformSmartCommand(FdoExtension, \
+ IOCTL_SCSI_MINIPORT_RETURN_STATUS, \
+ SMART_CMD, \
+ RETURN_SMART_STATUS, \
+ 0, \
+ 0, \
+ (SrbControl), \
+ (BufferSize))
+
+
+//
+// Read disks IDENTIFY data
+// SrbControl should be : sizeof(SRB_IO_CONTROL) + MAX[ sizeof(SENDCMDINPARAMS), sizeof(SENDCMDOUTPARAMS) - 1 + IDENTIFY_BUFFER_SIZE ]
+// Identify data returned at &SendCmdOutParams->bBuffer[0]
+//
+#define DiskGetIdentifyData(FdoExtension, \
+ SrbControl, \
+ BufferSize) \
+ DiskPerformSmartCommand(FdoExtension, \
+ IOCTL_SCSI_MINIPORT_IDENTIFY, \
+ ID_CMD, \
+ 0, \
+ 0, \
+ 0, \
+ (SrbControl), \
+ (BufferSize))
+
+
+//
+// Enable SMART
+//
+_inline NTSTATUS
+DiskEnableSmart(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ )
+{
+ UCHAR srbControl[sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS)] = {0};
+ ULONG bufferSize = sizeof(srbControl);
+
+ return DiskPerformSmartCommand(FdoExtension,
+ IOCTL_SCSI_MINIPORT_ENABLE_SMART,
+ SMART_CMD,
+ ENABLE_SMART,
+ 0,
+ 0,
+ (PSRB_IO_CONTROL)srbControl,
+ &bufferSize);
+}
+
+
+//
+// Disable SMART
+//
+_inline NTSTATUS
+DiskDisableSmart(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ )
+{
+ UCHAR srbControl[sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS)] = {0};
+ ULONG bufferSize = sizeof(srbControl);
+
+ return DiskPerformSmartCommand(FdoExtension,
+ IOCTL_SCSI_MINIPORT_DISABLE_SMART,
+ SMART_CMD,
+ DISABLE_SMART,
+ 0,
+ 0,
+ (PSRB_IO_CONTROL)srbControl,
+ &bufferSize);
+}
+
+
+//
+// Enable Attribute Autosave
+//
+_inline NTSTATUS
+DiskEnableSmartAttributeAutosave(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ )
+{
+ UCHAR srbControl[sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS)] = {0};
+ ULONG bufferSize = sizeof(srbControl);
+
+ return DiskPerformSmartCommand(FdoExtension,
+ IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE,
+ SMART_CMD,
+ ENABLE_DISABLE_AUTOSAVE,
+ 0xf1,
+ 0,
+ (PSRB_IO_CONTROL)srbControl,
+ &bufferSize);
+}
+
+
+//
+// Disable Attribute Autosave
+//
+_inline NTSTATUS
+DiskDisableSmartAttributeAutosave(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ )
+{
+ UCHAR srbControl[sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS)] = {0};
+ ULONG bufferSize = sizeof(srbControl);
+
+ return DiskPerformSmartCommand(FdoExtension,
+ IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE,
+ SMART_CMD,
+ ENABLE_DISABLE_AUTOSAVE,
+ 0x00,
+ 0,
+ (PSRB_IO_CONTROL)srbControl,
+ &bufferSize);
+}
+
+
+//
+// Initialize execution of SMART online diagnostics
+//
+_inline NTSTATUS
+DiskExecuteSmartDiagnostics(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ UCHAR Subcommand
+ )
+{
+ UCHAR srbControl[sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS)] = {0};
+ ULONG bufferSize = sizeof(srbControl);
+
+ return DiskPerformSmartCommand(FdoExtension,
+ IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS,
+ SMART_CMD,
+ EXECUTE_OFFLINE_DIAGS,
+ 0,
+ Subcommand,
+ (PSRB_IO_CONTROL)srbControl,
+ &bufferSize);
+}
+
+
+NTSTATUS
+DiskReadSmartLog(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN UCHAR SectorCount,
+ IN UCHAR LogAddress,
+ OUT PUCHAR Buffer
+ )
+{
+ PSRB_IO_CONTROL srbControl;
+ NTSTATUS status;
+ PSENDCMDOUTPARAMS sendCmdOutParams;
+ ULONG logSize, bufferSize;
+
+ PAGED_CODE();
+
+ logSize = SectorCount * SMART_LOG_SECTOR_SIZE;
+ bufferSize = sizeof(SRB_IO_CONTROL) + max( sizeof(SENDCMDINPARAMS), sizeof(SENDCMDOUTPARAMS) - 1 + logSize );
+
+ srbControl = ExAllocatePoolWithTag(NonPagedPoolNx,
+ bufferSize,
+ DISK_TAG_SMART);
+
+ if (srbControl != NULL)
+ {
+ status = DiskPerformSmartCommand(FdoExtension,
+ IOCTL_SCSI_MINIPORT_READ_SMART_LOG,
+ SMART_CMD,
+ SMART_READ_LOG,
+ SectorCount,
+ LogAddress,
+ srbControl,
+ &bufferSize);
+
+ if (NT_SUCCESS(status))
+ {
+ sendCmdOutParams = (PSENDCMDOUTPARAMS)((PUCHAR)srbControl +
+ sizeof(SRB_IO_CONTROL));
+ RtlCopyMemory(Buffer,
+ &sendCmdOutParams->bBuffer[0],
+ logSize);
+ }
+
+ FREE_POOL(srbControl);
+ } else {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+ return(status);
+}
+
+
+NTSTATUS
+DiskWriteSmartLog(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN UCHAR SectorCount,
+ IN UCHAR LogAddress,
+ IN PUCHAR Buffer
+ )
+{
+ PSRB_IO_CONTROL srbControl;
+ NTSTATUS status;
+ PSENDCMDINPARAMS sendCmdInParams;
+ ULONG logSize, bufferSize;
+
+ PAGED_CODE();
+
+ logSize = SectorCount * SMART_LOG_SECTOR_SIZE;
+ bufferSize = sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS) - 1 +
+ logSize;
+
+ srbControl = ExAllocatePoolWithTag(NonPagedPoolNx,
+ bufferSize,
+ DISK_TAG_SMART);
+
+ if (srbControl != NULL)
+ {
+ sendCmdInParams = (PSENDCMDINPARAMS)((PUCHAR)srbControl +
+ sizeof(SRB_IO_CONTROL));
+ RtlCopyMemory(&sendCmdInParams->bBuffer[0],
+ Buffer,
+ logSize);
+ status = DiskPerformSmartCommand(FdoExtension,
+ IOCTL_SCSI_MINIPORT_WRITE_SMART_LOG,
+ SMART_CMD,
+ SMART_WRITE_LOG,
+ SectorCount,
+ LogAddress,
+ srbControl,
+ &bufferSize);
+
+ FREE_POOL(srbControl);
+ } else {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+ return(status);
+}
+
+
+NTSTATUS
+DiskPerformSmartCommand(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN ULONG SrbControlCode,
+ IN UCHAR Command,
+ IN UCHAR Feature,
+ IN UCHAR SectorCount,
+ IN UCHAR SectorNumber,
+ IN OUT PSRB_IO_CONTROL SrbControl,
+ OUT PULONG BufferSize
+ )
+/*++
+
+Routine Description:
+
+ This routine will perform some SMART command
+
+Arguments:
+
+ FdoExtension is the FDO device extension
+
+ SrbControlCode is the SRB control code to use for the request
+
+ Command is the SMART command to be executed. It may be SMART_CMD or
+ ID_CMD.
+
+ Feature is the value to place in the IDE feature register.
+
+ SectorCount is the value to place in the IDE SectorCount register
+
+ SrbControl is the buffer used to build the SRB_IO_CONTROL and pass
+ any input parameters. It also returns the output parameters.
+
+ *BufferSize on entry has total size of SrbControl and on return has
+ the size used in SrbControl.
+
+
+
+Return Value:
+
+ status
+
+--*/
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = (PCOMMON_DEVICE_EXTENSION)FdoExtension;
+ PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
+ PUCHAR buffer;
+ PSENDCMDINPARAMS cmdInParameters;
+ NTSTATUS status;
+ ULONG availableBufferSize;
+ KEVENT event;
+ PIRP irp;
+ IO_STATUS_BLOCK ioStatus = { 0 };
+ SCSI_REQUEST_BLOCK srb = {0};
+ LARGE_INTEGER startingOffset;
+ ULONG length;
+ PIO_STACK_LOCATION irpStack;
+ UCHAR srbExBuffer[CLASS_SRBEX_NO_SRBEX_DATA_BUFFER_SIZE] = {0};
+ PSTORAGE_REQUEST_BLOCK srbEx = (PSTORAGE_REQUEST_BLOCK)srbExBuffer;
+ PSTOR_ADDR_BTL8 storAddrBtl8;
+
+ PAGED_CODE();
+
+ //
+ // Point to the 'buffer' portion of the SRB_CONTROL and compute how
+ // much room we have left in the srb control. Abort if the buffer
+ // isn't at least the size of SRB_IO_CONTROL.
+ //
+
+ buffer = (PUCHAR)SrbControl + sizeof(SRB_IO_CONTROL);
+
+ cmdInParameters = (PSENDCMDINPARAMS)buffer;
+
+ if (*BufferSize >= sizeof(SRB_IO_CONTROL)) {
+ availableBufferSize = *BufferSize - sizeof(SRB_IO_CONTROL);
+ } else {
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+#if DBG
+
+ //
+ // Ensure control codes and buffer lengths passed are correct
+ //
+ {
+ ULONG controlCode = 0;
+ ULONG lengthNeeded = sizeof(SENDCMDINPARAMS);
+
+ if (Command == SMART_CMD)
+ {
+ switch (Feature)
+ {
+ case ENABLE_SMART:
+ {
+ controlCode = IOCTL_SCSI_MINIPORT_ENABLE_SMART;
+ break;
+ }
+
+ case DISABLE_SMART:
+ {
+ controlCode = IOCTL_SCSI_MINIPORT_DISABLE_SMART;
+ break;
+ }
+
+ case RETURN_SMART_STATUS:
+ {
+ controlCode = IOCTL_SCSI_MINIPORT_RETURN_STATUS;
+ lengthNeeded = max( lengthNeeded, sizeof(SENDCMDOUTPARAMS) - 1 + sizeof(IDEREGS) );
+ break;
+ }
+
+ case ENABLE_DISABLE_AUTOSAVE:
+ {
+ controlCode = IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE;
+ break;
+ }
+
+ case SAVE_ATTRIBUTE_VALUES:
+ {
+ controlCode = IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES;
+ break;
+ }
+
+
+ case EXECUTE_OFFLINE_DIAGS:
+ {
+ controlCode = IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS;
+ break;
+ }
+
+ case READ_ATTRIBUTES:
+ {
+ controlCode = IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS;
+ lengthNeeded = max( lengthNeeded, sizeof(SENDCMDOUTPARAMS) - 1 + READ_ATTRIBUTE_BUFFER_SIZE );
+ break;
+ }
+
+ case READ_THRESHOLDS:
+ {
+ controlCode = IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS;
+ lengthNeeded = max( lengthNeeded, sizeof(SENDCMDOUTPARAMS) - 1 + READ_THRESHOLD_BUFFER_SIZE );
+ break;
+ }
+
+ case SMART_READ_LOG:
+ {
+ controlCode = IOCTL_SCSI_MINIPORT_READ_SMART_LOG;
+ lengthNeeded = max( lengthNeeded, sizeof(SENDCMDOUTPARAMS) - 1 + (SectorCount * SMART_LOG_SECTOR_SIZE) );
+ break;
+ }
+
+ case SMART_WRITE_LOG:
+ {
+ controlCode = IOCTL_SCSI_MINIPORT_WRITE_SMART_LOG;
+ lengthNeeded = lengthNeeded - 1 + (SectorCount * SMART_LOG_SECTOR_SIZE);
+ break;
+ }
+
+ }
+
+ } else if (Command == ID_CMD) {
+
+ controlCode = IOCTL_SCSI_MINIPORT_IDENTIFY;
+ lengthNeeded = max( lengthNeeded, sizeof(SENDCMDOUTPARAMS) - 1 + IDENTIFY_BUFFER_SIZE );
+
+ } else {
+
+ NT_ASSERT(FALSE);
+ }
+
+ NT_ASSERT(controlCode == SrbControlCode);
+ NT_ASSERT(availableBufferSize >= lengthNeeded);
+ }
+
+#endif
+
+ //
+ // Build SrbControl and input to SMART command
+ //
+ SrbControl->HeaderLength = sizeof(SRB_IO_CONTROL);
+ RtlMoveMemory (SrbControl->Signature, "SCSIDISK", 8);
+ SrbControl->Timeout = FdoExtension->TimeOutValue;
+ SrbControl->Length = availableBufferSize;
+ SrbControl->ControlCode = SrbControlCode;
+
+ cmdInParameters->cBufferSize = sizeof(SENDCMDINPARAMS);
+ cmdInParameters->bDriveNumber = diskData->ScsiAddress.TargetId;
+ cmdInParameters->irDriveRegs.bFeaturesReg = Feature;
+ cmdInParameters->irDriveRegs.bSectorCountReg = SectorCount;
+ cmdInParameters->irDriveRegs.bSectorNumberReg = SectorNumber;
+ cmdInParameters->irDriveRegs.bCylLowReg = SMART_CYL_LOW;
+ cmdInParameters->irDriveRegs.bCylHighReg = SMART_CYL_HI;
+ cmdInParameters->irDriveRegs.bCommandReg = Command;
+
+ //
+ // Create and send irp
+ //
+ KeInitializeEvent(&event, NotificationEvent, FALSE);
+
+ startingOffset.QuadPart = (LONGLONG) 1;
+
+ length = SrbControl->HeaderLength + SrbControl->Length;
+
+ irp = IoBuildSynchronousFsdRequest(
+ IRP_MJ_SCSI,
+ commonExtension->LowerDeviceObject,
+ SrbControl,
+ length,
+ &startingOffset,
+ &event,
+ &ioStatus);
+
+ if (irp == NULL) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ irpStack = IoGetNextIrpStackLocation(irp);
+
+ //
+ // Set major and minor codes.
+ //
+
+ irpStack->MajorFunction = IRP_MJ_SCSI;
+ irpStack->MinorFunction = 1;
+
+ //
+ // Fill in SRB fields.
+ //
+
+ if (FdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ irpStack->Parameters.Others.Argument1 = srbEx;
+
+ //
+ // Set up STORAGE_REQUEST_BLOCK fields
+ //
+
+ srbEx->Length = FIELD_OFFSET(STORAGE_REQUEST_BLOCK, Signature);
+ srbEx->Function = SRB_FUNCTION_STORAGE_REQUEST_BLOCK;
+ srbEx->Signature = SRB_SIGNATURE;
+ srbEx->Version = STORAGE_REQUEST_BLOCK_VERSION_1;
+ srbEx->SrbLength = sizeof(srbExBuffer);
+ srbEx->SrbFunction = SRB_FUNCTION_IO_CONTROL;
+ srbEx->RequestPriority = IoGetIoPriorityHint(irp);
+ srbEx->AddressOffset = sizeof(STORAGE_REQUEST_BLOCK);
+
+ srbEx->SrbFlags = FdoExtension->SrbFlags;
+ SET_FLAG(srbEx->SrbFlags, SRB_FLAGS_DATA_IN);
+ SET_FLAG(srbEx->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
+ SET_FLAG(srbEx->SrbFlags, SRB_FLAGS_NO_KEEP_AWAKE);
+
+ srbEx->RequestAttribute = SRB_SIMPLE_TAG_REQUEST;
+ srbEx->RequestTag = SP_UNTAGGED;
+
+ srbEx->OriginalRequest = irp;
+
+ //
+ // Set timeout to requested value.
+ //
+
+ srbEx->TimeOutValue = SrbControl->Timeout;
+
+ //
+ // Set the data buffer.
+ //
+
+ srbEx->DataBuffer = SrbControl;
+ srbEx->DataTransferLength = length;
+
+ //
+ // Set up address fields
+ //
+
+ storAddrBtl8 = (PSTOR_ADDR_BTL8) ((PUCHAR)srbEx + srbEx->AddressOffset);
+ storAddrBtl8->Type = STOR_ADDRESS_TYPE_BTL8;
+ storAddrBtl8->AddressLength = STOR_ADDR_BTL8_ADDRESS_LENGTH;
+ storAddrBtl8->Path = diskData->ScsiAddress.PathId;
+ storAddrBtl8->Target = diskData->ScsiAddress.TargetId;
+ storAddrBtl8->Lun = srb.Lun = diskData->ScsiAddress.Lun;
+
+ } else {
+ irpStack->Parameters.Others.Argument1 = &srb;
+
+ srb.PathId = diskData->ScsiAddress.PathId;
+ srb.TargetId = diskData->ScsiAddress.TargetId;
+ srb.Lun = diskData->ScsiAddress.Lun;
+
+ srb.Function = SRB_FUNCTION_IO_CONTROL;
+ srb.Length = sizeof(SCSI_REQUEST_BLOCK);
+
+ srb.SrbFlags = FdoExtension->SrbFlags;
+ SET_FLAG(srb.SrbFlags, SRB_FLAGS_DATA_IN);
+ SET_FLAG(srb.SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
+ SET_FLAG(srb.SrbFlags, SRB_FLAGS_NO_KEEP_AWAKE);
+
+ srb.QueueAction = SRB_SIMPLE_TAG_REQUEST;
+ srb.QueueTag = SP_UNTAGGED;
+
+ srb.OriginalRequest = irp;
+
+ //
+ // Set timeout to requested value.
+ //
+
+ srb.TimeOutValue = SrbControl->Timeout;
+
+ //
+ // Set the data buffer.
+ //
+
+ srb.DataBuffer = SrbControl;
+ srb.DataTransferLength = length;
+ }
+
+ //
+ // Flush the data buffer for output. This will insure that the data is
+ // written back to memory. Since the data-in flag is the the port driver
+ // will flush the data again for input which will ensure the data is not
+ // in the cache.
+ //
+
+ KeFlushIoBuffers(irp->MdlAddress, FALSE, TRUE);
+
+ //
+ // Call port driver to handle this request.
+ //
+
+ status = IoCallDriver(commonExtension->LowerDeviceObject, irp);
+
+ if (status == STATUS_PENDING) {
+ KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
+ status = ioStatus.Status;
+ }
+
+ return status;
+}
+
+
+NTSTATUS
+DiskGetIdentifyInfo(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ PBOOLEAN SupportSmart
+ )
+{
+ UCHAR outBuffer[sizeof(SRB_IO_CONTROL) + max( sizeof(SENDCMDINPARAMS), sizeof(SENDCMDOUTPARAMS) - 1 + IDENTIFY_BUFFER_SIZE )] = {0};
+ ULONG outBufferSize = sizeof(outBuffer);
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ status = DiskGetIdentifyData(FdoExtension,
+ (PSRB_IO_CONTROL)outBuffer,
+ &outBufferSize);
+
+ if (NT_SUCCESS(status))
+ {
+ PUSHORT identifyData = (PUSHORT)&(outBuffer[sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDOUTPARAMS) - 1]);
+ USHORT commandSetSupported = identifyData[82];
+
+ *SupportSmart = ((commandSetSupported != 0xffff) &&
+ (commandSetSupported != 0) &&
+ ((commandSetSupported & 1) == 1));
+ } else {
+ *SupportSmart = FALSE;
+ }
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DiskGetIdentifyInfo: SMART %s supported for device %p, status %lx\n",
+ *SupportSmart ? "is" : "is not",
+ FdoExtension->DeviceObject,
+ status));
+
+ return status;
+}
+
+
+//
+// FP Ioctl specific routines
+//
+
+NTSTATUS
+DiskSendFailurePredictIoctl(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ PSTORAGE_PREDICT_FAILURE checkFailure
+ )
+{
+ KEVENT event;
+ PDEVICE_OBJECT deviceObject;
+ IO_STATUS_BLOCK ioStatus = { 0 };
+ PIRP irp;
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ KeInitializeEvent(&event, SynchronizationEvent, FALSE);
+
+ deviceObject = IoGetAttachedDeviceReference(FdoExtension->DeviceObject);
+
+ irp = IoBuildDeviceIoControlRequest(
+ IOCTL_STORAGE_PREDICT_FAILURE,
+ deviceObject,
+ NULL,
+ 0,
+ checkFailure,
+ sizeof(STORAGE_PREDICT_FAILURE),
+ FALSE,
+ &event,
+ &ioStatus);
+
+ if (irp != NULL)
+ {
+ status = IoCallDriver(deviceObject, irp);
+ if (status == STATUS_PENDING)
+ {
+ KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
+ status = ioStatus.Status;
+ }
+
+ } else {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ ObDereferenceObject(deviceObject);
+
+ return status;
+}
+
+#if (NTDDI_VERSION >= NTDDI_WINBLUE)
+
+NTSTATUS
+DiskGetModePage(
+ _In_ PDEVICE_OBJECT Fdo,
+ _In_ UCHAR PageMode,
+ _In_ UCHAR PageControl,
+ _In_ PMODE_PARAMETER_HEADER ModeData,
+ _Inout_ PULONG ModeDataSize,
+ _Out_ PVOID* PageData
+ )
+{
+ ULONG size = 0;
+ PVOID pageData = NULL;
+
+ PAGED_CODE();
+
+ if (ModeData == NULL ||
+ ModeDataSize == NULL ||
+ *ModeDataSize < sizeof(MODE_PARAMETER_HEADER) ||
+ PageData == NULL) {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ RtlZeroMemory (ModeData, *ModeDataSize);
+
+ size = ClassModeSenseEx(Fdo,
+ (PCHAR) ModeData,
+ *ModeDataSize,
+ PageMode,
+ PageControl);
+
+ if (size < sizeof(MODE_PARAMETER_HEADER)) {
+
+ //
+ // Retry the request in case of a check condition.
+ //
+ size = ClassModeSenseEx(Fdo,
+ (PCHAR) ModeData,
+ *ModeDataSize,
+ PageMode,
+ PageControl);
+
+ if (size < sizeof(MODE_PARAMETER_HEADER)) {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_WMI, "DiskGetModePage: Mode Sense for Page Mode %d with Page Control %d failed\n",
+ PageMode, PageControl));
+ *ModeDataSize = 0;
+ return STATUS_IO_DEVICE_ERROR;
+ }
+ }
+
+ //
+ // If the length is greater than length indicated by the mode data reset
+ // the data to the mode data.
+ //
+ if (size > (ULONG) (ModeData->ModeDataLength + 1)) {
+ size = ModeData->ModeDataLength + 1;
+ }
+
+ *ModeDataSize = size;
+
+ //
+ // Find the mode page
+ //
+ pageData = ClassFindModePage((PCHAR) ModeData,
+ size,
+ PageMode,
+ TRUE);
+
+ if (pageData) {
+ *PageData = pageData;
+ return STATUS_SUCCESS;
+ } else {
+ *PageData = NULL;
+ return STATUS_NOT_SUPPORTED;
+ }
+}
+
+NTSTATUS
+DiskEnableInfoExceptions(
+ _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ _In_ BOOLEAN Enable
+ )
+{
+ PDISK_DATA diskData = (PDISK_DATA)(FdoExtension->CommonExtension.DriverData);
+ NTSTATUS status = STATUS_NOT_SUPPORTED;
+ PMODE_PARAMETER_HEADER modeData;
+ PMODE_INFO_EXCEPTIONS pageData;
+ MODE_INFO_EXCEPTIONS changeablePageData;
+ ULONG modeDataSize;
+
+ PAGED_CODE();
+
+ modeDataSize = MODE_DATA_SIZE;
+
+ modeData = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
+ modeDataSize,
+ DISK_TAG_INFO_EXCEPTION);
+
+ if (modeData == NULL) {
+
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_WMI, "DiskEnableInfoExceptions: Unable to allocate mode "
+ "data buffer\n"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ //
+ // First see which data is actually changeable.
+ //
+ status = DiskGetModePage(FdoExtension->DeviceObject,
+ MODE_PAGE_FAULT_REPORTING,
+ 1, // Page Control = 1 indicates we want changeable values.
+ modeData,
+ &modeDataSize,
+ (PVOID*)&pageData);
+
+ if (!NT_SUCCESS(status) || pageData == NULL) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "DiskEnableInfoExceptions: does NOT support SMART for device %p\n",
+ FdoExtension->DeviceObject));
+ FREE_POOL(modeData);
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "DiskEnableInfoExceptions: DOES support SMART for device %p\n",
+ FdoExtension->DeviceObject));
+
+ //
+ // At the very least, the DEXCPT bit must be changeable.
+ // If it's not, bail out now.
+ //
+ if (pageData->Dexcpt == 0) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "DiskEnableInfoExceptions: does NOT support DEXCPT bit for device %p\n",
+ FdoExtension->DeviceObject));
+ FREE_POOL(modeData);
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ //
+ // Cache away which values are changeable.
+ //
+ RtlCopyMemory(&changeablePageData, pageData, sizeof(MODE_INFO_EXCEPTIONS));
+
+ //
+ // Now get the current values.
+ //
+ status = DiskGetModePage(FdoExtension->DeviceObject,
+ MODE_PAGE_FAULT_REPORTING,
+ 0, // Page Control = 0 indicates we want current values.
+ modeData,
+ &modeDataSize,
+ (PVOID*)&pageData);
+
+ if (!NT_SUCCESS(status) || pageData == NULL) {
+ //
+ // At this point we know the device supports this mode page so
+ // assert if something goes wrong here.
+ //
+ NT_ASSERT(NT_SUCCESS(status) && pageData);
+ FREE_POOL(modeData);
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ //
+ // If the device is currently configured to not report any informational
+ // exceptions and we cannot change the value of that field, there's
+ // nothing to be done.
+ //
+ if (pageData->ReportMethod == 0 && changeablePageData.ReportMethod == 0) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "DiskEnableInfoExceptions: MRIE field is 0 and is not changeable for device %p\n",
+ FdoExtension->DeviceObject));
+ FREE_POOL(modeData);
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ //
+ // If the PERF bit is changeable, set it now.
+ //
+ if (changeablePageData.Perf) {
+ pageData->Perf = diskData->AllowFPPerfHit ? 0 : 1;
+ }
+
+ //
+ // If the MRIE field is changeable, set it to 4 so that informational
+ // exceptions get reported with the "Recovered Error" sense key.
+ //
+ if (changeablePageData.ReportMethod) {
+ pageData->ReportMethod = 4;
+ }
+
+ //
+ // Finally, set the DEXCPT bit appropriately to enable/disable
+ // informational exception reporting and send the Mode Select.
+ //
+ pageData->Dexcpt = !Enable;
+
+ status = ClassModeSelect(FdoExtension->DeviceObject,
+ (PCHAR)modeData,
+ modeDataSize,
+ pageData->PSBit);
+
+ //
+ // Update the failure prediction state. Note that for this particular
+ // mode FailurePredictionNone is used when it's not enabled.
+ //
+ if (NT_SUCCESS(status)) {
+ if (Enable) {
+ diskData->FailurePredictionCapability = FailurePredictionSense;
+ diskData->FailurePredictionEnabled = TRUE;
+ } else {
+ diskData->FailurePredictionCapability = FailurePredictionNone;
+ diskData->FailurePredictionEnabled = FALSE;
+ }
+ }
+
+ FREE_POOL(modeData);
+
+ return status;
+}
+#endif
+
+
+//
+// FP type independent routines
+//
+
+NTSTATUS
+DiskEnableDisableFailurePrediction(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ BOOLEAN Enable
+ )
+/*++
+
+Routine Description:
+
+ Enable or disable failure prediction at the hardware level
+
+Arguments:
+
+ FdoExtension
+
+ Enable
+
+Return Value:
+
+ NT Status
+
+--*/
+{
+ NTSTATUS status;
+ PCOMMON_DEVICE_EXTENSION commonExtension = &(FdoExtension->CommonExtension);
+ PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
+
+ PAGED_CODE();
+
+ switch(diskData->FailurePredictionCapability)
+ {
+ case FailurePredictionSmart:
+ {
+ if (Enable)
+ {
+ status = DiskEnableSmart(FdoExtension);
+ } else {
+ status = DiskDisableSmart(FdoExtension);
+ }
+
+ if (NT_SUCCESS(status)) {
+ diskData->FailurePredictionEnabled = Enable;
+ }
+
+ break;
+ }
+
+ case FailurePredictionSense:
+ case FailurePredictionIoctl:
+ {
+ //
+ // We assume that the drive is already setup properly for
+ // failure prediction
+ //
+ status = STATUS_SUCCESS;
+ break;
+ }
+
+ default:
+ {
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ }
+ }
+ return status;
+}
+
+
+NTSTATUS
+DiskEnableDisableFailurePredictPolling(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ BOOLEAN Enable,
+ ULONG PollTimeInSeconds
+ )
+/*++
+
+Routine Description:
+
+ Enable or disable polling for hardware failure detection
+
+Arguments:
+
+ FdoExtension
+
+ Enable
+
+ PollTimeInSeconds - if 0 then no change to current polling timer
+
+Return Value:
+
+ NT Status
+
+--*/
+{
+ NTSTATUS status;
+ PCOMMON_DEVICE_EXTENSION commonExtension = (PCOMMON_DEVICE_EXTENSION)FdoExtension;
+ PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
+
+ PAGED_CODE();
+
+ if (Enable)
+ {
+ status = DiskEnableDisableFailurePrediction(FdoExtension,
+ Enable);
+ } else {
+ status = STATUS_SUCCESS;
+ }
+
+ if (NT_SUCCESS(status))
+ {
+ status = ClassSetFailurePredictionPoll(FdoExtension,
+ Enable ? diskData->FailurePredictionCapability :
+ FailurePredictionNone,
+ PollTimeInSeconds);
+
+ //
+ // Even if this failed we do not want to disable FP on the
+ // hardware. FP is only ever disabled on the hardware by
+ // specific command of the user.
+ //
+ }
+
+ return status;
+}
+
+
+NTSTATUS
+DiskReadFailurePredictStatus(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ PSTORAGE_FAILURE_PREDICT_STATUS DiskSmartStatus
+ )
+/*++
+
+Routine Description:
+
+ Obtains current failure prediction status
+
+Arguments:
+
+ FdoExtension
+
+ DiskSmartStatus
+
+Return Value:
+
+ NT Status
+
+--*/
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = (PCOMMON_DEVICE_EXTENSION)FdoExtension;
+ PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ DiskSmartStatus->PredictFailure = FALSE;
+
+ switch(diskData->FailurePredictionCapability)
+ {
+ case FailurePredictionSmart:
+ {
+ UCHAR outBuffer[sizeof(SRB_IO_CONTROL) + max( sizeof(SENDCMDINPARAMS), sizeof(SENDCMDOUTPARAMS) - 1 + sizeof(IDEREGS) )] = {0};
+ ULONG outBufferSize = sizeof(outBuffer);
+ PSENDCMDOUTPARAMS cmdOutParameters;
+
+ status = DiskReadSmartStatus(FdoExtension,
+ (PSRB_IO_CONTROL)outBuffer,
+ &outBufferSize);
+
+ if (NT_SUCCESS(status))
+ {
+ cmdOutParameters = (PSENDCMDOUTPARAMS)(outBuffer +
+ sizeof(SRB_IO_CONTROL));
+
+ DiskSmartStatus->Reason = 0; // Unknown;
+ DiskSmartStatus->PredictFailure = ((cmdOutParameters->bBuffer[3] == 0xf4) &&
+ (cmdOutParameters->bBuffer[4] == 0x2c));
+ }
+ break;
+ }
+
+ case FailurePredictionSense:
+ {
+ DiskSmartStatus->Reason = FdoExtension->FailureReason;
+ DiskSmartStatus->PredictFailure = FdoExtension->FailurePredicted;
+ status = STATUS_SUCCESS;
+ break;
+ }
+
+ case FailurePredictionIoctl:
+ case FailurePredictionNone:
+ default:
+ {
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ break;
+ }
+ }
+
+ return status;
+}
+
+
+NTSTATUS
+DiskReadFailurePredictData(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ PSTORAGE_FAILURE_PREDICT_DATA DiskSmartData
+ )
+/*++
+
+Routine Description:
+
+ Obtains current failure prediction data. Not available for
+ FAILURE_PREDICT_SENSE types.
+
+Arguments:
+
+ FdoExtension
+
+ DiskSmartData
+
+Return Value:
+
+ NT Status
+
+--*/
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = (PCOMMON_DEVICE_EXTENSION)FdoExtension;
+ PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ switch(diskData->FailurePredictionCapability)
+ {
+ case FailurePredictionSmart:
+ {
+ PUCHAR outBuffer;
+ ULONG outBufferSize;
+ PSENDCMDOUTPARAMS cmdOutParameters;
+
+ outBufferSize = sizeof(SRB_IO_CONTROL) + max( sizeof(SENDCMDINPARAMS), sizeof(SENDCMDOUTPARAMS) - 1 + READ_ATTRIBUTE_BUFFER_SIZE );
+
+ outBuffer = ExAllocatePoolWithTag(NonPagedPoolNx,
+ outBufferSize,
+ DISK_TAG_SMART);
+
+ if (outBuffer != NULL)
+ {
+ status = DiskReadSmartData(FdoExtension,
+ (PSRB_IO_CONTROL)outBuffer,
+ &outBufferSize);
+
+ if (NT_SUCCESS(status))
+ {
+ cmdOutParameters = (PSENDCMDOUTPARAMS)(outBuffer +
+ sizeof(SRB_IO_CONTROL));
+
+ DiskSmartData->Length = READ_ATTRIBUTE_BUFFER_SIZE;
+ RtlCopyMemory(DiskSmartData->VendorSpecific,
+ cmdOutParameters->bBuffer,
+ min(READ_ATTRIBUTE_BUFFER_SIZE, sizeof(DiskSmartData->VendorSpecific)));
+ }
+ FREE_POOL(outBuffer);
+ } else {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ break;
+ }
+
+ case FailurePredictionSense:
+ {
+ DiskSmartData->Length = sizeof(ULONG);
+ *((PULONG)DiskSmartData->VendorSpecific) = FdoExtension->FailureReason;
+
+ status = STATUS_SUCCESS;
+ break;
+ }
+
+ case FailurePredictionIoctl:
+ case FailurePredictionNone:
+ default:
+ {
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ break;
+ }
+ }
+
+ return status;
+}
+
+
+NTSTATUS
+DiskReadFailurePredictThresholds(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ PSTORAGE_FAILURE_PREDICT_THRESHOLDS DiskSmartThresholds
+ )
+/*++
+
+Routine Description:
+
+ Obtains current failure prediction thresholds. Not available for
+ FAILURE_PREDICT_SENSE types.
+
+Arguments:
+
+ FdoExtension
+
+ DiskSmartData
+
+Return Value:
+
+ NT Status
+
+--*/
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = (PCOMMON_DEVICE_EXTENSION)FdoExtension;
+ PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ switch(diskData->FailurePredictionCapability)
+ {
+ case FailurePredictionSmart:
+ {
+ PUCHAR outBuffer;
+ PSENDCMDOUTPARAMS cmdOutParameters;
+ ULONG outBufferSize;
+
+ outBufferSize = sizeof(SRB_IO_CONTROL) + max( sizeof(SENDCMDINPARAMS), sizeof(SENDCMDOUTPARAMS) - 1 + READ_THRESHOLD_BUFFER_SIZE );
+
+ outBuffer = ExAllocatePoolWithTag(NonPagedPoolNx,
+ outBufferSize,
+ DISK_TAG_SMART);
+
+ if (outBuffer != NULL)
+ {
+ status = DiskReadSmartThresholds(FdoExtension,
+ (PSRB_IO_CONTROL)outBuffer,
+ &outBufferSize);
+
+ if (NT_SUCCESS(status))
+ {
+ cmdOutParameters = (PSENDCMDOUTPARAMS)(outBuffer +
+ sizeof(SRB_IO_CONTROL));
+
+ RtlCopyMemory(DiskSmartThresholds->VendorSpecific,
+ cmdOutParameters->bBuffer,
+ min(READ_THRESHOLD_BUFFER_SIZE, sizeof(DiskSmartThresholds->VendorSpecific)));
+ }
+ FREE_POOL(outBuffer);
+ } else {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ break;
+ }
+
+ case FailurePredictionSense:
+ case FailurePredictionIoctl:
+ case FailurePredictionNone:
+ default:
+ {
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ break;
+ }
+ }
+
+ return status;
+}
+
+
+VOID
+DiskReregWorker(
+ IN PDEVICE_OBJECT DevObject,
+ IN PVOID Context
+ )
+{
+ PDISKREREGREQUEST reregRequest;
+ NTSTATUS status;
+ PDEVICE_OBJECT deviceObject;
+ PIRP irp;
+
+ PAGED_CODE();
+ UNREFERENCED_PARAMETER(DevObject);
+
+ NT_ASSERT(Context != NULL);
+ _Analysis_assume_(Context != NULL);
+
+ do
+ {
+ reregRequest = (PDISKREREGREQUEST)ExInterlockedPopEntryList(
+ &DiskReregHead,
+ &DiskReregSpinlock);
+
+ if (reregRequest != NULL)
+ {
+ deviceObject = reregRequest->DeviceObject;
+ irp = reregRequest->Irp;
+
+ status = IoWMIRegistrationControl(deviceObject,
+ WMIREG_ACTION_UPDATE_GUIDS);
+
+ //
+ // Release remove lock and free irp, now that we are done
+ // processing this
+ //
+ ClassReleaseRemoveLock(deviceObject, irp);
+
+ IoFreeMdl(irp->MdlAddress);
+ IoFreeIrp(irp);
+
+ FREE_POOL(reregRequest);
+
+ } else {
+
+ NT_ASSERTMSG("Disk Re-registration request list should not be empty", FALSE);
+
+ status = STATUS_INTERNAL_ERROR;
+ }
+
+ if (!NT_SUCCESS(status))
+ {
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "DiskReregWorker: Reregistration failed %x\n",
+ status));
+ }
+
+ } while (InterlockedDecrement(&DiskReregWorkItems));
+
+ IoFreeWorkItem((PIO_WORKITEM)Context);
+}
+
+
+NTSTATUS
+DiskInitializeReregistration(
+ VOID
+ )
+{
+ PAGED_CODE();
+
+ //
+ // Initialize the spinlock used to manage the
+ // list of disks reregistering their guids
+ //
+ KeInitializeSpinLock(&DiskReregSpinlock);
+
+ return(STATUS_SUCCESS);
+}
+
+
+NTSTATUS
+DiskPostReregisterRequest(
+ PDEVICE_OBJECT DeviceObject,
+ PIRP Irp
+ )
+{
+ PDISKREREGREQUEST reregRequest;
+ PIO_WORKITEM workItem;
+ NTSTATUS status;
+
+ workItem = IoAllocateWorkItem(DeviceObject);
+
+ if (!workItem) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ reregRequest = ExAllocatePoolWithTag(NonPagedPoolNx,
+ sizeof(DISKREREGREQUEST),
+ DISK_TAG_SMART);
+ if (reregRequest != NULL)
+ {
+ //
+ // add the disk that needs reregistration to the stack of disks
+ // to reregister. If the list is transitioning from empty to
+ // non empty then also kick off the work item so that the
+ // reregistration worker can do the reregister.
+ //
+ reregRequest->DeviceObject = DeviceObject;
+ reregRequest->Irp = Irp;
+ ExInterlockedPushEntryList(
+ &DiskReregHead,
+ &reregRequest->Next,
+ &DiskReregSpinlock);
+
+ if (InterlockedIncrement(&DiskReregWorkItems) == 1)
+ {
+ //
+ // There is no worker routine running, queue this one.
+ // When the work item runs, it will process the reregistration
+ // list.
+ //
+
+ IoQueueWorkItem(workItem,
+ DiskReregWorker,
+ DelayedWorkQueue,
+ workItem);
+ } else {
+
+ //
+ // There is a worker routine already running, so we
+ // can free this unused work item.
+ //
+
+ IoFreeWorkItem(workItem);
+ }
+
+ status = STATUS_SUCCESS;
+
+ } else {
+
+ IoFreeWorkItem(workItem);
+ TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "DiskPostReregisterRequest: could not allocate reregRequest for %p\n",
+ DeviceObject));
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ return(status);
+}
+
+
+NTSTATUS
+DiskInfoExceptionComplete(
+ PDEVICE_OBJECT DeviceObject,
+ PIRP Irp,
+ PVOID Context
+ )
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
+ PIO_STACK_LOCATION nextIrpStack = IoGetNextIrpStackLocation(Irp);
+ PSCSI_REQUEST_BLOCK srb = Context;
+ NTSTATUS status;
+ BOOLEAN retry;
+ ULONG retryInterval;
+ ULONG srbStatus;
+ BOOLEAN freeLockAndIrp = TRUE;
+ PVOID originalSenseInfoBuffer = irpStack->Parameters.Others.Argument3;
+ PSTORAGE_REQUEST_BLOCK srbEx = NULL;
+ PVOID dataBuffer = NULL;
+ ULONG dataLength = 0;
+ PVOID senseBuffer = NULL;
+ UCHAR cdbLength8 = 0;
+ ULONG cdbLength32 = 0;
+ UCHAR senseBufferLength = 0;
+
+ srbStatus = SRB_STATUS(srb->SrbStatus);
+
+ if (srb->Function == SRB_FUNCTION_STORAGE_REQUEST_BLOCK) {
+ srbEx = (PSTORAGE_REQUEST_BLOCK)srb;
+ dataBuffer = srbEx->DataBuffer;
+ dataLength = srbEx->DataTransferLength;
+ if ((srbEx->SrbFunction == SRB_FUNCTION_EXECUTE_SCSI) &&
+ (srbEx->NumSrbExData > 0)) {
+ (void)GetSrbScsiData(srbEx, &cdbLength8, &cdbLength32, NULL, &senseBuffer, &senseBufferLength);
+ }
+ } else {
+ dataBuffer = srb->DataBuffer;
+ dataLength = srb->DataTransferLength;
+ senseBuffer = srb->SenseInfoBuffer;
+ }
+
+ //
+ // Check SRB status for success of completing request.
+ // SRB_STATUS_DATA_OVERRUN also indicates success.
+ //
+ if ((srbStatus != SRB_STATUS_SUCCESS) &&
+ (srbStatus != SRB_STATUS_DATA_OVERRUN))
+ {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "DiskInfoExceptionComplete: IRP %p, SRB %p\n", Irp, srb));
+
+ if (TEST_FLAG(srb->SrbStatus, SRB_STATUS_QUEUE_FROZEN))
+ {
+ ClassReleaseQueue(DeviceObject);
+ }
+
+ retry = ClassInterpretSenseInfo(
+ DeviceObject,
+ srb,
+ irpStack->MajorFunction,
+ 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 (TEST_FLAG(irpStack->Flags, SL_OVERRIDE_VERIFY_VOLUME) &&
+ status == STATUS_VERIFY_REQUIRED)
+ {
+ status = STATUS_IO_DEVICE_ERROR;
+ retry = TRUE;
+ }
+
+ retry = retry && irpStack->Parameters.Others.Argument4;
+
+ irpStack->Parameters.Others.Argument4 = (PVOID)((ULONG_PTR)irpStack->Parameters.Others.Argument4 - 1);
+
+ if (retry)
+ {
+ //
+ // Retry request.
+ //
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "DiskInfoExceptionComplete: Retry request %p\n", Irp));
+
+ NT_ASSERT(dataBuffer == MmGetMdlVirtualAddress(Irp->MdlAddress));
+
+ if (srb->Function == SRB_FUNCTION_STORAGE_REQUEST_BLOCK) {
+
+ //
+ // Reset byte count of transfer in SRB Extension.
+ //
+ srbEx->DataTransferLength = Irp->MdlAddress->ByteCount;
+
+ //
+ // Zero SRB statuses.
+ //
+
+ srbEx->SrbStatus = 0;
+ if ((srbEx->SrbFunction == SRB_FUNCTION_EXECUTE_SCSI) &&
+ (srbEx->NumSrbExData > 0)) {
+ SetSrbScsiData(srbEx, cdbLength8, cdbLength32, 0, senseBuffer, senseBufferLength);
+ }
+
+ //
+ // Set the no disconnect flag, disable synchronous data transfers and
+ // disable tagged queuing. This fixes some errors.
+ //
+
+ SET_FLAG(srbEx->SrbFlags, SRB_FLAGS_DISABLE_DISCONNECT);
+ SET_FLAG(srbEx->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
+ CLEAR_FLAG(srbEx->SrbFlags, SRB_FLAGS_QUEUE_ACTION_ENABLE);
+
+ srbEx->RequestAttribute = SRB_SIMPLE_TAG_REQUEST;
+ srbEx->RequestTag = SP_UNTAGGED;
+
+ } else {
+
+ //
+ // Reset byte count of transfer in SRB Extension.
+ //
+ srb->DataTransferLength = Irp->MdlAddress->ByteCount;
+
+ //
+ // Zero SRB statuses.
+ //
+
+ srb->SrbStatus = srb->ScsiStatus = 0;
+
+ //
+ // Set the no disconnect flag, disable synchronous data transfers and
+ // disable tagged queuing. This fixes some errors.
+ //
+
+ SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_DISCONNECT);
+ SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
+ CLEAR_FLAG(srb->SrbFlags, SRB_FLAGS_QUEUE_ACTION_ENABLE);
+
+ srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
+ srb->QueueTag = 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;
+
+
+ IoSetCompletionRoutine(Irp,
+ DiskInfoExceptionComplete,
+ srb,
+ TRUE, TRUE, TRUE);
+
+ (VOID)IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+
+ return STATUS_MORE_PROCESSING_REQUIRED;
+ }
+
+ } else {
+
+ //
+ // Get the results from the mode sense
+ //
+ PMODE_INFO_EXCEPTIONS pageData;
+ PMODE_PARAMETER_HEADER modeData;
+ ULONG modeDataLength;
+
+ modeData = dataBuffer;
+ modeDataLength = dataLength;
+
+ pageData = ClassFindModePage((PCHAR)modeData,
+ modeDataLength,
+ MODE_PAGE_FAULT_REPORTING,
+ TRUE);
+ if (pageData != NULL)
+ {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "DiskInfoExceptionComplete: %p supports SMART\n",
+ DeviceObject));
+
+ diskData->ScsiInfoExceptionsSupported = TRUE;
+
+ //
+ // The DEXCPT bit must be 0 and the MRIE field must be valid.
+ //
+ if (pageData->Dexcpt == 0 &&
+ pageData->ReportMethod >= 2 &&
+ pageData->ReportMethod <= 6)
+ {
+ diskData->FailurePredictionCapability = FailurePredictionSense;
+ diskData->FailurePredictionEnabled = TRUE;
+ status = DiskPostReregisterRequest(DeviceObject, Irp);
+
+ if (NT_SUCCESS(status))
+ {
+ //
+ // Make sure we won't free the remove lock and the irp
+ // since we need to keep these until after the work
+ // item has completed running
+ //
+ freeLockAndIrp = FALSE;
+ }
+ } else {
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_WMI, "DiskInfoExceptionComplete: %p is not enabled for SMART\n",
+ DeviceObject));
+
+ }
+
+ } else {
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_WMI, "DiskInfoExceptionComplete: %p does not supports SMART\n",
+ DeviceObject));
+
+ }
+
+ //
+ // Set status for successful request
+ //
+
+ status = STATUS_SUCCESS;
+
+ } // end if (SRB_STATUS(srb->SrbStatus) == SRB_STATUS_SUCCESS)
+
+ //
+ // Free the srb
+ //
+ if (senseBuffer != originalSenseInfoBuffer)
+ {
+ //
+ // Free the original sense info buffer in case the port driver has overwritten it
+ //
+ FREE_POOL(originalSenseInfoBuffer);
+ }
+
+ FREE_POOL(senseBuffer);
+ FREE_POOL(dataBuffer);
+ FREE_POOL(srb);
+
+ if (freeLockAndIrp)
+ {
+ //
+ // Set status in completing IRP.
+ //
+
+ Irp->IoStatus.Status = status;
+
+ //
+ // If pending has be returned for this irp then mark the current stack as
+ // pending.
+ //
+
+ if (Irp->PendingReturned) {
+ IoMarkIrpPending(Irp);
+ }
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ IoFreeMdl(Irp->MdlAddress);
+ IoFreeIrp(Irp);
+ }
+
+ return(STATUS_MORE_PROCESSING_REQUIRED);
+}
+
+
+NTSTATUS
+DiskInfoExceptionCheck(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ )
+{
+ PUCHAR modeData;
+ PSCSI_REQUEST_BLOCK srb;
+ PCDB cdb;
+ PIRP irp;
+ PIO_STACK_LOCATION irpStack;
+ PVOID senseInfoBuffer = NULL;
+ UCHAR senseInfoBufferLength = 0;
+ ULONG isRemoved;
+ ULONG srbSize;
+ PSTORAGE_REQUEST_BLOCK srbEx = NULL;
+ PSTOR_ADDR_BTL8 storAddrBtl8 = NULL;
+ PSRBEX_DATA_SCSI_CDB16 srbExDataCdb16 = NULL;
+
+ modeData = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
+ MODE_DATA_SIZE,
+ DISK_TAG_INFO_EXCEPTION);
+ if (modeData == NULL)
+ {
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_WMI, "DiskInfoExceptionCheck: Can't allocate mode data "
+ "buffer\n"));
+ return(STATUS_INSUFFICIENT_RESOURCES);
+ }
+
+ if (FdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+ srbSize = CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE;
+ } else {
+ srbSize = SCSI_REQUEST_BLOCK_SIZE;
+ }
+ srb = ExAllocatePoolWithTag(NonPagedPoolNx,
+ srbSize,
+ DISK_TAG_SRB);
+ if (srb == NULL)
+ {
+ FREE_POOL(modeData);
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_WMI, "DiskInfoExceptionCheck: Can't allocate srb "
+ "buffer\n"));
+ return(STATUS_INSUFFICIENT_RESOURCES);
+ }
+ RtlZeroMemory(srb, srbSize);
+
+ //
+ // Sense buffer is in aligned nonpaged pool.
+ //
+
+ senseInfoBuffer = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
+ SENSE_BUFFER_SIZE_EX,
+ '7CcS');
+
+ if (senseInfoBuffer == NULL)
+ {
+ FREE_POOL(srb);
+ FREE_POOL(modeData);
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_WMI, "DiskInfoExceptionCheck: Can't allocate request sense "
+ "buffer\n"));
+ return(STATUS_INSUFFICIENT_RESOURCES);
+ }
+
+ senseInfoBufferLength = SENSE_BUFFER_SIZE_EX;
+
+ //
+ // 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);
+ FREE_POOL(srb);
+ FREE_POOL(modeData);
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_WMI, "DiskInfoExceptionCheck: Can't allocate Irp\n"));
+ return(STATUS_INSUFFICIENT_RESOURCES);
+ }
+
+ isRemoved = ClassAcquireRemoveLock(FdoExtension->DeviceObject, irp);
+
+ if (isRemoved)
+ {
+ ClassReleaseRemoveLock(FdoExtension->DeviceObject, irp);
+ IoFreeIrp(irp);
+ FREE_POOL(senseInfoBuffer);
+ FREE_POOL(srb);
+ FREE_POOL(modeData);
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_WMI, "DiskInfoExceptionCheck: RemoveLock says isRemoved\n"));
+ return(STATUS_DEVICE_DOES_NOT_EXIST);
+ }
+
+ //
+ // Get next stack location.
+ //
+
+ IoSetNextIrpStackLocation(irp);
+ irpStack = IoGetCurrentIrpStackLocation(irp);
+ irpStack->DeviceObject = FdoExtension->DeviceObject;
+
+ //
+ // Save retry count in current Irp stack.
+ //
+ irpStack->Parameters.Others.Argument4 = (PVOID)MAXIMUM_RETRIES;
+
+ //
+ // Save allocated sense info buffer in case the port driver overwrites it
+ //
+ irpStack->Parameters.Others.Argument3 = senseInfoBuffer;
+
+ 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 = srb;
+
+ IoSetCompletionRoutine(irp,
+ DiskInfoExceptionComplete,
+ srb,
+ TRUE,
+ TRUE,
+ TRUE);
+
+ irp->MdlAddress = IoAllocateMdl( modeData,
+ MODE_DATA_SIZE,
+ FALSE,
+ FALSE,
+ irp );
+ if (irp->MdlAddress == NULL)
+ {
+ ClassReleaseRemoveLock(FdoExtension->DeviceObject, irp);
+ FREE_POOL(srb);
+ FREE_POOL(modeData);
+ FREE_POOL(senseInfoBuffer);
+ IoFreeIrp( irp );
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_WMI, "DiskINfoExceptionCheck: Can't allocate MDL\n"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ MmBuildMdlForNonPagedPool(irp->MdlAddress);
+
+ //
+ // Build the MODE SENSE CDB.
+ //
+
+ if (FdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
+
+ //
+ // Set up STORAGE_REQUEST_BLOCK fields
+ //
+
+ srbEx = (PSTORAGE_REQUEST_BLOCK)srb;
+ srbEx->Length = FIELD_OFFSET(STORAGE_REQUEST_BLOCK, Signature);
+ srbEx->Function = SRB_FUNCTION_STORAGE_REQUEST_BLOCK;
+ srbEx->Signature = SRB_SIGNATURE;
+ srbEx->Version = STORAGE_REQUEST_BLOCK_VERSION_1;
+ srbEx->SrbLength = srbSize;
+ srbEx->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
+ srbEx->RequestPriority = IoGetIoPriorityHint(irp);
+ srbEx->AddressOffset = sizeof(STORAGE_REQUEST_BLOCK);
+ srbEx->NumSrbExData = 1;
+
+ // Set timeout value from device extension.
+ srbEx->TimeOutValue = FdoExtension->TimeOutValue;
+
+ // Set the transfer length.
+ srbEx->DataTransferLength = MODE_DATA_SIZE;
+ srbEx->DataBuffer = modeData;
+
+ srbEx->SrbFlags = FdoExtension->SrbFlags;
+
+ SET_FLAG(srbEx->SrbFlags, SRB_FLAGS_DATA_IN);
+
+ //
+ // Disable synchronous transfer for these requests.
+ //
+ SET_FLAG(srbEx->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
+
+ //
+ // Don't freeze the queue on an error
+ //
+ SET_FLAG(srbEx->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
+
+ srbEx->RequestAttribute = SRB_SIMPLE_TAG_REQUEST;
+ srbEx->RequestTag = SP_UNTAGGED;
+
+ // Set up IRP Address.
+ srbEx->OriginalRequest = irp;
+
+ //
+ // Set up address fields
+ //
+
+ storAddrBtl8 = (PSTOR_ADDR_BTL8) ((PUCHAR)srbEx + srbEx->AddressOffset);
+ storAddrBtl8->Type = STOR_ADDRESS_TYPE_BTL8;
+ storAddrBtl8->AddressLength = STOR_ADDR_BTL8_ADDRESS_LENGTH;
+
+ //
+ // Set up SCSI SRB extended data fields
+ //
+
+ srbEx->SrbExDataOffset[0] = sizeof(STORAGE_REQUEST_BLOCK) +
+ sizeof(STOR_ADDR_BTL8);
+ if ((srbEx->SrbExDataOffset[0] + sizeof(SRBEX_DATA_SCSI_CDB16)) <= srbEx->SrbLength) {
+ srbExDataCdb16 = (PSRBEX_DATA_SCSI_CDB16)((PUCHAR)srbEx + srbEx->SrbExDataOffset[0]);
+ srbExDataCdb16->Type = SrbExDataTypeScsiCdb16;
+ srbExDataCdb16->Length = SRBEX_DATA_SCSI_CDB16_LENGTH;
+ srbExDataCdb16->CdbLength = 6;
+
+ // Enable auto request sense.
+ srbExDataCdb16->SenseInfoBufferLength = senseInfoBufferLength;
+ srbExDataCdb16->SenseInfoBuffer = senseInfoBuffer;
+
+ cdb = (PCDB)srbExDataCdb16->Cdb;
+ } else {
+ // Should not happen
+ NT_ASSERT(FALSE);
+
+ ClassReleaseRemoveLock(FdoExtension->DeviceObject, irp);
+ FREE_POOL(srb);
+ FREE_POOL(modeData);
+ FREE_POOL(senseInfoBuffer);
+ IoFreeIrp( irp );
+ TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_WMI, "DiskINfoExceptionCheck: Insufficient extended SRB size\n"));
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ } else {
+
+ //
+ // Write length to SRB.
+ //
+ srb->Length = SCSI_REQUEST_BLOCK_SIZE;
+
+ //
+ // Set SCSI bus address.
+ //
+
+ srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
+
+ //
+ // Enable auto request sense.
+ //
+
+ srb->SenseInfoBufferLength = senseInfoBufferLength;
+ srb->SenseInfoBuffer = senseInfoBuffer;
+
+ //
+ // Set timeout value from device extension.
+ //
+ srb->TimeOutValue = FdoExtension->TimeOutValue;
+
+ //
+ // Set the transfer length.
+ //
+ srb->DataTransferLength = MODE_DATA_SIZE;
+ srb->DataBuffer = modeData;
+
+ srb->SrbFlags = FdoExtension->SrbFlags;
+
+ SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_IN);
+
+ //
+ // Disable synchronous transfer for these requests.
+ //
+ SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
+
+ //
+ // Don't freeze the queue on an error
+ //
+ SET_FLAG(srb->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
+
+ srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
+ srb->QueueTag = SP_UNTAGGED;
+
+ //
+ // Set up IRP Address.
+ //
+ srb->OriginalRequest = irp;
+
+ srb->CdbLength = 6;
+ cdb = (PCDB)srb->Cdb;
+
+ }
+
+ cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE;
+ cdb->MODE_SENSE.PageCode = MODE_PAGE_FAULT_REPORTING;
+ cdb->MODE_SENSE.AllocationLength = MODE_DATA_SIZE;
+
+ //
+ // Call the port driver with the request and wait for it to complete.
+ //
+
+ IoMarkIrpPending(irp);
+ IoCallDriver(FdoExtension->CommonExtension.LowerDeviceObject,
+ irp);
+
+ return(STATUS_PENDING);
+}
+
+
+NTSTATUS
+DiskDetectFailurePrediction(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ PFAILURE_PREDICTION_METHOD FailurePredictCapability,
+ BOOLEAN ScsiAddressAvailable
+ )
+/*++
+
+Routine Description:
+
+ Detect if device has any failure prediction capabilities. First we
+ check for IDE SMART capability. This is done by sending the drive an
+ IDENTIFY command and checking if the SMART command set bit is set.
+
+ Next we check if SCSI SMART (aka Information Exception Control Page,
+ X3T10/94-190 Rev 4). This is done by querying for the Information
+ Exception mode page.
+
+ Lastly we check if the device has IOCTL failure prediction. This mechanism
+ a filter driver implements IOCTL_STORAGE_PREDICT_FAILURE and will respond
+ with the information in the IOCTL. We do this by sending the ioctl and
+ if the status returned is STATUS_SUCCESS we assume that it is supported.
+
+Arguments:
+
+ FdoExtension
+
+ *FailurePredictCapability
+
+ ScsiAddressAvailable TRUE if there is a valid SCSI_ADDRESS available
+ for this device, FALSE otherwise.
+ If FALSE we do not allow SMART IOCTLs (FailurePredictionSmart capability)
+ which require a valid SCSI_ADDRESS. The other capabilities
+ <FailurePredictionIoctl, FailurePredictionSense) do not requere
+ SCSI_ADDRESS so we'll still try to initialize them.
+
+Return Value:
+
+ NT Status
+
+--*/
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = (PCOMMON_DEVICE_EXTENSION)FdoExtension;
+ PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
+ BOOLEAN supportFP;
+ NTSTATUS status;
+ STORAGE_PREDICT_FAILURE checkFailure;
+ STORAGE_FAILURE_PREDICT_STATUS diskSmartStatus;
+
+ PAGED_CODE();
+
+ //
+ // Assume no failure predict mechanisms
+ //
+ *FailurePredictCapability = FailurePredictionNone;
+
+ //
+ // See if this is an IDE drive that supports SMART. If so enable SMART
+ // and then ensure that it suports the SMART READ STATUS command
+ //
+
+ if (ScsiAddressAvailable)
+ {
+ DiskGetIdentifyInfo(FdoExtension, &supportFP);
+
+ if (supportFP)
+ {
+ status = DiskEnableSmart(FdoExtension);
+ if (NT_SUCCESS(status))
+ {
+ *FailurePredictCapability = FailurePredictionSmart;
+ diskData->FailurePredictionEnabled = TRUE;
+
+ status = DiskReadFailurePredictStatus(FdoExtension,
+ &diskSmartStatus);
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "Disk: Device %p %s IDE SMART\n",
+ FdoExtension->DeviceObject,
+ NT_SUCCESS(status) ? "does" : "does not"));
+
+ if (!NT_SUCCESS(status))
+ {
+ *FailurePredictCapability = FailurePredictionNone;
+ diskData->FailurePredictionEnabled = FALSE;
+ }
+ }
+ return(status);
+ }
+ }
+ //
+ // See if there is a a filter driver to intercept
+ // IOCTL_STORAGE_PREDICT_FAILURE
+ //
+ status = DiskSendFailurePredictIoctl(FdoExtension,
+ &checkFailure);
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "Disk: Device %p %s IOCTL_STORAGE_FAILURE_PREDICT\n",
+ FdoExtension->DeviceObject,
+ NT_SUCCESS(status) ? "does" : "does not"));
+
+ if (NT_SUCCESS(status))
+ {
+ *FailurePredictCapability = FailurePredictionIoctl;
+ diskData->FailurePredictionEnabled = TRUE;
+ if (checkFailure.PredictFailure)
+ {
+ checkFailure.PredictFailure = 512;
+ ClassNotifyFailurePredicted(FdoExtension,
+ (PUCHAR)&checkFailure,
+ sizeof(checkFailure),
+ (BOOLEAN)(FdoExtension->FailurePredicted == FALSE),
+ 0x11,
+ diskData->ScsiAddress.PathId,
+ diskData->ScsiAddress.TargetId,
+ diskData->ScsiAddress.Lun);
+
+ FdoExtension->FailurePredicted = TRUE;
+ }
+ return(status);
+ }
+
+ //
+ // Finally we assume it will not be a scsi smart drive. but
+ // we'll also send off an asynchronous mode sense so that if
+ // it is SMART we'll reregister the device object
+ //
+
+ *FailurePredictCapability = FailurePredictionNone;
+
+ DiskInfoExceptionCheck(FdoExtension);
+
+ return(STATUS_SUCCESS);
+}
+
+
+NTSTATUS
+DiskWmiFunctionControl(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN ULONG GuidIndex,
+ IN CLASSENABLEDISABLEFUNCTION Function,
+ IN BOOLEAN Enable
+ )
+/*++
+
+Routine Description:
+
+ This routine is a callback into the driver to enabled or disable event
+ generation or data block collection. A device should only expect a
+ single enable when the first event or data consumer enables events or
+ data collection and a single disable when the last event or data
+ consumer disables events or data collection. Data blocks will only
+ receive collection enable/disable if they were registered as requiring
+ it.
+
+
+ When NT boots, failure prediction is not automatically enabled, although
+ it may have been persistantly enabled on a previous boot. Polling is also
+ not automatically enabled. When the first data block that accesses SMART
+ such as SmartStatusGuid, SmartDataGuid, SmartPerformFunction, or
+ SmartEventGuid is accessed then SMART is automatically enabled in the
+ hardware. Polling is enabled when SmartEventGuid is enabled and disabled
+ when it is disabled. Hardware SMART is only disabled when the DisableSmart
+ method is called. Polling is also disabled when this is called regardless
+ of the status of the other guids or events.
+
+Arguments:
+
+ DeviceObject is the device whose data block is being queried
+
+ GuidIndex is the index into the list of guids provided when the
+ device registered
+
+ Function specifies which functionality is being enabled or disabled
+
+ Enable is TRUE then the function is being enabled else disabled
+
+Return Value:
+
+ status
+
+--*/
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+
+ PAGED_CODE();
+
+ if ((Function == DataBlockCollection) && Enable)
+ {
+ if ((GuidIndex == SmartStatusGuid) ||
+ (GuidIndex == SmartDataGuid) ||
+ (GuidIndex == SmartThresholdsGuid) ||
+ (GuidIndex == SmartPerformFunction))
+ {
+ status = DiskEnableDisableFailurePrediction(fdoExtension,
+ TRUE);
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "Disk: DeviceObject %p, Irp %p Enable -> %lx\n",
+ DeviceObject,
+ Irp,
+ status));
+
+ } else {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "Disk: DeviceObject %p, Irp %p, GuidIndex %d %s for Collection\n",
+ DeviceObject, Irp,
+ GuidIndex,
+ Enable ? "Enabled" : "Disabled"));
+ }
+ } else if (Function == EventGeneration) {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "Disk: DeviceObject %p, Irp %p, GuidIndex %d %s for Event Generation\n",
+ DeviceObject, Irp,
+ GuidIndex,
+ Enable ? "Enabled" : "Disabled"));
+
+
+ if ((GuidIndex == SmartEventGuid) && Enable)
+ {
+ status = DiskEnableDisableFailurePredictPolling(fdoExtension,
+ Enable,
+ 0);
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "Disk: DeviceObject %p, Irp %p %s -> %lx\n",
+ DeviceObject,
+ Irp,
+ Enable ? "DiskEnableSmartPolling" : "DiskDisableSmartPolling",
+ status));
+ }
+
+#if DBG
+ } else {
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "Disk: DeviceObject %p, Irp %p, GuidIndex %d %s for function %d\n",
+ DeviceObject, Irp,
+ GuidIndex,
+ Enable ? "Enabled" : "Disabled",
+ Function));
+#endif
+ }
+
+ status = ClassWmiCompleteRequest(DeviceObject,
+ Irp,
+ status,
+ 0,
+ IO_NO_INCREMENT);
+ return status;
+}
+
+
+
+NTSTATUS
+DiskFdoQueryWmiRegInfo(
+ IN PDEVICE_OBJECT DeviceObject,
+ OUT ULONG *RegFlags,
+ OUT PUNICODE_STRING InstanceName
+ )
+/*++
+
+Routine Description:
+
+ This routine is a callback into the driver to retrieve the list of
+ guids or data blocks that the driver wants to register with WMI. This
+ routine may not pend or block. Driver should NOT call
+ ClassWmiCompleteRequest.
+
+Arguments:
+
+ DeviceObject is the device whose data block is being queried
+
+ *RegFlags returns with a set of flags that describe the guids being
+ registered for this device. If the device wants enable and disable
+ collection callbacks before receiving queries for the registered
+ guids then it should return the WMIREG_FLAG_EXPENSIVE flag. Also the
+ returned flags may specify WMIREG_FLAG_INSTANCE_PDO in which case
+ the instance name is determined from the PDO associated with the
+ device object. Note that the PDO must have an associated devnode. If
+ WMIREG_FLAG_INSTANCE_PDO is not set then Name must return a unique
+ name for the device.
+
+ InstanceName returns with the instance name for the guids if
+ WMIREG_FLAG_INSTANCE_PDO is not set in the returned *RegFlags. The
+ caller will call ExFreePool with the buffer returned.
+
+
+Return Value:
+
+ status
+
+--*/
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
+
+ PAGED_CODE();
+ UNREFERENCED_PARAMETER(InstanceName);
+
+ SET_FLAG(DiskWmiFdoGuidList[SmartThresholdsGuid].Flags, WMIREG_FLAG_REMOVE_GUID);
+ SET_FLAG(DiskWmiFdoGuidList[ScsiInfoExceptionsGuid].Flags, WMIREG_FLAG_REMOVE_GUID);
+
+ switch (diskData->FailurePredictionCapability)
+ {
+ case FailurePredictionSmart:
+ {
+ CLEAR_FLAG(DiskWmiFdoGuidList[SmartThresholdsGuid].Flags, WMIREG_FLAG_REMOVE_GUID);
+ //
+ // Fall Through
+ //
+ }
+ case FailurePredictionIoctl:
+ {
+ CLEAR_FLAG(DiskWmiFdoGuidList[SmartStatusGuid].Flags, WMIREG_FLAG_REMOVE_GUID);
+ CLEAR_FLAG(DiskWmiFdoGuidList[SmartDataGuid].Flags, WMIREG_FLAG_REMOVE_GUID);
+ CLEAR_FLAG(DiskWmiFdoGuidList[SmartEventGuid].Flags, WMIREG_FLAG_REMOVE_GUID);
+ CLEAR_FLAG(DiskWmiFdoGuidList[SmartPerformFunction].Flags, WMIREG_FLAG_REMOVE_GUID);
+
+ break;
+ }
+
+ case FailurePredictionSense:
+ {
+ CLEAR_FLAG(DiskWmiFdoGuidList[SmartStatusGuid].Flags, WMIREG_FLAG_REMOVE_GUID);
+ CLEAR_FLAG(DiskWmiFdoGuidList[SmartEventGuid].Flags, WMIREG_FLAG_REMOVE_GUID);
+ CLEAR_FLAG(DiskWmiFdoGuidList[SmartPerformFunction].Flags, WMIREG_FLAG_REMOVE_GUID);
+ CLEAR_FLAG(DiskWmiFdoGuidList[ScsiInfoExceptionsGuid].Flags, WMIREG_FLAG_REMOVE_GUID);
+ SET_FLAG (DiskWmiFdoGuidList[SmartDataGuid].Flags, WMIREG_FLAG_REMOVE_GUID);
+ break;
+ }
+
+
+ default:
+ {
+ SET_FLAG (DiskWmiFdoGuidList[SmartStatusGuid].Flags, WMIREG_FLAG_REMOVE_GUID);
+ SET_FLAG (DiskWmiFdoGuidList[SmartDataGuid].Flags, WMIREG_FLAG_REMOVE_GUID);
+ SET_FLAG (DiskWmiFdoGuidList[SmartEventGuid].Flags, WMIREG_FLAG_REMOVE_GUID);
+ SET_FLAG (DiskWmiFdoGuidList[SmartPerformFunction].Flags, WMIREG_FLAG_REMOVE_GUID);
+ break;
+ }
+ }
+
+ //
+ // Use devnode for FDOs
+ *RegFlags = WMIREG_FLAG_INSTANCE_PDO;
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+DiskFdoQueryWmiRegInfoEx(
+ IN PDEVICE_OBJECT DeviceObject,
+ OUT ULONG *RegFlags,
+ OUT PUNICODE_STRING InstanceName,
+ OUT PUNICODE_STRING MofName
+ )
+/*++
+
+Routine Description:
+
+ This routine is a callback into the driver to retrieve the list of
+ guids or data blocks that the driver wants to register with WMI. This
+ routine may not pend or block. Driver should NOT call
+ ClassWmiCompleteRequest.
+
+Arguments:
+
+ DeviceObject is the device whose data block is being queried
+
+ *RegFlags returns with a set of flags that describe the guids being
+ registered for this device. If the device wants enable and disable
+ collection callbacks before receiving queries for the registered
+ guids then it should return the WMIREG_FLAG_EXPENSIVE flag. Also the
+ returned flags may specify WMIREG_FLAG_INSTANCE_PDO in which case
+ the instance name is determined from the PDO associated with the
+ device object. Note that the PDO must have an associated devnode. If
+ WMIREG_FLAG_INSTANCE_PDO is not set then Name must return a unique
+ name for the device.
+
+ InstanceName returns with the instance name for the guids if
+ WMIREG_FLAG_INSTANCE_PDO is not set in the returned *RegFlags. The
+ caller will call ExFreePool with the buffer returned.
+
+ MofName returns initialized with the mof resource name for the
+ binary mof resource attached to the driver's image file. If the
+ driver does not have a mof resource then it should leave this
+ parameter untouched.
+
+Return Value:
+
+ status
+
+--*/
+{
+ NTSTATUS status;
+
+ UNREFERENCED_PARAMETER(MofName);
+
+ status = DiskFdoQueryWmiRegInfo(DeviceObject,
+ RegFlags,
+ InstanceName);
+
+ //
+ // Leave MofName alone since disk doesn't have one
+ //
+ return(status);
+}
+
+
+NTSTATUS
+DiskFdoQueryWmiDataBlock(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN ULONG GuidIndex,
+ IN ULONG BufferAvail,
+ OUT PUCHAR Buffer
+ )
+/*++
+
+Routine Description:
+
+ This routine is a callback into the driver to query for the contents of
+ a data block. When the driver has finished filling the data block it
+ must call ClassWmiCompleteRequest to complete the irp. The driver can
+ return STATUS_PENDING if the irp cannot be completed immediately.
+
+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;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
+ ULONG sizeNeeded;
+
+ PAGED_CODE();
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "Disk: DiskQueryWmiDataBlock, Device %p, Irp %p, GuiIndex %d\n"
+ " BufferAvail %lx Buffer %p\n",
+ DeviceObject, Irp,
+ GuidIndex, BufferAvail, Buffer));
+
+ switch (GuidIndex)
+ {
+ case DiskGeometryGuid:
+ {
+ sizeNeeded = sizeof(DISK_GEOMETRY);
+ if (BufferAvail >= sizeNeeded)
+ {
+ if (DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA)
+ {
+ //
+ // Issue ReadCapacity to update device extension
+ // with information for current media.
+ status = DiskReadDriveCapacity(commonExtension->PartitionZeroExtension->DeviceObject);
+
+ //
+ // Note whether the drive is ready.
+ diskData->ReadyStatus = status;
+
+ if (!NT_SUCCESS(status))
+ {
+ break;
+ }
+ }
+
+ //
+ // Copy drive geometry information from device extension.
+ RtlMoveMemory(Buffer,
+ &(fdoExtension->DiskGeometry),
+ sizeof(DISK_GEOMETRY));
+
+ status = STATUS_SUCCESS;
+ } else {
+ status = STATUS_BUFFER_TOO_SMALL;
+ }
+ break;
+ }
+
+ case SmartStatusGuid:
+ {
+ PSTORAGE_FAILURE_PREDICT_STATUS diskSmartStatus;
+
+ NT_ASSERT(diskData->FailurePredictionCapability != FailurePredictionNone);
+
+ sizeNeeded = sizeof(STORAGE_FAILURE_PREDICT_STATUS);
+ if (BufferAvail >= sizeNeeded)
+ {
+ STORAGE_PREDICT_FAILURE checkFailure;
+
+ diskSmartStatus = (PSTORAGE_FAILURE_PREDICT_STATUS)Buffer;
+
+ status = DiskSendFailurePredictIoctl(fdoExtension,
+ &checkFailure);
+
+ if (NT_SUCCESS(status))
+ {
+ if (diskData->FailurePredictionCapability ==
+ FailurePredictionSense)
+ {
+ diskSmartStatus->Reason = *((PULONG)checkFailure.VendorSpecific);
+ } else {
+ diskSmartStatus->Reason = 0; // unknown
+ }
+
+ diskSmartStatus->PredictFailure = (checkFailure.PredictFailure != 0);
+ }
+ } else {
+ status = STATUS_BUFFER_TOO_SMALL;
+ }
+ break;
+ }
+
+ case SmartDataGuid:
+ {
+ PSTORAGE_FAILURE_PREDICT_DATA diskSmartData;
+
+ NT_ASSERT((diskData->FailurePredictionCapability ==
+ FailurePredictionSmart) ||
+ (diskData->FailurePredictionCapability ==
+ FailurePredictionIoctl));
+
+ sizeNeeded = sizeof(STORAGE_FAILURE_PREDICT_DATA);
+ if (BufferAvail >= sizeNeeded)
+ {
+ PSTORAGE_PREDICT_FAILURE checkFailure = (PSTORAGE_PREDICT_FAILURE)Buffer;
+
+ diskSmartData = (PSTORAGE_FAILURE_PREDICT_DATA)Buffer;
+
+ status = DiskSendFailurePredictIoctl(fdoExtension,
+ checkFailure);
+
+ if (NT_SUCCESS(status))
+ {
+ diskSmartData->Length = 512;
+ }
+ } else {
+ status = STATUS_BUFFER_TOO_SMALL;
+ }
+
+ break;
+ }
+
+ case SmartThresholdsGuid:
+ {
+ PSTORAGE_FAILURE_PREDICT_THRESHOLDS diskSmartThresholds;
+
+ NT_ASSERT((diskData->FailurePredictionCapability ==
+ FailurePredictionSmart));
+
+ sizeNeeded = sizeof(STORAGE_FAILURE_PREDICT_THRESHOLDS);
+ if (BufferAvail >= sizeNeeded)
+ {
+ diskSmartThresholds = (PSTORAGE_FAILURE_PREDICT_THRESHOLDS)Buffer;
+ status = DiskReadFailurePredictThresholds(fdoExtension,
+ diskSmartThresholds);
+ } else {
+ status = STATUS_BUFFER_TOO_SMALL;
+ }
+
+ break;
+ }
+
+ case SmartPerformFunction:
+ {
+ sizeNeeded = 0;
+ status = STATUS_SUCCESS;
+ break;
+ }
+
+ case ScsiInfoExceptionsGuid:
+ {
+ PSTORAGE_SCSI_INFO_EXCEPTIONS infoExceptions;
+ MODE_INFO_EXCEPTIONS modeInfo;
+
+ NT_ASSERT((diskData->FailurePredictionCapability ==
+ FailurePredictionSense));
+
+ sizeNeeded = sizeof(STORAGE_SCSI_INFO_EXCEPTIONS);
+ if (BufferAvail >= sizeNeeded)
+ {
+ infoExceptions = (PSTORAGE_SCSI_INFO_EXCEPTIONS)Buffer;
+ status = DiskGetInfoExceptionInformation(fdoExtension,
+ &modeInfo);
+ if (NT_SUCCESS(status))
+ {
+ infoExceptions->PageSavable = modeInfo.PSBit;
+ infoExceptions->Flags = modeInfo.Flags;
+ infoExceptions->MRIE = modeInfo.ReportMethod;
+ infoExceptions->Padding = 0;
+ REVERSE_BYTES(&infoExceptions->IntervalTimer,
+ &modeInfo.IntervalTimer);
+ REVERSE_BYTES(&infoExceptions->ReportCount,
+ &modeInfo.ReportCount)
+ }
+ } else {
+ status = STATUS_BUFFER_TOO_SMALL;
+ }
+
+ break;
+ }
+
+ default:
+ {
+ sizeNeeded = 0;
+ status = STATUS_WMI_GUID_NOT_FOUND;
+ }
+ }
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "Disk: DiskQueryWmiDataBlock Device %p, Irp %p returns %lx\n",
+ DeviceObject, Irp, status));
+
+ status = ClassWmiCompleteRequest(DeviceObject,
+ Irp,
+ status,
+ sizeNeeded,
+ IO_NO_INCREMENT);
+
+ return status;
+}
+
+
+NTSTATUS
+DiskFdoSetWmiDataBlock(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN ULONG GuidIndex,
+ IN ULONG BufferSize,
+ IN PUCHAR Buffer
+ )
+/*++
+
+Routine Description:
+
+ This routine is a callback into the driver to query for the contents of
+ a data block. When the driver has finished filling the data block it
+ must call ClassWmiCompleteRequest to complete the irp. The driver can
+ return STATUS_PENDING if the irp cannot be completed immediately.
+
+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
+
+ BufferSize has the size of the data block passed
+
+ Buffer has the new values for the data block
+
+
+Return Value:
+
+ status
+
+--*/
+{
+ NTSTATUS status;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
+
+ PAGED_CODE();
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "Disk: DiskSetWmiDataBlock, Device %p, Irp %p, GuiIndex %d\n"
+ " BufferSize %#x Buffer %p\n",
+ DeviceObject, Irp,
+ GuidIndex, BufferSize, Buffer));
+
+ if (GuidIndex == ScsiInfoExceptionsGuid)
+ {
+ PSTORAGE_SCSI_INFO_EXCEPTIONS infoExceptions;
+ MODE_INFO_EXCEPTIONS modeInfo = {0};
+
+ if (BufferSize >= sizeof(STORAGE_SCSI_INFO_EXCEPTIONS))
+ {
+ infoExceptions = (PSTORAGE_SCSI_INFO_EXCEPTIONS)Buffer;
+
+ modeInfo.PageCode = MODE_PAGE_FAULT_REPORTING;
+ modeInfo.PageLength = sizeof(MODE_INFO_EXCEPTIONS) - 2;
+
+ modeInfo.PSBit = 0;
+ modeInfo.Flags = infoExceptions->Flags;
+
+ modeInfo.ReportMethod = infoExceptions->MRIE;
+
+ REVERSE_BYTES(&modeInfo.IntervalTimer[0],
+ &infoExceptions->IntervalTimer);
+
+ REVERSE_BYTES(&modeInfo.ReportCount[0],
+ &infoExceptions->ReportCount);
+
+ if (modeInfo.Perf == 1)
+ {
+ diskData->AllowFPPerfHit = FALSE;
+ } else {
+ diskData->AllowFPPerfHit = TRUE;
+ }
+
+ status = DiskSetInfoExceptionInformation(fdoExtension,
+ &modeInfo);
+ } else {
+ status = STATUS_INVALID_PARAMETER;
+ }
+
+ } else if (GuidIndex <= SmartThresholdsGuid)
+ {
+ status = STATUS_WMI_READ_ONLY;
+ } else {
+ status = STATUS_WMI_GUID_NOT_FOUND;
+ }
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "Disk: DiskSetWmiDataBlock Device %p, Irp %p returns %lx\n",
+ DeviceObject, Irp, status));
+
+ status = ClassWmiCompleteRequest(DeviceObject,
+ Irp,
+ status,
+ 0,
+ IO_NO_INCREMENT);
+
+ return status;
+}
+
+
+NTSTATUS
+DiskFdoSetWmiDataItem(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN ULONG GuidIndex,
+ IN ULONG DataItemId,
+ IN ULONG BufferSize,
+ IN PUCHAR Buffer
+ )
+/*++
+
+Routine Description:
+
+ This routine is a callback into the driver to query for the contents of
+ a data block. When the driver has finished filling the data block it
+ must call ClassWmiCompleteRequest to complete the irp. The driver can
+ return STATUS_PENDING if the irp cannot be completed immediately.
+
+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
+
+ DataItemId has the id of the data item being set
+
+ BufferSize has the size of the data item passed
+
+ Buffer has the new values for the data item
+
+
+Return Value:
+
+ status
+
+--*/
+{
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "Disk: DiskSetWmiDataItem, Device %p, Irp %p, GuiIndex %d, DataId %d\n"
+ " BufferSize %#x Buffer %p\n",
+ DeviceObject, Irp,
+ GuidIndex, DataItemId, BufferSize, Buffer));
+
+ if (GuidIndex <= SmartThresholdsGuid)
+ {
+ status = STATUS_WMI_READ_ONLY;
+ } else {
+ status = STATUS_WMI_GUID_NOT_FOUND;
+ }
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "Disk: DiskSetWmiDataItem Device %p, Irp %p returns %lx\n",
+ DeviceObject, Irp, status));
+
+ status = ClassWmiCompleteRequest(DeviceObject,
+ Irp,
+ status,
+ 0,
+ IO_NO_INCREMENT);
+
+ return status;
+}
+
+
+NTSTATUS
+DiskFdoExecuteWmiMethod(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN ULONG GuidIndex,
+ IN ULONG MethodId,
+ IN ULONG InBufferSize,
+ IN ULONG OutBufferSize,
+ IN PUCHAR Buffer
+ )
+/*++
+
+Routine Description:
+
+ This routine is a callback into the driver to execute a method. When the
+ driver has finished filling the data block it must call
+ ClassWmiCompleteRequest to complete the irp. The driver can
+ return STATUS_PENDING if the irp cannot be completed immediately.
+
+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
+
+ MethodId has the id of the method being called
+
+ InBufferSize has the size of the data block passed in as the input to
+ the method.
+
+ OutBufferSize on entry has the maximum size available to write the
+ returned data block.
+
+ Buffer is filled with the returned data block
+
+
+Return Value:
+
+ status
+
+--*/
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
+ ULONG sizeNeeded = 0;
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "Disk: DiskExecuteWmiMethod, DeviceObject %p, Irp %p, Guid Id %d, MethodId %d\n"
+ " InBufferSize %#x, OutBufferSize %#x, Buffer %p\n",
+ DeviceObject, Irp,
+ GuidIndex, MethodId, InBufferSize, OutBufferSize, Buffer));
+
+ switch(GuidIndex)
+ {
+ case SmartPerformFunction:
+ {
+
+ NT_ASSERT((diskData->FailurePredictionCapability ==
+ FailurePredictionSmart) ||
+ (diskData->FailurePredictionCapability ==
+ FailurePredictionIoctl) ||
+ (diskData->FailurePredictionCapability ==
+ FailurePredictionSense));
+
+
+ switch(MethodId)
+ {
+ //
+ // void AllowPerformanceHit([in] boolean Allow)
+ //
+ case AllowDisallowPerformanceHit:
+ {
+ BOOLEAN allowPerfHit;
+
+ sizeNeeded = 0;
+ if (InBufferSize >= sizeof(BOOLEAN))
+ {
+ status = STATUS_SUCCESS;
+
+ allowPerfHit = *((PBOOLEAN)Buffer);
+ if (diskData->AllowFPPerfHit != allowPerfHit)
+ {
+ diskData->AllowFPPerfHit = allowPerfHit;
+ if (diskData->FailurePredictionCapability ==
+ FailurePredictionSense)
+ {
+ MODE_INFO_EXCEPTIONS modeInfo;
+
+ status = DiskGetInfoExceptionInformation(fdoExtension,
+ &modeInfo);
+ if (NT_SUCCESS(status))
+ {
+ modeInfo.Perf = allowPerfHit ? 0 : 1;
+ status = DiskSetInfoExceptionInformation(fdoExtension,
+ &modeInfo);
+ }
+ }
+ else
+ {
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ }
+ }
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "DiskFdoWmiExecuteMethod: AllowPerformanceHit %x for device %p --> %lx\n",
+ allowPerfHit,
+ fdoExtension->DeviceObject,
+ status));
+ } else {
+ status = STATUS_INVALID_PARAMETER;
+ }
+ break;
+ }
+
+ //
+ // void EnableDisableHardwareFailurePrediction([in] boolean Enable)
+ //
+ case EnableDisableHardwareFailurePrediction:
+ {
+ BOOLEAN enable;
+
+ sizeNeeded = 0;
+ if (InBufferSize >= sizeof(BOOLEAN))
+ {
+ status = STATUS_SUCCESS;
+ enable = *((PBOOLEAN)Buffer);
+ if (!enable)
+ {
+ //
+ // If we are disabling we need to also disable
+ // polling
+ //
+ DiskEnableDisableFailurePredictPolling(
+ fdoExtension,
+ enable,
+ 0);
+ }
+
+ status = DiskEnableDisableFailurePrediction(
+ fdoExtension,
+ enable);
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "DiskFdoWmiExecuteMethod: EnableDisableHardwareFailurePrediction: %x for device %p --> %lx\n",
+ enable,
+ fdoExtension->DeviceObject,
+ status));
+ } else {
+ status = STATUS_INVALID_PARAMETER;
+ }
+ break;
+ }
+
+ //
+ // void EnableDisableFailurePredictionPolling(
+ // [in] uint32 Period,
+ // [in] boolean Enable)
+ //
+ case EnableDisableFailurePredictionPolling:
+ {
+ BOOLEAN enable;
+ ULONG period;
+
+ sizeNeeded = 0;
+ if (InBufferSize >= (sizeof(ULONG) + sizeof(BOOLEAN)))
+ {
+ period = *((PULONG)Buffer);
+ Buffer += sizeof(ULONG);
+ enable = *((PBOOLEAN)Buffer);
+
+ status = DiskEnableDisableFailurePredictPolling(
+ fdoExtension,
+ enable,
+ period);
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "DiskFdoWmiExecuteMethod: EnableDisableFailurePredictionPolling: %x %x for device %p --> %lx\n",
+ enable,
+ period,
+ fdoExtension->DeviceObject,
+ status));
+ } else {
+ status = STATUS_INVALID_PARAMETER;
+ }
+ break;
+ }
+
+ //
+ // void GetFailurePredictionCapability([out] uint32 Capability)
+ //
+ case GetFailurePredictionCapability:
+ {
+ sizeNeeded = sizeof(ULONG);
+ if (OutBufferSize >= sizeNeeded)
+ {
+ status = STATUS_SUCCESS;
+ *((PFAILURE_PREDICTION_METHOD)Buffer) = diskData->FailurePredictionCapability;
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "DiskFdoWmiExecuteMethod: GetFailurePredictionCapability: %x for device %p --> %lx\n",
+ *((PFAILURE_PREDICTION_METHOD)Buffer),
+ fdoExtension->DeviceObject,
+ status));
+ } else {
+ status = STATUS_BUFFER_TOO_SMALL;
+ }
+ break;
+ }
+
+ //
+ // void EnableOfflineDiags([out] boolean Success);
+ //
+ case EnableOfflineDiags:
+ {
+ sizeNeeded = sizeof(BOOLEAN);
+ if (OutBufferSize >= sizeNeeded)
+ {
+ if (diskData->FailurePredictionCapability ==
+ FailurePredictionSmart)
+ {
+ //
+ // Initiate or resume offline diagnostics.
+ // This may cause a loss of performance
+ // to the disk, but mayincrease the amount
+ // of disk checking.
+ //
+ status = DiskExecuteSmartDiagnostics(fdoExtension,
+ 0);
+
+ } else {
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ }
+
+ *((PBOOLEAN)Buffer) = NT_SUCCESS(status);
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "DiskFdoWmiExecuteMethod: EnableOfflineDiags for device %p --> %lx\n",
+ fdoExtension->DeviceObject,
+ status));
+ } else {
+ status = STATUS_BUFFER_TOO_SMALL;
+ }
+ break;
+ }
+
+ //
+ // void ReadLogSectors([in] uint8 LogAddress,
+ // [in] uint8 SectorCount,
+ // [out] uint32 Length,
+ // [out, WmiSizeIs("Length")] uint8 LogSectors[]
+ // );
+ //
+ case ReadLogSectors:
+ {
+ if (diskData->FailurePredictionCapability ==
+ FailurePredictionSmart)
+ {
+ if (InBufferSize >= sizeof(READ_LOG_SECTORS_IN))
+ {
+ PREAD_LOG_SECTORS_IN inParams;
+ PREAD_LOG_SECTORS_OUT outParams;
+ ULONG readSize;
+
+ inParams = (PREAD_LOG_SECTORS_IN)Buffer;
+ readSize = inParams->SectorCount * SMART_LOG_SECTOR_SIZE;
+ sizeNeeded = FIELD_OFFSET(READ_LOG_SECTORS_OUT,
+ LogSectors) + readSize;
+
+ if (OutBufferSize >= sizeNeeded)
+ {
+ outParams = (PREAD_LOG_SECTORS_OUT)Buffer;
+ status = DiskReadSmartLog(fdoExtension,
+ inParams->SectorCount,
+ inParams->LogAddress,
+ outParams->LogSectors);
+
+ if (NT_SUCCESS(status))
+ {
+ outParams->Length = readSize;
+ } else {
+ //
+ // SMART command failure is
+ // indicated by successful
+ // execution, but no data returned
+ //
+ outParams->Length = 0;
+ status = STATUS_SUCCESS;
+ }
+ } else {
+ status = STATUS_BUFFER_TOO_SMALL;
+ }
+
+ } else {
+ status = STATUS_INVALID_PARAMETER;
+ }
+ } else {
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ }
+ break;
+ }
+
+ // void WriteLogSectors([in] uint8 LogAddress,
+ // [in] uint8 SectorCount,
+ // [in] uint32 Length,
+ // [in, WmiSizeIs("Length")] uint8 LogSectors[],
+ // [out] boolean Success
+ // );
+ case WriteLogSectors:
+ {
+ if (diskData->FailurePredictionCapability ==
+ FailurePredictionSmart)
+ {
+ if ((LONG)InBufferSize >= FIELD_OFFSET(WRITE_LOG_SECTORS_IN,
+ LogSectors))
+ {
+ PWRITE_LOG_SECTORS_IN inParams;
+ PWRITE_LOG_SECTORS_OUT outParams;
+ ULONG writeSize;
+
+ inParams = (PWRITE_LOG_SECTORS_IN)Buffer;
+ writeSize = inParams->SectorCount * SMART_LOG_SECTOR_SIZE;
+ if (InBufferSize >= (FIELD_OFFSET(WRITE_LOG_SECTORS_IN,
+ LogSectors) +
+ writeSize))
+ {
+ sizeNeeded = sizeof(WRITE_LOG_SECTORS_OUT);
+
+ if (OutBufferSize >= sizeNeeded)
+ {
+ outParams = (PWRITE_LOG_SECTORS_OUT)Buffer;
+ status = DiskWriteSmartLog(fdoExtension,
+ inParams->SectorCount,
+ inParams->LogAddress,
+ inParams->LogSectors);
+
+ if (NT_SUCCESS(status))
+ {
+ outParams->Success = TRUE;
+ } else {
+ outParams->Success = FALSE;
+ status = STATUS_SUCCESS;
+ }
+ } else {
+ status = STATUS_BUFFER_TOO_SMALL;
+ }
+ } else {
+ status = STATUS_INVALID_PARAMETER;
+ }
+ } else {
+ status = STATUS_INVALID_PARAMETER;
+ }
+ } else {
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ }
+ break;
+ }
+
+ // void ExecuteSelfTest([in] uint8 Subcommand,
+ // [out,
+ // Values{"0", "1", "2"},
+ // ValueMap{"Successful Completion",
+ // "Captive Mode Required",
+ // "Unsuccessful Completion"}
+ // ]
+ // uint32 ReturnCode);
+ case ExecuteSelfTest:
+ {
+ if (diskData->FailurePredictionCapability ==
+ FailurePredictionSmart)
+ {
+ if (InBufferSize >= sizeof(EXECUTE_SELF_TEST_IN))
+ {
+ sizeNeeded = sizeof(EXECUTE_SELF_TEST_OUT);
+ if (OutBufferSize >= sizeNeeded)
+ {
+ PEXECUTE_SELF_TEST_IN inParam;
+ PEXECUTE_SELF_TEST_OUT outParam;
+
+ inParam = (PEXECUTE_SELF_TEST_IN)Buffer;
+ outParam = (PEXECUTE_SELF_TEST_OUT)Buffer;
+
+ if (DiskIsValidSmartSelfTest(inParam->Subcommand))
+ {
+ status = DiskExecuteSmartDiagnostics(fdoExtension,
+ inParam->Subcommand);
+ if (NT_SUCCESS(status))
+ {
+ //
+ // Return self test executed
+ // without a problem
+ //
+ outParam->ReturnCode = 0;
+ } else {
+ //
+ // Return Self test execution
+ // failed status
+ //
+ outParam->ReturnCode = 2;
+ status = STATUS_SUCCESS;
+ }
+ } else {
+ //
+ // If self test subcommand requires
+ // captive mode then return that
+ // status
+ //
+ outParam->ReturnCode = 1;
+ status = STATUS_SUCCESS;
+ }
+
+ } else {
+ status = STATUS_BUFFER_TOO_SMALL;
+ }
+
+ } else {
+ status = STATUS_INVALID_PARAMETER;
+ }
+ } else {
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ }
+
+ break;
+ }
+
+ default :
+ {
+ sizeNeeded = 0;
+ status = STATUS_WMI_ITEMID_NOT_FOUND;
+ break;
+ }
+ }
+
+ break;
+ }
+
+ case DiskGeometryGuid:
+ case SmartStatusGuid:
+ case SmartDataGuid:
+ case SmartEventGuid:
+ case SmartThresholdsGuid:
+ case ScsiInfoExceptionsGuid:
+ {
+ sizeNeeded = 0;
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ break;
+ }
+
+ default:
+ {
+ sizeNeeded = 0;
+ status = STATUS_WMI_GUID_NOT_FOUND;
+ }
+ }
+
+ TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "Disk: DiskExecuteMethod Device %p, Irp %p returns %lx\n",
+ DeviceObject, Irp, status));
+
+ status = ClassWmiCompleteRequest(DeviceObject,
+ Irp,
+ status,
+ sizeNeeded,
+ IO_NO_INCREMENT);
+
+ return status;
+}
+
+