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