windows-nt/Source/XPSP1/NT/drivers/apm/ntapm/i386/apmpnp.c
2020-09-26 16:20:57 +08:00

767 lines
20 KiB
C

/*++
Copyright (c) 1997-1998 Microsoft Corporation
Module Name:
apmpnp.c
Abstract:
This module contains contains the plugplay calls
needed to make ntapm.sys work.
Author:
Bryan Willman
Kenneth D. Ray
Doron J. Holan
Environment:
kernel mode only
Notes:
Revision History:
--*/
#include <wdm.h>
#include "ntapmp.h"
#include "ntapmdbg.h"
#include "ntapm.h"
//#include "stdio.h"
//
// Globals
//
PDEVICE_OBJECT NtApm_ApmBatteryPdo = NULL;
#ifdef ALLOC_PRAGMA
#pragma alloc_text (PAGE, NtApm_AddDevice)
#pragma alloc_text (PAGE, NtApm_PnP)
#pragma alloc_text (PAGE, NtApm_FDO_PnP)
#pragma alloc_text (PAGE, NtApm_PDO_PnP)
#pragma alloc_text (PAGE, NtApm_Power)
#pragma alloc_text (PAGE, NtApm_FDO_Power)
#pragma alloc_text (PAGE, NtApm_PDO_Power)
#pragma alloc_text (PAGE, NtApm_CreatePdo)
#pragma alloc_text (PAGE, NtApm_InitializePdo)
#endif
NTSTATUS
NtApm_AddDevice(
IN PDRIVER_OBJECT DriverObject,
IN PDEVICE_OBJECT BusPhysicalDeviceObject
)
/*++
Routine Description.
A bus has been found. Attach our FDO to it.
Allocate any required resources. Set things up. And be prepared for the
first ``start device.''
Arguments:
BusDeviceObject - Device object representing the bus. That to which we
attach a new FDO.
DriverObject - This very self referenced driver.
--*/
{
NTSTATUS status;
PDEVICE_OBJECT deviceObject;
PFDO_DEVICE_DATA deviceData;
UNICODE_STRING deviceNameUni;
PWCHAR deviceName;
ULONG nameLength;
PAGED_CODE ();
DrDebug(PNP_INFO, ("ntapm Add Device: 0x%x\n", BusPhysicalDeviceObject));
status = IoCreateDevice (
DriverObject, // our driver object
sizeof (FDO_DEVICE_DATA), // device object extension size
NULL, // FDOs do not have names
FILE_DEVICE_BUS_EXTENDER,
0, // No special characteristics
TRUE, // our FDO is exclusive
&deviceObject); // The device object created
if (!NT_SUCCESS (status)) {
return status;
}
deviceData = (PFDO_DEVICE_DATA) deviceObject->DeviceExtension;
RtlFillMemory (deviceData, sizeof (FDO_DEVICE_DATA), 0);
deviceData->IsFDO = TRUE;
deviceData->Self = deviceObject;
deviceData->UnderlyingPDO = BusPhysicalDeviceObject;
//
// Attach our filter driver to the device stack.
// the return value of IoAttachDeviceToDeviceStack is the top of the
// attachment chain. This is where all the IRPs should be routed.
//
// Our filter will send IRPs to the top of the stack and use the PDO
// for all PlugPlay functions.
//
deviceData->TopOfStack = IoAttachDeviceToDeviceStack (
deviceObject,
BusPhysicalDeviceObject
);
if (!deviceData->TopOfStack) {
IoDeleteDevice(deviceObject);
return STATUS_UNSUCCESSFUL;
}
status = ApmAddHelper();
if (!NT_SUCCESS(status)) {
IoDetachDevice(deviceData->TopOfStack);
IoDeleteDevice(deviceObject);
}
deviceObject->Flags |= DO_POWER_PAGABLE;
deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
return status;
}
NTSTATUS
NtApm_FDO_PnPComplete (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Pirp,
IN PVOID Context
);
NTSTATUS
NtApm_PnP (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
Answer the plithera of Irp Major PnP IRPS.
--*/
{
PIO_STACK_LOCATION irpStack;
NTSTATUS status;
PCOMMON_DEVICE_DATA commonData;
KIRQL oldIrq;
PAGED_CODE ();
status = STATUS_SUCCESS;
irpStack = IoGetCurrentIrpStackLocation (Irp);
ASSERT (IRP_MJ_PNP == irpStack->MajorFunction);
commonData = (PCOMMON_DEVICE_DATA) DeviceObject->DeviceExtension;
if (commonData->IsFDO) {
DrDebug(PNP_INFO, ("ntapm PNP: Functional DO: %x IRP: %x\n", DeviceObject, Irp));
status = NtApm_FDO_PnP (
DeviceObject,
Irp,
irpStack,
(PFDO_DEVICE_DATA) commonData);
} else {
DrDebug(PNP_INFO, ("ntapm: PNP: Physical DO: %x IRP: %x\n", DeviceObject, Irp));
status = NtApm_PDO_PnP (
DeviceObject,
Irp,
irpStack,
(PPDO_DEVICE_DATA) commonData);
}
return status;
}
NTSTATUS
NtApm_FDO_PnP (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpStack,
IN PFDO_DEVICE_DATA DeviceData
)
/*++
Routine Description:
Handle requests from the PlugPlay system for the BUS itself
NB: the various Minor functions of the PlugPlay system will not be
overlapped and do not have to be reentrant
--*/
{
NTSTATUS status;
KIRQL irql;
KEVENT event;
ULONG length;
ULONG i;
PLIST_ENTRY entry;
PPDO_DEVICE_DATA pdoData;
PDEVICE_RELATIONS relations;
PIO_STACK_LOCATION stack;
ULONG battresult;
PAGED_CODE ();
stack = IoGetCurrentIrpStackLocation (Irp);
switch (IrpStack->MinorFunction) {
case IRP_MN_START_DEVICE:
//
// BEFORE you are allowed to ``touch'' the device object to which
// the FDO is attached (that send an irp from the bus to the Device
// object to which the bus is attached). You must first pass down
// the start IRP. It might not be powered on, or able to access or
// something.
//
DrDebug(PNP_INFO, ("ntapm: Start Device\n"));
KeInitializeEvent (&event, NotificationEvent, FALSE);
IoCopyCurrentIrpStackLocationToNext (Irp);
IoSetCompletionRoutine (Irp,
NtApm_FDO_PnPComplete,
&event,
TRUE,
TRUE,
TRUE);
status = IoCallDriver (DeviceData->TopOfStack, Irp);
if (STATUS_PENDING == status) {
// wait for it...
status = KeWaitForSingleObject (&event,
Executive,
KernelMode,
FALSE, // Not allertable
NULL); // No timeout structure
ASSERT (STATUS_SUCCESS == status);
status = Irp->IoStatus.Status;
}
//
// We must now complete the IRP, since we stopped it in the
// completetion routine with MORE_PROCESSING_REQUIRED.
//
break;
case IRP_MN_QUERY_DEVICE_RELATIONS:
if (IrpStack->Parameters.QueryDeviceRelations.Type != BusRelations) {
//
// We don't support this
//
goto NtApm_FDO_PNP_DEFAULT;
}
//
// In theory, APM should be fired up by now.
// So call off into it to see if there is any sign
// of a battery on the box. If there is NOT, don't
// export the PDOs for the battery objects
//
battresult = DoApmReportBatteryStatus();
if (battresult & NTAPM_NO_SYS_BATT) {
//
// it appears that the machine does not have
// a battery. so don't export battery driver PDOs.
//
Irp->IoStatus.Status = STATUS_SUCCESS;
IoSkipCurrentIrpStackLocation(Irp);
return IoCallDriver(DeviceData->TopOfStack, Irp);
}
DrDebug(PNP_INFO, ("ntapm: Query Relations "));
//
// create PDO for apm battery
//
if (NtApm_ApmBatteryPdo == NULL) {
status = NtApm_CreatePdo(
DeviceData,
NTAPM_PDO_NAME_APM_BATTERY,
&NtApm_ApmBatteryPdo
);
if (!NT_SUCCESS(status)) {
goto NtApm_DONE;
}
}
NtApm_InitializePdo(NtApm_ApmBatteryPdo, DeviceData, NTAPM_ID_APM_BATTERY);
//
// Tell PNP about our two child PDOs.
//
i = (Irp->IoStatus.Information == 0) ? 0 :
((PDEVICE_RELATIONS) Irp->IoStatus.Information)->Count;
//
// above should be count of PDOs
// make a new structure and our PDO to the end
//
//
// Need to allocate a new relations structure and add our
// PDOs to it.
//
length = sizeof(DEVICE_RELATIONS) + ((i + 1) * sizeof (PDEVICE_OBJECT));
relations = (PDEVICE_RELATIONS) ExAllocatePool (NonPagedPool, length);
if (relations == NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto NtApm_DONE;
}
//
// Copy in the device objects so far
//
if (i) {
RtlCopyMemory (
relations->Objects,
((PDEVICE_RELATIONS) Irp->IoStatus.Information)->Objects,
i * sizeof (PDEVICE_OBJECT));
}
relations->Count = i + 1;
//
// add the apm battery PDO to the list
//
ObReferenceObject(NtApm_ApmBatteryPdo);
relations->Objects[i] = NtApm_ApmBatteryPdo;
//
// Replace the relations structure in the IRP with the new
// one.
//
if (Irp->IoStatus.Information != 0) {
ExFreePool ((PVOID) Irp->IoStatus.Information);
}
Irp->IoStatus.Information = (ULONG) relations;
//
// Set up and pass the IRP further down the stack
//
Irp->IoStatus.Status = STATUS_SUCCESS;
IoSkipCurrentIrpStackLocation (Irp);
status = IoCallDriver (DeviceData->TopOfStack, Irp);
return status;
break;
case IRP_MN_QUERY_STOP_DEVICE:
case IRP_MN_STOP_DEVICE:
case IRP_MN_REMOVE_DEVICE:
case IRP_MN_QUERY_REMOVE_DEVICE:
status = STATUS_UNSUCCESSFUL;
break;
case IRP_MN_CANCEL_REMOVE_DEVICE:
case IRP_MN_CANCEL_STOP_DEVICE:
Irp->IoStatus.Status = STATUS_SUCCESS; // we're lying, it's more like noop
IoSkipCurrentIrpStackLocation(Irp);
return IoCallDriver (DeviceData->TopOfStack, Irp);
break;
NtApm_FDO_PNP_DEFAULT:
default:
//
// In the default case we merely call the next driver since
// we don't know what to do.
//
IoSkipCurrentIrpStackLocation (Irp);
return IoCallDriver (DeviceData->TopOfStack, Irp);
}
NtApm_DONE:
Irp->IoStatus.Status = status;
IoCompleteRequest (Irp, IO_NO_INCREMENT);
return status;
}
NTSTATUS
NtApm_FDO_PnPComplete (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
/*++
Routine Description:
A completion routine for use when calling the lower device objects to
which our bus (FDO) is attached.
--*/
{
UNREFERENCED_PARAMETER (DeviceObject);
UNREFERENCED_PARAMETER (Irp);
KeSetEvent ((PKEVENT) Context, 1, FALSE);
// No special priority
// No Wait
return STATUS_MORE_PROCESSING_REQUIRED; // Keep this IRP
}
NTSTATUS
NtApm_PDO_PnP (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpStack,
IN PPDO_DEVICE_DATA DeviceData
)
/*++
Routine Description:
Handle requests from the PlugPlay system for the devices on the BUS
--*/
{
PDEVICE_CAPABILITIES deviceCapabilities;
ULONG information;
PWCHAR buffer, buffer2;
ULONG length, length2, i, j;
NTSTATUS status;
KIRQL oldIrq;
PDEVICE_RELATIONS relations;
PAGED_CODE ();
status = Irp->IoStatus.Status;
//
// NB: since we are a bus enumerator, we have no one to whom we could
// defer these irps. Therefore we do not pass them down but merely
// return them.
//
switch (IrpStack->MinorFunction) {
case IRP_MN_QUERY_CAPABILITIES:
DrDebug(PNP_INFO, ("ntapm: Query Caps \n"));
//
// Get the packet.
//
deviceCapabilities = IrpStack->Parameters.DeviceCapabilities.Capabilities;
deviceCapabilities->UniqueID = FALSE;
status = STATUS_SUCCESS;
break;
case IRP_MN_QUERY_ID:
// Query the IDs of the device
DrDebug(PNP_INFO, ("ntapm: QueryID: 0x%x\n", IrpStack->Parameters.QueryId.IdType));
switch (IrpStack->Parameters.QueryId.IdType) {
case BusQueryDeviceID:
// this can be the same as the hardware ids (which requires a multi
// sz) ... we are just allocating more than enough memory
case BusQueryHardwareIDs:
// return a multi WCHAR (null terminated) string (null terminated)
// array for use in matching hardare ids in inf files;
//
buffer = DeviceData->HardwareIDs;
while (*(buffer++)) {
while (*(buffer++)) {
;
}
}
length = (buffer - DeviceData->HardwareIDs) * sizeof (WCHAR);
buffer = ExAllocatePool (PagedPool, length);
if (buffer) {
RtlCopyMemory (buffer, DeviceData->HardwareIDs, length);
Irp->IoStatus.Information = (ULONG) buffer;
status = STATUS_SUCCESS;
} else {
status = STATUS_INSUFFICIENT_RESOURCES;
}
break;
case BusQueryInstanceID:
//
// Build an instance ID. This is what PnP uses to tell if it has
// seen this thing before or not.
//
//
// return 0000 for all devices and have the flag set to not unique
//
length = APM_INSTANCE_IDS_LENGTH * sizeof(WCHAR);
buffer = ExAllocatePool(PagedPool, length);
if (buffer != NULL) {
RtlCopyMemory(buffer, APM_INSTANCE_IDS, length);
Irp->IoStatus.Information = (ULONG_PTR)buffer;
status = STATUS_SUCCESS;
} else {
status = STATUS_INSUFFICIENT_RESOURCES;
}
break;
case BusQueryCompatibleIDs:
// The generic ids for installation of this pdo.
break;
}
break;
case IRP_MN_START_DEVICE:
DrDebug(PNP_INFO, ("ntapm: Start Device \n"));
// Here we do what ever initialization and ``turning on'' that is
// required to allow others to access this device.
status = STATUS_SUCCESS;
break;
case IRP_MN_STOP_DEVICE:
case IRP_MN_REMOVE_DEVICE:
case IRP_MN_QUERY_STOP_DEVICE:
case IRP_MN_QUERY_REMOVE_DEVICE:
DrDebug(PNP_INFO, ("ntapm: remove, stop, or Q remove or Q stop\n"));
//
// disallow Stop or Remove, since we don't want to test
// disengagement from APM if we don't have to
//
status = STATUS_UNSUCCESSFUL;
break;
case IRP_MN_CANCEL_STOP_DEVICE:
case IRP_MN_CANCEL_REMOVE_DEVICE:
DrDebug(PNP_INFO, ("ntapm: Cancel Stop Device or Cancel Remove \n"));
status = STATUS_SUCCESS; // more like "noop" than success
break;
case IRP_MN_QUERY_RESOURCE_REQUIREMENTS:
break;
case IRP_MN_QUERY_DEVICE_RELATIONS:
if (IrpStack->Parameters.QueryDeviceRelations.Type != TargetDeviceRelation) {
//
// Somebody else can handle this.
//
break;
}
ASSERT(((PULONG_PTR)Irp->IoStatus.Information) == NULL);
relations = (PDEVICE_RELATIONS) ExAllocatePool(PagedPool, sizeof(DEVICE_RELATIONS));
if (relations == NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
} else {
Irp->IoStatus.Information = (ULONG_PTR) relations;
relations->Count = 1;
relations->Objects[0] = DeviceObject;
ObReferenceObject(DeviceObject);
status = STATUS_SUCCESS;
}
break;
case IRP_MN_READ_CONFIG:
case IRP_MN_WRITE_CONFIG: // we have no config space
case IRP_MN_EJECT:
case IRP_MN_SET_LOCK:
case IRP_MN_QUERY_INTERFACE: // We do not have any non IRP based interfaces.
default:
DrDebug(PNP_INFO, ("ntapm: PNP Not handled 0x%x\n", IrpStack->MinorFunction));
// this is a leaf node
// status = STATUS_NOT_IMPLEMENTED
// For PnP requests to the PDO that we do not understand we should
// return the IRP WITHOUT setting the status or information fields.
// They may have already been set by a filter (eg acpi).
break;
}
Irp->IoStatus.Status = status;
IoCompleteRequest (Irp, IO_NO_INCREMENT);
return status;
}
NTSTATUS
NtApm_CreatePdo (
PFDO_DEVICE_DATA FdoData,
PWCHAR PdoName,
PDEVICE_OBJECT * PDO
)
{
UNICODE_STRING pdoUniName;
NTSTATUS status;
PAGED_CODE ();
//DbgBreakPoint();
//
// Create the PDOs
//
RtlInitUnicodeString (&pdoUniName, PdoName);
DrDebug(PNP_INFO, ("ntapm: CreatePdo: PDO Name: %ws\n", PdoName));
status = IoCreateDevice(
FdoData->Self->DriverObject,
sizeof (PDO_DEVICE_DATA),
&pdoUniName,
FILE_DEVICE_BUS_EXTENDER,
0,
FALSE,
PDO
);
DrDebug(PNP_L2, ("ntapm: CreatePdo: status = %08lx\n", status));
if (!NT_SUCCESS (status)) {
*PDO = NULL;
}
return status;
}
VOID
NtApm_InitializePdo(
PDEVICE_OBJECT Pdo,
PFDO_DEVICE_DATA FdoData,
PWCHAR Id
)
{
PPDO_DEVICE_DATA pdoData;
PAGED_CODE ();
pdoData = (PPDO_DEVICE_DATA) Pdo->DeviceExtension;
DrDebug(PNP_INFO, ("ntapm: pdo 0x%x, extension 0x%x\n", Pdo, pdoData));
//
// Initialize the rest
//
pdoData->IsFDO = FALSE;
pdoData->Self = Pdo;
pdoData->ParentFdo = FdoData->Self;
pdoData->HardwareIDs = Id;
pdoData->UniqueID = 1;
Pdo->Flags &= ~DO_DEVICE_INITIALIZING;
Pdo->Flags |= DO_POWER_PAGABLE;
}
NTSTATUS
NtApm_Power (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
We do nothing special for power;
--*/
{
PIO_STACK_LOCATION irpStack;
NTSTATUS status;
PCOMMON_DEVICE_DATA commonData;
PAGED_CODE ();
status = STATUS_SUCCESS;
irpStack = IoGetCurrentIrpStackLocation (Irp);
ASSERT (IRP_MJ_POWER == irpStack->MajorFunction);
commonData = (PCOMMON_DEVICE_DATA) DeviceObject->DeviceExtension;
if (commonData->IsFDO) {
status = NtApm_FDO_Power ((PFDO_DEVICE_DATA) DeviceObject->DeviceExtension,Irp);
} else {
status = NtApm_PDO_Power ((PPDO_DEVICE_DATA) DeviceObject->DeviceExtension,Irp);
}
return status;
}
NTSTATUS
NtApm_FDO_Power (
PFDO_DEVICE_DATA Data,
PIRP Irp
)
{
PIO_STACK_LOCATION stack;
PAGED_CODE ();
PoStartNextPowerIrp(Irp);
IoSkipCurrentIrpStackLocation(Irp);
return PoCallDriver(Data->TopOfStack, Irp);
}
NTSTATUS
NtApm_PDO_Power (
PPDO_DEVICE_DATA PdoData,
PIRP Irp
)
{
NTSTATUS status = STATUS_SUCCESS;
PIO_STACK_LOCATION stack;
stack = IoGetCurrentIrpStackLocation (Irp);
switch (stack->MinorFunction) {
case IRP_MN_SET_POWER:
if ((stack->Parameters.Power.Type == SystemPowerState) &&
(stack->Parameters.Power.State.SystemState == PowerSystemWorking))
{
//
// system has just returned to the working state
// assert the user is present (they must be for the APM case)
// so that the display will light up, idle timers behave, etc.
//
PoSetSystemState(ES_USER_PRESENT);
}
status = STATUS_SUCCESS;
break;
case IRP_MN_QUERY_POWER:
status = STATUS_SUCCESS;
break;
case IRP_MN_WAIT_WAKE:
case IRP_MN_POWER_SEQUENCE:
default:
status = Irp->IoStatus.Status;
break;
}
Irp->IoStatus.Status = status;
PoStartNextPowerIrp(Irp);
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
}