Leaked source code of windows server 2003
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.

1356 lines
40 KiB

  1. /*+
  2. Copyright (c) 1997-1998 Microsoft Corporation, All Rights Reserved
  3. Module Name:
  4. moupnp.c
  5. Abstract:
  6. This module contains plug & play code for the aux device (mouse) of the
  7. i8042prt device driver
  8. Environment:
  9. Kernel mode.
  10. Revision History:
  11. --*/
  12. #include "i8042prt.h"
  13. #include "i8042log.h"
  14. #include <initguid.h>
  15. #include <wdmguid.h>
  16. #ifdef ALLOC_PRAGMA
  17. #pragma alloc_text(PAGE, I8xMouseConnectInterruptAndEnable)
  18. #pragma alloc_text(PAGE, I8xMouseStartDevice)
  19. #pragma alloc_text(PAGE, I8xMouseInitializeHardware)
  20. #pragma alloc_text(PAGE, I8xMouseRemoveDevice)
  21. #pragma alloc_text(PAGE, I8xProfileNotificationCallback)
  22. #pragma alloc_text(PAGE, I8xMouseInitializeInterruptWorker)
  23. //
  24. // These will be locked down right before the mouse interrupt is enabled if a
  25. // mouse is present
  26. //
  27. #pragma alloc_text(PAGEMOUC, I8xMouseInitializePolledWorker)
  28. #pragma alloc_text(PAGEMOUC, I8xMouseEnableSynchRoutine)
  29. #pragma alloc_text(PAGEMOUC, I8xMouseEnableDpc)
  30. #pragma alloc_text(PAGEMOUC, I8xIsrResetDpc)
  31. #pragma alloc_text(PAGEMOUC, I8xMouseResetTimeoutProc)
  32. #pragma alloc_text(PAGEMOUC, I8xMouseResetSynchRoutine)
  33. #endif
  34. #define MOUSE_INIT_POLLED(MouseExtension) \
  35. { \
  36. KeInitializeDpc(&MouseExtension->EnableMouse.Dpc, \
  37. (PKDEFERRED_ROUTINE) I8xMouseEnableDpc, \
  38. MouseExtension); \
  39. KeInitializeTimerEx(&MouseExtension->EnableMouse.Timer, \
  40. SynchronizationTimer); \
  41. MouseExtension->InitializePolled = TRUE; \
  42. }
  43. #define MOUSE_INIT_INTERRUPT(MouseExtension) \
  44. { \
  45. KeInitializeDpc(&MouseExtension->ResetMouse.Dpc, \
  46. (PKDEFERRED_ROUTINE) I8xMouseResetTimeoutProc, \
  47. MouseExtension); \
  48. KeInitializeTimer(&MouseExtension->ResetMouse.Timer); \
  49. MouseExtension->InitializePolled = FALSE; \
  50. }
  51. NTSTATUS
  52. I8xMouseConnectInterruptAndEnable(
  53. PPORT_MOUSE_EXTENSION MouseExtension,
  54. BOOLEAN Reset
  55. )
  56. /*++
  57. Routine Description:
  58. Calls IoConnectInterupt to connect the mouse interrupt
  59. Arguments:
  60. MouseExtension - Mouse Extension
  61. Reset - flag to indicate if the mouse should be reset from within this function
  62. Return Value:
  63. STATUS_SUCCESSFUL if successful,
  64. --*/
  65. {
  66. NTSTATUS status = STATUS_SUCCESS;
  67. ULONG dumpData[1];
  68. PI8042_CONFIGURATION_INFORMATION configuration;
  69. PDEVICE_OBJECT self;
  70. PAGED_CODE();
  71. Print(DBG_SS_NOISE, ("Connect INT, reset = %d\n", (ULONG) Reset));
  72. //
  73. // If the devices were started in totally disparate manner, make sure we
  74. // retry to connect the interrupt (and fail and NULL out
  75. // MouseInterruptObject)
  76. //
  77. if (MouseExtension->InterruptObject) {
  78. return STATUS_SUCCESS;
  79. }
  80. configuration = &Globals.ControllerData->Configuration;
  81. self = MouseExtension->Self;
  82. //
  83. // Lock down all of the mouse related ISR/DPC functions
  84. //
  85. MmLockPagableCodeSection(I8042MouseInterruptService);
  86. //
  87. // Connect the interrupt and set everything in motion
  88. //
  89. Print(DBG_SS_NOISE,
  90. ("I8xMouseConnectInterruptAndEnable:\n"
  91. "\tFDO = 0x%x\n"
  92. "\tVector = 0x%x\n"
  93. "\tIrql = 0x%x\n"
  94. "\tSynchIrql = 0x%x\n"
  95. "\tIntterupt Mode = %s\n"
  96. "\tShared int: %s\n"
  97. "\tAffinity = 0x%x\n"
  98. "\tFloating Save = %s\n",
  99. self,
  100. (ULONG) MouseExtension->InterruptDescriptor.u.Interrupt.Vector,
  101. (ULONG) MouseExtension->InterruptDescriptor.u.Interrupt.Level,
  102. (ULONG) configuration->InterruptSynchIrql,
  103. MouseExtension->InterruptDescriptor.Flags
  104. == CM_RESOURCE_INTERRUPT_LATCHED ? "Latched" : "LevelSensitive",
  105. (ULONG) MouseExtension->InterruptDescriptor.ShareDisposition
  106. == CmResourceShareShared ? "true" : "false",
  107. (ULONG) MouseExtension->InterruptDescriptor.u.Interrupt.Affinity,
  108. configuration->FloatingSave ? "yes" : "no"
  109. ));
  110. MouseExtension->IsIsrActivated = TRUE;
  111. status = IoConnectInterrupt(
  112. &(MouseExtension->InterruptObject),
  113. (PKSERVICE_ROUTINE) I8042MouseInterruptService,
  114. self,
  115. &MouseExtension->InterruptSpinLock,
  116. MouseExtension->InterruptDescriptor.u.Interrupt.Vector,
  117. (KIRQL) MouseExtension->InterruptDescriptor.u.Interrupt.Level,
  118. configuration->InterruptSynchIrql,
  119. MouseExtension->InterruptDescriptor.Flags
  120. == CM_RESOURCE_INTERRUPT_LATCHED ?
  121. Latched : LevelSensitive,
  122. (BOOLEAN) (MouseExtension->InterruptDescriptor.ShareDisposition
  123. == CmResourceShareShared),
  124. MouseExtension->InterruptDescriptor.u.Interrupt.Affinity,
  125. configuration->FloatingSave
  126. );
  127. if (NT_SUCCESS(status)) {
  128. INTERNAL_I8042_START_INFORMATION startInfo;
  129. PDEVICE_OBJECT topOfStack = IoGetAttachedDeviceReference(self);
  130. ASSERT(MouseExtension->InterruptObject != NULL);
  131. ASSERT(topOfStack);
  132. RtlZeroMemory(&startInfo, sizeof(INTERNAL_I8042_START_INFORMATION));
  133. startInfo.Size = sizeof(INTERNAL_I8042_START_INFORMATION);
  134. startInfo.InterruptObject = MouseExtension->InterruptObject;
  135. I8xSendIoctl(topOfStack,
  136. IOCTL_INTERNAL_I8042_MOUSE_START_INFORMATION,
  137. &startInfo,
  138. sizeof(INTERNAL_I8042_START_INFORMATION)
  139. );
  140. ObDereferenceObject(topOfStack);
  141. }
  142. else {
  143. Print(DBG_SS_ERROR, ("Could not connect mouse isr!!!\n"));
  144. dumpData[0] = MouseExtension->InterruptDescriptor.u.Interrupt.Level;
  145. I8xLogError(self,
  146. I8042_NO_INTERRUPT_CONNECTED_MOU,
  147. I8042_ERROR_VALUE_BASE + 90,
  148. STATUS_INSUFFICIENT_RESOURCES,
  149. dumpData,
  150. 1
  151. );
  152. I8xManuallyRemoveDevice(GET_COMMON_DATA(MouseExtension));
  153. return status;
  154. }
  155. if (Reset) {
  156. if (MouseExtension->InitializePolled) {
  157. //
  158. // Enable mouse transmissions, now that the interrupts are enabled.
  159. // We've held off transmissions until now, in an attempt to
  160. // keep the driver's notion of mouse input data state in sync
  161. // with the mouse hardware.
  162. //
  163. status = I8xMouseEnableTransmission(MouseExtension);
  164. if (!NT_SUCCESS(status)) {
  165. //
  166. // Couldn't enable mouse transmissions. Continue on, anyway.
  167. //
  168. Print(DBG_SS_ERROR,
  169. ("I8xMouseConnectInterruptAndEnable: "
  170. "Could not enable mouse transmission (0x%x)\n", status));
  171. status = STATUS_SUCCESS;
  172. }
  173. }
  174. else {
  175. I8X_MOUSE_INIT_COUNTERS(MouseExtension);
  176. //
  177. // Reset the mouse and start the init state machine in the ISR
  178. //
  179. status = I8xResetMouse(MouseExtension);
  180. if (!NT_SUCCESS(status)) {
  181. Print(DBG_SS_ERROR,
  182. ("I8xMouseConnectInterruptAndEnable: "
  183. "failed to reset mouse (0x%x), reset count = %d, failed resets = %d, resend count = %d\n",
  184. status, MouseExtension->ResetCount,
  185. MouseExtension->FailedCompleteResetCount,
  186. MouseExtension->ResendCount));
  187. }
  188. }
  189. }
  190. else {
  191. Print(DBG_SS_NOISE, ("NOT resetting mouse on INT connect\n"));
  192. }
  193. return status;
  194. }
  195. NTSTATUS
  196. I8xMouseInitializeHardware(
  197. PPORT_KEYBOARD_EXTENSION KeyboardExtension,
  198. PPORT_MOUSE_EXTENSION MouseExtension
  199. )
  200. /*++
  201. Routine Description:
  202. Called if the mouse is the last device to initialized with respect to the
  203. keyboard (if it is present at all). It calls the initialization function and
  204. then connects the (possibly) two interrupts, synchronizing the lower IRQL'ed
  205. interrupt to the higher one.
  206. Arguments:
  207. MouseExtension - Mouse Extension
  208. SyncConnectContext - Structure to be filled in if synchronization needs to
  209. take place between this interrupt and the mouse
  210. interrupt.
  211. Return Value:
  212. STATUS_SUCCESSFUL if successful,
  213. --*/
  214. {
  215. NTSTATUS keyboardStatus = STATUS_UNSUCCESSFUL,
  216. mouseStatus = STATUS_UNSUCCESSFUL,
  217. status = STATUS_SUCCESS;
  218. BOOLEAN kbThoughtPresent;
  219. PAGED_CODE();
  220. //
  221. // Initialize the hardware for all types of devices present on the i8042
  222. //
  223. kbThoughtPresent = KEYBOARD_PRESENT();
  224. status = I8xInitializeHardwareAtBoot(&keyboardStatus, &mouseStatus);
  225. //
  226. // The kb has alread been started (from the controller's perspective, the
  227. // of the mouse is denied in fear of disabling the kb
  228. //
  229. if (status == STATUS_INVALID_DEVICE_REQUEST) {
  230. I8xManuallyRemoveDevice(GET_COMMON_DATA(MouseExtension));
  231. }
  232. if (!NT_SUCCESS(status)) {
  233. return status;
  234. }
  235. if (DEVICE_START_SUCCESS(keyboardStatus)) {
  236. //
  237. // Any errors will be logged by I8xKeyboardConnectInterrupt
  238. //
  239. status = I8xKeyboardConnectInterrupt(KeyboardExtension);
  240. //
  241. // kb couldn't start, make sure our count of started devices reflects
  242. // this
  243. //
  244. // if (!NT_SUCCESS(status)) {
  245. // InterlockedDecrement(&Globals.StartedDevices);
  246. // }
  247. }
  248. else {
  249. //
  250. // We thought the kb was present, but it is not, make sure that started
  251. // devices reflects this
  252. //
  253. if (kbThoughtPresent) {
  254. Print(DBG_SS_ERROR, ("thought kb was present, is not!\n"));
  255. }
  256. }
  257. //
  258. // The mouse could be present but not have been initialized
  259. // in I8xInitializeHardware
  260. //
  261. if (DEVICE_START_SUCCESS(mouseStatus)) {
  262. //
  263. // I8xMouseConnectInterruptAndEnable will log any errors if unsuccessful
  264. //
  265. mouseStatus = I8xMouseConnectInterruptAndEnable(
  266. MouseExtension,
  267. mouseStatus == STATUS_DEVICE_NOT_CONNECTED ? FALSE : TRUE
  268. );
  269. }
  270. return mouseStatus;
  271. }
  272. NTSTATUS
  273. I8xMouseStartDevice(
  274. PPORT_MOUSE_EXTENSION MouseExtension,
  275. IN PCM_RESOURCE_LIST ResourceList
  276. )
  277. /*++
  278. Routine Description:
  279. Configures the mouse's device extension (ie allocation of pool,
  280. initialization of DPCs, etc). If the mouse is the last device to start,
  281. it will also initialize the hardware and connect all the interrupts.
  282. Arguments:
  283. MouseExtension - Mouse extesnion
  284. ResourceList - Translated resource list for this device
  285. Return Value:
  286. STATUS_SUCCESSFUL if successful,
  287. --*/
  288. {
  289. ULONG dumpData[1];
  290. NTSTATUS status = STATUS_SUCCESS;
  291. PDEVICE_OBJECT self;
  292. I8042_INITIALIZE_DATA_CONTEXT initializeDataContext;
  293. BOOLEAN tryKbInit = FALSE;
  294. PAGED_CODE();
  295. Print(DBG_SS_TRACE, ("I8xMouseStartDevice, enter\n"));
  296. //
  297. // Check to see if a mouse has been started. If so, fail this start.
  298. //
  299. if (MOUSE_INITIALIZED()) {
  300. Print(DBG_SS_ERROR, ("too many mice!\n"));
  301. //
  302. // This is not really necessary because the value won't ever be checked
  303. // in the context of seeing if all the mice were bogus, but it is
  304. // done so that Globals.AddedMice == # of actual started mice
  305. //
  306. InterlockedDecrement(&Globals.AddedMice);
  307. status = STATUS_NO_SUCH_DEVICE;
  308. goto I8xMouseStartDeviceExit;
  309. }
  310. else if (MouseExtension->ConnectData.ClassService == NULL) {
  311. //
  312. // No class driver on top of us == BAD BAD BAD
  313. //
  314. // Fail the start of this device in the hope that there is another stack
  315. // that is correctly formed. Another side affect of having no class
  316. // driver is that the AddedMice count is not incremented for this
  317. // device
  318. //
  319. Print(DBG_SS_ERROR, ("Mouse started with out a service cb!\n"));
  320. status = STATUS_INVALID_DEVICE_STATE;
  321. goto I8xMouseStartDeviceExit;
  322. }
  323. //
  324. // Parse and store all of the resources associated with the mouse
  325. //
  326. status = I8xMouseConfiguration(MouseExtension,
  327. ResourceList
  328. );
  329. if (!NT_SUCCESS(status)) {
  330. if (I8xManuallyRemoveDevice(GET_COMMON_DATA(MouseExtension)) < 1) {
  331. tryKbInit = TRUE;
  332. }
  333. goto I8xMouseStartDeviceExit;
  334. }
  335. ASSERT( MOUSE_PRESENT() );
  336. Globals.MouseExtension = MouseExtension;
  337. self = MouseExtension->Self;
  338. if ((KIRQL) MouseExtension->InterruptDescriptor.u.Interrupt.Level >
  339. Globals.ControllerData->Configuration.InterruptSynchIrql) {
  340. Globals.ControllerData->Configuration.InterruptSynchIrql =
  341. (KIRQL) MouseExtension->InterruptDescriptor.u.Interrupt.Level;
  342. }
  343. I8xMouseServiceParameters(&Globals.RegistryPath,
  344. MouseExtension
  345. );
  346. //
  347. // Allocate memory for the mouse data queue.
  348. //
  349. MouseExtension->InputData =
  350. ExAllocatePool(NonPagedPool,
  351. MouseExtension->MouseAttributes.InputDataQueueLength
  352. );
  353. if (!MouseExtension->InputData) {
  354. //
  355. // Could not allocate memory for the mouse data queue.
  356. //
  357. Print(DBG_SS_ERROR,
  358. ("I8xMouseStartDevice: Could not allocate mouse input data queue\n"
  359. ));
  360. dumpData[0] = MouseExtension->MouseAttributes.InputDataQueueLength;
  361. //
  362. // Log the error
  363. //
  364. I8xLogError(self,
  365. I8042_NO_BUFFER_ALLOCATED_MOU,
  366. I8042_ERROR_VALUE_BASE + 50,
  367. STATUS_INSUFFICIENT_RESOURCES,
  368. dumpData,
  369. 1
  370. );
  371. status = STATUS_INSUFFICIENT_RESOURCES;
  372. //
  373. // Mouse failed initialization, but we can try to get the keyboard
  374. // working if it has been initialized
  375. //
  376. tryKbInit = TRUE;
  377. goto I8xMouseStartDeviceExit;
  378. }
  379. else {
  380. MouseExtension->DataEnd =
  381. (PMOUSE_INPUT_DATA)
  382. ((PCHAR) (MouseExtension->InputData) +
  383. MouseExtension->MouseAttributes.InputDataQueueLength);
  384. //
  385. // Zero the mouse input data ring buffer.
  386. //
  387. RtlZeroMemory(
  388. MouseExtension->InputData,
  389. MouseExtension->MouseAttributes.InputDataQueueLength
  390. );
  391. initializeDataContext.DeviceExtension = MouseExtension;
  392. initializeDataContext.DeviceType = MouseDeviceType;
  393. I8xInitializeDataQueue(&initializeDataContext);
  394. }
  395. #if MOUSE_RECORD_ISR
  396. if (MouseExtension->RecordHistoryFlags && MouseExtension->RecordHistoryCount) {
  397. IsrStateHistory = (PMOUSE_STATE_RECORD)
  398. ExAllocatePool(
  399. NonPagedPool,
  400. MouseExtension->RecordHistoryCount * sizeof(MOUSE_STATE_RECORD)
  401. );
  402. if (IsrStateHistory) {
  403. RtlZeroMemory(
  404. IsrStateHistory,
  405. MouseExtension->RecordHistoryCount * sizeof(MOUSE_STATE_RECORD)
  406. );
  407. CurrentIsrState = IsrStateHistory;
  408. IsrStateHistoryEnd =
  409. IsrStateHistory + MouseExtension->RecordHistoryCount;
  410. }
  411. else {
  412. MouseExtension->RecordHistoryFlags = 0x0;
  413. MouseExtension->RecordHistoryCount = 0;
  414. }
  415. }
  416. #endif // MOUSE_RECORD_ISR
  417. SET_RECORD_STATE(MouseExtension, RECORD_INIT);
  418. MouseExtension->DpcInterlockMouse = -1;
  419. //
  420. // Initialize the port DPC queue to log overrun and internal
  421. // driver errors.
  422. //
  423. KeInitializeDpc(
  424. &MouseExtension->ErrorLogDpc,
  425. (PKDEFERRED_ROUTINE) I8042ErrorLogDpc,
  426. self
  427. );
  428. //
  429. // Initialize the ISR DPC. The ISR DPC
  430. // is responsible for calling the connected class driver's callback
  431. // routine to process the input data queue.
  432. //
  433. KeInitializeDpc(
  434. &MouseExtension->MouseIsrDpc,
  435. (PKDEFERRED_ROUTINE) I8042MouseIsrDpc,
  436. self
  437. );
  438. KeInitializeDpc(
  439. &MouseExtension->MouseIsrDpcRetry,
  440. (PKDEFERRED_ROUTINE) I8042MouseIsrDpc,
  441. self
  442. );
  443. KeInitializeDpc(
  444. &MouseExtension->MouseIsrResetDpc,
  445. (PKDEFERRED_ROUTINE) I8xIsrResetDpc,
  446. MouseExtension
  447. );
  448. if (MouseExtension->InitializePolled) {
  449. MOUSE_INIT_POLLED(MouseExtension);
  450. }
  451. else {
  452. MOUSE_INIT_INTERRUPT(MouseExtension);
  453. }
  454. I8xInitWmi(GET_COMMON_DATA(MouseExtension));
  455. MouseExtension->Initialized = TRUE;
  456. IoRegisterPlugPlayNotification(
  457. EventCategoryHardwareProfileChange,
  458. 0x0,
  459. NULL,
  460. self->DriverObject,
  461. I8xProfileNotificationCallback,
  462. (PVOID) MouseExtension,
  463. &MouseExtension->NotificationEntry
  464. );
  465. //
  466. // This is not the last device to started on the i8042, defer h/w init
  467. // until the last device is started
  468. //
  469. if (KEYBOARD_PRESENT() && !KEYBOARD_STARTED()) {
  470. //
  471. // Delay the initialization until both have been started
  472. //
  473. Print(DBG_SS_INFO, ("skipping init until kb\n"));
  474. }
  475. else {
  476. status = I8xMouseInitializeHardware(Globals.KeyboardExtension,
  477. MouseExtension);
  478. }
  479. I8xMouseStartDeviceExit:
  480. if (tryKbInit && KEYBOARD_STARTED() && !KEYBOARD_INITIALIZED()) {
  481. Print(DBG_SS_INFO, ("moused may failed, trying to init kb\n"));
  482. I8xKeyboardInitializeHardware(Globals.KeyboardExtension,
  483. MouseExtension
  484. );
  485. }
  486. Print(DBG_SS_INFO,
  487. ("I8xMouseStartDevice %s\n",
  488. NT_SUCCESS(status) ? "successful" : "unsuccessful"
  489. ));
  490. Print(DBG_SS_TRACE, ("I8xMouseStartDevice exit (0x%x)\n", status));
  491. return status;
  492. }
  493. VOID
  494. I8xMouseRemoveDevice(
  495. PDEVICE_OBJECT DeviceObject
  496. )
  497. /*++
  498. Routine Description:
  499. Removes the device. This will only occur if the device removed itself.
  500. Disconnects the interrupt, removes the synchronization flag for the keyboard
  501. if present, and frees any memory associated with the device.
  502. Arguments:
  503. DeviceObject - The device object for the mouse
  504. Return Value:
  505. STATUS_SUCCESSFUL if successful,
  506. --*/
  507. {
  508. PPORT_MOUSE_EXTENSION mouseExtension = DeviceObject->DeviceExtension;
  509. PAGED_CODE();
  510. Print(DBG_PNP_INFO, ("I8xMouseRemoveDevice enter\n"));
  511. if (mouseExtension->Initialized) {
  512. if (mouseExtension->NotificationEntry) {
  513. IoUnregisterPlugPlayNotification(mouseExtension->NotificationEntry);
  514. mouseExtension->NotificationEntry = NULL;
  515. }
  516. }
  517. //
  518. // By this point, it is guaranteed that the other ISR will not be synching
  519. // against this one. We can safely disconnect and free all acquire resources
  520. //
  521. if (mouseExtension->InterruptObject) {
  522. IoDisconnectInterrupt(mouseExtension->InterruptObject);
  523. mouseExtension->InterruptObject = NULL;
  524. }
  525. if (mouseExtension->InputData) {
  526. ExFreePool(mouseExtension->InputData);
  527. mouseExtension->InputData = 0;
  528. }
  529. RtlFreeUnicodeString(&mouseExtension->WheelDetectionIDs);
  530. if (Globals.MouseExtension == mouseExtension) {
  531. CLEAR_MOUSE_PRESENT();
  532. Globals.MouseExtension = NULL;
  533. }
  534. }
  535. NTSTATUS
  536. I8xProfileNotificationCallback(
  537. IN PHWPROFILE_CHANGE_NOTIFICATION NotificationStructure,
  538. PPORT_MOUSE_EXTENSION MouseExtension
  539. )
  540. {
  541. PAGED_CODE();
  542. if (IsEqualGUID ((LPGUID) &(NotificationStructure->Event),
  543. (LPGUID) &GUID_HWPROFILE_CHANGE_COMPLETE)) {
  544. Print(DBG_PNP_INFO | DBG_SS_INFO,
  545. ("received hw profile change complete notification\n"));
  546. I8X_MOUSE_INIT_COUNTERS(MouseExtension);
  547. SET_RECORD_STATE(Globals.MouseExtension, RECORD_HW_PROFILE_CHANGE);
  548. I8xResetMouse(MouseExtension);
  549. }
  550. else {
  551. Print(DBG_PNP_NOISE, ("received other hw profile notification\n"));
  552. }
  553. return STATUS_SUCCESS;
  554. }
  555. //
  556. // Begin infrastructure for initializing the mouse via polling
  557. //
  558. BOOLEAN
  559. I8xMouseEnableSynchRoutine(
  560. IN PPORT_MOUSE_EXTENSION MouseExtension
  561. )
  562. /*++
  563. Routine Description:
  564. Writes the reset byte (if necessary to the mouse) in synch with the
  565. interrupt
  566. Arguments:
  567. MouseExtension - Mouse Extension
  568. Return Value:
  569. TRUE if the byte was written successfully
  570. --*/
  571. {
  572. NTSTATUS status;
  573. if (++MouseExtension->EnableMouse.Count > 15) {
  574. //
  575. // log an error b/c we tried this many times
  576. //
  577. Print(DBG_SS_ERROR, ("called enable 16 times!\n"));
  578. return FALSE;
  579. }
  580. Print(DBG_STARTUP_SHUTDOWN_MASK, ("resending enable mouse!\n"));
  581. status = I8xMouseEnableTransmission(MouseExtension);
  582. return NT_SUCCESS(status);
  583. }
  584. VOID
  585. I8xMouseEnableDpc(
  586. IN PKDPC Dpc,
  587. IN PPORT_MOUSE_EXTENSION MouseExtension,
  588. IN PVOID SystemArg1,
  589. IN PVOID SystemArg2
  590. )
  591. /*++
  592. Routine Description:
  593. DPC for making sure that the sending of the mouse enable command was
  594. successful. If it has failed, try to enable the mouse again synched up to
  595. the interrupt.
  596. Arguments:
  597. Dpc - The dpc request
  598. MouseExtension - Mouse extension
  599. SystemArg1 - Unused
  600. SystemArg2 - Unused
  601. Return Value:
  602. None.
  603. --*/
  604. {
  605. BOOLEAN result;
  606. UNREFERENCED_PARAMETER(Dpc);
  607. UNREFERENCED_PARAMETER(SystemArg1);
  608. UNREFERENCED_PARAMETER(SystemArg2);
  609. ASSERT(!MouseExtension->IsKeyboard);
  610. if (!MouseExtension->EnableMouse.Enabled) {
  611. //
  612. // Must be called at IRQL <= DISPATCH
  613. //
  614. Print(DBG_SS_NOISE, ("cancelling due to isr receiving ACK!\n"));
  615. KeCancelTimer(&MouseExtension->EnableMouse.Timer);
  616. return;
  617. }
  618. result = KeSynchronizeExecution(
  619. MouseExtension->InterruptObject,
  620. (PKSYNCHRONIZE_ROUTINE) I8xMouseEnableSynchRoutine,
  621. MouseExtension
  622. );
  623. if (!result) {
  624. Print(DBG_SS_NOISE, ("cancelling due to enable FALSE!\n"));
  625. KeCancelTimer(&MouseExtension->EnableMouse.Timer);
  626. }
  627. }
  628. //
  629. // End infrastructure for initializing the mouse via polling
  630. //
  631. //
  632. // Begin infrastructure for initializing the mouse via the interrupt
  633. //
  634. BOOLEAN
  635. I8xResetMouseFromDpc(
  636. PPORT_MOUSE_EXTENSION MouseExtension,
  637. MOUSE_RESET_SUBSTATE NewResetSubState
  638. )
  639. {
  640. PIO_WORKITEM item;
  641. item = IoAllocateWorkItem(MouseExtension->Self);
  642. if (item) {
  643. MouseExtension->WorkerResetSubState = NewResetSubState;
  644. IoQueueWorkItem(item,
  645. I8xMouseInitializeInterruptWorker,
  646. DelayedWorkQueue,
  647. item);
  648. }
  649. else {
  650. I8xResetMouseFailed(MouseExtension);
  651. }
  652. return (BOOLEAN) (item != NULL);
  653. }
  654. VOID
  655. I8xIsrResetDpc(
  656. IN PKDPC Dpc,
  657. IN PPORT_MOUSE_EXTENSION MouseExtension,
  658. IN ULONG ResetPolled,
  659. IN PVOID SystemArg2
  660. )
  661. /*++
  662. Routine Description:
  663. The ISR needs to reset the mouse so it queued this DPC. ResetPolled
  664. detemines if the reset and initialization are sychronous (ie polled) or
  665. asynchronous (using the interrupt).
  666. Arguments:
  667. Dpc - The request
  668. MouseExtension - Mouse Extension
  669. ResetPolled - If non zero, should reset and initialize the mouse in a polled
  670. manner
  671. SystemArg2 - Unused
  672. Return Value:
  673. None.
  674. --*/
  675. {
  676. PIO_WORKITEM item;
  677. UNREFERENCED_PARAMETER(Dpc);
  678. UNREFERENCED_PARAMETER(SystemArg2);
  679. if (ResetPolled) {
  680. item = IoAllocateWorkItem(MouseExtension->Self);
  681. if (!item) {
  682. I8xResetMouseFailed(MouseExtension);
  683. }
  684. if (!MouseExtension->InitializePolled) {
  685. MOUSE_INIT_POLLED(MouseExtension);
  686. }
  687. SET_RECORD_STATE(MouseExtension, RECORD_DPC_RESET_POLLED);
  688. IoQueueWorkItem(item,
  689. I8xMouseInitializePolledWorker,
  690. DelayedWorkQueue,
  691. item);
  692. }
  693. else {
  694. //
  695. // If we initialized the mouse polled, then we need to setup the data
  696. // structures so that we can mimic init via the interrupt
  697. //
  698. if (MouseExtension->InitializePolled) {
  699. MOUSE_INIT_INTERRUPT(MouseExtension);
  700. MouseExtension->InitializePolled = FALSE;
  701. }
  702. SET_RECORD_STATE(MouseExtension, RECORD_DPC_RESET);
  703. I8xResetMouseFromDpc(MouseExtension, IsrResetNormal);
  704. }
  705. }
  706. VOID
  707. I8xMouseResetTimeoutProc(
  708. IN PKDPC Dpc,
  709. IN PPORT_MOUSE_EXTENSION MouseExtension,
  710. IN PVOID SystemArg1,
  711. IN PVOID SystemArg2
  712. )
  713. /*++
  714. Routine Description:
  715. DPC for the watch dog timer that runs when the mouse is being initialized
  716. via the interrupt. The function checks upon the state of the mouse. If
  717. a certain action has timed out, then next state is initiated via a write to
  718. the device
  719. Arguments:
  720. Dpc - The dpc request
  721. MouseExtension - Mouse extension
  722. SystemArg1 - Unused
  723. SystemArg2 - Unused
  724. Return Value:
  725. None.
  726. --*/
  727. {
  728. LARGE_INTEGER li;
  729. I8X_MOUSE_RESET_INFO resetInfo;
  730. UNREFERENCED_PARAMETER(Dpc);
  731. UNREFERENCED_PARAMETER(SystemArg1);
  732. UNREFERENCED_PARAMETER(SystemArg2);
  733. if (MouseExtension->ResetMouse.IsrResetState == IsrResetStopResetting) {
  734. //
  735. // We have waited one second, send the reset and continue the state
  736. // machine. I8xResetMouse will set the state correctly and set all the
  737. // vars to the appropriate states.
  738. //
  739. Print(DBG_SS_ERROR | DBG_SS_INFO, ("Paused one second for reset\n"));
  740. I8xResetMouseFromDpc(MouseExtension, KeepOldSubState);
  741. return;
  742. }
  743. else if (MouseExtension->ResetMouse.IsrResetState == MouseResetFailed) {
  744. //
  745. // We have tried to repeatedly reset the mouse, but have failed. We
  746. // have already taken care of this in I8xResetMouseFailed.
  747. //
  748. return;
  749. }
  750. resetInfo.MouseExtension = MouseExtension;
  751. resetInfo.InternalResetState = InternalContinueTimer;
  752. if (KeSynchronizeExecution(MouseExtension->InterruptObject,
  753. (PKSYNCHRONIZE_ROUTINE) I8xMouseResetSynchRoutine,
  754. &resetInfo)) {
  755. switch (resetInfo.InternalResetState) {
  756. case InternalContinueTimer:
  757. //
  758. // Delay for 1.5 second
  759. //
  760. li = RtlConvertLongToLargeInteger(-MOUSE_RESET_TIMEOUT);
  761. KeSetTimer(&MouseExtension->ResetMouse.Timer,
  762. li,
  763. &MouseExtension->ResetMouse.Dpc
  764. );
  765. Print(DBG_SS_NOISE, ("Requeueing timer\n"));
  766. break;
  767. case InternalMouseReset:
  768. //
  769. // If we have had too many resets, I8xResetMouse will take of the
  770. // cleanup
  771. //
  772. I8xResetMouseFromDpc(MouseExtension, KeepOldSubState);
  773. break;
  774. case InternalPauseOneSec:
  775. //
  776. // Delay for 1 second, we will handle this case up above
  777. //
  778. li = RtlConvertLongToLargeInteger(-1 * 1000 * 1000 * 10);
  779. KeSetTimer(&MouseExtension->ResetMouse.Timer,
  780. li,
  781. &MouseExtension->ResetMouse.Dpc
  782. );
  783. break;
  784. }
  785. }
  786. }
  787. BOOLEAN
  788. I8xMouseResetSynchRoutine(
  789. PI8X_MOUSE_RESET_INFO ResetInfo
  790. )
  791. /*++
  792. Routine Description:
  793. Synchronized routine with the mouse interrupt to check upon the state of
  794. the mouse while it is being reset. Certain situations arise on a
  795. variety of platforms (lost bytes, numerous resend requests). These are
  796. taken care of here.
  797. Arguments:
  798. ResetInfo - struct to be filled in about the current state of the mouse
  799. Return Value:
  800. TRUE if the watchdog timer should keep on checking the state of the device
  801. FALSE if the watchdog timer should cease because the device has been
  802. initialized correctly.
  803. --*/
  804. {
  805. LARGE_INTEGER tickNow, tickDelta, oneSecond, threeSeconds;
  806. PPORT_MOUSE_EXTENSION mouseExtension;
  807. mouseExtension = ResetInfo->MouseExtension;
  808. Print(DBG_SS_NOISE, ("synch routine enter\n"));
  809. if (mouseExtension->InputState != MouseResetting) {
  810. return FALSE;
  811. }
  812. //
  813. // PreviousTick is set whenever the last byte was received
  814. //
  815. KeQueryTickCount(&tickNow);
  816. tickDelta.QuadPart =
  817. tickNow.QuadPart - mouseExtension->PreviousTick.QuadPart;
  818. //
  819. // convert one second into ticks
  820. //
  821. oneSecond = RtlConvertLongToLargeInteger(1000 * 1000 * 10);
  822. oneSecond.QuadPart /= KeQueryTimeIncrement();
  823. switch (mouseExtension->InputResetSubState) {
  824. case ExpectingReset:
  825. switch (mouseExtension->LastByteReceived) {
  826. case 0x00:
  827. if (tickDelta.QuadPart > oneSecond.QuadPart) {
  828. //
  829. // Didn't get any reset response, try another reset
  830. //
  831. ResetInfo->InternalResetState = InternalMouseReset;
  832. Print(DBG_SS_ERROR | DBG_SS_INFO,
  833. ("RESET command never responded, retrying\n"));
  834. }
  835. break;
  836. case ACKNOWLEDGE:
  837. if (tickDelta.QuadPart > oneSecond.QuadPart) {
  838. //
  839. // Assume that the 0xAA was eaten, just setup the state
  840. // machine to go to the next state after reset
  841. //
  842. I8X_WRITE_CMD_TO_MOUSE();
  843. I8X_MOUSE_COMMAND( GET_DEVICE_ID );
  844. mouseExtension->InputResetSubState = ExpectingGetDeviceIdACK;
  845. mouseExtension->LastByteReceived = 0x00;
  846. Print(DBG_SS_ERROR | DBG_SS_INFO,
  847. ("jump starting state machine\n"));
  848. }
  849. break;
  850. case RESEND:
  851. if (mouseExtension->ResendCount >= MOUSE_RESET_RESENDS_MAX) {
  852. //
  853. // Stop the ISR state machine from running and make sure
  854. // the timer is requeued
  855. //
  856. ResetInfo->InternalResetState = InternalPauseOneSec;
  857. mouseExtension->ResetMouse.IsrResetState =
  858. IsrResetStopResetting;
  859. }
  860. else if (tickDelta.QuadPart > oneSecond.QuadPart) {
  861. //
  862. // Some machines request a resend (which is honored),
  863. // but then don't respond again. Since we can't wait
  864. // +0.5 secs in the ISR, we take care of this case here
  865. //
  866. ResetInfo->InternalResetState = InternalMouseReset;
  867. Print(DBG_SS_ERROR | DBG_SS_INFO,
  868. ("resending RESET command\n"));
  869. }
  870. default:
  871. Print(DBG_SS_ERROR, ("unclassified response in ExpectingReset\n"));
  872. goto CheckForThreeSecondSilence;
  873. }
  874. break;
  875. //
  876. // These states is the state machine waiting for a sequence of bytes. In
  877. // each case, if we don't get what we want in the time allotted, goto the
  878. // next state
  879. //
  880. case ExpectingReadMouseStatusByte1:
  881. case ExpectingReadMouseStatusByte2:
  882. case ExpectingReadMouseStatusByte3:
  883. if (tickDelta.QuadPart > oneSecond.QuadPart) {
  884. I8X_WRITE_CMD_TO_MOUSE();
  885. I8X_MOUSE_COMMAND( POST_BUTTONDETECT_COMMAND );
  886. mouseExtension->InputResetSubState =
  887. POST_BUTTONDETECT_COMMAND_SUBSTATE;
  888. }
  889. break;
  890. case ExpectingPnpIdByte1:
  891. case ExpectingPnpIdByte2:
  892. case ExpectingPnpIdByte3:
  893. case ExpectingPnpIdByte4:
  894. case ExpectingPnpIdByte5:
  895. case ExpectingPnpIdByte6:
  896. case ExpectingPnpIdByte7:
  897. case ExpectingLegacyPnpIdByte2_Make:
  898. case ExpectingLegacyPnpIdByte2_Break:
  899. case ExpectingLegacyPnpIdByte3_Make:
  900. case ExpectingLegacyPnpIdByte3_Break:
  901. case ExpectingLegacyPnpIdByte4_Make:
  902. case ExpectingLegacyPnpIdByte4_Break:
  903. case ExpectingLegacyPnpIdByte5_Make:
  904. case ExpectingLegacyPnpIdByte5_Break:
  905. case ExpectingLegacyPnpIdByte6_Make:
  906. case ExpectingLegacyPnpIdByte6_Break:
  907. case ExpectingLegacyPnpIdByte7_Make:
  908. case ExpectingLegacyPnpIdByte7_Break:
  909. if (tickDelta.LowPart >= mouseExtension->WheelDetectionTimeout ||
  910. tickDelta.HighPart != 0) {
  911. //
  912. // Trying to acquire the mouse wheel ID failed, just skip it!
  913. //
  914. mouseExtension->EnableWheelDetection = 0;
  915. I8X_WRITE_CMD_TO_MOUSE();
  916. I8X_MOUSE_COMMAND( POST_WHEEL_DETECT_COMMAND );
  917. //
  918. // Best possible next state
  919. //
  920. mouseExtension->InputResetSubState =
  921. POST_WHEEL_DETECT_COMMAND_SUBSTATE;
  922. }
  923. break;
  924. case QueueingMouseReset:
  925. case QueueingMousePolledReset:
  926. //
  927. // A (polled) reset is somewhere in the works, don't collide with it
  928. //
  929. return FALSE;
  930. default:
  931. CheckForThreeSecondSilence:
  932. threeSeconds = RtlConvertLongToLargeInteger(1000 * 1000 * 30);
  933. threeSeconds.QuadPart /= KeQueryTimeIncrement();
  934. if (tickDelta.QuadPart > threeSeconds.QuadPart) {
  935. Print(DBG_SS_ERROR, ("No response from mouse in ~3 seconds\n"));
  936. ResetInfo->InternalResetState = InternalMouseReset;
  937. }
  938. break;
  939. }
  940. return TRUE;
  941. }
  942. VOID
  943. I8xMouseInitializeInterruptWorker(
  944. IN PDEVICE_OBJECT DeviceObject,
  945. IN PIO_WORKITEM Item
  946. )
  947. {
  948. PPORT_MOUSE_EXTENSION extension;
  949. PAGED_CODE();
  950. extension = (PPORT_MOUSE_EXTENSION) DeviceObject->DeviceExtension;
  951. if (extension->WorkerResetSubState != KeepOldSubState) {
  952. extension->ResetMouse.IsrResetState = extension->WorkerResetSubState;
  953. }
  954. I8xResetMouse(extension);
  955. IoFreeWorkItem(Item);
  956. }
  957. VOID
  958. I8xMouseInitializePolledWorker(
  959. IN PDEVICE_OBJECT DeviceObject,
  960. IN PIO_WORKITEM Item
  961. )
  962. /*++
  963. Routine Description:
  964. Queued work item to reset the mouse is a polled manner. Turns off the
  965. interrupts and attempts to synchronously reset and initialize the mouse then
  966. turn the interrupts back on.
  967. Arguments:
  968. Item - work item containing the mouse extension
  969. Return Value:
  970. None.
  971. --*/
  972. {
  973. NTSTATUS status;
  974. PIRP irp;
  975. PPORT_MOUSE_EXTENSION mouseExtension;
  976. DEVICE_POWER_STATE keyboardDeviceState;
  977. KIRQL oldIrql;
  978. Print(DBG_SS_ERROR | DBG_SS_INFO, ("forcing polled init!!!\n"));
  979. //
  980. // Force the keyboard to ignore interrupts
  981. //
  982. if (KEYBOARD_PRESENT() && Globals.KeyboardExtension) {
  983. keyboardDeviceState = Globals.KeyboardExtension->PowerState;
  984. Globals.KeyboardExtension->PowerState = PowerDeviceD3;
  985. }
  986. I8xToggleInterrupts(FALSE);
  987. mouseExtension = (PPORT_MOUSE_EXTENSION) DeviceObject->DeviceExtension;
  988. status = I8xInitializeMouse(mouseExtension);
  989. //
  990. // Turn the interrupts on no matter what the results, hopefully the kb is still
  991. // there and functional if the mouse is dead
  992. //
  993. I8xToggleInterrupts(TRUE);
  994. //
  995. // Undo the force from above
  996. //
  997. if (KEYBOARD_PRESENT() && Globals.KeyboardExtension) {
  998. Globals.KeyboardExtension->PowerState = keyboardDeviceState;
  999. }
  1000. if (NT_SUCCESS(status) && MOUSE_PRESENT()) {
  1001. status = I8xMouseEnableTransmission(mouseExtension);
  1002. if (!NT_SUCCESS(status)) {
  1003. goto init_failure;
  1004. }
  1005. Print(DBG_SS_ERROR | DBG_SS_INFO, ("polled init succeeded\n"));
  1006. I8xFinishResetRequest(mouseExtension,
  1007. FALSE, // success
  1008. TRUE, // raise to DISPATCH
  1009. FALSE); // no timer to cancel
  1010. }
  1011. else {
  1012. init_failure:
  1013. Print(DBG_SS_ERROR | DBG_SS_INFO,
  1014. ("polled init failed (0x%x)\n", status));
  1015. I8xResetMouseFailed(mouseExtension);
  1016. }
  1017. IoFreeWorkItem(Item);
  1018. }
  1019. //
  1020. // End infrastructure for initializing the mouse via the interrupt
  1021. //
  1022. BOOLEAN
  1023. I8xVerifyMousePnPID(
  1024. PPORT_MOUSE_EXTENSION MouseExtension,
  1025. PWSTR MouseID
  1026. )
  1027. /*++
  1028. Routine Description:
  1029. Verifies that the MouseID reported by the mouse is valid
  1030. Arguments:
  1031. MouseExtension - Mouse extension
  1032. MouseID - ID reported by the mouse
  1033. Return Value:
  1034. None.
  1035. --*/
  1036. {
  1037. PWSTR currentString = NULL;
  1038. ULONG length;
  1039. WCHAR szDefaultIDs[] = {
  1040. L"MSH0002\0" // original wheel
  1041. L"MSH0005\0" // trackball
  1042. L"MSH001F\0" // shiny gray optioal 5 btn mouse
  1043. L"MSH0020\0" // intellimouse with intellieye
  1044. L"MSH002A\0" // 2 tone optical 5 btn mouse (intellimouse web)
  1045. L"MSH0030\0" // trackball optical
  1046. L"MSH0031\0" // trackball explorer
  1047. L"MSH003A\0" // intellimouse optical
  1048. L"MSH0041\0" // wheel mouse optical
  1049. L"MSH0043\0" // 3 button wheel
  1050. L"MSH0044\0" // intellimouse optical 3.0
  1051. L"\0" };
  1052. currentString = MouseExtension->WheelDetectionIDs.Buffer;
  1053. //
  1054. // If the mouse got far enough to report an ID and we don't have one in
  1055. // memory, assume it is a wheel mouse id
  1056. //
  1057. if (currentString != NULL) {
  1058. while (*currentString != L'\0') {
  1059. if (wcscmp(currentString, MouseID) == 0) {
  1060. return TRUE;
  1061. }
  1062. //
  1063. // Increment to the next string (length of current string plus NULL)
  1064. //
  1065. currentString += wcslen(currentString) + 1;
  1066. }
  1067. }
  1068. currentString = szDefaultIDs;
  1069. if (currentString != NULL) {
  1070. while (*currentString != L'\0') {
  1071. if (wcscmp(currentString, MouseID) == 0) {
  1072. return TRUE;
  1073. }
  1074. //
  1075. // Increment to the next string (length of current string plus NULL)
  1076. //
  1077. currentString += wcslen(currentString) + 1;
  1078. }
  1079. }
  1080. return FALSE;
  1081. }