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