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