windows-nt/Source/XPSP1/NT/net/ndis/sys/ndispwr.c
2020-09-26 16:20:57 +08:00

2990 lines
99 KiB
C

/*++
Copyright (c) 1990-1995 Microsoft Corporation
Module Name:
ndispwr.c
Abstract:
This module contains the code to process power managment IRPs that are
sent under the IRP_MJ_POWER major code.
Author:
Kyle Brandon (KyleB)
Alireza Dabagh (alid)
Environment:
Kernel mode
Revision History:
02/11/97 KyleB Created
--*/
#include <precomp.h>
#pragma hdrstop
#define MODULE_NUMBER MODULE_POWER
NTSTATUS
FASTCALL
ndisQueryPowerCapabilities(
IN PNDIS_MINIPORT_BLOCK Miniport
)
/*++
Routine Description:
This routine will process the IRP_MN_QUERY_CAPABILITIES by querying the
next device object and saving information from that request.
Arguments:
pIrp - Pointer to the IRP.
pIrpSp - Pointer to the stack parameters for the IRP.
pAdapter - Pointer to the device.
Return Value:
--*/
{
PIRP pirp;
PIO_STACK_LOCATION pirpSpN;
NTSTATUS Status = STATUS_SUCCESS;
PDEVICE_CAPABILITIES pDeviceCaps;
PNDIS_PM_WAKE_UP_CAPABILITIES pWakeUpCaps;
DEVICE_CAPABILITIES deviceCaps;
POWER_QUERY pQuery;
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("==>ndisQueryPowerCapabilities: Miniport %p\n", Miniport));
do
{
//
// default = no PM support
//
MINIPORT_PNP_CLEAR_FLAG(Miniport, fMINIPORT_PM_SUPPORTED);
//
// if the Next device object is NULL, Don't bother, just flag the Miniport
// as not supporting PM.
// this can happen for IM devices under Memphis
//
if (Miniport->NextDeviceObject == NULL)
{
break;
}
//
// Send the IRP_MN_QUERY_CAPABILITIES to pdo.
//
pirp = IoAllocateIrp((CCHAR)(Miniport->NextDeviceObject->StackSize + 1),
FALSE);
if (NULL == pirp)
{
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("ndisQueryPowerCapabilities: Miniport %p, Failed to allocate an irp for IRP_MN_QUERY_CAPABILITIES\n", Miniport));
NdisWriteErrorLogEntry((NDIS_HANDLE)Miniport,
NDIS_ERROR_CODE_OUT_OF_RESOURCES,
0);
Status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
NdisZeroMemory(&deviceCaps, sizeof(deviceCaps));
deviceCaps.Size = sizeof(DEVICE_CAPABILITIES);
deviceCaps.Version = 1;
//
// should initalize deviceCaps.Address and deviceCaps.UINumber here as well
//
deviceCaps.Address = -1;
deviceCaps.UINumber= -1;
//
// Get the stack pointer.
//
pirpSpN = IoGetNextIrpStackLocation(pirp);
ASSERT(pirpSpN != NULL);
NdisZeroMemory(pirpSpN, sizeof(IO_STACK_LOCATION ) );
//
// Set the default device state to full on.
//
pirpSpN->MajorFunction = IRP_MJ_PNP;
pirpSpN->MinorFunction = IRP_MN_QUERY_CAPABILITIES;
pirpSpN->Parameters.DeviceCapabilities.Capabilities = &deviceCaps;
pirp->IoStatus.Status = STATUS_NOT_SUPPORTED;
//
// Setup the I/O completion routine to be called.
//
INITIALIZE_EVENT(&pQuery.Event);
IoSetCompletionRoutine(pirp,
ndisCompletionRoutine,
&pQuery,
TRUE,
TRUE,
TRUE);
//
// Call the next driver.
//
Status = IoCallDriver(Miniport->NextDeviceObject, pirp);
if (STATUS_PENDING == Status)
{
Status = WAIT_FOR_OBJECT(&pQuery.Event, NULL);
ASSERT(NT_SUCCESS(Status));
}
//
// If the lower device object successfully completed the request
// then we save that information.
//
if (NT_SUCCESS(pQuery.Status))
{
//
// Get the pointer to the device capabilities as returned by
// the parent PDO.
//
pDeviceCaps = &deviceCaps;
//
// save the entire device caps received from bus driver/ACPI
//
NdisMoveMemory(
&Miniport->DeviceCaps,
pDeviceCaps,
sizeof(DEVICE_CAPABILITIES));
if ((pDeviceCaps->DeviceWake != PowerDeviceUnspecified) &&
(pDeviceCaps->SystemWake != PowerSystemUnspecified))
{
MINIPORT_PNP_SET_FLAG(Miniport, fMINIPORT_PM_SUPPORTED);
}
IF_DBG(DBG_COMP_PM, DBG_LEVEL_INFO)
{
UINT i;
DbgPrint("ndisQueryPowerCapabilities: Miniport %p, Bus PM capabilities\n", Miniport);
DbgPrint("\tDeviceD1:\t\t%ld\n", (pDeviceCaps->DeviceD1 == 0) ? 0 : 1);
DbgPrint("\tDeviceD2:\t\t%ld\n", (pDeviceCaps->DeviceD2 == 0) ? 0 : 1);
DbgPrint("\tWakeFromD0:\t\t%ld\n", (pDeviceCaps->WakeFromD0 == 0) ? 0 : 1);
DbgPrint("\tWakeFromD1:\t\t%ld\n", (pDeviceCaps->WakeFromD1 == 0) ? 0 : 1);
DbgPrint("\tWakeFromD2:\t\t%ld\n", (pDeviceCaps->WakeFromD2 == 0) ? 0 : 1);
DbgPrint("\tWakeFromD3:\t\t%ld\n\n", (pDeviceCaps->WakeFromD3 == 0) ? 0 : 1);
DbgPrint("\tSystemState\t\tDeviceState\n");
if (pDeviceCaps->DeviceState[0] == PowerDeviceUnspecified)
DbgPrint("\tPowerSystemUnspecified\tPowerDeviceUnspecified\n");
else
DbgPrint("\tPowerSystemUnspecified\t\tD%ld\n", pDeviceCaps->DeviceState[0] - 1);
for (i = 1; i < PowerSystemMaximum; i++)
{
if (pDeviceCaps->DeviceState[i]== PowerDeviceUnspecified)
DbgPrint("\tS%ld\t\t\tPowerDeviceUnspecified\n",i-1);
else
DbgPrint("\tS%ld\t\t\tD%ld\n",i-1, pDeviceCaps->DeviceState[i] - 1);
}
if (pDeviceCaps->SystemWake == PowerSystemUnspecified)
DbgPrint("\t\tSystemWake: PowerSystemUnspecified\n");
else
DbgPrint("\t\tSystemWake: S%ld\n", pDeviceCaps->SystemWake - 1);
if (pDeviceCaps->DeviceWake == PowerDeviceUnspecified)
DbgPrint("\t\tDeviceWake: PowerDeviceUnspecified\n");
else
DbgPrint("\t\tDeviceWake: D%ld\n", pDeviceCaps->DeviceWake - 1);
}
}
else
{
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,("ndisQueryPowerCapabilities: Miniport %p, Bus driver failed IRP_MN_QUERY_CAPABILITIES\n", Miniport));
}
//
// The irp is no longer needed.
//
IoFreeIrp(pirp);
} while (FALSE);
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("<==ndisQueryPowerCapabilities: Miniport %p, Status 0x%x\n", Miniport, Status));
return(Status);
}
NTSTATUS
ndisMediaDisconnectComplete(
IN PDEVICE_OBJECT pdo,
IN UCHAR MinorFunction,
IN POWER_STATE PowerState,
IN PVOID Context,
IN PIO_STATUS_BLOCK IoStatus
)
/*++
Routine Description:
Arguments:
pdo - Pointer to the DEVICE_OBJECT for the miniport.
pirp - Pointer to the device set power state IRP that we created.
Context - Pointer to the miniport block
Return Value:
--*/
{
PNDIS_MINIPORT_BLOCK Miniport = (PNDIS_MINIPORT_BLOCK)Context;
NTSTATUS Status = STATUS_MORE_PROCESSING_REQUIRED;
KIRQL OldIrql;
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("==>ndisMediaDisconnectComplete: Miniport %p\n", Miniport));
NDIS_ACQUIRE_MINIPORT_SPIN_LOCK(Miniport, &OldIrql);
//
// double check that we didn't get a link up while we were doing all this.
//
if (!MINIPORT_PNP_TEST_FLAG(Miniport, fMINIPORT_MEDIA_DISCONNECT_CANCELLED))
{
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("ndisMediaDisconnectComplete: Miniport %p, disconnect complete\n", Miniport));
NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql);
}
else
{
MINIPORT_PNP_CLEAR_FLAG(Miniport, fMINIPORT_MEDIA_DISCONNECT_CANCELLED);
NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql);
//
// if system is not going to sleep, wake up the device
//
if (!MINIPORT_PNP_TEST_FLAG(Miniport, fMINIPORT_SYSTEM_SLEEPING))
{
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("ndisMediaDisconnectComplete: Miniport %p, disconnect was cancelled. Power back up the miniport\n", Miniport));
//
// Wake it back up
//
PowerState.DeviceState = PowerDeviceD0;
Status = PoRequestPowerIrp(Miniport->PhysicalDeviceObject,
IRP_MN_SET_POWER,
PowerState,
NULL,
NULL,
NULL);
}
}
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("<==ndisMediaDisconnectComplete: Miniport %p\n", Miniport));
return(STATUS_MORE_PROCESSING_REQUIRED);
}
VOID
ndisMediaDisconnectWorker(
IN PPOWER_WORK_ITEM pWorkItem,
IN PVOID Context
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PNDIS_MINIPORT_BLOCK Miniport = (PNDIS_MINIPORT_BLOCK)Context;
POWER_STATE PowerState;
NTSTATUS Status;
NDIS_STATUS NdisStatus;
ULONG WakeEnable;
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("==>ndisMediaDisconnectWorker: Miniport %p\n", Miniport));
//
// Determine the minimum device state we can go to and still get a link enabled.
//
if (Miniport->DeviceCaps.DeviceWake < Miniport->PMCapabilities.WakeUpCapabilities.MinLinkChangeWakeUp)
{
PowerState.DeviceState = Miniport->DeviceCaps.DeviceWake;
}
else
{
PowerState.DeviceState = Miniport->PMCapabilities.WakeUpCapabilities.MinLinkChangeWakeUp;
}
//
// enable the appropriate wakeup method. this includes link change,
// pattern match and/or magic packet.
// if LINK_CHANGE method is disabled, we should not even get here
//
//
// Miniport->WakeUpEnable is the wakeup methods enabled from protocol (and ndis point of view)
// with this qfe, when the user turns wol off from UI, the methods going down are not
// the methods set by protocol/ndis
//
WakeEnable = Miniport->WakeUpEnable;
if (Miniport->PnPCapabilities & NDIS_DEVICE_DISABLE_WAKE_ON_PATTERN_MATCH)
{
WakeEnable &= ~NDIS_PNP_WAKE_UP_PATTERN_MATCH;
}
if (Miniport->PnPCapabilities & NDIS_DEVICE_DISABLE_WAKE_ON_MAGIC_PACKET)
{
WakeEnable &= ~NDIS_PNP_WAKE_UP_MAGIC_PACKET;
}
NdisStatus = ndisQuerySetMiniportDeviceState(Miniport,
WakeEnable,
OID_PNP_ENABLE_WAKE_UP,
TRUE);
if (NdisStatus == NDIS_STATUS_SUCCESS)
{
//
// We need to request a device state irp.
//
Miniport->WaitWakeSystemState = Miniport->DeviceCaps.SystemWake;
Status = PoRequestPowerIrp(Miniport->PhysicalDeviceObject,
IRP_MN_SET_POWER,
PowerState,
ndisMediaDisconnectComplete,
Miniport,
NULL);
}
FREE_POOL(pWorkItem);
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("<==ndisMediaDisconnectWorker: Miniport %p\n", Miniport));
}
VOID
ndisMediaDisconnectTimeout(
IN PVOID SystemSpecific1,
IN PVOID Context,
IN PVOID SystemSpecific2,
IN PVOID SystemSpecific3
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
//
// Fire off a workitem to take care of this at passive level.
//
PNDIS_MINIPORT_BLOCK Miniport = (PNDIS_MINIPORT_BLOCK)Context;
PNDIS_OPEN_BLOCK MiniportOpen;
PPOWER_WORK_ITEM pWorkItem;
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("==>ndisMediaDisconnectTimeout: Miniport %p\n", Miniport));
do
{
NDIS_ACQUIRE_MINIPORT_SPIN_LOCK_DPC(Miniport);
if (!MINIPORT_PNP_TEST_FLAG(Miniport, fMINIPORT_MEDIA_DISCONNECT_WAIT))
{
NDIS_RELEASE_MINIPORT_SPIN_LOCK_DPC(Miniport);
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("ndisMediaDisconnectTimeout: Miniport %p, media disconnect was cancelled\n", Miniport));
break;
}
//
// Clear the disconnect wait flag.
//
MINIPORT_PNP_CLEAR_FLAG(Miniport, fMINIPORT_MEDIA_DISCONNECT_WAIT);
NDIS_RELEASE_MINIPORT_SPIN_LOCK_DPC(Miniport);
pWorkItem = ALLOC_FROM_POOL(sizeof(POWER_WORK_ITEM), NDIS_TAG_WORK_ITEM);
if (pWorkItem != NULL)
{
//
// Initialize the ndis work item to power on.
//
NdisInitializeWorkItem(&pWorkItem->WorkItem,
(NDIS_PROC)ndisMediaDisconnectWorker,
Miniport);
MINIPORT_PNP_SET_FLAG(Miniport, fMINIPORT_SEND_WAIT_WAKE);
//
// Schedule the workitem to fire.
//
NdisScheduleWorkItem(&pWorkItem->WorkItem);
}
} while (FALSE);
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("<==ndisMediaDisconnectTimeout: Miniport %p\n", Miniport));
}
NTSTATUS
ndisWaitWakeComplete(
IN PDEVICE_OBJECT pdo,
IN UCHAR MinorFunction,
IN POWER_STATE PowerState,
IN PVOID Context,
IN PIO_STATUS_BLOCK IoStatus
)
/*++
Routine Description:
Arguments:
DeviceObject
Irp
Context
Return Value:
--*/
{
PNDIS_MINIPORT_BLOCK Miniport = (PNDIS_MINIPORT_BLOCK)Context;
PIRP pirp;
NTSTATUS Status = IoStatus->Status;
POWER_STATE DevicePowerState;
KIRQL OldIrql;
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("==>ndisWaitWakeComplete: Miniport %p, pIrp %p, Status %lx\n",
Miniport, Miniport->pIrpWaitWake, Status));
NDIS_ACQUIRE_MINIPORT_SPIN_LOCK(Miniport, &OldIrql);
pirp = Miniport->pIrpWaitWake;
Miniport->pIrpWaitWake = NULL;
NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql);
if (pirp != NULL)
{
//
// If this completion routine was called because a wake-up occured at the device level
// then we need to initiate a device irp to start up the nic. If it was completed
// due to a cancellation then we skip this since it was cancelled due to a device irp
// being sent to wake-up the device.
//
if (Status == STATUS_SUCCESS)
{
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("ndisWaitWakeComplete: Miniport %p, Wake irp was complete due to wake event\n", Miniport));
if (!MINIPORT_PNP_TEST_FLAG(Miniport, fMINIPORT_SYSTEM_SLEEPING))
{
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("ndisWaitWakeComplete: Miniport %p, Powering up the Miniport\n", Miniport));
//
// We need to request a set power to power up the device.
//
DevicePowerState.DeviceState = PowerDeviceD0;
Status = PoRequestPowerIrp(Miniport->PhysicalDeviceObject,
IRP_MN_SET_POWER,
DevicePowerState,
NULL,
NULL,
NULL);
}
else
{
//
// it is also possible that the device woke up the whole system (WOL) in which case we
// will get a system power IRP eventually and we don't need to request a power IRP.
//
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("ndisWaitWakeComplete: Miniport %p woke up the system.\n", Miniport));
}
}
else
{
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("ndisWaitWakeComplete: Miniport %p, WAIT_WAKE irp failed or cancelled. Status %lx\n",
Miniport, Status));
}
}
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("<==ndisWaitWakeComplete: Miniport %p\n", Miniport));
return(STATUS_MORE_PROCESSING_REQUIRED);
}
NTSTATUS
ndisQueryPowerComplete(
IN PDEVICE_OBJECT pdo,
IN PIRP pirp,
IN PVOID Context
)
/*++
Routine Description:
Arguments:
pdo - Pointer to the device object
pirp - Pointer to the query power irp
Context - Pointer to the miniport.
Return Value:
--*/
{
NTSTATUS Status = pirp->IoStatus.Status;
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("==>ndisQueryPowerComplete: Miniport %p, Bus driver returned %lx for QueryPower\n",
Context, pirp->IoStatus.Status));
#ifdef TRACE_PM_PROBLEMS
if (!NT_SUCCESS(pirp->IoStatus.Status))
{
DbgPrint("ndisQueryPowerComplete: Miniport %p, Bus Driver returned %lx for QueryPower.\n",
Context, pirp->IoStatus.Status);
}
#endif
PoStartNextPowerIrp(pirp);
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("<==ndisQueryPowerComplete: Miniport %p\n", Context));
return(Status);
}
NTSTATUS
ndisQueryPower(
IN PIRP pirp,
IN PIO_STACK_LOCATION pirpSp,
IN PNDIS_MINIPORT_BLOCK Miniport
)
/*++
Routine Description:
This routine will process the IRP_MN_QUERY_POWER for a miniport driver.
Arguments:
pirp - Pointer to the IRP.
pirpSp - Pointer to the IRPs current stack location.
Adapter - Pointer to the adapter.
Return Value:
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
DEVICE_POWER_STATE DeviceState;
NDIS_STATUS NdisStatus;
PIO_STACK_LOCATION pirpSpN;
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("==>ndisQueryPower: Miniport %p\n", Miniport));
do
{
//
// We only handle system power states sent as a query.
//
if (pirpSp->Parameters.Power.Type != SystemPowerState)
{
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("ndisQueryPower: Miniport %p, Not a system state! Type: 0x%x. State: 0x%x\n",
Miniport, pirpSp->Parameters.Power.Type, pirpSp->Parameters.Power.State));
Status = STATUS_INVALID_DEVICE_REQUEST;
break;
}
//
// Determine if the system state is appropriate and what device state we
// should go to.
//
Status = ndisMPowerPolicy(Miniport,
pirpSp->Parameters.Power.State.SystemState,
&DeviceState,
TRUE);
if (!ndisIsMiniportStarted(Miniport) ||
(Miniport->PnPDeviceState != NdisPnPDeviceStarted) ||
(STATUS_DEVICE_POWERED_OFF == Status))
{
pirp->IoStatus.Status = STATUS_SUCCESS;
PoStartNextPowerIrp(pirp);
IoCompleteRequest(pirp, 0);
return(STATUS_SUCCESS);
}
//
// If we didn't succeed then fail the query power.
//
if (!NT_SUCCESS(Status))
{
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("ndisQueryPower: Miniport %p, Unable to go to system state 0x%x\n",
Miniport, pirpSp->Parameters.Power.State.SystemState));
break;
}
//
// Notify the transports with the query power PnP event.
//
NdisStatus = ndisPnPNotifyAllTransports(Miniport,
NetEventQueryPower,
&DeviceState,
sizeof(DeviceState));
if (NDIS_STATUS_SUCCESS != NdisStatus)
{
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_ERR,
("ndisQueryPower: Miniport %p, ndisPnPNotifyAllTransports failed\n", Miniport));
Status = NdisStatus;
break;
}
//
// Notify the miniport...
//
if (MINIPORT_PNP_TEST_FLAG(Miniport, fMINIPORT_DEVICE_POWER_ENABLE))
{
NdisStatus = ndisQuerySetMiniportDeviceState(Miniport,
DeviceState,
OID_PNP_QUERY_POWER,
FALSE);
if (NDIS_STATUS_SUCCESS != NdisStatus)
{
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("ndisQueryPower: Miniport %p, failed query power\n", Miniport));
Status = STATUS_UNSUCCESSFUL;
break;
}
}
} while (FALSE);
if (!NT_SUCCESS(Status))
{
pirp->IoStatus.Status = Status;
PoStartNextPowerIrp(pirp);
IoCompleteRequest(pirp, 0);
}
else
{
//
// Pass this irp down the stack.
//
pirpSpN = IoGetNextIrpStackLocation(pirp);
pirpSpN->MajorFunction = IRP_MJ_POWER;
pirpSpN->MinorFunction = IRP_MN_QUERY_POWER;
pirpSpN->Parameters.Power.SystemContext = pirpSp->Parameters.Power.SystemContext;
pirpSpN->Parameters.Power.Type = DevicePowerState;
pirpSpN->Parameters.Power.State.DeviceState = DeviceState;
IoSetCompletionRoutine(
pirp,
ndisQueryPowerComplete,
Miniport,
TRUE,
TRUE,
TRUE);
IoMarkIrpPending(pirp);
PoStartNextPowerIrp(pirp);
PoCallDriver(Miniport->NextDeviceObject, pirp);
Status = STATUS_PENDING;
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("ndisQueryPower: Miniport %p, PoCallDriver to NextDeviceObject returned %lx\n", Miniport, Status));
}
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("<==ndisQueryPower: Miniport %p\n", Miniport));
return(Status);
}
VOID
FASTCALL
ndisPmHaltMiniport(
IN PNDIS_MINIPORT_BLOCK Miniport
)
/*++
Routine Description:
This will stop the miniport from functioning...
Arguments:
Miniport - pointer to the mini-port to halt
Return Value:
None.
--*/
{
KIRQL OldIrql;
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("==>ndisPmHaltMiniport: Miniport \n", Miniport));
PnPReferencePackage();
NDIS_ACQUIRE_MINIPORT_SPIN_LOCK(Miniport, &OldIrql);
NdisResetEvent(&Miniport->OpenReadyEvent);
if (MINIPORT_PNP_TEST_FLAG(Miniport, fMINIPORT_PM_HALTED))
{
ASSERT(FALSE);
NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql);
return;
}
//
// Mark this miniport as halting.
//
MINIPORT_SET_FLAG(Miniport, fMINIPORT_PM_HALTING);
MINIPORT_PNP_SET_FLAG(Miniport, fMINIPORT_PM_HALTED);
MINIPORT_CLEAR_FLAG(Miniport, fMINIPORT_NORMAL_INTERRUPTS);
MINIPORT_PNP_SET_FLAG(Miniport, fMINIPORT_NO_SHUTDOWN);
NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql);
ndisMCommonHaltMiniport(Miniport);
NdisMDeregisterAdapterShutdownHandler(Miniport);
Miniport->MiniportAdapterContext = NULL;
PnPDereferencePackage();
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("<==ndisPmHaltMiniport: Miniport %p\n", Miniport));
}
NDIS_STATUS
ndisPmInitializeMiniport(
IN PNDIS_MINIPORT_BLOCK Miniport
)
/*++
Routine Description:
This routine will re-initialize a miniport that has been halted due to
a PM low power transition.
Arguments:
Miniport - Pointer to the miniport block to re-initialize.
Return Value:
--*/
{
PNDIS_M_DRIVER_BLOCK pMiniBlock = Miniport->DriverHandle;
NDIS_WRAPPER_CONFIGURATION_HANDLE ConfigurationHandle;
NDIS_STATUS Status;
NDIS_STATUS OpenErrorStatus;
UINT SelectedMediumIndex;
ULONG Flags;
ULONG GenericUlong = 0;
KIRQL OldIrql;
UCHAR SendFlags;
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("==>ndisPmInitializeMiniport: Miniport %p\n", Miniport));
do
{
MINIPORT_CLEAR_FLAG(Miniport, fMINIPORT_PM_HALTING |
fMINIPORT_DEREGISTERED_INTERRUPT |
fMINIPORT_RESET_REQUESTED |
fMINIPORT_RESET_IN_PROGRESS);
MINIPORT_PNP_CLEAR_FLAG(Miniport, fMINIPORT_REMOVE_IN_PROGRESS);
Flags = Miniport->Flags;
SendFlags = Miniport->SendFlags;
//
// Clean up any workitems that might have been queue'd
//
NDISM_DEQUEUE_WORK_ITEM(Miniport, NdisWorkItemMiniportCallback, NULL);
NDISM_DEQUEUE_WORK_ITEM(Miniport, NdisWorkItemRequest, NULL);
NDISM_DEQUEUE_WORK_ITEM(Miniport, NdisWorkItemSend, NULL);
NDISM_DEQUEUE_WORK_ITEM(Miniport, NdisWorkItemResetRequested, NULL);
NDISM_DEQUEUE_WORK_ITEM(Miniport, NdisWorkItemResetInProgress, NULL);
InitializeListHead(&Miniport->PacketList);
//
// Initialize the configuration handle for use during the initialization.
//
ConfigurationHandle.DriverObject = Miniport->DriverHandle->NdisDriverInfo->DriverObject;
ConfigurationHandle.DeviceObject = Miniport->DeviceObject;
ConfigurationHandle.DriverBaseName = &Miniport->BaseName;
ASSERT(KeGetCurrentIrql() == 0);
Status = ndisInitializeConfiguration((PNDIS_WRAPPER_CONFIGURATION_HANDLE)&ConfigurationHandle,
Miniport,
NULL);
ASSERT(KeGetCurrentIrql() == 0);
if (NDIS_STATUS_SUCCESS != Status)
{
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_ERR,
("ndisPmInitializeMiniport: Miniport %p, ndisInitializeConfiguration failed, Status: 0x%x\n", Miniport, Status));
break;
}
//
// Call adapter callback. The current value for "Export"
// is what we tell him to name this device.
//
MINIPORT_SET_FLAG(Miniport, fMINIPORT_IN_INITIALIZE);
MINIPORT_CLEAR_FLAG(Miniport, fMINIPORT_NORMAL_INTERRUPTS);
Miniport->CurrentDevicePowerState = PowerDeviceD0;
Status = (pMiniBlock->MiniportCharacteristics.InitializeHandler)(
&OpenErrorStatus,
&SelectedMediumIndex,
ndisMediumArray,
ndisMediumArraySize / sizeof(NDIS_MEDIUM),
(NDIS_HANDLE)Miniport,
(NDIS_HANDLE)&ConfigurationHandle);
ASSERT(KeGetCurrentIrql() == 0);
if (NDIS_STATUS_SUCCESS != Status)
{
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_ERR,
("ndisPmInitializeMiniport: Miniport %p, MiniportInitialize handler failed, Status 0x%x\n", Miniport, Status));
break;
}
ASSERT (Miniport->MediaType == ndisMediumArray[SelectedMediumIndex]);
//
// Restore saved settings
//
Miniport->Flags = Flags;
Miniport->SendFlags = SendFlags;
MINIPORT_PNP_CLEAR_FLAG(Miniport, fMINIPORT_PM_HALTED | fMINIPORT_REJECT_REQUESTS);
MINIPORT_CLEAR_FLAG(Miniport, fMINIPORT_IN_INITIALIZE);
CHECK_FOR_NORMAL_INTERRUPTS(Miniport);
//
// Clear the flag preventing the miniport's shutdown handler from being
// called if needed.
//
MINIPORT_PNP_CLEAR_FLAG(Miniport, fMINIPORT_NO_SHUTDOWN);
//
// if device does not need polling for connect status then assume it is connected
// as we always do when we intialize a miniport. if it does require media polling
// leave the media status as it was before suspend. it will be updated on the very first
// wakeup DPC.
//
if (!MINIPORT_TEST_FLAG(Miniport, fMINIPORT_REQUIRES_MEDIA_POLLING))
{
MINIPORT_SET_FLAG(Miniport, fMINIPORT_MEDIA_CONNECTED);
}
if (MINIPORT_TEST_FLAG(Miniport, fMINIPORT_MEDIA_CONNECTED))
{
//
// set the ReceivePacket handler
//
ndisMSetIndicatePacketHandler(Miniport);
}
BLOCK_LOCK_MINIPORT_LOCKED(Miniport, OldIrql);
//
// Restore the filter information.
//
ndisMRestoreFilterSettings(Miniport, NULL, FALSE);
//
// Make sure the filter settings get updated.
//
if (MINIPORT_TEST_FLAG(Miniport, fMINIPORT_DESERIALIZE))
{
ndisMDoRequests(Miniport);
}
else
{
NDISM_PROCESS_DEFERRED(Miniport);
}
UNLOCK_MINIPORT_L(Miniport);
//
// Start up the wake up timer.
//
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("ndisPmInitializeMiniport: Miniport %p, startup the wake-up DPC timer\n", Miniport));
NdisMSetPeriodicTimer((PNDIS_MINIPORT_TIMER)(&Miniport->WakeUpDpcTimer),
Miniport->CheckForHangSeconds*1000);
NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql);
ASSERT(KeGetCurrentIrql() == 0);
if (Miniport->MediaType == NdisMedium802_3)
{
ndisMNotifyMachineName(Miniport, NULL);
}
//
// Register with WMI.
//
Status = IoWMIRegistrationControl(Miniport->DeviceObject, WMIREG_ACTION_REGISTER);
if (!NT_SUCCESS(Status))
{
//
// This should NOT keep the adapter from initializing but we should log the error.
//
DBGPRINT_RAW((DBG_COMP_INIT | DBG_COMP_WMI), DBG_LEVEL_WARN,
("ndisPmInitializeMiniport: Miniport %p, Failed to register for WMI support\n", Miniport));
}
Status = NDIS_STATUS_SUCCESS;
KeQueryTickCount(&Miniport->NdisStats.StartTicks);
} while (FALSE);
if (NDIS_STATUS_SUCCESS != Status)
{
NdisMDeregisterAdapterShutdownHandler(Miniport);
ndisLastFailedInitErrorCode = Status;
ASSERT(Miniport->Interrupt == NULL);
ASSERT(Miniport->TimerQueue == NULL);
ASSERT(Miniport->MapRegisters == NULL);
if ((Miniport->TimerQueue != NULL) || (Miniport->Interrupt != NULL))
{
if (Miniport->Interrupt != NULL)
{
BAD_MINIPORT(Miniport, "Unloading without deregistering interrupt");
}
else
{
BAD_MINIPORT(Miniport, "Unloading without deregistering timer");
}
KeBugCheckEx(BUGCODE_ID_DRIVER,
(ULONG_PTR)Miniport,
(ULONG_PTR)Miniport->Interrupt,
(ULONG_PTR)Miniport->TimerQueue,
1);
}
MINIPORT_SET_FLAG(Miniport, fMINIPORT_PM_HALTING);
MINIPORT_PNP_SET_FLAG(Miniport, fMINIPORT_PM_HALTED);
MINIPORT_CLEAR_FLAG(Miniport, fMINIPORT_NORMAL_INTERRUPTS);
}
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("<==ndisPmInitializeMiniport: Miniport %p\n", Miniport));
return(Status);
}
NDIS_STATUS
ndisQuerySetMiniportDeviceState(
IN PNDIS_MINIPORT_BLOCK Miniport,
IN DEVICE_POWER_STATE DeviceState,
IN NDIS_OID Oid,
IN BOOLEAN fSet
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
NDIS_STATUS NdisStatus;
NDIS_REQUEST PowerReq;
PNDIS_COREQ_RESERVED CoReqRsvd;
ULONG State;
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("==>ndisQuerySetMiniportDeviceState: Miniport %p\n", Miniport));
//
// Setup the miniport's internal request for a set power OID.
//
CoReqRsvd = PNDIS_COREQ_RESERVED_FROM_REQUEST(&PowerReq);
INITIALIZE_EVENT(&CoReqRsvd->Event);
PowerReq.DATA.SET_INFORMATION.InformationBuffer = &State;
PowerReq.DATA.SET_INFORMATION.InformationBufferLength = sizeof(State);
PowerReq.RequestType = fSet ? NdisRequestSetInformation : NdisRequestQueryInformation;
PowerReq.DATA.SET_INFORMATION.Oid = Oid;
PowerReq.DATA.SET_INFORMATION.InformationBuffer = &DeviceState;
PowerReq.DATA.SET_INFORMATION.InformationBufferLength = sizeof(DeviceState);
NdisStatus = ndisQuerySetMiniport(Miniport,
NULL,
fSet,
&PowerReq,
NULL);
#ifdef TRACE_PM_PROBLEMS
if (NdisStatus != NDIS_STATUS_SUCCESS)
{
DbgPrint("ndisQuerySetMiniportDeviceState: Miniport %p, (Name: %p) failed Oid %lx, Set = %lx with error %lx\n",
Miniport,
Miniport->pAdapterInstanceName,
Oid,
fSet,
NdisStatus);
}
#endif
//
// Miniport can't fail the set power request.
//
if (fSet)
{
ASSERT(NDIS_STATUS_SUCCESS == NdisStatus);
}
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("<==ndisQuerySetMiniportDeviceState: Miniport %p, Status %lx\n", Miniport, NdisStatus));
return(NdisStatus);
}
NTSTATUS
ndisSetSystemPowerComplete(
IN PDEVICE_OBJECT pdo,
IN UCHAR MinorFunction,
IN POWER_STATE PowerState,
IN PVOID Context,
IN PIO_STATUS_BLOCK IoStatus
)
/*++
Routine Description:
Arguments:
pdo - Pointer to the DEVICE_OBJECT for the miniport.
pirp - Pointer to the device set power state IRP that we created.
Context - Pointer to the system set power state sent by the OS.
Return Value:
--*/
{
PIRP pirpSystem = Context;
PIO_STACK_LOCATION pirpSp;
PNDIS_MINIPORT_BLOCK Miniport;
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("==>ndisSetSystemPowerComplete: DeviceObject %p\n", pdo));
//
// Save the status code with the original IRP.
//
pirpSystem->IoStatus = *IoStatus;
//
// did everything go well?
//
if (NT_SUCCESS(IoStatus->Status))
{
//
// Get current stack pointer.
//
pirpSp = IoGetCurrentIrpStackLocation(pirpSystem);
ASSERT(SystemPowerState == pirpSp->Parameters.Power.Type);
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("ndisSetSystemPowerComplete: DeviceObject %p, Going to system power state %lx\n",
pdo, pirpSp->Parameters.Power.State));
//
// Notify the system that we are in the appropriate power state.
//
PoSetPowerState(pirpSp->DeviceObject,SystemPowerState, pirpSp->Parameters.Power.State);
Miniport = (PNDIS_MINIPORT_BLOCK)((PNDIS_WRAPPER_CONTEXT)pirpSp->DeviceObject->DeviceExtension + 1);
//
// now send down the System power IRP
//
IoCopyCurrentIrpStackLocationToNext(pirpSystem);
PoCallDriver(Miniport->NextDeviceObject, pirpSystem);
}
else
{
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_ERR,
("ndisSetSystemPowerComplete: DeviceObject %p, IRP_MN_SET_POWER failed!\n", pdo));
IoCompleteRequest(pirpSystem, 0);
}
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("<==ndisSetSystemPowerComplete: DeviceObject %p\n", pdo));
return(STATUS_MORE_PROCESSING_REQUIRED);
}
NTSTATUS
ndisSetSystemPowerOnComplete(
IN PDEVICE_OBJECT pdo,
IN PIRP pirp,
IN PVOID Context
)
/*++
Routine Description:
Completion routine for S0 irp completion. This routine requests a D0 irp to be sent down the stack.
Arguments:
pdo - Pointer to the DEVICE_OBJECT for the miniport.
pirp - Pointer to the S0 irp sent by the power manager.
Context - Pointer to the miniport context
Return Value:
--*/
{
PIO_STACK_LOCATION pirpSp = IoGetCurrentIrpStackLocation(pirp);
PNDIS_MINIPORT_BLOCK Miniport = Context;
POWER_STATE PowerState;
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("==>ndisSetSystemPowerOnComplete: DeviceObject %p\n", pdo));
//
// did everything go well?
//
if (NT_SUCCESS(pirp->IoStatus.Status))
{
//
// Request the D irp now.
//
PowerState.DeviceState = PowerDeviceD0;
PoRequestPowerIrp(Miniport->PhysicalDeviceObject,
IRP_MN_SET_POWER,
PowerState,
NULL,
NULL,
NULL);
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("ndisSetSystemPowerOnComplete: DeviceObject %p, Going to system power state %lx\n",
pdo, PowerState));
//
// Notify the system that we are in the appropriate power state.
//
PoSetPowerState(pdo ,SystemPowerState, pirpSp->Parameters.Power.State);
}
return(STATUS_SUCCESS);
}
VOID
ndisDevicePowerOn(
IN PPOWER_WORK_ITEM pWorkItem,
IN PVOID pContext
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PNDIS_MINIPORT_BLOCK Miniport = (PNDIS_MINIPORT_BLOCK)pContext;
DEVICE_POWER_STATE DeviceState;
POWER_STATE PowerState;
NDIS_STATUS NdisStatus;
PIRP pirp;
PIO_STACK_LOCATION pirpSp;
NTSTATUS NtStatus;
NDIS_POWER_PROFILE PowerProfile;
BOOLEAN fNotifyProtocols = FALSE;
BOOLEAN fStartMediaDisconnectTimer = FALSE;
KIRQL OldIrql;
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("==>ndisDevicePowerOn: Miniport %p\n", Miniport));
PnPReferencePackage();
pirp = pWorkItem->pIrp;
pirpSp = IoGetCurrentIrpStackLocation(pirp);
DeviceState = pirpSp->Parameters.Power.State.DeviceState;
#ifdef TRACE_PM_PROBLEMS
if (!NT_SUCCESS(pirp->IoStatus.Status))
{
DbgPrint("ndisDevicePowerOn: Miniport %p, Bus Driver returned %lx for Powering up the Miniport.\n",
Miniport, pirp->IoStatus.Status);
}
#endif
if (NT_SUCCESS(pirp->IoStatus.Status))
{
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("ndisDevicePowerOn: Miniport %p, Bus driver succeeded power up\n", Miniport));
//
// If the device is not in D0 then we need to wake up the miniport and
// restore the handlers.
//
if (Miniport->CurrentDevicePowerState != PowerDeviceD0)
{
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("ndisDevicePowerOn: Miniport %p, Power up the Miniport\n", Miniport));
//
// What type of miniport was this?
//
if (MINIPORT_PNP_TEST_FLAG(Miniport, fMINIPORT_DEVICE_POWER_ENABLE))
{
//
// Set the miniport's device state.
//
NdisStatus = ndisQuerySetMiniportDeviceState(Miniport,
DeviceState,
OID_PNP_SET_POWER,
TRUE);
ASSERT(KeGetCurrentIrql() == 0);
if (NdisStatus == NDIS_STATUS_SUCCESS)
Miniport->CurrentDevicePowerState = DeviceState;
//
// Start wake up timer
//
NdisMSetPeriodicTimer((PNDIS_MINIPORT_TIMER)(&Miniport->WakeUpDpcTimer),
Miniport->CheckForHangSeconds*1000);
}
else
{
ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
if (((Miniport->DriverHandle->Flags & fMINIBLOCK_INTERMEDIATE_DRIVER) == 0) &&
(MINIPORT_PNP_TEST_FLAG(Miniport, fMINIPORT_PM_HALTED)))
{
NdisStatus = ndisPmInitializeMiniport(Miniport);
ASSERT(KeGetCurrentIrql() == 0);
}
else
{
NdisStatus = NDIS_STATUS_SUCCESS;
}
}
if (NDIS_STATUS_SUCCESS == NdisStatus)
{
if (ndisIsMiniportStarted(Miniport))
{
NdisSetEvent(&Miniport->OpenReadyEvent);
//
// Restore the handlers.
//
NDIS_ACQUIRE_MINIPORT_SPIN_LOCK(Miniport, &OldIrql);
ndisMRestoreOpenHandlers(Miniport, fMINIPORT_STATE_PM_STOPPED);
NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql);
ASSERT(KeGetCurrentIrql() == 0);
ASSERT(Miniport->SymbolicLinkName.Buffer != NULL);
if (Miniport->SymbolicLinkName.Buffer != NULL)
{
NtStatus = IoSetDeviceInterfaceState(&Miniport->SymbolicLinkName, TRUE);
}
if (!NT_SUCCESS(NtStatus))
{
DBGPRINT_RAW(DBG_COMP_INIT, DBG_LEVEL_ERR,
("ndisDevicePowerOn: IoRegisterDeviceClassAssociation failed: Miniport %p, Status %lx\n", Miniport, NtStatus));
}
fNotifyProtocols = TRUE;
fStartMediaDisconnectTimer = TRUE;
//
// let the adapter know about the current power source
//
PowerProfile = ((BOOLEAN)ndisAcOnLine == TRUE) ?
NdisPowerProfileAcOnLine :
NdisPowerProfileBattery;
ndisNotifyMiniports(Miniport,
NdisDevicePnPEventPowerProfileChanged,
&PowerProfile,
sizeof(NDIS_POWER_PROFILE));
}
//
// Save the new power state the device is in.
//
Miniport->CurrentDevicePowerState = DeviceState;
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("ndisDevicePowerOn: Miniport %p, Going to device state 0x%x\n", Miniport, DeviceState));
//
// Notify the system that we are in the new device state.
//
PowerState.DeviceState = DeviceState;
PoSetPowerState(Miniport->DeviceObject, DevicePowerState, PowerState);
}
else
{
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_ERR,
("ndisDevicePowerOn: Miniport %p, Power on failed by device driver for the Miniport, Error %lx!\n",
Miniport, NdisStatus));
#ifdef TRACE_PM_PROBLEMS
DbgPrint("ndisDevicePowerOn: Miniport %p, Device Driver failed powering up Miniport with Error %lx.\n",
Miniport,
NdisStatus);
#endif
pirp->IoStatus.Status = STATUS_UNSUCCESSFUL;
}
}
else
{
//
// device is already in D0. we are here because of a cancel of QueryPower
//
if (ndisIsMiniportStarted(Miniport) &&
(Miniport->PnPDeviceState == NdisPnPDeviceStarted))
{
//
// even if the current state of the device is D0, we
// need to notify the protocol. because we could be getting
// this IRP as a cancel to a query IRP -or- the device
// never lost its D0 state, but the sytem went to sleep
// and woke up!
//
NdisSetEvent(&Miniport->OpenReadyEvent);
//
// Restore the handlers.
//
NDIS_ACQUIRE_MINIPORT_SPIN_LOCK(Miniport, &OldIrql);
ndisMRestoreOpenHandlers(Miniport, fMINIPORT_STATE_PM_STOPPED);
NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql);
fNotifyProtocols = TRUE;
fStartMediaDisconnectTimer = FALSE;
}
}
}
NtStatus = pirp->IoStatus.Status;
PoStartNextPowerIrp(pirp);
IoCompleteRequest(pirp, 0);
//
// notify the protocols here after completing the power IRP
// to avoid deadlocks when protocols block on a request that can only
// complete when the other power IRPs go through
//
//
// Handle the case where the device was not able to power up.
//
if (!NT_SUCCESS(NtStatus))
{
#ifdef TRACE_PM_PROBLEMS
DbgPrint("ndisDevicePowerOn: Miniport %p, Bus or Device failed powering up the Miniport with Error %lx.\n",
Miniport,
NtStatus);
#endif
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_ERR,
("ndisDevicePowerOn: Miniport %p, Power on failed by bus or device driver for Miniport with Error %lx!\n",
Miniport, NtStatus));
//
// Mark the miniport as having failed so that we remove it correctly.
//
MINIPORT_PNP_SET_FLAG(Miniport, fMINIPORT_DEVICE_FAILED);
//
// We need to tell pnp that the device state has changed.
//
IoInvalidateDeviceState(Miniport->PhysicalDeviceObject);
ASSERT(KeGetCurrentIrql() == 0);
}
if (fNotifyProtocols)
{
//
// for some protocols we may have closed the binding
//
ndisCheckAdapterBindings(Miniport, NULL);
//
// Notify the transports.
//
ndisPnPNotifyAllTransports(Miniport,
NetEventSetPower,
&DeviceState,
sizeof(DeviceState));
ndisNotifyDevicePowerStateChange(Miniport, DeviceState);
//
// if media state has changed from disconnect to connect
// and the last indicated status was disconnect,
// we should notify the protcols (and Ndis) that the media is
// connected
//
if (MINIPORT_PNP_TEST_FLAG(Miniport, fMINIPORT_MEDIA_DISCONNECT_INDICATED) &&
MINIPORT_TEST_FLAG(Miniport, fMINIPORT_MEDIA_CONNECTED))
{
BLOCK_LOCK_MINIPORT_LOCKED(Miniport, OldIrql);
NDIS_RELEASE_MINIPORT_SPIN_LOCK_DPC(Miniport);
MINIPORT_CLEAR_FLAG(Miniport, fMINIPORT_MEDIA_CONNECTED);
NdisMIndicateStatus(Miniport,
NDIS_STATUS_MEDIA_CONNECT,
INTERNAL_INDICATION_BUFFER,
INTERNAL_INDICATION_SIZE);
NdisMIndicateStatusComplete(Miniport);
UNLOCK_MINIPORT_L(Miniport);
LOWER_IRQL(OldIrql, DISPATCH_LEVEL);
}
//
// check the media status and if it is disconnected, start the timer
//
if (!MINIPORT_TEST_FLAG(Miniport, fMINIPORT_MEDIA_CONNECTED) &&
fStartMediaDisconnectTimer)
{
if (MINIPORT_PNP_TEST_FLAG(Miniport, fMINIPORT_DEVICE_POWER_ENABLE) &&
(Miniport->WakeUpEnable & NDIS_PNP_WAKE_UP_LINK_CHANGE) &&
(Miniport->MediaDisconnectTimeOut != (USHORT)(-1)))
{
//
// Are we already waiting for the disconnect timer to fire?
//
if (!MINIPORT_PNP_TEST_FLAG(Miniport, fMINIPORT_MEDIA_DISCONNECT_WAIT))
{
//
// Mark the miniport as disconnecting and fire off the
// timer.
//
MINIPORT_PNP_CLEAR_FLAG(Miniport, fMINIPORT_MEDIA_DISCONNECT_CANCELLED);
MINIPORT_PNP_SET_FLAG(Miniport, fMINIPORT_MEDIA_DISCONNECT_WAIT);
NdisSetTimer(&Miniport->MediaDisconnectTimer, Miniport->MediaDisconnectTimeOut * 1000);
}
}
}
}
ASSERT(KeGetCurrentIrql() == 0);
MINIPORT_DECREMENT_REF(Miniport);
FREE_POOL(pWorkItem);
PnPDereferencePackage();
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("<==ndisDevicePowerOn: Miniport %p\n", Miniport));
}
NTSTATUS
ndisSetDevicePowerOnComplete(
IN PDEVICE_OBJECT pdo,
IN PIRP pirp,
IN PVOID pContext
)
/*++
Routine Description:
Arguments:
pdo - Pointer to the device object for the miniport.
pirp - Pointer to the device set power state IRP that was completed.
Context - Not used
Return Value:
--*/
{
PNDIS_MINIPORT_BLOCK Miniport = (PNDIS_MINIPORT_BLOCK)pContext;
PPOWER_WORK_ITEM pWorkItem;
NDIS_STATUS NdisStatus;
DEVICE_POWER_STATE DeviceState;
POWER_STATE PowerState;
PIO_STACK_LOCATION pirpSp;
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("==>ndisSetDevicePowerOnComplete: Miniport %p, Irp %p, Status %lx\n",
Miniport, pirp, pirp->IoStatus.Status));
do
{
if (Miniport->PnPDeviceState != NdisPnPDeviceStarted)
{
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("ndisSetDevicePowerOnComplete: Miniport %p is not started yet.\n", Miniport));
pirpSp = IoGetCurrentIrpStackLocation(pirp);
DeviceState = pirpSp->Parameters.Power.State.DeviceState;
//
// Notify the system that we are in the new device state.
//
Miniport->CurrentDevicePowerState = DeviceState;
PowerState.DeviceState = DeviceState;
PoSetPowerState(Miniport->DeviceObject, DevicePowerState, PowerState);
PoStartNextPowerIrp(pirp);
IoCompleteRequest(pirp, 0);
break;
}
pWorkItem = ALLOC_FROM_POOL(sizeof(POWER_WORK_ITEM), NDIS_TAG_WORK_ITEM);
if (pWorkItem == NULL)
{
pirp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
PoStartNextPowerIrp(pirp);
IoCompleteRequest(pirp, 0);
break;
}
//
// Initialize the ndis work item to power on.
//
NdisInitializeWorkItem(&pWorkItem->WorkItem,
(NDIS_PROC)ndisDevicePowerOn,
Miniport);
pWorkItem->pIrp = pirp;
//
// this reference and corresponding dereference in ndisDevicePowerOn is done
// to ensure ndis does not return back from REMOVE IRP while we are waiting
// for ndisDevicePowerOn to fire.
//
MINIPORT_INCREMENT_REF(Miniport);
//
// Schedule the workitem to fire.
//
INITIALIZE_WORK_ITEM((PWORK_QUEUE_ITEM)(&pWorkItem->WorkItem.WrapperReserved),
ndisWorkItemHandler,
&pWorkItem->WorkItem);
XQUEUE_WORK_ITEM((PWORK_QUEUE_ITEM)(&pWorkItem->WorkItem.WrapperReserved), DelayedWorkQueue);
} while (FALSE);
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("<==ndisSetDevicePowerOnComplete: Miniport %p\n", Miniport));
return(STATUS_MORE_PROCESSING_REQUIRED);
}
VOID
ndisDevicePowerDown(
IN PPOWER_WORK_ITEM pWorkItem,
IN PVOID pContext
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PNDIS_MINIPORT_BLOCK Miniport = (PNDIS_MINIPORT_BLOCK)pContext;
DEVICE_POWER_STATE DeviceState;
POWER_STATE PowerState;
NDIS_STATUS NdisStatus;
PIRP pirp;
PIO_STACK_LOCATION pirpSp;
KIRQL OldIrql;
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("==>ndisDevicePowerDown: Miniport %p\n", Miniport));
PnPReferencePackage();
pirp = pWorkItem->pIrp;
pirpSp = IoGetCurrentIrpStackLocation(pirp);
DeviceState = pirpSp->Parameters.Power.State.DeviceState;
//
// If the complete status is successful then we need to continue with
// wakeing the stack.
//
if (NT_SUCCESS(pirp->IoStatus.Status))
{
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("ndisDevicePowerDown: Miniport %p, going to device state 0x%x\n", Miniport, DeviceState));
//
// Build a power state.
//
PowerState.DeviceState = DeviceState;
//
// Save the current device state with the miniport block.
//
Miniport->CurrentDevicePowerState = DeviceState;
//
// Let the system know about the devices new power state.
//
PoSetPowerState(Miniport->DeviceObject, DevicePowerState, PowerState);
}
else if (ndisIsMiniportStarted(Miniport) &&
(Miniport->PnPDeviceState == NdisPnPDeviceStarted))
{
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_ERR,
("ndisDevicePowerDown: Miniport %p, Bus driver failed to power down the Miniport\n", Miniport));
#ifdef TRACE_PM_PROBLEMS
DbgPrint("ndisDevicePowerDown: Miniport %p, Bus Driver returned %lx for Powering Down the Miniport\n",
Miniport, pirp->IoStatus.Status);
#endif
//
// We need to go back to the current device state.
//
PowerState.DeviceState = Miniport->CurrentDevicePowerState;
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("ndisDevicePowerDown: Miniport %p, going to device power state 0x%x\n", Miniport, Miniport->CurrentDevicePowerState));
//
// What type of miniport was this?
//
if (MINIPORT_PNP_TEST_FLAG(Miniport, fMINIPORT_DEVICE_POWER_ENABLE))
{
//
// Set the miniport's device state.
//
NdisStatus = ndisQuerySetMiniportDeviceState(Miniport,
PowerState.DeviceState,
OID_PNP_SET_POWER,
TRUE);
}
else
{
NdisStatus = ndisPmInitializeMiniport(Miniport);
}
//
// Is the miniport initialized?
//
if (NDIS_STATUS_SUCCESS != NdisStatus)
{
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("ndisDevicePowerDown: Miniport %p, failed to power down but we are not able to reinitialize it.\n", Miniport));
//
// Mark the miniport as having failed so that we remove it correctly.
//
MINIPORT_PNP_SET_FLAG(Miniport, fMINIPORT_DEVICE_FAILED);
//
// The bus driver failed the power off and we can't power the miniport back on.
// we invalidate the device state so that it will get removed.
//
IoInvalidateDeviceState(Miniport->PhysicalDeviceObject);
pirp->IoStatus.Status = STATUS_UNSUCCESSFUL;
}
else
{
//
// Restore the handlers.
//
NDIS_ACQUIRE_MINIPORT_SPIN_LOCK(Miniport, &OldIrql);
ndisMRestoreOpenHandlers(Miniport, fMINIPORT_STATE_PM_STOPPED);
NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql);
IoSetDeviceInterfaceState(&Miniport->SymbolicLinkName, TRUE);
ndisNotifyDevicePowerStateChange(Miniport, PowerState.DeviceState);
//
// Notify the transports.
//
NdisStatus = ndisPnPNotifyAllTransports(Miniport,
NetEventSetPower,
&PowerState.DeviceState,
sizeof(PowerState.DeviceState));
ASSERT(NDIS_STATUS_SUCCESS == NdisStatus);
}
}
PoStartNextPowerIrp(pirp);
IoCompleteRequest(pirp, 0);
FREE_POOL(pWorkItem);
ASSERT(KeGetCurrentIrql() == 0);
PnPDereferencePackage();
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("<==ndisDevicePowerDown: Miniport %p\n", Miniport));
}
NTSTATUS
ndisSetDevicePowerDownComplete(
IN PDEVICE_OBJECT pdo,
IN PIRP pirp,
IN PVOID pContext
)
/*++
Routine Description:
Arguments:
pdo - Pointer to the device object for the miniport.
pirp - Pointer to the device set power state IRP that was completed.
Context - Not used
Return Value:
--*/
{
PNDIS_MINIPORT_BLOCK Miniport = (PNDIS_MINIPORT_BLOCK)pContext;
NTSTATUS Status;
PPOWER_WORK_ITEM pWorkItem;
NDIS_STATUS NdisStatus;
BOOLEAN fTimerCancelled;
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("==>ndisSetDevicePowerDownComplete: Miniport %p, Irp %p, Status %lx\n",
Miniport, pirp, pirp->IoStatus.Status));
//
// cancel any pending media disconnect timers
//
if (MINIPORT_PNP_TEST_FLAG(Miniport, fMINIPORT_MEDIA_DISCONNECT_WAIT))
{
//
// Clear the disconnect wait bit and cancel the timer.
// IF the timer routine hasn't grabed the lock then we are ok.
//
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("ndisSetDevicePowerDownComplete: Miniport %p, cancelling media disconnect timer\n",Miniport));
MINIPORT_PNP_CLEAR_FLAG(Miniport, fMINIPORT_MEDIA_DISCONNECT_WAIT);
MINIPORT_PNP_SET_FLAG(Miniport, fMINIPORT_MEDIA_DISCONNECT_CANCELLED);
NdisCancelTimer(&Miniport->MediaDisconnectTimer, &fTimerCancelled);
}
do
{
pWorkItem = ALLOC_FROM_POOL(sizeof(POWER_WORK_ITEM), NDIS_TAG_WORK_ITEM);
if (pWorkItem == NULL)
{
pirp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
PoStartNextPowerIrp(pirp);
IoCompleteRequest(pirp, 0);
break;
}
NdisInitializeWorkItem(&pWorkItem->WorkItem,
(NDIS_PROC)ndisDevicePowerDown,
Miniport);
pWorkItem->pIrp = pirp;
//
// Schedule the workitem to fire.
//
NdisScheduleWorkItem(&pWorkItem->WorkItem);
} while (FALSE);
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("<==ndisSetDevicePowerDownComplete: Miniport %p\n", Miniport));
return(STATUS_MORE_PROCESSING_REQUIRED);
}
NTSTATUS
ndisSetPower(
IN PIRP pirp,
IN PIO_STACK_LOCATION pirpSp,
IN PNDIS_MINIPORT_BLOCK Miniport
)
/*++
Routine Description:
This routine will process the IRP_MN_SET_POWER for a miniport driver.
Arguments:
pirp - Pointer to the IRP.
pirpSp - Pointer to the IRPs current stack location.
Miniport - Pointer to the Miniport
Return Value:
--*/
{
POWER_STATE PowerState;
DEVICE_POWER_STATE DeviceState;
SYSTEM_POWER_STATE SystemState;
NDIS_DEVICE_POWER_STATE NdisDeviceState;
NTSTATUS Status;
PIO_STACK_LOCATION pirpSpN;
IO_STATUS_BLOCK IoStatus;
PIRP pirpWake;
NDIS_STATUS NdisStatus;
KEVENT Event;
ULONG WakeEnable = 0;
PIRP pIrpWaitWake;
KIRQL OldIrql;
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("==>ndisSetPower: Miniport %p, Irp %p\n", Miniport, pirp));
PnPReferencePackage();
switch (pirpSp->Parameters.Power.Type)
{
case SystemPowerState:
SystemState = pirpSp->Parameters.Power.State.SystemState;
Miniport->WaitWakeSystemState = SystemState;
//
// if system is shutting down, call the shutdown handler
// for miniport and be done with it
//
if (SystemState >= PowerSystemShutdown)
{
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("ndisSetPower: Miniport %p, SystemState %lx\n", Miniport, SystemState));
if ((Miniport->DriverHandle->Flags & fMINIBLOCK_INTERMEDIATE_DRIVER) == 0)
{
ndisMShutdownMiniport(Miniport);
}
pirp->IoStatus.Status = STATUS_SUCCESS;
PoStartNextPowerIrp(pirp);
IoSkipCurrentIrpStackLocation(pirp);
Status = PoCallDriver(Miniport->NextDeviceObject, pirp);
break;
}
else
{
//
// Get the device state for the system state. Note that this will
// set the fMINIPORT_SYSTEM_SLEEPING flag if we are going to
// SystemState > PowerSystemWorking
//
Status = ndisMPowerPolicy(Miniport, SystemState, &DeviceState, FALSE);
//
// Is the device already powered off?
//
if (STATUS_DEVICE_POWERED_OFF == Status)
{
pirp->IoStatus.Status = STATUS_SUCCESS;
PoStartNextPowerIrp(pirp);
IoCompleteRequest(pirp, 0);
Status = STATUS_SUCCESS;
break;
}
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("ndisSetPower: Miniport %p, SystemPowerState[0x%x] DevicePowerState[0x%x]\n",
Miniport, SystemState, DeviceState));
PowerState.DeviceState = DeviceState;
if (SystemState > PowerSystemWorking)
{
NdisResetEvent(&Miniport->OpenReadyEvent);
//
// if system is going to sleep mode, then notify protocols and
// request a WAIT_WAKE IRP
//
//
// Notify the transports of the impending state transition.
// There is nothing we can do if transports fail this
// Note: for all practical purposes there is no need to map
// SytemState to device state here
//
if (SystemState > PowerSystemSleeping3)
NdisDeviceState = PowerSystemSleeping3;
else
NdisDeviceState = SystemState;
ndisNotifyDevicePowerStateChange(Miniport, NdisDeviceState);
NdisStatus = ndisPnPNotifyAllTransports(Miniport,
NetEventSetPower,
&NdisDeviceState,
sizeof(SystemState));
//
// protocols can't fail going to a sleeping state
//
ASSERT(NDIS_STATUS_SUCCESS == NdisStatus);
MiniportReferencePackage();
//
// Swap the handlers.
//
NDIS_ACQUIRE_MINIPORT_SPIN_LOCK(Miniport, &OldIrql);
ndisMSwapOpenHandlers(Miniport,
NDIS_STATUS_ADAPTER_NOT_READY,
fMINIPORT_STATE_PM_STOPPED);
NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql);
MiniportDereferencePackage();
//
// What type of miniport was this?
//
if (MINIPORT_PNP_TEST_FLAG(Miniport, fMINIPORT_DEVICE_POWER_ENABLE))
{
NDIS_ACQUIRE_MINIPORT_SPIN_LOCK(Miniport, &OldIrql);
if (Miniport->pIrpWaitWake != NULL)
{
MINIPORT_PNP_CLEAR_FLAG(Miniport, fMINIPORT_SEND_WAIT_WAKE);
}
NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql);
//
// Is wake-up enabled?
//
if (MINIPORT_PNP_TEST_FLAG(Miniport, fMINIPORT_SEND_WAIT_WAKE))
{
MINIPORT_PNP_CLEAR_FLAG(Miniport, fMINIPORT_SEND_WAIT_WAKE);
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("ndisSetPower: Miniport %p, Creating a wake irp for the device\n", Miniport));
//
// reuquest a power irp for wake notification
//
PowerState.SystemState = Miniport->WaitWakeSystemState;
Status = PoRequestPowerIrp(Miniport->PhysicalDeviceObject,
IRP_MN_WAIT_WAKE,
PowerState,
ndisWaitWakeComplete,
Miniport,
&Miniport->pIrpWaitWake);
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("ndisSetPower: Miniport %p, WaiteWakeIrp %p\n",
Miniport, Miniport->pIrpWaitWake));
}
}
}
else
{
NDIS_ACQUIRE_MINIPORT_SPIN_LOCK(Miniport, &OldIrql);
pIrpWaitWake = Miniport->pIrpWaitWake;
NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql);
//
// if we are transitioning to PowerSystemWorking or just asserting
// it to cancel a query power, we will notify the protocols when
// we get the device power IRP
//
//
// If there is a wait-wake irp outstanding then we need to cancel it.
//
if (pIrpWaitWake)
{
if (IoCancelIrp(pIrpWaitWake))
{
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("ndisSetPower: Miniport %p, Successfully canceled wake irp\n", Miniport));
}
}
//
// Send the S0 irp down the stack first. When it completes, send the D0 irp. This
// allows the power manager to resume faster while the slow network initialization
// takes place in the background.
//
IoCopyCurrentIrpStackLocationToNext(pirp);
IoSetCompletionRoutine(pirp,
ndisSetSystemPowerOnComplete,
Miniport,
TRUE,
TRUE,
TRUE);
IoMarkIrpPending(pirp);
PoStartNextPowerIrp(pirp);
PoCallDriver(Miniport->NextDeviceObject, pirp);
Status = STATUS_PENDING;
break;
}
}
//
// no matter what was the outcome of trying to set a WAIT_WAKE IRP
// we still have to set the device state appropiately
//
PowerState.DeviceState = DeviceState;
//
// Save the device object with the system irp to use in the
// completion routine.
//
pirpSpN = IoGetNextIrpStackLocation(pirp);
pirpSpN->DeviceObject = Miniport->DeviceObject;
IoMarkIrpPending(pirp);
PoStartNextPowerIrp(pirp);
//
// Let the completion routine take care of everything.
//
Status = PoRequestPowerIrp(Miniport->PhysicalDeviceObject,
IRP_MN_SET_POWER,
PowerState,
ndisSetSystemPowerComplete,
pirp,
NULL);
if (STATUS_PENDING != Status)
{
IoStatus.Status = Status;
IoStatus.Information = 0;
ndisSetSystemPowerComplete(Miniport->PhysicalDeviceObject,
IRP_MN_SET_POWER,
PowerState,
pirp,
&IoStatus);
}
Status = STATUS_PENDING;
break;
case DevicePowerState:
//
// Get the device state.
//
DeviceState = pirpSp->Parameters.Power.State.DeviceState;
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("ndisSetPower: Miniport %p, DeviceState[0x%x]\n", Miniport, DeviceState));
//
// What state is the device going to?
//
switch (DeviceState)
{
case PowerDeviceD0:
//
// We need to pass this IRP down to the pdo so that
// it can power on.
//
IoCopyCurrentIrpStackLocationToNext(pirp);
IoSetCompletionRoutine(pirp,
ndisSetDevicePowerOnComplete,
Miniport,
TRUE,
TRUE,
TRUE);
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("ndisSetPower: Miniport %p, Power up the bus driver.\n", Miniport));
//
// Mark the IRP as pending and send it down the stack.
//
IoMarkIrpPending(pirp);
PoStartNextPowerIrp(pirp);
PoCallDriver(Miniport->NextDeviceObject, pirp);
Status = STATUS_PENDING;
break;
case PowerDeviceD1:
case PowerDeviceD2:
case PowerDeviceD3:
if (ndisIsMiniportStarted(Miniport) &&
(Miniport->PnPDeviceState == NdisPnPDeviceStarted))
{
//
// if the device state setting is not the result of going to
// a sleeping system state, (such as media disconnect case)
// then notify protocols, etc.
//
if (!MINIPORT_PNP_TEST_FLAG(Miniport, fMINIPORT_SYSTEM_SLEEPING))
{
NdisResetEvent(&Miniport->OpenReadyEvent);
ndisNotifyDevicePowerStateChange(Miniport, DeviceState);
//
// Notify the transports of the impending state transition.
//
NdisStatus = ndisPnPNotifyAllTransports(Miniport,
NetEventSetPower,
&DeviceState,
sizeof(DeviceState));
ASSERT(NDIS_STATUS_SUCCESS == NdisStatus);
//
// Swap the handlers.
//
NDIS_ACQUIRE_MINIPORT_SPIN_LOCK(Miniport, &OldIrql);
ndisMSwapOpenHandlers(Miniport,
NDIS_STATUS_ADAPTER_NOT_READY,
fMINIPORT_STATE_PM_STOPPED);
NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql);
}
//
// What type of miniport was this?
//
if (MINIPORT_PNP_TEST_FLAG(Miniport, fMINIPORT_DEVICE_POWER_ENABLE))
{
BOOLEAN Canceled;
if (!MINIPORT_PNP_TEST_FLAG(Miniport, fMINIPORT_SYSTEM_SLEEPING))
{
NDIS_ACQUIRE_MINIPORT_SPIN_LOCK(Miniport, &OldIrql);
if (Miniport->pIrpWaitWake != NULL)
{
MINIPORT_PNP_CLEAR_FLAG(Miniport, fMINIPORT_SEND_WAIT_WAKE);
}
NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql);
//
// Is wake-up enabled?
//
if (MINIPORT_PNP_TEST_FLAG(Miniport, fMINIPORT_SEND_WAIT_WAKE))
{
MINIPORT_PNP_CLEAR_FLAG(Miniport, fMINIPORT_SEND_WAIT_WAKE);
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("ndisSetPower: Miniport %p, Creating a wake irp for the device\n", Miniport));
//
// reuquest a power irp for wake notification
//
PowerState.SystemState = Miniport->WaitWakeSystemState;
Status = PoRequestPowerIrp(Miniport->PhysicalDeviceObject,
IRP_MN_WAIT_WAKE,
PowerState,
ndisWaitWakeComplete,
Miniport,
&Miniport->pIrpWaitWake);
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("ndisSetPower: Miniport %p, WaiteWakeIrp %p\n",
Miniport, Miniport->pIrpWaitWake));
}
}
//
// disable the interface
//
if (Miniport->SymbolicLinkName.Buffer != NULL)
{
IoSetDeviceInterfaceState(&Miniport->SymbolicLinkName, FALSE);
}
//
// Set the miniport device state.
//
NdisStatus = ndisQuerySetMiniportDeviceState(Miniport,
DeviceState,
OID_PNP_SET_POWER,
TRUE);
if (NDIS_STATUS_SUCCESS != NdisStatus)
{
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_ERR,
("ndisSetPower: Miniport %p, Failed to power the device down\n", Miniport));
if (Miniport->SymbolicLinkName.Buffer != NULL)
{
IoSetDeviceInterfaceState(&Miniport->SymbolicLinkName, TRUE);
}
pirp->IoStatus.Status = NdisStatus;
PoStartNextPowerIrp(pirp);
IoCompleteRequest(pirp, 0);
Status = NdisStatus;
break;
}
//
// Cancel the wake-up timer.
//
NdisCancelTimer(&Miniport->WakeUpDpcTimer, &Canceled);
if (!Canceled)
{
NdisStallExecution(NDIS_MINIPORT_WAKEUP_TIMEOUT * 1000);
}
}
else
{
if ((Miniport->DriverHandle->Flags & fMINIBLOCK_INTERMEDIATE_DRIVER) == 0)
{
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("ndisSetPower: Miniport %p, Halt the miniport\n", Miniport));
if (!MINIPORT_PNP_TEST_FLAG(Miniport, fMINIPORT_DEVICE_FAILED))
{
//
// Halt the legacy miniport.
//
ndisPmHaltMiniport(Miniport);
}
}
}
}
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("ndisSetPower: Miniport %p, Notify the bus driver of the low power state\n", Miniport));
//
// We need to pass this IRP down to the pdo so that
// it can power down.
//
IoCopyCurrentIrpStackLocationToNext(pirp);
IoSetCompletionRoutine(pirp,
ndisSetDevicePowerDownComplete,
Miniport,
TRUE,
TRUE,
TRUE);
IoMarkIrpPending(pirp);
PoStartNextPowerIrp(pirp);
PoCallDriver(Miniport->NextDeviceObject, pirp);
Status = STATUS_PENDING;
break;
}
//
// Done with processing the device set power state.
//
break;
}
PnPDereferencePackage();
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("<==ndisSetPower: Miniport %p, Status %lx\n", Miniport, Status));
return(Status);
}
NTSTATUS
ndisPowerDispatch(
IN PDEVICE_OBJECT pDeviceObject,
IN PIRP pirp
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PIO_STACK_LOCATION pirpSp;
NTSTATUS Status;
PNDIS_MINIPORT_BLOCK Miniport;
PDEVICE_OBJECT NextDeviceObject;
PIO_STACK_LOCATION pirpSpN;
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("==>ndisPowerDispatch: DeviceObject %p, Irp %p\n", pDeviceObject, pirp));
PnPReferencePackage();
//
// Get a pointer to the adapter block and miniport block then determine
// which one we should use.
//
Miniport = (PNDIS_MINIPORT_BLOCK)((PNDIS_WRAPPER_CONTEXT)pDeviceObject->DeviceExtension + 1);
if (Miniport->Signature != (PVOID)MINIPORT_DEVICE_MAGIC_VALUE)
{
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("ndisPowerDispatch: DeviceObject %p, Irp %p, Device extension is not a miniport.\n", pDeviceObject, pirp));
//
// Fail the invalid request.
//
pirp->IoStatus.Status = Status = STATUS_INVALID_DEVICE_REQUEST;
PoStartNextPowerIrp(pirp);
IoCompleteRequest(pirp, 0);
goto Done;
}
//
// Get a pointer to the next DeviceObject.
//
NextDeviceObject = Miniport->NextDeviceObject;
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("ndisPowerDispatch: Miniport %p\n", Miniport));
//
// Get the stack parameters for this IRP.
//
pirpSp = IoGetCurrentIrpStackLocation(pirp);
switch (pirpSp->MinorFunction)
{
//
// power management stuff
//
case IRP_MN_POWER_SEQUENCE:
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("ndisPowerDispatch: Miniport %p, Processing IRP_MN_POWER_SEQUENCE\n", Miniport));
PoStartNextPowerIrp(pirp);
//
// Generic routine that will pass the IRP to the next device
// object in the layer that wants to process it.
//
IoCopyCurrentIrpStackLocationToNext(pirp);
Status = ndisPassIrpDownTheStack(pirp, NextDeviceObject);
pirp->IoStatus.Status = Status;
IoCompleteRequest(pirp, 0);
break;
case IRP_MN_WAIT_WAKE:
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("ndisPowerDispatch: Miniport %p, Processing IRP_MN_WAIT_WAKE\n", Miniport));
//
// Fill in the wake information.
//
pirpSp->Parameters.WaitWake.PowerState = Miniport->WaitWakeSystemState;
IoCopyCurrentIrpStackLocationToNext(pirp);
PoStartNextPowerIrp(pirp);
Status = PoCallDriver(NextDeviceObject, pirp);
break;
case IRP_MN_QUERY_POWER:
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("ndisPowerDispatch: Miniport %p, Processing IRP_MN_QUERY_POWER\n", Miniport));
Status = ndisQueryPower(pirp, pirpSp, Miniport);
break;
case IRP_MN_SET_POWER:
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("ndisPowerDispatch: Miniport %p, Processing IRP_MN_SET_POWER\n", Miniport));
Status = ndisSetPower(pirp, pirpSp, Miniport);
break;
default:
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("ndisPowerDispatch: Miniport %p, Processing minor function: %lx\n",
Miniport, pirpSp->MinorFunction));
//
// send the IRP down
//
PoStartNextPowerIrp(pirp);
IoSkipCurrentIrpStackLocation(pirp);
Status = PoCallDriver(NextDeviceObject, pirp);
break;
}
Done:
PnPDereferencePackage();
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("<==ndisPowerDispatch: Miniport %p, Status 0x%x\n", Miniport, Status));
return(Status);
}
NTSTATUS
FASTCALL
ndisMShutdownMiniport(
IN PNDIS_MINIPORT_BLOCK Miniport
)
/*++
Routine Description:
The "shutdown handler" for the SHUTDOWN Irp. Will call the Ndis
shutdown routine, if one is registered.
Arguments:
DeviceObject - The adapter's device object.
Irp - The IRP.
Return Value:
Always STATUS_SUCCESS.
--*/
{
PDEVICE_OBJECT DeviceObject = Miniport->DeviceObject;
PNDIS_WRAPPER_CONTEXT WrapperContext = (PNDIS_WRAPPER_CONTEXT)DeviceObject->DeviceExtension;
KIRQL OldIrql;
DBGPRINT_RAW(DBG_COMP_INIT, DBG_LEVEL_INFO,
("==>ndisMShutdownMiniport: Miniport %p\n", Miniport));
NDIS_ACQUIRE_MINIPORT_SPIN_LOCK(Miniport, &OldIrql);
//
// Mark the miniport as halting and NOT using normal interrupts.
//
MINIPORT_SET_FLAG(Miniport, fMINIPORT_PM_HALTING);
MINIPORT_PNP_SET_FLAG(Miniport, fMINIPORT_SHUTTING_DOWN);
MINIPORT_CLEAR_FLAG(Miniport, fMINIPORT_NORMAL_INTERRUPTS);
NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql);
if ((WrapperContext->ShutdownHandler != NULL) &&
(MINIPORT_PNP_TEST_FLAG(Miniport, fMINIPORT_NO_SHUTDOWN) == 0))
{
//
// Call the shutdown routine.
//
if (WrapperContext->ShutdownHandler != NULL)
{
WrapperContext->ShutdownHandler(WrapperContext->ShutdownContext);
MINIPORT_PNP_SET_FLAG(Miniport, fMINIPORT_SHUT_DOWN);
}
}
DBGPRINT_RAW(DBG_COMP_INIT, DBG_LEVEL_INFO,
("<==ndisMShutdownMiniport: Miniport %p\n", Miniport));
return STATUS_SUCCESS;
}
NTSTATUS
ndisMPowerPolicy(
IN PNDIS_MINIPORT_BLOCK Miniport,
IN SYSTEM_POWER_STATE SystemState,
IN PDEVICE_POWER_STATE pDeviceState,
IN BOOLEAN fIsQuery
)
/*++
Routine Description:
This routine will determine if the miniport should go to the given device state.
Arguments:
Miniport - Pointer to the miniport block
SystemState - State the system wants to go to.
Return Value:
--*/
{
DEVICE_POWER_STATE DeviceStateForSystemState, MinDeviceWakeup = PowerDeviceUnspecified;
NTSTATUS Status = STATUS_SUCCESS;
DEVICE_POWER_STATE NewDeviceState = PowerDeviceD3;
PNDIS_PM_WAKE_UP_CAPABILITIES pWakeCaps;
NDIS_STATUS NdisStatus;
ULONG WakeEnable;
PIRP pIrpWaitWake;
KIRQL OldIrql;
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("==>ndisMPowerPolicy: Miniport %p, SystemState %lx\n", Miniport, SystemState));
if (SystemState >= PowerSystemShutdown)
{
//
// if this is a shutdown request, set device to D3 and return
//
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("ndisMPowerPolicy: Miniport %p, shutting down\n", Miniport));
*pDeviceState = PowerDeviceD3;
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("<==ndisMPowerPolicy: Miniport %p\n", Miniport));
return(STATUS_SUCCESS);
}
//
// If the system wants to transition to working then we are going to D0.
//
if (SystemState == PowerSystemWorking)
{
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("ndisMPowerPolicy: Miniport %p, Wakeing up the device\n", Miniport));
if (!fIsQuery)
{
MINIPORT_PNP_CLEAR_FLAG(Miniport, fMINIPORT_SYSTEM_SLEEPING);
}
*pDeviceState = PowerDeviceD0;
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("<==ndisMPowerPolicy: Miniport %p\n", Miniport));
return(STATUS_SUCCESS);
}
if (!fIsQuery)
{
//
// tag the miniport so when we get the device power IRP, we
// know we have already been here, taken care of protocols, etc.
//
MINIPORT_PNP_SET_FLAG(Miniport, fMINIPORT_SYSTEM_SLEEPING);
}
//
// if this is a legacy miniport or power-disabled miniport then throw it in D3
// do the same thing for IM miniports that have not been initialized yet
//
if ((!MINIPORT_PNP_TEST_FLAG(Miniport, fMINIPORT_DEVICE_POWER_ENABLE)) ||
(!(ndisIsMiniportStarted(Miniport) && (Miniport->PnPDeviceState == NdisPnPDeviceStarted))))
{
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("ndisMPowerPolicy: Miniport %p, Place legacy or PM disabled device in D3\n", Miniport));
*pDeviceState = PowerDeviceD3;
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("<==ndisMPowerPolicy: Miniport %p\n", Miniport));
return(STATUS_SUCCESS);
}
//
// First check for the case where the netcard is already asleep due to a
// media disconnect.
//
NDIS_ACQUIRE_MINIPORT_SPIN_LOCK(Miniport, &OldIrql);
pIrpWaitWake = Miniport->pIrpWaitWake;
NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql);
if ((Miniport->CurrentDevicePowerState > PowerDeviceD0) &&
(pIrpWaitWake != NULL))
{
///
// Miniport is in a lower power state than D0 and there is a wake irp pending in
// the bus driver. This is a pretty good indication that the cable was pulled.
// We are not going to enable any wake-up method seeing as the cable has been disconnect.
// but if the user does not want to wakeup the machine as a result of a cable
// reconnect, cancel any pending wait-wake IRP
///
if (!fIsQuery && ((!MINIPORT_PNP_TEST_FLAG (Miniport, fMINIPORT_DEVICE_POWER_WAKE_ENABLE)) ||
(Miniport->PnPCapabilities & NDIS_DEVICE_DISABLE_WAKE_ON_RECONNECT)))
{
if (IoCancelIrp(pIrpWaitWake))
{
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("ndisMPowerPolicy: Miniport %p, Successfully canceled media connect wake irp\n", Miniport));
}
}
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("<==ndisMPowerPolicy: Miniport %p\n", Miniport));
return(STATUS_DEVICE_POWERED_OFF);
}
do
{
//
// Is system wake-up enabled in the policy?
// if wake-up is not enabled then we simply power off.
//
if (!MINIPORT_PNP_TEST_FLAG(Miniport, fMINIPORT_DEVICE_POWER_WAKE_ENABLE))
{
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("ndisMPowerPolicy: Miniport %p, Device power wake is not enabled (%u)\n",
Miniport, MINIPORT_PNP_TEST_FLAG(Miniport, fMINIPORT_DEVICE_POWER_WAKE_ENABLE)));
break;
}
//
// This is the -lightest- state the device can go to for the requested
// system state.
//
DeviceStateForSystemState = Miniport->DeviceCaps.DeviceState[SystemState];
//
// Check to see if we are going below SystemSleeping3
//
//
//
// if we are going to S4 or deeper and device can not wake up the system from that state
// just do it
//
if ((SystemState >= PowerSystemHibernate) &&
((SystemState > Miniport->DeviceCaps.SystemWake) || (DeviceStateForSystemState > Miniport->DeviceCaps.DeviceWake)))
{
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("ndisMPowerPolicy: Miniport %p, System is either entering hibernate or shutting down.\n", Miniport));
//
// We succeed this call.
//
break;
}
//
// Get a nice pointer to the wake-up capabilities.
//
pWakeCaps = &Miniport->PMCapabilities.WakeUpCapabilities;
if ((NDIS_PNP_WAKE_UP_MAGIC_PACKET == (Miniport->WakeUpEnable & NDIS_PNP_WAKE_UP_MAGIC_PACKET)) &&
(PowerDeviceUnspecified != pWakeCaps->MinMagicPacketWakeUp))
{
MinDeviceWakeup = pWakeCaps->MinMagicPacketWakeUp;
}
if ((NDIS_PNP_WAKE_UP_PATTERN_MATCH == (Miniport->WakeUpEnable & NDIS_PNP_WAKE_UP_PATTERN_MATCH)) &&
(PowerDeviceUnspecified != pWakeCaps->MinPatternWakeUp))
{
if ((MinDeviceWakeup == PowerDeviceUnspecified) ||
(MinDeviceWakeup > pWakeCaps->MinPatternWakeUp))
{
MinDeviceWakeup = pWakeCaps->MinPatternWakeUp;
}
}
//
// if both MagicPacket and pattern match are NOT enabled (or the system can't do either)
// then we may as well go to D3.
//
if (MinDeviceWakeup == PowerDeviceUnspecified)
{
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("ndisMPowerPolicy: Miniport %p, MagicPacket and pattern match are not enabled.\n", Miniport));
break;
}
//
// from this point on, we try to go to power state that we can wake up the system from
//
//
// make sure we don't go too deep
//
if (MinDeviceWakeup > Miniport->DeviceCaps.DeviceWake)
{
MinDeviceWakeup = Miniport->DeviceCaps.DeviceWake;
}
//
// If the system state requested is lower than the minimum required to wake up the system
// or the corresponding device state is deeper than the lowest device state to wake
// up the system then we
// fail this call. Note that we also set the device state to D3. Since
// we are not going to be able to support wake-up then we power off.
// The query power will look at the failure code and return that to the
// system. The set power will ignore the failure code and set the device
// into D3.
//
if ((SystemState > Miniport->DeviceCaps.SystemWake) ||
(DeviceStateForSystemState > MinDeviceWakeup) ||
(DeviceStateForSystemState == PowerDeviceUnspecified))
{
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_ERR,
("ndisMPowerPolicy: Miniport %p, Requested system state is lower than the minimum wake-up system state\n", Miniport));
Status = STATUS_UNSUCCESSFUL;
break;
}
//
// starting from DeviceWake and up to DeviceState[SystemState], find a
// suitable device state
//
switch (MinDeviceWakeup)
{
case PowerDeviceD3:
if (Miniport->DeviceCaps.WakeFromD3)
{
NewDeviceState = PowerDeviceD3;
break;
}
case PowerDeviceD2:
if (Miniport->DeviceCaps.DeviceD2 && Miniport->DeviceCaps.WakeFromD2)
{
NewDeviceState = PowerDeviceD2;
break;
}
case PowerDeviceD1:
if (Miniport->DeviceCaps.DeviceD1 && Miniport->DeviceCaps.WakeFromD1)
{
NewDeviceState = PowerDeviceD1;
break;
}
case PowerDeviceD0:
if (Miniport->DeviceCaps.WakeFromD0)
{
NewDeviceState = PowerDeviceD0;
break;
}
default:
Status = STATUS_UNSUCCESSFUL;
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_ERR,
("ndisMPowerPolicy: Miniport %p, couldn't find any wake-able DeviceState 0x%x\n", Miniport));
break;
}
//
// ok, we started with deepest state (based on what device said can do)
// and went up. make sure we didn't go too far up. i.e. the statem state
// we are going to can maintain the device in desired power state
//
if ((Status == NDIS_STATUS_SUCCESS) &&
(DeviceStateForSystemState > NewDeviceState))
{
Status = STATUS_UNSUCCESSFUL;
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_ERR,
("ndisMPowerPolicy: Miniport %p, couldn't find any wake-able DeviceState 0x%x\n", Miniport));
}
//
// If this is for the set power then we need to enable wake-up on the miniport.
//
if (!fIsQuery)
{
//
// We need to send a request to the miniport to enable the correct wake-up types NOT
// including the link change.
//
WakeEnable = Miniport->WakeUpEnable & ~NDIS_PNP_WAKE_UP_LINK_CHANGE;
if (Miniport->PnPCapabilities & NDIS_DEVICE_DISABLE_WAKE_ON_PATTERN_MATCH)
{
WakeEnable &= ~NDIS_PNP_WAKE_UP_PATTERN_MATCH;
}
if (Miniport->PnPCapabilities & NDIS_DEVICE_DISABLE_WAKE_ON_MAGIC_PACKET)
{
WakeEnable &= ~NDIS_PNP_WAKE_UP_MAGIC_PACKET;
}
NdisStatus = ndisQuerySetMiniportDeviceState(Miniport,
WakeEnable,
OID_PNP_ENABLE_WAKE_UP,
TRUE);
if (NDIS_STATUS_SUCCESS == NdisStatus)
{
MINIPORT_PNP_SET_FLAG(Miniport, fMINIPORT_SEND_WAIT_WAKE);
}
else
{
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_ERR,
("ndisMPowerPolicy: Miniport %p, Unable to enable the following wake-up methods 0x%x\n", Miniport, WakeEnable));
//
// Since we can't enable the wake methods we may as well go to D3.
//
NewDeviceState = PowerDeviceD3;
break;
}
}
//
// Save the device state that we should go to.
//
*pDeviceState = NewDeviceState;
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("ndisMPowerPolicy: Miniport %p, SystemState 0x%x, DeviceState 0x%x\n", Miniport, SystemState, *pDeviceState));
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("<==ndisMPowerPolicy: Miniport %p\n", Miniport));
return(Status);
} while (FALSE);
//
// If this is not a query then we need to cancel wake-up on the miniport.
//
if (!fIsQuery && MINIPORT_PNP_TEST_FLAG(Miniport, fMINIPORT_DEVICE_POWER_WAKE_ENABLE))
{
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("ndisMPowerPolicy: Miniport %p, Disabling wake-up on the miniport\n", Miniport));
WakeEnable = 0;
ndisQuerySetMiniportDeviceState(Miniport,
WakeEnable,
OID_PNP_ENABLE_WAKE_UP,
TRUE);
}
//
// Save the device state that we should go to.
//
*pDeviceState = NewDeviceState;
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("ndisMPowerPolicy: Miniport %p, SystemState 0x%x, DeviceState 0x%x\n", Miniport, SystemState, *pDeviceState));
DBGPRINT_RAW(DBG_COMP_PM, DBG_LEVEL_INFO,
("<==ndisMPowerPolicy: Miniport %p\n", Miniport));
return(Status);
}