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