600 lines
14 KiB
C
600 lines
14 KiB
C
|
||
/*++
|
||
|
||
Copyright (c) 1996 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
apmbpnp.c
|
||
|
||
Abstract:
|
||
|
||
Control Method Battery Plug and Play support
|
||
|
||
Author:
|
||
|
||
Ron Mosgrove
|
||
|
||
Environment:
|
||
|
||
Kernel mode
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "ApmBattp.h"
|
||
#include <initguid.h>
|
||
#include <wdmguid.h>
|
||
#include <ntapm.h>
|
||
|
||
//
|
||
// Device Names
|
||
//
|
||
PCWSTR ApmBattDeviceName = L"\\Device\\ApmBattery";
|
||
//PCWSTR AcAdapterName = L"\\Device\\AcAdapter";
|
||
|
||
//
|
||
// This is a special Hack as part of this general APM special hack
|
||
//
|
||
PVOID ApmGlobalClass = NULL;
|
||
|
||
//
|
||
// Prototypes
|
||
//
|
||
NTSTATUS
|
||
ApmBattAddDevice(
|
||
IN PDRIVER_OBJECT DriverObject,
|
||
IN PDEVICE_OBJECT Pdo
|
||
);
|
||
|
||
NTSTATUS
|
||
ApmBattAddBattery(
|
||
IN PDRIVER_OBJECT DriverObject,
|
||
IN PDEVICE_OBJECT Pdo
|
||
);
|
||
|
||
#if 0
|
||
NTSTATUS
|
||
ApmBattAddAcAdapter(
|
||
IN PDRIVER_OBJECT DriverObject,
|
||
IN PDEVICE_OBJECT Pdo
|
||
);
|
||
#endif
|
||
|
||
NTSTATUS
|
||
ApmBattCreateFdo(
|
||
IN PDRIVER_OBJECT DriverObject,
|
||
IN ULONG DeviceId,
|
||
OUT PDEVICE_OBJECT *NewDeviceObject
|
||
);
|
||
|
||
NTSTATUS
|
||
ApmBattCompleteRequest(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN PVOID Context
|
||
);
|
||
|
||
|
||
NTSTATUS
|
||
ApmBattAddDevice(
|
||
IN PDRIVER_OBJECT DriverObject,
|
||
IN PDEVICE_OBJECT Pdo
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine creates functional device objects for each ApmBatt 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
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PAGED_CODE();
|
||
|
||
|
||
ApmBattPrint (APMBATT_TRACE, ("ApmBattAddDevice\n"));
|
||
ASSERT(DeviceCount == 0);
|
||
|
||
if (DeviceCount != 0) {
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
DeviceCount = 1;
|
||
|
||
ApmBattPrint ((APMBATT_TRACE | APMBATT_PNP), ("ApmBattAddDevice: 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
|
||
//
|
||
ApmBattPrint((APMBATT_WARN | APMBATT_PNP), ("ApmBattAddDevice: Asked to do detection\n"));
|
||
return STATUS_NO_MORE_ENTRIES;
|
||
|
||
} else {
|
||
//
|
||
// This device is a control-method battery
|
||
//
|
||
return (ApmBattAddBattery (DriverObject, Pdo));
|
||
}
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
ApmBattAddBattery(
|
||
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;
|
||
PDEVICE_OBJECT lowerDevice = NULL;
|
||
PCM_BATT ApmBatt;
|
||
NTSTATUS Status;
|
||
BATTERY_MINIPORT_INFO BattInit;
|
||
ULONG uniqueId;
|
||
PNTAPM_LINK pparms;
|
||
PIRP Irp;
|
||
PIO_STACK_LOCATION IrpSp;
|
||
|
||
PAGED_CODE();
|
||
|
||
ApmBattPrint ((APMBATT_TRACE | APMBATT_PNP), ("ApmBattAddBattery: pdo %x\n", Pdo));
|
||
//DbgBreakPoint();
|
||
|
||
uniqueId = 0;
|
||
|
||
//
|
||
// Create and initialize the new functional device object
|
||
//
|
||
Status = ApmBattCreateFdo(DriverObject, uniqueId, &Fdo);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
ApmBattPrint(APMBATT_ERROR, ("ApmBattAddBattery: error (0x%x) creating Fdo\n", Status));
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// Initialize Fdo device extension data
|
||
//
|
||
|
||
ApmBatt = (PCM_BATT) Fdo->DeviceExtension;
|
||
ApmBatt->Fdo = Fdo;
|
||
ApmBatt->Pdo = Pdo;
|
||
|
||
//
|
||
// Layer our FDO on top of the PDO
|
||
//
|
||
|
||
lowerDevice = IoAttachDeviceToDeviceStack(Fdo,Pdo);
|
||
|
||
//
|
||
// No status. Do the best we can.
|
||
//
|
||
if (!lowerDevice) {
|
||
ApmBattPrint(APMBATT_ERROR, ("ApmBattAddBattery: Could not attach to lower device\n"));
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
ApmBatt->LowerDeviceObject = lowerDevice;
|
||
|
||
//
|
||
// Attach to the Class Driver
|
||
//
|
||
|
||
RtlZeroMemory (&BattInit, sizeof(BattInit));
|
||
BattInit.MajorVersion = BATTERY_CLASS_MAJOR_VERSION;
|
||
BattInit.MinorVersion = BATTERY_CLASS_MINOR_VERSION;
|
||
BattInit.Context = ApmBatt;
|
||
BattInit.QueryTag = ApmBattQueryTag;
|
||
BattInit.QueryInformation = ApmBattQueryInformation;
|
||
BattInit.SetInformation = NULL; // tbd
|
||
BattInit.QueryStatus = ApmBattQueryStatus;
|
||
BattInit.SetStatusNotify = ApmBattSetStatusNotify;
|
||
BattInit.DisableStatusNotify = ApmBattDisableStatusNotify;
|
||
|
||
BattInit.Pdo = Pdo;
|
||
BattInit.DeviceName = ApmBatt->DeviceName;
|
||
|
||
Status = BatteryClassInitializeDevice (&BattInit, &ApmBatt->Class);
|
||
ApmGlobalClass = ApmBatt->Class;
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
//
|
||
// if we can't attach to class driver we're toast
|
||
//
|
||
ApmBattPrint(APMBATT_ERROR, ("ApmBattAddBattery: error (0x%x) registering with class\n", Status));
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// link up with APM driver (if we can't we're toast)
|
||
//
|
||
// Should be able to just call into Pdo.
|
||
//
|
||
// DO WORK HERE
|
||
//
|
||
Irp = IoAllocateIrp((CCHAR) (Pdo->StackSize+2), FALSE);
|
||
if (!Irp) {
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
IrpSp = IoGetNextIrpStackLocation(Irp);
|
||
IrpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
|
||
IrpSp->MinorFunction = 0;
|
||
IrpSp->DeviceObject = Pdo;
|
||
pparms = (PNTAPM_LINK) &(IrpSp->Parameters.Others);
|
||
pparms->Signature = NTAPM_LINK_SIGNATURE;
|
||
pparms->Version = NTAPM_LINK_VERSION;
|
||
pparms->BattLevelPtr = (ULONG)(&(NtApmGetBatteryLevel));
|
||
pparms->ChangeNotify = (ULONG)(&(ApmBattPowerNotifyHandler));
|
||
|
||
IoSetCompletionRoutine(Irp, ApmBattCompleteRequest, NULL, TRUE, TRUE, TRUE);
|
||
|
||
if (IoCallDriver(Pdo, Irp) != STATUS_SUCCESS) {
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
//DbgPrint("apmbatt: NtApmGetBatteryLevel: %08lx\n", NtApmGetBatteryLevel);
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
ApmBattCompleteRequest(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN PVOID Context
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Completion routine for ioctl call to apm.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - The target device which the request was sent
|
||
|
||
Irp - The irp completing
|
||
|
||
Context - The requestors completion routine
|
||
|
||
Return Value:
|
||
|
||
|
||
--*/
|
||
{
|
||
IoFreeIrp(Irp);
|
||
return STATUS_MORE_PROCESSING_REQUIRED;
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
ApmBattCreateFdo(
|
||
IN PDRIVER_OBJECT DriverObject,
|
||
IN ULONG DeviceId,
|
||
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
|
||
NewFdo - a location to store the pointer to the new device object
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS if everything was successful
|
||
reason for failure otherwise
|
||
|
||
--*/
|
||
|
||
{
|
||
PUNICODE_STRING unicodeString;
|
||
PDEVICE_OBJECT Fdo;
|
||
NTSTATUS Status;
|
||
PCM_BATT ApmBatt;
|
||
UNICODE_STRING numberString;
|
||
WCHAR numberBuffer[10];
|
||
|
||
PAGED_CODE();
|
||
|
||
ApmBattPrint ((APMBATT_TRACE | APMBATT_PNP), ("ApmBattCreateFdo, Battery Id=%x\n", DeviceId));
|
||
|
||
//
|
||
// Allocate the UNICODE_STRING for the device name
|
||
//
|
||
|
||
unicodeString = ExAllocatePoolWithTag (
|
||
PagedPool,
|
||
sizeof (UNICODE_STRING) + MAX_DEVICE_NAME_LENGTH,
|
||
'taBC'
|
||
);
|
||
|
||
if (!unicodeString) {
|
||
ApmBattPrint(APMBATT_ERROR, ("ApmBattCreateFdo: could not allocate unicode string\n"));
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
unicodeString->MaximumLength = MAX_DEVICE_NAME_LENGTH;
|
||
unicodeString->Length = 0;
|
||
unicodeString->Buffer = (PWCHAR) (unicodeString + 1);
|
||
|
||
//
|
||
// Create the PDO device name based on the battery instance
|
||
//
|
||
|
||
numberString.MaximumLength = 10;
|
||
numberString.Buffer = &numberBuffer[0];
|
||
|
||
RtlIntegerToUnicodeString (DeviceId, 10, &numberString);
|
||
RtlAppendUnicodeToString (unicodeString, (PWSTR) ApmBattDeviceName);
|
||
RtlAppendUnicodeToString (unicodeString, &numberString.Buffer[0]);
|
||
|
||
|
||
Status = IoCreateDevice(
|
||
DriverObject,
|
||
sizeof (CM_BATT),
|
||
unicodeString,
|
||
FILE_DEVICE_BATTERY,
|
||
0,
|
||
FALSE,
|
||
&Fdo
|
||
);
|
||
|
||
if (Status != STATUS_SUCCESS) {
|
||
ApmBattPrint(APMBATT_ERROR, ("ApmBattCreateFdo: 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;
|
||
Fdo->StackSize = 2;
|
||
|
||
//
|
||
// Initialize Fdo device extension data
|
||
//
|
||
|
||
ApmBatt = (PCM_BATT) Fdo->DeviceExtension;
|
||
RtlZeroMemory(ApmBatt, sizeof(CM_BATT));
|
||
ApmBatt->DeviceName = unicodeString;
|
||
ApmBatt->DeviceNumber = (USHORT) DeviceId;
|
||
ApmBatt->DeviceObject = Fdo;
|
||
*NewFdo = Fdo;
|
||
|
||
ApmBattPrint((APMBATT_TRACE | APMBATT_PNP), ("ApmBattCreateFdo: Created FDO %x\n", Fdo));
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
ApmBattPnpDispatch(
|
||
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 ApmBatt;
|
||
NTSTATUS Status;
|
||
|
||
PAGED_CODE();
|
||
|
||
ApmBattPrint (APMBATT_TRACE, ("ApmBattPnpDispatch\n"));
|
||
|
||
Status = STATUS_NOT_IMPLEMENTED;
|
||
|
||
Irp->IoStatus.Information = 0;
|
||
|
||
//
|
||
// Get a pointer to the current parameters for this request. The
|
||
// information is contained in the current stack location.
|
||
//
|
||
|
||
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
||
ApmBatt = DeviceObject->DeviceExtension;
|
||
|
||
//
|
||
// Dispatch minor function
|
||
//
|
||
switch (irpStack->MinorFunction) {
|
||
|
||
case IRP_MN_START_DEVICE:
|
||
//
|
||
// if the Add succeeded, we are actually started...
|
||
//
|
||
ApmBattPrint (APMBATT_PNP, ("ApmBattPnpDispatch: IRP_MN_START_DEVICE\n"));
|
||
Status = STATUS_SUCCESS;
|
||
Irp->IoStatus.Status = Status;
|
||
ApmBattCallLowerDriver(Status, ApmBatt->LowerDeviceObject, Irp);
|
||
break;
|
||
|
||
|
||
case IRP_MN_QUERY_DEVICE_RELATIONS:
|
||
ApmBattPrint (APMBATT_PNP, ("ApmBattPnpDispatch: IRP_MN_QUERY_DEVICE_RELATIONS - type (%d)\n",
|
||
irpStack->Parameters.QueryDeviceRelations.Type));
|
||
//
|
||
// Just pass it down
|
||
//
|
||
ApmBattCallLowerDriver(Status, ApmBatt->LowerDeviceObject, Irp);
|
||
break;
|
||
|
||
|
||
case IRP_MN_QUERY_STOP_DEVICE:
|
||
case IRP_MN_CANCEL_STOP_DEVICE:
|
||
case IRP_MN_QUERY_REMOVE_DEVICE:
|
||
case IRP_MN_CANCEL_REMOVE_DEVICE:
|
||
Status = Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED;
|
||
IoCompleteRequest(Irp, 0);
|
||
break;
|
||
|
||
default:
|
||
ApmBattPrint (APMBATT_PNP,
|
||
("ApmBattPnpDispatch: Unimplemented minor %0x\n",
|
||
irpStack->MinorFunction));
|
||
//
|
||
// Unimplemented minor, Pass this down to ACPI
|
||
//
|
||
ApmBattCallLowerDriver(Status, ApmBatt->LowerDeviceObject, Irp);
|
||
break;
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
ApmBattPowerDispatch(
|
||
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 ApmBatt;
|
||
NTSTATUS Status;
|
||
|
||
PAGED_CODE();
|
||
|
||
ApmBattPrint ((APMBATT_TRACE | APMBATT_POWER), ("ApmBattPowerDispatch\n"));
|
||
|
||
//
|
||
// Get a pointer to the current parameters for this request. The
|
||
// information is contained in the current stack location.
|
||
//
|
||
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
||
ApmBatt = DeviceObject->DeviceExtension;
|
||
|
||
//
|
||
// Dispatch minor function
|
||
//
|
||
switch (irpStack->MinorFunction) {
|
||
|
||
case IRP_MN_WAIT_WAKE:
|
||
ApmBattPrint (APMBATT_POWER, ("ApmBattPowerDispatch: IRP_MN_WAIT_WAKE\n"));
|
||
break;
|
||
|
||
case IRP_MN_POWER_SEQUENCE:
|
||
ApmBattPrint (APMBATT_POWER, ("ApmBattPowerDispatch: IRP_MN_POWER_SEQUENCE\n"));
|
||
break;
|
||
|
||
case IRP_MN_SET_POWER:
|
||
ApmBattPrint (APMBATT_POWER, ("ApmBattPowerDispatch: IRP_MN_SET_POWER\n"));
|
||
TagValue++;
|
||
break;
|
||
|
||
case IRP_MN_QUERY_POWER:
|
||
ApmBattPrint (APMBATT_POWER, ("ApmBattPowerDispatch: IRP_MN_QUERY_POWER\n"));
|
||
break;
|
||
|
||
default:
|
||
ApmBattPrint(APMBATT_LOW, ("ApmBattPowerDispatch: minor %d\n",
|
||
irpStack->MinorFunction));
|
||
break;
|
||
}
|
||
|
||
//
|
||
// What do we do with the irp?
|
||
//
|
||
PoStartNextPowerIrp( Irp );
|
||
if (ApmBatt->LowerDeviceObject != NULL) {
|
||
|
||
//
|
||
// Forward the request along
|
||
//
|
||
IoSkipCurrentIrpStackLocation( Irp );
|
||
Status = PoCallDriver( ApmBatt->LowerDeviceObject, Irp );
|
||
|
||
} else {
|
||
|
||
//
|
||
// Complete the request with the current status
|
||
//
|
||
Status = Irp->IoStatus.Status;
|
||
IoCompleteRequest( Irp, IO_NO_INCREMENT );
|
||
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|