1 /* 2 * Copyright 2004-2009 Haiku, Inc. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors (in chronological order): 6 * Stefano Ceccherini (burton666@libero.it) 7 * Axel Dörfler, axeld@pinc-software.de 8 * Marcus Overhagen <marcus@overhagen.de> 9 */ 10 11 /*! PS/2 bus manager */ 12 13 14 #include <string.h> 15 16 #include "ps2_common.h" 17 #include "ps2_service.h" 18 #include "ps2_dev.h" 19 20 21 //#define TRACE_PS2_COMMON 22 #ifdef TRACE_PS2_COMMON 23 # define TRACE(x...) dprintf(x) 24 # define TRACE_ONLY 25 #else 26 # define TRACE(x...) 27 # define TRACE_ONLY __attribute__((unused)) 28 #endif 29 30 31 isa_module_info *gIsa = NULL; 32 bool gActiveMultiplexingEnabled = false; 33 bool gSetupComplete = false; 34 sem_id gControllerSem; 35 36 static int32 sIgnoreInterrupts = 0; 37 38 39 uint8 40 ps2_read_ctrl(void) 41 { 42 return gIsa->read_io_8(PS2_PORT_CTRL); 43 } 44 45 46 uint8 47 ps2_read_data(void) 48 { 49 return gIsa->read_io_8(PS2_PORT_DATA); 50 } 51 52 53 void 54 ps2_write_ctrl(uint8 ctrl) 55 { 56 TRACE("ps2: ps2_write_ctrl 0x%02x\n", ctrl); 57 58 gIsa->write_io_8(PS2_PORT_CTRL, ctrl); 59 } 60 61 62 void 63 ps2_write_data(uint8 data) 64 { 65 TRACE("ps2: ps2_write_data 0x%02x\n", data); 66 67 gIsa->write_io_8(PS2_PORT_DATA, data); 68 } 69 70 71 status_t 72 ps2_wait_read(void) 73 { 74 int i; 75 for (i = 0; i < PS2_CTRL_WAIT_TIMEOUT / 50; i++) { 76 if (ps2_read_ctrl() & PS2_STATUS_OUTPUT_BUFFER_FULL) 77 return B_OK; 78 snooze(50); 79 } 80 return B_ERROR; 81 } 82 83 84 status_t 85 ps2_wait_write(void) 86 { 87 int i; 88 for (i = 0; i < PS2_CTRL_WAIT_TIMEOUT / 50; i++) { 89 if (!(ps2_read_ctrl() & PS2_STATUS_INPUT_BUFFER_FULL)) 90 return B_OK; 91 snooze(50); 92 } 93 return B_ERROR; 94 } 95 96 97 // #pragma mark - 98 99 100 void 101 ps2_flush(void) 102 { 103 int i; 104 105 acquire_sem(gControllerSem); 106 atomic_add(&sIgnoreInterrupts, 1); 107 108 for (i = 0; i < 64; i++) { 109 uint8 ctrl; 110 uint8 data TRACE_ONLY; 111 ctrl = ps2_read_ctrl(); 112 if (!(ctrl & PS2_STATUS_OUTPUT_BUFFER_FULL)) 113 break; 114 data = ps2_read_data(); 115 TRACE("ps2: ps2_flush: ctrl 0x%02x, data 0x%02x (%s)\n", ctrl, data, (ctrl & PS2_STATUS_AUX_DATA) ? "aux" : "keyb"); 116 snooze(100); 117 } 118 119 atomic_add(&sIgnoreInterrupts, -1); 120 release_sem(gControllerSem); 121 } 122 123 124 static status_t 125 ps2_selftest() 126 { 127 status_t res; 128 uint8 in; 129 res = ps2_command(PS2_CTRL_SELF_TEST, NULL, 0, &in, 1); 130 if (res != B_OK || in != 0x55) { 131 INFO("ps2: controller self test failed, status 0x%08" B_PRIx32 ", data " 132 "0x%02x\n", res, in); 133 return B_ERROR; 134 } 135 return B_OK; 136 } 137 138 139 static status_t 140 ps2_setup_command_byte(bool interruptsEnabled) 141 { 142 status_t res; 143 uint8 cmdbyte; 144 145 res = ps2_command(PS2_CTRL_READ_CMD, NULL, 0, &cmdbyte, 1); 146 TRACE("ps2: get command byte: res 0x%08" B_PRIx32 ", cmdbyte 0x%02x\n", 147 res, cmdbyte); 148 if (res != B_OK) 149 cmdbyte = 0x47; 150 151 cmdbyte |= PS2_BITS_TRANSLATE_SCANCODES; 152 cmdbyte &= ~(PS2_BITS_KEYBOARD_DISABLED | PS2_BITS_MOUSE_DISABLED); 153 154 if (interruptsEnabled) 155 cmdbyte |= PS2_BITS_KEYBOARD_INTERRUPT | PS2_BITS_AUX_INTERRUPT; 156 else 157 cmdbyte &= ~(PS2_BITS_KEYBOARD_INTERRUPT | PS2_BITS_AUX_INTERRUPT); 158 159 res = ps2_command(PS2_CTRL_WRITE_CMD, &cmdbyte, 1, NULL, 0); 160 TRACE("ps2: set command byte: res 0x%08" B_PRIx32 ", cmdbyte 0x%02x\n", 161 res, cmdbyte); 162 163 return res; 164 } 165 166 167 static status_t 168 ps2_setup_active_multiplexing(bool *enabled) 169 { 170 status_t res; 171 uint8 in, out; 172 173 // Disable the keyboard port to avoid any interference with the keyboard 174 ps2_command(PS2_CTRL_KEYBOARD_DISABLE, NULL, 0, NULL, 0); 175 176 out = 0xf0; 177 res = ps2_command(PS2_CTRL_AUX_LOOPBACK, &out, 1, &in, 1); 178 if (res) 179 goto fail; 180 // Step 1, if controller is good, in does match out. 181 // This test failes with MS Virtual PC. 182 if (in != out) 183 goto no_support; 184 185 out = 0x56; 186 res = ps2_command(PS2_CTRL_AUX_LOOPBACK, &out, 1, &in, 1); 187 if (res) 188 goto fail; 189 // Step 2, if controller is good, in does match out. 190 if (in != out) 191 goto no_support; 192 193 out = 0xa4; 194 res = ps2_command(PS2_CTRL_AUX_LOOPBACK, &out, 1, &in, 1); 195 if (res) 196 goto fail; 197 // Step 3, if the controller doesn't support active multiplexing, 198 // then in data does match out data (0xa4), else it's version number. 199 if (in == out) 200 goto no_support; 201 202 // With some broken USB legacy emulation, it's 0xac, and with 203 // MS Virtual PC, it's 0xa6. Since current active multiplexing 204 // specification version is 1.1 (0x11), we validate the data. 205 if (in > 0x9f) { 206 TRACE("ps2: active multiplexing v%d.%d detected, but ignored!\n", (in >> 4), in & 0xf); 207 goto no_support; 208 } 209 210 INFO("ps2: active multiplexing v%d.%d enabled\n", (in >> 4), in & 0xf); 211 *enabled = true; 212 goto done; 213 214 no_support: 215 TRACE("ps2: active multiplexing not supported\n"); 216 *enabled = false; 217 218 done: 219 // Some controllers get upset by the d3 command and will continue data 220 // loopback, thus we need to send a harmless command (enable keyboard 221 // interface) next. 222 // This fixes bug report #1175 223 res = ps2_command(PS2_CTRL_KEYBOARD_ENABLE, NULL, 0, NULL, 0); 224 if (res != B_OK) { 225 INFO("ps2: active multiplexing d3 workaround failed, status 0x%08" 226 B_PRIx32 "\n", res); 227 } 228 return B_OK; 229 230 fail: 231 TRACE("ps2: testing for active multiplexing failed\n"); 232 *enabled = false; 233 // this should revert the controller into legacy mode, 234 // just in case it has switched to multiplexed mode 235 return ps2_selftest(); 236 } 237 238 239 status_t 240 ps2_command(uint8 cmd, const uint8 *out, int outCount, uint8 *in, int inCount) 241 { 242 status_t res; 243 int i; 244 245 acquire_sem(gControllerSem); 246 atomic_add(&sIgnoreInterrupts, 1); 247 248 #ifdef TRACE_PS2_COMMON 249 TRACE("ps2: ps2_command cmd 0x%02x, out %d, in %d\n", cmd, outCount, inCount); 250 for (i = 0; i < outCount; i++) 251 TRACE("ps2: ps2_command out 0x%02x\n", out[i]); 252 #endif 253 254 res = ps2_wait_write(); 255 if (res == B_OK) 256 ps2_write_ctrl(cmd); 257 258 for (i = 0; res == B_OK && i < outCount; i++) { 259 res = ps2_wait_write(); 260 if (res == B_OK) 261 ps2_write_data(out[i]); 262 else 263 TRACE("ps2: ps2_command out byte %d failed\n", i); 264 } 265 266 for (i = 0; res == B_OK && i < inCount; i++) { 267 res = ps2_wait_read(); 268 if (res == B_OK) 269 in[i] = ps2_read_data(); 270 else 271 TRACE("ps2: ps2_command in byte %d failed\n", i); 272 } 273 274 #ifdef TRACE_PS2_COMMON 275 for (i = 0; i < inCount; i++) 276 TRACE("ps2: ps2_command in 0x%02x\n", in[i]); 277 TRACE("ps2: ps2_command result 0x%08" B_PRIx32 "\n", res); 278 #endif 279 280 atomic_add(&sIgnoreInterrupts, -1); 281 release_sem(gControllerSem); 282 283 return res; 284 } 285 286 287 // #pragma mark - 288 289 290 static int32 291 ps2_interrupt(void* cookie) 292 { 293 uint8 ctrl; 294 uint8 data; 295 bool error; 296 ps2_dev *dev; 297 298 ctrl = ps2_read_ctrl(); 299 if (!(ctrl & PS2_STATUS_OUTPUT_BUFFER_FULL)) { 300 TRACE("ps2: ps2_interrupt unhandled, OBF bit unset, ctrl 0x%02x (%s)\n", 301 ctrl, (ctrl & PS2_STATUS_AUX_DATA) ? "aux" : "keyb"); 302 return B_UNHANDLED_INTERRUPT; 303 } 304 305 if (atomic_get(&sIgnoreInterrupts)) { 306 TRACE("ps2: ps2_interrupt ignoring, ctrl 0x%02x (%s)\n", ctrl, 307 (ctrl & PS2_STATUS_AUX_DATA) ? "aux" : "keyb"); 308 return B_HANDLED_INTERRUPT; 309 } 310 311 data = ps2_read_data(); 312 313 if ((ctrl & PS2_STATUS_AUX_DATA) != 0) { 314 uint8 idx; 315 if (gActiveMultiplexingEnabled) { 316 idx = ctrl >> 6; 317 error = (ctrl & 0x04) != 0; 318 TRACE("ps2: ps2_interrupt ctrl 0x%02x, data 0x%02x (mouse %d)\n", 319 ctrl, data, idx); 320 } else { 321 idx = 0; 322 error = (ctrl & 0xC0) != 0; 323 TRACE("ps2: ps2_interrupt ctrl 0x%02x, data 0x%02x (aux)\n", ctrl, 324 data); 325 } 326 dev = &ps2_device[PS2_DEVICE_MOUSE + idx]; 327 } else { 328 TRACE("ps2: ps2_interrupt ctrl 0x%02x, data 0x%02x (keyb)\n", ctrl, 329 data); 330 331 dev = &ps2_device[PS2_DEVICE_KEYB]; 332 error = (ctrl & 0xC0) != 0; 333 } 334 335 dev->history[1] = dev->history[0]; 336 dev->history[0].time = system_time(); 337 dev->history[0].data = data; 338 dev->history[0].error = error; 339 340 return ps2_dev_handle_int(dev); 341 } 342 343 344 // #pragma mark - driver interface 345 346 347 status_t 348 ps2_init(void) 349 { 350 status_t status; 351 352 TRACE("ps2: init\n"); 353 354 status = get_module(B_ISA_MODULE_NAME, (module_info **)&gIsa); 355 if (status < B_OK) 356 return status; 357 358 gControllerSem = create_sem(1, "ps/2 keyb ctrl"); 359 360 ps2_flush(); 361 362 status = ps2_dev_init(); 363 if (status < B_OK) 364 goto err1; 365 366 status = ps2_service_init(); 367 if (status < B_OK) 368 goto err2; 369 370 status = install_io_interrupt_handler(INT_PS2_KEYBOARD, &ps2_interrupt, 371 NULL, 0); 372 if (status) 373 goto err3; 374 375 status = install_io_interrupt_handler(INT_PS2_MOUSE, &ps2_interrupt, NULL, 376 0); 377 if (status) 378 goto err4; 379 380 // While this might have fixed bug #1185, we can't do this unconditionally 381 // as it obviously messes up many controllers which couldn't reboot anymore 382 // after that 383 //ps2_selftest(); 384 385 // Setup the command byte with disabled keyboard and AUX interrupts 386 // to prevent interrupts storm on some KBCs during active multiplexing 387 // activation procedure. Fixes #7635. 388 status = ps2_setup_command_byte(false); 389 if (status) { 390 INFO("ps2: initial setup of command byte failed\n"); 391 goto err5; 392 } 393 394 ps2_flush(); 395 status = ps2_setup_active_multiplexing(&gActiveMultiplexingEnabled); 396 if (status) { 397 INFO("ps2: setting up active multiplexing failed\n"); 398 goto err5; 399 } 400 401 status = ps2_setup_command_byte(true); 402 if (status) { 403 INFO("ps2: setting up command byte with enabled interrupts failed\n"); 404 goto err5; 405 } 406 407 if (gActiveMultiplexingEnabled) { 408 // The multiplexing spec recommends to leave device 0 unconnected because it saves some 409 // confusion with the use of the D3 command which appears as if the replied data was 410 // coming from device 0. So we enable it only if it really looks like there is a device 411 // connected there. 412 if (ps2_dev_command_timeout(&ps2_device[PS2_DEVICE_MOUSE], 413 PS2_CMD_MOUSE_SET_SCALE11, NULL, 0, NULL, 0, 100000) == B_TIMED_OUT) { 414 INFO("ps2: accessing multiplexed mouse port 0 timed out, ignoring it!\n"); 415 } else { 416 ps2_service_notify_device_added(&ps2_device[PS2_DEVICE_MOUSE]); 417 } 418 419 for (int idx = 1; idx <= 3; idx++) { 420 ps2_service_notify_device_added(&ps2_device[PS2_DEVICE_MOUSE + idx]); 421 } 422 ps2_service_notify_device_added(&ps2_device[PS2_DEVICE_KEYB]); 423 } else { 424 ps2_service_notify_device_added(&ps2_device[PS2_DEVICE_MOUSE]); 425 ps2_service_notify_device_added(&ps2_device[PS2_DEVICE_KEYB]); 426 } 427 428 gSetupComplete = true; 429 430 TRACE("ps2: init done!\n"); 431 return B_OK; 432 433 err5: 434 remove_io_interrupt_handler(INT_PS2_MOUSE, &ps2_interrupt, NULL); 435 err4: 436 remove_io_interrupt_handler(INT_PS2_KEYBOARD, &ps2_interrupt, NULL); 437 err3: 438 ps2_service_exit(); 439 err2: 440 ps2_dev_exit(); 441 err1: 442 delete_sem(gControllerSem); 443 put_module(B_ISA_MODULE_NAME); 444 TRACE("ps2: init failed!\n"); 445 return B_ERROR; 446 } 447 448 449 void 450 ps2_uninit(void) 451 { 452 TRACE("ps2: uninit\n"); 453 remove_io_interrupt_handler(INT_PS2_MOUSE, &ps2_interrupt, NULL); 454 remove_io_interrupt_handler(INT_PS2_KEYBOARD, &ps2_interrupt, NULL); 455 ps2_service_exit(); 456 ps2_dev_exit(); 457 delete_sem(gControllerSem); 458 put_module(B_ISA_MODULE_NAME); 459 } 460