1448 lines
37 KiB
C
1448 lines
37 KiB
C
|
/*++
|
|||
|
|
|||
|
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>
|
|||
|
|
|||
|
//
|
|||
|
// Device Names
|
|||
|
//
|
|||
|
PCWSTR CmBattDeviceName = L"\\Device\\ControlMethodBattery";
|
|||
|
PCWSTR AcAdapterName = L"\\Device\\AcAdapter";
|
|||
|
|
|||
|
//
|
|||
|
// 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 PWSTR DeviceName,
|
|||
|
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, (PWSTR) CmBattDeviceName, 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, (PWSTR) AcAdapterName, 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 PWSTR DeviceName,
|
|||
|
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
|
|||
|
DeviceName - The namde of the device to create
|
|||
|
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;
|
|||
|
PUNICODE_STRING unicodeString;
|
|||
|
ULONG uniqueId;
|
|||
|
USHORT strLength = 0;
|
|||
|
UNICODE_STRING numberString;
|
|||
|
WCHAR numberBuffer[10];
|
|||
|
HANDLE devInstRegKey;
|
|||
|
UNICODE_STRING valueName;
|
|||
|
CHAR buffer [sizeof(KEY_VALUE_PARTIAL_INFORMATION)+sizeof(ULONG)];
|
|||
|
ULONG returnSize;
|
|||
|
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
CmBattPrint ((CMBATT_TRACE | CMBATT_PNP), ("CmBattCreateFdo: Device = %ws\n", DeviceName));
|
|||
|
|
|||
|
//
|
|||
|
// Create the PDO device name based on the _UID
|
|||
|
//
|
|||
|
|
|||
|
numberString.MaximumLength = 10;
|
|||
|
numberString.Length = 0;
|
|||
|
numberString.Buffer = &numberBuffer[0];
|
|||
|
|
|||
|
//
|
|||
|
// Get the unique ID of this device by running the _UID method.
|
|||
|
// If this fails, assume one device. Append no device number.
|
|||
|
//
|
|||
|
status = CmBattGetUniqueId (Pdo, &uniqueId);
|
|||
|
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
CmBattPrint(CMBATT_NOTE, ("CmBattCreateFdo: Error %x from _UID, assuming unit #0\n", status));
|
|||
|
uniqueId = 0;
|
|||
|
|
|||
|
} else {
|
|||
|
RtlIntegerToUnicodeString (uniqueId, 10, &numberString);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Allocate the UNICODE_STRING for the device name
|
|||
|
//
|
|||
|
|
|||
|
strLength = (USHORT) (wcslen (DeviceName) * 2 + numberString.Length);
|
|||
|
|
|||
|
unicodeString = ExAllocatePoolWithTag (
|
|||
|
PagedPool,
|
|||
|
sizeof (UNICODE_STRING) + strLength,
|
|||
|
'MtaB'
|
|||
|
);
|
|||
|
|
|||
|
if (!unicodeString) {
|
|||
|
CmBattPrint(CMBATT_ERROR, ("CmBattCreateFdo: could not allocate unicode string\n"));
|
|||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
}
|
|||
|
|
|||
|
unicodeString->MaximumLength = strLength;
|
|||
|
unicodeString->Length = 0;
|
|||
|
unicodeString->Buffer = (PWSTR) (unicodeString + 1);
|
|||
|
|
|||
|
RtlAppendUnicodeToString (unicodeString, DeviceName);
|
|||
|
RtlAppendUnicodeStringToString (unicodeString, &numberString);
|
|||
|
|
|||
|
//
|
|||
|
// Create the FDO
|
|||
|
//
|
|||
|
|
|||
|
status = IoCreateDevice(
|
|||
|
DriverObject,
|
|||
|
ExtensionSize,
|
|||
|
unicodeString,
|
|||
|
FILE_DEVICE_BATTERY,
|
|||
|
0,
|
|||
|
FALSE,
|
|||
|
&fdo
|
|||
|
);
|
|||
|
|
|||
|
if (status != STATUS_SUCCESS) {
|
|||
|
CmBattPrint(CMBATT_ERROR, ("CmBattCreateFdo: error (0x%x) creating device object\n", status));
|
|||
|
ExFreePool (unicodeString);
|
|||
|
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 = unicodeString;
|
|||
|
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 deallocating the DeviceName and 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"));
|
|||
|
|
|||
|
//
|
|||
|
// Deallocate the UNICODE_STRING for the device name
|
|||
|
//
|
|||
|
|
|||
|
ExFreePool (((PCM_BATT)Fdo->DeviceExtension)->DeviceName);
|
|||
|
((PCM_BATT)Fdo->DeviceExtension)->DeviceName = NULL;
|
|||
|
|
|||
|
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) {
|
|||
|
status = PoRequestPowerIrp(
|
|||
|
CmBatt->DeviceObject,
|
|||
|
IRP_MN_WAIT_WAKE,
|
|||
|
CmBatt->WakeSupportedState,
|
|||
|
CmBattWaitWakeLoop,
|
|||
|
NULL,
|
|||
|
&(CmBatt->WaitWakeIrp)
|
|||
|
);
|
|||
|
|
|||
|
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 string and the Fdo we created at AddDevice time
|
|||
|
//
|
|||
|
|
|||
|
ExFreePool (cmBatt->DeviceName);
|
|||
|
|
|||
|
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;
|
|||
|
}
|
|||
|
|