xref: /reactos/drivers/input/inport/hardware.c (revision 6924b8ff)
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 
75 #define MS_INPORT_SIGNATURE  0x02
76 
77 #define MS_BUTTON_MIDDLE   0x01
78 #define MS_BUTTON_LEFT     0x02
79 #define MS_BUTTON_RIGHT    0x04
80 
81 /*
82  * Logitech
83  */
84 #define LOG_BM_DATA          0x00
85 
86 #define LOG_BM_SIGNATURE     0x01
87     #define LOG_SIGNATURE_BYTE 0xA5
88 
89 #define LOG_BM_CONTROL       0x02
90     #define LOG_ENABLE_IRQ     0x00
91     #define LOG_DISABLE_IRQ    0x10
92 
93     #define LOG_READ_X_LOW     0x80
94     #define LOG_READ_X_HIGH    0xA0
95     #define LOG_READ_Y_LOW     0xC0
96     #define LOG_READ_Y_HIGH    0xE0
97 
98 #define LOG_BM_CONFIG        0x03
99     #define LOG_DEFAULT_MODE   0x90
100     #define LOG_CONFIG_BYTE    0x91
101 
102 #define LOG_BUTTON_RIGHT     0x20
103 #define LOG_BUTTON_MIDDLE    0x40
104 #define LOG_BUTTON_LEFT      0x80
105 
106 /* FUNCTIONS ******************************************************************/
107 
108 VOID
109 NTAPI
110 InPortDpcForIsr(
111     _In_ PKDPC Dpc,
112     _In_ PDEVICE_OBJECT DeviceObject,
113     _Inout_ PIRP Irp,
114     _In_opt_ PVOID Context)
115 {
116     PINPORT_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
117     KIRQL OldIrql;
118     ULONG DummyInputDataConsumed;
119     INPORT_RAW_DATA RawData;
120 
121     UNREFERENCED_PARAMETER(Dpc);
122     UNREFERENCED_PARAMETER(Irp);
123     UNREFERENCED_PARAMETER(Context);
124 
125     /* Copy raw data */
126     OldIrql = KeAcquireInterruptSpinLock(DeviceExtension->InterruptObject);
127     RawData = DeviceExtension->RawData;
128     KeReleaseInterruptSpinLock(DeviceExtension->InterruptObject, OldIrql);
129 
130     /* Fill out fields */
131     DeviceExtension->MouseInputData.LastX = RawData.DeltaX;
132     DeviceExtension->MouseInputData.LastY = RawData.DeltaY;
133     DeviceExtension->MouseInputData.ButtonFlags = 0;
134     if (RawData.ButtonDiff != 0)
135     {
136         switch (DeviceExtension->MouseType)
137         {
138             case NecBusMouse:
139             {
140                 if (RawData.ButtonDiff & NEC_BUTTON_LEFT)
141                 {
142                     if (RawData.Buttons & NEC_BUTTON_LEFT)
143                         DeviceExtension->MouseInputData.ButtonFlags |= MOUSE_LEFT_BUTTON_UP;
144                     else
145                         DeviceExtension->MouseInputData.ButtonFlags |= MOUSE_LEFT_BUTTON_DOWN;
146                 }
147                 if (RawData.ButtonDiff & NEC_BUTTON_RIGHT)
148                 {
149                     if (RawData.Buttons & NEC_BUTTON_RIGHT)
150                         DeviceExtension->MouseInputData.ButtonFlags |= MOUSE_RIGHT_BUTTON_UP;
151                     else
152                         DeviceExtension->MouseInputData.ButtonFlags |= MOUSE_RIGHT_BUTTON_DOWN;
153                 }
154 
155                 break;
156             }
157 
158             case MsInPortMouse:
159             {
160                 /* Button flags have to be inverted */
161                 if (RawData.ButtonDiff & MS_BUTTON_LEFT)
162                 {
163                     if (RawData.Buttons & MS_BUTTON_LEFT)
164                         DeviceExtension->MouseInputData.ButtonFlags |= MOUSE_LEFT_BUTTON_DOWN;
165                     else
166                         DeviceExtension->MouseInputData.ButtonFlags |= MOUSE_LEFT_BUTTON_UP;
167                 }
168                 if (RawData.ButtonDiff & MS_BUTTON_RIGHT)
169                 {
170                     if (RawData.Buttons & MS_BUTTON_RIGHT)
171                         DeviceExtension->MouseInputData.ButtonFlags |= MOUSE_RIGHT_BUTTON_DOWN;
172                     else
173                         DeviceExtension->MouseInputData.ButtonFlags |= MOUSE_RIGHT_BUTTON_UP;
174                 }
175                 if (RawData.ButtonDiff & MS_BUTTON_MIDDLE)
176                 {
177                     if (RawData.Buttons & MS_BUTTON_MIDDLE)
178                         DeviceExtension->MouseInputData.ButtonFlags |= MOUSE_MIDDLE_BUTTON_DOWN;
179                     else
180                         DeviceExtension->MouseInputData.ButtonFlags |= MOUSE_MIDDLE_BUTTON_UP;
181                 }
182 
183                 break;
184             }
185 
186             case LogitechBusMouse:
187             {
188                 if (RawData.ButtonDiff & LOG_BUTTON_LEFT)
189                 {
190                     if (RawData.Buttons & LOG_BUTTON_LEFT)
191                         DeviceExtension->MouseInputData.ButtonFlags |= MOUSE_LEFT_BUTTON_UP;
192                     else
193                         DeviceExtension->MouseInputData.ButtonFlags |= MOUSE_LEFT_BUTTON_DOWN;
194                 }
195                 if (RawData.ButtonDiff & LOG_BUTTON_RIGHT)
196                 {
197                     if (RawData.Buttons & LOG_BUTTON_RIGHT)
198                         DeviceExtension->MouseInputData.ButtonFlags |= MOUSE_RIGHT_BUTTON_UP;
199                     else
200                         DeviceExtension->MouseInputData.ButtonFlags |= MOUSE_RIGHT_BUTTON_DOWN;
201                 }
202                 if (RawData.ButtonDiff & LOG_BUTTON_MIDDLE)
203                 {
204                     if (RawData.Buttons & LOG_BUTTON_MIDDLE)
205                         DeviceExtension->MouseInputData.ButtonFlags |= MOUSE_MIDDLE_BUTTON_UP;
206                     else
207                         DeviceExtension->MouseInputData.ButtonFlags |= MOUSE_MIDDLE_BUTTON_DOWN;
208                 }
209 
210                 break;
211             }
212         }
213     }
214 
215     /* Send mouse packet */
216     (*(PSERVICE_CALLBACK_ROUTINE)DeviceExtension->ClassService)(
217         DeviceExtension->ClassDeviceObject,
218         &DeviceExtension->MouseInputData,
219         &DeviceExtension->MouseInputData + 1,
220         &DummyInputDataConsumed);
221 }
222 
223 BOOLEAN
224 NTAPI
225 InPortIsr(
226     _In_ PKINTERRUPT Interrupt,
227     _In_ PVOID Context)
228 {
229     UCHAR Buttons;
230     ULONG ButtonDiff;
231     CHAR DeltaX, DeltaY;
232     PINPORT_DEVICE_EXTENSION DeviceExtension = Context;
233 
234     UNREFERENCED_PARAMETER(Interrupt);
235 
236     switch (DeviceExtension->MouseType)
237     {
238         case NecBusMouse:
239         {
240             WRITE_MOUSE(DeviceExtension, NEC_BM_CONTROL,
241                         NEC_INPUT_CAPTURE | NEC_INT_DISABLE);
242 
243             WRITE_MOUSE(DeviceExtension, NEC_BM_CONTROL,
244                         NEC_INPUT_HOLD | NEC_INT_DISABLE | NEC_READ_X_LOW);
245             DeltaX = READ_MOUSE(DeviceExtension, NEC_BM_DATA) & 0x0F;
246 
247             WRITE_MOUSE(DeviceExtension, NEC_BM_CONTROL,
248                         NEC_INPUT_HOLD | NEC_INT_DISABLE | NEC_READ_X_HIGH);
249             DeltaX |= READ_MOUSE(DeviceExtension, NEC_BM_DATA) << 4;
250 
251             WRITE_MOUSE(DeviceExtension, NEC_BM_CONTROL,
252                         NEC_INPUT_HOLD | NEC_INT_DISABLE | NEC_READ_Y_LOW);
253             DeltaY = READ_MOUSE(DeviceExtension, NEC_BM_DATA) & 0x0F;
254 
255             WRITE_MOUSE(DeviceExtension, NEC_BM_CONTROL,
256                         NEC_INPUT_HOLD | NEC_INT_DISABLE | NEC_READ_Y_HIGH);
257             Buttons = READ_MOUSE(DeviceExtension, NEC_BM_DATA);
258             DeltaY |= Buttons << 4;
259             Buttons &= (NEC_BUTTON_LEFT | NEC_BUTTON_RIGHT);
260 
261             WRITE_MOUSE(DeviceExtension, NEC_BM_CONTROL,
262                         NEC_INPUT_HOLD | NEC_INT_ENABLE);
263 
264             break;
265         }
266 
267         case MsInPortMouse:
268         {
269             WRITE_MOUSE(DeviceExtension, MS_INPORT_CONTROL, INPORT_REG_MODE);
270             WRITE_MOUSE(DeviceExtension, MS_INPORT_DATA,
271                         INPORT_MODE_HOLD | INPORT_MODE_IRQ | INPORT_MODE_BASE);
272 
273             WRITE_MOUSE(DeviceExtension, MS_INPORT_CONTROL, INPORT_REG_X);
274             DeltaX = READ_MOUSE(DeviceExtension, MS_INPORT_DATA);
275 
276             WRITE_MOUSE(DeviceExtension, MS_INPORT_CONTROL, INPORT_REG_Y);
277             DeltaY = READ_MOUSE(DeviceExtension, MS_INPORT_DATA);
278 
279             WRITE_MOUSE(DeviceExtension, MS_INPORT_CONTROL, INPORT_REG_BTNS);
280             Buttons = READ_MOUSE(DeviceExtension, MS_INPORT_DATA);
281             Buttons &= (MS_BUTTON_MIDDLE | MS_BUTTON_LEFT | MS_BUTTON_RIGHT);
282 
283             WRITE_MOUSE(DeviceExtension, MS_INPORT_CONTROL, INPORT_REG_MODE);
284             WRITE_MOUSE(DeviceExtension, MS_INPORT_DATA,
285                         INPORT_MODE_IRQ | INPORT_MODE_BASE);
286 
287             break;
288         }
289 
290         case LogitechBusMouse:
291         {
292             WRITE_MOUSE(DeviceExtension, LOG_BM_CONTROL, LOG_READ_X_LOW);
293             DeltaX = READ_MOUSE(DeviceExtension, LOG_BM_DATA) & 0x0F;
294 
295             WRITE_MOUSE(DeviceExtension, LOG_BM_CONTROL, LOG_READ_X_HIGH);
296             DeltaX |= READ_MOUSE(DeviceExtension, LOG_BM_DATA) << 4;
297 
298             WRITE_MOUSE(DeviceExtension, LOG_BM_CONTROL, LOG_READ_Y_LOW);
299             DeltaY = READ_MOUSE(DeviceExtension, LOG_BM_DATA) & 0x0F;
300 
301             WRITE_MOUSE(DeviceExtension, LOG_BM_CONTROL, LOG_READ_Y_HIGH);
302             Buttons = READ_MOUSE(DeviceExtension, LOG_BM_DATA);
303             DeltaY |= Buttons << 4;
304             Buttons &= (LOG_BUTTON_RIGHT | LOG_BUTTON_MIDDLE | LOG_BUTTON_LEFT);
305 
306             WRITE_MOUSE(DeviceExtension, LOG_BM_CONTROL, LOG_ENABLE_IRQ);
307 
308             break;
309         }
310     }
311 
312     ButtonDiff = DeviceExtension->MouseButtonState ^ Buttons;
313     DeviceExtension->MouseButtonState = Buttons;
314 
315     /*
316      * Bus mouse devices don't have a status register to check
317      * whether this interrupt is indeed for us.
318      */
319     if ((DeltaX == 0) && (DeltaY == 0) && (ButtonDiff == 0))
320     {
321         /* We just pretend that the interrupt is not ours */
322         return FALSE;
323     }
324     else
325     {
326         DeviceExtension->RawData.DeltaX = DeltaX;
327         DeviceExtension->RawData.DeltaY = DeltaY;
328         DeviceExtension->RawData.Buttons = Buttons;
329         DeviceExtension->RawData.ButtonDiff = ButtonDiff;
330 
331         IoRequestDpc(DeviceExtension->Self, NULL, NULL);
332 
333         return TRUE;
334     }
335 }
336 
337 CODE_SEG("PAGE")
338 VOID
339 NTAPI
340 InPortInitializeMouse(
341     _In_ PINPORT_DEVICE_EXTENSION DeviceExtension)
342 {
343     PAGED_CODE();
344 
345     /* Initialize mouse and disable interrupts */
346     switch (DeviceExtension->MouseType)
347     {
348         case NecBusMouse:
349             WRITE_MOUSE(DeviceExtension, NEC_BM_CONFIG, NEC_PPI_DEFAULT_MODE);
350 
351             /* Setup interrupt rate (unavailable on hireso machines) */
352             if (DeviceExtension->IoBase != NEC_BM_HIRESO_BASE)
353                 WRITE_MOUSE(DeviceExtension, NEC_BM_INT_RATE, NEC_RATE_60_HZ);
354 
355             WRITE_MOUSE(DeviceExtension, NEC_BM_CONFIG, NEC_PPI_INT_DISABLE);
356             WRITE_MOUSE(DeviceExtension, NEC_BM_CONFIG, NEC_PPI_HC_NO_CLEAR);
357             WRITE_MOUSE(DeviceExtension, NEC_BM_CONFIG, NEC_PPI_HC_CLEAR);
358             break;
359 
360         case MsInPortMouse:
361             WRITE_MOUSE(DeviceExtension, MS_INPORT_CONTROL, INPORT_RESET);
362             WRITE_MOUSE(DeviceExtension, MS_INPORT_CONTROL, INPORT_REG_MODE);
363             WRITE_MOUSE(DeviceExtension, MS_INPORT_DATA, INPORT_MODE_BASE);
364             break;
365 
366         case LogitechBusMouse:
367             WRITE_MOUSE(DeviceExtension, LOG_BM_CONFIG, LOG_DEFAULT_MODE);
368             WRITE_MOUSE(DeviceExtension, LOG_BM_CONTROL, LOG_DISABLE_IRQ);
369             break;
370     }
371 }
372 
373 BOOLEAN
374 NTAPI
375 InPortStartMouse(
376     _In_ PVOID SynchronizeContext)
377 {
378     PINPORT_DEVICE_EXTENSION DeviceExtension = SynchronizeContext;
379 
380     /* Enable interrupts */
381     switch (DeviceExtension->MouseType)
382     {
383         case NecBusMouse:
384             WRITE_MOUSE(DeviceExtension, NEC_BM_CONFIG, NEC_PPI_INT_ENABLE);
385             WRITE_MOUSE(DeviceExtension, NEC_BM_CONFIG, NEC_PPI_HC_NO_CLEAR);
386             WRITE_MOUSE(DeviceExtension, NEC_BM_CONFIG, NEC_PPI_HC_CLEAR);
387             break;
388 
389         case MsInPortMouse:
390             WRITE_MOUSE(DeviceExtension, MS_INPORT_CONTROL, INPORT_REG_MODE);
391             WRITE_MOUSE(DeviceExtension, MS_INPORT_DATA,
392                         INPORT_MODE_IRQ | INPORT_MODE_BASE);
393             break;
394 
395         case LogitechBusMouse:
396             WRITE_MOUSE(DeviceExtension, LOG_BM_CONTROL, LOG_ENABLE_IRQ);
397             break;
398     }
399 
400     return TRUE;
401 }
402 
403 BOOLEAN
404 NTAPI
405 InPortStopMouse(
406     _In_ PVOID SynchronizeContext)
407 {
408     PINPORT_DEVICE_EXTENSION DeviceExtension = SynchronizeContext;
409 
410     /* Disable interrupts */
411     switch (DeviceExtension->MouseType)
412     {
413         case NecBusMouse:
414             WRITE_MOUSE(DeviceExtension, NEC_BM_CONFIG, NEC_PPI_INT_DISABLE);
415             WRITE_MOUSE(DeviceExtension, NEC_BM_CONFIG, NEC_PPI_HC_NO_CLEAR);
416             WRITE_MOUSE(DeviceExtension, NEC_BM_CONFIG, NEC_PPI_HC_CLEAR);
417             break;
418 
419         case MsInPortMouse:
420             WRITE_MOUSE(DeviceExtension, MS_INPORT_CONTROL, INPORT_REG_MODE);
421             WRITE_MOUSE(DeviceExtension, MS_INPORT_DATA, INPORT_MODE_BASE);
422             break;
423 
424         case LogitechBusMouse:
425             WRITE_MOUSE(DeviceExtension, LOG_BM_CONTROL, LOG_DISABLE_IRQ);
426             break;
427     }
428 
429     return TRUE;
430 }
431