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