xref: /reactos/drivers/bus/acpi/cmbatt/cmbpnp.c (revision 8a978a17)
1 /*
2  * PROJECT:         ReactOS ACPI-Compliant Control Method Battery
3  * LICENSE:         BSD - See COPYING.ARM in the top level directory
4  * FILE:            boot/drivers/bus/acpi/cmbatt/cmbpnp.c
5  * PURPOSE:         Plug-and-Play IOCTL/IRP Handling
6  * PROGRAMMERS:     ReactOS Portable Systems Group
7  */
8 
9 /* INCLUDES *******************************************************************/
10 
11 #include "cmbatt.h"
12 
13 /* FUNCTIONS ******************************************************************/
14 
15 VOID
16 NTAPI
17 CmBattWaitWakeLoop(IN PDEVICE_OBJECT DeviceObject,
18                    IN UCHAR MinorFunction,
19                    IN POWER_STATE PowerState,
20                    IN PVOID Context,
21                    IN PIO_STATUS_BLOCK IoStatusBlock)
22 {
23     NTSTATUS Status;
24     PCMBATT_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
25     if (CmBattDebug & 0x20) DbgPrint("CmBattWaitWakeLoop: Entered.\n");
26 
27     /* Check for success */
28     if ((NT_SUCCESS(IoStatusBlock->Status)) && (DeviceExtension->WaitWakeEnable))
29     {
30         /* Request a new power IRP */
31         if (CmBattDebug & 2) DbgPrint("CmBattWaitWakeLoop: completed successfully\n");
32         Status = PoRequestPowerIrp(DeviceObject,
33                                    MinorFunction,
34                                    PowerState,
35                                    CmBattWaitWakeLoop,
36                                    Context,
37                                    &DeviceExtension->PowerIrp);
38         if (CmBattDebug & 2)
39             DbgPrint("CmBattWaitWakeLoop: PoRequestPowerIrp: status = 0x%08x.\n",
40                      Status);
41     }
42     else
43     {
44         /* Clear the power IRP, we failed */
45         if (CmBattDebug & 0xC)
46             DbgPrint("CmBattWaitWakeLoop: failed: status = 0x%08x.\n",
47                      IoStatusBlock->Status);
48         DeviceExtension->PowerIrp = NULL;
49     }
50 }
51 
52 NTSTATUS
53 NTAPI
54 CmBattIoCompletion(IN PDEVICE_OBJECT DeviceObject,
55                    IN PIRP Irp,
56                    IN PKEVENT Event)
57 {
58     if (CmBattDebug & 2) DbgPrint("CmBattIoCompletion: Event (%x)\n", Event);
59 
60     /* Set the completion event */
61     KeSetEvent(Event, IO_NO_INCREMENT, FALSE);
62     return STATUS_MORE_PROCESSING_REQUIRED;
63 }
64 
65 NTSTATUS
66 NTAPI
67 CmBattGetAcpiInterfaces(IN PDEVICE_OBJECT DeviceObject,
68                         IN OUT PACPI_INTERFACE_STANDARD AcpiInterface)
69 {
70     PIRP Irp;
71     NTSTATUS Status;
72     PIO_STACK_LOCATION IoStackLocation;
73     KEVENT Event;
74 
75     /* Allocate the IRP */
76     Irp = IoAllocateIrp(DeviceObject->StackSize, 0);
77     if (!Irp)
78     {
79         /* Fail */
80         if (CmBattDebug & 0xC)
81           DbgPrint("CmBattGetAcpiInterfaces: Failed to allocate Irp\n");
82         return STATUS_INSUFFICIENT_RESOURCES;
83     }
84 
85     /* Set default error code */
86     Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
87 
88     /* Build the query */
89     IoStackLocation = IoGetNextIrpStackLocation(Irp);
90     IoStackLocation->MajorFunction = IRP_MJ_PNP;
91     IoStackLocation->MinorFunction = IRP_MN_QUERY_INTERFACE;
92     IoStackLocation->Parameters.QueryInterface.InterfaceType = &GUID_ACPI_INTERFACE_STANDARD;
93     IoStackLocation->Parameters.QueryInterface.Size = sizeof(ACPI_INTERFACE_STANDARD);
94     IoStackLocation->Parameters.QueryInterface.Version = 1;
95     IoStackLocation->Parameters.QueryInterface.Interface = (PINTERFACE)AcpiInterface;
96     IoStackLocation->Parameters.QueryInterface.InterfaceSpecificData = NULL;
97 
98     /* Set default ACPI interface data */
99     AcpiInterface->Size = sizeof(ACPI_INTERFACE_STANDARD);
100     AcpiInterface->Version = 1;
101 
102     /* Initialize our wait event */
103     KeInitializeEvent(&Event, SynchronizationEvent, 0);
104 
105     /* Set the completion routine */
106     IoSetCompletionRoutine(Irp,
107                            (PVOID)CmBattIoCompletion,
108                            &Event,
109                            TRUE,
110                            TRUE,
111                            TRUE);
112 
113     /* Now call ACPI */
114     Status = IoCallDriver(DeviceObject, Irp);
115     if (Status == STATUS_PENDING)
116     {
117         /* Wait for completion */
118         KeWaitForSingleObject(&Event,
119                               Executive,
120                               KernelMode,
121                               FALSE,
122                               NULL);
123         Status = Irp->IoStatus.Status;
124     }
125 
126     /* Free the IRP */
127     IoFreeIrp(Irp);
128 
129     /* Return status */
130     if (!(NT_SUCCESS(Status)) && (CmBattDebug & 0xC))
131         DbgPrint("CmBattGetAcpiInterfaces: Could not get ACPI driver interfaces, status = %x\n", Status);
132     return Status;
133 }
134 
135 VOID
136 NTAPI
137 CmBattDestroyFdo(IN PDEVICE_OBJECT DeviceObject)
138 {
139     PAGED_CODE();
140     if (CmBattDebug & 0x220) DbgPrint("CmBattDestroyFdo, Battery.\n");
141 
142     /* Delete the device */
143     IoDeleteDevice(DeviceObject);
144     if (CmBattDebug & 0x220) DbgPrint("CmBattDestroyFdo: done.\n");
145 }
146 
147 NTSTATUS
148 NTAPI
149 CmBattRemoveDevice(IN PDEVICE_OBJECT DeviceObject,
150                    IN PIRP Irp)
151 {
152     PCMBATT_DEVICE_EXTENSION DeviceExtension;
153     PVOID Context;
154     DeviceExtension = DeviceObject->DeviceExtension;
155     if (CmBattDebug & 2)
156         DbgPrint("CmBattRemoveDevice: CmBatt (%x), Type %d, _UID %d\n",
157                  DeviceExtension,
158                  DeviceExtension->FdoType,
159                  DeviceExtension->DeviceId);
160 
161     /* Make sure it's safe to go ahead */
162     IoReleaseRemoveLockAndWait(&DeviceExtension->RemoveLock, 0);
163 
164     /* Check for pending power IRP */
165     if (DeviceExtension->PowerIrp)
166     {
167         /* Cancel and clear */
168         IoCancelIrp(DeviceExtension->PowerIrp);
169         DeviceExtension->PowerIrp = NULL;
170     }
171 
172     /* Check what type of FDO is being removed */
173     Context = DeviceExtension->AcpiInterface.Context;
174     if (DeviceExtension->FdoType == CmBattBattery)
175     {
176         /* Unregister battery FDO */
177         DeviceExtension->AcpiInterface.UnregisterForDeviceNotifications(Context,
178                                                                         (PVOID)CmBattNotifyHandler);
179         CmBattWmiDeRegistration(DeviceExtension);
180         if (!NT_SUCCESS(BatteryClassUnload(DeviceExtension->ClassData))) ASSERT(FALSE);
181     }
182     else
183     {
184         /* Unregister AC adapter FDO */
185         DeviceExtension->AcpiInterface.UnregisterForDeviceNotifications(Context,
186                                                                         (PVOID)CmBattNotifyHandler);
187         CmBattWmiDeRegistration(DeviceExtension);
188         AcAdapterPdo = NULL;
189     }
190 
191     /* Detach and delete */
192     IoDetachDevice(DeviceExtension->AttachedDevice);
193     IoDeleteDevice(DeviceExtension->DeviceObject);
194     return STATUS_SUCCESS;
195 }
196 
197 NTSTATUS
198 NTAPI
199 CmBattPowerDispatch(IN PDEVICE_OBJECT DeviceObject,
200                     IN PIRP Irp)
201 {
202     PIO_STACK_LOCATION IoStackLocation;
203     PCMBATT_DEVICE_EXTENSION DeviceExtension;
204     NTSTATUS Status;
205     if (CmBattDebug & 0x210) DbgPrint("CmBattPowerDispatch\n");
206 
207     /* Get stack location and device extension */
208     IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
209     DeviceExtension = DeviceObject->DeviceExtension;
210     switch (IoStackLocation->MinorFunction)
211     {
212         case IRP_MN_WAIT_WAKE:
213             if (CmBattDebug & 0x10)
214                 DbgPrint("CmBattPowerDispatch: IRP_MN_WAIT_WAKE\n");
215             break;
216 
217         case IRP_MN_POWER_SEQUENCE:
218             if (CmBattDebug & 0x10)
219                 DbgPrint("CmBattPowerDispatch: IRP_MN_POWER_SEQUENCE\n");
220             break;
221 
222         case IRP_MN_QUERY_POWER:
223             if (CmBattDebug & 0x10)
224                 DbgPrint("CmBattPowerDispatch: IRP_MN_WAIT_WAKE\n");
225             break;
226 
227         case IRP_MN_SET_POWER:
228             if (CmBattDebug & 0x10)
229                 DbgPrint("CmBattPowerDispatch: IRP_MN_SET_POWER type: %d, State: %d \n",
230                          IoStackLocation->Parameters.Power.Type,
231                          IoStackLocation->Parameters.Power.State);
232             break;
233 
234         default:
235 
236             if (CmBattDebug & 1)
237                 DbgPrint("CmBattPowerDispatch: minor %d\n", IoStackLocation->MinorFunction);
238             break;
239     }
240 
241     /* Start the next IRP and see if we're attached */
242     PoStartNextPowerIrp(Irp);
243     if (DeviceExtension->AttachedDevice)
244     {
245         /* Call ACPI */
246         IoSkipCurrentIrpStackLocation(Irp);
247         Status = PoCallDriver(DeviceExtension->AttachedDevice, Irp);
248     }
249     else
250     {
251         /* Complete the request here */
252         Status = Irp->IoStatus.Status;
253         IoCompleteRequest(Irp, IO_NO_INCREMENT);
254     }
255 
256     /* Return status */
257     return Status;
258 }
259 
260 NTSTATUS
261 NTAPI
262 CmBattPnpDispatch(IN PDEVICE_OBJECT DeviceObject,
263                   IN PIRP Irp)
264 {
265     PIO_STACK_LOCATION IoStackLocation;
266     PCMBATT_DEVICE_EXTENSION DeviceExtension;
267     NTSTATUS Status;
268     KEVENT Event;
269     PAGED_CODE();
270 
271     /* Get stack location and device extension */
272     IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
273     DeviceExtension = DeviceObject->DeviceExtension;
274 
275     /* Set default error */
276     Status = STATUS_NOT_SUPPORTED;
277 
278     /* Try to acquire the lock before doing anything */
279     Status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, Irp);
280     if (!NT_SUCCESS(Status))
281     {
282         /* Complete the request */
283         Irp->IoStatus.Status = STATUS_DEVICE_REMOVED;
284         IoCompleteRequest(Irp, IO_NO_INCREMENT);
285         return STATUS_DEVICE_REMOVED;
286     }
287 
288     /* What's the operation? */
289     switch (IoStackLocation->MinorFunction)
290     {
291         case IRP_MN_QUERY_PNP_DEVICE_STATE:
292 
293             /* Initialize our wait event */
294             KeInitializeEvent(&Event, SynchronizationEvent, 0);
295 
296             /* Set the completion routine */
297             IoCopyCurrentIrpStackLocationToNext(Irp);
298             IoSetCompletionRoutine(Irp,
299                                    (PVOID)CmBattIoCompletion,
300                                    &Event,
301                                    TRUE,
302                                    TRUE,
303                                    TRUE);
304 
305             /* Now call ACPI to inherit its PnP Device State */
306             Status = IoCallDriver(DeviceExtension->AttachedDevice, Irp);
307             if (Status == STATUS_PENDING)
308             {
309                 /* Wait for completion */
310                 KeWaitForSingleObject(&Event,
311                                       Executive,
312                                       KernelMode,
313                                       FALSE,
314                                       NULL);
315                 Status = Irp->IoStatus.Status;
316             }
317 
318             /* However, a battery CAN be disabled */
319             Irp->IoStatus.Information &= ~PNP_DEVICE_NOT_DISABLEABLE;
320 
321             /* Release the remove lock and complete the request */
322             IoCompleteRequest(Irp, IO_NO_INCREMENT);
323             IoReleaseRemoveLock(&DeviceExtension->RemoveLock, Irp);
324             return Status;
325 
326         case IRP_MN_SURPRISE_REMOVAL:
327             if (CmBattDebug & 0x20)
328                 DbgPrint("CmBattPnpDispatch: IRP_MN_SURPRISE_REMOVAL\n");
329 
330             /* Lock the device extension and set the handle count to invalid */
331             ExAcquireFastMutex(&DeviceExtension->FastMutex);
332             DeviceExtension->HandleCount = -1;
333             ExReleaseFastMutex(&DeviceExtension->FastMutex);
334             Status = STATUS_SUCCESS;
335             break;
336 
337         case IRP_MN_START_DEVICE:
338             if (CmBattDebug & 0x20)
339                 DbgPrint("CmBattPnpDispatch: IRP_MN_START_DEVICE\n");
340 
341             /* Mark the extension as started */
342             if (DeviceExtension->FdoType == CmBattBattery) DeviceExtension->Started = TRUE;
343             Status = STATUS_SUCCESS;
344             break;
345 
346         case IRP_MN_STOP_DEVICE:
347             if (CmBattDebug & 0x20)
348                 DbgPrint("CmBattPnpDispatch: IRP_MN_STOP_DEVICE\n");
349 
350             /* Mark the extension as stopped */
351             if (DeviceExtension->FdoType == CmBattBattery) DeviceExtension->Started = FALSE;
352             Status = STATUS_SUCCESS;
353             break;
354 
355         case IRP_MN_QUERY_REMOVE_DEVICE:
356             if (CmBattDebug & 0x20)
357                 DbgPrint("CmBattPnpDispatch: IRP_MN_QUERY_REMOVE_DEVICE\n");
358 
359             /* Lock the extension and get the current handle count */
360             ExAcquireFastMutex(&DeviceExtension->FastMutex);
361             if (DeviceExtension->HandleCount == 0)
362             {
363                 /* No handles. Mark it as invalid since it'll be removed */
364                 DeviceExtension->HandleCount = -1;
365                 Status = STATUS_SUCCESS;
366             }
367             else if (DeviceExtension->HandleCount == -1)
368             {
369                 /* Don't do anything, but this is strange since it's already removed */
370                 Status = STATUS_SUCCESS;
371                 if (CmBattDebug & 4)
372                     DbgPrint("CmBattPnpDispatch: Received two consecutive QUERY_REMOVE requests.\n");
373             }
374             else
375             {
376                 /* Fail because there's still open handles */
377                 Status = STATUS_UNSUCCESSFUL;
378             }
379 
380             /* Release the lock and return */
381             ExReleaseFastMutex(&DeviceExtension->FastMutex);
382             break;
383 
384         case IRP_MN_REMOVE_DEVICE:
385             if (CmBattDebug & 0x20)
386                 DbgPrint("CmBattPnpDispatch: IRP_MN_REMOVE_DEVICE\n");
387 
388             /* Call the remove code */
389             Status = CmBattRemoveDevice(DeviceObject, Irp);
390             break;
391 
392         case IRP_MN_CANCEL_REMOVE_DEVICE:
393             if (CmBattDebug & 0x20)
394                 DbgPrint("CmBattPnpDispatch: IRP_MN_CANCEL_REMOVE_DEVICE\n");
395 
396             /* Lock the extension and get the handle count */
397             ExAcquireFastMutex(&DeviceExtension->FastMutex);
398             if (DeviceExtension->HandleCount == -1)
399             {
400                 /* A remove was in progress, set the handle count back to 0 */
401                 DeviceExtension->HandleCount = 0;
402             }
403             else if (CmBattDebug & 2)
404             {
405                 /* Nop, but warn about it */
406                 DbgPrint("CmBattPnpDispatch: Received CANCEL_REMOVE when OpenCount == %x\n",
407                          DeviceExtension->HandleCount);
408             }
409 
410             /* Return success in all cases, and release the lock */
411             Status = STATUS_SUCCESS;
412             ExReleaseFastMutex(&DeviceExtension->FastMutex);
413             break;
414 
415         case IRP_MN_QUERY_STOP_DEVICE:
416             if (CmBattDebug & 0x20)
417                 DbgPrint("CmBattPnpDispatch: IRP_MN_QUERY_STOP_DEVICE\n");
418 
419             /* There's no real support for this */
420             Status = STATUS_NOT_IMPLEMENTED;
421             break;
422 
423         case IRP_MN_CANCEL_STOP_DEVICE:
424             if (CmBattDebug & 0x20)
425                 DbgPrint("CmBattPnpDispatch: IRP_MN_CANCEL_STOP_DEVICE\n");
426 
427             /* There's no real support for this */
428             Status = STATUS_NOT_IMPLEMENTED;
429             break;
430 
431         case IRP_MN_QUERY_CAPABILITIES:
432 
433             /* Initialize our wait event */
434             KeInitializeEvent(&Event, SynchronizationEvent, 0);
435 
436             /* Set the completion routine */
437             IoCopyCurrentIrpStackLocationToNext(Irp);
438             IoSetCompletionRoutine(Irp,
439                                    (PVOID)CmBattIoCompletion,
440                                    &Event,
441                                    TRUE,
442                                    TRUE,
443                                    TRUE);
444 
445             /* Now call ACPI */
446             Status = IoCallDriver(DeviceExtension->AttachedDevice, Irp);
447             if (Status == STATUS_PENDING)
448             {
449                 /* Wait for completion */
450                 KeWaitForSingleObject(&Event,
451                                       Executive,
452                                       KernelMode,
453                                       FALSE,
454                                       NULL);
455                 Status = Irp->IoStatus.Status;
456             }
457 
458             /* Get the wake power state */
459             DeviceExtension->PowerState.SystemState = IoStackLocation->Parameters.DeviceCapabilities.Capabilities->SystemWake;
460             if (CmBattDebug & 0x20)
461                 DbgPrint("CmBattPnpDispatch: IRP_MN_QUERY_CAPABILITIES %d Capabilities->SystemWake = %x\n",
462                          DeviceExtension->FdoType,
463                          DeviceExtension->PowerState);
464 
465             /* Check if it's invalid */
466             if (DeviceExtension->PowerState.SystemState == PowerSystemUnspecified)
467             {
468                 /* Wait wake is not supported in this scenario */
469                 DeviceExtension->WaitWakeEnable = FALSE;
470                 if (CmBattDebug & 0x20)
471                     DbgPrint("CmBattPnpDispatch: IRP_MN_QUERY_CAPABILITIES Wake not supported.\n");
472             }
473             else if (!(DeviceExtension->PowerIrp) &&
474                      (DeviceExtension->WaitWakeEnable))
475             {
476                 /* If it was requested in the registry, request the power IRP for it */
477                 PoRequestPowerIrp(DeviceExtension->DeviceObject,
478                                   0,
479                                   DeviceExtension->PowerState,
480                                   CmBattWaitWakeLoop,
481                                   0,
482                                   &DeviceExtension->PowerIrp);
483                 if (CmBattDebug & 0x20)
484                     DbgPrint("CmBattPnpDispatch: IRP_MN_QUERY_CAPABILITIES wait/Wake irp sent.\n");
485             }
486 
487             /* Release the remove lock and complete the request */
488             IoCompleteRequest(Irp, IO_NO_INCREMENT);
489             IoReleaseRemoveLock(&DeviceExtension->RemoveLock, Irp);
490             return Status;
491 
492         default:
493             /* Unsupported */
494             if (CmBattDebug & 0x20)
495                 DbgPrint("CmBattPnpDispatch: Unimplemented minor %0x\n",
496                          IoStackLocation->MinorFunction);
497             break;
498     }
499 
500     /* Release the remove lock */
501     IoReleaseRemoveLock(&DeviceExtension->RemoveLock, Irp);
502 
503     /* Set IRP status if we have one */
504     if (Status != STATUS_NOT_SUPPORTED) Irp->IoStatus.Status = Status;
505 
506     /* Did someone pick it up? */
507     if ((NT_SUCCESS(Status)) || (Status == STATUS_NOT_SUPPORTED))
508     {
509         /* Still unsupported, try ACPI */
510         IoSkipCurrentIrpStackLocation(Irp);
511         Status = IoCallDriver(DeviceExtension->AttachedDevice, Irp);
512     }
513     else
514     {
515         /* Complete the request */
516         Status = Irp->IoStatus.Status;
517         IoCompleteRequest(Irp, IO_NO_INCREMENT);
518     }
519 
520     /* Release the remove lock and return status */
521     return Status;
522 }
523 
524 NTSTATUS
525 NTAPI
526 CmBattCreateFdo(IN PDRIVER_OBJECT DriverObject,
527                 IN PDEVICE_OBJECT DeviceObject,
528                 IN ULONG DeviceExtensionSize,
529                 IN PDEVICE_OBJECT *NewDeviceObject)
530 {
531     PDEVICE_OBJECT FdoDeviceObject;
532     HANDLE KeyHandle;
533     PCMBATT_DEVICE_EXTENSION FdoExtension;
534     UCHAR Buffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(ULONG)];
535     PKEY_VALUE_PARTIAL_INFORMATION PartialInfo = (PVOID)Buffer;
536     NTSTATUS Status;
537     UNICODE_STRING KeyString;
538     ULONG UniqueId;
539     ULONG ResultLength;
540     PAGED_CODE();
541     if (CmBattDebug & 0x220) DbgPrint("CmBattCreateFdo: Entered\n");
542 
543     /* Get unique ID */
544     Status = CmBattGetUniqueId(DeviceObject, &UniqueId);
545     if (!NT_SUCCESS(Status))
546     {
547         /* Assume 0 */
548         UniqueId = 0;
549         if (CmBattDebug & 2)
550           DbgPrint("CmBattCreateFdo: Error %x from _UID, assuming unit #0\n", Status);
551     }
552 
553     /* Create the FDO */
554     Status = IoCreateDevice(DriverObject,
555                             DeviceExtensionSize,
556                             0,
557                             FILE_DEVICE_BATTERY,
558                             FILE_DEVICE_SECURE_OPEN,
559                             0,
560                             &FdoDeviceObject);
561     if (!NT_SUCCESS(Status))
562     {
563         /* Fail */
564         if (CmBattDebug & 0xC)
565             DbgPrint("CmBattCreateFdo: error (0x%x) creating device object\n", Status);
566         return Status;
567     }
568 
569     /* Set FDO flags */
570     FdoDeviceObject->Flags |= (DO_POWER_PAGABLE | DO_BUFFERED_IO);
571     FdoDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
572 
573     /* Initialize the extension */
574     FdoExtension = FdoDeviceObject->DeviceExtension;
575     RtlZeroMemory(FdoExtension, DeviceExtensionSize);
576     FdoExtension->DeviceObject = FdoDeviceObject;
577     FdoExtension->FdoDeviceObject = FdoDeviceObject;
578     FdoExtension->PdoDeviceObject = DeviceObject;
579 
580     /* Attach to ACPI */
581     FdoExtension->AttachedDevice = IoAttachDeviceToDeviceStack(FdoDeviceObject,
582                                                                DeviceObject);
583     if (!FdoExtension->AttachedDevice)
584     {
585         /* Destroy and fail */
586         CmBattDestroyFdo(FdoExtension->FdoDeviceObject);
587         if (CmBattDebug & 0xC)
588             DbgPrint("CmBattCreateFdo: IoAttachDeviceToDeviceStack failed.\n");
589         return STATUS_UNSUCCESSFUL;
590     }
591 
592     /* Get ACPI interface for EVAL */
593     Status = CmBattGetAcpiInterfaces(FdoExtension->AttachedDevice,
594                                      &FdoExtension->AcpiInterface);
595     if (!FdoExtension->AttachedDevice)
596     {
597         /* Detach, destroy, and fail */
598         IoDetachDevice(FdoExtension->AttachedDevice);
599         CmBattDestroyFdo(FdoExtension->FdoDeviceObject);
600         if (CmBattDebug & 0xC)
601             DbgPrint("CmBattCreateFdo: Could not get ACPI interfaces: %x\n", Status);
602         return STATUS_UNSUCCESSFUL;
603     }
604 
605     /* Setup the rest of the extension */
606     ExInitializeFastMutex(&FdoExtension->FastMutex);
607     IoInitializeRemoveLock(&FdoExtension->RemoveLock, 'RbmC', 0, 0);
608     FdoExtension->HandleCount = 0;
609     FdoExtension->WaitWakeEnable = FALSE;
610     FdoExtension->DeviceId = UniqueId;
611     FdoExtension->DeviceName = NULL;
612     FdoExtension->DelayNotification = FALSE;
613     FdoExtension->ArFlag = 0;
614 
615     /* Open the device key */
616     Status = IoOpenDeviceRegistryKey(DeviceObject,
617                                      PLUGPLAY_REGKEY_DEVICE,
618                                      KEY_READ,
619                                      &KeyHandle);
620     if (NT_SUCCESS(Status))
621     {
622         /* Read wait wake value */
623         RtlInitUnicodeString(&KeyString, L"WaitWakeEnabled");
624         Status = ZwQueryValueKey(KeyHandle,
625                                  &KeyString,
626                                  KeyValuePartialInformation,
627                                  PartialInfo,
628                                  sizeof(Buffer),
629                                  &ResultLength);
630         if (NT_SUCCESS(Status))
631         {
632             /* Set value */
633             FdoExtension->WaitWakeEnable = ((*(PULONG)PartialInfo->Data) != 0);
634         }
635 
636         /* Close the handle */
637         ZwClose(KeyHandle);
638     }
639 
640     /* Return success and the new FDO */
641     *NewDeviceObject = FdoDeviceObject;
642     if (CmBattDebug & 0x220)
643         DbgPrint("CmBattCreateFdo: Created FDO %x\n", FdoDeviceObject);
644     return STATUS_SUCCESS;
645 }
646 
647 NTSTATUS
648 NTAPI
649 CmBattAddBattery(IN PDRIVER_OBJECT DriverObject,
650                  IN PDEVICE_OBJECT DeviceObject)
651 {
652     BATTERY_MINIPORT_INFO MiniportInfo;
653     NTSTATUS Status;
654     PDEVICE_OBJECT FdoDeviceObject;
655     PCMBATT_DEVICE_EXTENSION FdoExtension;
656     PAGED_CODE();
657     if (CmBattDebug & 0x220)
658         DbgPrint("CmBattAddBattery: pdo %x\n", DeviceObject);
659 
660     /* Create the FDO */
661     Status = CmBattCreateFdo(DriverObject,
662                              DeviceObject,
663                              sizeof(CMBATT_DEVICE_EXTENSION),
664                              &FdoDeviceObject);
665     if (!NT_SUCCESS(Status))
666     {
667         if (CmBattDebug & 0xC)
668             DbgPrint("CmBattAddBattery: error (0x%x) creating Fdo\n", Status);
669         return Status;
670     }
671 
672     /* Build the FDO extension, check if we support trip points */
673     FdoExtension = FdoDeviceObject->DeviceExtension;
674     FdoExtension->FdoType = CmBattBattery;
675     FdoExtension->Started = 0;
676     FdoExtension->NotifySent = TRUE;
677     InterlockedExchange(&FdoExtension->ArLockValue, 0);
678     FdoExtension->TripPointValue = BATTERY_UNKNOWN_CAPACITY;
679     FdoExtension->Tag = 0;
680     FdoExtension->InterruptTime = KeQueryInterruptTime();
681     FdoExtension->TripPointSet = CmBattSetTripPpoint(FdoExtension, 0) !=
682                                  STATUS_OBJECT_NAME_NOT_FOUND;
683 
684     /* Setup the battery miniport information structure */
685     RtlZeroMemory(&MiniportInfo, sizeof(MiniportInfo));
686     MiniportInfo.Pdo = DeviceObject;
687     MiniportInfo.MajorVersion = BATTERY_CLASS_MAJOR_VERSION;
688     MiniportInfo.MinorVersion = BATTERY_CLASS_MINOR_VERSION;
689     MiniportInfo.Context = FdoExtension;
690     MiniportInfo.QueryTag = (PVOID)CmBattQueryTag;
691     MiniportInfo.QueryInformation = (PVOID)CmBattQueryInformation;
692     MiniportInfo.SetInformation = NULL;
693     MiniportInfo.QueryStatus = (PVOID)CmBattQueryStatus;
694     MiniportInfo.SetStatusNotify = (PVOID)CmBattSetStatusNotify;
695     MiniportInfo.DisableStatusNotify = (PVOID)CmBattDisableStatusNotify;
696     MiniportInfo.DeviceName = FdoExtension->DeviceName;
697 
698     /* Register with the class driver */
699     Status = BatteryClassInitializeDevice(&MiniportInfo, &FdoExtension->ClassData);
700     if (!NT_SUCCESS(Status))
701     {
702         IoDetachDevice(FdoExtension->AttachedDevice);
703         CmBattDestroyFdo(FdoExtension->FdoDeviceObject);
704         if (CmBattDebug & 0xC)
705             DbgPrint("CmBattAddBattery: error (0x%x) registering with class\n", Status);
706         return Status;
707     }
708 
709     /* Register WMI */
710     Status = CmBattWmiRegistration(FdoExtension);
711     if (!NT_SUCCESS(Status))
712     {
713         if (CmBattDebug & 0xC)
714             DbgPrint("CmBattAddBattery: Could not register as a WMI provider, status = %Lx\n", Status);
715         return Status;
716     }
717 
718     /* Register ACPI */
719     Status = FdoExtension->AcpiInterface.RegisterForDeviceNotifications(FdoExtension->AcpiInterface.Context,
720                                                                         (PVOID)CmBattNotifyHandler,
721                                                                         FdoExtension);
722     if (!NT_SUCCESS(Status))
723     {
724         CmBattWmiDeRegistration(FdoExtension);
725         BatteryClassUnload(FdoExtension->ClassData);
726         IoDetachDevice(FdoExtension->AttachedDevice);
727         CmBattDestroyFdo(FdoExtension->FdoDeviceObject);
728         if (CmBattDebug & 0xC)
729             DbgPrint("CmBattAddBattery: Could not register for battery notify, status = %Lx\n", Status);
730     }
731 
732     /* Return status */
733     return Status;
734 }
735 
736 NTSTATUS
737 NTAPI
738 CmBattAddAcAdapter(IN PDRIVER_OBJECT DriverObject,
739                    IN PDEVICE_OBJECT PdoDeviceObject)
740 {
741     PDEVICE_OBJECT FdoDeviceObject;
742     NTSTATUS Status;
743     PCMBATT_DEVICE_EXTENSION DeviceExtension;
744     PAGED_CODE();
745     if (CmBattDebug & 0x220)
746         DbgPrint("CmBattAddAcAdapter: pdo %x\n", PdoDeviceObject);
747 
748     /* Check if we already have an AC adapter */
749     if (AcAdapterPdo)
750     {
751         /* Don't do anything */
752         if (CmBattDebug & 0xC)
753             DbgPrint("CmBatt: Second AC adapter found.  Current version of driver only supports 1 adapter.\n");
754     }
755     else
756     {
757         /* Set this as the AC adapter's PDO */
758         AcAdapterPdo = PdoDeviceObject;
759     }
760 
761     /* Create the FDO for the adapter */
762     Status = CmBattCreateFdo(DriverObject,
763                              PdoDeviceObject,
764                              sizeof(CMBATT_DEVICE_EXTENSION),
765                              &FdoDeviceObject);
766     if (!NT_SUCCESS(Status))
767     {
768         /* Fail */
769         if (CmBattDebug & 0xC)
770             DbgPrint("CmBattAddAcAdapter: error (0x%x) creating Fdo\n", Status);
771         return Status;
772     }
773 
774     /* Set the type and do WMI registration */
775     DeviceExtension = FdoDeviceObject->DeviceExtension;
776     DeviceExtension->FdoType = CmBattAcAdapter;
777     Status = CmBattWmiRegistration(DeviceExtension);
778     if (!NT_SUCCESS(Status))
779     {
780         /* We can go on without WMI */
781         if (CmBattDebug & 0xC)
782             DbgPrint("CmBattAddBattery: Could not register as a WMI provider, status = %Lx\n", Status);
783     }
784 
785     /* Register with ACPI */
786     Status = DeviceExtension->AcpiInterface.RegisterForDeviceNotifications(DeviceExtension->AcpiInterface.Context,
787                                                                            (PVOID)CmBattNotifyHandler,
788                                                                            DeviceExtension);
789     if (!(NT_SUCCESS(Status)) && (CmBattDebug & 0xC))
790         DbgPrint("CmBattAddAcAdapter: Could not register for power notify, status = %Lx\n", Status);
791 
792     /* Send the first manual notification */
793     CmBattNotifyHandler(DeviceExtension, ACPI_BATT_NOTIFY_STATUS);
794     return STATUS_SUCCESS;
795 }
796 
797 NTSTATUS
798 NTAPI
799 CmBattAddDevice(IN PDRIVER_OBJECT DriverObject,
800                 IN PDEVICE_OBJECT PdoDeviceObject)
801 {
802     NTSTATUS Status;
803     HANDLE KeyHandle;
804     ULONG ResultLength;
805     UNICODE_STRING KeyString;
806     UCHAR Buffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(ULONG)];
807     PKEY_VALUE_PARTIAL_INFORMATION PartialInfo = (PVOID)Buffer;
808     ULONG PowerSourceType;
809     PAGED_CODE();
810     if (CmBattDebug & 0x220)
811         DbgPrint("CmBattAddDevice: Entered with pdo %x\n", PdoDeviceObject);
812 
813     /* Make sure we have a PDO */
814     if (!PdoDeviceObject)
815     {
816         /* Should not be having as one */
817         if (CmBattDebug & 0x24) DbgPrint("CmBattAddDevice: Asked to do detection\n");
818         return STATUS_NO_MORE_ENTRIES;
819     }
820 
821     /* Open the driver key */
822     Status = IoOpenDeviceRegistryKey(PdoDeviceObject,
823                                      PLUGPLAY_REGKEY_DRIVER,
824                                      KEY_READ,
825                                      &KeyHandle);
826     if (!NT_SUCCESS(Status))
827     {
828         if (CmBattDebug & 0xC)
829             DbgPrint("CmBattAddDevice: Could not get the software branch: %x\n", Status);
830         return Status;
831     }
832 
833     /* Read the power source type */
834     RtlInitUnicodeString(&KeyString, L"PowerSourceType");
835     Status = ZwQueryValueKey(KeyHandle,
836                              &KeyString,
837                              KeyValuePartialInformation,
838                              PartialInfo,
839                              sizeof(Buffer),
840                              &ResultLength);
841     ZwClose(KeyHandle);
842     if (!NT_SUCCESS(Status))
843     {
844         /* We need the data, fail without it */
845         if (CmBattDebug & 0xC)
846             DbgPrint("CmBattAddDevice: Could not read the power type identifier: %x\n", Status);
847         return Status;
848     }
849 
850     /* Check what kind of power source this is */
851     PowerSourceType = *(PULONG)PartialInfo->Data;
852     if (PowerSourceType == 1)
853     {
854         /* Create an AC adapter */
855         Status = CmBattAddAcAdapter(DriverObject, PdoDeviceObject);
856     }
857     else if (PowerSourceType == 0)
858     {
859         /* Create a battery */
860         Status = CmBattAddBattery(DriverObject, PdoDeviceObject);
861     }
862     else
863     {
864         /* Unknown type, fail */
865         if (CmBattDebug & 0xC)
866             DbgPrint("CmBattAddDevice: Invalid POWER_SOURCE_TYPE == %d \n", PowerSourceType);
867         return STATUS_UNSUCCESSFUL;
868     }
869 
870     /* Return whatever the FDO creation routine did */
871     return Status;
872 }
873 
874 /* EOF */
875