/*++ Copyright (C) Microsoft Corporation, 1999 - 2001 Module Name: power.c Abstract: Environment: kernel mode only Notes: Revision History: --*/ #include #include "stddef.h" #include "wdm.h" #include "usbscan.h" #include "usbd_api.h" #include "private.h" #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, USPower) #endif NTSTATUS USPower( IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp ) /*++ Routine Description: Process the Power IRPs sent to the PDO for this device. Arguments: pDeviceObject - pointer to the functional device object (FDO) for this device. pIrp - pointer to an I/O Request Packet Return Value: NT status code --*/ { NTSTATUS Status; PUSBSCAN_DEVICE_EXTENSION pde; PIO_STACK_LOCATION pIrpStack; BOOLEAN hookIt = FALSE; POWER_STATE powerState; PAGED_CODE(); DebugTrace(TRACE_PROC_ENTER,("USPower: Enter... \n")); USIncrementIoCount(pDeviceObject); pde = (PUSBSCAN_DEVICE_EXTENSION)pDeviceObject -> DeviceExtension; pIrpStack = IoGetCurrentIrpStackLocation( pIrp ); Status = STATUS_SUCCESS; switch (pIrpStack -> MinorFunction) { case IRP_MN_SET_POWER: { DebugTrace(TRACE_STATUS,("USPower: IRP_MN_SET_POWER\n")); switch (pIrpStack -> Parameters.Power.Type) { case SystemPowerState: { DebugTrace(TRACE_STATUS,("USPower: SystemPowerState (0x%x)\n",pIrpStack->Parameters.Power.State.SystemState)); // // Let lower layer know S IRP, we'll catch on the way up. // IoMarkIrpPending(pIrp); IoCopyCurrentIrpStackLocationToNext(pIrp); IoSetCompletionRoutine(pIrp, USSystemPowerIrpComplete, // always pass FDO to completion routine pDeviceObject, TRUE, TRUE, TRUE); PoCallDriver(pde ->pStackDeviceObject, pIrp); Status = STATUS_PENDING; goto USPower_return; } // case SystemPowerState: case DevicePowerState: { DebugTrace(TRACE_STATUS,("USPower: DevicePowerState\n")); Status = USSetDevicePowerState(pDeviceObject, pIrpStack -> Parameters.Power.State.DeviceState, &hookIt); if (hookIt) { DebugTrace(TRACE_STATUS,("USPower: Set PowerIrp Completion Routine\n")); IoCopyCurrentIrpStackLocationToNext(pIrp); IoSetCompletionRoutine(pIrp, USDevicePowerIrpComplete, // always pass FDO to completion routine pDeviceObject, hookIt, hookIt, hookIt); } else { PoStartNextPowerIrp(pIrp); IoSkipCurrentIrpStackLocation(pIrp); } Status = PoCallDriver(pde ->pStackDeviceObject, pIrp); if (!hookIt) { USDecrementIoCount(pDeviceObject); } goto USPower_return; } // case DevicePowerState: } /* case irpStack->Parameters.Power.Type */ break; /* IRP_MN_SET_POWER */ } // case IRP_MN_SET_POWER: case IRP_MN_QUERY_POWER: { DebugTrace(TRACE_STATUS,("USPower: IRP_MN_QUERY_POWER\n")); if(PowerDeviceD3 == pde -> DeviceCapabilities.DeviceState[pIrpStack->Parameters.Power.State.SystemState]){ // // We're going down to D3 state, which we can't wake from. Cancel WaitWakeIRP. // USDisarmWake(pde); } // if(PowerDeviceD3 == pde -> DeviceCapabilities.DeviceState[irpStack->Parameters.Power.State.SystemState]) PoStartNextPowerIrp(pIrp); IoSkipCurrentIrpStackLocation(pIrp); Status = PoCallDriver(pde -> pStackDeviceObject, pIrp); USDecrementIoCount(pDeviceObject); break; /* IRP_MN_QUERY_POWER */ } // case IRP_MN_QUERY_POWER: case IRP_MN_WAIT_WAKE: { LONG oldWakeState; DebugTrace(TRACE_STATUS,("USPower: IRP_MN_WAIT_WAKE\n")); pde->pWakeIrp = pIrp; // // Now we're armed. // oldWakeState = InterlockedCompareExchange(&pde->WakeState, WAKESTATE_ARMED, WAKESTATE_WAITING); if(WAKESTATE_WAITING_CANCELLED == oldWakeState){ // // We got disarmed, finish up and complete the IRP // pde->WakeState = WAKESTATE_COMPLETING; pIrp->IoStatus.Status = STATUS_CANCELLED; IoCompleteRequest(pIrp, IO_NO_INCREMENT ); Status = STATUS_CANCELLED; USDecrementIoCount(pDeviceObject); break; } // if(WAKESTATE_WAITING_CANCELLED == oldWakeState) // We went from WAITING to ARMED. Set a completion routine and forward // the IRP. Note that our completion routine might complete the IRP // asynchronously, so we mark the IRP pending IoMarkIrpPending(pIrp); IoCopyCurrentIrpStackLocationToNext(pIrp); IoSetCompletionRoutine(pIrp, USWaitWakeIoCompletionRoutine, NULL, TRUE, TRUE, TRUE ); PoCallDriver(pde->pStackDeviceObject, pIrp); Status = STATUS_PENDING; USDecrementIoCount(pDeviceObject); break; } // case IRP_MN_WAIT_WAKE: default: DebugTrace(TRACE_STATUS,("USPower: Unknown power message (%x)\n",pIrpStack->MinorFunction)); PoStartNextPowerIrp(pIrp); IoSkipCurrentIrpStackLocation(pIrp); Status = PoCallDriver(pde -> pStackDeviceObject, pIrp); USDecrementIoCount(pDeviceObject); } /* pIrpStack -> MinorFunction */ USPower_return: DebugTrace(TRACE_PROC_LEAVE,("USPower: Leaving... Status = 0x%x\n", Status)); return Status; } // USPower() NTSTATUS USPoRequestCompletion( IN PDEVICE_OBJECT pPdo, IN UCHAR MinorFunction, IN POWER_STATE PowerState, IN PDEVICE_OBJECT pDeviceObject, IN PIO_STATUS_BLOCK pIoStatus ) /*++ Routine Description: This routine is called when the port driver completes an IRP. Arguments: Return Value: The function value is the final status from the operation. --*/ { NTSTATUS Status; PUSBSCAN_DEVICE_EXTENSION pde; PIRP pIrp; DebugTrace(TRACE_PROC_ENTER,("USPoRequestCompletion: Enter...\n")); // // Initialize local. // pde = (PUSBSCAN_DEVICE_EXTENSION)pDeviceObject -> DeviceExtension; pIrp = pde -> pPowerIrp; Status = pIoStatus -> Status; // // Copy status from D IRP to S IRP. // pIrp->IoStatus.Status = pIoStatus->Status; // // Complete S IRP. // PoStartNextPowerIrp(pIrp); IoCompleteRequest(pIrp, IO_NO_INCREMENT ); USDecrementIoCount(pDeviceObject); DebugTrace(TRACE_PROC_LEAVE,("USPoRequestCompletion: Leaving... Status = 0x%x\n", Status)); return Status; } // USPoRequestCompletion() NTSTATUS USDevicePowerIrpComplete( IN PDEVICE_OBJECT pPdo, IN PIRP pIrp, IN PDEVICE_OBJECT pDeviceObject ) /*++ Routine Description: This routine is called when the port driver completes SetD0 IRP. Arguments: Return Value: The function value is the final status from the operation. --*/ { NTSTATUS Status; PUSBSCAN_DEVICE_EXTENSION pde; PIO_STACK_LOCATION pIrpStack; DebugTrace(TRACE_PROC_ENTER,("USDevicePowerIrpComplete: Enter...\n")); pde = (PUSBSCAN_DEVICE_EXTENSION)pDeviceObject -> DeviceExtension; Status = STATUS_SUCCESS; if (pIrp -> PendingReturned) { IoMarkIrpPending(pIrp); } // if (pIrp -> PendingReturned) pIrpStack = IoGetCurrentIrpStackLocation (pIrp); ASSERT(pIrpStack -> MajorFunction == IRP_MJ_POWER); ASSERT(pIrpStack -> MinorFunction == IRP_MN_SET_POWER); ASSERT(pIrpStack -> Parameters.Power.Type == DevicePowerState); ASSERT(pIrpStack -> Parameters.Power.State.DeviceState == PowerDeviceD0); // // This completion is called only for D0 IRP. // pde->CurrentDevicePowerState = PowerDeviceD0; pde->AcceptingRequests = TRUE; // // Now power is on. Rearm for wakeup. // USQueuePassiveLevelCallback(pde->pOwnDeviceObject, USPassiveLevelReArmCallbackWorker); // // Ready for next D IRP. // PoStartNextPowerIrp(pIrp); // // Leaving... // USDecrementIoCount(pDeviceObject); DebugTrace(TRACE_PROC_LEAVE,("USDevicePowerIrpComplete: Leaving... Status = 0x%x\n", Status)); return Status; } // USDevicePowerIrpComplete() NTSTATUS USSystemPowerIrpComplete( IN PDEVICE_OBJECT pPdo, IN PIRP pIrp, IN PDEVICE_OBJECT pDeviceObject ) /*++ Routine Description: This routine is called when the port driver completes SetD0 IRP. Arguments: Return Value: The function value is the final status from the operation. --*/ { NTSTATUS Status; PUSBSCAN_DEVICE_EXTENSION pde; PIO_STACK_LOCATION pIrpStack; POWER_STATE powerState; DebugTrace(TRACE_PROC_ENTER,("USSystemPowerIrpComplete: Enter... IRP(0x%p)\n", pIrp)); // // Initialize local. // pde = (PUSBSCAN_DEVICE_EXTENSION)pDeviceObject -> DeviceExtension; Status = pIrp->IoStatus.Status; pIrpStack = IoGetCurrentIrpStackLocation (pIrp); ASSERT(pIrpStack -> MajorFunction == IRP_MJ_POWER); ASSERT(pIrpStack -> MinorFunction == IRP_MN_SET_POWER); ASSERT(pIrpStack -> Parameters.Power.Type == SystemPowerState); if(!NT_SUCCESS(Status)){ DebugTrace(TRACE_STATUS,("USSystemPowerIrpComplete: IRP failed (0x%x).\n", Status)); Status = STATUS_SUCCESS; USDecrementIoCount(pDeviceObject); goto USSystemPowerIrpComplete_return; } // if(!NT_SUCCESS(Status)) // // Now Request D IRP based on what we got. // if(TRUE == pde ->bEnabledForWakeup){ DebugTrace(TRACE_STATUS,("USSystemPowerIrpComplete: We have remote wakeup support, getting powerState from table.\n")); // // We support wakeup, we'll just follow device stated set by PDO. // powerState.DeviceState = pde -> DeviceCapabilities.DeviceState[pIrpStack->Parameters.Power.State.SystemState]; } else { // if(TRUE == pde ->EnabledForWakeup) DebugTrace(TRACE_STATUS,("USSystemPowerIrpComplete: We don't have remote wakeup support.\n")); // // We don't support remote wake, we're in D0 only when PowerSystemWorking. // if(PowerSystemWorking == pIrpStack -> Parameters.Power.State.SystemState){ DebugTrace(TRACE_STATUS,("USSystemPowerIrpComplete: PowerSystemWorking is requested, powering up to D0.\n")); powerState.DeviceState = PowerDeviceD0; } else { // if(PowerSystemWorking == pIrpStack -> Parameters.Power.State.SystemState) DebugTrace(TRACE_STATUS,("USSystemPowerIrpComplete: Going other than PowerSystemWorking, turning off the device to D3.\n")); powerState.DeviceState = PowerDeviceD3; } } // else(TRUE == pde ->EnabledForWakeup) // // are we already in this state? // if(powerState.DeviceState != pde -> CurrentDevicePowerState){ // // No, request that we be put into this state // DebugTrace(TRACE_STATUS,("USSystemPowerIrpComplete: Requesting DevicePowerState(0x%x).\n", powerState.DeviceState)); pde -> pPowerIrp = pIrp; Status = PoRequestPowerIrp(pde -> pPhysicalDeviceObject, IRP_MN_SET_POWER, powerState, USPoRequestCompletion, pDeviceObject, NULL); if(NT_SUCCESS(Status)){ // // D IRP is successfully requested. S IRP will be completed in D IRP completion routine together. // Status = STATUS_MORE_PROCESSING_REQUIRED; } else { // if(NT_SUCCESS(Status)) DebugTrace(TRACE_WARNING,("USSystemPowerIrpComplete: WARNING!! DevicePowerState(0x%x) request failed..\n", powerState.DeviceState)); PoStartNextPowerIrp(pIrp); Status = STATUS_SUCCESS; USDecrementIoCount(pDeviceObject); } } else { // if(powerState.DeviceState != pde -> CurrentDevicePowerState) // // We're already in this device state, no need to issue D IRP. // PoStartNextPowerIrp(pIrp); Status = STATUS_SUCCESS; USDecrementIoCount(pDeviceObject); } // else(powerState.DeviceState != pde -> CurrentDevicePowerState) USSystemPowerIrpComplete_return: DebugTrace(TRACE_PROC_LEAVE,("USSystemPowerIrpComplete: Leaving... Status = 0x%x\n", Status)); return Status; } // USSystemPowerIrpComplete() NTSTATUS USSetDevicePowerState( IN PDEVICE_OBJECT pDeviceObject, IN DEVICE_POWER_STATE DeviceState, IN PBOOLEAN pHookIt ) /*++ Routine Description: Arguments: pDeviceObject - Pointer to the device object for the class device. DeviceState - Device specific power state to set the device in to. Return Value: --*/ { NTSTATUS Status; PUSBSCAN_DEVICE_EXTENSION pde; POWER_STATE PowerState; DebugTrace(TRACE_PROC_ENTER,("USSetDevicePowerState: Enter...\n")); pde = (PUSBSCAN_DEVICE_EXTENSION)pDeviceObject -> DeviceExtension; Status = STATUS_SUCCESS; switch (DeviceState){ case PowerDeviceD3: // ASSERT(pde -> AcceptingRequests); // pde -> AcceptingRequests = FALSE; // USCancelPipe(pDeviceObject, ALL_PIPE, TRUE); // pde -> CurrentDevicePowerState = DeviceState; // break; case PowerDeviceD1: case PowerDeviceD2: #if DBG if(PowerDeviceD3 == DeviceState){ DebugTrace(TRACE_STATUS,("USSetDevicePowerState: PowerDeviceD3 (OFF)\n")); } else { // if(PowerDeviceD3 == DeviceState) DebugTrace(TRACE_STATUS,("USSetDevicePowerState: PowerDeviceD1/D2 (SUSPEND)\n")); } // else(PowerDeviceD3 == DeviceState) #endif USCancelPipe(pDeviceObject, NULL, ALL_PIPE, TRUE); // // power states D1,D2 translate to USB suspend // D3 transltes to OFF pde -> CurrentDevicePowerState = DeviceState; break; case PowerDeviceD0: DebugTrace(TRACE_STATUS,("USSetDevicePowerState: PowerDeviceD0 (ON)\n")); // // finish the rest in the completion routine // *pHookIt = TRUE; // pass on to PDO break; default: DebugTrace(TRACE_WARNING,("USSetDevicePowerState: Bogus DeviceState = %x\n", DeviceState)); } // switch (DeviceState) DebugTrace(TRACE_PROC_LEAVE,("USSetDevicePowerState: Leaving... Status = 0x%x\n", Status)); return Status; } // USSetDevicePowerState() NTSTATUS USWaitWakeIoCompletionRoutine( PDEVICE_OBJECT pDeviceObject, PIRP pIrp, PVOID pContext ) { PUSBSCAN_DEVICE_EXTENSION pde; LONG oldWakeState; NTSTATUS Status; DebugTrace(TRACE_PROC_ENTER,("USWaitWakeIoCompletionRoutine: Enter...\n")); // // Initialize local. // pde = (PUSBSCAN_DEVICE_EXTENSION) pDeviceObject->DeviceExtension; oldWakeState = 0; Status = STATUS_SUCCESS; // Advance the state to completing oldWakeState = InterlockedExchange( &pde->WakeState, WAKESTATE_COMPLETING ); if(WAKESTATE_ARMED == oldWakeState){ // Normal case, IoCancelIrp isnft being called. Note that we already // marked the IRP pending in our dispatch routine Status = STATUS_SUCCESS; goto USWaitWakeIoCompletionRoutine_return; } else { // if(WAKESTATE_ARMED == oldWakeState) if(WAKESTATE_ARMING_CANCELLED != oldWakeState){ DebugTrace(TRACE_ERROR,("USWaitWakeIoCompletionRoutine: ERROR!! wake IRP is completed but oldState(0x%x) isn't ARMED/CALCELLED.", oldWakeState)); } else { // if(WAKESTATE_ARMING_CANCELLED != oldWakeState) DebugTrace(TRACE_STATUS,("USWaitWakeIoCompletionRoutine: WakeIRP is canceled.\n")); } // IoCancelIrp is being called RIGHT NOW. The disarm code will try // to put back the WAKESTATE_ARMED state. It will then see our // WAKESTATE_COMPLETED value, and complete the IRP itself! Status = STATUS_MORE_PROCESSING_REQUIRED; goto USWaitWakeIoCompletionRoutine_return; } // else(WAKESTATE_ARMED == oldWakeState) USWaitWakeIoCompletionRoutine_return: DebugTrace(TRACE_PROC_LEAVE,("USWaitWakeIoCompletionRoutine: Leaving... Status = 0x%x\n", Status)); return Status; } // USWaitWakeIoCompletionRoutine(