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