5966cda482625be80211f2cafba2365c90e37ea8
[reactos.git] / reactos / drivers / bus / acpi / compbatt / comppnp.c
1 /*
2 * PROJECT: ReactOS Composite Battery Driver
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * FILE: boot/drivers/bus/acpi/compbatt/comppnp.c
5 * PURPOSE: Plug-and-Play IOCTL/IRP Handling
6 * PROGRAMMERS: ReactOS Portable Systems Group
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #include "compbatt.h"
12
13 #include <wdmguid.h>
14
15 /* FUNCTIONS ******************************************************************/
16
17 NTSTATUS
18 NTAPI
19 CompBattPowerDispatch(IN PDEVICE_OBJECT DeviceObject,
20 IN PIRP Irp)
21 {
22 PCOMPBATT_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
23 if (CompBattDebug & 1) DbgPrint("CompBatt: PowerDispatch received power IRP.\n");
24
25 /* Start the next IRP */
26 PoStartNextPowerIrp(Irp);
27
28 /* Call the next driver in the stack */
29 IoSkipCurrentIrpStackLocation(Irp);
30 return PoCallDriver(DeviceExtension->AttachedDevice, Irp);
31 }
32
33 PCOMPBATT_BATTERY_DATA
34 NTAPI
35 RemoveBatteryFromList(IN PCUNICODE_STRING BatteryName,
36 IN PCOMPBATT_DEVICE_EXTENSION DeviceExtension)
37 {
38 PLIST_ENTRY ListHead, NextEntry;
39 PCOMPBATT_BATTERY_DATA BatteryData;
40 if (CompBattDebug & 1)
41 DbgPrint("CompBatt: ENTERING RemoveBatteryFromList\n");
42
43 /* Loop the battery list */
44 ExAcquireFastMutex(&DeviceExtension->Lock);
45 ListHead = &DeviceExtension->BatteryList;
46 NextEntry = ListHead->Flink;
47 while (NextEntry != ListHead)
48 {
49 /* Get the battery information and compare the name */
50 BatteryData = CONTAINING_RECORD(NextEntry, COMPBATT_BATTERY_DATA, BatteryLink);
51 if (!RtlCompareUnicodeString(BatteryName, &BatteryData->BatteryName, TRUE))
52 {
53 /* Flush pending deletes and any lock waiters */
54 IoAcquireRemoveLock(&BatteryData->RemoveLock, 0);
55 ExReleaseFastMutex(&DeviceExtension->Lock);
56 IoReleaseRemoveLockAndWait(&BatteryData->RemoveLock, 0);
57
58 /* Remove the entry from the list */
59 ExAcquireFastMutex(&DeviceExtension->Lock);
60 RemoveEntryList(&BatteryData->BatteryLink);
61 ExReleaseFastMutex(&DeviceExtension->Lock);
62 return BatteryData;
63 }
64
65 /* Next */
66 NextEntry = NextEntry->Flink;
67 }
68
69 /* Done */
70 ExReleaseFastMutex(&DeviceExtension->Lock);
71 if (CompBattDebug & 1) DbgPrint("CompBatt: EXITING RemoveBatteryFromList\n");
72 return STATUS_SUCCESS;
73 }
74
75 BOOLEAN
76 NTAPI
77 IsBatteryAlreadyOnList(IN PCUNICODE_STRING BatteryName,
78 IN PCOMPBATT_DEVICE_EXTENSION DeviceExtension)
79 {
80 PLIST_ENTRY ListHead, NextEntry;
81 PCOMPBATT_BATTERY_DATA BatteryData;
82 BOOLEAN Found = FALSE;
83 if (CompBattDebug & 1)
84 DbgPrint("CompBatt: ENTERING IsBatteryAlreadyOnList\n");
85
86 /* Loop the battery list */
87 ExAcquireFastMutex(&DeviceExtension->Lock);
88 ListHead = &DeviceExtension->BatteryList;
89 NextEntry = ListHead->Flink;
90 while (NextEntry != ListHead)
91 {
92 /* Get the battery information and compare the name */
93 BatteryData = CONTAINING_RECORD(NextEntry, COMPBATT_BATTERY_DATA, BatteryLink);
94 if (!RtlCompareUnicodeString(BatteryName, &BatteryData->BatteryName, TRUE))
95 {
96 /* Got it */
97 Found = TRUE;
98 break;
99 }
100
101 /* Next */
102 NextEntry = NextEntry->Flink;
103 }
104
105 /* Release the lock and return search status */
106 ExReleaseFastMutex(&DeviceExtension->Lock);
107 if (CompBattDebug & 1) DbgPrint("CompBatt: EXITING IsBatteryAlreadyOnList\n");
108 return Found;
109 }
110
111 NTSTATUS
112 NTAPI
113 CompBattAddNewBattery(IN PUNICODE_STRING BatteryName,
114 IN PCOMPBATT_DEVICE_EXTENSION DeviceExtension)
115 {
116 NTSTATUS Status = STATUS_SUCCESS;
117 PCOMPBATT_BATTERY_DATA BatteryData;
118 PIRP Irp;
119 PIO_STACK_LOCATION IoStackLocation;
120 PFILE_OBJECT FileObject;
121 PAGED_CODE();
122 if (CompBattDebug & 1)
123 DbgPrint("CompBatt: ENTERING AddNewBattery \"%w\" \n", BatteryName->Buffer);
124
125 /* Is this a new battery? */
126 if (!IsBatteryAlreadyOnList(BatteryName, DeviceExtension))
127 {
128 /* Allocate battery data */
129 BatteryData = ExAllocatePoolWithTag(NonPagedPool,
130 sizeof(COMPBATT_BATTERY_DATA) +
131 BatteryName->Length,
132 'CtaB');
133 if (BatteryData)
134 {
135 /* Initialize the data and write the battery name */
136 RtlZeroMemory(BatteryData, sizeof(COMPBATT_BATTERY_DATA));
137 BatteryData->Tag = 0;
138 BatteryData->BatteryName.MaximumLength = BatteryName->Length;
139 BatteryData->BatteryName.Buffer = (PWCHAR)(BatteryData + 1);
140 RtlCopyUnicodeString(&BatteryData->BatteryName, BatteryName);
141
142 /* Get the device object */
143 Status = CompBattGetDeviceObjectPointer(BatteryName,
144 FILE_ALL_ACCESS,
145 &FileObject,
146 &BatteryData->DeviceObject);
147 if (NT_SUCCESS(Status))
148 {
149 /* Reference the DO and drop the FO */
150 ObReferenceObject(BatteryData->DeviceObject);
151 ObDereferenceObject(FileObject);
152
153 /* Allocate the battery IRP */
154 Irp = IoAllocateIrp(BatteryData->DeviceObject->StackSize + 1, 0);
155 if (Irp)
156 {
157 /* Save it */
158 BatteryData->Irp = Irp;
159
160 /* Setup the stack location */
161 IoStackLocation = IoGetNextIrpStackLocation(Irp);
162 IoStackLocation->Parameters.Others.Argument1 = DeviceExtension;
163 IoStackLocation->Parameters.Others.Argument2 = BatteryData;
164
165 /* Set IRP data */
166 IoSetNextIrpStackLocation(Irp);
167 Irp->IoStatus.Status = STATUS_DEVICE_NOT_CONNECTED;
168 BatteryData->WaitFlag = 0;
169
170 /* Insert this battery in the list */
171 ExAcquireFastMutex(&DeviceExtension->Lock);
172 InsertTailList(&DeviceExtension->BatteryList,
173 &BatteryData->BatteryLink);
174 ExReleaseFastMutex(&DeviceExtension->Lock);
175
176 /* Initialize the work item and delete lock */
177 IoInitializeRemoveLock(&BatteryData->RemoveLock, 0, 0, 0);
178 ExInitializeWorkItem(&BatteryData->WorkItem,
179 (PVOID)CompBattMonitorIrpCompleteWorker,
180 BatteryData);
181
182 /* Setup the IRP work entry */
183 CompBattMonitorIrpComplete(BatteryData->DeviceObject, Irp, 0);
184 Status = STATUS_SUCCESS;
185 }
186 else
187 {
188 /* Fail, no memory */
189 if (CompBattDebug & 8)
190 DbgPrint("CompBatt: Couldn't allocate new battery Irp\n");
191 Status = STATUS_INSUFFICIENT_RESOURCES;
192 ObDereferenceObject(BatteryData->DeviceObject);
193 }
194 }
195 else if (CompBattDebug & 8)
196 {
197 /* Fail */
198 DbgPrint("CompBattAddNewBattery: Failed to get device Object. status = %lx\n",
199 Status);
200 }
201
202 /* Free the battery data */
203 ExFreePool(BatteryData);
204 }
205 else
206 {
207 /* Fail, no memory */
208 if (CompBattDebug & 8)
209 DbgPrint("CompBatt: Couldn't allocate new battery node\n");
210 Status = STATUS_INSUFFICIENT_RESOURCES;
211 }
212 }
213
214 /* We're done */
215 if (CompBattDebug & 1) DbgPrint("CompBatt: EXITING AddNewBattery\n");
216 return Status;
217 }
218
219 NTSTATUS
220 NTAPI
221 CompBattRemoveBattery(IN PCUNICODE_STRING BatteryName,
222 IN PCOMPBATT_DEVICE_EXTENSION DeviceExtension)
223 {
224 PCOMPBATT_BATTERY_DATA BatteryData;
225 if (CompBattDebug & 1) DbgPrint("CompBatt: RemoveBattery\n");
226
227 /* Remove the entry */
228 BatteryData = RemoveBatteryFromList(BatteryName, DeviceExtension);
229 if (BatteryData)
230 {
231 /* Dereference and free it */
232 ObDereferenceObject(BatteryData->DeviceObject);
233 ExFreePool(BatteryData);
234
235 /* Notify class driver */
236 DeviceExtension->Flags = 0;
237 BatteryClassStatusNotify(DeviceExtension->ClassData);
238 }
239
240 /* It's done */
241 return STATUS_SUCCESS;
242 }
243
244 NTSTATUS
245 NTAPI
246 CompBattGetBatteries(IN PCOMPBATT_DEVICE_EXTENSION DeviceExtension)
247 {
248 PWCHAR p;
249 NTSTATUS Status;
250 PWCHAR LinkList;
251 UNICODE_STRING LinkString;
252 if (CompBattDebug & 1) DbgPrint("CompBatt: ENTERING GetBatteries\n");
253
254 /* Get all battery links */
255 Status = IoGetDeviceInterfaces(&GUID_DEVICE_BATTERY, NULL, 0, &LinkList);
256 p = LinkList;
257 if (NT_SUCCESS(Status))
258 {
259 /* Loop all strings inside */
260 while (TRUE)
261 {
262 /* Create the string */
263 RtlInitUnicodeString(&LinkString, p);
264 if (!LinkString.Length) break;
265
266 /* Add this battery and move on */
267 Status = CompBattAddNewBattery(&LinkString, DeviceExtension);
268 p += (LinkString.Length / sizeof(WCHAR)) + sizeof(UNICODE_NULL);
269 }
270
271 /* Parsing complete, clean up buffer */
272 ExFreePool(LinkList);
273 }
274 else if (CompBattDebug & 8)
275 {
276 /* Fail */
277 DbgPrint("CompBatt: Couldn't get list of batteries\n");
278 }
279
280 /* Done */
281 if (CompBattDebug & 1) DbgPrint("CompBatt: EXITING GetBatteries\n");
282 return Status;
283 }
284
285 NTSTATUS
286 NTAPI
287 CompBattPnpEventHandler(IN PDEVICE_INTERFACE_CHANGE_NOTIFICATION Notification,
288 IN PCOMPBATT_DEVICE_EXTENSION DeviceExtension)
289 {
290 if (CompBattDebug & 1) DbgPrint("CompBatt: ENTERING PnpEventHandler\n");
291 if (CompBattDebug & 2) DbgPrint("CompBatt: Received device interface change notification\n");
292
293 /* Check what happened */
294 if (IsEqualGUIDAligned(&Notification->Event, &GUID_DEVICE_INTERFACE_ARRIVAL))
295 {
296 /* Add the new battery */
297 if (CompBattDebug & 2)
298 DbgPrint("CompBatt: Received notification of battery arrival\n");
299 CompBattAddNewBattery(Notification->SymbolicLinkName, DeviceExtension);
300 }
301 else if (IsEqualGUIDAligned(&Notification->Event, &GUID_DEVICE_INTERFACE_REMOVAL))
302 {
303 /* Don't do anything */
304 if (CompBattDebug & 2)
305 DbgPrint("CompBatt: Received notification of battery removal\n");
306 }
307 else
308 {
309 /* Shouldn't happen */
310 if (CompBattDebug & 2) DbgPrint("CompBatt: Received unhandled PnP event\n");
311 }
312
313 /* Done, return success */
314 if (CompBattDebug & 1) DbgPrint("CompBatt: EXITING PnpEventHandler\n");
315 return STATUS_SUCCESS;
316 }
317
318 NTSTATUS
319 NTAPI
320 CompBattAddDevice(IN PDRIVER_OBJECT DriverObject,
321 IN PDEVICE_OBJECT PdoDeviceObject)
322 {
323 NTSTATUS Status;
324 UNICODE_STRING DeviceName;
325 PCOMPBATT_DEVICE_EXTENSION DeviceExtension;
326 PDEVICE_OBJECT DeviceObject;
327 UNICODE_STRING SymbolicLinkName;
328 BATTERY_MINIPORT_INFO MiniportInfo;
329 if (CompBattDebug & 2) DbgPrint("CompBatt: Got an AddDevice - %x\n", PdoDeviceObject);
330
331 /* Create the device */
332 RtlInitUnicodeString(&DeviceName, L"\\Device\\CompositeBattery");
333 Status = IoCreateDevice(DriverObject,
334 sizeof(COMPBATT_DEVICE_EXTENSION),
335 &DeviceName,
336 FILE_DEVICE_BATTERY,
337 FILE_DEVICE_SECURE_OPEN,
338 FALSE,
339 &DeviceObject);
340 if (!NT_SUCCESS(Status)) return Status;
341
342 /* Setup symbolic link for Win32 access */
343 RtlInitUnicodeString(&SymbolicLinkName, L"\\DosDevices\\CompositeBattery");
344 IoCreateSymbolicLink(&SymbolicLinkName, &DeviceName);
345
346 /* Initialize the device extension */
347 DeviceExtension = DeviceObject->DeviceExtension;
348 RtlZeroMemory(DeviceExtension, sizeof(COMPBATT_DEVICE_EXTENSION));
349
350 /* Attach to device stack and set DO pointers */
351 DeviceExtension->AttachedDevice = IoAttachDeviceToDeviceStack(DeviceObject,
352 PdoDeviceObject);
353 DeviceExtension->DeviceObject = DeviceObject;
354 if (!DeviceExtension->AttachedDevice)
355 {
356 /* Fail */
357 if (CompBattDebug & 8)
358 DbgPrint("CompBattAddDevice: Could not attach to LowerDevice.\n");
359 IoDeleteDevice(DeviceObject);
360 return STATUS_UNSUCCESSFUL;
361 }
362
363 /* Set device object flags */
364 DeviceObject->Flags |= (DO_POWER_PAGABLE | DO_BUFFERED_IO);
365 DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
366
367 /* Setup the device extension */
368 ExInitializeFastMutex(&DeviceExtension->Lock);
369 InitializeListHead(&DeviceExtension->BatteryList);
370 DeviceExtension->Flags = 0;
371 DeviceExtension->NextTag = 1;
372
373 /* Setup the miniport data */
374 RtlZeroMemory(&MiniportInfo, sizeof(MiniportInfo));
375 MiniportInfo.MajorVersion = BATTERY_CLASS_MAJOR_VERSION;
376 MiniportInfo.MinorVersion = BATTERY_CLASS_MINOR_VERSION;
377 MiniportInfo.Context = DeviceExtension;
378 MiniportInfo.DeviceName = &DeviceName;
379 MiniportInfo.QueryTag = (BCLASS_QUERY_TAG)CompBattQueryTag;
380 MiniportInfo.QueryInformation = (BCLASS_QUERY_INFORMATION)CompBattQueryInformation;
381 MiniportInfo.SetInformation = NULL;
382 MiniportInfo.QueryStatus = (BCLASS_QUERY_STATUS)CompBattQueryStatus;
383 MiniportInfo.SetStatusNotify = (BCLASS_SET_STATUS_NOTIFY)CompBattSetStatusNotify;
384 MiniportInfo.DisableStatusNotify = (BCLASS_DISABLE_STATUS_NOTIFY)CompBattDisableStatusNotify;
385 MiniportInfo.Pdo = NULL;
386
387 /* Register with the class driver */
388 Status = BatteryClassInitializeDevice(&MiniportInfo,
389 &DeviceExtension->ClassData);
390 if (!NT_SUCCESS(Status))
391 {
392 /* Undo everything */
393 IoDetachDevice(DeviceExtension->AttachedDevice);
394 IoDeleteDevice(DeviceObject);
395 }
396
397 /* Return status */
398 return Status;
399 }
400
401 NTSTATUS
402 NTAPI
403 CompBattPnpDispatch(IN PDEVICE_OBJECT DeviceObject,
404 IN PIRP Irp)
405 {
406 PIO_STACK_LOCATION IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
407 NTSTATUS Status;
408 PCOMPBATT_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
409 if (CompBattDebug & 1) DbgPrint("CompBatt: ENTERING PnpDispatch\n");
410
411 /* Set default error */
412 Status = STATUS_NOT_SUPPORTED;
413
414 /* Check what kind of PnP function this is */
415 switch (IoStackLocation->MinorFunction)
416 {
417 case IRP_MN_START_DEVICE:
418
419 /* Device is starting, register for new batteries and pick up current ones */
420 Status = IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange,
421 0,
422 (PVOID)&GUID_DEVICE_BATTERY,
423 DeviceObject->DriverObject,
424 (PDRIVER_NOTIFICATION_CALLBACK_ROUTINE)CompBattPnpEventHandler,
425 DeviceExtension,
426 &DeviceExtension->NotificationEntry);
427 if (NT_SUCCESS(Status))
428 {
429 /* Now go get the batteries */
430 if (CompBattDebug & 2)
431 DbgPrint("CompBatt: Successfully registered for PnP notification\n");
432 Status = CompBattGetBatteries(DeviceExtension);
433 }
434 else
435 {
436 /* We failed */
437 if (CompBattDebug & 8)
438 DbgPrint("CompBatt: Couldn't register for PnP notification - %x\n",
439 Status);
440 }
441 break;
442 case IRP_MN_CANCEL_STOP_DEVICE:
443
444 /* Explicitly say ok */
445 Status = STATUS_SUCCESS;
446 break;
447
448 case IRP_MN_CANCEL_REMOVE_DEVICE:
449
450 /* Explicitly say ok */
451 Status = STATUS_SUCCESS;
452 break;
453
454 case IRP_MN_SURPRISE_REMOVAL:
455
456 /* Explicitly say ok */
457 Status = STATUS_SUCCESS;
458 break;
459
460 case IRP_MN_QUERY_PNP_DEVICE_STATE:
461
462 /* Add this in */
463 Irp->IoStatus.Information |= PNP_DEVICE_NOT_DISABLEABLE;
464 Status = STATUS_SUCCESS;
465 break;
466
467 default:
468
469 /* Not supported */
470 Status = STATUS_INVALID_DEVICE_REQUEST;
471 break;
472 }
473
474 /* Set IRP status if we have one */
475 if (Status != STATUS_NOT_SUPPORTED) Irp->IoStatus.Status = Status;
476
477 /* Did someone pick it up? */
478 if ((NT_SUCCESS(Status)) || (Status == STATUS_NOT_SUPPORTED))
479 {
480 /* Still unsupported, try ACPI */
481 IoSkipCurrentIrpStackLocation(Irp);
482 Status = IoCallDriver(DeviceExtension->AttachedDevice, Irp);
483 }
484 else
485 {
486 /* Complete the request */
487 Status = Irp->IoStatus.Status;
488 IoCompleteRequest(Irp, IO_NO_INCREMENT);
489 }
490
491 /* Release the remove lock and return status */
492 if (CompBattDebug & 1) DbgPrint("CompBatt: EXITING PnpDispatch\n");
493 return Status;
494 }
495
496 /* EOF */