sync with trunk r46493
[reactos.git] / drivers / bus / acpi / cmbatt / cmbpnp.c
index 3a56dce..55d3ae6 100644 (file)
 
 /* FUNCTIONS ******************************************************************/
 
+VOID
+NTAPI
+CmBattWaitWakeLoop(IN PDEVICE_OBJECT DeviceObject,
+                   IN UCHAR MinorFunction,
+                   IN POWER_STATE PowerState,
+                   IN PVOID Context,
+                   IN PIO_STATUS_BLOCK IoStatusBlock)
+{
+    NTSTATUS Status;
+    PCMBATT_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
+    if (CmBattDebug & 0x20) DbgPrint("CmBattWaitWakeLoop: Entered.\n");
+    
+    /* Check for success */
+    if ((NT_SUCCESS(IoStatusBlock->Status)) && (DeviceExtension->WaitWakeEnable))
+    {
+        /* Request a new power IRP */
+        if (CmBattDebug & 2) DbgPrint("CmBattWaitWakeLoop: completed successfully\n");
+        Status = PoRequestPowerIrp(DeviceObject,
+                                   MinorFunction,
+                                   PowerState,
+                                   CmBattWaitWakeLoop,
+                                   Context,
+                                   &DeviceExtension->PowerIrp);
+        if (CmBattDebug & 2)
+            DbgPrint("CmBattWaitWakeLoop: PoRequestPowerIrp: status = 0x%08x.\n",
+                     Status);
+    }
+    else
+    {
+        /* Clear the power IRP, we failed */
+        if (CmBattDebug & 0xC)
+            DbgPrint("CmBattWaitWakeLoop: failed: status = 0x%08x.\n",
+                     IoStatusBlock->Status);
+        DeviceExtension->PowerIrp = NULL;
+    }
+}
 NTSTATUS
 NTAPI
-CmBattIoCompletion(PDEVICE_OBJECT DeviceObject,
-                   PIRP Irp,
-                   PKEVENT Event)
+CmBattIoCompletion(IN PDEVICE_OBJECT DeviceObject,
+                   IN PIRP Irp,
+                   IN PKEVENT Event)
 {
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
+    if (CmBattDebug & 2) DbgPrint("CmBattIoCompletion: Event (%x)\n", Event);
+
+    /* Set the completion event */
+    KeSetEvent(Event, IO_NO_INCREMENT, FALSE);
+    return STATUS_MORE_PROCESSING_REQUIRED;
 }
 
 NTSTATUS
 NTAPI
-CmBattGetAcpiInterfaces(PDEVICE_OBJECT DeviceObject,
-                        PACPI_INTERFACE_STANDARD2 *AcpiInterface)
+CmBattGetAcpiInterfaces(IN PDEVICE_OBJECT DeviceObject,
+                        IN OUT PACPI_INTERFACE_STANDARD AcpiInterface)
 {
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
+    PIRP Irp;
+    NTSTATUS Status;
+    PIO_STACK_LOCATION IoStackLocation;
+    KEVENT Event;
+
+    /* Allocate the IRP */
+    Irp = IoAllocateIrp(DeviceObject->StackSize, 0);
+    if (!Irp)
+    {
+        /* Fail */
+        if (CmBattDebug & 0xC)
+          DbgPrint("CmBattGetAcpiInterfaces: Failed to allocate Irp\n");
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    /* Set default error code */
+    Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
+
+    /* Build the query */
+    IoStackLocation = IoGetNextIrpStackLocation(Irp);
+    IoStackLocation->MinorFunction = IRP_MN_QUERY_INTERFACE;
+    IoStackLocation->Parameters.QueryInterface.InterfaceType = &GUID_ACPI_INTERFACE_STANDARD;
+    IoStackLocation->Parameters.QueryInterface.Size = sizeof(ACPI_INTERFACE_STANDARD);
+    IoStackLocation->Parameters.QueryInterface.Version = 1;
+    IoStackLocation->Parameters.QueryInterface.Interface = (PINTERFACE)AcpiInterface;
+    IoStackLocation->Parameters.QueryInterface.InterfaceSpecificData = NULL;
+
+    /* Set default ACPI interface data */
+    AcpiInterface->Size = sizeof(ACPI_INTERFACE_STANDARD);
+    AcpiInterface->Version = 1;
+
+    /* Initialize our wait event */
+    KeInitializeEvent(&Event, SynchronizationEvent, 0);
+    
+    /* Set the completion routine */
+    IoCopyCurrentIrpStackLocationToNext(Irp);
+    IoSetCompletionRoutine(Irp,
+                           (PVOID)CmBattIoCompletion,
+                           &Event,
+                           TRUE,
+                           TRUE,
+                           TRUE);
+    /* Now call ACPI */
+    Status = IoCallDriver(DeviceObject, Irp);
+    if (Status == STATUS_PENDING)
+    {
+        /* Wait for completion */
+        KeWaitForSingleObject(&Event,
+                              Executive,
+                              KernelMode,
+                              FALSE,
+                              NULL);
+        Status = Irp->IoStatus.Status;
+    }
+    
+    /* Free the IRP */
+    IoFreeIrp(Irp);
+    
+    /* Return status */
+    if (!(NT_SUCCESS(Status)) && (CmBattDebug & 0xC))
+        DbgPrint("CmBattGetAcpiInterfaces: Could not get ACPI driver interfaces, status = %x\n", Status);
+    return Status;
 }
 
 VOID
 NTAPI
-CmBattDestroyFdo(PDEVICE_OBJECT DeviceObject)
+CmBattDestroyFdo(IN PDEVICE_OBJECT DeviceObject)
 {
-    UNIMPLEMENTED;
+    PAGED_CODE();
+    if (CmBattDebug & 0x220) DbgPrint("CmBattDestroyFdo, Battery.\n");
+
+    /* Delete the device */
+    IoDeleteDevice(DeviceObject);
+    if (CmBattDebug & 0x220) DbgPrint("CmBattDestroyFdo: done.\n");
 }
 
 NTSTATUS
 NTAPI
-CmBattRemoveDevice(PDEVICE_OBJECT DeviceObject,
-                   PIRP Irp)
+CmBattRemoveDevice(IN PDEVICE_OBJECT DeviceObject,
+                   IN PIRP Irp)
 {
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
+    PCMBATT_DEVICE_EXTENSION DeviceExtension;
+    PVOID Context;
+    DeviceExtension = DeviceObject->DeviceExtension;
+    if (CmBattDebug & 2)
+        DbgPrint("CmBattRemoveDevice: CmBatt (%x), Type %d, _UID %d\n",
+                 DeviceExtension,
+                 DeviceExtension->FdoType,
+                 DeviceExtension->DeviceId);
+                 
+    /* Make sure it's safe to go ahead */
+    IoReleaseRemoveLockAndWait(&DeviceExtension->RemoveLock, 0);
+
+    /* Check for pending power IRP */
+    if (DeviceExtension->PowerIrp)
+    {
+        /* Cancel and clear */
+        IoCancelIrp(DeviceExtension->PowerIrp);
+        DeviceExtension->PowerIrp = NULL;
+    }
+    
+    /* Check what type of FDO is being removed */
+    Context = DeviceExtension->AcpiInterface.Context;
+    if (DeviceExtension->FdoType == CmBattBattery)
+    {
+        /* Unregister battery FDO */
+        DeviceExtension->AcpiInterface.UnregisterForDeviceNotifications(Context,
+                                                                        (PVOID)CmBattNotifyHandler);
+        CmBattWmiDeRegistration(DeviceExtension);
+        if (!NT_SUCCESS(BatteryClassUnload(DeviceExtension->ClassData))) ASSERT(FALSE);
+    }
+    else
+    {
+        /* Unregister AC adapter FDO */
+        DeviceExtension->AcpiInterface.UnregisterForDeviceNotifications(Context,
+                                                                        (PVOID)CmBattNotifyHandler);
+        CmBattWmiDeRegistration(DeviceExtension);
+        AcAdapterPdo = NULL;
+    }
+    
+    /* Detach and delete */
+    IoDetachDevice(DeviceExtension->AttachedDevice);
+    IoDeleteDevice(DeviceExtension->DeviceObject);
+    return STATUS_SUCCESS;
 }
 
 NTSTATUS
 NTAPI
-CmBattPowerDispatch(PDEVICE_OBJECT DeviceObject,
-                    PIRP Irp)
+CmBattPowerDispatch(IN PDEVICE_OBJECT DeviceObject,
+                    IN PIRP Irp)
 {
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
+    PIO_STACK_LOCATION IoStackLocation;
+    PCMBATT_DEVICE_EXTENSION DeviceExtension;
+    NTSTATUS Status;
+    if (CmBattDebug & 0x210) DbgPrint("CmBattPowerDispatch\n");
+
+    /* Get stack location and device extension */
+    IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
+    DeviceExtension = DeviceObject->DeviceExtension;
+    switch (IoStackLocation->MinorFunction)
+    {
+        case IRP_MN_WAIT_WAKE:        
+            if (CmBattDebug & 0x10)
+                DbgPrint("CmBattPowerDispatch: IRP_MN_WAIT_WAKE\n");
+            break;
+        
+        case IRP_MN_POWER_SEQUENCE:
+            if (CmBattDebug & 0x10)
+                DbgPrint("CmBattPowerDispatch: IRP_MN_POWER_SEQUENCE\n");
+            break;
+        
+        case IRP_MN_QUERY_POWER:
+            if (CmBattDebug & 0x10)
+                DbgPrint("CmBattPowerDispatch: IRP_MN_WAIT_WAKE\n");
+            break;
+        
+        case IRP_MN_SET_POWER:
+            if (CmBattDebug & 0x10)
+                DbgPrint("CmBattPowerDispatch: IRP_MN_SET_POWER type: %d, State: %d \n",
+                         IoStackLocation->Parameters.Power.Type,
+                         IoStackLocation->Parameters.Power.State);
+            break;
+            
+        default:
+        
+            if (CmBattDebug & 1)
+                DbgPrint("CmBattPowerDispatch: minor %d\n", IoStackLocation->MinorFunction);
+            break;
+    }
+
+    /* Start the next IRP and see if we're attached */
+    PoStartNextPowerIrp(Irp);
+    if (DeviceExtension->AttachedDevice)
+    {
+        /* Call ACPI */
+        IoSkipCurrentIrpStackLocation(Irp);
+        Status = PoCallDriver(DeviceExtension->AttachedDevice, Irp);
+    }
+    else
+    {
+        /* Complete the request here */
+        Status = Irp->IoStatus.Status;
+        IofCompleteRequest(Irp, IO_NO_INCREMENT);
+    }
+    
+    /* Return status */
+    return Status;
 }
 
 NTSTATUS
 NTAPI
-CmBattCreateFdo(PDRIVER_OBJECT DriverObject,
-                PDEVICE_OBJECT DeviceObject,
-                ULONG DeviceExtensionSize,
-                PDEVICE_OBJECT *NewDeviceObject)
+CmBattPnpDispatch(IN PDEVICE_OBJECT DeviceObject,
+                  IN PIRP Irp)
 {
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
+    PIO_STACK_LOCATION IoStackLocation;
+    PCMBATT_DEVICE_EXTENSION DeviceExtension;
+    NTSTATUS Status;
+    KEVENT Event;
+    PAGED_CODE();
+
+    /* Get stack location and device extension */
+    IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
+    DeviceExtension = DeviceObject->DeviceExtension;
+    
+    /* Set default error */
+    Status = STATUS_NOT_SUPPORTED;
+
+    /* Try to acquire the lock before doing anything */
+    Status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, 0);
+    if (!NT_SUCCESS(Status))
+    {
+        /* Complete the request */
+        Irp->IoStatus.Status = STATUS_DEVICE_REMOVED;
+        IofCompleteRequest(Irp, IO_NO_INCREMENT);
+        return STATUS_DEVICE_REMOVED;
+    }
+
+    /* What's the operation? */
+    switch (IoStackLocation->MinorFunction)
+    {            
+        case IRP_MN_QUERY_PNP_DEVICE_STATE:
+        
+            /* Initialize our wait event */
+            KeInitializeEvent(&Event, SynchronizationEvent, 0);
+
+            /* Set the completion routine */
+            IoCopyCurrentIrpStackLocationToNext(Irp);
+            IoSetCompletionRoutine(Irp,
+                                   (PVOID)CmBattIoCompletion,
+                                   &Event,
+                                   TRUE,
+                                   TRUE,
+                                   TRUE);
+
+            /* Now call ACPI to inherit its PnP Device State */
+            Status = IoCallDriver(DeviceObject, Irp);
+            if (Status == STATUS_PENDING)
+            {
+                /* Wait for completion */
+                KeWaitForSingleObject(&Event,
+                                      Executive,
+                                      KernelMode,
+                                      FALSE,
+                                      NULL);
+                Status = Irp->IoStatus.Status;
+            }
+
+            /* However, a battery CAN be disabled */
+            Irp->IoStatus.Information &= ~PNP_DEVICE_NOT_DISABLEABLE;
+
+            /* Release the remove lock and complete the request */
+            IoCompleteRequest(Irp, IO_NO_INCREMENT);
+            IoReleaseRemoveLock(&DeviceExtension->RemoveLock, Irp);
+            return Status;
+        
+        case IRP_MN_SURPRISE_REMOVAL:
+            if (CmBattDebug & 0x20)
+                DbgPrint("CmBattPnpDispatch: IRP_MN_SURPRISE_REMOVAL\n");
+                
+            /* Lock the device extension and set the handle count to invalid */
+            ExAcquireFastMutex(&DeviceExtension->FastMutex);
+            DeviceExtension->HandleCount = -1;
+            ExReleaseFastMutex(&DeviceExtension->FastMutex);
+            Status = STATUS_SUCCESS;
+            break;
+        
+        case IRP_MN_START_DEVICE:
+            if (CmBattDebug & 0x20)
+                DbgPrint("CmBattPnpDispatch: IRP_MN_START_DEVICE\n");
+          
+            /* Mark the extension as started */
+            if (DeviceExtension->FdoType == CmBattBattery) DeviceExtension->Started = TRUE;
+            Status = STATUS_SUCCESS;
+            break;
+        
+        case IRP_MN_STOP_DEVICE:        
+            if (CmBattDebug & 0x20)
+                DbgPrint("CmBattPnpDispatch: IRP_MN_STOP_DEVICE\n");
+
+            /* Mark the extension as stopped */
+            if (DeviceExtension->FdoType == CmBattBattery) DeviceExtension->Started = FALSE;
+            Status = STATUS_SUCCESS;
+            break;
+        
+        case IRP_MN_QUERY_REMOVE_DEVICE:
+            if (CmBattDebug & 0x20)
+                DbgPrint("CmBattPnpDispatch: IRP_MN_QUERY_REMOVE_DEVICE\n");
+
+            /* Lock the extension and get the current handle count */
+            ExAcquireFastMutex(&DeviceExtension->FastMutex);
+            if (DeviceExtension->HandleCount == 0)
+            {
+                /* No handles. Mark it as invalid since it'll be removed */
+                DeviceExtension->HandleCount = -1;
+                Status = STATUS_SUCCESS;
+            }
+            else if (DeviceExtension->HandleCount == -1)
+            {
+                /* Don't do anything, but this is strange since it's already removed */
+                Status = STATUS_SUCCESS;
+                if (CmBattDebug & 4)
+                    DbgPrint("CmBattPnpDispatch: Recieved two consecutive QUERY_REMOVE requests.\n");
+            }
+            else
+            {
+                /* Fail because there's still open handles */
+                Status = STATUS_UNSUCCESSFUL;
+            }
+            
+            /* Release the lock and return */
+            ExReleaseFastMutex(&DeviceExtension->FastMutex);
+            break;
+        
+        case IRP_MN_REMOVE_DEVICE:
+            if (CmBattDebug & 0x20)
+                DbgPrint("CmBattPnpDispatch: IRP_MN_REMOVE_DEVICE\n");
+             
+            /* Call the remove code */
+            Status = CmBattRemoveDevice(DeviceObject, Irp);
+            break;
+        
+        case IRP_MN_CANCEL_REMOVE_DEVICE:
+            if (CmBattDebug & 0x20)
+                DbgPrint("CmBattPnpDispatch: IRP_MN_CANCEL_REMOVE_DEVICE\n");
+            
+            /* Lock the extension and get the handle count */
+            ExAcquireFastMutex(&DeviceExtension->FastMutex);
+            if (DeviceExtension->HandleCount == -1)
+            {
+                /* A remove was in progress, set the handle count back to 0 */
+                DeviceExtension->HandleCount = 0;
+            }
+            else if (CmBattDebug & 2)
+            {
+                /* Nop, but warn about it */
+                DbgPrint("CmBattPnpDispatch: Received CANCEL_REMOVE when OpenCount == %x\n", 
+                         DeviceExtension->HandleCount);
+            }
+            
+            /* Return success in all cases, and release the lock */
+            Status = STATUS_SUCCESS;
+            ExReleaseFastMutex(&DeviceExtension->FastMutex);
+            break;
+        
+        case IRP_MN_QUERY_STOP_DEVICE:
+            if (CmBattDebug & 0x20)
+                DbgPrint("CmBattPnpDispatch: IRP_MN_QUERY_STOP_DEVICE\n");
+            
+            /* There's no real support for this */    
+            Status = STATUS_NOT_IMPLEMENTED;
+            break;
+
+        case IRP_MN_CANCEL_STOP_DEVICE:
+            if (CmBattDebug & 0x20)
+                DbgPrint("CmBattPnpDispatch: IRP_MN_CANCEL_STOP_DEVICE\n");
+        
+            /* There's no real support for this */    
+            Status = STATUS_NOT_IMPLEMENTED;
+            break;
+        
+        case IRP_MN_QUERY_CAPABILITIES:
+        
+            /* Initialize our wait event */
+            KeInitializeEvent(&Event, SynchronizationEvent, 0);
+
+            /* Set the completion routine */
+            IoCopyCurrentIrpStackLocationToNext(Irp);
+            IoSetCompletionRoutine(Irp,
+                                   (PVOID)CmBattIoCompletion,
+                                   &Event,
+                                   TRUE,
+                                   TRUE,
+                                   TRUE);
+
+            /* Now call ACPI */
+            Status = IoCallDriver(DeviceObject, Irp);
+            if (Status == STATUS_PENDING)
+            {
+                /* Wait for completion */
+                KeWaitForSingleObject(&Event,
+                                      Executive,
+                                      KernelMode,
+                                      FALSE,
+                                      NULL);
+                Status = Irp->IoStatus.Status;
+            }
+
+            /* Get the wake power state */
+            DeviceExtension->PowerState.SystemState = IoStackLocation->Parameters.DeviceCapabilities.Capabilities->SystemWake;
+            if (CmBattDebug & 0x20)
+                DbgPrint("CmBattPnpDispatch: IRP_MN_QUERY_CAPABILITIES %d Capabilities->SystemWake = %x\n",
+                         DeviceExtension->FdoType,
+                         DeviceExtension->PowerState);
+
+            /* Check if it's invalid */
+            if (DeviceExtension->PowerState.SystemState == PowerSystemUnspecified)
+            {
+                /* Wait wake is not supported in this scenario */
+                DeviceExtension->WaitWakeEnable = FALSE;
+                if (CmBattDebug & 0x20)
+                    DbgPrint("CmBattPnpDispatch: IRP_MN_QUERY_CAPABILITIES Wake not supported.\n");
+            }
+            else if (!(DeviceExtension->PowerIrp) &&
+                     (DeviceExtension->WaitWakeEnable))
+            {
+                /* If it was requested in the registry, request the power IRP for it */
+                PoRequestPowerIrp(DeviceExtension->DeviceObject,
+                                  0,
+                                  DeviceExtension->PowerState,
+                                  CmBattWaitWakeLoop,
+                                  0,
+                                  &DeviceExtension->PowerIrp);
+                if (CmBattDebug & 0x20)
+                    DbgPrint("CmBattPnpDispatch: IRP_MN_QUERY_CAPABILITIES wait/Wake irp sent.\n");
+            }
+
+            /* Release the remove lock and complete the request */
+            IofCompleteRequest(Irp, IO_NO_INCREMENT);
+            IoReleaseRemoveLock(&DeviceExtension->RemoveLock, Irp);
+            return Status;
+
+        default:
+            /* Unsupported */
+            if (CmBattDebug & 0x20)
+                DbgPrint("CmBattPnpDispatch: Unimplemented minor %0x\n",
+                         IoStackLocation->MinorFunction);
+            break;
+    }
+    
+    /* Release the remove lock */  
+    IoReleaseRemoveLock(&DeviceExtension->RemoveLock, Irp);
+
+    /* Set IRP status if we have one */
+    if (Status != STATUS_NOT_SUPPORTED) Irp->IoStatus.Status = Status;
+
+    /* Did someone pick it up? */    
+    if ((NT_SUCCESS(Status)) || (Status == STATUS_NOT_SUPPORTED))
+    {
+        /* Still unsupported, try ACPI */
+        IoSkipCurrentIrpStackLocation(Irp);
+        Status = IoCallDriver(DeviceExtension->AttachedDevice, Irp);
+    }
+    else
+    {
+        /* Complete the request */
+        Status = Irp->IoStatus.Status;
+        IofCompleteRequest(Irp, IO_NO_INCREMENT);
+    }
+
+    /* Release the remove lock and return status */
+    return Status;
 }
 
 NTSTATUS
 NTAPI
-CmBattPnpDispatch(PDEVICE_OBJECT DeviceObject,
-                  PIRP Irp)
+CmBattCreateFdo(IN PDRIVER_OBJECT DriverObject,
+                IN PDEVICE_OBJECT DeviceObject,
+                IN ULONG DeviceExtensionSize,
+                IN PDEVICE_OBJECT *NewDeviceObject)
 {
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
+    PDEVICE_OBJECT FdoDeviceObject;
+    HANDLE KeyHandle;
+    PCMBATT_DEVICE_EXTENSION FdoExtension;
+    UCHAR Buffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(ULONG)];
+    PKEY_VALUE_PARTIAL_INFORMATION PartialInfo = (PVOID)Buffer;
+    NTSTATUS Status;
+    UNICODE_STRING KeyString;
+    ULONG UniqueId;
+    ULONG ResultLength;
+    PAGED_CODE();
+    if (CmBattDebug & 0x220) DbgPrint("CmBattCreateFdo: Entered\n");
+    
+    /* Get unique ID */
+    Status = CmBattGetUniqueId(DeviceObject, &UniqueId);
+    if (!NT_SUCCESS(Status))
+    {
+        /* Assume 0 */
+        UniqueId = 0;
+        if (CmBattDebug & 2)
+          DbgPrint("CmBattCreateFdo: Error %x from _UID, assuming unit #0\n", Status);
+    }
+    
+    /* Create the FDO */
+    Status = IoCreateDevice(DriverObject,
+                            DeviceExtensionSize,
+                            0,
+                            FILE_DEVICE_BATTERY,
+                            FILE_DEVICE_SECURE_OPEN,
+                            0,
+                            &FdoDeviceObject);
+    if (!NT_SUCCESS(Status))
+    {
+        /* Fail */
+        if (CmBattDebug & 0xC)
+            DbgPrint("CmBattCreateFdo: error (0x%x) creating device object\n", Status);
+        return Status;
+    }
+    
+    /* Set FDO flags */
+    FdoDeviceObject->Flags |= DO_BUFFERED_IO;
+    FdoDeviceObject->Flags |= DO_MAP_IO_BUFFER;
+    FdoDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
+
+    /* Initialize the extension */
+    FdoExtension = FdoDeviceObject->DeviceExtension;
+    RtlZeroMemory(FdoExtension, DeviceExtensionSize);
+    FdoExtension->DeviceObject = FdoDeviceObject;
+    FdoExtension->FdoDeviceObject = FdoDeviceObject;
+    FdoExtension->PdoDeviceObject = DeviceObject;
+    
+    /* Attach to ACPI */
+    FdoExtension->AttachedDevice = IoAttachDeviceToDeviceStack(FdoDeviceObject,
+                                                               DeviceObject);
+    if (!FdoExtension->AttachedDevice)
+    {
+        /* Destroy and fail */
+        CmBattDestroyFdo(FdoExtension->FdoDeviceObject);
+        if (CmBattDebug & 0xC)
+            DbgPrint("CmBattCreateFdo: IoAttachDeviceToDeviceStack failed.\n");
+        return STATUS_UNSUCCESSFUL;
+    }
+    
+    /* Get ACPI interface for EVAL */
+    Status = CmBattGetAcpiInterfaces(FdoExtension->AttachedDevice,
+                                     &FdoExtension->AcpiInterface);
+    if (!FdoExtension->AttachedDevice)
+    {
+        /* Detach, destroy, and fail */
+        IoDetachDevice(FdoExtension->AttachedDevice);
+        CmBattDestroyFdo(FdoExtension->FdoDeviceObject);
+        if (CmBattDebug & 0xC)
+            DbgPrint("CmBattCreateFdo: Could not get ACPI interfaces: %x\n", Status);
+        return STATUS_UNSUCCESSFUL;
+    }
+
+    /* Setup the rest of the extension */
+    ExInitializeFastMutex(&FdoExtension->FastMutex);
+    IoInitializeRemoveLock(&FdoExtension->RemoveLock, 0, 0, 0);
+    FdoExtension->HandleCount = 0;
+    FdoExtension->WaitWakeEnable = FALSE;
+    FdoExtension->DeviceId = UniqueId;
+    FdoExtension->DeviceName = NULL;
+    FdoExtension->DelayNotification = FALSE;
+    FdoExtension->ArFlag = 0;
+    
+    /* Open the device key */
+    Status = IoOpenDeviceRegistryKey(DeviceObject,
+                                     PLUGPLAY_REGKEY_DEVICE,
+                                     KEY_READ,
+                                     &KeyHandle);
+    if (NT_SUCCESS(Status))
+    {
+        /* Read wait wake value */
+        RtlInitUnicodeString(&KeyString, L"WaitWakeEnabled");
+        Status = ZwQueryValueKey(KeyHandle,
+                                 &KeyString,
+                                 KeyValuePartialInformation,
+                                 PartialInfo,
+                                 sizeof(Buffer),
+                                 &ResultLength);
+        if (NT_SUCCESS(Status))
+        {
+            /* Set value */
+            FdoExtension->WaitWakeEnable = *(PULONG)PartialInfo->Data;
+        }
+        
+        /* Close the handle */
+        ZwClose(KeyHandle);
+    }
+
+    /* Return success and the new FDO */
+    *NewDeviceObject = FdoDeviceObject;
+    if (CmBattDebug & 0x220)
+        DbgPrint("CmBattCreateFdo: Created FDO %x\n", FdoDeviceObject);
+    return STATUS_SUCCESS;
 }
 
 NTSTATUS
 NTAPI
-CmBattAddBattery(PDRIVER_OBJECT DriverObject,
-                 PDEVICE_OBJECT DeviceObject)
+CmBattAddBattery(IN PDRIVER_OBJECT DriverObject,
+                 IN PDEVICE_OBJECT DeviceObject)
 {
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
+    BATTERY_MINIPORT_INFO MiniportInfo;
+    NTSTATUS Status;
+    PDEVICE_OBJECT FdoDeviceObject;
+    PCMBATT_DEVICE_EXTENSION FdoExtension; 
+    PAGED_CODE();
+    if (CmBattDebug & 0x220)
+        DbgPrint("CmBattAddBattery: pdo %x\n", DeviceObject);
+
+    /* Create the FDO */
+    Status = CmBattCreateFdo(DriverObject,
+                             DeviceObject,
+                             sizeof(CMBATT_DEVICE_EXTENSION),
+                             &FdoDeviceObject);
+    if (!NT_SUCCESS(Status))
+    {
+        if (CmBattDebug & 0xC)
+            DbgPrint("CmBattAddBattery: error (0x%x) creating Fdo\n", Status);
+        return Status;
+    }
+
+    /* Build the FDO extensio, check if we support trip points */
+    FdoExtension = FdoDeviceObject->DeviceExtension;
+    FdoExtension->FdoType = CmBattBattery;
+    FdoExtension->Started = 0;
+    FdoExtension->NotifySent = TRUE;
+    InterlockedExchange(&FdoExtension->ArLockValue, 0);
+    FdoExtension->TripPointValue = BATTERY_UNKNOWN_CAPACITY;
+    FdoExtension->Tag = 0;
+    FdoExtension->InterruptTime = KeQueryInterruptTime();
+    FdoExtension->TripPointSet = CmBattSetTripPpoint(FdoExtension, 0) !=
+                                 STATUS_OBJECT_NAME_NOT_FOUND;
+       
+    /* Setup the battery miniport information structure */
+    RtlZeroMemory(&MiniportInfo, sizeof(MiniportInfo));
+    MiniportInfo.Pdo = DeviceObject;
+    MiniportInfo.MajorVersion = BATTERY_CLASS_MAJOR_VERSION;
+    MiniportInfo.MinorVersion = BATTERY_CLASS_MINOR_VERSION;
+    MiniportInfo.Context = FdoExtension;
+    MiniportInfo.QueryTag = (PVOID)CmBattQueryTag;
+    MiniportInfo.QueryInformation = (PVOID)CmBattQueryInformation;
+    MiniportInfo.SetInformation = NULL;
+    MiniportInfo.QueryStatus = (PVOID)CmBattQueryStatus;
+    MiniportInfo.SetStatusNotify = (PVOID)CmBattSetStatusNotify;
+    MiniportInfo.DisableStatusNotify = (PVOID)CmBattDisableStatusNotify;
+    MiniportInfo.DeviceName = FdoExtension->DeviceName;
+    /* Register with the class driver */
+    Status = BatteryClassInitializeDevice(&MiniportInfo, &FdoExtension->ClassData);
+    if (!NT_SUCCESS(Status))
+    {
+        IoDetachDevice(FdoExtension->AttachedDevice);
+        CmBattDestroyFdo(FdoExtension->FdoDeviceObject);
+        if (CmBattDebug & 0xC)
+            DbgPrint("CmBattAddBattery: error (0x%x) registering with class\n", Status);
+        return Status;
+    }
+
+    /* Register WMI */
+    Status = CmBattWmiRegistration(FdoExtension);
+    if (!NT_SUCCESS(Status))
+    {
+        if (CmBattDebug & 0xC)
+            DbgPrint("CmBattAddBattery: Could not register as a WMI provider, status = %Lx\n", Status);
+        return Status;
+    }
+    
+    /* Register ACPI */
+    Status = FdoExtension->AcpiInterface.RegisterForDeviceNotifications(FdoExtension->AcpiInterface.Context,
+                                                                        (PVOID)CmBattNotifyHandler,
+                                                                        FdoExtension);
+    if (!NT_SUCCESS(Status))
+    {
+        CmBattWmiDeRegistration(FdoExtension);
+        BatteryClassUnload(FdoExtension->ClassData);
+        IoDetachDevice(FdoExtension->AttachedDevice);
+        CmBattDestroyFdo(FdoExtension->FdoDeviceObject);
+        if (CmBattDebug & 0xC)
+            DbgPrint("CmBattAddBattery: Could not register for battery notify, status = %Lx\n", Status);
+    }
+    
+    /* Return status */
+    return Status;
 }
 
 NTSTATUS
 NTAPI
-CmBattAddAcAdapter(PDRIVER_OBJECT DriverObject,
-                   PDEVICE_OBJECT DeviceObject)
+CmBattAddAcAdapter(IN PDRIVER_OBJECT DriverObject,
+                   IN PDEVICE_OBJECT PdoDeviceObject)
 {
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
+    PDEVICE_OBJECT FdoDeviceObject;
+    NTSTATUS Status;
+    PCMBATT_DEVICE_EXTENSION DeviceExtension;
+    PAGED_CODE();
+    if (CmBattDebug & 0x220)
+        DbgPrint("CmBattAddAcAdapter: pdo %x\n", PdoDeviceObject);
+
+    /* Check if we already have an AC adapter */
+    if (AcAdapterPdo)
+    {
+        /* Don't do anything */
+        if (CmBattDebug & 0xC)
+            DbgPrint("CmBatt: Second AC adapter found.  Current version of driver only supports 1 aadapter.\n");
+    }
+    else
+    {
+        /* Set this as the AC adapter's PDO */
+        AcAdapterPdo = PdoDeviceObject;
+    }
+    
+    /* Create the FDO for the adapter */
+    Status = CmBattCreateFdo(DriverObject,
+                             PdoDeviceObject,
+                             sizeof(CMBATT_DEVICE_EXTENSION),
+                             &FdoDeviceObject);
+    if (!NT_SUCCESS(Status))
+    {
+        /* Fail */
+        if (CmBattDebug & 0xC)
+            DbgPrint("CmBattAddAcAdapter: error (0x%x) creating Fdo\n", Status);
+        return Status;
+    }
+    
+    /* Set the type and do WMI registration */
+    DeviceExtension = FdoDeviceObject->DeviceExtension;
+    DeviceExtension->FdoType = CmBattAcAdapter;
+    Status = CmBattWmiRegistration(DeviceExtension);
+    if (!NT_SUCCESS(Status))
+    {
+        /* We can go on without WMI */
+        if (CmBattDebug & 0xC)
+            DbgPrint("CmBattAddBattery: Could not register as a WMI provider, status = %Lx\n", Status);
+    }
+    
+    /* Register with ACPI */
+    Status = DeviceExtension->AcpiInterface.RegisterForDeviceNotifications(DeviceExtension->AcpiInterface.Context,
+                                                                           (PVOID)CmBattNotifyHandler,
+                                                                           DeviceExtension);
+    if (!(NT_SUCCESS(Status)) && (CmBattDebug & 0xC))
+        DbgPrint("CmBattAddAcAdapter: Could not register for power notify, status = %Lx\n", Status);
+
+    /* Send the first manual notification */
+    CmBattNotifyHandler(DeviceExtension, ACPI_BATT_NOTIFY_STATUS);
+    return STATUS_SUCCESS;
 }
 
 NTSTATUS
 NTAPI
-CmBattAddDevice(PDRIVER_OBJECT DriverObject,
-                PDEVICE_OBJECT DeviceObject)
+CmBattAddDevice(IN PDRIVER_OBJECT DriverObject,
+                IN PDEVICE_OBJECT PdoDeviceObject)
 {
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
+    NTSTATUS Status;
+    HANDLE KeyHandle;
+    ULONG ResultLength;
+    UNICODE_STRING KeyString;
+    UCHAR Buffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(ULONG)];
+    PKEY_VALUE_PARTIAL_INFORMATION PartialInfo = (PVOID)Buffer;
+    ULONG PowerSourceType;
+    PAGED_CODE();
+    if (CmBattDebug & 0x220)
+        DbgPrint("CmBattAddDevice: Entered with pdo %x\n", PdoDeviceObject);
+    
+    /* Make sure we have a PDO */
+    if (!PdoDeviceObject)
+    {
+        /* Should not be having as one */
+        if (CmBattDebug & 0x24) DbgPrint("CmBattAddDevice: Asked to do detection\n");
+        return STATUS_NO_MORE_ENTRIES;
+    }
+    
+    /* Open the driver key */
+    Status = IoOpenDeviceRegistryKey(PdoDeviceObject,
+                                     PLUGPLAY_REGKEY_DRIVER,
+                                     KEY_READ,
+                                     &KeyHandle);
+    if (!NT_SUCCESS(Status))
+    {
+        if (CmBattDebug & 0xC)
+            DbgPrint("CmBattAddDevice: Could not get the software branch: %x\n", Status);
+        return Status;
+    }
+
+    /* Read the power source type */
+    RtlInitUnicodeString(&KeyString, L"PowerSourceType");
+    Status = ZwQueryValueKey(KeyHandle,
+                             &KeyString,
+                             KeyValuePartialInformation,
+                             PartialInfo,
+                             sizeof(Buffer),
+                             &ResultLength);
+    ZwClose(KeyHandle);
+    if (!NT_SUCCESS(Status))
+    {
+        /* We need the data, fail without it */
+        if (CmBattDebug & 0xC)
+            DbgPrint("CmBattAddDevice: Could not read the power type identifier: %x\n", Status);
+        return Status;
+    }
+    
+    /* Check what kind of power source this is */
+    PowerSourceType = *(PULONG)PartialInfo->Data;
+    if (PowerSourceType == 1)
+    {
+        /* Create an AC adapter */
+        Status = CmBattAddAcAdapter(DriverObject, PdoDeviceObject);
+    }
+    else if (PowerSourceType == 0)
+    {
+        /* Create a battery */
+        Status = CmBattAddBattery(DriverObject, PdoDeviceObject);
+    }
+    else
+    {
+        /* Unknown type, fail */
+        if (CmBattDebug & 0xC)
+            DbgPrint("CmBattAddDevice: Invalid POWER_SOURCE_TYPE == %d \n", PowerSourceType);
+        return STATUS_UNSUCCESSFUL;
+    }
+    
+    /* Return whatever the FDO creation routine did */
+    return Status;
 }
 
 /* EOF */