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