Fix little bug in mouse detection
[reactos.git] / reactos / drivers / input / sermouse / readmouse.c
1 /*
2 * PROJECT: ReactOS Serial mouse driver
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: drivers/input/sermouse/fdo.c
5 * PURPOSE: Read mouse moves and send them to mouclass
6 * PROGRAMMERS: Copyright Jason Filby (jasonfilby@yahoo.com)
7 Copyright Filip Navara (xnavara@volny.cz)
8 Copyright 2005-2006 Hervé Poussineau (hpoussin@reactos.org)
9 */
10
11 #define NDEBUG
12 #include <debug.h>
13
14 #include "sermouse.h"
15
16 static NTSTATUS
17 SermouseDeviceIoControl(
18 IN PDEVICE_OBJECT DeviceObject,
19 IN ULONG CtlCode,
20 IN PVOID InputBuffer OPTIONAL,
21 IN ULONG InputBufferSize,
22 IN OUT PVOID OutputBuffer OPTIONAL,
23 IN OUT PULONG OutputBufferSize)
24 {
25 KEVENT Event;
26 PIRP Irp;
27 IO_STATUS_BLOCK IoStatus;
28 NTSTATUS Status;
29
30 KeInitializeEvent(&Event, NotificationEvent, FALSE);
31
32 Irp = IoBuildDeviceIoControlRequest(CtlCode,
33 DeviceObject,
34 InputBuffer,
35 InputBufferSize,
36 OutputBuffer,
37 (OutputBufferSize) ? *OutputBufferSize : 0,
38 FALSE,
39 &Event,
40 &IoStatus);
41 if (Irp == NULL)
42 {
43 DPRINT("IoBuildDeviceIoControlRequest() failed\n");
44 return STATUS_INSUFFICIENT_RESOURCES;
45 }
46
47 Status = IoCallDriver(DeviceObject, Irp);
48
49 if (Status == STATUS_PENDING)
50 {
51 DPRINT("Operation pending\n");
52 KeWaitForSingleObject(&Event, Suspended, KernelMode, FALSE, NULL);
53 Status = IoStatus.Status;
54 }
55
56 if (OutputBufferSize)
57 {
58 *OutputBufferSize = IoStatus.Information;
59 }
60
61 return Status;
62 }
63
64 VOID NTAPI
65 SermouseDeviceWorker(
66 PVOID Context)
67 {
68 PSERMOUSE_DEVICE_EXTENSION DeviceExtension;
69 PDEVICE_OBJECT LowerDevice;
70 UCHAR Buffer[PACKET_BUFFER_SIZE];
71 PIRP Irp;
72 IO_STATUS_BLOCK ioStatus;
73 KEVENT event;
74 PUCHAR PacketBuffer;
75 UCHAR ReceivedByte;
76 ULONG Queue;
77 PMOUSE_INPUT_DATA Input;
78 ULONG ButtonsDifference;
79 KIRQL OldIrql;
80 ULONG i;
81 ULONG Fcr;
82 ULONG BaudRate;
83 SERIAL_TIMEOUTS Timeouts;
84 SERIAL_LINE_CONTROL LCR;
85 LARGE_INTEGER Zero;
86 NTSTATUS Status;
87
88 DPRINT("SermouseDeviceWorker() called\n");
89
90 DeviceExtension = (PSERMOUSE_DEVICE_EXTENSION)((PDEVICE_OBJECT)Context)->DeviceExtension;
91 LowerDevice = DeviceExtension->LowerDevice;
92 Zero.QuadPart = 0;
93 PacketBuffer = DeviceExtension->PacketBuffer;
94
95 ASSERT(LowerDevice);
96
97 /* Initialize device extension */
98 DeviceExtension->ActiveQueue = 0;
99 DeviceExtension->PacketBufferPosition = 0;
100 DeviceExtension->PreviousButtons = 0;
101
102 /* Initialize serial port */
103 Fcr = 0;
104 Status = SermouseDeviceIoControl(LowerDevice, IOCTL_SERIAL_SET_FIFO_CONTROL,
105 &Fcr, sizeof(Fcr), NULL, NULL);
106 if (!NT_SUCCESS(Status)) PsTerminateSystemThread(Status);
107 /* Set serial port speed */
108 BaudRate = DeviceExtension->AttributesInformation.SampleRate * 8;
109 Status = SermouseDeviceIoControl(LowerDevice, IOCTL_SERIAL_SET_BAUD_RATE,
110 &BaudRate, sizeof(BaudRate), NULL, NULL);
111 if (!NT_SUCCESS(Status)) PsTerminateSystemThread(Status);
112 /* Set LCR */
113 LCR.WordLength = 7;
114 LCR.Parity = NO_PARITY;
115 LCR.StopBits = STOP_BIT_1;
116 Status = SermouseDeviceIoControl(LowerDevice, IOCTL_SERIAL_SET_LINE_CONTROL,
117 &LCR, sizeof(LCR), NULL, NULL);
118 if (!NT_SUCCESS(Status)) PsTerminateSystemThread(Status);
119
120 /* Set timeouts */
121 Timeouts.ReadTotalTimeoutConstant = Timeouts.ReadTotalTimeoutMultiplier = 0;
122 Timeouts.ReadIntervalTimeout = 100;
123 Timeouts.WriteTotalTimeoutMultiplier = Timeouts.WriteTotalTimeoutConstant = 0;
124 Status = SermouseDeviceIoControl(LowerDevice, IOCTL_SERIAL_SET_TIMEOUTS,
125 &Timeouts, sizeof(Timeouts), NULL, NULL);
126 if (!NT_SUCCESS(Status)) PsTerminateSystemThread(Status);
127
128 /* main read loop */
129 while (TRUE)
130 {
131 Status = KeWaitForSingleObject(
132 &DeviceExtension->StopWorkerThreadEvent,
133 Executive,
134 KernelMode,
135 TRUE,
136 &Zero);
137 if (Status != STATUS_TIMEOUT)
138 {
139 /* we need to stop the worker thread */
140 KeResetEvent(&DeviceExtension->StopWorkerThreadEvent);
141 break;
142 }
143
144 KeInitializeEvent(&event, NotificationEvent, FALSE);
145 Irp = IoBuildSynchronousFsdRequest(
146 IRP_MJ_READ,
147 LowerDevice,
148 Buffer, PACKET_BUFFER_SIZE,
149 &Zero,
150 &event,
151 &ioStatus);
152 if (!Irp)
153 {
154 /* no memory actually, try later */
155 CHECKPOINT;
156 KeStallExecutionProcessor(10);
157 continue;
158 }
159
160 Status = IoCallDriver(LowerDevice, Irp);
161 if (Status == STATUS_PENDING)
162 {
163 KeWaitForSingleObject(&event, Suspended, KernelMode, FALSE, NULL);
164 Status = ioStatus.Status;
165 }
166
167 if (!NT_SUCCESS(Status))
168 continue;
169
170 /* Read all available data and process */
171 for (i = 0; i < ioStatus.Information; i++)
172 {
173 ReceivedByte = Buffer[i];
174 DPRINT("ReceivedByte 0x%02x\n", ReceivedByte);
175
176 /* Synchronize */
177 if ((ReceivedByte & 0x40) == 0x40)
178 DeviceExtension->PacketBufferPosition = 0;
179
180 PacketBuffer[DeviceExtension->PacketBufferPosition] = ReceivedByte & 0x7f;
181 DeviceExtension->PacketBufferPosition++;
182
183 /* Process packet if complete */
184 if (DeviceExtension->PacketBufferPosition >= 3)
185 {
186 Queue = DeviceExtension->ActiveQueue % 2;
187
188 /* Prevent buffer overflow */
189 if (DeviceExtension->InputDataCount[Queue] == 1)
190 continue;
191
192 Input = &DeviceExtension->MouseInputData[Queue];
193
194 if (DeviceExtension->PacketBufferPosition == 3)
195 {
196 /* Retrieve change in x and y from packet */
197 Input->LastX = (signed char)(PacketBuffer[1] | ((PacketBuffer[0] & 0x03) << 6));
198 Input->LastY = (signed char)(PacketBuffer[2] | ((PacketBuffer[0] & 0x0c) << 4));
199
200 /* Determine the current state of the buttons */
201 Input->RawButtons = (DeviceExtension->PreviousButtons & MOUSE_BUTTON_MIDDLE) |
202 ((UCHAR)(PacketBuffer[0] & LEFT_BUTTON_MASK) >> LEFT_BUTTON_SHIFT) |
203 ((UCHAR)(PacketBuffer[0] & RIGHT_BUTTON_MASK) >> RIGHT_BUTTON_SHIFT);
204 }
205 else if (DeviceExtension->PacketBufferPosition == 4)
206 {
207 DeviceExtension->PacketBufferPosition = 0;
208 /* If middle button state changed than report event */
209 if (((UCHAR)(PacketBuffer[3] & MIDDLE_BUTTON_MASK) >> MIDDLE_BUTTON_SHIFT) ^
210 (DeviceExtension->PreviousButtons & MOUSE_BUTTON_MIDDLE))
211 {
212 Input->RawButtons ^= MOUSE_BUTTON_MIDDLE;
213 Input->LastX = 0;
214 Input->LastY = 0;
215 }
216 else
217 {
218 continue;
219 }
220 }
221
222 /* Determine ButtonFlags */
223 Input->ButtonFlags = 0;
224 ButtonsDifference = DeviceExtension->PreviousButtons ^ Input->RawButtons;
225
226 if (ButtonsDifference != 0)
227 {
228 if (ButtonsDifference & MOUSE_BUTTON_LEFT
229 && DeviceExtension->AttributesInformation.NumberOfButtons >= 1)
230 {
231 if (Input->RawButtons & MOUSE_BUTTON_LEFT)
232 Input->ButtonFlags |= MOUSE_LEFT_BUTTON_DOWN;
233 else
234 Input->ButtonFlags |= MOUSE_LEFT_BUTTON_UP;
235 }
236
237 if (ButtonsDifference & MOUSE_BUTTON_RIGHT
238 && DeviceExtension->AttributesInformation.NumberOfButtons >= 2)
239 {
240 if (Input->RawButtons & MOUSE_BUTTON_RIGHT)
241 Input->ButtonFlags |= MOUSE_RIGHT_BUTTON_DOWN;
242 else
243 Input->ButtonFlags |= MOUSE_RIGHT_BUTTON_UP;
244 }
245
246 if (ButtonsDifference & MOUSE_BUTTON_MIDDLE
247 && DeviceExtension->AttributesInformation.NumberOfButtons >= 3)
248 {
249 if (Input->RawButtons & MOUSE_BUTTON_MIDDLE)
250 Input->ButtonFlags |= MOUSE_MIDDLE_BUTTON_DOWN;
251 else
252 Input->ButtonFlags |= MOUSE_MIDDLE_BUTTON_UP;
253 }
254 }
255
256 /* Send the Input data to the Mouse Class driver */
257 DeviceExtension->InputDataCount[Queue]++;
258
259 KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
260 InterlockedIncrement((PLONG)&DeviceExtension->ActiveQueue);
261 (*(PSERVICE_CALLBACK_ROUTINE)DeviceExtension->ConnectData.ClassService)(
262 DeviceExtension->ConnectData.ClassDeviceObject,
263 &DeviceExtension->MouseInputData[Queue],
264 &DeviceExtension->MouseInputData[Queue] + 1,
265 &DeviceExtension->InputDataCount[Queue]);
266 KeLowerIrql(OldIrql);
267 DeviceExtension->InputDataCount[Queue] = 0;
268
269 /* Copy RawButtons to Previous Buttons for Input */
270 DeviceExtension->PreviousButtons = Input->RawButtons;
271 }
272 }
273 }
274
275 PsTerminateSystemThread(STATUS_SUCCESS);
276 }