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.

1502 lines
38 KiB

  1. /*++
  2. Copyright (c) 1990, 1991, 1992, 1993 Microsoft Corporation
  3. Copyright (c) 1993 Logitech Inc.
  4. Module Name:
  5. sermcmn.c
  6. Abstract:
  7. The common portions of the Microsoft serial (i8250) mouse port driver.
  8. This file should not require modification to support new mice
  9. that are similar to the serial mouse.
  10. Environment:
  11. Kernel mode only.
  12. Notes:
  13. NOTES: (Future/outstanding issues)
  14. - Powerfail not implemented.
  15. - IOCTL_INTERNAL_MOUSE_DISCONNECT has not been implemented. It's not
  16. needed until the class unload routine is implemented. Right now,
  17. we don't want to allow the mouse class driver to unload.
  18. - Consolidate duplicate code, where possible and appropriate.
  19. Revision History:
  20. --*/
  21. #include "stdarg.h"
  22. #include "stdio.h"
  23. #include "string.h"
  24. #include "ntddk.h"
  25. #include "sermouse.h"
  26. #include "sermlog.h"
  27. #include "debug.h"
  28. VOID
  29. SerialMouseErrorLogDpc(
  30. IN PKDPC Dpc,
  31. IN PDEVICE_OBJECT DeviceObject,
  32. IN PIRP Irp,
  33. IN PVOID Context
  34. )
  35. /*++
  36. Routine Description:
  37. This routine runs at DISPATCH_LEVEL IRQL to log errors that are
  38. discovered at IRQL > DISPATCH_LEVEL (e.g., in the ISR routine or
  39. in a routine that is executed via KeSynchronizeExecution). There
  40. is not necessarily a current request associated with this condition.
  41. Arguments:
  42. Dpc - Pointer to the DPC object.
  43. DeviceObject - Pointer to the device object.
  44. Irp - Not used.
  45. Context - Indicates type of error to log.
  46. Return Value:
  47. None.
  48. --*/
  49. {
  50. PDEVICE_EXTENSION deviceExtension;
  51. PIO_ERROR_LOG_PACKET errorLogEntry;
  52. UNREFERENCED_PARAMETER(Dpc);
  53. UNREFERENCED_PARAMETER(Irp);
  54. SerMouPrint((2, "SERMOUSE-SerialMouseErrorLogDpc: enter\n"));
  55. deviceExtension = DeviceObject->DeviceExtension;
  56. //
  57. // Log an error packet.
  58. //
  59. errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry(
  60. DeviceObject,
  61. sizeof(IO_ERROR_LOG_PACKET)
  62. + (2 * sizeof(ULONG))
  63. );
  64. if (errorLogEntry != NULL) {
  65. errorLogEntry->DumpDataSize = 2 * sizeof(ULONG);
  66. if ((ULONG) Context == SERMOUSE_MOU_BUFFER_OVERFLOW) {
  67. errorLogEntry->UniqueErrorValue = SERIAL_MOUSE_ERROR_VALUE_BASE + 210;
  68. errorLogEntry->DumpData[0] = sizeof(MOUSE_INPUT_DATA);
  69. errorLogEntry->DumpData[1] =
  70. deviceExtension->Configuration.MouseAttributes.InputDataQueueLength;
  71. } else {
  72. errorLogEntry->UniqueErrorValue = SERIAL_MOUSE_ERROR_VALUE_BASE + 220;
  73. errorLogEntry->DumpData[0] = 0;
  74. errorLogEntry->DumpData[1] = 0;
  75. }
  76. errorLogEntry->ErrorCode = (ULONG) Context;
  77. errorLogEntry->SequenceNumber = 0;
  78. errorLogEntry->MajorFunctionCode = 0;
  79. errorLogEntry->IoControlCode = 0;
  80. errorLogEntry->RetryCount = 0;
  81. errorLogEntry->FinalStatus = 0;
  82. IoWriteErrorLogEntry(errorLogEntry);
  83. }
  84. SerMouPrint((2, "SERMOUSE-SerialMouseErrorLogDpc: exit\n"));
  85. }
  86. NTSTATUS
  87. SerialMouseFlush(
  88. IN PDEVICE_OBJECT DeviceObject,
  89. IN PIRP Irp
  90. )
  91. {
  92. UNREFERENCED_PARAMETER(DeviceObject);
  93. UNREFERENCED_PARAMETER(Irp);
  94. SerMouPrint((2,"SERMOUSE-SerialMouseFlush: enter\n"));
  95. SerMouPrint((2,"SERMOUSE-SerialMouseFlush: exit\n"));
  96. return(STATUS_NOT_IMPLEMENTED);
  97. }
  98. NTSTATUS
  99. SerialMouseInternalDeviceControl(
  100. IN PDEVICE_OBJECT DeviceObject,
  101. IN PIRP Irp
  102. )
  103. /*++
  104. Routine Description:
  105. This routine is the dispatch routine for internal device control requests.
  106. Arguments:
  107. DeviceObject - Pointer to the device object.
  108. Irp - Pointer to the request packet.
  109. Return Value:
  110. Status is returned.
  111. --*/
  112. {
  113. PIO_STACK_LOCATION irpSp;
  114. PDEVICE_EXTENSION deviceExtension;
  115. NTSTATUS status;
  116. SerMouPrint((2,"SERMOUSE-SerialMouseInternalDeviceControl: enter\n"));
  117. //
  118. // Get a pointer to the device extension.
  119. //
  120. deviceExtension = DeviceObject->DeviceExtension;
  121. //
  122. // Initialize the returned Information field.
  123. //
  124. Irp->IoStatus.Information = 0;
  125. //
  126. // Get a pointer to the current parameters for this request. The
  127. // information is contained in the current stack location.
  128. //
  129. irpSp = IoGetCurrentIrpStackLocation(Irp);
  130. //
  131. // Case on the device control subfunction that is being performed by the
  132. // requestor.
  133. //
  134. switch (irpSp->Parameters.DeviceIoControl.IoControlCode) {
  135. //
  136. // Connect a mouse class device driver to the port driver.
  137. //
  138. case IOCTL_INTERNAL_MOUSE_CONNECT:
  139. SerMouPrint((
  140. 2,
  141. "SERMOUSE-SerialMouseInternalDeviceControl: mouse connect\n"
  142. ));
  143. //
  144. // Only allow one connection.
  145. //
  146. // FUTURE: Consider allowing multiple connections, just for
  147. // the sake of generality?
  148. //
  149. if (deviceExtension->ConnectData.ClassService
  150. != NULL) {
  151. SerMouPrint((
  152. 2,
  153. "SERMOUSE-SerialMouseInternalDeviceControl: error - already connected\n"
  154. ));
  155. status = STATUS_SHARING_VIOLATION;
  156. break;
  157. } else
  158. if (irpSp->Parameters.DeviceIoControl.InputBufferLength <
  159. sizeof(CONNECT_DATA)) {
  160. SerMouPrint((
  161. 2,
  162. "SERMOUSE-SerialMouseInternalDeviceControl: error - invalid buffer length\n"
  163. ));
  164. status = STATUS_INVALID_PARAMETER;
  165. break;
  166. }
  167. //
  168. // Copy the connection parameters to the device extension.
  169. //
  170. deviceExtension->ConnectData =
  171. *((PCONNECT_DATA) (irpSp->Parameters.DeviceIoControl.Type3InputBuffer));
  172. //
  173. // Reinitialize the port input data queue synchronously.
  174. //
  175. KeSynchronizeExecution(
  176. deviceExtension->InterruptObject,
  177. (PKSYNCHRONIZE_ROUTINE) SerMouInitializeDataQueue,
  178. (PVOID) deviceExtension
  179. );
  180. //
  181. // Set the completion status.
  182. //
  183. status = STATUS_SUCCESS;
  184. break;
  185. //
  186. // Disconnect a mouse class device driver from the port driver.
  187. //
  188. // NOTE: Not implemented.
  189. //
  190. case IOCTL_INTERNAL_MOUSE_DISCONNECT:
  191. SerMouPrint((
  192. 2,
  193. "SERMOUSE-SerialMouseInternalDeviceControl: mouse disconnect\n"
  194. ));
  195. //
  196. // Perform a mouse interrupt disable call.
  197. //
  198. //
  199. // Clear the connection parameters in the device extension.
  200. // NOTE: Must synchronize this with the mouse ISR.
  201. //
  202. //
  203. //deviceExtension->ConnectData.ClassDeviceObject =
  204. // Null;
  205. //deviceExtension->ConnectData.ClassService =
  206. // Null;
  207. //
  208. // Set the completion status.
  209. //
  210. status = STATUS_NOT_IMPLEMENTED;
  211. break;
  212. //
  213. // Enable mouse interrupts (mark the request pending and handle
  214. // it in StartIo).
  215. //
  216. case IOCTL_INTERNAL_MOUSE_ENABLE:
  217. SerMouPrint((
  218. 2,
  219. "SERMOUSE-SerialMouseInternalDeviceControl: mouse enable\n"
  220. ));
  221. status = STATUS_PENDING;
  222. break;
  223. //
  224. // Disable mouse interrupts (mark the request pending and handle
  225. // it in StartIo).
  226. //
  227. case IOCTL_INTERNAL_MOUSE_DISABLE:
  228. SerMouPrint((
  229. 2,
  230. "SERMOUSE-SerialMouseInternalDeviceControl: mouse disable\n"
  231. ));
  232. status = STATUS_PENDING;
  233. break;
  234. //
  235. // Query the mouse attributes. First check for adequate buffer
  236. // length. Then, copy the mouse attributes from the device
  237. // extension to the output buffer.
  238. //
  239. case IOCTL_MOUSE_QUERY_ATTRIBUTES:
  240. SerMouPrint((
  241. 2,
  242. "SERMOUSE-SerialMouseInternalDeviceControl: mouse query attributes\n"
  243. ));
  244. if (irpSp->Parameters.DeviceIoControl.OutputBufferLength <
  245. sizeof(MOUSE_ATTRIBUTES)) {
  246. status = STATUS_BUFFER_TOO_SMALL;
  247. } else {
  248. //
  249. // Copy the attributes from the DeviceExtension to the
  250. // buffer.
  251. //
  252. *(PMOUSE_ATTRIBUTES) Irp->AssociatedIrp.SystemBuffer =
  253. deviceExtension->Configuration.MouseAttributes;
  254. Irp->IoStatus.Information = sizeof(MOUSE_ATTRIBUTES);
  255. status = STATUS_SUCCESS;
  256. }
  257. break;
  258. default:
  259. SerMouPrint((
  260. 2,
  261. "SERMOUSE-SerialMouseInternalDeviceControl: INVALID REQUEST\n"
  262. ));
  263. status = STATUS_INVALID_DEVICE_REQUEST;
  264. break;
  265. }
  266. Irp->IoStatus.Status = status;
  267. if (status == STATUS_PENDING) {
  268. IoMarkIrpPending(Irp);
  269. IoStartPacket(DeviceObject, Irp, (PULONG)NULL, NULL);
  270. } else {
  271. IoCompleteRequest(Irp, IO_NO_INCREMENT);
  272. }
  273. SerMouPrint((2,"SERMOUSE-SerialMouseInternalDeviceControl: exit\n"));
  274. return(status);
  275. }
  276. BOOLEAN
  277. SerialMouseInterruptService(
  278. IN PKINTERRUPT Interrupt,
  279. IN PVOID Context
  280. )
  281. /*++
  282. Routine Description:
  283. This is the interrupt service routine for the mouse device.
  284. Arguments:
  285. Interrupt - A pointer to the interrupt object for this interrupt.
  286. Context - A pointer to the device object.
  287. Return Value:
  288. Returns TRUE if the interrupt was expected (and therefore processed);
  289. otherwise, FALSE is returned.
  290. --*/
  291. {
  292. PDEVICE_EXTENSION deviceExtension;
  293. PDEVICE_OBJECT deviceObject;
  294. PMOUSE_INPUT_DATA currentInput;
  295. PUCHAR port;
  296. UCHAR value;
  297. UCHAR lineState;
  298. ULONG buttonsDelta;
  299. UNREFERENCED_PARAMETER(Interrupt);
  300. SerMouPrint((2, "SERMOUSE-SerialMouseInterruptService: enter\n"));
  301. //
  302. // Get the device extension.
  303. //
  304. deviceObject = (PDEVICE_OBJECT) Context;
  305. deviceExtension = (PDEVICE_EXTENSION) deviceObject->DeviceExtension;
  306. //
  307. // Get the serial mouse port address.
  308. //
  309. port = deviceExtension->Configuration.DeviceRegisters[0];
  310. //
  311. // Verify that the interrupt really belongs to this driver.
  312. //
  313. if ((READ_PORT_UCHAR((PUCHAR) (port + ACE_IIDR)) & ACE_IIP) == ACE_IIP) {
  314. //
  315. // Not our interrupt.
  316. //
  317. SerMouPrint((
  318. 2,
  319. "SERMOUSE-SerialMouseInterruptService: not our interrupt\n"
  320. ));
  321. return(FALSE);
  322. }
  323. //
  324. // Get the line state byte. This value can be checked by the
  325. // protocol handler for errors.
  326. //
  327. lineState = READ_PORT_UCHAR((PUCHAR) (port + ACE_LSR));
  328. SerMouPrint((
  329. 2,
  330. "SERMOUSE-Line status: 0x%x\n", lineState
  331. ));
  332. //
  333. // Read the byte from the serial mouse port. If the mouse has not
  334. // been enabled, don't process the byte further.
  335. //
  336. value = READ_PORT_UCHAR((PUCHAR) port + ACE_RBR);
  337. SerMouPrint((
  338. 2,
  339. "SERMOUSE-SerialMouseInterruptService: byte 0x%x\n", value
  340. ));
  341. if (deviceExtension->MouseEnableCount == 0) {
  342. SerMouPrint((
  343. 2,
  344. "SERMOUSE-SerialMouseInterruptService: not enabled\n"
  345. ));
  346. return(TRUE);
  347. }
  348. //
  349. // At this point, the protocol handler should already be set because
  350. // the hardware is enabled.
  351. //
  352. ASSERT(deviceExtension->ProtocolHandler);
  353. currentInput = &deviceExtension->CurrentInput;
  354. //
  355. // Call the current protocol handler for this device
  356. //
  357. if ((*deviceExtension->ProtocolHandler)(
  358. currentInput,
  359. &deviceExtension->HandlerData,
  360. value,
  361. lineState
  362. )){
  363. //
  364. // The report is complete, compute the button deltas and queue it.
  365. //
  366. currentInput->UnitId = deviceExtension->UnitId;
  367. //
  368. // Do we have a button state change?
  369. //
  370. if (deviceExtension->HandlerData.PreviousButtons ^ currentInput->RawButtons) {
  371. //
  372. // The state of the buttons changed. Make some calculations...
  373. //
  374. buttonsDelta = deviceExtension->HandlerData.PreviousButtons ^
  375. currentInput->RawButtons;
  376. //
  377. // Button 1.
  378. //
  379. if (buttonsDelta & MOUSE_BUTTON_1) {
  380. if (currentInput->RawButtons & MOUSE_BUTTON_1) {
  381. currentInput->ButtonFlags |= MOUSE_BUTTON_1_DOWN;
  382. }
  383. else {
  384. currentInput->ButtonFlags |= MOUSE_BUTTON_1_UP;
  385. }
  386. }
  387. //
  388. // Button 2.
  389. //
  390. if (buttonsDelta & MOUSE_BUTTON_2) {
  391. if (currentInput->RawButtons & MOUSE_BUTTON_2) {
  392. currentInput->ButtonFlags |= MOUSE_BUTTON_2_DOWN;
  393. }
  394. else {
  395. currentInput->ButtonFlags |= MOUSE_BUTTON_2_UP;
  396. }
  397. }
  398. //
  399. // Button 3.
  400. //
  401. if (buttonsDelta & MOUSE_BUTTON_3) {
  402. if (currentInput->RawButtons & MOUSE_BUTTON_3) {
  403. currentInput->ButtonFlags |= MOUSE_BUTTON_3_DOWN;
  404. }
  405. else {
  406. currentInput->ButtonFlags |= MOUSE_BUTTON_3_UP;
  407. }
  408. }
  409. deviceExtension->HandlerData.PreviousButtons =
  410. currentInput->RawButtons;
  411. }
  412. SerMouPrint((1, "SERMOUSE-Buttons: %0lx\n", currentInput->Buttons));
  413. SerMouSendReport(deviceObject);
  414. //
  415. // Clear the button flags for the next packet
  416. //
  417. currentInput->Buttons = 0;
  418. }
  419. SerMouPrint((2, "SERMOUSE-SerialMouseInterruptService: exit\n"));
  420. return TRUE;
  421. }
  422. VOID
  423. SerialMouseIsrDpc(
  424. IN PKDPC Dpc,
  425. IN PDEVICE_OBJECT DeviceObject,
  426. IN PIRP Irp,
  427. IN PVOID Context
  428. )
  429. /*++
  430. Routine Description:
  431. This routine runs at DISPATCH_LEVEL IRQL to finish processing
  432. mouse interrupts. It is queued in the mouse ISR. The real
  433. work is done via a callback to the connected mouse class driver.
  434. Arguments:
  435. Dpc - Pointer to the DPC object.
  436. DeviceObject - Pointer to the device object.
  437. Irp - Pointer to the Irp.
  438. Context - Not used.
  439. Return Value:
  440. None.
  441. --*/
  442. {
  443. PDEVICE_EXTENSION deviceExtension;
  444. GET_DATA_POINTER_CONTEXT getPointerContext;
  445. SET_DATA_POINTER_CONTEXT setPointerContext;
  446. VARIABLE_OPERATION_CONTEXT operationContext;
  447. PVOID classService;
  448. PVOID classDeviceObject;
  449. LONG interlockedResult;
  450. BOOLEAN moreDpcProcessing;
  451. ULONG dataNotConsumed = 0;
  452. ULONG inputDataConsumed = 0;
  453. LARGE_INTEGER deltaTime;
  454. UNREFERENCED_PARAMETER(Dpc);
  455. UNREFERENCED_PARAMETER(Irp);
  456. UNREFERENCED_PARAMETER(Context);
  457. SerMouPrint((2, "SERMOUSE-SerialMouseIsrDpc: enter\n"));
  458. deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
  459. //
  460. // Use DpcInterlockVariable to determine whether the DPC is running
  461. // concurrently on another processor. We only want one instantiation
  462. // of the DPC to actually do any work. DpcInterlockVariable is -1
  463. // when no DPC is executing. We increment it, and if the result is
  464. // zero then the current instantiation is the only one executing, and it
  465. // is okay to proceed. Otherwise, we just return.
  466. //
  467. //
  468. operationContext.VariableAddress =
  469. &deviceExtension->DpcInterlockVariable;
  470. operationContext.Operation = IncrementOperation;
  471. operationContext.NewValue = &interlockedResult;
  472. KeSynchronizeExecution(
  473. deviceExtension->InterruptObject,
  474. (PKSYNCHRONIZE_ROUTINE) SerMouDpcVariableOperation,
  475. (PVOID) &operationContext
  476. );
  477. moreDpcProcessing = (interlockedResult == 0)? TRUE:FALSE;
  478. while (moreDpcProcessing) {
  479. dataNotConsumed = 0;
  480. inputDataConsumed = 0;
  481. //
  482. // Get the port InputData queue pointers synchronously.
  483. //
  484. getPointerContext.DeviceExtension = deviceExtension;
  485. setPointerContext.DeviceExtension = deviceExtension;
  486. setPointerContext.InputCount = 0;
  487. KeSynchronizeExecution(
  488. deviceExtension->InterruptObject,
  489. (PKSYNCHRONIZE_ROUTINE) SerMouGetDataQueuePointer,
  490. (PVOID) &getPointerContext
  491. );
  492. if (getPointerContext.InputCount != 0) {
  493. //
  494. // Call the connected class driver's callback ISR with the
  495. // port InputData queue pointers. If we have to wrap the queue,
  496. // break the operation into two pieces, and call the class callback
  497. // ISR once for each piece.
  498. //
  499. classDeviceObject =
  500. deviceExtension->ConnectData.ClassDeviceObject;
  501. classService =
  502. deviceExtension->ConnectData.ClassService;
  503. ASSERT(classService != NULL);
  504. if (getPointerContext.DataOut >= getPointerContext.DataIn) {
  505. //
  506. // We'll have to wrap the InputData circular buffer. Call
  507. // the class callback ISR with the chunk of data starting at
  508. // DataOut and ending at the end of the queue.
  509. //
  510. SerMouPrint((
  511. 2,
  512. "SERMOUSE-SerialMouseIsrDpc: calling class callback\n"
  513. ));
  514. SerMouPrint((
  515. 2,
  516. "SERMOUSE-SerialMouseIsrDpc: with Start 0x%x and End 0x%x\n",
  517. getPointerContext.DataOut,
  518. deviceExtension->DataEnd
  519. ));
  520. (*(PSERVICE_CALLBACK_ROUTINE) classService)(
  521. classDeviceObject,
  522. getPointerContext.DataOut,
  523. deviceExtension->DataEnd,
  524. &inputDataConsumed
  525. );
  526. dataNotConsumed = ((ULONG)((PUCHAR)
  527. deviceExtension->DataEnd -
  528. (PUCHAR) getPointerContext.DataOut)
  529. / sizeof(MOUSE_INPUT_DATA)) - inputDataConsumed;
  530. SerMouPrint((
  531. 2,
  532. "SERMOUSE-SerialMouseIsrDpc: (Wrap) Call callback consumed %d items, left %d\n",
  533. inputDataConsumed,
  534. dataNotConsumed
  535. ));
  536. setPointerContext.InputCount += inputDataConsumed;
  537. if (dataNotConsumed) {
  538. setPointerContext.DataOut =
  539. ((PUCHAR)getPointerContext.DataOut) +
  540. (inputDataConsumed * sizeof(MOUSE_INPUT_DATA));
  541. } else {
  542. setPointerContext.DataOut =
  543. deviceExtension->InputData;
  544. getPointerContext.DataOut = setPointerContext.DataOut;
  545. }
  546. }
  547. //
  548. // Call the class callback ISR with data remaining in the queue.
  549. //
  550. if ((dataNotConsumed == 0) &&
  551. (inputDataConsumed < getPointerContext.InputCount)){
  552. SerMouPrint((
  553. 2,
  554. "SERMOUSE-SerialMouseIsrDpc: calling class callback\n"
  555. ));
  556. SerMouPrint((
  557. 2,
  558. "SERMOUSE-SerialMouseIsrDpc: with Start 0x%x and End 0x%x\n",
  559. getPointerContext.DataOut,
  560. getPointerContext.DataIn
  561. ));
  562. (*(PSERVICE_CALLBACK_ROUTINE) classService)(
  563. classDeviceObject,
  564. getPointerContext.DataOut,
  565. getPointerContext.DataIn,
  566. &inputDataConsumed
  567. );
  568. dataNotConsumed = ((ULONG)((PUCHAR) getPointerContext.DataIn -
  569. (PUCHAR) getPointerContext.DataOut)
  570. / sizeof(MOUSE_INPUT_DATA)) - inputDataConsumed;
  571. SerMouPrint((
  572. 2,
  573. "SERMOUSE-SerialMouseIsrDpc: Call callback consumed %d items, left %d\n",
  574. inputDataConsumed,
  575. dataNotConsumed
  576. ));
  577. setPointerContext.DataOut =
  578. ((PUCHAR)getPointerContext.DataOut) +
  579. (inputDataConsumed * sizeof(MOUSE_INPUT_DATA));
  580. setPointerContext.InputCount += inputDataConsumed;
  581. }
  582. //
  583. // Update the port InputData queue DataOut pointer and InputCount
  584. // synchronously.
  585. //
  586. KeSynchronizeExecution(
  587. deviceExtension->InterruptObject,
  588. (PKSYNCHRONIZE_ROUTINE) SerMouSetDataQueuePointer,
  589. (PVOID) &setPointerContext
  590. );
  591. }
  592. if (dataNotConsumed) {
  593. //
  594. // The class driver was unable to consume all the data.
  595. // Reset the interlocked variable to -1. We do not want
  596. // to attempt to move more data to the class driver at this
  597. // point, because it is already overloaded. Need to wait a
  598. // while to give the Raw Input Thread a chance to read some
  599. // of the data out of the class driver's queue. We accomplish
  600. // this "wait" via a timer.
  601. //
  602. SerMouPrint((2, "SERMOUSE-SerialMouseIsrDpc: set timer in DPC\n"));
  603. operationContext.Operation = WriteOperation;
  604. interlockedResult = -1;
  605. operationContext.NewValue = &interlockedResult;
  606. KeSynchronizeExecution(
  607. deviceExtension->InterruptObject,
  608. (PKSYNCHRONIZE_ROUTINE) SerMouDpcVariableOperation,
  609. (PVOID) &operationContext
  610. );
  611. deltaTime.LowPart = (ULONG)(-10 * 1000 * 1000);
  612. deltaTime.HighPart = -1;
  613. (VOID) KeSetTimer(
  614. &deviceExtension->DataConsumptionTimer,
  615. deltaTime,
  616. &deviceExtension->IsrDpcRetry
  617. );
  618. moreDpcProcessing = FALSE;
  619. } else {
  620. //
  621. // Decrement DpcInterlockVariable. If the result goes negative,
  622. // then we're all finished processing the DPC. Otherwise, either
  623. // the ISR incremented DpcInterlockVariable because it has more
  624. // work for the ISR DPC to do, or a concurrent DPC executed on
  625. // some processor while the current DPC was running (the
  626. // concurrent DPC wouldn't have done any work). Make sure that
  627. // the current DPC handles any extra work that is ready to be
  628. // done.
  629. //
  630. operationContext.Operation = DecrementOperation;
  631. operationContext.NewValue = &interlockedResult;
  632. KeSynchronizeExecution(
  633. deviceExtension->InterruptObject,
  634. (PKSYNCHRONIZE_ROUTINE) SerMouDpcVariableOperation,
  635. (PVOID) &operationContext
  636. );
  637. if (interlockedResult != -1) {
  638. //
  639. // The interlocked variable is still greater than or equal to
  640. // zero. Reset it to zero, so that we execute the loop one
  641. // more time (assuming no more DPCs execute and bump the
  642. // variable up again).
  643. //
  644. operationContext.Operation = WriteOperation;
  645. interlockedResult = 0;
  646. operationContext.NewValue = &interlockedResult;
  647. KeSynchronizeExecution(
  648. deviceExtension->InterruptObject,
  649. (PKSYNCHRONIZE_ROUTINE) SerMouDpcVariableOperation,
  650. (PVOID) &operationContext
  651. );
  652. SerMouPrint((2, "SERMOUSE-SerialMouseIsrDpc: loop in DPC\n"));
  653. } else {
  654. moreDpcProcessing = FALSE;
  655. }
  656. }
  657. }
  658. SerMouPrint((2, "SERMOUSE-SerialMouseIsrDpc: exit\n"));
  659. }
  660. NTSTATUS
  661. SerialMouseOpenClose(
  662. IN PDEVICE_OBJECT DeviceObject,
  663. IN PIRP Irp
  664. )
  665. /*++
  666. Routine Description:
  667. This is the dispatch routine for create/open and close requests.
  668. These requests complete successfully.
  669. Arguments:
  670. DeviceObject - Pointer to the device object.
  671. Irp - Pointer to the request packet.
  672. Return Value:
  673. Status is returned.
  674. --*/
  675. {
  676. UNREFERENCED_PARAMETER(DeviceObject);
  677. SerMouPrint((3,"SERMOUSE-SerialMouseOpenClose: enter\n"));
  678. //
  679. // Complete the request with successful status.
  680. //
  681. Irp->IoStatus.Status = STATUS_SUCCESS;
  682. Irp->IoStatus.Information = 0;
  683. IoCompleteRequest(Irp, IO_NO_INCREMENT);
  684. SerMouPrint((3,"SERMOUSE-SerialMouseOpenClose: exit\n"));
  685. return(STATUS_SUCCESS);
  686. } // end SerialMouseOpenClose
  687. VOID
  688. SerialMouseStartIo(
  689. IN PDEVICE_OBJECT DeviceObject,
  690. IN PIRP Irp
  691. )
  692. /*++
  693. Routine Description:
  694. This routine starts an I/O operation for the device.
  695. Arguments:
  696. DeviceObject - Pointer to the device object.
  697. Irp - Pointer to the request packet.
  698. Return Value:
  699. None.
  700. --*/
  701. {
  702. PDEVICE_EXTENSION deviceExtension;
  703. PIO_STACK_LOCATION irpSp;
  704. SerMouPrint((2, "SERMOUSE-SerialMouseStartIo: enter\n"));
  705. deviceExtension = DeviceObject->DeviceExtension;
  706. //
  707. // Bump the error log sequence number.
  708. //
  709. deviceExtension->SequenceNumber += 1;
  710. //
  711. // Get a pointer to the current parameters for this request. The
  712. // information is contained in the current stack location.
  713. //
  714. irpSp = IoGetCurrentIrpStackLocation(Irp);
  715. //
  716. // We know we got here with an internal device control request. Switch
  717. // on IoControlCode.
  718. //
  719. switch(irpSp->Parameters.DeviceIoControl.IoControlCode) {
  720. //
  721. // Enable mouse interrupts, by calling SerMouEnableInterrupts
  722. // synchronously.
  723. //
  724. case IOCTL_INTERNAL_MOUSE_ENABLE:
  725. KeSynchronizeExecution(
  726. deviceExtension->InterruptObject,
  727. (PKSYNCHRONIZE_ROUTINE) SerMouEnableInterrupts,
  728. (PVOID) deviceExtension
  729. );
  730. SerMouPrint((
  731. 2,
  732. "SERMOUSE-SerialMouseStartIo: mouse enable (count %d)\n",
  733. deviceExtension->MouseEnableCount
  734. ));
  735. Irp->IoStatus.Status = STATUS_SUCCESS;
  736. //
  737. // Complete the request.
  738. //
  739. IoStartNextPacket(DeviceObject, FALSE);
  740. IoCompleteRequest(Irp, IO_MOUSE_INCREMENT);
  741. break;
  742. //
  743. // Disable mouse interrupts, by calling SerMouDisableInterrupts
  744. // synchronously.
  745. //
  746. case IOCTL_INTERNAL_MOUSE_DISABLE:
  747. SerMouPrint((2, "SERMOUSE-SerialMouseStartIo: mouse disable"));
  748. if (deviceExtension->MouseEnableCount == 0) {
  749. //
  750. // Mouse already disabled.
  751. //
  752. SerMouPrint((2, " - error\n"));
  753. Irp->IoStatus.Status = STATUS_DEVICE_DATA_ERROR;
  754. } else {
  755. //
  756. // Disable mouse by calling SerMouDisableInterrupts.
  757. //
  758. KeSynchronizeExecution(
  759. deviceExtension->InterruptObject,
  760. (PKSYNCHRONIZE_ROUTINE) SerMouDisableInterrupts,
  761. (PVOID) deviceExtension
  762. );
  763. SerMouPrint((
  764. 2,
  765. " (count %d)\n",
  766. deviceExtension->MouseEnableCount
  767. ));
  768. Irp->IoStatus.Status = STATUS_SUCCESS;
  769. }
  770. //
  771. // Complete the request.
  772. //
  773. IoStartNextPacket(DeviceObject, FALSE);
  774. IoCompleteRequest(Irp, IO_MOUSE_INCREMENT);
  775. break;
  776. default:
  777. SerMouPrint((2, "SERMOUSE-SerialMouseStartIo: INVALID REQUEST\n"));
  778. //
  779. // Log an internal error. Note that we're calling the
  780. // error log DPC routine directly, rather than duplicating
  781. // code.
  782. //
  783. SerialMouseErrorLogDpc(
  784. (PKDPC) NULL,
  785. DeviceObject,
  786. Irp,
  787. (PVOID) (ULONG) SERMOUSE_INVALID_STARTIO_REQUEST
  788. );
  789. ASSERT(FALSE);
  790. break;
  791. }
  792. SerMouPrint((2, "SERMOUSE-SerialMouseStartIo: exit\n"));
  793. return;
  794. }
  795. VOID
  796. SerMouDpcVariableOperation(
  797. IN PVOID Context
  798. )
  799. /*++
  800. Routine Description:
  801. This routine is called synchronously by the ISR DPC to perform an
  802. operation on the InterlockedDpcVariable. The operations that can be
  803. performed include increment, decrement, write, and read. The ISR
  804. itself reads and writes the InterlockedDpcVariable without calling this
  805. routine.
  806. Arguments:
  807. Context - Pointer to a structure containing the address of the variable
  808. to be operated on, the operation to perform, and the address at
  809. which to copy the resulting value of the variable (the latter is also
  810. used to pass in the value to write to the variable, on a write
  811. operation).
  812. Return Value:
  813. None.
  814. --*/
  815. {
  816. PVARIABLE_OPERATION_CONTEXT operationContext = Context;
  817. SerMouPrint((3,"SERMOUSE-SerMouDpcVariableOperation: enter\n"));
  818. SerMouPrint((
  819. 3,
  820. "\tPerforming %s at 0x%x (current value 0x%x)\n",
  821. (operationContext->Operation == IncrementOperation)? "increment":
  822. (operationContext->Operation == DecrementOperation)? "decrement":
  823. (operationContext->Operation == WriteOperation)? "write":
  824. (operationContext->Operation == ReadOperation)? "read":"",
  825. operationContext->VariableAddress,
  826. *(operationContext->VariableAddress)
  827. ));
  828. //
  829. // Perform the specified operation at the specified address.
  830. //
  831. switch(operationContext->Operation) {
  832. case IncrementOperation:
  833. *(operationContext->VariableAddress) += 1;
  834. break;
  835. case DecrementOperation:
  836. *(operationContext->VariableAddress) -= 1;
  837. break;
  838. case ReadOperation:
  839. break;
  840. case WriteOperation:
  841. SerMouPrint((
  842. 3,
  843. "\tWriting 0x%x\n",
  844. *(operationContext->NewValue)
  845. ));
  846. *(operationContext->VariableAddress) =
  847. *(operationContext->NewValue);
  848. break;
  849. default:
  850. ASSERT(FALSE);
  851. break;
  852. }
  853. *(operationContext->NewValue) = *(operationContext->VariableAddress);
  854. SerMouPrint((
  855. 3,
  856. "SERMOUSE-SerMouDpcVariableOperation: exit with value 0x%x\n",
  857. *(operationContext->NewValue)
  858. ));
  859. }
  860. VOID
  861. SerMouGetDataQueuePointer(
  862. IN PVOID Context
  863. )
  864. /*++
  865. Routine Description:
  866. This routine is called synchronously to get the current DataIn and DataOut
  867. pointers for the port InputData queue.
  868. Arguments:
  869. Context - Pointer to a structure containing the device extension,
  870. address at which to store the current DataIn pointer, and the
  871. address at which to store the current DataOut pointer.
  872. Return Value:
  873. None.
  874. --*/
  875. {
  876. PDEVICE_EXTENSION deviceExtension;
  877. SerMouPrint((3,"SERMOUSE-SerMouGetDataQueuePointer: enter\n"));
  878. //
  879. // Get address of device extension.
  880. //
  881. deviceExtension = (PDEVICE_EXTENSION)
  882. ((PGET_DATA_POINTER_CONTEXT) Context)->DeviceExtension;
  883. //
  884. // Get the DataIn and DataOut pointers.
  885. //
  886. SerMouPrint((
  887. 3,
  888. "SERMOUSE-SerMouGetDataQueuePointer: DataIn 0x%x, DataOut 0x%x\n",
  889. deviceExtension->DataIn,
  890. deviceExtension->DataOut
  891. ));
  892. ((PGET_DATA_POINTER_CONTEXT) Context)->DataIn = deviceExtension->DataIn;
  893. ((PGET_DATA_POINTER_CONTEXT) Context)->DataOut = deviceExtension->DataOut;
  894. ((PGET_DATA_POINTER_CONTEXT) Context)->InputCount =
  895. deviceExtension->InputCount;
  896. SerMouPrint((3,"SERMOUSE-SerMouGetDataQueuePointer: exit\n"));
  897. }
  898. VOID
  899. SerMouInitializeDataQueue (
  900. IN PVOID Context
  901. )
  902. /*++
  903. Routine Description:
  904. This routine initializes the input data queue. It is called
  905. via KeSynchronization, except when called from the initialization routine.
  906. Arguments:
  907. Context - Pointer to the device extension.
  908. Return Value:
  909. None.
  910. --*/
  911. {
  912. PDEVICE_EXTENSION deviceExtension;
  913. SerMouPrint((3,"SERMOUSE-SerMouInitializeDataQueue: enter\n"));
  914. //
  915. // Get address of device extension.
  916. //
  917. deviceExtension = (PDEVICE_EXTENSION) Context;
  918. //
  919. // Initialize the input data queue.
  920. //
  921. deviceExtension->InputCount = 0;
  922. deviceExtension->DataIn = deviceExtension->InputData;
  923. deviceExtension->DataOut = deviceExtension->InputData;
  924. deviceExtension->OkayToLogOverflow = TRUE;
  925. SerMouPrint((3,"SERMOUSE-SerMouInitializeDataQueue: exit\n"));
  926. } // end SerMouInitializeDataQueue
  927. VOID
  928. SerMouSendReport(
  929. IN PDEVICE_OBJECT DeviceObject
  930. )
  931. /*++
  932. Routine Description:
  933. Place a completed report in the queue for subsequent processing by a DPC.
  934. Arguments:
  935. Device Object - Pointer to the device object.
  936. Return Value:
  937. None.
  938. --*/
  939. {
  940. PDEVICE_EXTENSION deviceExtension;
  941. deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
  942. if (!SerMouWriteDataToQueue(
  943. deviceExtension,
  944. &deviceExtension->CurrentInput
  945. )) {
  946. //
  947. // The mouse input data queue is full. Just drop the
  948. // latest input on the floor.
  949. //
  950. // Queue a DPC to log an overrun error.
  951. //
  952. SerMouPrint((
  953. 1,
  954. "SERMOUSE-SerMouSendReport: queue overflow\n"
  955. ));
  956. if (deviceExtension->OkayToLogOverflow) {
  957. KeInsertQueueDpc(
  958. &deviceExtension->ErrorLogDpc,
  959. (PIRP) NULL,
  960. (PVOID) (ULONG) SERMOUSE_MOU_BUFFER_OVERFLOW
  961. );
  962. deviceExtension->OkayToLogOverflow = FALSE;
  963. }
  964. } else if (deviceExtension->DpcInterlockVariable >= 0) {
  965. //
  966. // The ISR DPC is already executing. Tell the ISR DPC it has
  967. // more work to do by incrementing the DpcInterlockVariable.
  968. //
  969. deviceExtension->DpcInterlockVariable += 1;
  970. } else {
  971. //
  972. // Queue the ISR DPC.
  973. //
  974. KeInsertQueueDpc(
  975. &deviceExtension->IsrDpc,
  976. DeviceObject->CurrentIrp,
  977. NULL
  978. );
  979. }
  980. return;
  981. }
  982. VOID
  983. SerMouSetDataQueuePointer(
  984. IN PVOID Context
  985. )
  986. /*++
  987. Routine Description:
  988. This routine is called synchronously to set the DataOut pointer
  989. and InputCount for the port InputData queue.
  990. Arguments:
  991. Context - Pointer to a structure containing the device extension
  992. and the new DataOut value for the port InputData queue.
  993. Return Value:
  994. None.
  995. --*/
  996. {
  997. PDEVICE_EXTENSION deviceExtension;
  998. SerMouPrint((3,"SERMOUSE-SerMouSetDataQueuePointer: enter\n"));
  999. //
  1000. // Get address of device extension.
  1001. //
  1002. deviceExtension = (PDEVICE_EXTENSION)
  1003. ((PSET_DATA_POINTER_CONTEXT) Context)->DeviceExtension;
  1004. //
  1005. // Set the DataOut pointer.
  1006. //
  1007. SerMouPrint((
  1008. 3,
  1009. "SERMOUSE-SerMouSetDataQueuePointer: old mouse DataOut 0x%x, InputCount %d\n",
  1010. deviceExtension->DataOut,
  1011. deviceExtension->InputCount
  1012. ));
  1013. deviceExtension->DataOut = ((PSET_DATA_POINTER_CONTEXT) Context)->DataOut;
  1014. deviceExtension->InputCount -=
  1015. ((PSET_DATA_POINTER_CONTEXT) Context)->InputCount;
  1016. if (deviceExtension->InputCount == 0) {
  1017. //
  1018. // Reset the flag that determines whether it is time to log
  1019. // queue overflow errors. We don't want to log errors too often.
  1020. // Instead, log an error on the first overflow that occurs after
  1021. // the ring buffer has been emptied, and then stop logging errors
  1022. // until it gets cleared out and overflows again.
  1023. //
  1024. SerMouPrint((
  1025. 1,
  1026. "SERMOUSE-SerMouSetDataQueuePointer: Okay to log overflow\n"
  1027. ));
  1028. deviceExtension->OkayToLogOverflow = TRUE;
  1029. }
  1030. SerMouPrint((
  1031. 3,
  1032. "SERMOUSE-SerMouSetDataQueuePointer: new mouse DataOut 0x%x, InputCount %d\n",
  1033. deviceExtension->DataOut,
  1034. deviceExtension->InputCount
  1035. ));
  1036. SerMouPrint((3,"SERMOUSE-SerMouSetDataQueuePointer: exit\n"));
  1037. }
  1038. BOOLEAN
  1039. SerMouWriteDataToQueue(
  1040. PDEVICE_EXTENSION DeviceExtension,
  1041. IN PMOUSE_INPUT_DATA InputData
  1042. )
  1043. /*++
  1044. Routine Description:
  1045. This routine adds input data from the mouse to the InputData queue.
  1046. Arguments:
  1047. DeviceExtension - Pointer to the device extension.
  1048. InputData - Pointer to the data to add to the InputData queue.
  1049. Return Value:
  1050. Returns TRUE if the data was added, otherwise FALSE.
  1051. --*/
  1052. {
  1053. SerMouPrint((2,"SERMOUSE-SerMouWriteDataToQueue: enter\n"));
  1054. SerMouPrint((
  1055. 3,
  1056. "SERMOUSE-SerMouWriteDataToQueue: DataIn 0x%x, DataOut 0x%x\n",
  1057. DeviceExtension->DataIn,
  1058. DeviceExtension->DataOut
  1059. ));
  1060. SerMouPrint((
  1061. 3,
  1062. "SERMOUSE-SerMouWriteDataToQueue: InputCount %d\n",
  1063. DeviceExtension->InputCount
  1064. ));
  1065. //
  1066. // Check for full input data queue.
  1067. //
  1068. if ((DeviceExtension->DataIn == DeviceExtension->DataOut) &&
  1069. (DeviceExtension->InputCount != 0)) {
  1070. //
  1071. // The input data queue is full. Intentionally ignore
  1072. // the new data.
  1073. //
  1074. SerMouPrint((1,"SERMOUSE-SerMouWriteDataToQueue: OVERFLOW\n"));
  1075. return(FALSE);
  1076. } else {
  1077. *(DeviceExtension->DataIn) = *InputData;
  1078. DeviceExtension->InputCount += 1;
  1079. DeviceExtension->DataIn++;
  1080. SerMouPrint((
  1081. 2,
  1082. "SERMOUSE-SerMouWriteDataToQueue: new InputCount %d\n",
  1083. DeviceExtension->InputCount
  1084. ));
  1085. if (DeviceExtension->DataIn ==
  1086. DeviceExtension->DataEnd) {
  1087. SerMouPrint((2,"SERMOUSE-SerMouWriteDataToQueue: wrap buffer\n"));
  1088. DeviceExtension->DataIn = DeviceExtension->InputData;
  1089. }
  1090. }
  1091. SerMouPrint((2,"SERMOUSE-SerMouWriteDataToQueue: exit\n"));
  1092. return(TRUE);
  1093. }
  1094.