/*++ 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 #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); }