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/cdrom/src/pnppower.c | |
| parent | 02e5b090a6131a7cc1b90f4a9373117c9e3c7088 (diff) | |
samples update for //build
Diffstat (limited to 'storage/class/cdrom/src/pnppower.c')
| -rw-r--r-- | storage/class/cdrom/src/pnppower.c | 684 |
1 files changed, 684 insertions, 0 deletions
diff --git a/storage/class/cdrom/src/pnppower.c b/storage/class/cdrom/src/pnppower.c new file mode 100644 index 00000000..56f239e4 --- /dev/null +++ b/storage/class/cdrom/src/pnppower.c @@ -0,0 +1,684 @@ +/*-- + +Copyright (C) Microsoft Corporation. All rights reserved. + +Module Name: + + pnppower.c + +Abstract: + + Functions to handle PnP and Power IRPs. + +Environment: + + kernel mode only + +Notes: + optical devices do not need to issue SPIN UP when power up. + The device itself should SPIN UP to process commands. + + +Revision History: + +--*/ + + +#include "ntddk.h" +#include "wdfcore.h" + +#include "cdrom.h" +#include "ioctl.h" +#include "scratch.h" +#include "mmc.h" + +#ifdef DEBUG_USE_WPP +#include "pnppower.tmh" +#endif + + +NTSTATUS +DeviceScratchSyncCache( + _In_ PCDROM_DEVICE_EXTENSION DeviceExtension + ); + +NTSTATUS +DeviceScratchPreventMediaRemoval( + _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, + _In_ BOOLEAN Prevent + ); + +NTSTATUS +RequestIssueShutdownFlush( + _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, + _In_ PIRP Irp + ); + +IO_COMPLETION_ROUTINE RequestProcessPowerIrpCompletion; + +EVT_WDF_REQUEST_COMPLETION_ROUTINE RequestUnlockQueueCompletion; + +#ifdef ALLOC_PRAGMA + +#pragma alloc_text(PAGE, DevicePowerSettingCallback) + +#endif + +#pragma warning(push) +#pragma warning(disable:4152) // nonstandard extension, function/data pointer conversion in expression + +#pragma warning(disable:28118) // Dispatch routines for IRP_MJ_SHUTDOWN, IRP_MJ_FLUSH_BUFFERS occur at PASSIVE_LEVEL. + // WDF defines EVT_WDFDEVICE_WDM_IRP_PREPROCESS with _IRQL_requires_max_(DISPATCH_LEVEL), + // triggering a false positive for this warning. + +NTSTATUS +RequestProcessShutdownFlush( + WDFDEVICE Device, + PIRP Irp + ) +/*++ + +Routine Description: + + process IRP: IRP_MJ_SHUTDOWN, IRP_MJ_FLUSH_BUFFERS + +Arguments: + + Device - device object + Irp - the irp + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PIO_STACK_LOCATION currentStack = NULL; + PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device); + + //add trace info + + // acquire the shutdown/flush lock + WdfWaitLockAcquire(deviceExtension->ShutdownFlushWaitLock, NULL); + + currentStack = IoGetCurrentIrpStackLocation(Irp); + + // finish all current requests + WdfIoQueueStopSynchronously(deviceExtension->SerialIOQueue); + + // sync cache + if (NT_SUCCESS(status)) + { + // safe to use scratch srb to send the request. + status = DeviceScratchSyncCache(deviceExtension); + } + + // For SHUTDOWN, allow media removal. + if (NT_SUCCESS(status)) + { + if (currentStack->MajorFunction == IRP_MJ_SHUTDOWN) + { + // safe to use scratch srb to send the request. + status = DeviceScratchPreventMediaRemoval(deviceExtension, FALSE); + } + } + + // Use original IRP, send SRB_FUNCTION_SHUTDOWN or SRB_FUNCTION_FLUSH (no retry) + if (NT_SUCCESS(status)) + { + status = RequestIssueShutdownFlush(deviceExtension, Irp); + } + + // restart queue to allow processing further requests. + WdfIoQueueStart(deviceExtension->SerialIOQueue); + + // release the shutdown/flush lock + WdfWaitLockRelease(deviceExtension->ShutdownFlushWaitLock); + + // 6. complete the irp + Irp->IoStatus.Status = status; + IoCompleteRequest(Irp, 0); + + return status; +} + +NTSTATUS +RequestProcessSetPower( + WDFDEVICE Device, + PIRP Irp + ) +/*++ + +Routine Description: + + process IRP: IRP_MJ_POWER + +Arguments: + + Device - device object + Irp - the irp + +Return Value: + + NTSTATUS + +--*/ +{ + PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device); + PIO_STACK_LOCATION currentStack; + NTSTATUS status; + BOOLEAN IrpMarkedPending = FALSE; + + currentStack = IoGetCurrentIrpStackLocation(Irp); + + if ((currentStack->Parameters.Power.Type == DevicePowerState) && + (currentStack->Parameters.Power.State.DeviceState != PowerDeviceD0)) + { + // We need to unlock the device queue in D3 postprocessing. + IoCopyCurrentIrpStackLocationToNext(Irp); + IoSetCompletionRoutine(Irp, + RequestProcessPowerIrpCompletion, + deviceExtension, + TRUE, + TRUE, + TRUE); + + // Mark the Irp pending as we'll defer the I/O completion. + IoMarkIrpPending(Irp); + IrpMarkedPending = TRUE; + } + else { + + IoSkipCurrentIrpStackLocation(Irp); + } + +#pragma warning(push) +#pragma warning(disable: 28193) // OACR will complain that the status variable is not examined. + + // + // Deliver the IRP back to the framework. + // + + status = WdfDeviceWdmDispatchPreprocessedIrp(Device, Irp); + + if (IrpMarkedPending) + { + UNREFERENCED_PARAMETER(status); + return STATUS_PENDING; + } + +#pragma warning(pop) + + return status; +} + +// use scratch SRB to issue SYNC CACHE command. +NTSTATUS +DeviceScratchSyncCache( + _In_ PCDROM_DEVICE_EXTENSION DeviceExtension + ) +/*++ + +Routine Description: + + use scratch buffer to send SYNC CACHE command + +Arguments: + + DeviceExtension - device context + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + ULONG transferSize = 0; + CDB cdb; + + ScratchBuffer_BeginUse(DeviceExtension); + + RtlZeroMemory(&cdb, sizeof(CDB)); + // Set up the CDB + cdb.SYNCHRONIZE_CACHE10.OperationCode = SCSIOP_SYNCHRONIZE_CACHE; + //srb->QueueTag = SP_UNTAGGED; + //srb->QueueAction = SRB_SIMPLE_TAG_REQUEST; + + status = ScratchBuffer_ExecuteCdbEx(DeviceExtension, NULL, transferSize, FALSE, &cdb, 10, TimeOutValueGetCapValue(DeviceExtension->TimeOutValue, 4)); + + ScratchBuffer_EndUse(DeviceExtension); + + return status; +} + +NTSTATUS +DeviceScratchPreventMediaRemoval( + _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, + _In_ BOOLEAN Prevent + ) +/*++ + +Routine Description: + + use scratch SRB to issue ALLOW/PREVENT MEDIA REMOVAL command. + +Arguments: + + DeviceExtension - device context + Prevent - TRUE (prevent media removal); FALSE (allow media removal) + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + ULONG transferSize = 0; + CDB cdb; + + ScratchBuffer_BeginUse(DeviceExtension); + + RtlZeroMemory(&cdb, sizeof(CDB)); + // Set up the CDB + cdb.MEDIA_REMOVAL.OperationCode = SCSIOP_MEDIUM_REMOVAL; + cdb.MEDIA_REMOVAL.Prevent = Prevent; + //srb->QueueTag = SP_UNTAGGED; + //srb->QueueAction = SRB_SIMPLE_TAG_REQUEST; + + status = ScratchBuffer_ExecuteCdb(DeviceExtension, NULL, transferSize, FALSE, &cdb, 6); + + ScratchBuffer_EndUse(DeviceExtension); + + return status; +} + +NTSTATUS +RequestIssueShutdownFlush( + _In_ PCDROM_DEVICE_EXTENSION DeviceExtension, + _In_ PIRP Irp + ) +/*++ + +Routine Description: + + issue SRB function Flush/Shutdown command. + +Arguments: + + DeviceExtension - device context + Irp - the irp + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + PSCSI_REQUEST_BLOCK srb = DeviceExtension->ScratchContext.ScratchSrb; + PIO_STACK_LOCATION currentStack = NULL; + + ULONG transferSize = 0; + BOOLEAN shouldRetry = TRUE; + ULONG timesAlreadyRetried = 0; + LONGLONG retryIn100nsUnits = 0; + + + currentStack = IoGetCurrentIrpStackLocation(Irp); + + + ScratchBuffer_BeginUse(DeviceExtension); + + // no retry needed. + { + ScratchBuffer_SetupSrb(DeviceExtension, NULL, transferSize, FALSE); + + // Set up the SRB/CDB + srb->QueueTag = SP_UNTAGGED; + srb->QueueAction = SRB_SIMPLE_TAG_REQUEST; + srb->TimeOutValue = TimeOutValueGetCapValue(DeviceExtension->TimeOutValue, 4); + srb->CdbLength = 0; + + if (currentStack->MajorFunction == IRP_MJ_SHUTDOWN) + { + srb->Function = SRB_FUNCTION_SHUTDOWN; + } + else + { + srb->Function = SRB_FUNCTION_FLUSH; + } + + ScratchBuffer_SendSrb(DeviceExtension, TRUE, NULL); + + shouldRetry = RequestSenseInfoInterpretForScratchBuffer(DeviceExtension, + timesAlreadyRetried, + &status, + &retryIn100nsUnits); + UNREFERENCED_PARAMETER(shouldRetry); //defensive coding, avoid PREFAST warning. + UNREFERENCED_PARAMETER(status); //defensive coding, avoid PREFAST warning. + + // retrieve the real status from the request. + status = WdfRequestGetStatus(DeviceExtension->ScratchContext.ScratchRequest); + } + + ScratchBuffer_EndUse(DeviceExtension); + + + return status; +} + +VOID +RequestUnlockQueueCompletion ( + _In_ WDFREQUEST Request, + _In_ WDFIOTARGET Target, + _In_ PWDF_REQUEST_COMPLETION_PARAMS Params, + _In_ WDFCONTEXT Context + ) +{ + PIRP Irp = Context; + WDFDEVICE device = WdfIoTargetGetDevice(Target); + PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(device); + + UNREFERENCED_PARAMETER(Request); + UNREFERENCED_PARAMETER(Params); + + deviceExtension->PowerContext.Options.LockQueue = FALSE; + + PowerContextEndUse(deviceExtension); + + // Complete the original power irp + IoCompleteRequest( Irp, IO_NO_INCREMENT ); +} + +NTSTATUS +RequestProcessPowerIrpCompletion( + _In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp, + _In_reads_opt_(_Inexpressible_("varies")) PVOID Context + ) +/*++ + +Routine Description: + + Free the Irp. + +Arguments: + + DeviceObject - device that the completion routine fires on. + + Irp - The irp to be completed. + + Context - IRP context + +Return Value: + NTSTATUS + +--*/ +{ + PCDROM_DEVICE_EXTENSION deviceExtension = Context; + PIO_STACK_LOCATION currentStack; + + UNREFERENCED_PARAMETER(DeviceObject); + + if (Irp->PendingReturned) + { + IoMarkIrpPending( Irp ); + } + + currentStack = IoGetCurrentIrpStackLocation(Irp); + + NT_ASSERT(currentStack->Parameters.Power.Type == DevicePowerState); + NT_ASSERT(currentStack->Parameters.Power.State.DeviceState != PowerDeviceD0); + + _Analysis_assume_(deviceExtension != NULL); + + deviceExtension->PowerContext.PowerChangeState.PowerDown++; + + // Step 5. UNLOCK QUEUE + if (deviceExtension->PowerContext.Options.LockQueue) + { + (VOID)DeviceSendPowerDownProcessRequest(deviceExtension, + RequestUnlockQueueCompletion, + Irp); + + // Let the completion routine complete the Irp + return STATUS_MORE_PROCESSING_REQUIRED; + } + + // Release the power context if it wasn't already done as part of D0Exit handling + if (deviceExtension->PowerContext.InUse) + { + PowerContextEndUse(deviceExtension); + } + + return STATUS_CONTINUE_COMPLETION; +} + + +_IRQL_requires_max_(DISPATCH_LEVEL) +NTSTATUS +PowerContextReuseRequest( + _In_ PCDROM_DEVICE_EXTENSION DeviceExtension + ) +/*++ + +Routine Description: + + reset fields for the request. + +Arguments: + + DeviceExtension - device context + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + WDF_REQUEST_REUSE_PARAMS reuseParams; + PIRP irp = NULL; + + RtlZeroMemory(&(DeviceExtension->PowerContext.SenseData), sizeof(DeviceExtension->PowerContext.SenseData)); + RtlZeroMemory(&(DeviceExtension->PowerContext.Srb), sizeof(DeviceExtension->PowerContext.Srb)); + + irp = WdfRequestWdmGetIrp(DeviceExtension->PowerContext.PowerRequest); + + // Re-use the previously created PowerRequest object and format it + WDF_REQUEST_REUSE_PARAMS_INIT(&reuseParams, WDF_REQUEST_REUSE_NO_FLAGS, STATUS_NOT_SUPPORTED); + status = WdfRequestReuse(DeviceExtension->PowerContext.PowerRequest, &reuseParams); + if (NT_SUCCESS(status)) + { + // This request was preformated during initialization so this call should never fail. + status = WdfIoTargetFormatRequestForInternalIoctlOthers(DeviceExtension->IoTarget, + DeviceExtension->PowerContext.PowerRequest, + IOCTL_SCSI_EXECUTE_IN, + NULL, NULL, + NULL, NULL, + NULL, NULL); + + if (!NT_SUCCESS(status)) + { + TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, + "PowerContextReuseRequest: WdfIoTargetFormatRequestForInternalIoctlOthers failed, %!STATUS!\n", + status)); + } + } + + // Do some basic initialization of the PowerRequest, the rest will be done by the caller + // of this function + if (NT_SUCCESS(status)) + { + PIO_STACK_LOCATION nextStack = NULL; + + nextStack = IoGetNextIrpStackLocation(irp); + + nextStack->MajorFunction = IRP_MJ_SCSI; + nextStack->Parameters.Scsi.Srb = &(DeviceExtension->PowerContext.Srb); + + DeviceExtension->PowerContext.Srb.Length = sizeof(SCSI_REQUEST_BLOCK); + DeviceExtension->PowerContext.Srb.OriginalRequest = irp; + + DeviceExtension->PowerContext.Srb.SenseInfoBuffer = &(DeviceExtension->PowerContext.SenseData); + DeviceExtension->PowerContext.Srb.SenseInfoBufferLength = SENSE_BUFFER_SIZE; + } + + return status; +} + +_IRQL_requires_max_(DISPATCH_LEVEL) +NTSTATUS +PowerContextBeginUse( + _In_ PCDROM_DEVICE_EXTENSION DeviceExtension + ) +/*++ + +Routine Description: + + initialize fields in power context + +Arguments: + + DeviceExtension - device context + +Return Value: + + NTSTATUS + +--*/ +{ + NTSTATUS status = STATUS_SUCCESS; + + NT_ASSERT(!DeviceExtension->PowerContext.InUse); + + DeviceExtension->PowerContext.InUse = TRUE; + DeviceExtension->PowerContext.RetryCount = MAXIMUM_RETRIES; + DeviceExtension->PowerContext.RetryIntervalIn100ns = 0; + + KeQueryTickCount(&DeviceExtension->PowerContext.StartTime); + + RtlZeroMemory(&(DeviceExtension->PowerContext.Options), sizeof(DeviceExtension->PowerContext.Options)); + RtlZeroMemory(&(DeviceExtension->PowerContext.PowerChangeState), sizeof(DeviceExtension->PowerContext.PowerChangeState)); + + status = PowerContextReuseRequest(DeviceExtension); + + RequestClearSendTime(DeviceExtension->PowerContext.PowerRequest); + + return status; +} + +_IRQL_requires_max_(DISPATCH_LEVEL) +NTSTATUS +PowerContextEndUse( + _In_ PCDROM_DEVICE_EXTENSION DeviceExtension + ) +/*++ + +Routine Description: + + inidate that power context using is finished. + +Arguments: + + DeviceExtension - device context + +Return Value: + + NTSTATUS + +--*/ +{ + NT_ASSERT(DeviceExtension->PowerContext.InUse); + + DeviceExtension->PowerContext.InUse = FALSE; + + KeQueryTickCount(&DeviceExtension->PowerContext.CompleteTime); + + return STATUS_SUCCESS; +} + + +_Function_class_(POWER_SETTING_CALLBACK) +_IRQL_requires_same_ +NTSTATUS +DevicePowerSettingCallback( + _In_ LPCGUID SettingGuid, + _In_reads_bytes_(ValueLength) PVOID Value, + _In_ ULONG ValueLength, + _Inout_opt_ PVOID Context + ) +/*++ +Description: + + This function is the callback for power setting notifications (registered + when ClasspGetD3IdleTimeout() is called for the first time). + + Currently, this function is used to get the disk idle timeout value from + the system power settings. + + This function is guaranteed to be called at PASSIVE_LEVEL. + +Arguments: + + SettingGuid - The power setting GUID. + Value - Pointer to the power setting value. + ValueLength - Size of the Value buffer. + Context - The FDO's device extension. + +Return Value: + + STATUS_SUCCESS + +--*/ +{ + PCDROM_DEVICE_EXTENSION DeviceExtension = Context; + MONITOR_DISPLAY_STATE DisplayState; + + PAGED_CODE(); + + if (IsEqualGUID(SettingGuid, &GUID_CONSOLE_DISPLAY_STATE)) { + + if ((ValueLength == sizeof(ULONG)) && (Value != NULL)) { + + DisplayState = *((PULONG)Value); + + _Analysis_assume_(DeviceExtension != NULL); + + // + // Power setting callbacks are asynchronous so make sure the device + // is completely initialized before taking any actions. + // + if (DeviceExtension->IsInitialized) { + + // + // If monitor is off, change media change requests to not keep device active. + // This allows the devices to go to sleep if there are no other active requests. + // + + if (DisplayState == PowerMonitorOff) { + + // + // Mark the device inactive so that it can enter a low power state. + // + + DeviceMarkActive(DeviceExtension, FALSE, TRUE); + SET_FLAG(DeviceExtension->MediaChangeDetectionInfo->SrbFlags, SRB_FLAGS_NO_KEEP_AWAKE); + } + else + { + CLEAR_FLAG(DeviceExtension->MediaChangeDetectionInfo->SrbFlags, SRB_FLAGS_NO_KEEP_AWAKE); + DeviceMarkActive(DeviceExtension, TRUE, TRUE); + } + } + } + } + + return STATUS_SUCCESS; +} + +#pragma warning(pop) // un-sets any local warning changes + |
