diff options
| author | Dave Wilson <[email protected]> | 2015-04-29 09:48:49 -0700 |
|---|---|---|
| committer | Dave Wilson <[email protected]> | 2015-04-29 10:47:29 -0700 |
| commit | d40232638faaab19de557e70a3ee938da7a35295 (patch) | |
| tree | 14e61c973745949af186b7940e3a53d25322f5de /storage/class/disk/src/diskwmi.c | |
| parent | 02e5b090a6131a7cc1b90f4a9373117c9e3c7088 (diff) | |
samples update for //build
Diffstat (limited to 'storage/class/disk/src/diskwmi.c')
| -rw-r--r-- | storage/class/disk/src/diskwmi.c | 3501 |
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; +} + + |
