1 /*
2 * PROJECT: ReactOS InPort (Bus) Mouse Driver
3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4 * PURPOSE: Hardware support code
5 * COPYRIGHT: Copyright 2020 Dmitry Borisov (di.sean@protonmail.com)
6 */
7
8 /* Note: Some code was taken from Linux */
9
10 /* INCLUDES *******************************************************************/
11
12 #include "inport.h"
13
14 #define NDEBUG
15 #include <debug.h>
16
17 /* GLOBALS ********************************************************************/
18
19 #define READ_MOUSE(DeviceExtension, Port) \
20 READ_PORT_UCHAR((DeviceExtension)->IoBase + (Port))
21
22 #define WRITE_MOUSE(DeviceExtension, Port, Data) \
23 WRITE_PORT_UCHAR((DeviceExtension)->IoBase + (Port), (Data))
24
25 /*
26 * NEC
27 */
28 #define NEC_BM_DATA 0x00
29
30 #define NEC_BM_CONTROL 0x04
31 #define NEC_INT_ENABLE 0x00
32 #define NEC_INT_DISABLE 0x10
33
34 #define NEC_READ_X_LOW 0x00
35 #define NEC_READ_X_HIGH 0x20
36 #define NEC_READ_Y_LOW 0x40
37 #define NEC_READ_Y_HIGH 0x60
38
39 #define NEC_INPUT_CAPTURE 0x00
40 #define NEC_INPUT_HOLD 0x80
41
42 #define NEC_BM_CONFIG 0x06
43 #define NEC_PPI_INT_ENABLE 0x08
44 #define NEC_PPI_INT_DISABLE 0x09
45 #define NEC_PPI_HC_NO_CLEAR 0x0E
46 #define NEC_PPI_HC_CLEAR 0x0F
47 #define NEC_PPI_DEFAULT_MODE 0x93
48
49 #define NEC_BM_INT_RATE 0x4002
50 #define NEC_RATE_120_HZ 0x00
51 #define NEC_RATE_60_HZ 0x01
52 #define NEC_RATE_30_HZ 0x02
53 #define NEC_RATE_15_HZ 0x03
54
55 #define NEC_BM_HIRESO_BASE (PUCHAR)0x61
56
57 #define NEC_BUTTON_RIGHT 0x20
58 #define NEC_BUTTON_LEFT 0x80
59
60 /*
61 * Microsoft InPort
62 */
63 #define MS_INPORT_CONTROL 0x00
64 #define INPORT_REG_BTNS 0x00
65 #define INPORT_REG_X 0x01
66 #define INPORT_REG_Y 0x02
67 #define INPORT_REG_MODE 0x07
68 #define INPORT_RESET 0x80
69
70 #define MS_INPORT_DATA 0x01
71 #define INPORT_MODE_IRQ 0x01
72 #define INPORT_MODE_BASE 0x10
73 #define INPORT_MODE_HOLD 0x20
74 #define INPORT_HAS_MOVED 0x40
75
76 #define MS_INPORT_SIGNATURE 0x02
77
78 #define MS_BUTTON_RIGHT 0x01
79 #define MS_BUTTON_MIDDLE 0x02
80 #define MS_BUTTON_LEFT 0x04
81
82 /*
83 * Logitech
84 */
85 #define LOG_BM_DATA 0x00
86
87 #define LOG_BM_SIGNATURE 0x01
88 #define LOG_SIGNATURE_BYTE 0xA5
89
90 #define LOG_BM_CONTROL 0x02
91 #define LOG_ENABLE_IRQ 0x00
92 #define LOG_DISABLE_IRQ 0x10
93
94 #define LOG_READ_X_LOW 0x80
95 #define LOG_READ_X_HIGH 0xA0
96 #define LOG_READ_Y_LOW 0xC0
97 #define LOG_READ_Y_HIGH 0xE0
98
99 #define LOG_BM_CONFIG 0x03
100 #define LOG_DEFAULT_MODE 0x90
101 #define LOG_CONFIG_BYTE 0x91
102
103 #define LOG_BUTTON_RIGHT 0x20
104 #define LOG_BUTTON_MIDDLE 0x40
105 #define LOG_BUTTON_LEFT 0x80
106
107 /* FUNCTIONS ******************************************************************/
108
109 VOID
110 NTAPI
InPortDpcForIsr(_In_ PKDPC Dpc,_In_ PDEVICE_OBJECT DeviceObject,_Inout_ PIRP Irp,_In_opt_ PVOID Context)111 InPortDpcForIsr(
112 _In_ PKDPC Dpc,
113 _In_ PDEVICE_OBJECT DeviceObject,
114 _Inout_ PIRP Irp,
115 _In_opt_ PVOID Context)
116 {
117 PINPORT_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
118 KIRQL OldIrql;
119 ULONG DummyInputDataConsumed;
120 INPORT_RAW_DATA RawData;
121
122 UNREFERENCED_PARAMETER(Dpc);
123 UNREFERENCED_PARAMETER(Irp);
124 UNREFERENCED_PARAMETER(Context);
125
126 /* Copy raw data */
127 OldIrql = KeAcquireInterruptSpinLock(DeviceExtension->InterruptObject);
128 RawData = DeviceExtension->RawData;
129 KeReleaseInterruptSpinLock(DeviceExtension->InterruptObject, OldIrql);
130
131 /* Fill out fields */
132 DeviceExtension->MouseInputData.LastX = RawData.DeltaX;
133 DeviceExtension->MouseInputData.LastY = RawData.DeltaY;
134 DeviceExtension->MouseInputData.ButtonFlags = 0;
135 if (RawData.ButtonDiff != 0)
136 {
137 switch (DeviceExtension->MouseType)
138 {
139 case NecBusMouse:
140 {
141 if (RawData.ButtonDiff & NEC_BUTTON_LEFT)
142 {
143 if (RawData.Buttons & NEC_BUTTON_LEFT)
144 DeviceExtension->MouseInputData.ButtonFlags |= MOUSE_LEFT_BUTTON_UP;
145 else
146 DeviceExtension->MouseInputData.ButtonFlags |= MOUSE_LEFT_BUTTON_DOWN;
147 }
148 if (RawData.ButtonDiff & NEC_BUTTON_RIGHT)
149 {
150 if (RawData.Buttons & NEC_BUTTON_RIGHT)
151 DeviceExtension->MouseInputData.ButtonFlags |= MOUSE_RIGHT_BUTTON_UP;
152 else
153 DeviceExtension->MouseInputData.ButtonFlags |= MOUSE_RIGHT_BUTTON_DOWN;
154 }
155
156 break;
157 }
158
159 case MsInPortMouse:
160 {
161 /* Button flags have to be inverted */
162 if (RawData.ButtonDiff & MS_BUTTON_LEFT)
163 {
164 if (RawData.Buttons & MS_BUTTON_LEFT)
165 DeviceExtension->MouseInputData.ButtonFlags |= MOUSE_LEFT_BUTTON_DOWN;
166 else
167 DeviceExtension->MouseInputData.ButtonFlags |= MOUSE_LEFT_BUTTON_UP;
168 }
169 if (RawData.ButtonDiff & MS_BUTTON_RIGHT)
170 {
171 if (RawData.Buttons & MS_BUTTON_RIGHT)
172 DeviceExtension->MouseInputData.ButtonFlags |= MOUSE_RIGHT_BUTTON_DOWN;
173 else
174 DeviceExtension->MouseInputData.ButtonFlags |= MOUSE_RIGHT_BUTTON_UP;
175 }
176 if (RawData.ButtonDiff & MS_BUTTON_MIDDLE)
177 {
178 if (RawData.Buttons & MS_BUTTON_MIDDLE)
179 DeviceExtension->MouseInputData.ButtonFlags |= MOUSE_MIDDLE_BUTTON_DOWN;
180 else
181 DeviceExtension->MouseInputData.ButtonFlags |= MOUSE_MIDDLE_BUTTON_UP;
182 }
183
184 break;
185 }
186
187 case LogitechBusMouse:
188 {
189 if (RawData.ButtonDiff & LOG_BUTTON_LEFT)
190 {
191 if (RawData.Buttons & LOG_BUTTON_LEFT)
192 DeviceExtension->MouseInputData.ButtonFlags |= MOUSE_LEFT_BUTTON_UP;
193 else
194 DeviceExtension->MouseInputData.ButtonFlags |= MOUSE_LEFT_BUTTON_DOWN;
195 }
196 if (RawData.ButtonDiff & LOG_BUTTON_RIGHT)
197 {
198 if (RawData.Buttons & LOG_BUTTON_RIGHT)
199 DeviceExtension->MouseInputData.ButtonFlags |= MOUSE_RIGHT_BUTTON_UP;
200 else
201 DeviceExtension->MouseInputData.ButtonFlags |= MOUSE_RIGHT_BUTTON_DOWN;
202 }
203 if (RawData.ButtonDiff & LOG_BUTTON_MIDDLE)
204 {
205 if (RawData.Buttons & LOG_BUTTON_MIDDLE)
206 DeviceExtension->MouseInputData.ButtonFlags |= MOUSE_MIDDLE_BUTTON_UP;
207 else
208 DeviceExtension->MouseInputData.ButtonFlags |= MOUSE_MIDDLE_BUTTON_DOWN;
209 }
210
211 break;
212 }
213 }
214 }
215
216 /* Send mouse packet */
217 (*(PSERVICE_CALLBACK_ROUTINE)DeviceExtension->ClassService)(
218 DeviceExtension->ClassDeviceObject,
219 &DeviceExtension->MouseInputData,
220 &DeviceExtension->MouseInputData + 1,
221 &DummyInputDataConsumed);
222 }
223
224 BOOLEAN
225 NTAPI
InPortIsr(_In_ PKINTERRUPT Interrupt,_In_ PVOID Context)226 InPortIsr(
227 _In_ PKINTERRUPT Interrupt,
228 _In_ PVOID Context)
229 {
230 UCHAR Buttons;
231 ULONG ButtonDiff;
232 CHAR DeltaX, DeltaY;
233 PINPORT_DEVICE_EXTENSION DeviceExtension = Context;
234
235 UNREFERENCED_PARAMETER(Interrupt);
236
237 switch (DeviceExtension->MouseType)
238 {
239 case NecBusMouse:
240 {
241 WRITE_MOUSE(DeviceExtension, NEC_BM_CONTROL,
242 NEC_INPUT_CAPTURE | NEC_INT_DISABLE);
243
244 WRITE_MOUSE(DeviceExtension, NEC_BM_CONTROL,
245 NEC_INPUT_HOLD | NEC_INT_DISABLE | NEC_READ_X_LOW);
246 DeltaX = READ_MOUSE(DeviceExtension, NEC_BM_DATA) & 0x0F;
247
248 WRITE_MOUSE(DeviceExtension, NEC_BM_CONTROL,
249 NEC_INPUT_HOLD | NEC_INT_DISABLE | NEC_READ_X_HIGH);
250 DeltaX |= READ_MOUSE(DeviceExtension, NEC_BM_DATA) << 4;
251
252 WRITE_MOUSE(DeviceExtension, NEC_BM_CONTROL,
253 NEC_INPUT_HOLD | NEC_INT_DISABLE | NEC_READ_Y_LOW);
254 DeltaY = READ_MOUSE(DeviceExtension, NEC_BM_DATA) & 0x0F;
255
256 WRITE_MOUSE(DeviceExtension, NEC_BM_CONTROL,
257 NEC_INPUT_HOLD | NEC_INT_DISABLE | NEC_READ_Y_HIGH);
258 Buttons = READ_MOUSE(DeviceExtension, NEC_BM_DATA);
259 DeltaY |= Buttons << 4;
260 Buttons &= (NEC_BUTTON_LEFT | NEC_BUTTON_RIGHT);
261
262 WRITE_MOUSE(DeviceExtension, NEC_BM_CONTROL,
263 NEC_INPUT_HOLD | NEC_INT_ENABLE);
264
265 break;
266 }
267
268 case MsInPortMouse:
269 {
270 WRITE_MOUSE(DeviceExtension, MS_INPORT_CONTROL, INPORT_REG_MODE);
271 WRITE_MOUSE(DeviceExtension, MS_INPORT_DATA,
272 INPORT_MODE_HOLD | INPORT_MODE_IRQ | INPORT_MODE_BASE);
273
274 WRITE_MOUSE(DeviceExtension, MS_INPORT_CONTROL, INPORT_REG_BTNS);
275 Buttons = READ_MOUSE(DeviceExtension, MS_INPORT_DATA);
276
277 if (Buttons & INPORT_HAS_MOVED)
278 {
279 WRITE_MOUSE(DeviceExtension, MS_INPORT_CONTROL, INPORT_REG_X);
280 DeltaX = READ_MOUSE(DeviceExtension, MS_INPORT_DATA);
281
282 WRITE_MOUSE(DeviceExtension, MS_INPORT_CONTROL, INPORT_REG_Y);
283 DeltaY = READ_MOUSE(DeviceExtension, MS_INPORT_DATA);
284 }
285 else
286 {
287 DeltaX = 0;
288 DeltaY = 0;
289 }
290
291 Buttons &= (MS_BUTTON_MIDDLE | MS_BUTTON_LEFT | MS_BUTTON_RIGHT);
292
293 WRITE_MOUSE(DeviceExtension, MS_INPORT_CONTROL, INPORT_REG_MODE);
294 WRITE_MOUSE(DeviceExtension, MS_INPORT_DATA,
295 INPORT_MODE_IRQ | INPORT_MODE_BASE);
296
297 break;
298 }
299
300 case LogitechBusMouse:
301 {
302 WRITE_MOUSE(DeviceExtension, LOG_BM_CONTROL, LOG_READ_X_LOW);
303 DeltaX = READ_MOUSE(DeviceExtension, LOG_BM_DATA) & 0x0F;
304
305 WRITE_MOUSE(DeviceExtension, LOG_BM_CONTROL, LOG_READ_X_HIGH);
306 DeltaX |= READ_MOUSE(DeviceExtension, LOG_BM_DATA) << 4;
307
308 WRITE_MOUSE(DeviceExtension, LOG_BM_CONTROL, LOG_READ_Y_LOW);
309 DeltaY = READ_MOUSE(DeviceExtension, LOG_BM_DATA) & 0x0F;
310
311 WRITE_MOUSE(DeviceExtension, LOG_BM_CONTROL, LOG_READ_Y_HIGH);
312 Buttons = READ_MOUSE(DeviceExtension, LOG_BM_DATA);
313 DeltaY |= Buttons << 4;
314 Buttons &= (LOG_BUTTON_RIGHT | LOG_BUTTON_MIDDLE | LOG_BUTTON_LEFT);
315
316 WRITE_MOUSE(DeviceExtension, LOG_BM_CONTROL, LOG_ENABLE_IRQ);
317
318 break;
319 }
320 }
321
322 ButtonDiff = DeviceExtension->MouseButtonState ^ Buttons;
323 DeviceExtension->MouseButtonState = Buttons;
324
325 /*
326 * Bus mouse devices don't have a status register to check
327 * whether this interrupt is indeed for us.
328 */
329 if ((DeltaX == 0) && (DeltaY == 0) && (ButtonDiff == 0))
330 {
331 /* We just pretend that the interrupt is not ours */
332 return FALSE;
333 }
334 else
335 {
336 DeviceExtension->RawData.DeltaX = DeltaX;
337 DeviceExtension->RawData.DeltaY = DeltaY;
338 DeviceExtension->RawData.Buttons = Buttons;
339 DeviceExtension->RawData.ButtonDiff = ButtonDiff;
340
341 IoRequestDpc(DeviceExtension->Self, NULL, NULL);
342
343 return TRUE;
344 }
345 }
346
347 CODE_SEG("PAGE")
348 VOID
349 NTAPI
InPortInitializeMouse(_In_ PINPORT_DEVICE_EXTENSION DeviceExtension)350 InPortInitializeMouse(
351 _In_ PINPORT_DEVICE_EXTENSION DeviceExtension)
352 {
353 PAGED_CODE();
354
355 /* Initialize mouse and disable interrupts */
356 switch (DeviceExtension->MouseType)
357 {
358 case NecBusMouse:
359 WRITE_MOUSE(DeviceExtension, NEC_BM_CONFIG, NEC_PPI_DEFAULT_MODE);
360
361 /* Setup interrupt rate (unavailable on hireso machines) */
362 if (DeviceExtension->IoBase != NEC_BM_HIRESO_BASE)
363 WRITE_MOUSE(DeviceExtension, NEC_BM_INT_RATE, NEC_RATE_60_HZ);
364
365 WRITE_MOUSE(DeviceExtension, NEC_BM_CONFIG, NEC_PPI_INT_DISABLE);
366 WRITE_MOUSE(DeviceExtension, NEC_BM_CONFIG, NEC_PPI_HC_NO_CLEAR);
367 WRITE_MOUSE(DeviceExtension, NEC_BM_CONFIG, NEC_PPI_HC_CLEAR);
368 break;
369
370 case MsInPortMouse:
371 WRITE_MOUSE(DeviceExtension, MS_INPORT_CONTROL, INPORT_RESET);
372 WRITE_MOUSE(DeviceExtension, MS_INPORT_CONTROL, INPORT_REG_MODE);
373 WRITE_MOUSE(DeviceExtension, MS_INPORT_DATA, INPORT_MODE_BASE);
374 break;
375
376 case LogitechBusMouse:
377 WRITE_MOUSE(DeviceExtension, LOG_BM_CONFIG, LOG_DEFAULT_MODE);
378 WRITE_MOUSE(DeviceExtension, LOG_BM_CONTROL, LOG_DISABLE_IRQ);
379 break;
380 }
381 }
382
383 BOOLEAN
384 NTAPI
InPortStartMouse(_In_ PVOID SynchronizeContext)385 InPortStartMouse(
386 _In_ PVOID SynchronizeContext)
387 {
388 PINPORT_DEVICE_EXTENSION DeviceExtension = SynchronizeContext;
389
390 /* Enable interrupts */
391 switch (DeviceExtension->MouseType)
392 {
393 case NecBusMouse:
394 WRITE_MOUSE(DeviceExtension, NEC_BM_CONFIG, NEC_PPI_INT_ENABLE);
395 WRITE_MOUSE(DeviceExtension, NEC_BM_CONFIG, NEC_PPI_HC_NO_CLEAR);
396 WRITE_MOUSE(DeviceExtension, NEC_BM_CONFIG, NEC_PPI_HC_CLEAR);
397 break;
398
399 case MsInPortMouse:
400 WRITE_MOUSE(DeviceExtension, MS_INPORT_CONTROL, INPORT_REG_MODE);
401 WRITE_MOUSE(DeviceExtension, MS_INPORT_DATA,
402 INPORT_MODE_IRQ | INPORT_MODE_BASE);
403 break;
404
405 case LogitechBusMouse:
406 WRITE_MOUSE(DeviceExtension, LOG_BM_CONTROL, LOG_ENABLE_IRQ);
407 break;
408 }
409
410 return TRUE;
411 }
412
413 BOOLEAN
414 NTAPI
InPortStopMouse(_In_ PVOID SynchronizeContext)415 InPortStopMouse(
416 _In_ PVOID SynchronizeContext)
417 {
418 PINPORT_DEVICE_EXTENSION DeviceExtension = SynchronizeContext;
419
420 /* Disable interrupts */
421 switch (DeviceExtension->MouseType)
422 {
423 case NecBusMouse:
424 WRITE_MOUSE(DeviceExtension, NEC_BM_CONFIG, NEC_PPI_INT_DISABLE);
425 WRITE_MOUSE(DeviceExtension, NEC_BM_CONFIG, NEC_PPI_HC_NO_CLEAR);
426 WRITE_MOUSE(DeviceExtension, NEC_BM_CONFIG, NEC_PPI_HC_CLEAR);
427 break;
428
429 case MsInPortMouse:
430 WRITE_MOUSE(DeviceExtension, MS_INPORT_CONTROL, INPORT_REG_MODE);
431 WRITE_MOUSE(DeviceExtension, MS_INPORT_DATA, INPORT_MODE_BASE);
432 break;
433
434 case LogitechBusMouse:
435 WRITE_MOUSE(DeviceExtension, LOG_BM_CONTROL, LOG_DISABLE_IRQ);
436 break;
437 }
438
439 return TRUE;
440 }
441