windows-nt/Source/XPSP1/NT/drivers/wdm/usb/driver/usbser/usbserpw.c

1299 lines
40 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1997-1998 Microsoft Corporation
Module Name:
USBSERPW.C
Abstract:
Power Management module
Environment:
kernel mode only
Notes:
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
PURPOSE.
Copyright (c) 1997-1998 Microsoft Corporation. All Rights Reserved.
Revision History:
10/29/98 : created
Authors:
Tom Green
--*/
#include <wdm.h>
#include <ntddser.h>
#include <stdio.h>
#include <stdlib.h>
#include <usb.h>
#include <usbdrivr.h>
#include <usbdlib.h>
#include <usbcomm.h>
#ifdef WMI_SUPPORT
#include <wmilib.h>
#include <wmidata.h>
#include <wmistr.h>
#endif
#include "usbser.h"
#include "usbserpw.h"
#include "serioctl.h"
#include "utils.h"
#include "debugwdm.h"
//
// Power code is not pagable since we do not set DO_POWER_PAGABLE
//
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGEUSBS, UsbSerSendWaitWake)
#endif // ALLOC_PRAGMA
/*++
Routine Description:
This routine handles completion of the waitwake IRP.
Arguments:
DeviceObject - Pointer to the device object for this device
MinorFunction - Minor function previously supplied to PoRequestPowerIrp
PowerState - PowerState previously supplied to PoRequestPowerIrp
Context - a pointer to the device extension
IoStatus - current/final status of the waitwake IRP
Return Value:
The function value is the final status of attempting to process the
waitwake.
--*/
NTSTATUS
UsbSerWakeCompletion(IN PDEVICE_OBJECT DeviceObject, IN UCHAR MinorFunction,
IN POWER_STATE PowerState, IN PVOID Context,
IN PIO_STATUS_BLOCK IoStatus)
{
NTSTATUS Status;
PDEVICE_EXTENSION DeviceExtension = (PDEVICE_EXTENSION)Context;
POWER_STATE NewPowerState;
DEBUG_TRACE1(("UsbSerWakeCompletion\n"));
DEBUG_TRACE1(("Status (%08X)\n", IoStatus->Status));
Status = IoStatus->Status;
if(NT_SUCCESS(Status))
{
//
// A wakeup has occurred -- powerup our stack
//
DEBUG_TRACE1(("Powerup Device\n"));
NewPowerState.DeviceState = PowerDeviceD0;
PoRequestPowerIrp(DeviceExtension->PhysDeviceObject,
IRP_MN_SET_POWER, NewPowerState,
NULL, NULL, NULL);
}
DeviceExtension->PendingWakeIrp = NULL;
return Status;
} // UsbSerWakeCompletion
/*++
Routine Description:
This routine causes a waitwake IRP to be sent
Arguments:
DeviceExtension - Pointer to the device extension for this device
Return Value:
STATUS_INVALID_DEVICE_STATE if one is already pending, else result
of call to PoRequestPowerIrp.
--*/
NTSTATUS
UsbSerSendWaitWake(PDEVICE_EXTENSION DeviceExtension)
{
NTSTATUS Status;
PIRP Irp;
POWER_STATE PowerState;
USBSER_LOCKED_PAGED_CODE();
DEBUG_TRACE1(("UsbSerSendWaitWake\n"));
//
// Make sure one isn't pending already -- usbser will only handle one at
// a time.
//
if(DeviceExtension->PendingWakeIrp != NULL)
{
return STATUS_INVALID_DEVICE_STATE;
}
//
// Make sure we are capable of waking the machine
//
if(DeviceExtension->SystemWake <= PowerSystemWorking)
{
return STATUS_INVALID_DEVICE_STATE;
}
if(DeviceExtension->DeviceWake == PowerDeviceUnspecified)
{
return STATUS_INVALID_DEVICE_STATE;
}
//
// Send IRP to request wait wake
//
//
DEBUG_TRACE1(("Request Wait Wake\n"));
PowerState.SystemState = DeviceExtension->SystemWake;
Status = PoRequestPowerIrp(DeviceExtension->PhysDeviceObject, IRP_MN_WAIT_WAKE,
PowerState, UsbSerWakeCompletion, DeviceExtension, &Irp);
if(Status == STATUS_PENDING)
{
Status = STATUS_SUCCESS;
DeviceExtension->PendingWakeIrp = Irp;
}
return Status;
} // UsbSerSendWaitWake
/*++
Routine Description:
This is our FDO's dispatch table function for IRP_MJ_POWER.
It processes the Power IRPs sent to the PDO for this device.
For every power IRP, drivers must call PoStartNextPowerIrp and use PoCallDriver
to pass the IRP all the way down the driver stack to the underlying PDO.
Arguments:
DeviceObject - pointer to our device object (FDO)
Irp - pointer to an I/O Request Packet
Return Value:
NT status code
--*/
NTSTATUS
UsbSer_ProcessPowerIrp(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
PIO_STACK_LOCATION IrpStack;
NTSTATUS Status = STATUS_SUCCESS;
PDEVICE_EXTENSION DeviceExtension;
BOOLEAN GoingToD0 = FALSE;
POWER_STATE SysPowerState, DesiredDevicePowerState;
DEBUG_LOG_PATH("enter UsbSer_ProcessPowerIrp");
DEBUG_TRACE1(("UsbSer_ProcessPowerIrp\n"));
DeviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
IrpStack = IoGetCurrentIrpStackLocation (Irp);
switch(IrpStack->MinorFunction)
{
case IRP_MN_WAIT_WAKE:
DEBUG_TRACE1(("IRP_MN_WAIT_WAKE\n"));
// A driver sends IRP_MN_WAIT_WAKE to indicate that the system should
// wait for its device to signal a wake event. The exact nature of the event
// is device-dependent.
// Drivers send this IRP for two reasons:
// 1) To allow a device to wake the system
// 2) To wake a device that has been put into a sleep state to save power
// but still must be able to communicate with its driver under certain circumstances.
// When a wake event occurs, the driver completes the IRP and returns
// STATUS_SUCCESS. If the device is sleeping when the event occurs,
// the driver must first wake up the device before completing the IRP.
// In a completion routine, the driver calls PoRequestPowerIrp to send a
// PowerDeviceD0 request. When the device has powered up, the driver can
// handle the IRP_MN_WAIT_WAKE request.
// DeviceCapabilities.DeviceWake specifies the lowest device power state (least powered)
// from which the device can signal a wake event
DeviceExtension->PowerDownLevel = DeviceExtension->DeviceWake;
DEBUG_TRACE1(("CurrentDevicePowerState (%08X) DeviceWakeup (%08X)\n",
DeviceExtension->CurrentDevicePowerState,
DeviceExtension->DeviceWake));
if((PowerDeviceD0 == DeviceExtension->CurrentDevicePowerState) ||
(DeviceExtension->DeviceWake > DeviceExtension->CurrentDevicePowerState))
{
//
// STATUS_INVALID_DEVICE_STATE is returned if the device in the PowerD0 state
// or a state below which it can support waking, or if the SystemWake state
// is below a state which can be supported. A pending IRP_MN_WAIT_WAKE will complete
// with this error if the device's state is changed to be incompatible with the wake
// request.
// If a driver fails this IRP, it should complete the IRP immediately without
// passing the IRP to the next-lower driver.
Status = STATUS_INVALID_DEVICE_STATE;
Irp->IoStatus.Status = Status;
PoStartNextPowerIrp(Irp);
IoCompleteRequest (Irp, IO_NO_INCREMENT);
return Status;
}
// If not failing outright, pass this on to our PDO for further handling
IoCopyCurrentIrpStackLocationToNext(Irp);
// Set a completion routine so it can signal our event when
// the PDO is done with the Irp
IoSetCompletionRoutine(Irp,
UsbSerWaitWakeIrpCompletionRoutine,
DeviceObject,// pass the event to the completion routine as the Context
TRUE, // invoke on success
TRUE, // invoke on error
TRUE); // invoke on cancellation
DEBUG_TRACE1(("Send down wait wake\n"));
PoStartNextPowerIrp(Irp);
Status = PoCallDriver(DeviceExtension->StackDeviceObject,
Irp);
Status = STATUS_PENDING;
break;
case IRP_MN_SET_POWER:
{
DEBUG_TRACE1(("IRP_MN_SET_POWER\n"));
// The system power policy manager sends this IRP to set the system power state.
// A device power policy manager sends this IRP to set the device power state for a device.
// Set Irp->IoStatus.Status to STATUS_SUCCESS to indicate that the device
// has entered the requested state. Drivers cannot fail this IRP.
switch(IrpStack->Parameters.Power.Type)
{
case SystemPowerState:
DEBUG_TRACE1(("SystemPowerState\n"));
// Get input system power state
SysPowerState.SystemState = IrpStack->Parameters.Power.State.SystemState;
DEBUG_TRACE1(("SystemState (%08X)\n", SysPowerState.SystemState));
#if DBG
{
ULONG Index;
for(Index = 0; Index < 8; Index++)
{
DEBUG_TRACE1(("DeviceState[%08X] (%08X)\n", Index,
DeviceExtension->DeviceCapabilities.DeviceState[Index]));
}
DEBUG_TRACE1(("DeviceWake (%08X)\n", DeviceExtension->DeviceCapabilities.DeviceWake));
DEBUG_TRACE1(("SystemWake (%08X)\n", DeviceExtension->DeviceCapabilities.SystemWake));
}
#endif
// If system is in working state always set our device to D0
// regardless of the wait state or system-to-device state power map
if(SysPowerState.SystemState == PowerSystemWorking)
{
DEBUG_TRACE1(("Setting to D0\n"));
DesiredDevicePowerState.DeviceState = PowerDeviceD0;
}
else
{
// set to corresponding system state if IRP_MN_WAIT_WAKE pending
if(DeviceExtension->SendWaitWake)
{
// got a WAIT_WAKE IRP pending?
DEBUG_TRACE1(("We want to send a wait wake Irp\n"));
// Find the device power state equivalent to the given system state.
// We get this info from the DEVICE_CAPABILITIES struct in our device
// extension (initialized in UsbSer_PnPAddDevice() )
DesiredDevicePowerState.DeviceState =
DeviceExtension->DeviceCapabilities.DeviceState[SysPowerState.SystemState];
}
else
{
DEBUG_TRACE1(("No wait wake Irp to send\n"));
// if no wait pending and the system's not in working state, just turn off
DesiredDevicePowerState.DeviceState = PowerDeviceD3;
}
}
DEBUG_TRACE1(("DesiredDevicePowerState (%08X)\n", DesiredDevicePowerState.DeviceState));
//
// We've determined the desired device state; are we already in this state?
//
if(DesiredDevicePowerState.DeviceState !=
DeviceExtension->CurrentDevicePowerState)
{
// No, request that we be put into this state
// by requesting a new Power Irp from the Pnp manager
DeviceExtension->PowerIrp = Irp;
Status = PoRequestPowerIrp(DeviceExtension->PhysDeviceObject,
IRP_MN_SET_POWER,
DesiredDevicePowerState,
UsbSer_PoRequestCompletion,
DeviceObject,
NULL);
}
else
{
// Yes, just pass it on to PDO (Physical Device Object)
IoCopyCurrentIrpStackLocationToNext(Irp);
PoStartNextPowerIrp(Irp);
Status = PoCallDriver(DeviceExtension->StackDeviceObject,
Irp);
}
break;
case DevicePowerState:
DEBUG_TRACE1(("DevicePowerState\n"));
// For requests to D1, D2, or D3 ( sleep or off states ),
// sets deviceExtension->CurrentDevicePowerState to DeviceState immediately.
// This enables any code checking state to consider us as sleeping or off
// already, as this will imminently become our state.
// For requests to DeviceState D0 ( fully on ), sets fGoingToD0 flag TRUE
// to flag that we must set a completion routine and update
// deviceExtension->CurrentDevicePowerState there.
// In the case of powering up to fully on, we really want to make sure
// the process is completed before updating our CurrentDevicePowerState,
// so no IO will be attempted or accepted before we're really ready.
GoingToD0 = UsbSer_SetDevicePowerState(DeviceObject,
IrpStack->Parameters.Power.State.DeviceState);
IoCopyCurrentIrpStackLocationToNext(Irp);
if(GoingToD0)
{
IoSetCompletionRoutine(Irp,
UsbSer_PowerIrp_Complete,
DeviceObject,
TRUE, // invoke on success
TRUE, // invoke on error
TRUE); // invoke on cancellation of the Irp
}
PoStartNextPowerIrp(Irp);
Status = PoCallDriver(DeviceExtension->StackDeviceObject,
Irp);
break;
} /* case irpStack->Parameters.Power.Type */
}
break; /* IRP_MN_SET_POWER */
case IRP_MN_QUERY_POWER:
DEBUG_TRACE1(("IRP_MN_QUERY_POWER\n"));
//
// A power policy manager sends this IRP to determine whether it can change
// the system or device power state, typically to go to sleep.
//
if(DeviceExtension->SendWaitWake)
{
if(IrpStack->Parameters.Power.Type == SystemPowerState
&& DeviceExtension->DeviceCapabilities.DeviceState[IrpStack->Parameters.Power.State.SystemState]
> DeviceExtension->DeviceWake)
{
Status = Irp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE;
PoStartNextPowerIrp(Irp);
IoCompleteRequest(Irp, IO_NO_INCREMENT);
break;
}
}
else
{
IoCopyCurrentIrpStackLocationToNext(Irp);
PoStartNextPowerIrp(Irp);
Status = PoCallDriver(DeviceExtension->StackDeviceObject,
Irp);
break;
}
// fall through to default
default:
//
// All unhandled power messages are passed on to the PDO
//
IoCopyCurrentIrpStackLocationToNext(Irp);
PoStartNextPowerIrp(Irp);
Status = PoCallDriver(DeviceExtension->StackDeviceObject, Irp);
} /* IrpStack->MinorFunction */
DEBUG_LOG_PATH("exit UsbSer_ProcessPowerIrp");
return Status;
} // UsbSer_ProcessPowerIrp
/*++
Routine Description:
This is the completion routine set in a call to PoRequestPowerIrp()
that was made in BulkUsb_ProcessPowerIrp() in response to receiving
an IRP_MN_SET_POWER of type 'SystemPowerState' when the device was
not in a compatible device power state. In this case, a pointer to
the IRP_MN_SET_POWER Irp is saved into the FDO device extension
(deviceExtension->PowerIrp), and then a call must be
made to PoRequestPowerIrp() to put the device into a proper power state,
and this routine is set as the completion routine.
We decrement our pending io count and pass the saved IRP_MN_SET_POWER Irp
on to the next driver
Arguments:
DeviceObject - Pointer to the device object for the class device.
Note that we must get our own device object from the Context
Context - Driver defined context, in this case our own functional device object ( FDO )
Return Value:
The function value is the final status from the operation.
--*/
NTSTATUS
UsbSer_PoRequestCompletion(IN PDEVICE_OBJECT DeviceObject, IN UCHAR MinorFunction,
IN POWER_STATE PowerState, IN PVOID Context, IN PIO_STATUS_BLOCK IoStatus)
{
PIRP Irp;
PDEVICE_EXTENSION DeviceExtension;
PDEVICE_OBJECT ContextDeviceObject = Context;
NTSTATUS Status;
DEBUG_LOG_PATH("enter UsbSer_PoRequestCompletion");
DEBUG_TRACE1(("UsbSer_PoRequestCompletion\n"));
DeviceExtension = ContextDeviceObject->DeviceExtension;
// Get the Irp we saved for later processing in Usbser_ProcessPowerIrp()
// when we decided to request the Power Irp that this routine
// is the completion routine for.
Irp = DeviceExtension->PowerIrp;
// We will return the status set by the PDO for the power request we're completing
Status = IoStatus->Status;
// we must pass down to the next driver in the stack
IoCopyCurrentIrpStackLocationToNext(Irp);
// Calling PoStartNextPowerIrp() indicates that the driver is finished
// with the previous power IRP, if any, and is ready to handle the next power IRP.
// It must be called for every power IRP.Although power IRPs are completed only once,
// typically by the lowest-level driver for a device, PoStartNextPowerIrp must be called
// for every stack location. Drivers must call PoStartNextPowerIrp while the current IRP
// stack location points to the current driver. Therefore, this routine must be called
// before IoCompleteRequest, IoSkipCurrentStackLocation, and PoCallDriver.
PoStartNextPowerIrp(Irp);
// PoCallDriver is used to pass any power IRPs to the PDO instead of IoCallDriver.
// When passing a power IRP down to a lower-level driver, the caller should use
// IoSkipCurrentIrpStackLocation or IoCopyCurrentIrpStackLocationToNext to copy the IRP to
// the next stack location, then call PoCallDriver. Use IoCopyCurrentIrpStackLocationToNext
// if processing the IRP requires setting a completion routine, or IoSkipCurrentStackLocation
// if no completion routine is needed.
PoCallDriver(DeviceExtension->StackDeviceObject,
Irp);
DeviceExtension->PowerIrp = NULL;
DEBUG_LOG_PATH("exit UsbSer_PoRequestCompletion");
return Status;
} // UsbSer_PoRequestCompletion
/*++
Routine Description:
This routine is called when An IRP_MN_SET_POWER of type 'DevicePowerState'
has been received by Usbser_ProcessPowerIrp(), and that routine has determined
1) the request is for full powerup ( to PowerDeviceD0 ), and
2) We are not already in that state
A call is then made to PoRequestPowerIrp() with this routine set as the completion routine.
Arguments:
DeviceObject - Pointer to the device object for the class device.
Irp - Irp completed.
Context - Driver defined context.
Return Value:
The function value is the final status from the operation.
--*/
NTSTATUS
UsbSer_PowerIrp_Complete(IN PDEVICE_OBJECT NullDeviceObject, IN PIRP Irp, IN PVOID Context)
{
NTSTATUS Status = STATUS_SUCCESS;
PDEVICE_OBJECT DeviceObject;
PIO_STACK_LOCATION IrpStack;
PDEVICE_EXTENSION DeviceExtension;
KIRQL OldIrql;
DEBUG_LOG_PATH("enter UsbSer_PowerIrp_Complete");
DEBUG_TRACE1(("UsbSer_PowerIrp_Complete\n"));
DeviceObject = (PDEVICE_OBJECT) Context;
DeviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
// If the lower driver returned PENDING, mark our stack location as pending also.
if(Irp->PendingReturned)
{
IoMarkIrpPending(Irp);
}
IrpStack = IoGetCurrentIrpStackLocation (Irp);
// Now that we know we've let the lower drivers do what was needed to power up,
// we can set our device extension flags accordingly
ACQUIRE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, &OldIrql);
DeviceExtension->CurrentDevicePowerState = PowerDeviceD0;
DeviceExtension->AcceptingRequests = TRUE;
RELEASE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, OldIrql);
//
// Restart the read and notify which we stopped when we powered down
//
RestartRead(DeviceExtension);
RestartNotifyRead(DeviceExtension);
UsbSerRestoreModemSettings(DeviceObject);
Irp->IoStatus.Status = Status;
DEBUG_LOG_PATH("exit UsbSer_PowerIrp_Complete");
return Status;
} // UsbSer_PowerIrp_Complete
/*++
Routine Description:
Called on Usbser_PnPAddDevice() to power down until needed (i.e., till a pipe is actually opened).
Called on Usbser_Create() to power up device to D0 before opening 1st pipe.
Called on Usbser_Close() to power down device if this is the last pipe.
Arguments:
DeviceObject - Pointer to the device object
Suspend; TRUE to Suspend, FALSE to acivate.
Return Value:
If the operation is not attemtped, SUCCESS is returned.
If the operation is attemtped, the value is the final status from the operation.
--*/
NTSTATUS
UsbSer_SelfSuspendOrActivate(IN PDEVICE_OBJECT DeviceObject, IN BOOLEAN Suspend)
{
NTSTATUS Status = STATUS_SUCCESS;
POWER_STATE PowerState;
PDEVICE_EXTENSION DeviceExtension;
DEBUG_LOG_PATH("enter UsbSer_SelfSuspendOrActivate");
DEBUG_TRACE1(("UsbSer_SelfSuspendOrActivate\n"));
DeviceExtension = DeviceObject->DeviceExtension;
// Can't accept request if:
// 1) device is removed,
// 2) has never been started,
// 3) is stopped,
// 4) has a remove request pending,
// 5) has a stop device pending
if(!DeviceExtension->AcceptingRequests)
{
Status = STATUS_DELETE_PENDING;
return Status;
}
// don't do anything if any System-generated Device Pnp irps are pending
if(NULL != DeviceExtension->PowerIrp )
{
return Status;
}
// don't do anything if any self-generated Device Pnp irps are pending
if(DeviceExtension->SelfPowerIrp )
{
return Status;
}
// dont do anything if registry CurrentControlSet\Services\BulkUsb\Parameters\PowerDownLevel
// has been set to zero, PowerDeviceD0 ( 1 ), or a bogus high value
if((DeviceExtension->PowerDownLevel == PowerDeviceD0) ||
(DeviceExtension->PowerDownLevel == PowerDeviceUnspecified) ||
(DeviceExtension->PowerDownLevel >= PowerDeviceMaximum))
{
return Status;
}
if(Suspend)
PowerState.DeviceState = DeviceExtension->PowerDownLevel;
else
PowerState.DeviceState = PowerDeviceD0; // power up all the way; we're probably just about to do some IO
Status = UsbSer_SelfRequestPowerIrp(DeviceObject, PowerState);
DEBUG_LOG_PATH("exit UsbSer_SelfSuspendOrActivate");
return Status;
} // UsbSer_SelfSuspendOrActivate
/*++
Routine Description:
This routine is called by UsbSer_SelfSuspendOrActivate() to
actually make the system request for a powerdown/up to PowerState.
It first checks to see if we are already in Powerstate and immediately
returns SUCCESS with no further processing if so
Arguments:
DeviceObject - Pointer to the device object
PowerState. power state requested, e.g PowerDeviceD0.
Return Value:
The function value is the final status from the operation.
--*/
NTSTATUS
UsbSer_SelfRequestPowerIrp(IN PDEVICE_OBJECT DeviceObject, IN POWER_STATE PowerState)
{
NTSTATUS Status = STATUS_SUCCESS;
PDEVICE_EXTENSION DeviceExtension;
PIRP Irp = NULL;
DEBUG_LOG_PATH("enter UsbSer_SelfRequestPowerIrp");
DEBUG_TRACE1(("UsbSer_SelfRequestPowerIrp\n"));
DeviceExtension = DeviceObject->DeviceExtension;
if(DeviceExtension->CurrentDevicePowerState == PowerState.DeviceState)
return STATUS_SUCCESS; // nothing to do
// flag we're handling a self-generated power irp
DeviceExtension->SelfPowerIrp = TRUE;
// actually request the Irp
Status = PoRequestPowerIrp(DeviceExtension->PhysDeviceObject,
IRP_MN_SET_POWER,
PowerState,
UsbSer_PoSelfRequestCompletion,
DeviceObject,
NULL);
if(Status == STATUS_PENDING)
{
// status pending is the return code we wanted
// We only need to wait for completion if we're powering up
if((ULONG) PowerState.DeviceState < DeviceExtension->PowerDownLevel)
{
NTSTATUS WaitStatus;
WaitStatus = KeWaitForSingleObject(&DeviceExtension->SelfRequestedPowerIrpEvent,
Suspended,
KernelMode,
FALSE,
NULL);
}
Status = STATUS_SUCCESS;
DeviceExtension->SelfPowerIrp = FALSE;
}
else
{
// The return status was not STATUS_PENDING; any other codes must be considered in error here;
// i.e., it is not possible to get a STATUS_SUCCESS or any other non-error return from this call;
}
DEBUG_LOG_PATH("exit UsbSer_SelfRequestPowerIrp");
return Status;
} // UsbSer_SelfRequestPowerIrp
/*++
Routine Description:
This routine is called when the driver completes a self-originated power IRP
that was generated by a call to BulkUsb_SelfSuspendOrActivate().
We power down whenever the last pipe is closed and power up when the first pipe is opened.
For power-up , we set an event in our FDO extension to signal this IRP done
so the power request can be treated as a synchronous call.
We need to know the device is powered up before opening the first pipe, for example.
For power-down, we do not set the event, as no caller waits for powerdown complete.
Arguments:
DeviceObject - Pointer to the device object for the class device. ( Physical Device Object )
Context - Driver defined context, in this case our FDO ( functional device object )
Return Value:
The function value is the final status from the operation.
--*/
NTSTATUS
UsbSer_PoSelfRequestCompletion(IN PDEVICE_OBJECT DeviceObject, IN UCHAR MinorFunction, IN POWER_STATE PowerState,
IN PVOID Context, IN PIO_STATUS_BLOCK IoStatus)
{
PDEVICE_OBJECT ContextDeviceObject = Context;
PDEVICE_EXTENSION DeviceExtension = ContextDeviceObject->DeviceExtension;
NTSTATUS Status = IoStatus->Status;
DEBUG_LOG_PATH("enter UsbSer_PoSelfRequestCompletion");
DEBUG_TRACE1(("UsbSer_PoSelfRequestCompletion\n"));
// We only need to set the event if we're powering up;
// No caller waits on power down complete
if((ULONG) PowerState.DeviceState < DeviceExtension->PowerDownLevel)
{
// Trigger Self-requested power irp completed event;
// The caller is waiting for completion
KeSetEvent(&DeviceExtension->SelfRequestedPowerIrpEvent, 1, FALSE);
}
DEBUG_LOG_PATH("exit UsbSer_PoSelfRequestCompletion");
return Status;
} // UsbSer_PoSelfRequestCompletion
/*++
Routine Description:
This routine is called when An IRP_MN_SET_POWER of type 'DevicePowerState'
has been received by Usbser_ProcessPowerIrp().
Arguments:
DeviceObject - Pointer to the device object for the class device.
DeviceState - Device specific power state to set the device in to.
Return Value:
For requests to DeviceState D0 ( fully on ), returns TRUE to signal caller
that we must set a completion routine and finish there.
--*/
BOOLEAN
UsbSer_SetDevicePowerState(IN PDEVICE_OBJECT DeviceObject, IN DEVICE_POWER_STATE DeviceState)
{
NTSTATUS Status = STATUS_SUCCESS;
PDEVICE_EXTENSION DeviceExtension;
BOOLEAN Res = FALSE;
KIRQL OldIrql;
POWER_STATE State;
DEBUG_LOG_PATH("enter UsbSer_SetDevicePowerState");
DEBUG_TRACE1(("UsbSer_SetDevicePowerState\n"));
DeviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
State.DeviceState = DeviceState;
switch(DeviceState)
{
case PowerDeviceD3:
DEBUG_TRACE1(("PowerDeviceD3\n"));
//
// Device will be going OFF,
//
ACQUIRE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, &OldIrql);
DeviceExtension->CurrentDevicePowerState = DeviceState;
RELEASE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, OldIrql);
UsbSerAbortPipes(DeviceObject);
break;
case PowerDeviceD1:
DEBUG_TRACE1(("PowerDeviceD1\n"));
case PowerDeviceD2:
DEBUG_TRACE1(("PowerDeviceD2\n"));
//
// power states D1, D2 translate to USB suspend
ACQUIRE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, &OldIrql);
DeviceExtension->CurrentDevicePowerState = DeviceState;
RELEASE_SPINLOCK(DeviceExtension, &DeviceExtension->ControlLock, OldIrql);
UsbSerAbortPipes(DeviceObject);
if(DeviceExtension->SendWaitWake && DeviceState <= DeviceExtension->DeviceWake)
{
UsbSerSendWaitWake(DeviceExtension);
}
break;
case PowerDeviceD0:
DEBUG_TRACE1(("PowerDevice0\n"));
// We'll need to finish the rest in the completion routine;
// signal caller we're going to D0 and will need to set a completion routine
Res = TRUE;
// Caller will pass on to PDO ( Physical Device object )
break;
default:
break;
}
PoSetPowerState(DeviceObject, DevicePowerState, State);
DEBUG_LOG_PATH("exit UsbSer_SetDevicePowerState");
return Res;
} // UsbSer_SetDevicePowerState
NTSTATUS
UsbSerQueryCapabilities(IN PDEVICE_OBJECT DeviceObject, IN PDEVICE_CAPABILITIES DeviceCapabilities)
{
PIO_STACK_LOCATION NextStack;
PIRP Irp;
NTSTATUS NtStatus;
KEVENT Event;
DEBUG_LOG_PATH("enter UsbSerQueryCapabilities");
DEBUG_TRACE1(("UsbSerQueryCapabilities\n"));
// Build an IRP for us to generate an internal query request to the PDO
Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
if(!Irp)
return STATUS_INSUFFICIENT_RESOURCES;
NextStack = IoGetNextIrpStackLocation(Irp);
NextStack->MajorFunction= IRP_MJ_PNP;
NextStack->MinorFunction= IRP_MN_QUERY_CAPABILITIES;
// init an event to tell us when the completion routine's been called
KeInitializeEvent(&Event, NotificationEvent, FALSE);
// Set a completion routine so it can signal our event when
// the next lower driver is done with the Irp
IoSetCompletionRoutine(Irp,
UsbSerIrpCompletionRoutine,
&Event, // pass the event as Context to completion routine
TRUE, // invoke on success
TRUE, // invoke on error
TRUE); // invoke on cancellation of the Irp
// init Irp and query struct
Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
RtlZeroMemory(DeviceCapabilities, sizeof(DEVICE_CAPABILITIES));
DeviceCapabilities->Version = DEVICE_CAP_VERSION;
DeviceCapabilities->Size = sizeof(DEVICE_CAPABILITIES);
DeviceCapabilities->Address = DEVICE_CAP_UNUSED_PARAM;
DeviceCapabilities->UINumber = DEVICE_CAP_UNUSED_PARAM;
DeviceCapabilities->SurpriseRemovalOK = TRUE;
// set our pointer to the DEVICE_CAPABILITIES struct
NextStack->Parameters.DeviceCapabilities.Capabilities = DeviceCapabilities;
NtStatus = IoCallDriver(DeviceObject, Irp);
if(NtStatus == STATUS_PENDING)
{
// wait for irp to complete
KeWaitForSingleObject(
&Event,
Suspended,
KernelMode,
FALSE,
NULL);
}
IoFreeIrp(Irp);
DEBUG_LOG_PATH("exit UsbSerQueryCapabilities");
return NtStatus;
} // UsbSerQueryCapabilities
NTSTATUS
UsbSerIrpCompletionRoutine(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context)
{
PKEVENT Event = Context;
DEBUG_LOG_PATH("enter UsbSerIrpCompletionRoutine");
DEBUG_TRACE1(("UsbSerIrpCompletionRoutine\n"));
// Set the input event
KeSetEvent(Event,
1, // Priority increment for waiting thread.
FALSE); // Flag this call is not immediately followed by wait.
DEBUG_LOG_PATH("exit UsbSerIrpCompletionRoutine");
return STATUS_MORE_PROCESSING_REQUIRED;
} // UsbSerIrpCompletionRoutine
NTSTATUS
UsbSerWaitWakeIrpCompletionRoutine(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context)
{
PDEVICE_OBJECT DevObj = Context;
PDEVICE_EXTENSION DevExt = DevObj->DeviceExtension;
NTSTATUS Status;
DEBUG_LOG_PATH("enter UsbSerWaitWakeIrpCompletionRoutine");
DEBUG_TRACE1(("UsbSerWaitWakeIrpCompletionRoutine\n"));
DEBUG_TRACE1(("Status (%08X)\n", Irp->IoStatus.Status));
DEBUG_TRACE1(("Tell device to wake up\n"));
// // now tell the device to actually wake up
// UsbSer_SelfSuspendOrActivate(DevObj, FALSE);
Status = Irp->IoStatus.Status;
PoStartNextPowerIrp(Irp);
IoCompleteRequest (Irp, IO_NO_INCREMENT);
DEBUG_LOG_PATH("exit UsbSerWaitWakeIrpCompletionRoutine");
return STATUS_MORE_PROCESSING_REQUIRED;
} // UsbSerWaitWakeIrpCompletionRoutine
VOID
UsbSerFdoIdleNotificationCallback(IN PDEVICE_EXTENSION DevExt)
/*++
Routine Description:
Called when it is time to idle out USB Modem
--*/
{
POWER_STATE powerState;
NTSTATUS ntStatus;
DEBUG_TRACE1(("USB Modem (%08X) going idle\n", DevExt));
if(DevExt->DeviceState == DEVICE_STATE_STOPPED || DevExt->OpenCnt)
{
// Don't idle this modem if it is being stopped, or someone opened it
DEBUG_TRACE1(("USB Modem (%08X) being stopped, abort idle\n", DevExt));
return;
}
powerState.DeviceState = DevExt->DeviceWake;
// request new device power state, wait wake Irp will be posted on request
PoRequestPowerIrp(DevExt->PhysDeviceObject,
IRP_MN_SET_POWER,
powerState,
NULL,
NULL,
NULL);
} // UsbSerFdoIdleNotificationCallback
NTSTATUS
UsbSerFdoIdleNotificationRequestComplete(
PDEVICE_OBJECT DeviceObject,
PIRP Irp,
PDEVICE_EXTENSION DevExt
)
/*++
Routine Description:
Completion routine for the Idle request IRP for the USB Modem device
--*/
{
NTSTATUS ntStatus;
PUSB_IDLE_CALLBACK_INFO idleCallbackInfo;
//
// DeviceObject is NULL because we sent the irp
//
UNREFERENCED_PARAMETER(DeviceObject);
DEBUG_TRACE1(("Idle notification IRP for USB Modem (%08X) completed (%08X)\n",
DevExt, Irp->IoStatus.Status));
// save completion status in device extension
idleCallbackInfo = DevExt->IdleCallbackInfo;
DevExt->IdleCallbackInfo = NULL;
DevExt->PendingIdleIrp = NULL;
// free up callback info
if(idleCallbackInfo)
{
DEBUG_MEMFREE(idleCallbackInfo);
}
ntStatus = Irp->IoStatus.Status;
return ntStatus;
} // UsbSerFdoIdleNotificationRequestComplete
NTSTATUS
UsbSerFdoSubmitIdleRequestIrp(IN PDEVICE_EXTENSION DevExt)
/*++
Routine Description:
Called when all handles to the USB modem are closed. This function allocates
an idle request IOCTL IRP and passes it to the parent's PDO.
--*/
{
PIRP irp = NULL;
NTSTATUS ntStatus = STATUS_SUCCESS;
PUSB_IDLE_CALLBACK_INFO idleCallbackInfo = NULL;
DEBUG_TRACE1(("UsbSerFdoSubmitIdleRequestIrp (%08X)\n", DevExt));
// if we have an Irp pending, or we are already idled, don't bother to send another
if(DevExt->PendingIdleIrp || DevExt->CurrentDevicePowerState == DevExt->DeviceWake)
return ntStatus;
idleCallbackInfo = DEBUG_MEMALLOC(NonPagedPool,
sizeof(struct _USB_IDLE_CALLBACK_INFO));
if (idleCallbackInfo)
{
idleCallbackInfo->IdleCallback = UsbSerFdoIdleNotificationCallback;
idleCallbackInfo->IdleContext = (PVOID)DevExt;
DevExt->IdleCallbackInfo = idleCallbackInfo;
irp = IoBuildDeviceIoControlRequest(
IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION,
DevExt->PhysDeviceObject,
idleCallbackInfo,
sizeof(struct _USB_IDLE_CALLBACK_INFO),
NULL,
0,
TRUE, /* INTERNAL */
NULL,
&DevExt->StatusBlock);
if (irp == NULL)
{
DEBUG_MEMFREE(idleCallbackInfo);
return STATUS_INSUFFICIENT_RESOURCES;
}
IoSetCompletionRoutine(irp,
UsbSerFdoIdleNotificationRequestComplete,
DevExt,
TRUE,
TRUE,
TRUE);
ntStatus = IoCallDriver(DevExt->PhysDeviceObject, irp);
if(ntStatus == STATUS_PENDING)
{
DEBUG_TRACE1(("USB Modem (%08X) Idle Irp Pending\n", DevExt));
// Successfully posted an Idle IRP.
DevExt->PendingIdleIrp = irp;
}
}
return ntStatus;
} // UsbSerFdoSubmitIdleRequestIrp
VOID
UsbSerFdoRequestWake(IN PDEVICE_EXTENSION DevExt)
/*++
Routine Description:
Called when we want to wake up the device after an idle request
--*/
{
POWER_STATE powerState;
DEBUG_TRACE1(("USB Modem (%08X) waking up\n", DevExt));
if(DevExt->DeviceState == DEVICE_STATE_STOPPED || DevExt->CurrentDevicePowerState == PowerDeviceD0)
{
// Don't wake this modem if it is stopped, or already at D0
DEBUG_TRACE1(("USB Modem (%08X) abort wake\n", DevExt));
return;
}
powerState.DeviceState = PowerDeviceD0;
// request new device power state, wake up the device
PoRequestPowerIrp(DevExt->PhysDeviceObject,
IRP_MN_SET_POWER,
powerState,
NULL,
NULL,
NULL);
} // UsbSerFdoRequestWake