Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1447 lines
37 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. cmbpnp.c
  5. Abstract:
  6. Control Method Battery Plug and Play support
  7. Author:
  8. Ron Mosgrove
  9. Environment:
  10. Kernel mode
  11. Revision History:
  12. --*/
  13. #include "CmBattp.h"
  14. #include <wdmguid.h>
  15. #include <string.h>
  16. //
  17. // Device Names
  18. //
  19. PCWSTR CmBattDeviceName = L"\\Device\\ControlMethodBattery";
  20. PCWSTR AcAdapterName = L"\\Device\\AcAdapter";
  21. //
  22. // Power Source Type registry key
  23. //
  24. PCWSTR PowerSourceType = L"PowerSourceType";
  25. #define POWER_SOURCE_TYPE_BATTERY 0
  26. #define POWER_SOURCE_TYPE_AC_ADAPTER 1
  27. //
  28. // WaitWake registry key
  29. //
  30. PCWSTR WaitWakeEnableKey = L"WaitWakeEnabled";
  31. //
  32. // Globals
  33. //
  34. PDEVICE_OBJECT AcAdapterPdo = NULL;
  35. //
  36. // Prototypes
  37. //
  38. NTSTATUS
  39. CmBattAddDevice(
  40. IN PDRIVER_OBJECT DriverObject,
  41. IN PDEVICE_OBJECT Pdo
  42. );
  43. NTSTATUS
  44. CmBattRemoveDevice(
  45. IN PDEVICE_OBJECT DeviceObject,
  46. IN PIRP Irp
  47. );
  48. NTSTATUS
  49. CmBattGetAcpiInterfaces(
  50. IN PDEVICE_OBJECT LowerDevice,
  51. OUT PACPI_INTERFACE_STANDARD AcpiInterfaces
  52. );
  53. NTSTATUS
  54. CmBattAddBattery(
  55. IN PDRIVER_OBJECT DriverObject,
  56. IN PDEVICE_OBJECT Pdo
  57. );
  58. NTSTATUS
  59. CmBattAddAcAdapter(
  60. IN PDRIVER_OBJECT DriverObject,
  61. IN PDEVICE_OBJECT Pdo
  62. );
  63. NTSTATUS
  64. CmBattCreateFdo(
  65. IN PDRIVER_OBJECT DriverObject,
  66. IN PDEVICE_OBJECT Pdo,
  67. IN PWSTR DeviceName,
  68. IN ULONG ExtensionSize,
  69. OUT PDEVICE_OBJECT *NewFdo
  70. );
  71. VOID
  72. CmBattDestroyFdo(
  73. IN PDEVICE_OBJECT Fdo
  74. );
  75. NTSTATUS
  76. CmBattIoCompletion(
  77. IN PDEVICE_OBJECT DeviceObject,
  78. IN PIRP Irp,
  79. IN PKEVENT pdoIoCompletedEvent
  80. )
  81. /*++
  82. Routine Description:
  83. This routine catches completion notifications.
  84. Arguments:
  85. DeviceObject - Pointer to class device object.
  86. Irp - Pointer to the request packet.
  87. pdoIoCompletedEvent - the just completed event
  88. Return Value:
  89. Status is returned.
  90. --*/
  91. {
  92. CmBattPrint (CMBATT_TRACE, ("CmBattIoCompletion: Event (%x)\n", pdoIoCompletedEvent));
  93. KeSetEvent(pdoIoCompletedEvent, IO_NO_INCREMENT, FALSE);
  94. return STATUS_MORE_PROCESSING_REQUIRED;
  95. }
  96. NTSTATUS
  97. CmBattAddDevice(
  98. IN PDRIVER_OBJECT DriverObject,
  99. IN PDEVICE_OBJECT Pdo
  100. )
  101. /*++
  102. Routine Description:
  103. This routine creates functional device objects for each CmBatt controller in the
  104. system and attaches them to the physical device objects for the controllers
  105. Arguments:
  106. DriverObject - a pointer to the object for this driver
  107. PhysicalDeviceObject - a pointer to the physical object we need to attach to
  108. Return Value:
  109. Status from device creation and initialization
  110. --*/
  111. {
  112. NTSTATUS Status;
  113. HANDLE handle;
  114. UNICODE_STRING unicodeString;
  115. CHAR buffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION)+sizeof(ULONG)];
  116. ULONG unused;
  117. PAGED_CODE();
  118. CmBattPrint ((CMBATT_TRACE | CMBATT_PNP), ("CmBattAddDevice: Entered with pdo %x\n", Pdo));
  119. if (Pdo == NULL) {
  120. //
  121. // Have we been asked to do detection on our own?
  122. // if so just return no more devices
  123. //
  124. CmBattPrint((CMBATT_WARN | CMBATT_PNP), ("CmBattAddDevice: Asked to do detection\n"));
  125. return STATUS_NO_MORE_ENTRIES;
  126. }
  127. //
  128. // Get the software branch.
  129. //
  130. Status = IoOpenDeviceRegistryKey (Pdo,
  131. PLUGPLAY_REGKEY_DRIVER,
  132. STANDARD_RIGHTS_READ,
  133. &handle);
  134. if (!NT_SUCCESS(Status)) {
  135. CmBattPrint(CMBATT_ERROR, ("CmBattAddDevice: Could not get the software branch: %x\n", Status));
  136. return Status;
  137. }
  138. //
  139. // Check if this is for an AC adapter or a battery.
  140. //
  141. RtlInitUnicodeString (&unicodeString, PowerSourceType);
  142. Status = ZwQueryValueKey(
  143. handle,
  144. &unicodeString,
  145. KeyValuePartialInformation,
  146. &buffer,
  147. sizeof(buffer),
  148. &unused
  149. );
  150. ZwClose( handle );
  151. if (!NT_SUCCESS(Status)) {
  152. CmBattPrint(CMBATT_ERROR, ("CmBattAddDevice: Could not read the power type identifier: %x\n", Status));
  153. } else {
  154. switch (*(PULONG)&((PKEY_VALUE_PARTIAL_INFORMATION)buffer)->Data) {
  155. case POWER_SOURCE_TYPE_BATTERY:
  156. Status = CmBattAddBattery (DriverObject, Pdo);
  157. break;
  158. case POWER_SOURCE_TYPE_AC_ADAPTER:
  159. Status = CmBattAddAcAdapter (DriverObject, Pdo);
  160. break;
  161. default:
  162. CmBattPrint(CMBATT_ERROR, ("CmBattAddDevice: Invalid POWER_SOURCE_TYPE == %d \n", *(PULONG)&((PKEY_VALUE_PARTIAL_INFORMATION)buffer)->Data));
  163. Status = STATUS_UNSUCCESSFUL;
  164. break;
  165. }
  166. }
  167. //
  168. // Return the status.
  169. //
  170. return Status;
  171. }
  172. NTSTATUS
  173. CmBattAddBattery(
  174. IN PDRIVER_OBJECT DriverObject,
  175. IN PDEVICE_OBJECT Pdo
  176. )
  177. /*++
  178. Routine Description:
  179. This routine creates a functional device object for a CM battery, and attache it
  180. to the physical device object for the battery.
  181. Arguments:
  182. DriverObject - a pointer to the object for this driver
  183. PhysicalDeviceObject - a pointer to the physical object we need to attach to
  184. Return Value:
  185. Status from device creation and initialization
  186. --*/
  187. {
  188. PDEVICE_OBJECT Fdo = NULL;
  189. PCM_BATT CmBatt;
  190. NTSTATUS Status;
  191. BATTERY_MINIPORT_INFO BattInit;
  192. PAGED_CODE();
  193. CmBattPrint ((CMBATT_TRACE | CMBATT_PNP), ("CmBattAddBattery: pdo %x\n", Pdo));
  194. //
  195. // Create and initialize the new functional device object
  196. //
  197. Status = CmBattCreateFdo(DriverObject, Pdo, (PWSTR) CmBattDeviceName, sizeof(CM_BATT), &Fdo);
  198. if (!NT_SUCCESS(Status)) {
  199. CmBattPrint(CMBATT_ERROR, ("CmBattAddBattery: error (0x%x) creating Fdo\n", Status));
  200. return Status;
  201. }
  202. //
  203. // Initialize Fdo device extension data
  204. //
  205. CmBatt = (PCM_BATT) Fdo->DeviceExtension;
  206. CmBatt->Type = CM_BATTERY_TYPE;
  207. CmBatt->IsStarted = FALSE;
  208. CmBatt->ReCheckSta = TRUE;
  209. InterlockedExchange (&CmBatt->CacheState, 0);
  210. CmBatt->Info.Tag = BATTERY_TAG_INVALID;
  211. CmBatt->Alarm.Setting = CM_ALARM_INVALID;
  212. CmBatt->DischargeTime = KeQueryInterruptTime();
  213. if (CmBattSetTripPpoint (CmBatt, 0) == STATUS_OBJECT_NAME_NOT_FOUND) {
  214. CmBatt->Info.BtpExists = FALSE;
  215. } else {
  216. CmBatt->Info.BtpExists = TRUE;
  217. }
  218. //
  219. // Attach to the Class Driver
  220. //
  221. RtlZeroMemory (&BattInit, sizeof(BattInit));
  222. BattInit.MajorVersion = BATTERY_CLASS_MAJOR_VERSION;
  223. BattInit.MinorVersion = BATTERY_CLASS_MINOR_VERSION;
  224. BattInit.Context = CmBatt;
  225. BattInit.QueryTag = CmBattQueryTag;
  226. BattInit.QueryInformation = CmBattQueryInformation;
  227. BattInit.SetInformation = NULL; // tbd
  228. BattInit.QueryStatus = CmBattQueryStatus;
  229. BattInit.SetStatusNotify = CmBattSetStatusNotify;
  230. BattInit.DisableStatusNotify = CmBattDisableStatusNotify;
  231. BattInit.Pdo = Pdo;
  232. BattInit.DeviceName = CmBatt->DeviceName;
  233. Status = BatteryClassInitializeDevice (&BattInit, &CmBatt->Class);
  234. if (!NT_SUCCESS(Status)) {
  235. //
  236. // if we can't attach to class driver we're toast
  237. //
  238. CmBattPrint(CMBATT_ERROR, ("CmBattAddBattery: error (0x%x) registering with class\n", Status));
  239. IoDetachDevice (CmBatt->LowerDeviceObject);
  240. CmBattDestroyFdo (CmBatt->Fdo);
  241. return Status;
  242. }
  243. //
  244. // Register WMI support.
  245. //
  246. Status = CmBattWmiRegistration(CmBatt);
  247. if (!NT_SUCCESS(Status)) {
  248. //
  249. // WMI support is not critical to operation. Just log an error.
  250. //
  251. CmBattPrint(CMBATT_ERROR,
  252. ("CmBattAddBattery: Could not register as a WMI provider, status = %Lx\n", Status));
  253. }
  254. //
  255. // Register the battery notify handler for this battery with ACPI
  256. // This registration is performed after registering with the battery
  257. // class because CmBattNotifyHandler must not be run until the battery
  258. // class is ready.
  259. //
  260. Status = CmBatt->AcpiInterfaces.RegisterForDeviceNotifications (
  261. CmBatt->AcpiInterfaces.Context,
  262. CmBattNotifyHandler,
  263. CmBatt);
  264. if (!NT_SUCCESS(Status)) {
  265. CmBattPrint(CMBATT_ERROR,
  266. ("CmBattAddBattery: Could not register for battery notify, status = %Lx\n", Status));
  267. CmBattWmiDeRegistration(CmBatt);
  268. BatteryClassUnload (CmBatt->Class);
  269. IoDetachDevice (CmBatt->LowerDeviceObject);
  270. CmBattDestroyFdo (CmBatt->Fdo);
  271. return Status;
  272. }
  273. return STATUS_SUCCESS;
  274. }
  275. NTSTATUS
  276. CmBattAddAcAdapter(
  277. IN PDRIVER_OBJECT DriverObject,
  278. IN PDEVICE_OBJECT Pdo
  279. )
  280. /*++
  281. Routine Description:
  282. This routine registers a notify handler for the AC Adapter. And saves the PDO so we can run
  283. the _STA method against it to get the AC status.
  284. Arguments:
  285. DriverObject - a pointer to the object for this driver
  286. Pdo - a pointer to the Pdo
  287. Return Value:
  288. Status from device creation and initialization
  289. --*/
  290. {
  291. PDEVICE_OBJECT Fdo;
  292. NTSTATUS Status;
  293. PAC_ADAPTER acExtension;
  294. PAGED_CODE();
  295. CmBattPrint ((CMBATT_TRACE | CMBATT_PNP), ("CmBattAddAcAdapter: pdo %x\n", Pdo));
  296. //
  297. // Save PDO so we can run _STA method on it later
  298. //
  299. if (AcAdapterPdo != NULL) {
  300. CmBattPrint(CMBATT_ERROR, ("CmBatt: Second AC adapter found. Current version of driver only supports 1 aadapter.\n"));
  301. } else {
  302. AcAdapterPdo = Pdo;
  303. }
  304. Status = CmBattCreateFdo(DriverObject, Pdo, (PWSTR) AcAdapterName, sizeof(AC_ADAPTER), &Fdo);
  305. if (!NT_SUCCESS(Status)) {
  306. CmBattPrint(CMBATT_ERROR, ("CmBattAddAcAdapter: error (0x%x) creating Fdo\n", Status));
  307. return Status;
  308. }
  309. //
  310. // Initialize Fdo device extension data
  311. //
  312. acExtension = (PAC_ADAPTER) Fdo->DeviceExtension;
  313. acExtension->Type = AC_ADAPTER_TYPE;
  314. //
  315. // Register WMI support.
  316. //
  317. Status = CmBattWmiRegistration((PCM_BATT)acExtension);
  318. if (!NT_SUCCESS(Status)) {
  319. //
  320. // WMI support is not critical to operation. Just log an error.
  321. //
  322. CmBattPrint(CMBATT_ERROR,
  323. ("CmBattAddBattery: Could not register as a WMI provider, status = %Lx\n", Status));
  324. }
  325. //
  326. // Register the AC adapter notify handler with ACPI
  327. //
  328. Status = acExtension->AcpiInterfaces.RegisterForDeviceNotifications (
  329. acExtension->AcpiInterfaces.Context,
  330. CmBattNotifyHandler,
  331. acExtension);
  332. //
  333. // We will ignore errors, since this is not a critical operation
  334. //
  335. if (!NT_SUCCESS(Status)) {
  336. CmBattPrint(CMBATT_ERROR,
  337. ("CmBattAddAcAdapter: Could not register for power notify, status = %Lx\n", Status));
  338. }
  339. //
  340. // Give one notification, to make sure all batteries get updated.
  341. //
  342. CmBattNotifyHandler (acExtension, BATTERY_STATUS_CHANGE);
  343. return STATUS_SUCCESS;
  344. }
  345. NTSTATUS
  346. CmBattGetAcpiInterfaces(
  347. IN PDEVICE_OBJECT LowerDevice,
  348. OUT PACPI_INTERFACE_STANDARD AcpiInterfaces
  349. )
  350. /*++
  351. Routine Description:
  352. Call ACPI driver to get the direct-call interfaces. It does
  353. this the first time it is called, no more.
  354. Arguments:
  355. None.
  356. Return Value:
  357. Status
  358. --*/
  359. {
  360. NTSTATUS Status = STATUS_SUCCESS;
  361. PIRP Irp;
  362. PIO_STACK_LOCATION IrpSp;
  363. KEVENT syncEvent;
  364. //
  365. // Allocate an IRP for below
  366. //
  367. Irp = IoAllocateIrp (LowerDevice->StackSize, FALSE); // Get stack size from PDO
  368. if (!Irp) {
  369. CmBattPrint((CMBATT_ERROR),
  370. ("CmBattGetAcpiInterfaces: Failed to allocate Irp\n"));
  371. return STATUS_INSUFFICIENT_RESOURCES;
  372. }
  373. IrpSp = IoGetNextIrpStackLocation(Irp);
  374. //
  375. // Use QUERY_INTERFACE to get the address of the direct-call ACPI interfaces.
  376. //
  377. IrpSp->MajorFunction = IRP_MJ_PNP;
  378. IrpSp->MinorFunction = IRP_MN_QUERY_INTERFACE;
  379. Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
  380. IrpSp->Parameters.QueryInterface.InterfaceType = (LPGUID) &GUID_ACPI_INTERFACE_STANDARD;
  381. IrpSp->Parameters.QueryInterface.Version = 1;
  382. IrpSp->Parameters.QueryInterface.Size = sizeof (*AcpiInterfaces);
  383. IrpSp->Parameters.QueryInterface.Interface = (PINTERFACE) AcpiInterfaces;
  384. IrpSp->Parameters.QueryInterface.InterfaceSpecificData = NULL;
  385. //
  386. // Initialize an event so this will be a syncronous call.
  387. //
  388. KeInitializeEvent(&syncEvent, SynchronizationEvent, FALSE);
  389. IoSetCompletionRoutine (Irp, CmBattIoCompletion, &syncEvent, TRUE, TRUE, TRUE);
  390. //
  391. // Call ACPI
  392. //
  393. Status = IoCallDriver (LowerDevice, Irp);
  394. //
  395. // Wait if necessary, then clean up.
  396. //
  397. if (Status == STATUS_PENDING) {
  398. KeWaitForSingleObject(&syncEvent, Executive, KernelMode, FALSE, NULL);
  399. Status = Irp->IoStatus.Status;
  400. }
  401. IoFreeIrp (Irp);
  402. if (!NT_SUCCESS(Status)) {
  403. CmBattPrint(CMBATT_ERROR,
  404. ("CmBattGetAcpiInterfaces: Could not get ACPI driver interfaces, status = %x\n", Status));
  405. }
  406. return Status;
  407. }
  408. NTSTATUS
  409. CmBattCreateFdo(
  410. IN PDRIVER_OBJECT DriverObject,
  411. IN PDEVICE_OBJECT Pdo,
  412. IN PWSTR DeviceName,
  413. IN ULONG ExtensionSize,
  414. OUT PDEVICE_OBJECT *NewFdo
  415. )
  416. /*++
  417. Routine Description:
  418. This routine will create and initialize a functional device object to
  419. be attached to a Control Method Battery PDO.
  420. Arguments:
  421. DriverObject - a pointer to the driver object this is created under
  422. DeviceName - The namde of the device to create
  423. ExtensionSize - device extension size: sizeof (CM_BATT) or sizeof (AC_ADAPTER)
  424. NewFdo - a location to store the pointer to the new device object
  425. Return Value:
  426. STATUS_SUCCESS if everything was successful
  427. reason for failure otherwise
  428. --*/
  429. {
  430. PDEVICE_OBJECT fdo;
  431. NTSTATUS status;
  432. PCM_BATT cmBatt;
  433. PUNICODE_STRING unicodeString;
  434. ULONG uniqueId;
  435. USHORT strLength = 0;
  436. UNICODE_STRING numberString;
  437. WCHAR numberBuffer[10];
  438. HANDLE devInstRegKey;
  439. UNICODE_STRING valueName;
  440. CHAR buffer [sizeof(KEY_VALUE_PARTIAL_INFORMATION)+sizeof(ULONG)];
  441. ULONG returnSize;
  442. PAGED_CODE();
  443. CmBattPrint ((CMBATT_TRACE | CMBATT_PNP), ("CmBattCreateFdo: Device = %ws\n", DeviceName));
  444. //
  445. // Create the PDO device name based on the _UID
  446. //
  447. numberString.MaximumLength = 10;
  448. numberString.Length = 0;
  449. numberString.Buffer = &numberBuffer[0];
  450. //
  451. // Get the unique ID of this device by running the _UID method.
  452. // If this fails, assume one device. Append no device number.
  453. //
  454. status = CmBattGetUniqueId (Pdo, &uniqueId);
  455. if (!NT_SUCCESS(status)) {
  456. CmBattPrint(CMBATT_NOTE, ("CmBattCreateFdo: Error %x from _UID, assuming unit #0\n", status));
  457. uniqueId = 0;
  458. } else {
  459. RtlIntegerToUnicodeString (uniqueId, 10, &numberString);
  460. }
  461. //
  462. // Allocate the UNICODE_STRING for the device name
  463. //
  464. strLength = (USHORT) (wcslen (DeviceName) * 2 + numberString.Length);
  465. unicodeString = ExAllocatePoolWithTag (
  466. PagedPool,
  467. sizeof (UNICODE_STRING) + strLength,
  468. 'MtaB'
  469. );
  470. if (!unicodeString) {
  471. CmBattPrint(CMBATT_ERROR, ("CmBattCreateFdo: could not allocate unicode string\n"));
  472. return STATUS_INSUFFICIENT_RESOURCES;
  473. }
  474. unicodeString->MaximumLength = strLength;
  475. unicodeString->Length = 0;
  476. unicodeString->Buffer = (PWSTR) (unicodeString + 1);
  477. RtlAppendUnicodeToString (unicodeString, DeviceName);
  478. RtlAppendUnicodeStringToString (unicodeString, &numberString);
  479. //
  480. // Create the FDO
  481. //
  482. status = IoCreateDevice(
  483. DriverObject,
  484. ExtensionSize,
  485. unicodeString,
  486. FILE_DEVICE_BATTERY,
  487. 0,
  488. FALSE,
  489. &fdo
  490. );
  491. if (status != STATUS_SUCCESS) {
  492. CmBattPrint(CMBATT_ERROR, ("CmBattCreateFdo: error (0x%x) creating device object\n", status));
  493. ExFreePool (unicodeString);
  494. return(status);
  495. }
  496. fdo->Flags |= DO_BUFFERED_IO;
  497. fdo->Flags |= DO_POWER_PAGABLE; // Don't want power Irps at irql 2
  498. fdo->Flags &= ~DO_DEVICE_INITIALIZING;
  499. //
  500. // Initialize Fdo device extension data
  501. //
  502. cmBatt = (PCM_BATT) fdo->DeviceExtension;
  503. //
  504. // Note: This is note necessarily a battery. It could be an AC adapter, so only fields
  505. // common to both should be initialized here.
  506. //
  507. RtlZeroMemory(cmBatt, ExtensionSize);
  508. //CmBatt->Type must be initialized after this call.
  509. cmBatt->DeviceObject = fdo;
  510. cmBatt->Fdo = fdo;
  511. cmBatt->Pdo = Pdo;
  512. //
  513. // Connect to lower device
  514. //
  515. cmBatt->LowerDeviceObject = IoAttachDeviceToDeviceStack(fdo, Pdo);
  516. if (!cmBatt->LowerDeviceObject) {
  517. CmBattPrint(CMBATT_ERROR, ("CmBattCreateFdo: IoAttachDeviceToDeviceStack failed.\n"));
  518. CmBattDestroyFdo (cmBatt->Fdo);
  519. return STATUS_UNSUCCESSFUL;
  520. }
  521. //
  522. // Get the direct-call ACPI interfaces
  523. //
  524. status = CmBattGetAcpiInterfaces (cmBatt->LowerDeviceObject, &cmBatt->AcpiInterfaces);
  525. if (!NT_SUCCESS(status)) {
  526. CmBattPrint(CMBATT_ERROR, ("CmBattCreateFdor: Could not get ACPI interfaces: %x\n", status));
  527. IoDetachDevice (cmBatt->LowerDeviceObject);
  528. CmBattDestroyFdo (cmBatt->Fdo);
  529. return status;
  530. }
  531. //
  532. // Initializes File handle tracking.
  533. //
  534. ExInitializeFastMutex (&cmBatt->OpenCloseMutex);
  535. cmBatt->OpenCount = 0;
  536. //
  537. // Removal lock initialization
  538. //
  539. cmBatt->WantToRemove = FALSE;
  540. cmBatt->InUseCount = 1;
  541. KeInitializeEvent(&cmBatt->ReadyToRemove, SynchronizationEvent, FALSE);
  542. cmBatt->DeviceNumber = uniqueId;
  543. cmBatt->DeviceName = unicodeString;
  544. cmBatt->Sleeping = FALSE;
  545. cmBatt->ActionRequired = CMBATT_AR_NO_ACTION;
  546. //
  547. // Determine if wake on Battery should be enabled
  548. //
  549. cmBatt->WakeEnabled = FALSE;
  550. status = IoOpenDeviceRegistryKey (Pdo,
  551. PLUGPLAY_REGKEY_DEVICE,
  552. STANDARD_RIGHTS_ALL,
  553. &devInstRegKey);
  554. if (NT_SUCCESS (status)) {
  555. RtlInitUnicodeString (&valueName, WaitWakeEnableKey);
  556. status = ZwQueryValueKey(
  557. devInstRegKey,
  558. &valueName,
  559. KeyValuePartialInformation,
  560. &buffer,
  561. sizeof(buffer),
  562. &returnSize
  563. );
  564. if (NT_SUCCESS (status)) {
  565. cmBatt->WakeEnabled = (*(PULONG)((PKEY_VALUE_PARTIAL_INFORMATION)buffer)->Data ? TRUE : FALSE);
  566. }
  567. ZwClose(devInstRegKey);
  568. }
  569. *NewFdo = fdo;
  570. CmBattPrint((CMBATT_TRACE | CMBATT_PNP), ("CmBattCreateFdo: Created FDO %x\n", fdo));
  571. return STATUS_SUCCESS;
  572. }
  573. VOID
  574. CmBattDestroyFdo(
  575. IN PDEVICE_OBJECT Fdo
  576. )
  577. /*++
  578. Routine Description:
  579. This routine will deallocate a functional device object.
  580. This includes deallocating the DeviceName and calling IoDeleteDevice.
  581. Arguments:
  582. Fdo - a pointer to the FDO to destroy.
  583. Return Value:
  584. STATUS_SUCCESS if everything was successful
  585. reason for failure otherwise
  586. --*/
  587. {
  588. PAGED_CODE();
  589. CmBattPrint ((CMBATT_TRACE | CMBATT_PNP), ("CmBattDestroyFdo, Battery.\n"));
  590. //
  591. // Deallocate the UNICODE_STRING for the device name
  592. //
  593. ExFreePool (((PCM_BATT)Fdo->DeviceExtension)->DeviceName);
  594. ((PCM_BATT)Fdo->DeviceExtension)->DeviceName = NULL;
  595. IoDeleteDevice (Fdo);
  596. CmBattPrint((CMBATT_TRACE | CMBATT_PNP), ("CmBattDestroyFdo: done.\n"));
  597. }
  598. NTSTATUS
  599. CmBattPnpDispatch(
  600. IN PDEVICE_OBJECT DeviceObject,
  601. IN PIRP Irp
  602. )
  603. /*++
  604. Routine Description:
  605. This routine is the dispatch routine for plug and play requests.
  606. Arguments:
  607. DeviceObject - Pointer to class device object.
  608. Irp - Pointer to the request packet.
  609. Return Value:
  610. Status is returned.
  611. --*/
  612. {
  613. PIO_STACK_LOCATION irpStack;
  614. PCM_BATT CmBatt;
  615. NTSTATUS status;
  616. KEVENT syncEvent;
  617. PAGED_CODE();
  618. status = STATUS_NOT_SUPPORTED;
  619. //
  620. // Get a pointer to the current parameters for this request. The
  621. // information is contained in the current stack location.
  622. //
  623. irpStack = IoGetCurrentIrpStackLocation(Irp);
  624. CmBatt = DeviceObject->DeviceExtension;
  625. //
  626. // Aquire remove lock
  627. //
  628. InterlockedIncrement (&CmBatt->InUseCount);
  629. if (CmBatt->WantToRemove == TRUE) {
  630. //
  631. // Failed to acquire remove lock.
  632. //
  633. status = STATUS_DEVICE_REMOVED;
  634. } else {
  635. //
  636. // Remove lock acquired.
  637. //
  638. //
  639. // Dispatch minor function
  640. //
  641. switch (irpStack->MinorFunction) {
  642. case IRP_MN_START_DEVICE: {
  643. CmBattPrint (CMBATT_PNP, ("CmBattPnpDispatch: IRP_MN_START_DEVICE\n"));
  644. if (CmBatt->Type == CM_BATTERY_TYPE) {
  645. //
  646. // We only want to handle batteries, not AC adapters.
  647. //
  648. CmBatt->IsStarted = TRUE;
  649. }
  650. status = STATUS_SUCCESS;
  651. break;
  652. } // IRP_MN_START_DEVICE
  653. case IRP_MN_STOP_DEVICE: {
  654. CmBattPrint (CMBATT_PNP, ("CmBattPnpDispatch: IRP_MN_STOP_DEVICE\n"));
  655. if (CmBatt->Type == CM_BATTERY_TYPE) {
  656. CmBatt->IsStarted = FALSE;
  657. }
  658. status = STATUS_SUCCESS;
  659. break;
  660. } // IRP_MN_STOP_DEVICE
  661. case IRP_MN_REMOVE_DEVICE: {
  662. CmBattPrint (CMBATT_PNP, ("CmBattPnpDispatch: IRP_MN_REMOVE_DEVICE\n"));
  663. status = CmBattRemoveDevice(DeviceObject, Irp);
  664. break;
  665. } // IRP_MN_REMOVE_DEVICE
  666. case IRP_MN_SURPRISE_REMOVAL: {
  667. CmBattPrint (CMBATT_PNP, ("CmBattPnpDispatch: IRP_MN_SURPRISE_REMOVAL\n"));
  668. ExAcquireFastMutex (&CmBatt->OpenCloseMutex);
  669. status = STATUS_SUCCESS;
  670. CmBatt->OpenCount = (ULONG) -1;
  671. ExReleaseFastMutex (&CmBatt->OpenCloseMutex);
  672. break;
  673. } // IRP_MN_QUERY_REMOVE_DEVICE
  674. case IRP_MN_QUERY_REMOVE_DEVICE: {
  675. CmBattPrint (CMBATT_PNP, ("CmBattPnpDispatch: IRP_MN_QUERY_REMOVE_DEVICE\n"));
  676. ExAcquireFastMutex (&CmBatt->OpenCloseMutex);
  677. status = STATUS_SUCCESS;
  678. if (CmBatt->OpenCount == 0) {
  679. CmBatt->OpenCount = (ULONG) -1;
  680. } else if (CmBatt->OpenCount == (ULONG) -1) {
  681. CmBattPrint (CMBATT_WARN, ("CmBattPnpDispatch: Recieved two consecutive QUERY_REMOVE requests.\n"));
  682. } else {
  683. status = STATUS_UNSUCCESSFUL;
  684. }
  685. ExReleaseFastMutex (&CmBatt->OpenCloseMutex);
  686. break;
  687. } // IRP_MN_QUERY_REMOVE_DEVICE
  688. case IRP_MN_CANCEL_REMOVE_DEVICE: {
  689. CmBattPrint (CMBATT_PNP, ("CmBattPnpDispatch: IRP_MN_CANCEL_REMOVE_DEVICE\n"));
  690. ExAcquireFastMutex (&CmBatt->OpenCloseMutex);
  691. if (CmBatt->OpenCount == (ULONG) -1) {
  692. CmBatt->OpenCount = 0;
  693. } else {
  694. CmBattPrint (CMBATT_NOTE, ("CmBattPnpDispatch: Received CANCEL_REMOVE when OpenCount == %x\n",
  695. CmBatt->OpenCount));
  696. }
  697. status = STATUS_SUCCESS;
  698. ExReleaseFastMutex (&CmBatt->OpenCloseMutex);
  699. break;
  700. } // IRP_MN_CANCEL_REMOVE_DEVICE
  701. case IRP_MN_QUERY_STOP_DEVICE: {
  702. CmBattPrint (CMBATT_PNP, ("CmBattPnpDispatch: IRP_MN_QUERY_STOP_DEVICE\n"));
  703. status = STATUS_NOT_IMPLEMENTED;
  704. break;
  705. } // IRP_MN_QUERY_STOP_DEVICE
  706. case IRP_MN_CANCEL_STOP_DEVICE: {
  707. CmBattPrint (CMBATT_PNP, ("CmBattPnpDispatch: IRP_MN_CANCEL_STOP_DEVICE\n"));
  708. status = STATUS_NOT_IMPLEMENTED;
  709. break;
  710. } // IRP_MN_CANCEL_STOP_DEVICE
  711. case IRP_MN_QUERY_PNP_DEVICE_STATE: {
  712. IoCopyCurrentIrpStackLocationToNext (Irp);
  713. KeInitializeEvent(&syncEvent, SynchronizationEvent, FALSE);
  714. IoSetCompletionRoutine(Irp, CmBattIoCompletion, &syncEvent, TRUE, TRUE, TRUE);
  715. status = IoCallDriver(CmBatt->LowerDeviceObject, Irp);
  716. if (status == STATUS_PENDING) {
  717. KeWaitForSingleObject(&syncEvent, Executive, KernelMode, FALSE, NULL);
  718. status = Irp->IoStatus.Status;
  719. }
  720. Irp->IoStatus.Information &= ~PNP_DEVICE_NOT_DISABLEABLE;
  721. IoCompleteRequest(Irp, IO_NO_INCREMENT);
  722. if (0 == InterlockedDecrement(&CmBatt->InUseCount)) {
  723. KeSetEvent (&CmBatt->ReadyToRemove, IO_NO_INCREMENT, FALSE);
  724. }
  725. return status;
  726. }
  727. case IRP_MN_QUERY_CAPABILITIES: {
  728. IoCopyCurrentIrpStackLocationToNext (Irp);
  729. KeInitializeEvent(&syncEvent, SynchronizationEvent, FALSE);
  730. IoSetCompletionRoutine(Irp, CmBattIoCompletion, &syncEvent, TRUE, TRUE, TRUE);
  731. status = IoCallDriver(CmBatt->LowerDeviceObject, Irp);
  732. if (status == STATUS_PENDING) {
  733. KeWaitForSingleObject(&syncEvent, Executive, KernelMode, FALSE, NULL);
  734. status = Irp->IoStatus.Status;
  735. }
  736. CmBatt->WakeSupportedState.SystemState = irpStack->Parameters.DeviceCapabilities.Capabilities->SystemWake;
  737. CmBattPrint (CMBATT_PNP, ("CmBattPnpDispatch: IRP_MN_QUERY_CAPABILITIES %d Capabilities->SystemWake = %x\n", CmBatt->Type, CmBatt->WakeSupportedState.SystemState));
  738. if (CmBatt->WakeSupportedState.SystemState != PowerSystemUnspecified) {
  739. if (CmBatt->WaitWakeIrp == NULL && CmBatt->WakeEnabled) {
  740. status = PoRequestPowerIrp(
  741. CmBatt->DeviceObject,
  742. IRP_MN_WAIT_WAKE,
  743. CmBatt->WakeSupportedState,
  744. CmBattWaitWakeLoop,
  745. NULL,
  746. &(CmBatt->WaitWakeIrp)
  747. );
  748. CmBattPrint (CMBATT_PNP, ("CmBattPnpDispatch: IRP_MN_QUERY_CAPABILITIES wait/Wake irp sent.\n"));
  749. }
  750. } else {
  751. CmBatt->WakeEnabled=FALSE;
  752. CmBattPrint (CMBATT_PNP, ("CmBattPnpDispatch: IRP_MN_QUERY_CAPABILITIES Wake not supported.\n"));
  753. }
  754. IoCompleteRequest(Irp, IO_NO_INCREMENT);
  755. if (0 == InterlockedDecrement(&CmBatt->InUseCount)) {
  756. KeSetEvent (&CmBatt->ReadyToRemove, IO_NO_INCREMENT, FALSE);
  757. }
  758. return status;
  759. }
  760. default: {
  761. //
  762. // Unimplemented minor, Pass this down
  763. //
  764. CmBattPrint (CMBATT_PNP,
  765. ("CmBattPnpDispatch: Unimplemented minor %0x\n", \
  766. irpStack->MinorFunction));
  767. } // default
  768. // Fall through...
  769. case IRP_MN_QUERY_RESOURCES:
  770. case IRP_MN_READ_CONFIG:
  771. case IRP_MN_WRITE_CONFIG:
  772. case IRP_MN_EJECT:
  773. case IRP_MN_SET_LOCK:
  774. case IRP_MN_QUERY_ID:
  775. case IRP_MN_QUERY_DEVICE_RELATIONS: {
  776. break ;
  777. }
  778. }
  779. }
  780. //
  781. // Release remove lock
  782. //
  783. if (0 == InterlockedDecrement(&CmBatt->InUseCount)) {
  784. KeSetEvent (&CmBatt->ReadyToRemove, IO_NO_INCREMENT, FALSE);
  785. }
  786. //
  787. // Only set status if we have something to add
  788. //
  789. if (status != STATUS_NOT_SUPPORTED) {
  790. Irp->IoStatus.Status = status;
  791. }
  792. //
  793. // Do we need to send it down?
  794. //
  795. if (NT_SUCCESS(status) || (status == STATUS_NOT_SUPPORTED)) {
  796. CmBattCallLowerDriver(status, CmBatt->LowerDeviceObject, Irp);
  797. return status;
  798. }
  799. //
  800. // At this point, it must have been passed down and needs recompletion,
  801. // or the status is unsuccessful.
  802. //
  803. ASSERT(!NT_SUCCESS(status) && (status != STATUS_NOT_SUPPORTED));
  804. status = Irp->IoStatus.Status ;
  805. IoCompleteRequest(Irp, IO_NO_INCREMENT);
  806. return status;
  807. }
  808. NTSTATUS
  809. CmBattRemoveDevice(
  810. IN PDEVICE_OBJECT DeviceObject,
  811. IN PIRP Irp
  812. )
  813. /*++
  814. Routine Description:
  815. This routine processes a IRP_MN_REMOVE_DEVICE
  816. Arguments:
  817. DeviceObject - Pointer to class device object.
  818. Irp - Pointer to the request packet.
  819. Return Value:
  820. Returns STATUS_SUCCESS. (This function must not fail.)
  821. --*/
  822. {
  823. PCM_BATT cmBatt;
  824. NTSTATUS status;
  825. cmBatt = (PCM_BATT) DeviceObject->DeviceExtension;
  826. CmBattPrint (CMBATT_TRACE, ("CmBattRemoveDevice: CmBatt (%x), Type %d, _UID %d\n",
  827. cmBatt, cmBatt->Type, cmBatt->DeviceNumber));
  828. //
  829. // Remove device syncronization
  830. //
  831. //
  832. // Prevent more locks from being acquired.
  833. //
  834. cmBatt->WantToRemove = TRUE;
  835. //
  836. // Release lock acquired at start of CmBattPnpDispatch
  837. //
  838. if (InterlockedDecrement (&cmBatt->InUseCount) <= 0) {
  839. CmBattPrint (CMBATT_ERROR, ("CmBattRemoveDevice: Remove lock error.\n"));
  840. ASSERT(FALSE);
  841. }
  842. //
  843. // Final release and wait.
  844. //
  845. // Note: there will be one more relase at the end of CmBattPnpDispatch
  846. // but it will decrement the InUseCount to -1 so it won't set the event.
  847. //
  848. if (InterlockedDecrement (&cmBatt->InUseCount) > 0) {
  849. KeWaitForSingleObject (&cmBatt->ReadyToRemove,
  850. Executive,
  851. KernelMode,
  852. FALSE,
  853. NULL
  854. );
  855. }
  856. //
  857. // Cancel the Wait/wake IRP;
  858. //
  859. if (cmBatt->WaitWakeIrp != NULL) {
  860. IoCancelIrp (cmBatt->WaitWakeIrp);
  861. cmBatt->WaitWakeIrp = NULL;
  862. }
  863. if (cmBatt->Type == CM_BATTERY_TYPE) {
  864. //
  865. // This is a control method battery FDO
  866. //
  867. //
  868. // Disconnect from receiving device (battery) notifications
  869. //
  870. cmBatt->AcpiInterfaces.UnregisterForDeviceNotifications (
  871. cmBatt->AcpiInterfaces.Context,
  872. CmBattNotifyHandler);
  873. //
  874. // Unregister as a WMI Provider.
  875. //
  876. CmBattWmiDeRegistration(cmBatt);
  877. //
  878. // Tell the class driver we are going away
  879. //
  880. status = BatteryClassUnload (cmBatt->Class);
  881. ASSERT (NT_SUCCESS(status));
  882. } else {
  883. //
  884. // This is an AC adapter FDO
  885. //
  886. //
  887. // Disconnect from receiving device (battery) notifications
  888. //
  889. cmBatt->AcpiInterfaces.UnregisterForDeviceNotifications (
  890. cmBatt->AcpiInterfaces.Context,
  891. CmBattNotifyHandler);
  892. //
  893. // Unregister as a WMI Provider.
  894. //
  895. CmBattWmiDeRegistration(cmBatt);
  896. AcAdapterPdo = NULL;
  897. }
  898. //
  899. // Clean up, delete the string and the Fdo we created at AddDevice time
  900. //
  901. ExFreePool (cmBatt->DeviceName);
  902. IoDetachDevice (cmBatt->LowerDeviceObject);
  903. IoDeleteDevice (cmBatt->DeviceObject);
  904. return STATUS_SUCCESS;
  905. }
  906. NTSTATUS
  907. CmBattPowerDispatch(
  908. IN PDEVICE_OBJECT DeviceObject,
  909. IN PIRP Irp
  910. )
  911. /*++
  912. Routine Description:
  913. This routine is the dispatch routine for power requests.
  914. Arguments:
  915. DeviceObject - Pointer to class device object.
  916. Irp - Pointer to the request packet.
  917. Return Value:
  918. Status is returned.
  919. --*/
  920. {
  921. PIO_STACK_LOCATION irpStack;
  922. PCM_BATT CmBatt;
  923. NTSTATUS Status;
  924. //
  925. // A remove lock is not needed in this dispatch function because
  926. // all data accessed is in the device extension. If any other functionality
  927. // was added to this routine, a remove lock might be neccesary.
  928. //
  929. CmBattPrint ((CMBATT_TRACE | CMBATT_POWER), ("CmBattPowerDispatch\n"));
  930. //
  931. // Get a pointer to the current parameters for this request. The
  932. // information is contained in the current stack location.
  933. //
  934. irpStack = IoGetCurrentIrpStackLocation(Irp);
  935. CmBatt = DeviceObject->DeviceExtension;
  936. //
  937. // Dispatch minor function
  938. //
  939. switch (irpStack->MinorFunction) {
  940. case IRP_MN_WAIT_WAKE: {
  941. CmBattPrint (CMBATT_POWER, ("CmBattPowerDispatch: IRP_MN_WAIT_WAKE\n"));
  942. break;
  943. }
  944. case IRP_MN_POWER_SEQUENCE: {
  945. CmBattPrint (CMBATT_POWER, ("CmBattPowerDispatch: IRP_MN_POWER_SEQUENCE\n"));
  946. break;
  947. }
  948. case IRP_MN_SET_POWER: {
  949. CmBattPrint (CMBATT_POWER, ("CmBattPowerDispatch: IRP_MN_SET_POWER type: %d, State: %d \n",
  950. irpStack->Parameters.Power.Type,
  951. irpStack->Parameters.Power.State));
  952. break;
  953. }
  954. case IRP_MN_QUERY_POWER: {
  955. CmBattPrint (CMBATT_POWER, ("CmBattPowerDispatch: IRP_MN_QUERY_POWER\n"));
  956. break;
  957. }
  958. default: {
  959. CmBattPrint(CMBATT_LOW, ("CmBattPowerDispatch: minor %d\n",
  960. irpStack->MinorFunction));
  961. break;
  962. }
  963. }
  964. //
  965. // What do we do with the irp?
  966. //
  967. PoStartNextPowerIrp( Irp );
  968. if (CmBatt->LowerDeviceObject != NULL) {
  969. //
  970. // Forward the request along
  971. //
  972. IoSkipCurrentIrpStackLocation( Irp );
  973. Status = PoCallDriver( CmBatt->LowerDeviceObject, Irp );
  974. } else {
  975. //
  976. // Complete the request with the current status
  977. //
  978. Status = Irp->IoStatus.Status;
  979. IoCompleteRequest( Irp, IO_NO_INCREMENT );
  980. }
  981. return Status;
  982. }
  983. NTSTATUS
  984. CmBattForwardRequest(
  985. IN PDEVICE_OBJECT DeviceObject,
  986. IN PIRP Irp
  987. )
  988. /*++
  989. Routine Description:
  990. This routine passes the request down the stack
  991. Arguments:
  992. DeviceObject - The target
  993. Irp - The request
  994. Return Value:
  995. NTSTATUS
  996. --*/
  997. {
  998. NTSTATUS status;
  999. PCM_BATT cmBatt = DeviceObject->DeviceExtension;
  1000. //
  1001. // A remove lock is not needed in this dispatch function because
  1002. // all data accessed is in the device extension. If any other functionality was
  1003. // added to this routine, a remove lock might be neccesary.
  1004. //
  1005. if (cmBatt->LowerDeviceObject != NULL) {
  1006. IoSkipCurrentIrpStackLocation( Irp );
  1007. status = IoCallDriver( cmBatt->LowerDeviceObject, Irp );
  1008. } else {
  1009. Irp->IoStatus.Status = status = STATUS_NOT_SUPPORTED;
  1010. IoCompleteRequest( Irp, IO_NO_INCREMENT );
  1011. }
  1012. return status;
  1013. }
  1014. NTSTATUS
  1015. CmBattWaitWakeLoop(
  1016. IN PDEVICE_OBJECT DeviceObject,
  1017. IN UCHAR MinorFunction,
  1018. IN POWER_STATE PowerState,
  1019. IN PVOID Context,
  1020. IN PIO_STATUS_BLOCK IoStatus
  1021. )
  1022. /*++
  1023. Routine Description:
  1024. This routine is called after the WAIT_WAKE has been completed
  1025. Arguments:
  1026. DeviceObject - The PDO
  1027. MinorFunction - IRP_MN_WAIT_WAKE
  1028. PowerState - The Sleep state that it could wake from
  1029. Context - NOT USED
  1030. IoStatus - The status of the request
  1031. Return Value:
  1032. NTSTATUS
  1033. --*/
  1034. {
  1035. NTSTATUS status;
  1036. PCM_BATT cmBatt = (PCM_BATT) DeviceObject->DeviceExtension;
  1037. CmBattPrint (CMBATT_PNP, ("CmBattWaitWakeLoop: Entered.\n"));
  1038. if (!NT_SUCCESS(IoStatus->Status) || !cmBatt->WakeEnabled) {
  1039. CmBattPrint (CMBATT_ERROR, ("CmBattWaitWakeLoop: failed: status = 0x%08x.\n", IoStatus->Status));
  1040. cmBatt->WaitWakeIrp = NULL;
  1041. return IoStatus->Status;
  1042. } else {
  1043. CmBattPrint (CMBATT_NOTE, ("CmBattWaitWakeLoop: completed successfully\n"));
  1044. }
  1045. //
  1046. // In this case, we just cause the same thing to happen again
  1047. //
  1048. status = PoRequestPowerIrp(
  1049. DeviceObject,
  1050. MinorFunction,
  1051. PowerState,
  1052. CmBattWaitWakeLoop,
  1053. Context,
  1054. &(cmBatt->WaitWakeIrp)
  1055. );
  1056. CmBattPrint (CMBATT_NOTE, ("CmBattWaitWakeLoop: PoRequestPowerIrp: status = 0x%08x.\n", status));
  1057. //
  1058. // Done
  1059. //
  1060. return STATUS_SUCCESS;
  1061. }