xref: /reactos/drivers/input/inport/hardware.c (revision 9cfd8dd9)
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
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
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
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
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
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