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