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