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.

579 lines
17 KiB

  1. /*++
  2. Copyright (c) 1997-1998 Microsoft Corporation, All Rights Reserved
  3. Module Name:
  4. kbdpnp.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 <poclass.h>
  14. #ifdef ALLOC_PRAGMA
  15. #pragma alloc_text(PAGE, I8xKeyboardConnectInterrupt)
  16. #pragma alloc_text(PAGE, I8xKeyboardInitializeHardware)
  17. #pragma alloc_text(PAGE, I8xKeyboardStartDevice)
  18. #pragma alloc_text(PAGE, I8xKeyboardRemoveDevice)
  19. #endif
  20. NTSTATUS
  21. I8xKeyboardConnectInterrupt(
  22. PPORT_KEYBOARD_EXTENSION KeyboardExtension
  23. )
  24. /*++
  25. Routine Description:
  26. Calls IoConnectInterupt to connect the keyboard interrupt
  27. Arguments:
  28. KeyboardExtension - Keyboard Extension
  29. SyncConnectContext - Structure to be filled in if synchronization needs to
  30. take place between this interrupt and the mouse
  31. interrupt.
  32. Return Value:
  33. STATUS_SUCCESSFUL if successful,
  34. --*/
  35. {
  36. NTSTATUS status = STATUS_SUCCESS;
  37. ULONG dumpData[1];
  38. PI8042_CONFIGURATION_INFORMATION configuration;
  39. PDEVICE_OBJECT self;
  40. PAGED_CODE();
  41. //
  42. // If the devices were started in totally disparate manner, make sure we
  43. // retry to connect the interrupt (and fail and NULL out InterruptObject)
  44. //
  45. if (KeyboardExtension->InterruptObject) {
  46. return STATUS_SUCCESS;
  47. }
  48. configuration = &Globals.ControllerData->Configuration;
  49. self = KeyboardExtension->Self;
  50. Print(DBG_SS_NOISE,
  51. ("I8xKeyboardConnectInterrupt:\n"
  52. "\tFDO = 0x%x\n"
  53. "\tVector = 0x%x\n"
  54. "\tIrql = 0x%x\n"
  55. "\tSynchIrql = 0x%x\n"
  56. "\tIntterupt Mode = %s\n"
  57. "\tShared int: %s\n"
  58. "\tAffinity = 0x%x\n"
  59. "\tFloating Save = %s\n",
  60. self,
  61. (ULONG) KeyboardExtension->InterruptDescriptor.u.Interrupt.Vector,
  62. (ULONG) KeyboardExtension->InterruptDescriptor.u.Interrupt.Level,
  63. (ULONG) configuration->InterruptSynchIrql,
  64. KeyboardExtension->InterruptDescriptor.Flags
  65. == CM_RESOURCE_INTERRUPT_LATCHED ? "Latched" : "LevelSensitive",
  66. (ULONG) KeyboardExtension->InterruptDescriptor.ShareDisposition
  67. == CmResourceShareShared ? "true" : "false",
  68. (ULONG) KeyboardExtension->InterruptDescriptor.u.Interrupt.Affinity,
  69. configuration->FloatingSave ? "yes" : "no"
  70. ));
  71. KeyboardExtension->IsIsrActivated = TRUE;
  72. //
  73. // Connect the interrupt and set everything in motion
  74. //
  75. status = IoConnectInterrupt(
  76. &(KeyboardExtension->InterruptObject),
  77. (PKSERVICE_ROUTINE) I8042KeyboardInterruptService,
  78. self,
  79. &KeyboardExtension->InterruptSpinLock,
  80. KeyboardExtension->InterruptDescriptor.u.Interrupt.Vector,
  81. (KIRQL) KeyboardExtension->InterruptDescriptor.u.Interrupt.Level,
  82. configuration->InterruptSynchIrql,
  83. KeyboardExtension->InterruptDescriptor.Flags
  84. == CM_RESOURCE_INTERRUPT_LATCHED ? Latched : LevelSensitive,
  85. (BOOLEAN) (KeyboardExtension->InterruptDescriptor.ShareDisposition
  86. == CmResourceShareShared),
  87. KeyboardExtension->InterruptDescriptor.u.Interrupt.Affinity,
  88. configuration->FloatingSave
  89. );
  90. if (NT_SUCCESS(status)) {
  91. INTERNAL_I8042_START_INFORMATION startInfo;
  92. PDEVICE_OBJECT topOfStack = IoGetAttachedDeviceReference(self);
  93. ASSERT(KeyboardExtension->InterruptObject != NULL);
  94. ASSERT(topOfStack);
  95. RtlZeroMemory(&startInfo, sizeof(INTERNAL_I8042_START_INFORMATION));
  96. startInfo.Size = sizeof(INTERNAL_I8042_START_INFORMATION);
  97. startInfo.InterruptObject = KeyboardExtension->InterruptObject;
  98. I8xSendIoctl(topOfStack,
  99. IOCTL_INTERNAL_I8042_KEYBOARD_START_INFORMATION,
  100. &startInfo,
  101. sizeof(INTERNAL_I8042_START_INFORMATION)
  102. );
  103. ObDereferenceObject(topOfStack);
  104. }
  105. else {
  106. //
  107. // Failed to install. Free up resources before exiting.
  108. //
  109. Print(DBG_SS_ERROR, ("Could not connect keyboard isr!!!\n"));
  110. dumpData[0] = KeyboardExtension->InterruptDescriptor.u.Interrupt.Level;
  111. //
  112. // Log the error
  113. //
  114. I8xLogError(self,
  115. I8042_NO_INTERRUPT_CONNECTED_KBD,
  116. I8042_ERROR_VALUE_BASE + 80,
  117. STATUS_INSUFFICIENT_RESOURCES,
  118. dumpData,
  119. 1
  120. );
  121. I8xManuallyRemoveDevice(GET_COMMON_DATA(KeyboardExtension));
  122. }
  123. return status;
  124. }
  125. NTSTATUS
  126. I8xKeyboardInitializeHardware(
  127. PPORT_KEYBOARD_EXTENSION KeyboardExtension,
  128. PPORT_MOUSE_EXTENSION MouseExtension
  129. )
  130. /*++
  131. Routine Description:
  132. Called if the keyboard is the last device to initialized with respect to the
  133. mouse (if it is present at all). It calls the initialization function and
  134. then connects the (possibly) two interrupts, synchronizing the lower IRQL'ed
  135. interrupt to the higher one.
  136. Arguments:
  137. KeyboardExtension - Keyboard Extension
  138. SyncConnectContext - Structure to be filled in if synchronization needs to
  139. take place between this interrupt and the mouse
  140. interrupt.
  141. Return Value:
  142. STATUS_SUCCESSFUL if successful,
  143. --*/
  144. {
  145. NTSTATUS keyboardStatus = STATUS_UNSUCCESSFUL,
  146. mouseStatus = STATUS_UNSUCCESSFUL,
  147. status;
  148. BOOLEAN mouThoughtPresent;
  149. PAGED_CODE();
  150. //
  151. // Initialize the i8042 for all devices present on it.
  152. // If either device is unresponsive, then XXX_PRESENT() will be false
  153. //
  154. mouThoughtPresent = MOUSE_PRESENT();
  155. status = I8xInitializeHardwareAtBoot(&keyboardStatus, &mouseStatus);
  156. //
  157. // failure here means that we couldn't toggle the interrupts on the i8042
  158. //
  159. if (!NT_SUCCESS(status)) {
  160. return status;
  161. }
  162. if (DEVICE_START_SUCCESS(mouseStatus)) {
  163. //
  164. // Any errors will be logged by I8xMouseConnectInterruptAndEnable
  165. //
  166. status = I8xMouseConnectInterruptAndEnable(
  167. MouseExtension,
  168. mouseStatus == STATUS_DEVICE_NOT_CONNECTED ? FALSE : TRUE
  169. );
  170. //
  171. // mou couldn't connect, make sure our count of started devices reflects
  172. // this
  173. //
  174. if (!NT_SUCCESS(status)) {
  175. Print(DBG_SS_ERROR, ("thought mou was present, is not (2)!\n"));
  176. }
  177. }
  178. else {
  179. //
  180. // We thought the mouse was present, but it is not, make sure that started
  181. // devices reflects this
  182. //
  183. // if (mouThoughtPresent) {
  184. // InterlockedDecrement(&Globals.StartedDevices);
  185. // }
  186. }
  187. //
  188. // The keyboard could be present but not have been initialized
  189. // in I8xInitializeHardware
  190. //
  191. if (DEVICE_START_SUCCESS(keyboardStatus)) {
  192. //
  193. // I8xKeyboardConnectInterrupt will log any errors if unsuccessful
  194. //
  195. keyboardStatus = I8xKeyboardConnectInterrupt(KeyboardExtension);
  196. }
  197. return keyboardStatus;
  198. }
  199. NTSTATUS
  200. I8xKeyboardStartDevice(
  201. IN OUT PPORT_KEYBOARD_EXTENSION KeyboardExtension,
  202. IN PCM_RESOURCE_LIST ResourceList
  203. )
  204. /*++
  205. Routine Description:
  206. Configures the keyboard's device extension (ie allocation of pool,
  207. initialization of DPCs, etc). If the keyboard is the last device to start,
  208. it will also initialize the hardware and connect all the interrupts.
  209. Arguments:
  210. KeyboardExtension - Keyboard extesnion
  211. ResourceList - Translated resource list for this device
  212. Return Value:
  213. STATUS_SUCCESSFUL if successful,
  214. --*/
  215. {
  216. ULONG dumpData[1];
  217. NTSTATUS status = STATUS_SUCCESS;
  218. PDEVICE_OBJECT self;
  219. I8042_INITIALIZE_DATA_CONTEXT initializeDataContext;
  220. BOOLEAN tryMouseInit = FALSE;
  221. PAGED_CODE();
  222. Print(DBG_SS_TRACE, ("I8xKeyboardStartDevice, enter\n"));
  223. //
  224. // Check to see if kb has been started. If so, fail this start
  225. //
  226. if (KEYBOARD_INITIALIZED()) {
  227. Print(DBG_SS_ERROR, ("too many kbs!\n"));
  228. //
  229. // This is not really necessary because the value won't ever be checked
  230. // in the context of seeing if all the keyboards were bogus, but it is
  231. // done so that Globals.AddedKeyboards == # of actual started keyboards
  232. //
  233. InterlockedDecrement(&Globals.AddedKeyboards);
  234. status = STATUS_NO_SUCH_DEVICE;
  235. goto I8xKeyboardStartDeviceExit;
  236. }
  237. else if (KeyboardExtension->ConnectData.ClassService == NULL) {
  238. //
  239. // We are never really going to get here because if we don't have the
  240. // class driver on top of us, extension->IsKeyboard will be false and
  241. // we will think that the device is a mouse, but for completeness
  242. //
  243. // No class driver on top of us == BAD BAD BAD
  244. //
  245. // Fail the start of this device in the hope that there is another stack
  246. // that is correctly formed. Another side affect of having no class
  247. // driver is that the AddedKeyboards count is not incremented for this
  248. // device
  249. //
  250. Print(DBG_SS_ERROR, ("Keyboard started with out a service cb!\n"));
  251. return STATUS_INVALID_DEVICE_STATE;
  252. }
  253. status = I8xKeyboardConfiguration(KeyboardExtension,
  254. ResourceList
  255. );
  256. if (!NT_SUCCESS(status)) {
  257. if (I8xManuallyRemoveDevice(GET_COMMON_DATA(KeyboardExtension)) < 1) {
  258. tryMouseInit = TRUE;
  259. }
  260. goto I8xKeyboardStartDeviceExit;
  261. }
  262. ASSERT( KEYBOARD_PRESENT() );
  263. Globals.KeyboardExtension = KeyboardExtension;
  264. self = KeyboardExtension->Self;
  265. if ((KIRQL) KeyboardExtension->InterruptDescriptor.u.Interrupt.Level >
  266. Globals.ControllerData->Configuration.InterruptSynchIrql) {
  267. Globals.ControllerData->Configuration.InterruptSynchIrql =
  268. (KIRQL) KeyboardExtension->InterruptDescriptor.u.Interrupt.Level;
  269. }
  270. //
  271. // Initialize crash dump configuration
  272. //
  273. KeyboardExtension->CrashFlags = 0;
  274. KeyboardExtension->CurrentCrashFlags = 0;
  275. KeyboardExtension->CrashScanCode = (UCHAR) 0;
  276. KeyboardExtension->CrashScanCode2 = (UCHAR) 0;
  277. I8xKeyboardServiceParameters(
  278. &Globals.RegistryPath,
  279. KeyboardExtension
  280. );
  281. //
  282. // These may have been set by a value in the Parameters key. It overrides
  283. // the "Crash Dump" key
  284. //
  285. if (KeyboardExtension->CrashFlags == 0) {
  286. //
  287. // Get the crashdump information.
  288. //
  289. I8xServiceCrashDump(KeyboardExtension,
  290. &Globals.RegistryPath
  291. );
  292. }
  293. //
  294. // Allocate memory for the keyboard data queue.
  295. //
  296. KeyboardExtension->InputData = ExAllocatePool(
  297. NonPagedPool,
  298. KeyboardExtension->KeyboardAttributes.InputDataQueueLength
  299. );
  300. if (!KeyboardExtension->InputData) {
  301. //
  302. // Could not allocate memory for the keyboard data queue.
  303. //
  304. Print(DBG_SS_ERROR,
  305. ("I8xStartDevice: Could not allocate keyboard input data queue\n"
  306. ));
  307. dumpData[0] = KeyboardExtension->KeyboardAttributes.InputDataQueueLength;
  308. //
  309. // Log the error
  310. //
  311. I8xLogError(self,
  312. I8042_NO_BUFFER_ALLOCATED_KBD,
  313. I8042_ERROR_VALUE_BASE + 50,
  314. STATUS_INSUFFICIENT_RESOURCES,
  315. dumpData,
  316. 1
  317. );
  318. status = STATUS_INSUFFICIENT_RESOURCES;
  319. tryMouseInit = TRUE;
  320. goto I8xKeyboardStartDeviceExit;
  321. }
  322. else {
  323. KeyboardExtension->DataEnd =
  324. (PKEYBOARD_INPUT_DATA)
  325. ((PCHAR) (KeyboardExtension->InputData) +
  326. KeyboardExtension->KeyboardAttributes.InputDataQueueLength);
  327. //
  328. // Zero the keyboard input data ring buffer.
  329. //
  330. RtlZeroMemory(
  331. KeyboardExtension->InputData,
  332. KeyboardExtension->KeyboardAttributes.InputDataQueueLength
  333. );
  334. initializeDataContext.DeviceExtension = KeyboardExtension;
  335. initializeDataContext.DeviceType = KeyboardDeviceType;
  336. I8xInitializeDataQueue(&initializeDataContext);
  337. }
  338. KeyboardExtension->DpcInterlockKeyboard = -1;
  339. //
  340. // Initialize the ISR DPC . The ISR DPC
  341. // is responsible for calling the connected class driver's callback
  342. // routine to process the input data queue.
  343. //
  344. KeInitializeDpc(
  345. &KeyboardExtension->KeyboardIsrDpc,
  346. (PKDEFERRED_ROUTINE) I8042KeyboardIsrDpc,
  347. self
  348. );
  349. KeInitializeDpc(
  350. &KeyboardExtension->KeyboardIsrDpcRetry,
  351. (PKDEFERRED_ROUTINE) I8042KeyboardIsrDpc,
  352. self
  353. );
  354. KeInitializeDpc(
  355. &KeyboardExtension->SysButtonEventDpc,
  356. (PKDEFERRED_ROUTINE) I8xKeyboardSysButtonEventDpc,
  357. self
  358. );
  359. KeInitializeSpinLock(&KeyboardExtension->SysButtonSpinLock);
  360. if (KeyboardExtension->PowerCaps) {
  361. I8xRegisterDeviceInterface(KeyboardExtension->PDO,
  362. &GUID_DEVICE_SYS_BUTTON,
  363. &KeyboardExtension->SysButtonInterfaceName
  364. );
  365. }
  366. I8xInitWmi(GET_COMMON_DATA(KeyboardExtension));
  367. KeyboardExtension->Initialized = TRUE;
  368. //
  369. // This is not the last device to started on the i8042, defer h/w init
  370. // until the last device is started
  371. //
  372. if (MOUSE_PRESENT() && !MOUSE_STARTED()) {
  373. //
  374. // A mouse is present, but it has not been started yet
  375. //
  376. Print(DBG_SS_INFO, ("skipping init until mouse\n"));
  377. }
  378. else {
  379. status = I8xKeyboardInitializeHardware(KeyboardExtension,
  380. Globals.MouseExtension);
  381. }
  382. I8xKeyboardStartDeviceExit:
  383. if (tryMouseInit && MOUSE_STARTED() && !MOUSE_INITIALIZED()) {
  384. Print(DBG_SS_INFO, ("keyboard may have failed, trying to init mouse\n"));
  385. I8xMouseInitializeHardware(KeyboardExtension,
  386. Globals.MouseExtension
  387. );
  388. }
  389. Print(DBG_SS_INFO,
  390. ("I8xKeyboardStartDevice %s\n",
  391. NT_SUCCESS(status) ? "successful" : "unsuccessful"
  392. ));
  393. Print(DBG_SS_TRACE, ("I8xKeyboardStartDevice exit (0x%x)\n", status));
  394. return status;
  395. }
  396. VOID
  397. I8xKeyboardRemoveDeviceInitialized(
  398. PPORT_KEYBOARD_EXTENSION KeyboardExtension
  399. )
  400. {
  401. PIRP irp = NULL;
  402. KIRQL irql;
  403. KeAcquireSpinLock(&KeyboardExtension->SysButtonSpinLock, &irql);
  404. KeRemoveQueueDpc(&KeyboardExtension->SysButtonEventDpc);
  405. irp = KeyboardExtension->SysButtonEventIrp;
  406. KeyboardExtension->SysButtonEventIrp = NULL;
  407. if (irp && (irp->Cancel || IoSetCancelRoutine(irp, NULL) == NULL)) {
  408. irp = NULL;
  409. }
  410. KeReleaseSpinLock(&KeyboardExtension->SysButtonSpinLock, irql);
  411. if (irp) {
  412. I8xCompleteSysButtonIrp(irp, 0x0, STATUS_DELETE_PENDING);
  413. }
  414. }
  415. VOID
  416. I8xKeyboardRemoveDevice(
  417. PDEVICE_OBJECT DeviceObject
  418. )
  419. /*++
  420. Routine Description:
  421. Removes the device. This will only occur if the device removed itself.
  422. Disconnects the interrupt, removes the synchronization flag for the mouse if
  423. present, and frees any memory associated with the device.
  424. Arguments:
  425. DeviceObject - The device object for the keyboard
  426. Return Value:
  427. STATUS_SUCCESSFUL if successful,
  428. --*/
  429. {
  430. PPORT_KEYBOARD_EXTENSION keyboardExtension = DeviceObject->DeviceExtension;
  431. PIRP irp;
  432. Print(DBG_PNP_INFO, ("I8xKeyboardRemoveDevice enter\n"));
  433. PAGED_CODE();
  434. if (Globals.KeyboardExtension == keyboardExtension && keyboardExtension) {
  435. CLEAR_KEYBOARD_PRESENT();
  436. Globals.KeyboardExtension = NULL;
  437. }
  438. if (keyboardExtension->InterruptObject) {
  439. IoDisconnectInterrupt(keyboardExtension->InterruptObject);
  440. keyboardExtension->InterruptObject = NULL;
  441. }
  442. if (keyboardExtension->InputData) {
  443. ExFreePool(keyboardExtension->InputData);
  444. keyboardExtension->InputData = 0;
  445. }
  446. //
  447. // See if we have gotten a sys button key press in the midst of a removal.
  448. // If so, then the request will be in PendingCompletion...Irp, otherwise we
  449. // might have the irp in Pending...Irp
  450. //
  451. if (keyboardExtension->Initialized) {
  452. I8xKeyboardRemoveDeviceInitialized(keyboardExtension);
  453. }
  454. if (keyboardExtension->SysButtonInterfaceName.Buffer) {
  455. IoSetDeviceInterfaceState(&keyboardExtension->SysButtonInterfaceName,
  456. FALSE
  457. );
  458. RtlFreeUnicodeString(&keyboardExtension->SysButtonInterfaceName);
  459. }
  460. }