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