Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2937 lines
83 KiB

/*++
Copyright (c) 1999 Microsoft Corporation
Module Name:
power.c
Abstract:
the power code
Environment:
kernel mode only
Notes:
Revision History:
6-20-99 : created
--*/
#include "common.h"
// paged functions
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, USBPORT_ComputeRootHubDeviceCaps)
#pragma alloc_text(PAGE, USBPORT_ComputeHcPowerStates)
#endif
// non paged functions
//USBPORT_PdoPowerIrp
//USBPORT_FdoPowerIrp
//USBPORT_SystemPowerState
//USBPORT_DevicePowerState
//USBPORT_PoRequestCompletion
//USBPORT_CancelPendingWakeIrp
#if DBG
PUCHAR
S_State(
SYSTEM_POWER_STATE S
)
{
switch (S) {
case PowerSystemUnspecified:
return "SystemUnspecified S?";
case PowerSystemWorking:
return "SystemWorking S0";
case PowerSystemSleeping1:
return "SystemSleeping1 S1";
case PowerSystemSleeping2:
return "SystemSleeping2 S2";
case PowerSystemSleeping3:
return "SystemSleeping3 S3";
case PowerSystemHibernate:
return "SystemHibernate";
case PowerSystemShutdown:
return "SystemShutdown";
case PowerSystemMaximum:
return "SystemMaximum";
}
return "???";
}
PUCHAR
D_State(
DEVICE_POWER_STATE D
)
{
switch (D) {
case PowerDeviceUnspecified:
return "D?";
case PowerDeviceD0:
return "D0";
case PowerDeviceD1:
return "D1";
case PowerDeviceD2:
return "D2";
case PowerDeviceD3:
return "D3";
case PowerDeviceMaximum:
return "DX";
}
return "??";
}
#endif
PHC_POWER_STATE
USBPORT_GetHcPowerState(
PDEVICE_OBJECT FdoDeviceObject,
PHC_POWER_STATE_TABLE HcPowerStateTbl,
SYSTEM_POWER_STATE SystemState
)
/*++
Routine Description:
For a given system power state return a pointer to
the hc power state stored in the device extension.
Arguments:
Return Value:
NTSTATUS
--*/
{
PDEVICE_EXTENSION devExt;
PHC_POWER_STATE powerState;
ULONG i;
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
powerState = NULL;
for (i=0; i<USBPORT_MAPPED_SLEEP_STATES; i++) {
if (HcPowerStateTbl->PowerState[i].SystemState ==
SystemState) {
powerState = &HcPowerStateTbl->PowerState[i];
break;
}
}
USBPORT_ASSERT(powerState != NULL);
LOGENTRY(NULL, FdoDeviceObject, LOG_POWER, 'ghcP', 0, powerState, 0);
return powerState;
}
VOID
USBPORT_ComputeHcPowerStates(
PDEVICE_OBJECT FdoDeviceObject,
PDEVICE_CAPABILITIES HcDeviceCaps,
PHC_POWER_STATE_TABLE HcPowerStateTbl
)
/*++
Routine Description:
Using the HC capabilities reported by the parent bus compute
the host controllers power attributes.
Power attributes are defined as follows:
{ attributes }
| SystemState | DeviceState | Powered? | Wake? |
+-------------+---------------+------------+--------+
S1-S4 D0-D3 Y/N Y/N
The table includes entries for every possible system sleep state
the OS may ask us to enter.
(S1) PowerSystemSleeping1
(S2) PowerSystemSleeping2
(S3) PowerSystemSleeping3
(S4) PowerSystemHibernate
We have four possible cases for each sleep state:
Powered? Wake?
case 1 Y Y
case 2 N N
case 3 Y N
*case 4 N Y
currently we only support cases 1 & 2 but we recognize all 4
in the event we need to support them all.
In reality there exists a lot of case 3 but we currently have
no way to detect it.
Arguments:
Return Value:
none
--*/
{
SYSTEM_POWER_STATE s;
ULONG i;
SYSTEM_POWER_STATE systemWake;
DEVICE_POWER_STATE deviceWake;
PAGED_CODE();
systemWake = HcDeviceCaps->SystemWake;
deviceWake = HcDeviceCaps->DeviceWake;
// The HC can wake the system for any sleep state lighter (<=)
// systemWake
// iniialize the table
s = PowerSystemSleeping1;
for (i=0; i<USBPORT_MAPPED_SLEEP_STATES; i++) {
HcPowerStateTbl->PowerState[i].SystemState = s;
HcPowerStateTbl->PowerState[i].DeviceState =
HcDeviceCaps->DeviceState[s];
// it follows that if the map indicates that the DeviceState
// is D3 but the system state is still <= SystemWake then the
// hc is still powered
if (s <= systemWake) {
if (HcDeviceCaps->DeviceState[s] == PowerDeviceUnspecified) {
// for unspecified we go with case 2, ie no power
// case 2
HcPowerStateTbl->PowerState[i].Attributes =
HcPower_N_Wakeup_N;
} else {
// case 1
HcPowerStateTbl->PowerState[i].Attributes =
HcPower_Y_Wakeup_Y;
}
} else {
if (HcDeviceCaps->DeviceState[s] == PowerDeviceD3 ||
HcDeviceCaps->DeviceState[s] == PowerDeviceUnspecified) {
// case 2
HcPowerStateTbl->PowerState[i].Attributes =
HcPower_N_Wakeup_N;
} else {
//
// case 3
HcPowerStateTbl->PowerState[i].Attributes =
HcPower_Y_Wakeup_N;
}
}
// 330157
// disable wake from s4 since we do not support it yet
//if (s == PowerSystemHibernate) {
// HcPowerStateTbl->PowerState[i].Attributes =
// HcPower_N_Wakeup_N;
//}
s++;
}
USBPORT_ASSERT(s == PowerSystemShutdown);
}
VOID
USBPORT_ComputeRootHubDeviceCaps(
PDEVICE_OBJECT FdoDeviceObject,
PDEVICE_OBJECT PdoDeviceObject
)
/*++
Routine Description:
Attempt to create the root hub
Power Summary:
<Gloassary>
Lightest - PowerDeviceD0, PowerSystemWorking
Deepest - PowerDeviceD3, PowerSystemHibernate
SystemWake - this is defined to be the 'deepest' System state in which
the hardware can wake the system.
DeviceWake -
DeviceState[] - map of system states and the corresponding D states
these are the states the HW is in for any given
System Sleep state.
HostControllerPowerAttributes - we define our own structure to describe
the attributes of a host comtroller -- this allows us to
map all possible controller scenarios on to the messed
up WDM power rules.
Arguments:
Return Value:
NTSTATUS
--*/
{
PDEVICE_CAPABILITIES hcDeviceCaps, rhDeviceCaps;
PDEVICE_EXTENSION rhDevExt, devExt;
BOOLEAN wakeupSupport;
SYSTEM_POWER_STATE s;
PAGED_CODE();
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
GET_DEVICE_EXT(rhDevExt, PdoDeviceObject);
ASSERT_PDOEXT(rhDevExt);
hcDeviceCaps = &devExt->DeviceCapabilities;
rhDeviceCaps = &rhDevExt->DeviceCapabilities;
// do we wish to support wakeup?
// if the USBPORT_FDOFLAG_ENABLE_SYSTEM_WAKE flag NOT is set
// then wakeup is disabled and the HC power attributes have been
// modified to reflect this.
if (TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_ENABLE_SYSTEM_WAKE)) {
wakeupSupport = TRUE;
} else {
// wakeup is disabled
USBPORT_KdPrint((1, " USB SYSTEM WAKEUP is Disabled\n"));
wakeupSupport = FALSE;
}
#if DBG
if (wakeupSupport) {
USBPORT_KdPrint((1, " USB SYSTEM WAKEUP is Supported\n"));
} else {
USBPORT_KdPrint((1, " USB SYSTEM WAKEUP is NOT Supported\n"));
}
#endif
// clone capabilities from the HC
RtlCopyMemory(rhDeviceCaps,
hcDeviceCaps,
sizeof(DEVICE_CAPABILITIES));
// construct the root hub device capabilities
// root hub is not removable
rhDeviceCaps->Removable=FALSE;
rhDeviceCaps->UniqueID=FALSE;
rhDeviceCaps->Address = 0;
rhDeviceCaps->UINumber = 0;
// for the root hub D2 translates to 'USB suspend'
// so we always indicate we can wake from D2
rhDeviceCaps->DeviceWake = PowerDeviceD2;
rhDeviceCaps->WakeFromD0 = TRUE;
rhDeviceCaps->WakeFromD1 = FALSE;
rhDeviceCaps->WakeFromD2 = TRUE;
rhDeviceCaps->WakeFromD3 = FALSE;
rhDeviceCaps->DeviceD2 = TRUE;
rhDeviceCaps->DeviceD1 = FALSE;
// generate the root hub power capabilities from the
// HC Power Attributes plus a little magic
USBPORT_ASSERT(rhDeviceCaps->SystemWake >= PowerSystemUnspecified &&
rhDeviceCaps->SystemWake <= PowerSystemMaximum);
rhDeviceCaps->SystemWake =
(PowerSystemUnspecified == rhDeviceCaps->SystemWake) ?
PowerSystemWorking :
rhDeviceCaps->SystemWake;
for (s=PowerSystemSleeping1; s<=PowerSystemHibernate; s++) {
PHC_POWER_STATE hcPowerState;
hcPowerState = USBPORT_GetHcPowerState(FdoDeviceObject,
&devExt->Fdo.HcPowerStateTbl,
s);
if (hcPowerState != NULL) {
switch (hcPowerState->Attributes) {
case HcPower_Y_Wakeup_Y:
rhDeviceCaps->DeviceState[s] = PowerDeviceD2;
break;
case HcPower_N_Wakeup_N:
case HcPower_Y_Wakeup_N:
case HcPower_N_Wakeup_Y:
rhDeviceCaps->DeviceState[s] = PowerDeviceD3;
break;
}
}
}
}
NTSTATUS
USBPORT_PoRequestCompletion(
PDEVICE_OBJECT DeviceObject,
UCHAR MinorFunction,
POWER_STATE PowerState,
PVOID Context,
PIO_STATUS_BLOCK IoStatus
)
/*++
Routine Description:
Called when the Device Power State Irp we requested is completed.
this is where we Call down the systemPowerIrp
Arguments:
DeviceObject - Pointer to the device object for the class device.
DevicePowerState - The Dx that we are in/tagetted.
Context - Driver defined context.
IoStatus - The status of the IRP.
Return Value:
The function value is the final status from the operation.
--*/
{
PIRP irp;
PDEVICE_EXTENSION devExt;
PDEVICE_OBJECT fdoDeviceObject = Context;
NTSTATUS ntStatus = IoStatus->Status;
// a call to this function basically tells us
// that we are now in the requested D-state
// we now finish the whole process by calling
// down the original SysPower request to our
// PDO
GET_DEVICE_EXT(devExt, fdoDeviceObject);
ASSERT_FDOEXT(devExt);
USBPORT_KdPrint((1,
"PoRequestComplete fdo(%x) MN_SET_POWER DEV(%s)\n",
fdoDeviceObject, D_State(PowerState.DeviceState)));
LOGENTRY(NULL, fdoDeviceObject, LOG_POWER, 'PwCp', ntStatus,
devExt->CurrentDevicePowerState, PowerState.DeviceState);
// note that if the SetD0 has failed we do not attempt
// to re-start the controller
if (NT_SUCCESS(ntStatus)) {
if (PowerState.DeviceState == PowerDeviceD0) {
#ifdef XPSE
{
LARGE_INTEGER t, dt;
// compute time to D0
KeQuerySystemTime(&t);
dt.QuadPart = t.QuadPart - devExt->Fdo.D0ResumeTimeStart.QuadPart;
devExt->Fdo.D0ResumeTime = (ULONG) (dt.QuadPart/10000);
KeQuerySystemTime(&devExt->Fdo.ThreadResumeTimeStart);
USBPORT_KdPrint((1, " D0ResumeTime %d ms %x %x\n",
devExt->Fdo.D0ResumeTime,
t.HighPart, t.LowPart));
}
#endif
// defer start to our worker thread or workitem
SET_FDO_FLAG(devExt, USBPORT_FDOFLAG_NEED_SET_POWER_D0);
MP_FlushInterrupts(devExt);
if (USBPORT_IS_USB20(devExt)) {
MP_TakePortControl(devExt);
}
USBPORT_SignalWorker(fdoDeviceObject);
// enable sligthtly faster completion of S irps
USBPORT_QueuePowerWorkItem(fdoDeviceObject);
// on completion of this function the controller is
// in D0, we may not have powered up yet though.
devExt->CurrentDevicePowerState = PowerDeviceD0;
} else {
// we should not receive another power irp until
// we make a call to PoStartNextPowerIrp so there
// is no protection here.
irp = devExt->SystemPowerIrp;
devExt->SystemPowerIrp = NULL;
USBPORT_ASSERT(irp != NULL);
IoCopyCurrentIrpStackLocationToNext(irp);
PoStartNextPowerIrp(irp);
DECREMENT_PENDING_REQUEST_COUNT(fdoDeviceObject, irp);
PoCallDriver(devExt->Fdo.TopOfStackDeviceObject,
irp);
devExt->CurrentDevicePowerState = PowerState.DeviceState;
}
} else {
// try to complete the irp with an error but don't attempt
// to power the bus
irp = devExt->SystemPowerIrp;
devExt->SystemPowerIrp = NULL;
USBPORT_ASSERT(irp != NULL);
IoCopyCurrentIrpStackLocationToNext(irp);
PoStartNextPowerIrp(irp);
DECREMENT_PENDING_REQUEST_COUNT(fdoDeviceObject, irp);
// According to adriano 'S IRP should be immediately completed with
// the same status as the D IRP in the failure case'
// Since the method of handling this is not documented anywhere we will
// go with what Adrian says.
//
// The fact that this request has failed will probably cause other
// complaints
irp->IoStatus.Status = ntStatus;
IoCompleteRequest(irp,
IO_NO_INCREMENT);
//PoCallDriver(devExt->Fdo.TopOfStackDeviceObject,
// irp);
// set the system irp status
// note that the current power state is now undefined
}
// Note that the status returned here does not matter, this routine
// is called by the kernel (PopCompleteRequestIrp) when the irp
// completes to PDO and this function ignores the returned status.
// PopCompleteRequestIrp also immediatly frees the irp so we need
// take care not to reference it after this routine has run.
return ntStatus;
}
NTSTATUS
USBPORT_FdoSystemPowerState(
PDEVICE_OBJECT FdoDeviceObject,
PIRP Irp
)
/*++
Routine Description:
Handle SystemPowerState Messages for the HC FDO
Arguments:
DeviceObject - pointer to a hcd device object (FDO)
Irp - pointer to an I/O Request Packet
Return Value:
NT status code
--*/
{
PIO_STACK_LOCATION irpStack;
NTSTATUS ntStatus;
PDEVICE_EXTENSION devExt;
POWER_STATE powerState;
SYSTEM_POWER_STATE requestedSystemState;
PHC_POWER_STATE hcPowerState;
irpStack = IoGetCurrentIrpStackLocation(Irp);
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
USBPORT_ASSERT(irpStack->MajorFunction == IRP_MJ_POWER);
USBPORT_ASSERT(irpStack->MinorFunction == IRP_MN_SET_POWER);
USBPORT_ASSERT(irpStack->Parameters.Power.Type == SystemPowerState);
requestedSystemState = irpStack->Parameters.Power.State.SystemState;
USBPORT_KdPrint((1,
"MJ_POWER HC fdo(%x) MN_SET_POWER SYS(%s)\n",
FdoDeviceObject, S_State(requestedSystemState)));
LOGENTRY(NULL, FdoDeviceObject, LOG_POWER, 'RspS', 0,
FdoDeviceObject, requestedSystemState);
// ** begin special case
// OS may send us a power irps even if we are not 'started'. In this
// case we just pass them on with 'STATUS_SUCCESS' since we don't
// really need to do anything.
if (!TEST_FLAG(devExt->PnpStateFlags, USBPORT_PNP_STARTED)) {
// we should probably be in an 'Unspecified' power state.
ntStatus = STATUS_SUCCESS;
goto USBPORT_FdoSystemPowerState_Done;
}
// ** end special case
// compute the appropriate D-state
// remember the last 'sleep' system state we entered
// for debugging
if (requestedSystemState != PowerSystemWorking) {
devExt->Fdo.LastSystemSleepState = requestedSystemState;
}
switch (requestedSystemState) {
case PowerSystemWorking:
//
// go to 'ON'
//
powerState.DeviceState = PowerDeviceD0;
#ifdef XPSE
KeQuerySystemTime(&devExt->Fdo.S0ResumeTimeStart);
#endif
break;
case PowerSystemShutdown:
USBPORT_KdPrint((1, " >Shutdown HC Detected\n"));
// For this driver this will always map to D3.
//
// this driver will only run on Win98gold or Win98se
// to support USB2 controllers that don't have Legacy
// BIOS.
//
// For Win98 Millenium or Win2k it doesn't matter if
// the controllers have a BIOS since we never hand
// control back to DOS.
// not sure yet if it is legitimate to 'Wake' from shutdown
// or how we are supposed to handle this. Some BIOSes Falsely
// report that they can do this.
powerState.DeviceState = PowerDeviceD3;
USBPORT_TurnControllerOff(FdoDeviceObject);
break;
case PowerSystemHibernate:
USBPORT_KdPrint((1, " >Hibernate HC Detected\n"));
// powerState.DeviceState = PowerDeviceD3;
//
// USBPORT_TurnControllerOff(FdoDeviceObject);
// break;
case PowerSystemSleeping1:
case PowerSystemSleeping2:
case PowerSystemSleeping3:
{
PDEVICE_EXTENSION rhDevExt;
USBPORT_ASSERT(devExt->Fdo.RootHubPdo != NULL);
GET_DEVICE_EXT(rhDevExt, devExt->Fdo.RootHubPdo);
ASSERT_PDOEXT(rhDevExt);
USBPORT_KdPrint((1, " >Sleeping Detected\n"));
//
// Take action based on what happens to the controller
// in the requested S state. This minimizes the chance
// of confusing the Hub driver or other USB devices/drivers.
// It also speeds up the resume process.
// get our power info summary
hcPowerState = USBPORT_GetHcPowerState(FdoDeviceObject,
&devExt->Fdo.HcPowerStateTbl,
requestedSystemState);
// keep lint tools happy.
if (hcPowerState == NULL) {
return STATUS_UNSUCCESSFUL;
}
// get the current power state of the root hub
if (rhDevExt->CurrentDevicePowerState == PowerDeviceD2 ||
rhDevExt->CurrentDevicePowerState == PowerDeviceD1) {
USBPORT_ASSERT(hcPowerState->Attributes == HcPower_Y_Wakeup_Y ||
hcPowerState->Attributes == HcPower_Y_Wakeup_N);
// take action on the controller
USBPORT_SuspendController(FdoDeviceObject);
// it is 'impure' for the controller to interrupt while in a
// low power state so if we suspended it we disable interrupts now.
// The presence of a wake IRP should enable the PME that wakes
// the system.
// **We disable here so that we don't take a resume interrupt from
// the controller while going into suspend
if (hcPowerState->DeviceState != PowerDeviceD0) {
MP_DisableInterrupts(FdoDeviceObject, devExt);
}
// select the D state for the HC
powerState.DeviceState = hcPowerState->DeviceState;
// if the root hub is enabled for wakeup and this this
// system state supports it then mark the controller as
// 'enabled' for wake.
if (USBPORT_RootHubEnabledForWake(FdoDeviceObject) &&
hcPowerState->Attributes == HcPower_Y_Wakeup_Y) {
DEBUG_BREAK();
SET_FDO_FLAG(devExt, USBPORT_FDOFLAG_WAKE_ENABLED);
if (hcPowerState->DeviceState == PowerDeviceD0) {
USBPORT_ArmHcForWake(FdoDeviceObject);
USBPORT_Wait(FdoDeviceObject, 100);
}
}
} else {
// if the controller remains powered then it is optimal
// to 'suspend' otherwise we must turn it off
// always 'suspend' the USB 2 controller, this will hopefuly
// keep us from restting the CC in the case where a device
// on the CC is enabled for wake for a the 20 controller is
// not
if ((hcPowerState->Attributes == HcPower_Y_Wakeup_Y ||
hcPowerState->Attributes == HcPower_Y_Wakeup_N ||
USBPORT_IS_USB20(devExt)) &&
!TEST_FLAG(rhDevExt->PnpStateFlags, USBPORT_PNP_REMOVED)) {
USBPORT_SuspendController(FdoDeviceObject);
powerState.DeviceState = hcPowerState->DeviceState;
if (USBPORT_IS_USB20(devExt) &&
powerState.DeviceState == PowerDeviceUnspecified) {
// if no state specified go to D3
powerState.DeviceState = PowerDeviceD3;
}
// clear the IRQ enabled flag since it is invalid for
// the hardware to interrupt in any state but D0
// it is 'impure' for the controller to interrupt while in a
// low power state so if we suspended it we disable interrupts now.
// The presence of a wake IRP should enable the PME that wakes
// the system.
// **We disable here so that we don't take a resume interrupt from
// the controller while going into suspend
MP_DisableInterrupts(FdoDeviceObject, devExt);
} else {
USBPORT_TurnControllerOff(FdoDeviceObject);
powerState.DeviceState = PowerDeviceD3;
}
}
} // PowerSystemSleepingX
break;
default:
// This is the case where the requested system state is unkown
// to us. It not clear what to do here.
// Vince sez try to ignore it so we will
powerState.DeviceState = devExt->CurrentDevicePowerState;
//powerState.DeviceState = PowerDeviceD3;
//USBPORT_TurnControllerOff(FdoDeviceObject);
DEBUG_BREAK();
}
//
// now based on the D state request a Power irp
// if necessary
//
//
// are we already in this state?
//
// Note: if we get a D3 request before we are started
// we don't need to pass the irp down to turn us off
// we consider the controller initially off until we
// get start.
//
if (devExt->CurrentDevicePowerState != powerState.DeviceState) {
// No,
// now allocate another irp and use PoCallDriver
// to send it to ourselves
IoMarkIrpPending(Irp);
// remember the system power irp, we should
// not receive another power irp until we
// make a call to PoStartNextPowerIrp so there
// is no protection here.
USBPORT_ASSERT(devExt->SystemPowerIrp == NULL);
devExt->SystemPowerIrp = Irp;
USBPORT_KdPrint((1, " >Requesting HC D-State - %s\n",
D_State(powerState.DeviceState)));
LOGENTRY(NULL, FdoDeviceObject, LOG_POWER, 'RqPw', FdoDeviceObject,
devExt->CurrentDevicePowerState, powerState.DeviceState);
#ifdef XPSE
KeQuerySystemTime(&devExt->Fdo.D0ResumeTimeStart);
#endif
ntStatus =
PoRequestPowerIrp(devExt->Fdo.PhysicalDeviceObject,
IRP_MN_SET_POWER,
powerState,
USBPORT_PoRequestCompletion,
FdoDeviceObject,
NULL);
// hardcode STATUS_PENDING so that it is is returned
// by the Dispatch routine
// can we rely on what PoRequestPowerIrp returns?
ntStatus = STATUS_PENDING;
} else {
// Yes,
// We are already in the requested D state
// just pass this irp along
if (powerState.DeviceState == PowerDeviceD0) {
MP_EnableInterrupts(devExt);
}
ntStatus = STATUS_SUCCESS;
}
USBPORT_FdoSystemPowerState_Done:
return ntStatus;
}
NTSTATUS
USBPORT_FdoDevicePowerState(
PDEVICE_OBJECT FdoDeviceObject,
PIRP Irp
)
/*++
Routine Description:
Handle DevicePowerState Messages for the HC FDO
Arguments:
DeviceObject - pointer to a hcd device object (FDO)
Irp - pointer to an I/O Request Packet
Return Value:
NT status code
returning STATUS_PENDING indicates that the Irp should
not be called down to the PDO yet.
--*/
{
PIO_STACK_LOCATION irpStack;
NTSTATUS ntStatus;
PDEVICE_EXTENSION devExt;
POWER_STATE powerState;
DEVICE_POWER_STATE requestedDeviceState;
irpStack = IoGetCurrentIrpStackLocation(Irp);
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
USBPORT_ASSERT(irpStack->MajorFunction == IRP_MJ_POWER);
USBPORT_ASSERT(irpStack->MinorFunction == IRP_MN_SET_POWER);
USBPORT_ASSERT(irpStack->Parameters.Power.Type == DevicePowerState);
requestedDeviceState = irpStack->Parameters.Power.State.SystemState;
USBPORT_KdPrint((1,
"MJ_POWER HC fdo(%x) MN_SET_POWER DEV(%s)\n",
FdoDeviceObject, D_State(requestedDeviceState)));
switch (requestedDeviceState) {
case PowerDeviceD0:
// we cannot enter D0 until we pass the power irp
// down to our parent BUS. return success here - we
// will turn the controller on from the completion
// routine of the original request for this power
// irp
ntStatus = STATUS_SUCCESS;
break;
case PowerDeviceD1:
case PowerDeviceD2:
case PowerDeviceD3:
// we took action when we received the SystemPowerMessage
// because thats when we know what the state of the HW will be.
// it is 'impure' for the controller to interrupt while in a
// low power state so if we suspended it we disable interrupts now.
// The presence of a wake IRP should enable the PME that wakes
// the system.
MP_DisableInterrupts(FdoDeviceObject, devExt);
//
if (USBPORT_IS_USB20(devExt)) {
PDEVICE_RELATIONS devR;
// set magic count to number of CCs plus usb2 controller
devR = USBPORT_FindCompanionControllers(FdoDeviceObject,
FALSE,
FALSE);
devExt->Fdo.DependentControllers = 0;
if (devR) {
devExt->Fdo.DependentControllers = devR->Count + 1;
FREE_POOL(FdoDeviceObject, devR);
}
}
// if wakeup is enabled (on the root hub PDO) then we will
// enable on the platform before entering the low
// power state.
if (TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_WAKE_ENABLED)) {
USBPORT_ArmHcForWake(FdoDeviceObject);
}
ntStatus = STATUS_SUCCESS;
break;
case PowerDeviceUnspecified:
// for unspecified we will turn the HW off -- I'm not sure we
// will ever see this since the D messages originate from us.
USBPORT_TurnControllerOff(FdoDeviceObject);
ntStatus = STATUS_SUCCESS;
break;
default:
ntStatus = STATUS_UNSUCCESSFUL;
}
return ntStatus;
}
NTSTATUS
USBPORT_FdoPowerIrp(
PDEVICE_OBJECT FdoDeviceObject,
PIRP Irp
)
/*++
Routine Description:
Process the Power IRPs sent to the FDO for the host
controller.
Arguments:
DeviceObject - pointer to a hcd device object (FDO)
Irp - pointer to an I/O Request Packet
Return Value:
NT status code
--*/
{
PIO_STACK_LOCATION irpStack;
NTSTATUS ntStatus;
PDEVICE_EXTENSION devExt;
irpStack = IoGetCurrentIrpStackLocation(Irp);
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
USBPORT_ASSERT(irpStack->MajorFunction == IRP_MJ_POWER);
LOGENTRY(NULL, FdoDeviceObject, LOG_POWER, 'fPow', irpStack->MinorFunction,
FdoDeviceObject, devExt->CurrentDevicePowerState);
LOGENTRY(NULL, FdoDeviceObject, LOG_POWER, 'fpow',
irpStack->Parameters.Others.Argument1, // WAIT_WAKE: PowerState
irpStack->Parameters.Others.Argument2, // SET_POWER: Type
irpStack->Parameters.Others.Argument3); // SET_POWER: State
// map system state to D state
switch (irpStack->MinorFunction) {
case IRP_MN_WAIT_WAKE:
USBPORT_KdPrint((1,
"MJ_POWER HC fdo(%x) MN_WAIT_WAKE\n",
FdoDeviceObject));
ntStatus = USBPORT_ProcessHcWakeIrp(FdoDeviceObject, Irp);
goto USBPORT_FdoPowerIrp_Done;
break;
case IRP_MN_SET_POWER:
if (irpStack->Parameters.Power.Type == SystemPowerState) {
ntStatus = USBPORT_FdoSystemPowerState(FdoDeviceObject, Irp);
} else {
ntStatus = USBPORT_FdoDevicePowerState(FdoDeviceObject, Irp);
}
if (ntStatus == STATUS_PENDING) {
// we deferred to a completion routine
// returned STATUS_PENDING and bail.
goto USBPORT_FdoPowerIrp_Done;
}
break;
case IRP_MN_QUERY_POWER:
// we succeed all requests to enter low power
// states for the HC fdo
Irp->IoStatus.Status = ntStatus = STATUS_SUCCESS;
LOGENTRY(NULL, FdoDeviceObject, LOG_POWER, 'QpFD', 0, 0, ntStatus);
USBPORT_KdPrint((1,
"MJ_POWER HC fdo(%x) MN_QUERY_POWER\n",
FdoDeviceObject));
break;
default:
USBPORT_KdPrint((1,
"MJ_POWER HC fdo(%x) MN_%d not handled\n",
FdoDeviceObject,
irpStack->MinorFunction));
} /* irpStack->MinorFunction */
IoCopyCurrentIrpStackLocationToNext(Irp);
//
// All PNP_POWER POWER messages get passed to the
// top of the PDO stack we attached to when loaded
//
// In some cases we finish processing in a completion
// routine
//
// pass on to our PDO
DECREMENT_PENDING_REQUEST_COUNT(FdoDeviceObject, Irp);
PoStartNextPowerIrp(Irp);
ntStatus =
PoCallDriver(devExt->Fdo.TopOfStackDeviceObject,
Irp);
USBPORT_FdoPowerIrp_Done:
return ntStatus;
}
NTSTATUS
USBPORT_PdoPowerIrp(
PDEVICE_OBJECT PdoDeviceObject,
PIRP Irp
)
/*++
Routine Description:
Disptach routine for Power Irps sent to the PDO for the root hub.
NOTE:
irps sent to the PDO are always completed by the bus driver
Arguments:
DeviceObject - Pdo for the root hub
Return Value:
NTSTATUS
--*/
{
PIO_STACK_LOCATION irpStack;
NTSTATUS ntStatus;
PDEVICE_EXTENSION rhDevExt, devExt;
PDEVICE_OBJECT fdoDeviceObject;
GET_DEVICE_EXT(rhDevExt, PdoDeviceObject);
ASSERT_PDOEXT(rhDevExt);
fdoDeviceObject = rhDevExt->HcFdoDeviceObject;
GET_DEVICE_EXT(devExt, fdoDeviceObject);
ASSERT_FDOEXT(devExt);
irpStack = IoGetCurrentIrpStackLocation (Irp);
USBPORT_ASSERT(irpStack->MajorFunction == IRP_MJ_POWER);
// use whatever status is in the IRP by default
ntStatus = Irp->IoStatus.Status;
// PNP messages for the PDO created for the root hub
LOGENTRY(NULL, fdoDeviceObject, LOG_POWER, 'pPow',
irpStack->MinorFunction, PdoDeviceObject, ntStatus);
LOGENTRY(NULL, fdoDeviceObject, LOG_POWER, 'ppow',
irpStack->Parameters.Others.Argument1, // WAIT_WAKE: PowerState
irpStack->Parameters.Others.Argument2, // SET_POWER: Type
irpStack->Parameters.Others.Argument3); // SET_POWER: State
switch (irpStack->MinorFunction) {
case IRP_MN_WAIT_WAKE:
USBPORT_KdPrint((1,
"MJ_POWER RH pdo(%x) MN_WAIT_WAKE\n",
PdoDeviceObject));
if (TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_ENABLE_SYSTEM_WAKE)) {
KIRQL irql;
PDRIVER_CANCEL cr;
// we only support one wait_wake irp pending
// in the root hub -- basically we have a pending
// irp table with one entry
ACQUIRE_WAKEIRP_LOCK(fdoDeviceObject, irql);
cr = IoSetCancelRoutine(Irp, USBPORT_CancelPendingWakeIrp);
USBPORT_ASSERT(cr == NULL);
if (Irp->Cancel &&
IoSetCancelRoutine(Irp, NULL)) {
// irp was canceled and our cancel routine
// did not run
RELEASE_WAKEIRP_LOCK(fdoDeviceObject, irql);
ntStatus = STATUS_CANCELLED;
// no postartnextpowerIrp for waitwake
goto USBPORT_PdoPowerIrp_Complete;
} else {
// cancel routine is set, if irp is canceled
// the cancel routine will stall on the
// WAKE_IRP_LOCK
if (rhDevExt->Pdo.PendingWaitWakeIrp == NULL) {
// keep the irp in our table, we take no
// other action until we actully enter a
// low power state.
IoMarkIrpPending(Irp);
rhDevExt->Pdo.PendingWaitWakeIrp = Irp;
LOGENTRY(NULL, fdoDeviceObject, LOG_POWER, 'pWWi',
Irp, 0, 0);
ntStatus = STATUS_PENDING;
RELEASE_WAKEIRP_LOCK(fdoDeviceObject, irql);
goto USBPORT_PdoPowerIrp_Done;
} else {
// we already have a wake irp, complete this
// one with STATUS_BUSY.
// note that since it is not in our table if
// the cancel routine is running (ie stalled
// on the WAKEIRP_LOCK it will ignore the irp
// when we release the lock.
if (IoSetCancelRoutine(Irp, NULL) != NULL) {
ntStatus = STATUS_DEVICE_BUSY;
} else {
// let the cancel routine complete it.
RELEASE_WAKEIRP_LOCK(fdoDeviceObject, irql);
goto USBPORT_PdoPowerIrp_Done;
}
}
RELEASE_WAKEIRP_LOCK(fdoDeviceObject, irql);
}
} else {
ntStatus = STATUS_NOT_SUPPORTED;
// no postartnextpowerIrp for waitwake
goto USBPORT_PdoPowerIrp_Complete;
}
break;
case IRP_MN_QUERY_POWER:
ntStatus = STATUS_SUCCESS;
LOGENTRY(NULL, fdoDeviceObject, LOG_POWER, 'RqrP', 0, 0, ntStatus);
USBPORT_KdPrint((1,
"MJ_POWER RH pdo(%x) MN_QUERY_POWER\n",
PdoDeviceObject));
break;
case IRP_MN_SET_POWER:
LOGENTRY(NULL, fdoDeviceObject, LOG_POWER, 'RspP', 0, 0,
irpStack->Parameters.Power.Type);
switch (irpStack->Parameters.Power.Type) {
case SystemPowerState:
LOGENTRY(NULL, fdoDeviceObject, LOG_POWER, 'RspS', 0, 0,
irpStack->Parameters.Power.Type);
//
// since the fdo driver for the root hub pdo is our own
// hub driver and it is well behaved, we don't expect to see
// a system message where the power state is still undefined
//
// we just complete this with success
//
ntStatus = STATUS_SUCCESS;
USBPORT_KdPrint((1,
"MJ_POWER RH pdo(%x) MN_SET_POWER SYS(%s))\n",
PdoDeviceObject,
S_State(irpStack->Parameters.Power.State.SystemState)));
break;
case DevicePowerState:
{
DEVICE_POWER_STATE deviceState =
irpStack->Parameters.Power.State.DeviceState;
USBPORT_KdPrint((1,
"MJ_POWER RH pdo(%x) MN_SET_POWER DEV(%s)\n",
PdoDeviceObject,
D_State(deviceState)));
LOGENTRY(NULL, fdoDeviceObject, LOG_POWER, 'RspD', deviceState, 0,
irpStack->Parameters.Power.Type);
// Handle D states for the ROOT HUB Pdo:
//
// NOTE:
// if the root hub is placed in D3 then it is considered OFF.
//
// if the root hub is placed in D2 or D1 then it is 'suspended',
// the hub driver should not do this unless all the ports have
// been selectively suspended first
//
// if the root hub is placed in D0 it is on
//
// We are not required to take any action here, however
// this is where 'selective suspend' of the bus is handled
//
// For D1 - D3 we can tweak the host controller, ie stop
// the schedule disable ints, etc. since it won't be in use
// while the root hub PDO is suspended.
//
// Whatever we do to the controller here we need to be able to
// recognize resume signalling.
// assume success
ntStatus = STATUS_SUCCESS;
switch (deviceState) {
case PowerDeviceD0:
// re-activate controller if idle
if (devExt->CurrentDevicePowerState != PowerDeviceD0) {
// trap the condition in case this is our bug
USBPORT_PowerFault(fdoDeviceObject,
"controller not powered");
// fail the request
ntStatus = STATUS_UNSUCCESSFUL;
} else {
while (TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_NEED_SET_POWER_D0)) {
// wait for the driver thread to finsih
// D0 processing
USBPORT_Wait(fdoDeviceObject, 10);
}
USBPORT_ResumeController(fdoDeviceObject);
rhDevExt->CurrentDevicePowerState = deviceState;
//662596
if (TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_CC_LOCK) &&
USBPORT_IS_USB20(devExt)) {
USBPORT_KdPrint((1, " power 20 (release)\n"));
CLEAR_FDO_FLAG(devExt, USBPORT_FDOFLAG_CC_LOCK);
KeReleaseSemaphore(&devExt->Fdo.CcLock,
LOW_REALTIME_PRIORITY,
1,
FALSE);
}
//662596
USBPORT_CompletePdoWaitWake(fdoDeviceObject);
// if we have an idle irp, complete it now
USBPORT_CompletePendingIdleIrp(PdoDeviceObject);
}
break;
case PowerDeviceD1:
case PowerDeviceD2:
case PowerDeviceD3:
// suspend/idle the controller
// The controller is only turned off and on by power
// action to the FDO, suspend and resume are tied
// to the root hub PDO.
USBPORT_SuspendController(fdoDeviceObject);
rhDevExt->CurrentDevicePowerState = deviceState;
break;
case PowerDeviceUnspecified:
// do nothing
break;
}
}
break;
}
break;
default:
//
// default behavior for an unhandled Power irp is to return the
// status currently in the irp
// is this true for power?
USBPORT_KdPrint((1,
"MJ_POWER RH pdo(%x) MN_%d not handled\n",
PdoDeviceObject,
irpStack->MinorFunction));
} /* switch, POWER minor function */
// NOTE: for some reason we don't call PoStartnextPowerIrp for
// WaitWake Irps -- I guess they are not power irps
PoStartNextPowerIrp(Irp);
USBPORT_PdoPowerIrp_Complete:
USBPORT_CompleteIrp(PdoDeviceObject,
Irp,
ntStatus,
0);
USBPORT_PdoPowerIrp_Done:
return ntStatus;
}
BOOLEAN
USBPORT_RootHubEnabledForWake(
PDEVICE_OBJECT FdoDeviceObject
)
/*++
Routine Description:
Arguments:
Return Value:
True if the root hub has been enabled for wake via
a waitwake irp.
--*/
{
BOOLEAN wakeEnabled;
PDEVICE_EXTENSION rhDevExt, devExt;
KIRQL irql;
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
GET_DEVICE_EXT(rhDevExt, devExt->Fdo.RootHubPdo);
ASSERT_PDOEXT(rhDevExt);
ACQUIRE_WAKEIRP_LOCK(FdoDeviceObject, irql);
wakeEnabled = rhDevExt->Pdo.PendingWaitWakeIrp != NULL ? TRUE: FALSE;
RELEASE_WAKEIRP_LOCK(FdoDeviceObject, irql);
return wakeEnabled;
}
VOID
USBPORT_CancelPendingWakeIrp(
PDEVICE_OBJECT PdoDeviceObject,
PIRP CancelIrp
)
/*++
Routine Description:
Handle Cancel for the root hub wake irp
Arguments:
Return Value:
none.
--*/
{
PDEVICE_EXTENSION rhDevExt, devExt;
PDEVICE_OBJECT fdoDeviceObject;
KIRQL irql;
// release cancel spinlock immediatly,
// we are protected by the WAKEIRP_LOCK
IoReleaseCancelSpinLock(CancelIrp->CancelIrql);
GET_DEVICE_EXT(rhDevExt, PdoDeviceObject);
ASSERT_PDOEXT(rhDevExt);
fdoDeviceObject = rhDevExt->HcFdoDeviceObject;
GET_DEVICE_EXT(devExt, fdoDeviceObject);
ASSERT_FDOEXT(devExt);
LOGENTRY(NULL, fdoDeviceObject, LOG_POWER, 'cnWW', fdoDeviceObject, CancelIrp, 0);
ACQUIRE_WAKEIRP_LOCK(fdoDeviceObject, irql);
USBPORT_ASSERT(rhDevExt->Pdo.PendingWaitWakeIrp == CancelIrp);
rhDevExt->Pdo.PendingWaitWakeIrp = NULL;
RELEASE_WAKEIRP_LOCK(fdoDeviceObject, irql);
USBPORT_CompleteIrp(PdoDeviceObject,
CancelIrp,
STATUS_CANCELLED,
0);
}
VOID
USBPORT_CancelPendingIdleIrp(
PDEVICE_OBJECT PdoDeviceObject,
PIRP CancelIrp
)
/*++
Routine Description:
Handle Cancel for the root hub wake irp
Arguments:
Return Value:
--*/
{
PDEVICE_EXTENSION rhDevExt, devExt;
PDEVICE_OBJECT fdoDeviceObject;
KIRQL irql;
// release cancel spinlock immediatly,
// we are protected by the IDLEIRP_LOCK
IoReleaseCancelSpinLock(CancelIrp->CancelIrql);
GET_DEVICE_EXT(rhDevExt, PdoDeviceObject);
ASSERT_PDOEXT(rhDevExt);
fdoDeviceObject = rhDevExt->HcFdoDeviceObject;
GET_DEVICE_EXT(devExt, fdoDeviceObject);
ASSERT_FDOEXT(devExt);
LOGENTRY(NULL, fdoDeviceObject, LOG_POWER, 'cnIR', fdoDeviceObject, CancelIrp, 0);
ACQUIRE_IDLEIRP_LOCK(fdoDeviceObject, irql);
USBPORT_ASSERT(rhDevExt->Pdo.PendingIdleNotificationIrp == CancelIrp);
rhDevExt->Pdo.PendingIdleNotificationIrp = NULL;
CLEAR_PDO_FLAG(rhDevExt, USBPORT_PDOFLAG_HAVE_IDLE_IRP);
RELEASE_IDLEIRP_LOCK(fdoDeviceObject, irql);
USBPORT_CompleteIrp(PdoDeviceObject,
CancelIrp,
STATUS_CANCELLED,
0);
}
VOID
USBPORT_TurnControllerOff(
PDEVICE_OBJECT FdoDeviceObject
)
/*++
Routine Description:
When we say OFF we mean OFF.
This is similar to a stop -- the mniport does not
know the difference. The port however does and
does not free the miniports resources
This function may be called multiple times ie even
if controller is already off with no ill effects.
Arguments:
DeviceObject - DeviceObject of the controller to turn off
Return Value:
this is NON FAILABLE.
--*/
{
PDEVICE_EXTENSION devExt;
KIRQL irql;
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
if (!TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_OFF)) {
LOGENTRY(NULL, FdoDeviceObject, LOG_POWER, 'Coff', 0, 0, 0);
USBPORT_KdPrint((1, " >Turning Controller OFF\n"));
DEBUG_BREAK();
// tell the DM tiner not to poll the controller
USBPORT_ACQUIRE_DM_LOCK(devExt, irql);
SET_FDO_FLAG(devExt, USBPORT_FDOFLAG_SKIP_TIMER_WORK);
USBPORT_RELEASE_DM_LOCK(devExt, irql);
if (TEST_FLAG(devExt->Fdo.MpStateFlags, MP_STATE_STARTED)) {
MP_DisableInterrupts(FdoDeviceObject, devExt);
CLEAR_FLAG(devExt->Fdo.MpStateFlags, MP_STATE_STARTED);
MP_StopController(devExt, TRUE);
}
USBPORT_NukeAllEndpoints(FdoDeviceObject);
// Off overrides suspended
CLEAR_FDO_FLAG(devExt, USBPORT_FDOFLAG_SUSPENDED);
CLEAR_FLAG(devExt->Fdo.MpStateFlags, MP_STATE_SUSPENDED);
USBPORT_AcquireSpinLock(FdoDeviceObject,
&devExt->Fdo.CoreFunctionSpin, &irql);
SET_FDO_FLAG(devExt, USBPORT_FDOFLAG_OFF);
USBPORT_ReleaseSpinLock(FdoDeviceObject,
&devExt->Fdo.CoreFunctionSpin, irql);
}
}
VOID
USBPORT_RestoreController(
PDEVICE_OBJECT FdoDeviceObject
)
/*++
Routine Description:
Turns the controller back on to the 'suspended' state after a
power event.
Arguments:
DeviceObject - DeviceObject of the controller to turn off
Return Value:
none.
--*/
{
PDEVICE_EXTENSION devExt;
USB_MINIPORT_STATUS mpStatus;
PIRP irp;
KIRQL irql;
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
LOGENTRY(NULL, FdoDeviceObject, LOG_POWER, 'REST', devExt->SystemPowerIrp, 0, 0);
// call down the orginal system Power request
// no protection since we haven't
// called PoStartNextPowerIrp
irp = devExt->SystemPowerIrp;
devExt->SystemPowerIrp = NULL;
// we are now in D0, we must set the flag here
// because the PoCallDriver will initiate the
// power up for the root hub which checks the
// power state of the controller.
devExt->CurrentDevicePowerState = PowerDeviceD0;
MP_EnableInterrupts(devExt);
// we may not have a system power irp if the power
// up requested originated from wake completion so
// in this case we don't need to cal it down.
if (irp != NULL) {
IoCopyCurrentIrpStackLocationToNext(irp);
PoStartNextPowerIrp(irp);
DECREMENT_PENDING_REQUEST_COUNT(FdoDeviceObject, irp);
PoCallDriver(devExt->Fdo.TopOfStackDeviceObject,
irp);
}
}
VOID
USBPORT_TurnControllerOn(
PDEVICE_OBJECT FdoDeviceObject
)
/*++
Routine Description:
Similar to start -- but we already have our resources.
NOTE that the miniport activates as if it the system
was booted normally.
We only get here after the Set D0 request has been passed
to the parent bus.
Arguments:
DeviceObject - DeviceObject of the controller to turn off
Return Value:
NT status code.
--*/
{
PDEVICE_EXTENSION devExt;
PHC_RESOURCES hcResources;
USB_MINIPORT_STATUS mpStatus;
PIRP irp;
KIRQL irql;
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
hcResources = &devExt->Fdo.HcResources;
LOGENTRY(NULL, FdoDeviceObject, LOG_POWER, 'C_on', 0, 0, 0);
DEBUG_BREAK();
// zero the controller extension
RtlZeroMemory(devExt->Fdo.MiniportDeviceData,
devExt->Fdo.MiniportDriver->RegistrationPacket.DeviceDataSize);
// zero miniport common buffer
RtlZeroMemory(hcResources->CommonBufferVa,
REGISTRATION_PACKET(devExt).CommonBufferBytes);
// attempt to re-start the miniport
MP_StartController(devExt, hcResources, mpStatus);
LOGENTRY(NULL, FdoDeviceObject, LOG_POWER, 'mpRS', mpStatus, 0, 0);
if (mpStatus == USBMP_STATUS_SUCCESS) {
// controller started, set flag and begin passing
// interrupts to the miniport
SET_FLAG(devExt->Fdo.MpStateFlags, MP_STATE_STARTED);
LOGENTRY(NULL, FdoDeviceObject, LOG_POWER, 'rIRQ', mpStatus, 0, 0);
MP_EnableInterrupts(devExt);
USBPORT_ACQUIRE_DM_LOCK(devExt, irql);
CLEAR_FDO_FLAG(devExt, USBPORT_FDOFLAG_SKIP_TIMER_WORK);
CLEAR_FDO_FLAG(devExt, USBPORT_FDOFLAG_FAIL_URBS);
USBPORT_RELEASE_DM_LOCK(devExt, irql);
} else {
// failure on re-start?
TEST_TRAP();
}
// we are now in D0,
//
// since we don't hook the completion of the
// system power irp we will consider ourselves
// on at this point since we have already received
// the D0 completion.
devExt->CurrentDevicePowerState = PowerDeviceD0;
CLEAR_FDO_FLAG(devExt, USBPORT_FDOFLAG_OFF);
// call down the orginal system Power request
// no protection since we haven't
// called PoStartNextPowerIrp
irp = devExt->SystemPowerIrp;
devExt->SystemPowerIrp = NULL;
// we may not have a system power irp if the power
// up requested originated from wake completion so
// in this case we don't need to cal it down.
if (irp != NULL) {
IoCopyCurrentIrpStackLocationToNext(irp);
PoStartNextPowerIrp(irp);
DECREMENT_PENDING_REQUEST_COUNT(FdoDeviceObject, irp);
PoCallDriver(devExt->Fdo.TopOfStackDeviceObject,
irp);
}
}
VOID
USBPORT_SuspendController(
PDEVICE_OBJECT FdoDeviceObject
)
/*++
Routine Description:
Suspends the USB Host controller
Arguments:
DeviceObject - DeviceObject of the controller to turn off
Return Value:
this is NON FAILABLE.
--*/
{
PDEVICE_EXTENSION devExt;
KIRQL irql;
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
// There should be no transfers on the HW at time of suspend.
SET_FDO_FLAG(devExt, USBPORT_FDOFLAG_FAIL_URBS);
USBPORT_FlushController(FdoDeviceObject);
// Our job here is to 'idle' controller and twiddle the
// appropriate bits to allow it to recognize resume signalling
USBPORT_ASSERT(!TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_OFF));
if (!TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_SUSPENDED)) {
LOGENTRY(NULL, FdoDeviceObject, LOG_POWER, 'Csus', 0, 0, 0);
USBPORT_KdPrint((1, " >SUSPEND controller\n"));
DEBUG_BREAK();
// tell the DM timer not to poll the controller
USBPORT_ACQUIRE_DM_LOCK(devExt, irql);
SET_FDO_FLAG(devExt, USBPORT_FDOFLAG_SKIP_TIMER_WORK);
USBPORT_RELEASE_DM_LOCK(devExt, irql);
if (TEST_FLAG(devExt->Fdo.MpStateFlags, MP_STATE_STARTED)) {
SET_FLAG(devExt->Fdo.MpStateFlags, MP_STATE_SUSPENDED);
// introduce a 10ms wait here to allow any
// port suspends to finish
USBPORT_Wait(FdoDeviceObject, 10);
// BUGBUG HP ia64 fix
// we cannot suspend until we finish notifying the companions
// that it is OK to start
if (USBPORT_IS_USB20(devExt)) {
// we cannot suspend until we finish notifying the companions
// that it is OK to start
InterlockedDecrement(&devExt->Fdo.PendingRhCallback);
while (devExt->Fdo.PendingRhCallback) {
USBPORT_Wait(FdoDeviceObject, 10);
}
// reset the counter for the next time through
devExt->Fdo.PendingRhCallback = 1;
KeWaitForSingleObject(&devExt->Fdo.CcLock,
Executive,
KernelMode,
FALSE,
NULL);
SET_FDO_FLAG(devExt, USBPORT_FDOFLAG_CC_LOCK);
}
MP_SuspendController(devExt);
if (USBPORT_IS_USB20(devExt)) {
CLEAR_FDO_FLAG(devExt, USBPORT_FDOFLAG_CC_LOCK);
KeReleaseSemaphore(&devExt->Fdo.CcLock,
LOW_REALTIME_PRIORITY,
1,
FALSE);
}
}
USBPORT_AcquireSpinLock(FdoDeviceObject,
&devExt->Fdo.CoreFunctionSpin, &irql);
SET_FDO_FLAG(devExt, USBPORT_FDOFLAG_SUSPENDED);
USBPORT_ReleaseSpinLock(FdoDeviceObject,
&devExt->Fdo.CoreFunctionSpin, irql);
}
}
VOID
USBPORT_ResumeController(
PDEVICE_OBJECT FdoDeviceObject
)
/*++
Routine Description:
Suspends the USB Host controller
Arguments:
DeviceObject - DeviceObject of the controller to turn off
Return Value:
this is NON FAILABLE.
--*/
{
PDEVICE_EXTENSION devExt;
KIRQL irql;
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
if (TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_SUSPENDED)) {
LOGENTRY(NULL, FdoDeviceObject, LOG_POWER, 'Cres', 0, 0, 0);
USBPORT_KdPrint((1, " >RESUME controller\n"));
DEBUG_BREAK();
USBPORT_ACQUIRE_DM_LOCK(devExt, irql);
CLEAR_FDO_FLAG(devExt, USBPORT_FDOFLAG_SKIP_TIMER_WORK);
CLEAR_FDO_FLAG(devExt, USBPORT_FDOFLAG_FAIL_URBS);
USBPORT_RELEASE_DM_LOCK(devExt, irql);
USBPORT_ASSERT(!TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_OFF));
if (TEST_FLAG(devExt->Fdo.MpStateFlags, MP_STATE_SUSPENDED)) {
USB_MINIPORT_STATUS mpStatus;
CLEAR_FLAG(devExt->Fdo.MpStateFlags, MP_STATE_SUSPENDED);
MP_ResumeController(devExt, mpStatus);
if (mpStatus != USBMP_STATUS_SUCCESS) {
USBPORT_KdPrint((1, " >controller failed resume, re-start\n"));
USBPORT_ACQUIRE_DM_LOCK(devExt, irql);
SET_FDO_FLAG(devExt, USBPORT_FDOFLAG_SKIP_TIMER_WORK);
USBPORT_RELEASE_DM_LOCK(devExt, irql);
MP_DisableInterrupts(FdoDeviceObject, devExt);
MP_StopController(devExt, TRUE);
USBPORT_NukeAllEndpoints(FdoDeviceObject);
// zero the controller extension
RtlZeroMemory(devExt->Fdo.MiniportDeviceData,
devExt->Fdo.MiniportDriver->RegistrationPacket.DeviceDataSize);
// zero miniport common buffer
RtlZeroMemory(devExt->Fdo.HcResources.CommonBufferVa,
REGISTRATION_PACKET(devExt).CommonBufferBytes);
devExt->Fdo.HcResources.Restart = TRUE;
MP_StartController(devExt, &devExt->Fdo.HcResources, mpStatus);
devExt->Fdo.HcResources.Restart = FALSE;
if (mpStatus == USBMP_STATUS_SUCCESS) {
// don't need to enable interrupts if start failed
MP_EnableInterrupts(devExt);
if (TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_IS_CC)) {
// if this is a CC then power the ports here
USBPORT_KdPrint((1, " >power CC ports\n"));
USBPORT_RootHub_PowerAndChirpAllCcPorts(
FdoDeviceObject);
}
}
USBPORT_ACQUIRE_DM_LOCK(devExt, irql);
CLEAR_FDO_FLAG(devExt, USBPORT_FDOFLAG_SKIP_TIMER_WORK);
CLEAR_FDO_FLAG(devExt, USBPORT_FDOFLAG_FAIL_URBS);
USBPORT_RELEASE_DM_LOCK(devExt, irql);
}
// wait 100 after bus resume before allowing drivers to talk
// to the device. Unfortuantely many USB devices are busted
// and will not respond if accessed immediately after resume.
USBPORT_Wait(FdoDeviceObject, 100);
}
CLEAR_FDO_FLAG(devExt, USBPORT_FDOFLAG_SUSPENDED);
}
}
VOID
USBPORT_DoIdleNotificationCallback(
PDEVICE_OBJECT PdoDeviceObject
)
/*++
Routine Description:
Our mission here is to do the 'IdleNotification' callback if we have
an irp. The trick is to synchronize the callback with the cancel
routine ie we don't want the hub driver to cancel the irp and unload
while we are calling it back.
Arguments:
Return Value:
NTSTATUS
--*/
{
PIRP irp;
PDEVICE_EXTENSION rhDevExt, devExt;
PDEVICE_OBJECT fdoDeviceObject;
PUSB_IDLE_CALLBACK_INFO idleCallbackInfo;
KIRQL irql;
GET_DEVICE_EXT(rhDevExt, PdoDeviceObject);
ASSERT_PDOEXT(rhDevExt);
fdoDeviceObject = rhDevExt->HcFdoDeviceObject;
GET_DEVICE_EXT(devExt, fdoDeviceObject);
ASSERT_FDOEXT(devExt);
// cancel routine will stall here,
// if cancel is running we will stall here
ACQUIRE_IDLEIRP_LOCK(fdoDeviceObject, irql);
// remove the irp from the table so that the
// cancel routine cannot find it
irp = rhDevExt->Pdo.PendingIdleNotificationIrp;
rhDevExt->Pdo.PendingIdleNotificationIrp = NULL;
LOGENTRY(NULL, fdoDeviceObject, LOG_POWER, 'idCB', irp, 0, 0);
RELEASE_IDLEIRP_LOCK(fdoDeviceObject, irql);
// do the callback if we have an irp
if (irp != NULL) {
idleCallbackInfo = (PUSB_IDLE_CALLBACK_INFO)
IoGetCurrentIrpStackLocation(irp)->\
Parameters.DeviceIoControl.Type3InputBuffer;
USBPORT_ASSERT(idleCallbackInfo && idleCallbackInfo->IdleCallback);
if (idleCallbackInfo && idleCallbackInfo->IdleCallback) {
USBPORT_KdPrint((1, "-do idle callback\n"));
// the hub driver expects this to happen at passive level
ASSERT_PASSIVE();
LOGENTRY(NULL, fdoDeviceObject, LOG_POWER, 'doCB', irp, 0, 0);
idleCallbackInfo->IdleCallback(idleCallbackInfo->IdleContext);
}
// put the irp back in the table, if the cancel routine
// has run the IRP will be marked canceled
ACQUIRE_IDLEIRP_LOCK(fdoDeviceObject, irql);
if (irp->Cancel) {
LOGENTRY(NULL, fdoDeviceObject, LOG_POWER, 'caCB', irp, 0, 0);
CLEAR_PDO_FLAG(rhDevExt, USBPORT_PDOFLAG_HAVE_IDLE_IRP);
RELEASE_IDLEIRP_LOCK(fdoDeviceObject, irql);
USBPORT_CompleteIrp(PdoDeviceObject,
irp,
STATUS_CANCELLED,
0);
} else {
LOGENTRY(NULL, fdoDeviceObject, LOG_POWER, 'rsCB', irp, 0, 0);
rhDevExt->Pdo.PendingIdleNotificationIrp = irp;
RELEASE_IDLEIRP_LOCK(fdoDeviceObject, irql);
}
}
}
NTSTATUS
USBPORT_IdleNotificationRequest(
PDEVICE_OBJECT PdoDeviceObject,
PIRP Irp
)
/*++
Routine Description:
Request by the hub driver to go 'idle' is suspend.
If we call the callback the hub will request a D2 power irp.
If we do not call the callback, now poer irp will be sent and
the bus will noy enter UsbSuspend.
We are required to sit on the Irp until canceled. We permit only
one 'selective suspend' IRP in the driver at a time.
NOTE: a possible optimization is to have the hub driver simply
not issue this IOCTL since it doesn't actualy do anything.
Arguments:
Return Value:
NTSTATUS
--*/
{
PIO_STACK_LOCATION irpStack;
NTSTATUS ntStatus = STATUS_BOGUS;
PDEVICE_EXTENSION rhDevExt, devExt;
PDEVICE_OBJECT fdoDeviceObject;
KIRQL irql;
PDRIVER_CANCEL cr;
GET_DEVICE_EXT(rhDevExt, PdoDeviceObject);
ASSERT_PDOEXT(rhDevExt);
fdoDeviceObject = rhDevExt->HcFdoDeviceObject;
GET_DEVICE_EXT(devExt, fdoDeviceObject);
ASSERT_FDOEXT(devExt);
LOGENTRY(NULL, fdoDeviceObject, LOG_POWER, 'iNOT', PdoDeviceObject, Irp, 0);
irpStack = IoGetCurrentIrpStackLocation(Irp);
USBPORT_ASSERT(irpStack->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL);
if (!TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_RH_CAN_SUSPEND)) {
// NOTE: This is where we override selective suspend
// ifthe HW (controller)
LOGENTRY(NULL, fdoDeviceObject, LOG_POWER, 'noSS', PdoDeviceObject, Irp, 0);
ntStatus = STATUS_NOT_SUPPORTED;
goto USBPORT_IdleNotificationRequest_Complete;
}
if (TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_CONTROLLER_GONE)) {
LOGENTRY(NULL, fdoDeviceObject, LOG_POWER, 'gone', PdoDeviceObject, Irp, 0);
ntStatus = STATUS_DEVICE_NOT_CONNECTED;
goto USBPORT_IdleNotificationRequest_Complete;
}
// we only support one idle irp pending
// in the root hub -- basically we have a pending
// irp table with one entry
ACQUIRE_IDLEIRP_LOCK(fdoDeviceObject, irql);
cr = IoSetCancelRoutine(Irp, USBPORT_CancelPendingIdleIrp);
USBPORT_ASSERT(cr == NULL);
if (Irp->Cancel &&
IoSetCancelRoutine(Irp, NULL)) {
// irp was canceled and our cancel routine
// did not run
RELEASE_IDLEIRP_LOCK(fdoDeviceObject, irql);
ntStatus = STATUS_CANCELLED;
goto USBPORT_IdleNotificationRequest_Complete;
} else {
// cancel routine is set, if irp is canceled
// the cancel routine will stall on the
// IDLE_IRP_LOCK which we are holding
if (!TEST_PDO_FLAG(rhDevExt, USBPORT_PDOFLAG_HAVE_IDLE_IRP)) {
// keep the irp in our table
IoMarkIrpPending(Irp);
rhDevExt->Pdo.PendingIdleNotificationIrp = Irp;
SET_PDO_FLAG(rhDevExt, USBPORT_PDOFLAG_HAVE_IDLE_IRP);
LOGENTRY(NULL, fdoDeviceObject, LOG_POWER, 'pNOT', PdoDeviceObject, Irp, 0);
ntStatus = STATUS_PENDING;
RELEASE_IDLEIRP_LOCK(fdoDeviceObject, irql);
goto USBPORT_IdleNotificationRequest_Done;
} else {
// we already have a wake irp, complete this
// one with STATUS_BUSY.
// note that since it is not in our table if
// the cancel routine is running (ie stalled
// on the IDLEIRP_LOCK it will ignore the irp
// when we release the lock.
if (IoSetCancelRoutine(Irp, NULL) != NULL) {
// cancel routine did not run
ntStatus = STATUS_DEVICE_BUSY;
} else {
// let the cancel routine complete it.
IoMarkIrpPending(Irp);
ntStatus = STATUS_PENDING;
RELEASE_IDLEIRP_LOCK(fdoDeviceObject, irql);
goto USBPORT_IdleNotificationRequest_Done_NoCB;
}
}
RELEASE_IDLEIRP_LOCK(fdoDeviceObject, irql);
}
LOGENTRY(NULL, fdoDeviceObject, LOG_POWER, 'cpNT', PdoDeviceObject, Irp, ntStatus);
USBPORT_IdleNotificationRequest_Complete:
USBPORT_CompleteIrp(PdoDeviceObject,
Irp,
ntStatus,
0);
USBPORT_IdleNotificationRequest_Done:
// now issue the callback immediatly if we have an irp
USBPORT_DoIdleNotificationCallback(PdoDeviceObject);
USBPORT_IdleNotificationRequest_Done_NoCB:
return ntStatus;
}
VOID
USBPORT_CompletePdoWaitWake(
PDEVICE_OBJECT FdoDeviceObject
)
/*++
Routine Description:
called when the root hub pdo has 'woke up'
Arguments:
Return Value:
none
--*/
{
PIRP irp;
PDEVICE_EXTENSION rhDevExt, devExt;
KIRQL irql;
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
GET_DEVICE_EXT(rhDevExt, devExt->Fdo.RootHubPdo);
ASSERT_PDOEXT(rhDevExt);
ACQUIRE_WAKEIRP_LOCK(FdoDeviceObject, irql);
irp = rhDevExt->Pdo.PendingWaitWakeIrp;
LOGENTRY(NULL, FdoDeviceObject, LOG_POWER, 'WAKi', FdoDeviceObject, irp, 0);
if (irp != NULL &&
IoSetCancelRoutine(irp, NULL)) {
// we have an irp and the cancel routine has not
// run, complete the irp.
rhDevExt->Pdo.PendingWaitWakeIrp = NULL;
RELEASE_WAKEIRP_LOCK(FdoDeviceObject, irql);
// since this irp was sent to the PDO we
// complete it to the PDO
USBPORT_KdPrint((1, " Complete PDO Wake Irp %x\n", irp));
DEBUG_BREAK();
USBPORT_CompleteIrp(devExt->Fdo.RootHubPdo,
irp,
STATUS_SUCCESS,
0);
} else {
RELEASE_WAKEIRP_LOCK(FdoDeviceObject, irql);
}
}
VOID
USBPORT_CompletePendingIdleIrp(
PDEVICE_OBJECT PdoDeviceObject
)
/*++
Routine Description:
If we have one complete the idle notification request
irp
Arguments:
Return Value:
--*/
{
PIRP irp;
PDEVICE_EXTENSION rhDevExt, devExt;
PDEVICE_OBJECT fdoDeviceObject;
KIRQL irql;
GET_DEVICE_EXT(rhDevExt, PdoDeviceObject);
ASSERT_PDOEXT(rhDevExt);
fdoDeviceObject = rhDevExt->HcFdoDeviceObject;
GET_DEVICE_EXT(devExt, fdoDeviceObject);
ASSERT_FDOEXT(devExt);
// cancel routine will stall here,
// if cancel is running we will stall here
ACQUIRE_IDLEIRP_LOCK(fdoDeviceObject, irql);
// remove the irp from the table so that the
// cancel routine cannot find it
irp = rhDevExt->Pdo.PendingIdleNotificationIrp;
LOGENTRY(NULL, fdoDeviceObject, LOG_POWER, 'idCP', irp, 0, 0);
rhDevExt->Pdo.PendingIdleNotificationIrp = NULL;
if (irp != NULL) {
CLEAR_PDO_FLAG(rhDevExt, USBPORT_PDOFLAG_HAVE_IDLE_IRP);
}
RELEASE_IDLEIRP_LOCK(fdoDeviceObject, irql);
// do the callback if we have an irp
if (irp != NULL) {
// we need to complete this Irp
IoSetCancelRoutine(irp, NULL);
USBPORT_KdPrint((1, "-complete idle irp\n"));
USBPORT_CompleteIrp(PdoDeviceObject,
irp,
STATUS_SUCCESS,
0);
}
}
NTSTATUS
USBPORT_ProcessHcWakeIrp(
PDEVICE_OBJECT FdoDeviceObject,
PIRP Irp
)
/*++
Routine Description:
Process the HC wake irp
Arguments:
Return Value:
--*/
{
PDEVICE_EXTENSION devExt;
USBHC_WAKE_STATE oldWakeState;
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
devExt->Fdo.HcPendingWakeIrp = Irp;
// Advance the state if the armed if we are to proceed
oldWakeState = InterlockedCompareExchange( (PULONG) &devExt->Fdo.HcWakeState,
HCWAKESTATE_ARMED,
HCWAKESTATE_WAITING );
if (oldWakeState == HCWAKESTATE_WAITING_CANCELLED) {
// We got disarmed, finish up and complete the IRP
devExt->Fdo.HcWakeState = HCWAKESTATE_COMPLETING;
DECREMENT_PENDING_REQUEST_COUNT(FdoDeviceObject, Irp);
Irp->IoStatus.Status = STATUS_CANCELLED;
IoCompleteRequest( Irp, IO_NO_INCREMENT );
return STATUS_CANCELLED;
}
// We went from WAITING to ARMED. Set a completion routine and forward
// the IRP. Note that our completion routine might complete the IRP
// asynchronously, so we mark the IRP pending
IoMarkIrpPending(Irp);
IoCopyCurrentIrpStackLocationToNext( Irp );
IoSetCompletionRoutine( Irp,
USBPORT_HcWakeIrp_Io_Completion,
NULL,
TRUE,
TRUE,
TRUE);
DECREMENT_PENDING_REQUEST_COUNT(FdoDeviceObject, Irp);
PoCallDriver(devExt->Fdo.TopOfStackDeviceObject,
Irp);
return STATUS_PENDING;
}
NTSTATUS
USBPORT_HcWakeIrp_Io_Completion(
PDEVICE_OBJECT DeviceObject,
PIRP Irp,
PVOID Context
)
/*++
Routine Description:
Called when the HC wake irp completes we use this to hook completion
so we can handle the cancel
This routine runs before the USBPORT_USBPORT_HcWakeIrp_Po_Completion
Arguments:
Return Value:
The function value is the final status from the operation.
--*/
{
PIRP irp;
PDEVICE_EXTENSION devExt;
USBHC_WAKE_STATE oldWakeState;
GET_DEVICE_EXT(devExt, DeviceObject);
ASSERT_FDOEXT(devExt);
// Advance the state to completing
oldWakeState = InterlockedExchange( (PULONG) &devExt->Fdo.HcWakeState,
HCWAKESTATE_COMPLETING );
if (oldWakeState == HCWAKESTATE_ARMED) {
// Normal case, IoCancelIrp isn’t being called. Note that we already
// marked the IRP pending in our dispatch routine
return STATUS_SUCCESS;
} else {
ASSERT(oldWakeState == HCWAKESTATE_ARMING_CANCELLED);
// IoCancelIrp is being called RIGHT NOW. The disarm code will try
// to put back the WAKESTATE_ARMED state. It will then see our
// WAKESTATE_COMPLETED value, and complete the IRP itself!
return STATUS_MORE_PROCESSING_REQUIRED;
}
}
NTSTATUS
USBPORT_HcWakeIrp_Po_Completion(
PDEVICE_OBJECT DeviceObject,
UCHAR MinorFunction,
POWER_STATE DeviceState,
PVOID Context,
PIO_STATUS_BLOCK IoStatus
)
/*++
Routine Description:
Called when a wake irp completes for the controller
Arguments:
DeviceObject - Pointer to the device object for the class device.
Irp - Irp completed.
Context - Driver defined context.
Return Value:
STATUS_MORE_PROCESSING_REQUIRED - stalls completion of the irp.
--*/
{
NTSTATUS poNtStatus;
PDEVICE_EXTENSION devExt = Context;
KIRQL irql;
POWER_STATE powerState;
USBPORT_KdPrint((1,
"HcWakeIrpCompletion (%x)\n", IoStatus->Status));
LOGENTRY(NULL, devExt->HcFdoDeviceObject, LOG_POWER, 'WAKc',
devExt, IoStatus->Status, 0);
//
// Zero already freed IRP pointer (not necessary, but nice when debugging)
//
devExt->Fdo.HcPendingWakeIrp = NULL;
//
// Restore state (old state will have been completing)
//
devExt->Fdo.HcWakeState = HCWAKESTATE_DISARMED;
if (IoStatus->Status == STATUS_SUCCESS) {
LOGENTRY(NULL, devExt->HcFdoDeviceObject, LOG_POWER, 'WAK0', 0, 0, 0);
// a successful completion of the wake Irp means something
// generated resume signalling
// The idea here is that we won't have a wake irp down
// unless we are in some D state other than D0. Remember
// this is the controller FDO not the root hub.
SET_FDO_FLAG(devExt, USBPORT_FDOFLAG_RESUME_SIGNALLING);
// we canceled the wake irp when entering D0 so we should
// not see any completions unless we are in a low power
// state
//USBPORT_ASSERT(devExt->CurrentDevicePowerState != PowerDeviceD0);
// we must now attempt to put the controller in D0
powerState.DeviceState = PowerDeviceD0;
USBPORT_KdPrint((1, " >Wakeup Requesting HC D-State - %s\n",
D_State(powerState.DeviceState)));
poNtStatus =
PoRequestPowerIrp(devExt->Fdo.PhysicalDeviceObject,
IRP_MN_SET_POWER,
powerState,
USBPORT_PoRequestCompletion,
devExt->HcFdoDeviceObject,
NULL);
} else {
// some other error, means we f'up probably with the
// help of the ACPI BIOS
if (IoStatus->Status == STATUS_CANCELLED) {
LOGENTRY(NULL, devExt->HcFdoDeviceObject, LOG_POWER, 'WAK1',
0, 0, 0);
USBPORT_KdPrint((1, " >Wakeup Irp Completed with cancel %x\n",
IoStatus->Status));
} else {
LOGENTRY(NULL, devExt->HcFdoDeviceObject, LOG_POWER, 'WAK2',
0, 0, 0);
USBPORT_KdPrint((0, " >Wakeup Irp Completed with error %x\n",
IoStatus->Status));
// if status is STATUS_INVALID_DEVICE_STATE then you need
// to complain to the ACPI guys about your system not waking
// from USB. This is likely due to a bad Device Capability
// structure.
if (IoStatus->Status == STATUS_INVALID_DEVICE_STATE) {
BUG_TRAP();
}
}
}
DECREMENT_PENDING_REQUEST_COUNT(devExt->HcFdoDeviceObject, NULL);
// Note that the status returned here does not matter, this routine
// is called by the kernel (PopCompleteRequestIrp) when the irp
// completes to PDO and this function ignores the returned status.
// PopCompleteRequestIrp also immediatly frees the irp so we need
// take care not to reference it after this routine has run.
KeSetEvent(&devExt->Fdo.HcPendingWakeIrpEvent,
1,
FALSE);
return IoStatus->Status;
}
VOID
USBPORT_ArmHcForWake(
PDEVICE_OBJECT FdoDeviceObject
)
/*++
Routine Description:
ArmHcforWake
Allocate and submit a 'WaitWake' Irp to the host controllers PDO
(usually owned by PCI). This will enable the PME event needed to
wake the system.
Note: We only post the wake irp if the root hub PDO is 'enabled'
for wakeup AND the host controller supports it.
Arguments:
Return Value:
none.
--*/
{
PIRP irp;
PDEVICE_EXTENSION devExt;
KIRQL irql;
BOOLEAN post = FALSE;
POWER_STATE powerState;
NTSTATUS ntStatus, waitStatus;
USBHC_WAKE_STATE oldWakeState;
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
USBPORT_ASSERT(TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_WAKE_ENABLED));
// this check just prevents us from posting a wake irp when we
// already have one pending, although I'm not sure how we might
// get into this situation.
LOGENTRY(NULL, FdoDeviceObject, LOG_POWER, 'hWW>',
0, 0, 0);
while (1) {
oldWakeState = InterlockedCompareExchange((PULONG)&devExt->Fdo.HcWakeState,
HCWAKESTATE_WAITING,
HCWAKESTATE_DISARMED);
LOGENTRY(NULL, FdoDeviceObject, LOG_POWER, 'hWWx', oldWakeState, 0, 0);
if (oldWakeState == HCWAKESTATE_DISARMED) {
break;
}
if ((oldWakeState == HCWAKESTATE_ARMED) ||
(oldWakeState == HCWAKESTATE_WAITING)) {
// The device is already arming
return;
}
// wait for previous wake irp to finish
USBPORT_DisarmHcForWake(FdoDeviceObject);
}
// current state is HCWAKESTATE_WAITING
// set flag for tracking purposes only
SET_FDO_FLAG(devExt, USBPORT_FDOFLAG_HCPENDING_WAKE_IRP);
// wait for wake irp to finish
waitStatus = KeWaitForSingleObject(
&devExt->Fdo.HcPendingWakeIrpEvent,
Suspended,
KernelMode,
FALSE,
NULL);
LOGENTRY(NULL, FdoDeviceObject, LOG_POWER, 'hWWp',
0, 0, 0);
// According to the NTDDK this should be systemwake
powerState.DeviceState = devExt->DeviceCapabilities.SystemWake;
// send the wake irp to our PDO, since it is not our
// responsibility to free the irp we don't keep track
// of it
ntStatus = PoRequestPowerIrp(devExt->Fdo.PhysicalDeviceObject,
IRP_MN_WAIT_WAKE,
powerState,
USBPORT_HcWakeIrp_Po_Completion,
devExt,
NULL);
INCREMENT_PENDING_REQUEST_COUNT(FdoDeviceObject, NULL);
if (ntStatus != STATUS_PENDING) {
LOGENTRY(NULL, FdoDeviceObject, LOG_POWER, 'WAKp',
FdoDeviceObject, 0, ntStatus);
devExt->Fdo.HcWakeState = HCWAKESTATE_DISARMED;
KeSetEvent(&devExt->Fdo.HcPendingWakeIrpEvent,
1,
FALSE);
DECREMENT_PENDING_REQUEST_COUNT(FdoDeviceObject, NULL);
} else {
USBPORT_KdPrint((1, ">HC enabled for wakeup (%x) \n", ntStatus));
DEBUG_BREAK();
}
}
#ifdef IA64
__forceinline
LONG
InterlockedOr (
IN OUT LONG volatile *Target,
IN LONG Set
)
{
LONG i;
LONG j;
j = *Target;
do {
i = j;
j = InterlockedCompareExchange(Target,
i | Set,
i);
} while (i != j);
return j;
}
#else
#define InterlockedOr _InterlockedOr
#endif
VOID
USBPORT_DisarmHcForWake(
PDEVICE_OBJECT FdoDeviceObject
)
/*++
Routine Description:
DisarmForWake
cancels and frees the Pending wake irp for the host controller
Arguments:
Return Value:
none.
--*/
{
PIRP irp;
KIRQL irql;
PDEVICE_EXTENSION devExt;
USBHC_WAKE_STATE oldWakeState;
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
// no longer enabled for wake
CLEAR_FDO_FLAG(devExt, USBPORT_FDOFLAG_WAKE_ENABLED);
// Go from HCWAKESTATE_WAITING to HCWAKESTATE_WAITING_CANCELLED, or
// HCWAKESTATE_ARMED to HCWAKESTATE_ARMING_CANCELLED, or
// stay in HCWAKESTATE_DISARMED or HCWAKESTATE_COMPLETING
oldWakeState = InterlockedOr( (PULONG)&devExt->Fdo.HcWakeState, 1 );
//oldWakeState = RtlInterlockedSetBits((PULONG)&devExt->Fdo.HcWakeState, 1 );
if (oldWakeState == HCWAKESTATE_ARMED) {
IoCancelIrp(devExt->Fdo.HcPendingWakeIrp);
//
// Now that we’ve cancelled the IRP, try to give back ownership
// to the completion routine by restoring the HCWAKESTATE_ARMED state
//
oldWakeState = InterlockedCompareExchange( (PULONG) &devExt->Fdo.HcWakeState,
HCWAKESTATE_ARMED,
HCWAKESTATE_ARMING_CANCELLED );
if (oldWakeState == HCWAKESTATE_COMPLETING) {
//
// We didn’t give back control of IRP in time, so we own it now.
//
// this will cause tp PoCompletion routine to run
IoCompleteRequest( devExt->Fdo.HcPendingWakeIrp, IO_NO_INCREMENT);
}
}
}
#if 0
VOID
USBPORT_SubmitHcWakeIrp(
PDEVICE_OBJECT FdoDeviceObject
)
/*++
Routine Description:
Allocate and submit a 'WaitWake' Irp to the host controllers PDO
(usually owned by PCI). This will enable the PME event needed to
wake the system.
Note: We only post the wake irp if the root hub PDO is 'enabled'
for wakeup AND the host controller supports it.
Arguments:
Return Value:
none.
--*/
{
PIRP irp;
PDEVICE_EXTENSION devExt;
KIRQL irql;
BOOLEAN post = FALSE;
POWER_STATE powerState;
NTSTATUS ntStatus;
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
USBPORT_ASSERT(TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_WAKE_ENABLED));
// this check just prevents us from posting a wake irp when we
// already have one pending, although I'm not sure how we might
// get into this situation.
LOGENTRY(NULL, FdoDeviceObject, LOG_POWER, 'hWW>',
0, 0, 0);
// if USBPORT_FDOFLAG_PENDING_WAKE_IRP is set then we have an irp
// pending, or are about to have one otherwise we set the field and
// post an irp
KeAcquireSpinLock(&devExt->Fdo.HcPendingWakeIrpSpin.sl, &irql);
if (!TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_HCPENDING_WAKE_IRP)) {
LOGENTRY(NULL, FdoDeviceObject, LOG_POWER, 'hWW0',
0, 0, 0);
// no wake irp pending, indicate that we
// are about to post one
SET_FDO_FLAG(devExt, USBPORT_FDOFLAG_HCPENDING_WAKE_IRP);
USBPORT_ASSERT(devExt->Fdo.HcPendingWakeIrp == NULL);
post = TRUE;
// this event will be signalled when the irp completes
KeInitializeEvent(&devExt->Fdo.HcPendingWakeIrpEvent,
NotificationEvent, FALSE);
KeInitializeEvent(&devExt->Fdo.HcPendingWakeIrpPostedEvent,
NotificationEvent, FALSE);
}
KeReleaseSpinLock(&devExt->Fdo.HcPendingWakeIrpSpin.sl, irql);
if (post) {
// no wake irp, post one
LOGENTRY(NULL, FdoDeviceObject, LOG_POWER, 'hWWp',
0, 0, 0);
// According to the NTDDK this should be systemwake
powerState.DeviceState = devExt->DeviceCapabilities.SystemWake;
// send the wake irp to our PDO, since it is not our
// responsibility to free the irp we don't keep track
// of it
ntStatus = PoRequestPowerIrp(devExt->Fdo.PhysicalDeviceObject,
IRP_MN_WAIT_WAKE,
powerState,
USBPORT_HcWakeIrpCompletion,
devExt,
&irp);
// serialize the cancel code so that we don't free
// the irp until we know the address
// track the pending request since we have the completion
// routine hooked
INCREMENT_PENDING_REQUEST_COUNT(FdoDeviceObject, NULL);
if (ntStatus == STATUS_PENDING) {
devExt->Fdo.HcPendingWakeIrp = irp;
LOGENTRY(NULL, FdoDeviceObject, LOG_POWER, 'WAKp',
FdoDeviceObject, irp, ntStatus);
KeSetEvent(&devExt->Fdo.HcPendingWakeIrpPostedEvent,
1,
FALSE);
} else {
TEST_TRAP();
}
USBPORT_KdPrint((1, ">HC enabled for wakeup (%x) (irp = %x)\n",
ntStatus, irp));
DEBUG_BREAK();
}
}
#endif
VOID
USBPORT_HcQueueWakeDpc(
PDEVICE_OBJECT FdoDeviceObject
)
{
PDEVICE_EXTENSION devExt;
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
if (KeInsertQueueDpc(&devExt->Fdo.HcWakeDpc, 0, 0)) {
INCREMENT_PENDING_REQUEST_COUNT(FdoDeviceObject, NULL);
}
}
VOID
USBPORT_HcWakeDpc(
PKDPC Dpc,
PVOID DeferredContext,
PVOID SystemArgument1,
PVOID SystemArgument2
)
/*++
Routine Description:
This routine runs at DISPATCH_LEVEL IRQL.
Arguments:
Dpc - Pointer to the DPC object.
DeferredContext - supplies FdoDeviceObject.
SystemArgument1 - not used.
SystemArgument2 - not used.
Return Value:
None.
--*/
{
PDEVICE_OBJECT fdoDeviceObject = DeferredContext;
PDEVICE_EXTENSION devExt;
GET_DEVICE_EXT(devExt, fdoDeviceObject);
ASSERT_FDOEXT(devExt);
USBPORT_CompletePdoWaitWake(fdoDeviceObject);
DECREMENT_PENDING_REQUEST_COUNT(fdoDeviceObject, NULL);
}