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.

414 lines
13 KiB

  1. /*++
  2. Copyright (c) 1990-1998 Microsoft Corporation, All Rights Reserved
  3. Module Name:
  4. moucmn.c
  5. Abstract:
  6. The common portions of the Intel i8042 port driver which
  7. apply to the auxiliary (PS/2 mouse) device.
  8. Environment:
  9. Kernel mode only.
  10. Notes:
  11. NOTES: (Future/outstanding issues)
  12. - Powerfail not implemented.
  13. - Consolidate duplicate code, where possible and appropriate.
  14. Revision History:
  15. --*/
  16. #include "stdarg.h"
  17. #include "stdio.h"
  18. #include "string.h"
  19. #include "i8042prt.h"
  20. #ifdef ALLOC_PRAGMA
  21. #if 1
  22. #pragma alloc_text(PAGEMOUC, I8042MouseIsrDpc)
  23. #pragma alloc_text(PAGEMOUC, I8xWriteDataToMouseQueue)
  24. #endif
  25. #endif
  26. VOID
  27. I8042MouseIsrDpc(
  28. IN PKDPC Dpc,
  29. IN PDEVICE_OBJECT DeviceObject,
  30. IN PIRP Irp,
  31. IN PVOID Context
  32. )
  33. /*++
  34. Routine Description:
  35. This routine runs at DISPATCH_LEVEL IRQL to finish processing
  36. mouse interrupts. It is queued in the mouse ISR. The real
  37. work is done via a callback to the connected mouse class driver.
  38. Arguments:
  39. Dpc - Pointer to the DPC object.
  40. DeviceObject - Pointer to the device object.
  41. Irp - Pointer to the Irp.
  42. Context - Not used.
  43. Return Value:
  44. None.
  45. --*/
  46. {
  47. PPORT_MOUSE_EXTENSION deviceExtension;
  48. GET_DATA_POINTER_CONTEXT getPointerContext;
  49. SET_DATA_POINTER_CONTEXT setPointerContext;
  50. VARIABLE_OPERATION_CONTEXT operationContext;
  51. PVOID classService;
  52. PVOID classDeviceObject;
  53. LONG interlockedResult;
  54. BOOLEAN moreDpcProcessing;
  55. ULONG dataNotConsumed = 0;
  56. ULONG inputDataConsumed = 0;
  57. LARGE_INTEGER deltaTime;
  58. UNREFERENCED_PARAMETER(Dpc);
  59. UNREFERENCED_PARAMETER(Irp);
  60. UNREFERENCED_PARAMETER(Context);
  61. Print(DBG_DPC_TRACE, ("I8042MouseIsrDpc: enter\n"));
  62. deviceExtension = (PPORT_MOUSE_EXTENSION) DeviceObject->DeviceExtension;
  63. //
  64. // Use DpcInterlockMouse to determine whether the DPC is running
  65. // concurrently on another processor. We only want one instantiation
  66. // of the DPC to actually do any work. DpcInterlockMouse is -1
  67. // when no DPC is executing. We increment it, and if the result is
  68. // zero then the current instantiation is the only one executing, and it
  69. // is okay to proceed. Otherwise, we just return.
  70. //
  71. //
  72. operationContext.VariableAddress =
  73. &deviceExtension->DpcInterlockMouse;
  74. operationContext.Operation = IncrementOperation;
  75. operationContext.NewValue = &interlockedResult;
  76. KeSynchronizeExecution(
  77. deviceExtension->InterruptObject,
  78. (PKSYNCHRONIZE_ROUTINE) I8xDpcVariableOperation,
  79. (PVOID) &operationContext
  80. );
  81. moreDpcProcessing = (interlockedResult == 0)? TRUE:FALSE;
  82. while (moreDpcProcessing) {
  83. dataNotConsumed = 0;
  84. inputDataConsumed = 0;
  85. //
  86. // Get the port InputData queue pointers synchronously.
  87. //
  88. getPointerContext.DeviceExtension = deviceExtension;
  89. setPointerContext.DeviceExtension = deviceExtension;
  90. getPointerContext.DeviceType = (CCHAR) MouseDeviceType;
  91. setPointerContext.DeviceType = (CCHAR) MouseDeviceType;
  92. setPointerContext.InputCount = 0;
  93. KeSynchronizeExecution(
  94. deviceExtension->InterruptObject,
  95. (PKSYNCHRONIZE_ROUTINE) I8xGetDataQueuePointer,
  96. (PVOID) &getPointerContext
  97. );
  98. if (getPointerContext.InputCount != 0) {
  99. //
  100. // Call the connected class driver's callback ISR with the
  101. // port InputData queue pointers. If we have to wrap the queue,
  102. // break the operation into two pieces, and call the class callback
  103. // ISR once for each piece.
  104. //
  105. classDeviceObject =
  106. deviceExtension->ConnectData.ClassDeviceObject;
  107. classService =
  108. deviceExtension->ConnectData.ClassService;
  109. ASSERT(classService != NULL);
  110. if (getPointerContext.DataOut >= getPointerContext.DataIn) {
  111. //
  112. // We'll have to wrap the InputData circular buffer. Call
  113. // the class callback ISR with the chunk of data starting at
  114. // DataOut and ending at the end of the queue.
  115. //
  116. Print(DBG_DPC_NOISE,
  117. ("I8042MouseIsrDpc: calling class callback\n"
  118. ));
  119. Print(DBG_DPC_INFO,
  120. ("I8042MouseIsrDpc: with Start 0x%x and End 0x%x\n",
  121. getPointerContext.DataOut,
  122. deviceExtension->DataEnd
  123. ));
  124. (*(PSERVICE_CALLBACK_ROUTINE) classService)(
  125. classDeviceObject,
  126. getPointerContext.DataOut,
  127. deviceExtension->DataEnd,
  128. &inputDataConsumed
  129. );
  130. dataNotConsumed = ((ULONG)((PUCHAR)
  131. deviceExtension->DataEnd -
  132. (PUCHAR) getPointerContext.DataOut)
  133. / sizeof(MOUSE_INPUT_DATA)) - inputDataConsumed;
  134. Print(DBG_DPC_INFO,
  135. ("I8042MouseIsrDpc: (Wrap) Call callback consumed %d items, left %d\n",
  136. inputDataConsumed,
  137. dataNotConsumed
  138. ));
  139. setPointerContext.InputCount += inputDataConsumed;
  140. if (dataNotConsumed) {
  141. setPointerContext.DataOut =
  142. ((PUCHAR)getPointerContext.DataOut) +
  143. (inputDataConsumed * sizeof(MOUSE_INPUT_DATA));
  144. } else {
  145. setPointerContext.DataOut =
  146. deviceExtension->InputData;
  147. getPointerContext.DataOut = setPointerContext.DataOut;
  148. }
  149. }
  150. //
  151. // Call the class callback ISR with data remaining in the queue.
  152. //
  153. if ((dataNotConsumed == 0) &&
  154. (inputDataConsumed < getPointerContext.InputCount)){
  155. Print(DBG_DPC_NOISE,
  156. ("I8042MouseIsrDpc: calling class callback\n"
  157. ));
  158. Print(DBG_DPC_INFO,
  159. ("I8042MouseIsrDpc: with Start 0x%x and End 0x%x\n",
  160. getPointerContext.DataOut,
  161. getPointerContext.DataIn
  162. ));
  163. (*(PSERVICE_CALLBACK_ROUTINE) classService)(
  164. classDeviceObject,
  165. getPointerContext.DataOut,
  166. getPointerContext.DataIn,
  167. &inputDataConsumed
  168. );
  169. dataNotConsumed = ((ULONG)((PUCHAR) getPointerContext.DataIn -
  170. (PUCHAR) getPointerContext.DataOut)
  171. / sizeof(MOUSE_INPUT_DATA)) - inputDataConsumed;
  172. Print(DBG_DPC_INFO,
  173. ("I8042MouseIsrDpc: Call callback consumed %d items, left %d\n",
  174. inputDataConsumed,
  175. dataNotConsumed
  176. ));
  177. setPointerContext.DataOut =
  178. ((PUCHAR)getPointerContext.DataOut) +
  179. (inputDataConsumed * sizeof(MOUSE_INPUT_DATA));
  180. setPointerContext.InputCount += inputDataConsumed;
  181. }
  182. //
  183. // Update the port InputData queue DataOut pointer and InputCount
  184. // synchronously.
  185. //
  186. KeSynchronizeExecution(
  187. deviceExtension->InterruptObject,
  188. (PKSYNCHRONIZE_ROUTINE) I8xSetDataQueuePointer,
  189. (PVOID) &setPointerContext
  190. );
  191. }
  192. if (dataNotConsumed) {
  193. //
  194. // The class driver was unable to consume all the data.
  195. // Reset the interlocked variable to -1. We do not want
  196. // to attempt to move more data to the class driver at this
  197. // point, because it is already overloaded. Need to wait a
  198. // while to give the Raw Input Thread a chance to read some
  199. // of the data out of the class driver's queue. We accomplish
  200. // this "wait" via a timer.
  201. //
  202. Print(DBG_DPC_INFO,
  203. ("I8042MouseIsrDpc: set timer in DPC\n"
  204. ));
  205. operationContext.Operation = WriteOperation;
  206. interlockedResult = -1;
  207. operationContext.NewValue = &interlockedResult;
  208. KeSynchronizeExecution(
  209. deviceExtension->InterruptObject,
  210. (PKSYNCHRONIZE_ROUTINE) I8xDpcVariableOperation,
  211. (PVOID) &operationContext
  212. );
  213. deltaTime.LowPart = (ULONG)(-10 * 1000 * 1000);
  214. deltaTime.HighPart = -1;
  215. (VOID) KeSetTimer(
  216. &deviceExtension->DataConsumptionTimer,
  217. deltaTime,
  218. &deviceExtension->MouseIsrDpcRetry
  219. );
  220. moreDpcProcessing = FALSE;
  221. } else {
  222. //
  223. // Decrement DpcInterlockMouse. If the result goes negative,
  224. // then we're all finished processing the DPC. Otherwise, either
  225. // the ISR incremented DpcInterlockMouse because it has more
  226. // work for the ISR DPC to do, or a concurrent DPC executed on
  227. // some processor while the current DPC was running (the
  228. // concurrent DPC wouldn't have done any work). Make sure that
  229. // the current DPC handles any extra work that is ready to be
  230. // done.
  231. //
  232. operationContext.Operation = DecrementOperation;
  233. operationContext.NewValue = &interlockedResult;
  234. KeSynchronizeExecution(
  235. deviceExtension->InterruptObject,
  236. (PKSYNCHRONIZE_ROUTINE) I8xDpcVariableOperation,
  237. (PVOID) &operationContext
  238. );
  239. if (interlockedResult != -1) {
  240. //
  241. // The interlocked variable is still greater than or equal to
  242. // zero. Reset it to zero, so that we execute the loop one
  243. // more time (assuming no more DPCs execute and bump the
  244. // variable up again).
  245. //
  246. operationContext.Operation = WriteOperation;
  247. interlockedResult = 0;
  248. operationContext.NewValue = &interlockedResult;
  249. KeSynchronizeExecution(
  250. deviceExtension->InterruptObject,
  251. (PKSYNCHRONIZE_ROUTINE) I8xDpcVariableOperation,
  252. (PVOID) &operationContext
  253. );
  254. Print(DBG_DPC_INFO, ("I8042MouseIsrDpc: loop in DPC\n"));
  255. }
  256. else {
  257. moreDpcProcessing = FALSE;
  258. }
  259. }
  260. }
  261. Print(DBG_DPC_TRACE, ("I8042MouseIsrDpc: exit\n"));
  262. }
  263. BOOLEAN
  264. I8xWriteDataToMouseQueue(
  265. PPORT_MOUSE_EXTENSION MouseExtension,
  266. IN PMOUSE_INPUT_DATA InputData
  267. )
  268. /*++
  269. Routine Description:
  270. This routine adds input data from the mouse to the InputData queue.
  271. Arguments:
  272. MouseExtension - Pointer to the mouse portion of the device extension.
  273. InputData - Pointer to the data to add to the InputData queue.
  274. Return Value:
  275. Returns TRUE if the data was added, otherwise FALSE.
  276. --*/
  277. {
  278. Print(DBG_CALL_TRACE, ("I8xWriteDataToMouseQueue: enter\n"));
  279. Print(DBG_CALL_NOISE,
  280. ("I8xWriteDataToMouseQueue: DataIn 0x%x, DataOut 0x%x\n",
  281. MouseExtension->DataIn,
  282. MouseExtension->DataOut
  283. ));
  284. Print(DBG_CALL_NOISE,
  285. ("I8xWriteDataToMouseQueue: InputCount %d\n",
  286. MouseExtension->InputCount
  287. ));
  288. //
  289. // Check for full input data queue.
  290. //
  291. if ((MouseExtension->DataIn == MouseExtension->DataOut) &&
  292. (MouseExtension->InputCount != 0)) {
  293. //
  294. // The input data queue is full. Intentionally ignore
  295. // the new data.
  296. //
  297. Print(DBG_CALL_ERROR, ("I8xWriteDataToMouseQueue: OVERFLOW\n"));
  298. return(FALSE);
  299. } else {
  300. *(MouseExtension->DataIn) = *InputData;
  301. MouseExtension->InputCount += 1;
  302. MouseExtension->DataIn++;
  303. Print(DBG_DPC_INFO,
  304. ("I8xWriteDataToMouseQueue: new InputCount %d\n",
  305. MouseExtension->InputCount
  306. ));
  307. if (MouseExtension->DataIn == MouseExtension->DataEnd) {
  308. Print(DBG_DPC_NOISE, ("I8xWriteDataToMouseQueue: wrap buffer\n"));
  309. MouseExtension->DataIn = MouseExtension->InputData;
  310. }
  311. }
  312. Print(DBG_DPC_TRACE, ("I8xWriteDataToMouseQueue: exit\n"));
  313. return(TRUE);
  314. }