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