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.

506 lines
14 KiB

  1. /*++
  2. Copyright (c) 1997-1998 Microsoft Corporation, All Rights Reserved
  3. Module Name:
  4. power.c
  5. Abstract:
  6. This module contains plug & play code for the I8042 Keyboard Filter Driver.
  7. Environment:
  8. Kernel mode.
  9. Revision History:
  10. --*/
  11. #include "i8042prt.h"
  12. #include "i8042log.h"
  13. #include <initguid.h>
  14. #include <poclass.h>
  15. VOID
  16. I8xUpdateSysButtonCaps(
  17. IN PDEVICE_OBJECT DeviceObject,
  18. IN PI8X_KEYBOARD_WORK_ITEM Item
  19. );
  20. VOID
  21. I8xCompleteSysButtonEventWorker(
  22. IN PDEVICE_OBJECT DeviceObject,
  23. IN PI8X_KEYBOARD_WORK_ITEM Item
  24. );
  25. #ifdef ALLOC_PRAGMA
  26. #pragma alloc_text(PAGE, I8xKeyboardGetSysButtonCaps)
  27. #pragma alloc_text(PAGE, I8xUpdateSysButtonCaps)
  28. #if DELAY_SYSBUTTON_COMPLETION
  29. #pragma alloc_text(PAGE, I8xCompleteSysButtonEventWorker)
  30. #endif
  31. #endif
  32. VOID
  33. I8xCompleteSysButtonIrp(
  34. PIRP Irp,
  35. ULONG Event,
  36. NTSTATUS Status
  37. )
  38. {
  39. Print(DBG_POWER_NOISE,
  40. ("completing sys button irp 0x%x, event %d, status 0x%x\n",
  41. Irp, Event, Status));
  42. ASSERT(IoSetCancelRoutine(Irp, NULL) == NULL);
  43. *(PULONG) Irp->AssociatedIrp.SystemBuffer = Event;
  44. Irp->IoStatus.Information = sizeof(Event);
  45. Irp->IoStatus.Status = Status;
  46. IoCompleteRequest(Irp, IO_NO_INCREMENT);
  47. }
  48. NTSTATUS
  49. I8xKeyboardGetSysButtonCaps(
  50. PPORT_KEYBOARD_EXTENSION KeyboardExtension,
  51. PIRP Irp
  52. )
  53. {
  54. PIO_STACK_LOCATION stack;
  55. NTSTATUS status;
  56. ULONG caps, size;
  57. PAGED_CODE();
  58. stack = IoGetCurrentIrpStackLocation(Irp);
  59. size = 0x0;
  60. if (stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(ULONG)) {
  61. Print(DBG_POWER_ERROR, ("get caps, buffer too small\n"));
  62. status = STATUS_INVALID_BUFFER_SIZE;
  63. }
  64. else {
  65. caps = 0x0;
  66. size = sizeof(caps);
  67. if (KeyboardExtension->PowerCaps & I8042_POWER_SYS_BUTTON) {
  68. Print(DBG_POWER_NOISE | DBG_IOCTL_NOISE,
  69. ("get cap: reporting power button\n"));
  70. caps |= SYS_BUTTON_POWER;
  71. }
  72. if (KeyboardExtension->PowerCaps & I8042_SLEEP_SYS_BUTTON) {
  73. Print(DBG_POWER_NOISE | DBG_IOCTL_NOISE,
  74. ("get cap: reporting sleep button\n"));
  75. caps |= SYS_BUTTON_SLEEP;
  76. }
  77. if (KeyboardExtension->PowerCaps & I8042_WAKE_SYS_BUTTON) {
  78. Print(DBG_POWER_NOISE | DBG_IOCTL_NOISE,
  79. ("get cap: reporting wake button\n"));
  80. caps |= SYS_BUTTON_WAKE;
  81. }
  82. // can't do this b/c SYS_BUTTON_WAKE is == 0x0
  83. // ASSERT(caps != 0x0);
  84. *(PULONG) Irp->AssociatedIrp.SystemBuffer = caps;
  85. status = STATUS_SUCCESS;
  86. }
  87. Irp->IoStatus.Information = size;
  88. Irp->IoStatus.Status = status;
  89. IoCompleteRequest(Irp, IO_NO_INCREMENT);
  90. return status;
  91. }
  92. #if DELAY_SYSBUTTON_COMPLETION
  93. VOID
  94. I8xCompleteSysButtonEventWorker(
  95. IN PDEVICE_OBJECT DeviceObject,
  96. IN PI8X_KEYBOARD_WORK_ITEM Item
  97. )
  98. {
  99. NTSTATUS status = STATUS_SUCCESS;
  100. PAGED_CODE();
  101. //
  102. // Check to see if, in the short time that we queued the work item and it
  103. // firing, that the irp has been cancelled
  104. //
  105. if (Item->Irp->Cancel) {
  106. status = STATUS_CANCELLED;
  107. Item->MakeCode = 0x0;
  108. }
  109. I8xCompleteSysButtonIrp(Item->Irp, Item->MakeCode, status);
  110. IoFreeWorkItem(Item->Item);
  111. ExFreePool(Item);
  112. }
  113. #endif
  114. NTSTATUS
  115. I8xKeyboardGetSysButtonEvent(
  116. PPORT_KEYBOARD_EXTENSION KeyboardExtension,
  117. PIRP Irp
  118. )
  119. {
  120. PIO_STACK_LOCATION stack;
  121. PIRP oldIrp, pendingIrp;
  122. NTSTATUS status;
  123. ULONG event = 0x0;
  124. KIRQL irql;
  125. stack = IoGetCurrentIrpStackLocation(Irp);
  126. if (stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(ULONG)) {
  127. Print(DBG_POWER_ERROR, ("get event, buffer too small\n"));
  128. status = STATUS_INVALID_BUFFER_SIZE;
  129. Irp->IoStatus.Status = status;
  130. Irp->IoStatus.Information = 0x0;
  131. IoCompleteRequest(Irp, IO_NO_INCREMENT);
  132. return status;
  133. }
  134. else if (KeyboardExtension->PowerEvent) {
  135. #if DELAY_SYSBUTTON_COMPLETION
  136. PI8X_KEYBOARD_WORK_ITEM item;
  137. status = STATUS_INSUFFICIENT_RESOURCES;
  138. item = (PI8X_KEYBOARD_WORK_ITEM)
  139. ExAllocatePool(NonPagedPool, sizeof(I8X_KEYBOARD_WORK_ITEM));
  140. if (item) {
  141. item->Item = IoAllocateWorkItem(KeyboardExtension->Self);
  142. if (item->Item) {
  143. Print(DBG_POWER_NOISE, ("Queueing work item to complete event\n"));
  144. item->MakeCode = KeyboardExtension->PowerEvent;
  145. item->Irp = Irp;
  146. //
  147. // No need to set a cancel routine b/c we will always be
  148. // completing the irp in very short period of time
  149. //
  150. IoMarkIrpPending(Irp);
  151. IoQueueWorkItem(item->Item,
  152. I8xCompleteSysButtonEventWorker,
  153. DelayedWorkQueue,
  154. item);
  155. status = STATUS_PENDING;
  156. }
  157. else {
  158. ExFreePool(item);
  159. }
  160. }
  161. #else // DELAY_SYSBUTTON_COMPLETION
  162. Print(DBG_POWER_INFO, ("completing event immediately\n"));
  163. event = KeyboardExtension->PowerEvent;
  164. status = STATUS_SUCCESS;
  165. #endif // DELAY_SYSBUTTON_COMPLETION
  166. KeyboardExtension->PowerEvent = 0x0;
  167. }
  168. else {
  169. //
  170. // See if the pending sys button is NULL. If it is, then Irp will
  171. // put into the slot
  172. //
  173. KeAcquireSpinLock(&KeyboardExtension->SysButtonSpinLock, &irql);
  174. if (KeyboardExtension->SysButtonEventIrp == NULL) {
  175. Print(DBG_POWER_INFO, ("pending sys button event\n"));
  176. KeyboardExtension->SysButtonEventIrp = Irp;
  177. IoMarkIrpPending(Irp);
  178. IoSetCancelRoutine(Irp, I8xSysButtonCancelRoutine);
  179. status = STATUS_PENDING;
  180. //
  181. // We don't care if Irp->Cancel is TRUE. If it is, then the cancel
  182. // routine will complete the irp an everything will be all set.
  183. // Since status == STATUS_PENDING, nobody in this code path is going
  184. // to touch the irp
  185. //
  186. }
  187. else {
  188. Print(DBG_POWER_ERROR | DBG_POWER_INFO,
  189. ("got 1+ get sys button event requests!\n"));
  190. status = STATUS_UNSUCCESSFUL;
  191. }
  192. KeReleaseSpinLock(&KeyboardExtension->SysButtonSpinLock, irql);
  193. }
  194. if (status != STATUS_PENDING) {
  195. Print(DBG_POWER_NOISE,
  196. ("completing get sys power event with 0x%x\n", status));
  197. I8xCompleteSysButtonIrp(Irp, event, status);
  198. }
  199. return status;
  200. }
  201. VOID
  202. I8xKeyboardSysButtonEventDpc(
  203. IN PKDPC Dpc,
  204. IN PDEVICE_OBJECT DeviceObject,
  205. IN SYS_BUTTON_ACTION Action,
  206. IN ULONG MakeCode
  207. )
  208. {
  209. PPORT_KEYBOARD_EXTENSION kbExtension = DeviceObject->DeviceExtension;
  210. PI8X_KEYBOARD_WORK_ITEM item;
  211. KIRQL irql;
  212. ULONG event;
  213. PIRP irp;
  214. UNREFERENCED_PARAMETER(Dpc);
  215. ASSERT(Action != NoAction);
  216. //
  217. // Check to see if we need to complete the IRP or actually register for a
  218. // notification
  219. //
  220. switch (MakeCode) {
  221. case KEYBOARD_POWER_CODE: event = SYS_BUTTON_POWER; break;
  222. case KEYBOARD_SLEEP_CODE: event = SYS_BUTTON_SLEEP; break;
  223. case KEYBOARD_WAKE_CODE: event = SYS_BUTTON_WAKE; break;
  224. default: event = 0x0; TRAP();
  225. }
  226. if (Action == SendAction) {
  227. Print(DBG_POWER_INFO, ("button event complete (0x%x)\n", event));
  228. KeAcquireSpinLock(&kbExtension->SysButtonSpinLock, &irql);
  229. irp = kbExtension->SysButtonEventIrp;
  230. kbExtension->SysButtonEventIrp = NULL;
  231. if (irp && (irp->Cancel || IoSetCancelRoutine(irp, NULL) == NULL)) {
  232. irp = NULL;
  233. }
  234. KeReleaseSpinLock(&kbExtension->SysButtonSpinLock, irql);
  235. if (irp) {
  236. I8xCompleteSysButtonIrp(irp, event, STATUS_SUCCESS);
  237. }
  238. }
  239. else {
  240. ASSERT(Action == UpdateAction);
  241. //
  242. // Queue the work item. We need to write the value to the registry and
  243. // set the device interface
  244. //
  245. item = (PI8X_KEYBOARD_WORK_ITEM)
  246. ExAllocatePool(NonPagedPool, sizeof(I8X_KEYBOARD_WORK_ITEM));
  247. if (item) {
  248. item->Item = IoAllocateWorkItem(DeviceObject);
  249. if (item->Item) {
  250. Print(DBG_POWER_NOISE, ("Queueing work item to update caps\n"));
  251. //
  252. // Save this off so when we get the IOCTL, we can complete it immediately
  253. //
  254. kbExtension->PowerEvent |= (UCHAR) event;
  255. item->MakeCode = MakeCode;
  256. IoQueueWorkItem(item->Item,
  257. I8xUpdateSysButtonCaps,
  258. DelayedWorkQueue,
  259. item);
  260. }
  261. else {
  262. ExFreePool(item);
  263. }
  264. }
  265. }
  266. }
  267. VOID
  268. I8xSysButtonCancelRoutine(
  269. IN PDEVICE_OBJECT DeviceObject,
  270. IN PIRP Irp
  271. )
  272. {
  273. PPORT_KEYBOARD_EXTENSION kbExtension = DeviceObject->DeviceExtension;
  274. PIRP irp;
  275. KIRQL irql;
  276. Print(DBG_POWER_TRACE, ("SysButtonCancelRoutine\n"));
  277. KeAcquireSpinLock(&kbExtension->SysButtonSpinLock, &irql);
  278. irp = kbExtension->SysButtonEventIrp;
  279. kbExtension->SysButtonEventIrp = NULL;
  280. Print(DBG_POWER_INFO, ("pending event irp = 0x%x\n", irp));
  281. KeReleaseSpinLock(&kbExtension->SysButtonSpinLock, irql);
  282. IoReleaseCancelSpinLock(Irp->CancelIrql);
  283. Irp->IoStatus.Information = 0x0;
  284. Irp->IoStatus.Status = STATUS_CANCELLED;
  285. IoCompleteRequest(Irp, IO_NO_INCREMENT);
  286. }
  287. PIRP
  288. I8xUpdateSysButtonCapsGetPendedIrp(
  289. PPORT_KEYBOARD_EXTENSION KeyboardExtension
  290. )
  291. {
  292. KIRQL irql;
  293. PIRP irp;
  294. KeAcquireSpinLock(&KeyboardExtension->SysButtonSpinLock, &irql);
  295. irp = KeyboardExtension->SysButtonEventIrp;
  296. KeyboardExtension->SysButtonEventIrp = NULL;
  297. if (irp && IoSetCancelRoutine(irp, NULL) == NULL) {
  298. //
  299. // Cancel routine take care of the irp
  300. //
  301. irp = NULL;
  302. }
  303. KeReleaseSpinLock(&KeyboardExtension->SysButtonSpinLock, irql);
  304. return irp;
  305. }
  306. VOID
  307. I8xUpdateSysButtonCaps(
  308. IN PDEVICE_OBJECT DeviceObject,
  309. IN PI8X_KEYBOARD_WORK_ITEM Item
  310. )
  311. {
  312. UNICODE_STRING strPowerCaps;
  313. PPORT_KEYBOARD_EXTENSION kbExtension;
  314. HANDLE devInstRegKey;
  315. ULONG newPowerCaps;
  316. NTSTATUS status = STATUS_SUCCESS;
  317. PIRP irp;
  318. PAGED_CODE();
  319. kbExtension = (PPORT_KEYBOARD_EXTENSION) DeviceObject->DeviceExtension;
  320. if (Item->MakeCode != 0x0) {
  321. if ((NT_SUCCESS(IoOpenDeviceRegistryKey(kbExtension->PDO,
  322. PLUGPLAY_REGKEY_DEVICE,
  323. STANDARD_RIGHTS_ALL,
  324. &devInstRegKey)))) {
  325. //
  326. // Update the power caps
  327. //
  328. switch (Item->MakeCode) {
  329. case KEYBOARD_POWER_CODE:
  330. Print(DBG_POWER_NOISE, ("Adding Power Sys Button cap\n"));
  331. kbExtension->PowerCaps |= I8042_POWER_SYS_BUTTON;
  332. break;
  333. case KEYBOARD_SLEEP_CODE:
  334. Print(DBG_POWER_NOISE, ("Adding Power Sleep Button cap\n"));
  335. kbExtension->PowerCaps |= I8042_SLEEP_SYS_BUTTON;
  336. break;
  337. case KEYBOARD_WAKE_CODE:
  338. Print(DBG_POWER_NOISE, ("Adding Power Wake Button cap\n"));
  339. kbExtension->PowerCaps |= I8042_WAKE_SYS_BUTTON;
  340. break;
  341. default:
  342. Print(DBG_POWER_ERROR,
  343. ("Adding power cap, unknown makecode 0x%x\n",
  344. (ULONG) Item->MakeCode
  345. ));
  346. TRAP();
  347. }
  348. RtlInitUnicodeString(&strPowerCaps,
  349. pwPowerCaps
  350. );
  351. newPowerCaps = kbExtension->PowerCaps;
  352. ZwSetValueKey(devInstRegKey,
  353. &strPowerCaps,
  354. 0,
  355. REG_DWORD,
  356. &newPowerCaps,
  357. sizeof(newPowerCaps)
  358. );
  359. ZwClose(devInstRegKey);
  360. if (!kbExtension->SysButtonInterfaceName.Buffer) {
  361. //
  362. // No prev caps so we must register and turn on the interface now
  363. //
  364. ASSERT(kbExtension->SysButtonEventIrp == NULL);
  365. status = I8xRegisterDeviceInterface(kbExtension->PDO,
  366. &GUID_DEVICE_SYS_BUTTON,
  367. &kbExtension->SysButtonInterfaceName
  368. );
  369. Print(DBG_POWER_NOISE,
  370. ("Registering Interface for 1st time (0x%x)\n", status));
  371. }
  372. else {
  373. //
  374. // We better have a pending event irp already then!
  375. //
  376. Print(DBG_POWER_INFO, ("failing old sys button event irp\n"));
  377. if ((irp = I8xUpdateSysButtonCapsGetPendedIrp(kbExtension))) {
  378. //
  379. // Complete the old irp, the PO subsystem will then
  380. // remove this system button.
  381. //
  382. I8xCompleteSysButtonIrp(irp, 0x0, STATUS_DEVICE_NOT_CONNECTED);
  383. }
  384. //
  385. // We need to reregister with the PO subsystem so that it will
  386. // requery this interface
  387. //
  388. IoSetDeviceInterfaceState(&kbExtension->SysButtonInterfaceName,
  389. FALSE);
  390. IoSetDeviceInterfaceState(&kbExtension->SysButtonInterfaceName,
  391. TRUE);
  392. }
  393. }
  394. else {
  395. Print(DBG_POWER_ERROR, ("could not open devnode key!\n"));
  396. }
  397. }
  398. else {
  399. //
  400. // Must report the device interface
  401. //
  402. }
  403. IoFreeWorkItem(Item->Item);
  404. ExFreePool(Item);
  405. }