|
|
/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
cmbpnp.c
Abstract:
Control Method Battery Plug and Play support
Author:
Ron Mosgrove
Environment:
Kernel mode
Revision History:
--*/
#include "CmBattp.h"
#include <wdmguid.h>
#include <string.h>
//
// Power Source Type registry key
//
PCWSTR PowerSourceType = L"PowerSourceType"; #define POWER_SOURCE_TYPE_BATTERY 0
#define POWER_SOURCE_TYPE_AC_ADAPTER 1
//
// WaitWake registry key
//
PCWSTR WaitWakeEnableKey = L"WaitWakeEnabled";
//
// Globals
//
PDEVICE_OBJECT AcAdapterPdo = NULL;
//
// Prototypes
//
NTSTATUS CmBattAddDevice( IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT Pdo );
NTSTATUS CmBattRemoveDevice( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp );
NTSTATUS CmBattGetAcpiInterfaces( IN PDEVICE_OBJECT LowerDevice, OUT PACPI_INTERFACE_STANDARD AcpiInterfaces );
NTSTATUS CmBattAddBattery( IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT Pdo );
NTSTATUS CmBattAddAcAdapter( IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT Pdo );
NTSTATUS CmBattCreateFdo( IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT Pdo, IN ULONG ExtensionSize, OUT PDEVICE_OBJECT *NewFdo );
VOID CmBattDestroyFdo( IN PDEVICE_OBJECT Fdo );
NTSTATUS CmBattIoCompletion( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PKEVENT pdoIoCompletedEvent ) /*++
Routine Description:
This routine catches completion notifications.
Arguments:
DeviceObject - Pointer to class device object. Irp - Pointer to the request packet. pdoIoCompletedEvent - the just completed event
Return Value:
Status is returned.
--*/ {
CmBattPrint (CMBATT_TRACE, ("CmBattIoCompletion: Event (%x)\n", pdoIoCompletedEvent));
KeSetEvent(pdoIoCompletedEvent, IO_NO_INCREMENT, FALSE); return STATUS_MORE_PROCESSING_REQUIRED; }
NTSTATUS CmBattAddDevice( IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT Pdo )
/*++
Routine Description:
This routine creates functional device objects for each CmBatt controller in the system and attaches them to the physical device objects for the controllers
Arguments:
DriverObject - a pointer to the object for this driver PhysicalDeviceObject - a pointer to the physical object we need to attach to
Return Value:
Status from device creation and initialization
--*/
{ NTSTATUS Status; HANDLE handle; UNICODE_STRING unicodeString; CHAR buffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION)+sizeof(ULONG)]; ULONG unused;
PAGED_CODE();
CmBattPrint ((CMBATT_TRACE | CMBATT_PNP), ("CmBattAddDevice: Entered with pdo %x\n", Pdo));
if (Pdo == NULL) {
//
// Have we been asked to do detection on our own?
// if so just return no more devices
//
CmBattPrint((CMBATT_WARN | CMBATT_PNP), ("CmBattAddDevice: Asked to do detection\n")); return STATUS_NO_MORE_ENTRIES; }
//
// Get the software branch.
//
Status = IoOpenDeviceRegistryKey (Pdo, PLUGPLAY_REGKEY_DRIVER, STANDARD_RIGHTS_READ, &handle); if (!NT_SUCCESS(Status)) { CmBattPrint(CMBATT_ERROR, ("CmBattAddDevice: Could not get the software branch: %x\n", Status)); return Status; }
//
// Check if this is for an AC adapter or a battery.
//
RtlInitUnicodeString (&unicodeString, PowerSourceType); Status = ZwQueryValueKey( handle, &unicodeString, KeyValuePartialInformation, buffer, sizeof(buffer), &unused );
ZwClose( handle );
if (!NT_SUCCESS(Status)) {
CmBattPrint(CMBATT_ERROR, ("CmBattAddDevice: Could not read the power type identifier: %x\n", Status));
} else {
switch (*(PULONG)&((PKEY_VALUE_PARTIAL_INFORMATION)buffer)->Data) {
case POWER_SOURCE_TYPE_BATTERY: Status = CmBattAddBattery (DriverObject, Pdo); break;
case POWER_SOURCE_TYPE_AC_ADAPTER: Status = CmBattAddAcAdapter (DriverObject, Pdo); break;
default: CmBattPrint(CMBATT_ERROR, ("CmBattAddDevice: Invalid POWER_SOURCE_TYPE == %d \n", *(PULONG)&((PKEY_VALUE_PARTIAL_INFORMATION)buffer)->Data)); Status = STATUS_UNSUCCESSFUL; break; } }
//
// Return the status.
//
return Status; }
NTSTATUS CmBattAddBattery( IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT Pdo ) /*++
Routine Description:
This routine creates a functional device object for a CM battery, and attache it to the physical device object for the battery.
Arguments:
DriverObject - a pointer to the object for this driver PhysicalDeviceObject - a pointer to the physical object we need to attach to
Return Value:
Status from device creation and initialization
--*/
{ PDEVICE_OBJECT Fdo = NULL; PCM_BATT CmBatt; NTSTATUS Status; BATTERY_MINIPORT_INFO BattInit;
PAGED_CODE();
CmBattPrint ((CMBATT_TRACE | CMBATT_PNP), ("CmBattAddBattery: pdo %x\n", Pdo));
//
// Create and initialize the new functional device object
//
Status = CmBattCreateFdo(DriverObject, Pdo, sizeof(CM_BATT), &Fdo);
if (!NT_SUCCESS(Status)) { CmBattPrint(CMBATT_ERROR, ("CmBattAddBattery: error (0x%x) creating Fdo\n", Status)); return Status; }
//
// Initialize Fdo device extension data
//
CmBatt = (PCM_BATT) Fdo->DeviceExtension; CmBatt->Type = CM_BATTERY_TYPE; CmBatt->IsStarted = FALSE; CmBatt->ReCheckSta = TRUE; InterlockedExchange (&CmBatt->CacheState, 0);
CmBatt->Info.Tag = BATTERY_TAG_INVALID; CmBatt->Alarm.Setting = CM_ALARM_INVALID; CmBatt->DischargeTime = KeQueryInterruptTime();
if (CmBattSetTripPpoint (CmBatt, 0) == STATUS_OBJECT_NAME_NOT_FOUND) { CmBatt->Info.BtpExists = FALSE; } else { CmBatt->Info.BtpExists = TRUE; }
//
// Attach to the Class Driver
//
RtlZeroMemory (&BattInit, sizeof(BattInit)); BattInit.MajorVersion = BATTERY_CLASS_MAJOR_VERSION; BattInit.MinorVersion = BATTERY_CLASS_MINOR_VERSION; BattInit.Context = CmBatt; BattInit.QueryTag = CmBattQueryTag; BattInit.QueryInformation = CmBattQueryInformation; BattInit.SetInformation = NULL; // tbd
BattInit.QueryStatus = CmBattQueryStatus; BattInit.SetStatusNotify = CmBattSetStatusNotify; BattInit.DisableStatusNotify = CmBattDisableStatusNotify;
BattInit.Pdo = Pdo; BattInit.DeviceName = CmBatt->DeviceName;
Status = BatteryClassInitializeDevice (&BattInit, &CmBatt->Class); if (!NT_SUCCESS(Status)) { //
// if we can't attach to class driver we're toast
//
CmBattPrint(CMBATT_ERROR, ("CmBattAddBattery: error (0x%x) registering with class\n", Status)); IoDetachDevice (CmBatt->LowerDeviceObject); CmBattDestroyFdo (CmBatt->Fdo); return Status; }
//
// Register WMI support.
//
Status = CmBattWmiRegistration(CmBatt);
if (!NT_SUCCESS(Status)) { //
// WMI support is not critical to operation. Just log an error.
//
CmBattPrint(CMBATT_ERROR, ("CmBattAddBattery: Could not register as a WMI provider, status = %Lx\n", Status)); }
//
// Register the battery notify handler for this battery with ACPI
// This registration is performed after registering with the battery
// class because CmBattNotifyHandler must not be run until the battery
// class is ready.
//
Status = CmBatt->AcpiInterfaces.RegisterForDeviceNotifications ( CmBatt->AcpiInterfaces.Context, CmBattNotifyHandler, CmBatt);
if (!NT_SUCCESS(Status)) {
CmBattPrint(CMBATT_ERROR, ("CmBattAddBattery: Could not register for battery notify, status = %Lx\n", Status)); CmBattWmiDeRegistration(CmBatt); BatteryClassUnload (CmBatt->Class); IoDetachDevice (CmBatt->LowerDeviceObject); CmBattDestroyFdo (CmBatt->Fdo); return Status; }
return STATUS_SUCCESS; }
NTSTATUS CmBattAddAcAdapter( IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT Pdo ) /*++
Routine Description:
This routine registers a notify handler for the AC Adapter. And saves the PDO so we can run the _STA method against it to get the AC status.
Arguments:
DriverObject - a pointer to the object for this driver Pdo - a pointer to the Pdo
Return Value:
Status from device creation and initialization
--*/
{ PDEVICE_OBJECT Fdo; NTSTATUS Status; PAC_ADAPTER acExtension;
PAGED_CODE();
CmBattPrint ((CMBATT_TRACE | CMBATT_PNP), ("CmBattAddAcAdapter: pdo %x\n", Pdo));
//
// Save PDO so we can run _STA method on it later
//
if (AcAdapterPdo != NULL) { CmBattPrint(CMBATT_ERROR, ("CmBatt: Second AC adapter found. Current version of driver only supports 1 aadapter.\n")); } else { AcAdapterPdo = Pdo; }
Status = CmBattCreateFdo(DriverObject, Pdo, sizeof(AC_ADAPTER), &Fdo);
if (!NT_SUCCESS(Status)) { CmBattPrint(CMBATT_ERROR, ("CmBattAddAcAdapter: error (0x%x) creating Fdo\n", Status)); return Status; }
//
// Initialize Fdo device extension data
//
acExtension = (PAC_ADAPTER) Fdo->DeviceExtension; acExtension->Type = AC_ADAPTER_TYPE;
//
// Register WMI support.
//
Status = CmBattWmiRegistration((PCM_BATT)acExtension);
if (!NT_SUCCESS(Status)) { //
// WMI support is not critical to operation. Just log an error.
//
CmBattPrint(CMBATT_ERROR, ("CmBattAddBattery: Could not register as a WMI provider, status = %Lx\n", Status)); }
//
// Register the AC adapter notify handler with ACPI
//
Status = acExtension->AcpiInterfaces.RegisterForDeviceNotifications ( acExtension->AcpiInterfaces.Context, CmBattNotifyHandler, acExtension);
//
// We will ignore errors, since this is not a critical operation
//
if (!NT_SUCCESS(Status)) {
CmBattPrint(CMBATT_ERROR, ("CmBattAddAcAdapter: Could not register for power notify, status = %Lx\n", Status)); }
//
// Give one notification, to make sure all batteries get updated.
//
CmBattNotifyHandler (acExtension, BATTERY_STATUS_CHANGE);
return STATUS_SUCCESS; }
NTSTATUS CmBattGetAcpiInterfaces( IN PDEVICE_OBJECT LowerDevice, OUT PACPI_INTERFACE_STANDARD AcpiInterfaces )
/*++
Routine Description:
Call ACPI driver to get the direct-call interfaces. It does this the first time it is called, no more.
Arguments:
None.
Return Value:
Status
--*/
{ NTSTATUS Status = STATUS_SUCCESS; PIRP Irp; PIO_STACK_LOCATION IrpSp; KEVENT syncEvent;
//
// Allocate an IRP for below
//
Irp = IoAllocateIrp (LowerDevice->StackSize, FALSE); // Get stack size from PDO
if (!Irp) { CmBattPrint((CMBATT_ERROR), ("CmBattGetAcpiInterfaces: Failed to allocate Irp\n")); return STATUS_INSUFFICIENT_RESOURCES; }
IrpSp = IoGetNextIrpStackLocation(Irp);
//
// Use QUERY_INTERFACE to get the address of the direct-call ACPI interfaces.
//
IrpSp->MajorFunction = IRP_MJ_PNP; IrpSp->MinorFunction = IRP_MN_QUERY_INTERFACE; Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
IrpSp->Parameters.QueryInterface.InterfaceType = (LPGUID) &GUID_ACPI_INTERFACE_STANDARD; IrpSp->Parameters.QueryInterface.Version = 1; IrpSp->Parameters.QueryInterface.Size = sizeof (*AcpiInterfaces); IrpSp->Parameters.QueryInterface.Interface = (PINTERFACE) AcpiInterfaces; IrpSp->Parameters.QueryInterface.InterfaceSpecificData = NULL;
//
// Initialize an event so this will be a syncronous call.
//
KeInitializeEvent(&syncEvent, SynchronizationEvent, FALSE);
IoSetCompletionRoutine (Irp, CmBattIoCompletion, &syncEvent, TRUE, TRUE, TRUE);
//
// Call ACPI
//
Status = IoCallDriver (LowerDevice, Irp);
//
// Wait if necessary, then clean up.
//
if (Status == STATUS_PENDING) { KeWaitForSingleObject(&syncEvent, Executive, KernelMode, FALSE, NULL); Status = Irp->IoStatus.Status; }
IoFreeIrp (Irp);
if (!NT_SUCCESS(Status)) {
CmBattPrint(CMBATT_ERROR, ("CmBattGetAcpiInterfaces: Could not get ACPI driver interfaces, status = %x\n", Status)); }
return Status; }
NTSTATUS CmBattCreateFdo( IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT Pdo, IN ULONG ExtensionSize, OUT PDEVICE_OBJECT *NewFdo )
/*++
Routine Description:
This routine will create and initialize a functional device object to be attached to a Control Method Battery PDO.
Arguments:
DriverObject - a pointer to the driver object this is created under ExtensionSize - device extension size: sizeof (CM_BATT) or sizeof (AC_ADAPTER) NewFdo - a location to store the pointer to the new device object
Return Value:
STATUS_SUCCESS if everything was successful reason for failure otherwise
--*/
{ PDEVICE_OBJECT fdo; NTSTATUS status; PCM_BATT cmBatt; ULONG uniqueId; USHORT strLength = 0; HANDLE devInstRegKey; UNICODE_STRING valueName; CHAR buffer [sizeof(KEY_VALUE_PARTIAL_INFORMATION)+sizeof(ULONG)]; ULONG returnSize;
PAGED_CODE();
CmBattPrint ((CMBATT_TRACE | CMBATT_PNP), ("CmBattCreateFdo: Entered\n"));
//
// Get the unique ID of this device by running the _UID method.
// If this fails, assume one device.
//
status = CmBattGetUniqueId (Pdo, &uniqueId);
if (!NT_SUCCESS(status)) { CmBattPrint(CMBATT_NOTE, ("CmBattCreateFdo: Error %x from _UID, assuming unit #0\n", status)); uniqueId = 0; }
//
// Create the FDO
//
status = IoCreateDevice( DriverObject, ExtensionSize, NULL, FILE_DEVICE_BATTERY, FILE_DEVICE_SECURE_OPEN, FALSE, &fdo );
if (status != STATUS_SUCCESS) { CmBattPrint(CMBATT_ERROR, ("CmBattCreateFdo: error (0x%x) creating device object\n", status)); return(status); }
fdo->Flags |= DO_BUFFERED_IO; fdo->Flags |= DO_POWER_PAGABLE; // Don't want power Irps at irql 2
fdo->Flags &= ~DO_DEVICE_INITIALIZING;
//
// Initialize Fdo device extension data
//
cmBatt = (PCM_BATT) fdo->DeviceExtension;
//
// Note: This is note necessarily a battery. It could be an AC adapter, so only fields
// common to both should be initialized here.
//
RtlZeroMemory(cmBatt, ExtensionSize); //CmBatt->Type must be initialized after this call.
cmBatt->DeviceObject = fdo; cmBatt->Fdo = fdo; cmBatt->Pdo = Pdo;
//
// Connect to lower device
//
cmBatt->LowerDeviceObject = IoAttachDeviceToDeviceStack(fdo, Pdo); if (!cmBatt->LowerDeviceObject) { CmBattPrint(CMBATT_ERROR, ("CmBattCreateFdo: IoAttachDeviceToDeviceStack failed.\n")); CmBattDestroyFdo (cmBatt->Fdo); return STATUS_UNSUCCESSFUL; }
//
// Get the direct-call ACPI interfaces
//
status = CmBattGetAcpiInterfaces (cmBatt->LowerDeviceObject, &cmBatt->AcpiInterfaces); if (!NT_SUCCESS(status)) { CmBattPrint(CMBATT_ERROR, ("CmBattCreateFdor: Could not get ACPI interfaces: %x\n", status)); IoDetachDevice (cmBatt->LowerDeviceObject); CmBattDestroyFdo (cmBatt->Fdo); return status; }
//
// Initializes File handle tracking.
//
ExInitializeFastMutex (&cmBatt->OpenCloseMutex); cmBatt->OpenCount = 0;
//
// Removal lock initialization
//
cmBatt->WantToRemove = FALSE; cmBatt->InUseCount = 1; KeInitializeEvent(&cmBatt->ReadyToRemove, SynchronizationEvent, FALSE);
cmBatt->DeviceNumber = uniqueId; cmBatt->DeviceName = NULL; cmBatt->Sleeping = FALSE; cmBatt->ActionRequired = CMBATT_AR_NO_ACTION;
//
// Determine if wake on Battery should be enabled
//
cmBatt->WakeEnabled = FALSE;
status = IoOpenDeviceRegistryKey (Pdo, PLUGPLAY_REGKEY_DEVICE, STANDARD_RIGHTS_ALL, &devInstRegKey);
if (NT_SUCCESS (status)) { RtlInitUnicodeString (&valueName, WaitWakeEnableKey); status = ZwQueryValueKey( devInstRegKey, &valueName, KeyValuePartialInformation, buffer, sizeof(buffer), &returnSize );
if (NT_SUCCESS (status)) { cmBatt->WakeEnabled = (*(PULONG)((PKEY_VALUE_PARTIAL_INFORMATION)buffer)->Data ? TRUE : FALSE); } ZwClose(devInstRegKey); }
*NewFdo = fdo;
CmBattPrint((CMBATT_TRACE | CMBATT_PNP), ("CmBattCreateFdo: Created FDO %x\n", fdo)); return STATUS_SUCCESS; }
VOID CmBattDestroyFdo( IN PDEVICE_OBJECT Fdo )
/*++
Routine Description:
This routine will deallocate a functional device object. This includes calling IoDeleteDevice.
Arguments:
Fdo - a pointer to the FDO to destroy.
Return Value:
STATUS_SUCCESS if everything was successful reason for failure otherwise
--*/
{
PAGED_CODE();
CmBattPrint ((CMBATT_TRACE | CMBATT_PNP), ("CmBattDestroyFdo, Battery.\n"));
IoDeleteDevice (Fdo);
CmBattPrint((CMBATT_TRACE | CMBATT_PNP), ("CmBattDestroyFdo: done.\n")); }
NTSTATUS CmBattPnpDispatch( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp )
/*++
Routine Description:
This routine is the dispatch routine for plug and play requests.
Arguments:
DeviceObject - Pointer to class device object. Irp - Pointer to the request packet.
Return Value:
Status is returned.
--*/
{ PIO_STACK_LOCATION irpStack; PCM_BATT CmBatt; NTSTATUS status; KEVENT syncEvent;
PAGED_CODE();
status = STATUS_NOT_SUPPORTED;
//
// Get a pointer to the current parameters for this request. The
// information is contained in the current stack location.
//
irpStack = IoGetCurrentIrpStackLocation(Irp); CmBatt = DeviceObject->DeviceExtension;
//
// Aquire remove lock
//
InterlockedIncrement (&CmBatt->InUseCount); if (CmBatt->WantToRemove == TRUE) { //
// Failed to acquire remove lock.
//
status = STATUS_DEVICE_REMOVED; } else { //
// Remove lock acquired.
//
//
// Dispatch minor function
//
switch (irpStack->MinorFunction) {
case IRP_MN_START_DEVICE: { CmBattPrint (CMBATT_PNP, ("CmBattPnpDispatch: IRP_MN_START_DEVICE\n"));
if (CmBatt->Type == CM_BATTERY_TYPE) { //
// We only want to handle batteries, not AC adapters.
//
CmBatt->IsStarted = TRUE;
} status = STATUS_SUCCESS;
break; } // IRP_MN_START_DEVICE
case IRP_MN_STOP_DEVICE: { CmBattPrint (CMBATT_PNP, ("CmBattPnpDispatch: IRP_MN_STOP_DEVICE\n"));
if (CmBatt->Type == CM_BATTERY_TYPE) { CmBatt->IsStarted = FALSE; } status = STATUS_SUCCESS;
break; } // IRP_MN_STOP_DEVICE
case IRP_MN_REMOVE_DEVICE: { CmBattPrint (CMBATT_PNP, ("CmBattPnpDispatch: IRP_MN_REMOVE_DEVICE\n"));
status = CmBattRemoveDevice(DeviceObject, Irp); break; } // IRP_MN_REMOVE_DEVICE
case IRP_MN_SURPRISE_REMOVAL: { CmBattPrint (CMBATT_PNP, ("CmBattPnpDispatch: IRP_MN_SURPRISE_REMOVAL\n"));
ExAcquireFastMutex (&CmBatt->OpenCloseMutex); status = STATUS_SUCCESS;
CmBatt->OpenCount = (ULONG) -1;
ExReleaseFastMutex (&CmBatt->OpenCloseMutex);
break; } // IRP_MN_QUERY_REMOVE_DEVICE
case IRP_MN_QUERY_REMOVE_DEVICE: { CmBattPrint (CMBATT_PNP, ("CmBattPnpDispatch: IRP_MN_QUERY_REMOVE_DEVICE\n"));
ExAcquireFastMutex (&CmBatt->OpenCloseMutex); status = STATUS_SUCCESS;
if (CmBatt->OpenCount == 0) { CmBatt->OpenCount = (ULONG) -1; } else if (CmBatt->OpenCount == (ULONG) -1) { CmBattPrint (CMBATT_WARN, ("CmBattPnpDispatch: Recieved two consecutive QUERY_REMOVE requests.\n"));
} else { status = STATUS_UNSUCCESSFUL; }
ExReleaseFastMutex (&CmBatt->OpenCloseMutex);
break; } // IRP_MN_QUERY_REMOVE_DEVICE
case IRP_MN_CANCEL_REMOVE_DEVICE: { CmBattPrint (CMBATT_PNP, ("CmBattPnpDispatch: IRP_MN_CANCEL_REMOVE_DEVICE\n"));
ExAcquireFastMutex (&CmBatt->OpenCloseMutex);
if (CmBatt->OpenCount == (ULONG) -1) { CmBatt->OpenCount = 0; } else { CmBattPrint (CMBATT_NOTE, ("CmBattPnpDispatch: Received CANCEL_REMOVE when OpenCount == %x\n", CmBatt->OpenCount)); } status = STATUS_SUCCESS;
ExReleaseFastMutex (&CmBatt->OpenCloseMutex);
break; } // IRP_MN_CANCEL_REMOVE_DEVICE
case IRP_MN_QUERY_STOP_DEVICE: { CmBattPrint (CMBATT_PNP, ("CmBattPnpDispatch: IRP_MN_QUERY_STOP_DEVICE\n")); status = STATUS_NOT_IMPLEMENTED; break; } // IRP_MN_QUERY_STOP_DEVICE
case IRP_MN_CANCEL_STOP_DEVICE: { CmBattPrint (CMBATT_PNP, ("CmBattPnpDispatch: IRP_MN_CANCEL_STOP_DEVICE\n")); status = STATUS_NOT_IMPLEMENTED; break; } // IRP_MN_CANCEL_STOP_DEVICE
case IRP_MN_QUERY_PNP_DEVICE_STATE: {
IoCopyCurrentIrpStackLocationToNext (Irp);
KeInitializeEvent(&syncEvent, SynchronizationEvent, FALSE);
IoSetCompletionRoutine(Irp, CmBattIoCompletion, &syncEvent, TRUE, TRUE, TRUE);
status = IoCallDriver(CmBatt->LowerDeviceObject, Irp);
if (status == STATUS_PENDING) { KeWaitForSingleObject(&syncEvent, Executive, KernelMode, FALSE, NULL); status = Irp->IoStatus.Status; }
Irp->IoStatus.Information &= ~PNP_DEVICE_NOT_DISABLEABLE;
IoCompleteRequest(Irp, IO_NO_INCREMENT); if (0 == InterlockedDecrement(&CmBatt->InUseCount)) { KeSetEvent (&CmBatt->ReadyToRemove, IO_NO_INCREMENT, FALSE); } return status;
}
case IRP_MN_QUERY_CAPABILITIES: {
IoCopyCurrentIrpStackLocationToNext (Irp);
KeInitializeEvent(&syncEvent, SynchronizationEvent, FALSE);
IoSetCompletionRoutine(Irp, CmBattIoCompletion, &syncEvent, TRUE, TRUE, TRUE);
status = IoCallDriver(CmBatt->LowerDeviceObject, Irp);
if (status == STATUS_PENDING) { KeWaitForSingleObject(&syncEvent, Executive, KernelMode, FALSE, NULL); status = Irp->IoStatus.Status; }
CmBatt->WakeSupportedState.SystemState = irpStack->Parameters.DeviceCapabilities.Capabilities->SystemWake; CmBattPrint (CMBATT_PNP, ("CmBattPnpDispatch: IRP_MN_QUERY_CAPABILITIES %d Capabilities->SystemWake = %x\n", CmBatt->Type, CmBatt->WakeSupportedState.SystemState)); if (CmBatt->WakeSupportedState.SystemState != PowerSystemUnspecified) { if (CmBatt->WaitWakeIrp == NULL && CmBatt->WakeEnabled) { PoRequestPowerIrp( CmBatt->DeviceObject, IRP_MN_WAIT_WAKE, CmBatt->WakeSupportedState, CmBattWaitWakeLoop, NULL, &(CmBatt->WaitWakeIrp) );
//
// Ignore return value. Capbilities IRP should still succeed.
//
CmBattPrint (CMBATT_PNP, ("CmBattPnpDispatch: IRP_MN_QUERY_CAPABILITIES wait/Wake irp sent.\n")); } } else { CmBatt->WakeEnabled=FALSE; CmBattPrint (CMBATT_PNP, ("CmBattPnpDispatch: IRP_MN_QUERY_CAPABILITIES Wake not supported.\n")); }
IoCompleteRequest(Irp, IO_NO_INCREMENT); if (0 == InterlockedDecrement(&CmBatt->InUseCount)) { KeSetEvent (&CmBatt->ReadyToRemove, IO_NO_INCREMENT, FALSE); } return status;
}
default: { //
// Unimplemented minor, Pass this down
//
CmBattPrint (CMBATT_PNP, ("CmBattPnpDispatch: Unimplemented minor %0x\n", \ irpStack->MinorFunction)); } // default
// Fall through...
case IRP_MN_QUERY_RESOURCES: case IRP_MN_READ_CONFIG: case IRP_MN_WRITE_CONFIG: case IRP_MN_EJECT: case IRP_MN_SET_LOCK: case IRP_MN_QUERY_ID: case IRP_MN_QUERY_DEVICE_RELATIONS: {
break ; } } }
//
// Release remove lock
//
if (0 == InterlockedDecrement(&CmBatt->InUseCount)) { KeSetEvent (&CmBatt->ReadyToRemove, IO_NO_INCREMENT, FALSE); }
//
// Only set status if we have something to add
//
if (status != STATUS_NOT_SUPPORTED) {
Irp->IoStatus.Status = status;
}
//
// Do we need to send it down?
//
if (NT_SUCCESS(status) || (status == STATUS_NOT_SUPPORTED)) {
CmBattCallLowerDriver(status, CmBatt->LowerDeviceObject, Irp); return status; }
//
// At this point, it must have been passed down and needs recompletion,
// or the status is unsuccessful.
//
ASSERT(!NT_SUCCESS(status) && (status != STATUS_NOT_SUPPORTED));
status = Irp->IoStatus.Status ; IoCompleteRequest(Irp, IO_NO_INCREMENT); return status; }
NTSTATUS CmBattRemoveDevice( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++
Routine Description:
This routine processes a IRP_MN_REMOVE_DEVICE
Arguments:
DeviceObject - Pointer to class device object. Irp - Pointer to the request packet.
Return Value:
Returns STATUS_SUCCESS. (This function must not fail.)
--*/
{ PCM_BATT cmBatt; NTSTATUS status;
cmBatt = (PCM_BATT) DeviceObject->DeviceExtension; CmBattPrint (CMBATT_TRACE, ("CmBattRemoveDevice: CmBatt (%x), Type %d, _UID %d\n", cmBatt, cmBatt->Type, cmBatt->DeviceNumber));
//
// Remove device syncronization
//
//
// Prevent more locks from being acquired.
//
cmBatt->WantToRemove = TRUE;
//
// Release lock acquired at start of CmBattPnpDispatch
//
if (InterlockedDecrement (&cmBatt->InUseCount) <= 0) { CmBattPrint (CMBATT_ERROR, ("CmBattRemoveDevice: Remove lock error.\n")); ASSERT(FALSE); }
//
// Final release and wait.
//
// Note: there will be one more relase at the end of CmBattPnpDispatch
// but it will decrement the InUseCount to -1 so it won't set the event.
//
if (InterlockedDecrement (&cmBatt->InUseCount) > 0) { KeWaitForSingleObject (&cmBatt->ReadyToRemove, Executive, KernelMode, FALSE, NULL ); }
//
// Cancel the Wait/wake IRP;
//
if (cmBatt->WaitWakeIrp != NULL) { IoCancelIrp (cmBatt->WaitWakeIrp); cmBatt->WaitWakeIrp = NULL; }
if (cmBatt->Type == CM_BATTERY_TYPE) { //
// This is a control method battery FDO
//
//
// Disconnect from receiving device (battery) notifications
//
cmBatt->AcpiInterfaces.UnregisterForDeviceNotifications ( cmBatt->AcpiInterfaces.Context, CmBattNotifyHandler);
//
// Unregister as a WMI Provider.
//
CmBattWmiDeRegistration(cmBatt);
//
// Tell the class driver we are going away
//
status = BatteryClassUnload (cmBatt->Class); ASSERT (NT_SUCCESS(status));
} else { //
// This is an AC adapter FDO
//
//
// Disconnect from receiving device (battery) notifications
//
cmBatt->AcpiInterfaces.UnregisterForDeviceNotifications ( cmBatt->AcpiInterfaces.Context, CmBattNotifyHandler);
//
// Unregister as a WMI Provider.
//
CmBattWmiDeRegistration(cmBatt);
AcAdapterPdo = NULL; }
//
// Clean up, delete the Fdo we created at AddDevice time
//
IoDetachDevice (cmBatt->LowerDeviceObject); IoDeleteDevice (cmBatt->DeviceObject);
return STATUS_SUCCESS;
}
NTSTATUS CmBattPowerDispatch( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++
Routine Description:
This routine is the dispatch routine for power requests.
Arguments:
DeviceObject - Pointer to class device object. Irp - Pointer to the request packet.
Return Value:
Status is returned.
--*/ { PIO_STACK_LOCATION irpStack; PCM_BATT CmBatt; NTSTATUS Status;
//
// A remove lock is not needed in this dispatch function because
// all data accessed is in the device extension. If any other functionality
// was added to this routine, a remove lock might be neccesary.
//
CmBattPrint ((CMBATT_TRACE | CMBATT_POWER), ("CmBattPowerDispatch\n"));
//
// Get a pointer to the current parameters for this request. The
// information is contained in the current stack location.
//
irpStack = IoGetCurrentIrpStackLocation(Irp); CmBatt = DeviceObject->DeviceExtension;
//
// Dispatch minor function
//
switch (irpStack->MinorFunction) {
case IRP_MN_WAIT_WAKE: { CmBattPrint (CMBATT_POWER, ("CmBattPowerDispatch: IRP_MN_WAIT_WAKE\n")); break; }
case IRP_MN_POWER_SEQUENCE: { CmBattPrint (CMBATT_POWER, ("CmBattPowerDispatch: IRP_MN_POWER_SEQUENCE\n")); break; }
case IRP_MN_SET_POWER: { CmBattPrint (CMBATT_POWER, ("CmBattPowerDispatch: IRP_MN_SET_POWER type: %d, State: %d \n", irpStack->Parameters.Power.Type, irpStack->Parameters.Power.State)); break; }
case IRP_MN_QUERY_POWER: { CmBattPrint (CMBATT_POWER, ("CmBattPowerDispatch: IRP_MN_QUERY_POWER\n")); break; }
default: {
CmBattPrint(CMBATT_LOW, ("CmBattPowerDispatch: minor %d\n", irpStack->MinorFunction));
break; } }
//
// What do we do with the irp?
//
PoStartNextPowerIrp( Irp ); if (CmBatt->LowerDeviceObject != NULL) {
//
// Forward the request along
//
IoSkipCurrentIrpStackLocation( Irp ); Status = PoCallDriver( CmBatt->LowerDeviceObject, Irp );
} else {
//
// Complete the request with the current status
//
Status = Irp->IoStatus.Status; IoCompleteRequest( Irp, IO_NO_INCREMENT );
}
return Status; }
NTSTATUS CmBattForwardRequest( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++
Routine Description:
This routine passes the request down the stack
Arguments:
DeviceObject - The target Irp - The request
Return Value:
NTSTATUS
--*/ { NTSTATUS status; PCM_BATT cmBatt = DeviceObject->DeviceExtension;
//
// A remove lock is not needed in this dispatch function because
// all data accessed is in the device extension. If any other functionality was
// added to this routine, a remove lock might be neccesary.
//
if (cmBatt->LowerDeviceObject != NULL) {
IoSkipCurrentIrpStackLocation( Irp ); status = IoCallDriver( cmBatt->LowerDeviceObject, Irp );
} else {
Irp->IoStatus.Status = status = STATUS_NOT_SUPPORTED; IoCompleteRequest( Irp, IO_NO_INCREMENT );
}
return status; }
NTSTATUS CmBattWaitWakeLoop( IN PDEVICE_OBJECT DeviceObject, IN UCHAR MinorFunction, IN POWER_STATE PowerState, IN PVOID Context, IN PIO_STATUS_BLOCK IoStatus ) /*++
Routine Description:
This routine is called after the WAIT_WAKE has been completed
Arguments:
DeviceObject - The PDO MinorFunction - IRP_MN_WAIT_WAKE PowerState - The Sleep state that it could wake from Context - NOT USED IoStatus - The status of the request
Return Value:
NTSTATUS
--*/ { NTSTATUS status; PCM_BATT cmBatt = (PCM_BATT) DeviceObject->DeviceExtension;
CmBattPrint (CMBATT_PNP, ("CmBattWaitWakeLoop: Entered.\n")); if (!NT_SUCCESS(IoStatus->Status) || !cmBatt->WakeEnabled) {
CmBattPrint (CMBATT_ERROR, ("CmBattWaitWakeLoop: failed: status = 0x%08x.\n", IoStatus->Status)); cmBatt->WaitWakeIrp = NULL; return IoStatus->Status;
} else { CmBattPrint (CMBATT_NOTE, ("CmBattWaitWakeLoop: completed successfully\n")); }
//
// In this case, we just cause the same thing to happen again
//
status = PoRequestPowerIrp( DeviceObject, MinorFunction, PowerState, CmBattWaitWakeLoop, Context, &(cmBatt->WaitWakeIrp) );
CmBattPrint (CMBATT_NOTE, ("CmBattWaitWakeLoop: PoRequestPowerIrp: status = 0x%08x.\n", status));
//
// Done
//
return STATUS_SUCCESS; }
|