Sample Code

windows driver samples/ CDROM Storage Class Driver/ C++/ src/ pnppower.c/

/*--

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);

            //
            // 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

Our Services

  • What our customers say about us?

© 2011-2025 All Rights Reserved. Joya Systems. 4425 South Mopac Building II Suite 101 Austin, TX 78735 Tel: 800-DEV-KERNEL

Privacy Policy. Terms of use. Valid XHTML & CSS