📄 usbfx2lk_selsusp.cpp
字号:
}
}
} else {
//
// If SS is not initialized, we should NEVER
// be called here and not at D0. If so, then
// the caller either didn't check the PnP state
// before calling here or the PnP state machine
// is broken.
//
ASSERT(DevExt->DevicePowerState == PowerDeviceD0);
}
KeReleaseSpinLock(&DevExt->SSLock,oldIrql);
#ifndef W2K
//
// If we got an idle IRP, we're going to need to
// cancel it
//
if (idleIrp) {
//
// Cancel the IRP
//
IoCancelIrp(idleIrp);
//
// Wait for the IRP to finish
//
(VOID)OsrWaitForSingleObject(&DevExt->SSIdleCompletionRoutineCalled);
//
// We got the IRP, so we free it.
//
IoFreeIrp(idleIrp);
}
#endif
if (queuePowerUp) {
//
// We need to power the device ourselves
//
SSPowerUpDeviceAsync(DevExt);
}
//
// If we queued the power up IRP or there
// was already a power up queued, indicate
// that the device is suspended by returning
// TRUE
//
return (queuePowerUp || powerUpQueued);
}
///////////////////////////////////////////////////////////////////////////////
//
// SSSubmissionThreadRoutine
//
// This thread runs while the device is attached to the system. Its goal
// in life is to wake up when the device no longer has any active I/O
// and attempt to suspend it
//
// INPUTS:
//
// Context - One of our device extensions
//
// OUTPUTS:
//
// None
//
// RETURNS:
//
// None
//
// IRQL:
//
// IRQL == PASSIVE_LEVEL
//
// CONTEXT:
//
// System context
//
// NOTES:
//
///////////////////////////////////////////////////////////////////////////////
VOID SSSubmissionThreadRoutine(PVOID Context)
{
PUSBFX2LK_EXT devExt = (PUSBFX2LK_EXT)Context;
NTSTATUS status;
PVOID waitObjects[2];
LARGE_INTEGER delay;
LARGE_INTEGER currentTime;
LARGE_INTEGER timeSinceIdle;
KIRQL oldIrql;
BOOLEAN powerDeviceBackUp;
BOOLEAN restartQueues;
OsrTracePrint(TRACE_LEVEL_VERBOSE,OSRDBG_SELECTIVE_SUSPEND,
("SSSubmissionThreadRoutine: Entered.\n"));
//
// Define an arbitrary thread delay that we'll add to
// our loop.
//
delay.QuadPart = RELATIVE(SECONDS(1));
//
// Wait on the STOP event, which is signaled when the oustanding
// I/O count on the device drops to 1 and also the
// thread termination event,
//
waitObjects[0] = &devExt->StopEvent;
waitObjects[1] = &devExt->SSSubmissionThreadTerminateEvent;
//
// Loop forever...
//
while (TRUE) {
//
// Wait on our above two events.
//
// We don't use OsrWaitForMultipleObjects here
// because we expect to be just sit here for a
// loooong time and the otherwise helpful debug output
// produced by OsrWaitForMultipleObjects just becomes
// noise in this case
//
status = KeWaitForMultipleObjects(2,
waitObjects,
WaitAny,
Executive,
KernelMode,
FALSE,
NULL,
NULL);
//
// We expect a wait status indicating that either
// the stop event was fired OR that the
// terminate thread event was fired. Anything else is an
// error.
//
if (status != STATUS_WAIT_0) {
OsrTracePrint(TRACE_LEVEL_INFORMATION,OSRDBG_SELECTIVE_SUSPEND,
("EnableSelectiveSuspend: KeWaitForMultipleObjects returned "\
"0x%x (%s). Exiting...\n",
status,OsrNtStatusToString(status)));
PsTerminateSystemThread(status);
return;
}
//
// Default to not needing to power the device up
// or restarting the queues, let the logic below
// change this as necessary.
//
powerDeviceBackUp = FALSE;
restartQueues = FALSE;
//
// Grab the current time. We'll use this to determine if
// the device has been idle long enough.
//
KeQuerySystemTime(¤tTime);
//
// We need to try to suspend the device
//
KeAcquireSpinLock(&devExt->SSLock,&oldIrql);
//
// If the device can suspend right now AND
// we haven't been suspended, we'll do the power
// down.
//
// Not that while the device is suspended, the stop
// event stays fired so we'll keep coming back here
// every "delay" seconds and check it. This isn't an
// optimal solution, as it requires polling even if
// the device is already idle or SS is disabled by the
// user, but it does provide for a fairly straightforward
// SS implementation. SS is very difficult to get correct,
// so we have chosen to not attempt to optimize and provide
// something that is safe instead. Whether or not this is
// acceptable to your application is a design decision
// that you will have to make.
//
if (devExt->SSDeviceCanNowSuspend &&
devExt->SSState == SS_NOT_STARTED) {
//
// Calculate how long it has been since the
// device became idle
//
timeSinceIdle.QuadPart =
currentTime.QuadPart -
devExt->SSTimeOfLastActivity.QuadPart;
//
// Is the difference sufficient to constitute an idle
// device?
//
if (timeSinceIdle.QuadPart >= SS_DEVICE_IDLE_DELAY) {
//
// We're about to power down the device, set the state
// to SS_PROCESSING
//
devExt->SSState = SS_PROCESSING;
//
// Clear the "device is out of selective suspend" event
//
KeClearEvent(&devExt->SSDeviceNotSuspendedEvent);
//
// We don't need this again until the device
// has suspended. The fact that we have
// set the state to SS_PROCESSING will
// cause any arriving operations that need the
// device fully powered to queue (we'll deal
// with getting them started again once we've processed
// the power down).
//
// Note that we're being overly cautious and a bit
// obtuse with our selective suspend algorithm here.
// If an operation comes in at this point we don't
// try to cancel the in progress power down. Instead,
// we wait for the device to power all the way down
// and then we'll power it back up again before restarting
// the queues. This is a safe approach (as it eliminates
// many race conditions) but it may not be ideal for
// your environment.
//
KeReleaseSpinLock(&devExt->SSLock,oldIrql);
//
// Synchronously suspend the device.
//
#ifndef W2K
//
// If we're not built for Windows 2000 compatibility,
// we can't just power the device down, we need to let
// the USB hus driver know about it first. In this case
// we'll call SSSuspendDeviceWithCallback which will
// send the appropriate IRP and power down from within
// the callback
//
SSSuspendDeviceWithCallback(devExt);
#else
//
// Otherwise, there is no concept of notifiying the bus
// driver first. So, just power the device down inline
//
SSSuspendDevice(devExt);
#endif
//
// Acquire our lock again and see if the state of the
// world has changed. If not, then we'll set our
// SS state to be SUSPENDED. If it HAS changed, then
// we need to power up the device again
//
KeAcquireSpinLock(&devExt->SSLock,&oldIrql);
//
// Make sure that the power down actually succeeded.
// If not, then there's nothing to be done
//
if (devExt->DevicePowerState != devExt->PowerDownLevel) {
OsrTracePrint(TRACE_LEVEL_ERROR,OSRDBG_SELECTIVE_SUSPEND,
("SSSubmissionThreadRoutine: Power down of device did"\
" not succeed. Device power state is 0x%x\n",
devExt->DevicePowerState));
devExt->SSState = SS_NOT_STARTED;
//
// Set the "device is now out of selective suspend" event
//
KeSetEvent(&devExt->SSDeviceNotSuspendedEvent,
EVENT_INCREMENT,
FALSE);
//
// Restart the queues that were stalled
// as part of the powering down
//
restartQueues = TRUE;
} else {
//
// Is everyone still OK with us suspending the device?
//
if (devExt->SSDeviceCanNowSuspend == TRUE) {
//
// Yup, no one wants the device just yet sp
// the Device is now suspended!
//
devExt->SSState = SS_SUSPENDED;
} else {
//
// Whoops. While we were powering down, a request
// came in that required access to the device.
// Power back up again once we've dropped the lock
//
powerDeviceBackUp = TRUE;
}
}
//
// Lock acquisition above isn't leaked, it's released by
// the upcoming KeRelease outside of the original IF
//
}
}
KeReleaseSpinLock(&devExt->SSLock,oldIrql);
if (powerDeviceBackUp) {
//
// While we were powering the device down,
// a request came in that needed the device
// to be powered. Power the device back up
// here.
//
//
// We never leave the "selective suspend
// processing" state in this case, which
// allows us to queue all of those operations
// that requested the power up.
//
// The power up code deals with getting the
// queues restarted by calling OsrProcessQueuedRequests
//
ASSERT(devExt->SSState == SS_PROCESSING);
SSPowerUpDevice(devExt);
}
if (restartQueues) {
//
// The suspend failed for whatever reason and the
// queues need to be restarted
//
OsrProcessQueuedRequests(devExt);
}
//
// Put a bit of a delay in. Because the stop
// event is a notification event, we'll keep
// hitting this loop while suspended so
// the delay just leaves some breathing room.
//
KeDelayExecutionThread(KernelMode,
FALSE,
&delay);
}
return;
}
///////////////////////////////////////////////////////////////////////////////
//
// SSPowerUpDevice
//
// This routine synchronously powers up the device
//
// INPUTS:
//
// DevExt - One of our device extensions
//
// OUTPUTS:
//
// None
//
// RETURNS:
//
// None
//
// IRQL:
//
// IRQL == PASSIVE_LEVEL
//
// CONTEXT:
//
// Arbitrary
//
// NOTES:
//
//
///////////////////////////////////////////////////////////////////////////////
VOID SSPowerUpDevice(PUSBFX2LK_EXT DevExt)
{
POWER_STATE devicePowerState;
KEVENT powerUpCompleteEvent;
NTSTATUS status;
OsrTracePrint(TRACE_LEVEL_VERBOSE,OSRDBG_SELECTIVE_SUSPEND,
("SSPowerUpDevice: Entered\n"));
//
// We should not already be in D0 at this point
//
ASSERT(DevExt->DevicePowerState != PowerDeviceD0);
//
// Submit a device power IRP to our device
//
//
// This constitutes a new I/O on the device,
// so bump the oustanding I/O count
//
OsrIncrementOutstandingIoCount(DevExt,__FILE__,__LINE__);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -