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.

431 lines
13 KiB

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