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