You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
452 lines
12 KiB
452 lines
12 KiB
/*++
|
|
|
|
Copyright (c) 2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
selSusp.c
|
|
|
|
Abstract:
|
|
|
|
This module contains code for a generic client driver that can be loaded
|
|
for all USB devices/child interfaces.
|
|
|
|
Author:
|
|
|
|
Environment:
|
|
|
|
kernel mode only
|
|
|
|
Notes:
|
|
|
|
Copyright (c) 2000 Microsoft Corporation.
|
|
All Rights Reserved.
|
|
|
|
--*/
|
|
|
|
#include "selSusp.h"
|
|
#include "sSPnP.h"
|
|
#include "sSPwr.h"
|
|
#include "sSUsr.h"
|
|
#include "sSDevCtr.h"
|
|
#include "sSWmi.h"
|
|
|
|
//
|
|
// Globals
|
|
//
|
|
|
|
GLOBALS Globals;
|
|
ULONG DebugLevel = 1;
|
|
|
|
NTSTATUS
|
|
DriverEntry(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PUNICODE_STRING UniRegistryPath
|
|
);
|
|
|
|
VOID
|
|
SS_DriverUnload(
|
|
IN PDRIVER_OBJECT DriverObject
|
|
);
|
|
|
|
NTSTATUS
|
|
SS_AddDevice(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PDEVICE_OBJECT PhysicalDeviceObject
|
|
);
|
|
|
|
#ifdef PAGE_CODE
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(INIT, DriverEntry)
|
|
#pragma alloc_text(PAGE, SS_DriverUnload)
|
|
#pragma alloc_text(PAGE, SS_DispatchCreate)
|
|
#pragma alloc_text(PAGE, SS_DispatchClose)
|
|
#endif
|
|
#endif
|
|
|
|
NTSTATUS
|
|
DriverEntry(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PUNICODE_STRING UniRegistryPath
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Installable driver initialization entry point.
|
|
This entry point is called directly by the I/O system.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - pointer to driver object
|
|
|
|
RegistryPath - pointer to a unicode string representing the path to driver
|
|
specific key in the registry.
|
|
|
|
Return Values:
|
|
|
|
NT status code
|
|
|
|
--*/
|
|
{
|
|
|
|
NTSTATUS ntStatus;
|
|
PUNICODE_STRING registryPath;
|
|
|
|
//
|
|
// initialization of variables
|
|
//
|
|
|
|
registryPath = &Globals.SSRegistryPath;
|
|
|
|
//
|
|
// Allocate pool to hold a null-terminated copy of the path.
|
|
// Safe in paged pool since all registry routines execute at
|
|
// PASSIVE_LEVEL.
|
|
//
|
|
|
|
registryPath->MaximumLength = UniRegistryPath->Length + sizeof(UNICODE_NULL);
|
|
registryPath->Length = UniRegistryPath->Length;
|
|
registryPath->Buffer = ExAllocatePool(PagedPool,
|
|
registryPath->MaximumLength);
|
|
|
|
if (!registryPath->Buffer) {
|
|
|
|
SSDbgPrint(1, ("Failed to allocate memory for registryPath\n"));
|
|
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto DriverEntry_Exit;
|
|
}
|
|
|
|
|
|
RtlZeroMemory (registryPath->Buffer,
|
|
registryPath->MaximumLength);
|
|
RtlMoveMemory (registryPath->Buffer,
|
|
UniRegistryPath->Buffer,
|
|
UniRegistryPath->Length);
|
|
|
|
ntStatus = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Initialize the driver object with this driver's entry points.
|
|
//
|
|
|
|
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = SS_DispatchDevCtrl;
|
|
DriverObject->MajorFunction[IRP_MJ_POWER] = SS_DispatchPower;
|
|
DriverObject->MajorFunction[IRP_MJ_PNP] = SS_DispatchPnP;
|
|
DriverObject->MajorFunction[IRP_MJ_CREATE] = SS_DispatchCreate;
|
|
DriverObject->MajorFunction[IRP_MJ_CLOSE] = SS_DispatchClose;
|
|
DriverObject->MajorFunction[IRP_MJ_CLEANUP] = SS_DispatchClean;
|
|
DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = SS_DispatchSysCtrl;
|
|
DriverObject->DriverUnload = SS_DriverUnload;
|
|
DriverObject->DriverExtension->AddDevice = (PDRIVER_ADD_DEVICE)
|
|
SS_AddDevice;
|
|
DriverEntry_Exit:
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
VOID
|
|
SS_DriverUnload(
|
|
IN PDRIVER_OBJECT DriverObject
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
This function will free the memory allocations in DriverEntry.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - pointer to driver object
|
|
|
|
Return:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PUNICODE_STRING registryPath;
|
|
|
|
SSDbgPrint(3, ("SS_DriverUnload - begins\n"));
|
|
|
|
registryPath = &Globals.SSRegistryPath;
|
|
|
|
if(registryPath->Buffer) {
|
|
|
|
ExFreePool(registryPath->Buffer);
|
|
registryPath->Buffer = NULL;
|
|
}
|
|
|
|
SSDbgPrint(3, ("SS_DriverUnload - ends\n"));
|
|
|
|
return;
|
|
}
|
|
|
|
NTSTATUS
|
|
SS_AddDevice(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PDEVICE_OBJECT PhysicalDeviceObject
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Arguments:
|
|
|
|
DriverObject - Store the pointer to the object representing us.
|
|
|
|
PhysicalDeviceObject - Pointer to the device object created by the
|
|
undelying bus driver.
|
|
|
|
Return:
|
|
|
|
STATUS_SUCCESS - if successful
|
|
STATUS_UNSUCCESSFUL - otherwise
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS ntStatus;
|
|
PDEVICE_OBJECT deviceObject;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
POWER_STATE state;
|
|
KIRQL oldIrql;
|
|
|
|
SSDbgPrint(3, ("SS_AddDevice - begins\n"));
|
|
|
|
deviceObject = NULL;
|
|
|
|
ntStatus = IoCreateDevice(
|
|
DriverObject, // our driver object
|
|
sizeof(DEVICE_EXTENSION), // extension size for us
|
|
NULL, // name for this device
|
|
FILE_DEVICE_UNKNOWN,
|
|
FILE_AUTOGENERATED_DEVICE_NAME, // device characteristics
|
|
FALSE, // Not exclusive
|
|
&deviceObject); // Our device object
|
|
|
|
if(!NT_SUCCESS(ntStatus)) {
|
|
//
|
|
// returning failure here prevents the entire stack from functioning,
|
|
// but most likely the rest of the stack will not be able to create
|
|
// device objects either, so it is still OK.
|
|
//
|
|
SSDbgPrint(1, ("Failed to create device object\n"));
|
|
return ntStatus;
|
|
}
|
|
|
|
//
|
|
// Initialize the device extension
|
|
//
|
|
|
|
deviceExtension = (PDEVICE_EXTENSION) deviceObject->DeviceExtension;
|
|
deviceExtension->FunctionalDeviceObject = deviceObject;
|
|
deviceExtension->PhysicalDeviceObject = PhysicalDeviceObject;
|
|
deviceObject->Flags |= DO_BUFFERED_IO;
|
|
|
|
//
|
|
// initialize the device state lock and set the device state
|
|
//
|
|
|
|
KeInitializeSpinLock(&deviceExtension->DevStateLock);
|
|
INITIALIZE_PNP_STATE(deviceExtension);
|
|
|
|
//
|
|
//initialize OpenHandleCount
|
|
//
|
|
deviceExtension->OpenHandleCount = 0;
|
|
|
|
//
|
|
// Initialize the selective suspend variables
|
|
//
|
|
KeInitializeSpinLock(&deviceExtension->IdleReqStateLock);
|
|
deviceExtension->IdleReqPend = 0;
|
|
deviceExtension->PendingIdleIrp = NULL;
|
|
|
|
//
|
|
// Hold requests until the device is started
|
|
//
|
|
|
|
deviceExtension->QueueState = HoldRequests;
|
|
|
|
//
|
|
// Initialize the queue and the queue spin lock
|
|
//
|
|
|
|
InitializeListHead(&deviceExtension->NewRequestsQueue);
|
|
KeInitializeSpinLock(&deviceExtension->QueueLock);
|
|
|
|
//
|
|
// Initialize the remove event to not-signaled.
|
|
//
|
|
|
|
KeInitializeEvent(&deviceExtension->RemoveEvent,
|
|
SynchronizationEvent,
|
|
FALSE);
|
|
|
|
//
|
|
// Initialize the stop event to signaled.
|
|
// This event is signaled when the OutstandingIO becomes 1
|
|
//
|
|
|
|
KeInitializeEvent(&deviceExtension->StopEvent,
|
|
SynchronizationEvent,
|
|
TRUE);
|
|
|
|
//
|
|
// OutstandingIo count biased to 1.
|
|
// Transition to 0 during remove device means IO is finished.
|
|
// Transition to 1 means the device can be stopped
|
|
//
|
|
|
|
deviceExtension->OutStandingIO = 1;
|
|
KeInitializeSpinLock(&deviceExtension->IOCountLock);
|
|
|
|
//
|
|
// Delegating to WMILIB
|
|
//
|
|
ntStatus = SSWmiRegistration(deviceExtension);
|
|
|
|
if(!NT_SUCCESS(ntStatus)) {
|
|
|
|
SSDbgPrint(1, ("SSWmiRegistration failed with %X\n", ntStatus));
|
|
IoDeleteDevice(deviceObject);
|
|
return ntStatus;
|
|
}
|
|
|
|
//
|
|
// set the flags as underlying PDO
|
|
//
|
|
|
|
if(PhysicalDeviceObject->Flags & DO_POWER_PAGABLE) {
|
|
|
|
deviceObject->Flags |= DO_POWER_PAGABLE;
|
|
}
|
|
|
|
//
|
|
// Typically, the function driver for a device is its
|
|
// power policy owner, although for some devices another
|
|
// driver or system component may assume this role.
|
|
// Set the initial power state of the device, if known, by calling
|
|
// PoSetPowerState.
|
|
//
|
|
deviceExtension->DevPower = PowerDeviceD0;
|
|
deviceExtension->SysPower = PowerSystemWorking;
|
|
|
|
state.DeviceState = PowerDeviceD0;
|
|
PoSetPowerState(deviceObject, DevicePowerState, state);
|
|
|
|
//
|
|
// attach our driver to device stack
|
|
// The return value of IoAttachDeviceToDeviceStack is the top of the
|
|
// attachment chain. This is where all the IRPs should be routed.
|
|
//
|
|
deviceExtension->TopOfStackDeviceObject =
|
|
IoAttachDeviceToDeviceStack(deviceObject,
|
|
PhysicalDeviceObject);
|
|
|
|
if(NULL == deviceExtension->TopOfStackDeviceObject) {
|
|
|
|
SSWmiDeRegistration(deviceExtension);
|
|
IoDeleteDevice(deviceObject);
|
|
return STATUS_NO_SUCH_DEVICE;
|
|
}
|
|
|
|
//
|
|
// Register device interfaces
|
|
//
|
|
|
|
ntStatus = IoRegisterDeviceInterface(deviceExtension->PhysicalDeviceObject,
|
|
&GUID_GENERIC_SELECTIVE_SUSPEND,
|
|
NULL,
|
|
&deviceExtension->InterfaceName);
|
|
|
|
if(!NT_SUCCESS(ntStatus)) {
|
|
|
|
SSWmiDeRegistration(deviceExtension);
|
|
IoDetachDevice(deviceExtension->TopOfStackDeviceObject);
|
|
IoDeleteDevice(deviceObject);
|
|
return ntStatus;
|
|
}
|
|
|
|
if(IoIsWdmVersionAvailable(1, 0x20)) {
|
|
|
|
deviceExtension->WdmVersion = WinXpOrBetter;
|
|
}
|
|
else if(IoIsWdmVersionAvailable(1, 0x10)) {
|
|
|
|
deviceExtension->WdmVersion = Win2kOrBetter;
|
|
}
|
|
else if(IoIsWdmVersionAvailable(1, 0x5)) {
|
|
|
|
deviceExtension->WdmVersion = WinMeOrBetter;
|
|
}
|
|
else if(IoIsWdmVersionAvailable(1, 0x0)) {
|
|
|
|
deviceExtension->WdmVersion = Win98OrBetter;
|
|
}
|
|
|
|
deviceExtension->SSRegistryEnable = 0;
|
|
deviceExtension->SSEnable = 0;
|
|
|
|
//
|
|
// Win XP only
|
|
// check the registry flag -
|
|
// whether the device should selectively
|
|
// suspend when idle
|
|
//
|
|
|
|
if(WinXpOrBetter == deviceExtension->WdmVersion) {
|
|
|
|
SS_GetRegistryDword(SELSUSP_REGISTRY_PARAMETERS_PATH,
|
|
L"SelectSuspendEnable",
|
|
&deviceExtension->SSRegistryEnable);
|
|
|
|
if(deviceExtension->SSRegistryEnable) {
|
|
|
|
//
|
|
// initialize the DPC
|
|
//
|
|
KeInitializeDpc(&deviceExtension->DeferredProcCall,
|
|
DpcRoutine,
|
|
deviceObject);
|
|
|
|
//
|
|
// initialize the timer.
|
|
// the DPC and the timer in conjunction,
|
|
// monitor the state of the device to
|
|
// selectively suspend the device.
|
|
//
|
|
KeInitializeTimerEx(&deviceExtension->Timer,
|
|
NotificationTimer);
|
|
|
|
//
|
|
// Initialize the NoDpcWorkItemPendingEvent to signaled state.
|
|
// This event is cleared when a Dpc is fired and signaled
|
|
// on completion of the work-item.
|
|
//
|
|
KeInitializeEvent(&deviceExtension->NoDpcWorkItemPendingEvent,
|
|
NotificationEvent,
|
|
TRUE);
|
|
|
|
//
|
|
// Initialize the NoIdleReqPendEvent to ensure that the idle request
|
|
// is indeed complete before we unload the drivers.
|
|
//
|
|
KeInitializeEvent(&deviceExtension->NoIdleReqPendEvent,
|
|
NotificationEvent,
|
|
TRUE);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Clear the DO_DEVICE_INITIALIZING flag.
|
|
// Note: Do not clear this flag until the driver has set the
|
|
// device power state and the power DO flags.
|
|
//
|
|
deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
|
|
|
|
SSDbgPrint(3, ("SS_AddDevice - ends\n"));
|
|
|
|
return ntStatus;
|
|
}
|
|
|