1 /* 2 * Copyright 2005-2014 Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors (in chronological order): 6 * Marcus Overhagen (marcus@overhagen.de) 7 * Clemens Zeidler (haiku@clemens-zeidler.de) 8 */ 9 10 11 /*! PS/2 bus manager */ 12 13 #include "ps2_dev.h" 14 #include "ps2_service.h" 15 16 #include "ps2_alps.h" 17 #include "ps2_elantech.h" 18 #include "ps2_standard_mouse.h" 19 #include "ps2_synaptics.h" 20 #include "ps2_trackpoint.h" 21 22 #include <fs/devfs.h> 23 24 #include <string.h> 25 26 27 //#define TRACE_PS2_DEV 28 #ifdef TRACE_PS2_DEV 29 # define TRACE(x...) dprintf(x) 30 #else 31 # define TRACE(x...) 32 #endif 33 34 35 ps2_dev ps2_device[PS2_DEVICE_COUNT]; 36 37 38 status_t 39 ps2_reset_mouse(ps2_dev* dev) 40 { 41 uint8 data[2]; 42 status_t status; 43 44 TRACE("ps2: ps2_reset_mouse\n"); 45 46 status = ps2_dev_command_timeout(dev, PS2_CMD_RESET, NULL, 0, data, 2, 500000); 47 48 if (status == B_OK && data[0] == 0xFE && data[1] == 0xAA) { 49 // workaround for HP/Compaq KBCs timeout condition. #2867 #3594 #4315 50 TRACE("ps2: KBC has timed out the mouse reset request. " 51 "Response was: 0x%02x 0x%02x. Requesting the answer data.\n", 52 data[0], data[1]); 53 status = ps2_dev_command(dev, PS2_CMD_RESEND, NULL, 0, data, 2); 54 } 55 56 if (status == B_OK && (data[0] != 0xAA || data[1] != 0x00)) { 57 TRACE("ps2: reset mouse failed, response was: 0x%02x 0x%02x\n", 58 data[0], data[1]); 59 status = B_ERROR; 60 } else if (status != B_OK) { 61 TRACE("ps2: reset mouse failed\n"); 62 } else { 63 TRACE("ps2: reset mouse success\n"); 64 } 65 66 return status; 67 } 68 69 70 status_t 71 ps2_dev_detect_pointing(ps2_dev* dev, device_hooks** hooks) 72 { 73 status_t status = ps2_reset_mouse(dev); 74 if (status != B_OK) { 75 INFO("ps2_dev: reset failed: %s\n", strerror(status)); 76 return B_ERROR; 77 } 78 79 // probe devices 80 // the probe function has to set the dev name and the dev packet size 81 82 status = probe_trackpoint(dev); 83 if (status == B_OK) { 84 *hooks = &gStandardMouseDeviceHooks; 85 goto dev_found; 86 } 87 88 status = probe_synaptics(dev); 89 if (status == B_OK) { 90 *hooks = &gSynapticsDeviceHooks; 91 goto dev_found; 92 } 93 94 status = probe_alps(dev); 95 if (status == B_OK) { 96 *hooks = &gALPSDeviceHooks; 97 goto dev_found; 98 } 99 100 status = probe_elantech(dev); 101 if (status == B_OK) { 102 *hooks = &gElantechDeviceHooks; 103 goto dev_found; 104 } 105 106 // reset the mouse for the case that the previous probes left the mouse in 107 // a undefined state 108 status = ps2_reset_mouse(dev); 109 if (status != B_OK) { 110 INFO("ps2: reset after probe failed\n"); 111 return B_ERROR; 112 } 113 114 status = probe_standard_mouse(dev); 115 if (status == B_OK) { 116 *hooks = &gStandardMouseDeviceHooks; 117 goto dev_found; 118 } 119 120 return B_ERROR; 121 122 dev_found: 123 if (dev == &(ps2_device[PS2_DEVICE_SYN_PASSTHROUGH])) 124 synaptics_pass_through_set_packet_size(dev, dev->packet_size); 125 126 return B_OK; 127 } 128 129 130 status_t 131 ps2_dev_init(void) 132 { 133 ps2_device[0].name = "input/mouse/ps2/0"; 134 ps2_device[0].active = false; 135 ps2_device[0].idx = 0; 136 ps2_device[0].result_sem = -1; 137 ps2_device[0].command = standard_command_timeout; 138 139 ps2_device[1].name = "input/mouse/ps2/1"; 140 ps2_device[1].active = false; 141 ps2_device[1].idx = 1; 142 ps2_device[1].result_sem = -1; 143 ps2_device[1].command = standard_command_timeout; 144 145 ps2_device[2].name = "input/mouse/ps2/2"; 146 ps2_device[2].active = false; 147 ps2_device[2].idx = 2; 148 ps2_device[2].result_sem = -1; 149 ps2_device[2].command = standard_command_timeout; 150 151 ps2_device[3].name = "input/mouse/ps2/3"; 152 ps2_device[3].active = false; 153 ps2_device[3].idx = 3; 154 ps2_device[3].result_sem = -1; 155 ps2_device[3].command = standard_command_timeout; 156 157 ps2_device[4].name = "input/mouse/ps2/synaptics_passthrough"; 158 ps2_device[4].active = false; 159 ps2_device[4].result_sem = -1; 160 ps2_device[4].command = passthrough_command; 161 162 ps2_device[5].name = "input/keyboard/at/0"; 163 ps2_device[5].active = false; 164 ps2_device[5].result_sem = -1; 165 ps2_device[5].flags = PS2_FLAG_KEYB; 166 ps2_device[5].command = standard_command_timeout; 167 168 int i; 169 for (i = 0; i < PS2_DEVICE_COUNT; i++) { 170 ps2_dev* dev = &ps2_device[i]; 171 dev->result_sem = create_sem(0, "ps2 result"); 172 if (dev->result_sem < 0) 173 goto err; 174 } 175 return B_OK; 176 177 err: 178 ps2_dev_exit(); 179 180 return B_ERROR; 181 } 182 183 184 void 185 ps2_dev_exit(void) 186 { 187 for (int i = 0; i < PS2_DEVICE_COUNT; i++) { 188 ps2_dev* dev = &ps2_device[i]; 189 if (dev->result_sem >= 0) { 190 delete_sem(dev->result_sem); 191 dev->result_sem = -1; 192 } 193 } 194 } 195 196 197 void 198 ps2_dev_publish(ps2_dev* dev) 199 { 200 status_t status = B_OK; 201 TRACE("ps2: ps2_dev_publish %s\n", dev->name); 202 203 if (dev->active) 204 return; 205 206 if (atomic_get(&dev->flags) & PS2_FLAG_KEYB) { 207 status = devfs_publish_device(dev->name, &gKeyboardDeviceHooks); 208 } else { 209 // Check if this is the "pass-through" device and wait until 210 // the parent_dev goes to enabled state. It is required to prevent 211 // from messing up the Synaptics command sequences in synaptics_open. 212 if (dev->parent_dev) { 213 const bigtime_t timeout = 2000000; 214 bigtime_t start = system_time(); 215 while (!(atomic_get(&dev->parent_dev->flags) & PS2_FLAG_ENABLED)) { 216 if ((system_time() - start) > timeout) { 217 status = B_BUSY; 218 break; 219 } 220 snooze(timeout / 20); 221 } 222 TRACE("ps2: publishing %s: parent %s is %s; wait time %" B_PRId64 223 "\n", dev->name, dev->parent_dev->name, 224 status == B_OK ? "enabled" : "busy", system_time() - start); 225 } 226 227 if (status == B_OK) { 228 device_hooks* hooks; 229 status = ps2_dev_detect_pointing(dev, &hooks); 230 if (status == B_OK) { 231 status = devfs_publish_device(dev->name, hooks); 232 } 233 } 234 } 235 236 dev->active = true; 237 238 INFO("ps2: devfs_publish_device %s, status = 0x%08" B_PRIx32 "\n", 239 dev->name, status); 240 } 241 242 243 void 244 ps2_dev_unpublish(ps2_dev* dev) 245 { 246 status_t status; 247 TRACE("ps2: ps2_dev_unpublish %s\n", dev->name); 248 249 if (!dev->active) 250 return; 251 252 dev->active = false; 253 254 status = devfs_unpublish_device(dev->name, true); 255 256 if ((dev->flags & PS2_FLAG_ENABLED) && dev->disconnect) 257 dev->disconnect(dev); 258 259 INFO("ps2: devfs_unpublish_device %s, status = 0x%08" B_PRIx32 "\n", 260 dev->name, status); 261 } 262 263 264 int32 265 ps2_dev_handle_int(ps2_dev* dev) 266 { 267 const uint8 data = dev->history[0].data; 268 uint32 flags; 269 270 flags = atomic_get(&dev->flags); 271 272 if (flags & PS2_FLAG_CMD) { 273 if ((flags & (PS2_FLAG_ACK | PS2_FLAG_NACK)) == 0) { 274 int cnt = 1; 275 if (data == PS2_REPLY_ACK) { 276 atomic_or(&dev->flags, PS2_FLAG_ACK); 277 } else if (data == PS2_REPLY_RESEND || data == PS2_REPLY_ERROR) { 278 atomic_or(&dev->flags, PS2_FLAG_NACK); 279 } else if ((flags & PS2_FLAG_GETID) 280 && (data == 0 || data == 3 || data == 4)) { 281 // workaround for broken mice that don't ack the "get id" 282 // command 283 TRACE("ps2: ps2_dev_handle_int: mouse didn't ack the 'get id' " 284 "command\n"); 285 atomic_or(&dev->flags, PS2_FLAG_ACK); 286 if (dev->result_buf_cnt) { 287 dev->result_buf[dev->result_buf_idx] = data; 288 dev->result_buf_idx++; 289 dev->result_buf_cnt--; 290 if (dev->result_buf_cnt == 0) { 291 atomic_and(&dev->flags, ~PS2_FLAG_CMD); 292 cnt++; 293 } 294 } 295 } else if ((flags & PS2_FLAG_RESEND)) { 296 TRACE("ps2: ps2_dev_handle_int: processing RESEND request\n"); 297 atomic_or(&dev->flags, PS2_FLAG_ACK); 298 if (dev->result_buf_cnt) { 299 dev->result_buf[dev->result_buf_idx] = data; 300 dev->result_buf_idx++; 301 dev->result_buf_cnt--; 302 if (dev->result_buf_cnt == 0) { 303 atomic_and(&dev->flags, ~PS2_FLAG_CMD); 304 cnt++; 305 } 306 } 307 } else { 308 // TRACE("ps2: ps2_dev_handle_int unexpected data 0x%02x while " 309 // "waiting for ack\n", data); 310 TRACE("ps2: int1 %02x\n", data); 311 goto pass_to_handler; 312 } 313 release_sem_etc(dev->result_sem, cnt, B_DO_NOT_RESCHEDULE); 314 return B_INVOKE_SCHEDULER; 315 } else if (dev->result_buf_cnt) { 316 dev->result_buf[dev->result_buf_idx] = data; 317 dev->result_buf_idx++; 318 dev->result_buf_cnt--; 319 if (dev->result_buf_cnt == 0) { 320 atomic_and(&dev->flags, ~PS2_FLAG_CMD); 321 release_sem_etc(dev->result_sem, 1, B_DO_NOT_RESCHEDULE); 322 return B_INVOKE_SCHEDULER; 323 } 324 } else { 325 // TRACE("ps2: ps2_dev_handle_int unexpected data 0x%02x during " 326 // "command processing\n", data); 327 TRACE("ps2: int2 %02x\n", data); 328 goto pass_to_handler; 329 } 330 return B_HANDLED_INTERRUPT; 331 } 332 333 pass_to_handler: 334 335 if ((flags & PS2_FLAG_KEYB) == 0) { 336 if (dev->history[0].error && data == 0xfd) { 337 INFO("ps2: hot removal of %s\n", dev->name); 338 ps2_service_notify_device_removed(dev); 339 return B_INVOKE_SCHEDULER; 340 } 341 if (data == 0x00 && dev->history[1].data == 0xaa 342 && (dev->history[0].time - dev->history[1].time) < 50000) { 343 INFO("ps2: hot plugin of %s\n", dev->name); 344 if (dev->active) { 345 INFO("ps2: device %s still active, republishing...\n", 346 dev->name); 347 ps2_service_notify_device_republish(dev); 348 } else { 349 ps2_service_notify_device_added(dev); 350 } 351 return B_INVOKE_SCHEDULER; 352 } 353 } 354 355 if (!dev->active) { 356 TRACE("ps2: %s not active, data 0x%02x dropped\n", dev->name, data); 357 if (gSetupComplete && data != 0x00 && data != 0xaa) { 358 INFO("ps2: possibly a hot plugin of %s\n", dev->name); 359 ps2_service_notify_device_added(dev); 360 return B_INVOKE_SCHEDULER; 361 } 362 return B_HANDLED_INTERRUPT; 363 } 364 365 if ((flags & PS2_FLAG_ENABLED) == 0) { 366 TRACE("ps2: %s not enabled, data 0x%02x dropped\n", dev->name, data); 367 return B_HANDLED_INTERRUPT; 368 } 369 370 return dev->handle_int(dev); 371 } 372 373 374 status_t 375 standard_command_timeout(ps2_dev* dev, uint8 cmd, const uint8* out, 376 int out_count, uint8* in, int in_count, bigtime_t timeout) 377 { 378 status_t res; 379 #ifdef TRACE_PS2_DEV 380 bigtime_t start; 381 #endif 382 int32 sem_count; 383 int i; 384 385 TRACE("ps2: ps2_dev_command cmd 0x%02x, out-count %d, in-count %d, " 386 "dev %s\n", cmd, out_count, in_count, dev->name); 387 for (i = 0; i < out_count; i++) 388 TRACE("ps2: ps2_dev_command tx: 0x%02x\n", out[i]); 389 390 res = get_sem_count(dev->result_sem, &sem_count); 391 if (res == B_OK && sem_count != 0) { 392 TRACE("ps2: ps2_dev_command: sem_count %" B_PRId32 ", fixing!\n", 393 sem_count); 394 if (sem_count > 0) 395 acquire_sem_etc(dev->result_sem, sem_count, 0, 0); 396 else 397 release_sem_etc(dev->result_sem, -sem_count, 0); 398 } 399 400 dev->result_buf_cnt = in_count; 401 dev->result_buf_idx = 0; 402 dev->result_buf = in; 403 404 res = B_OK; 405 for (i = -1; res == B_OK && i < out_count; i++) { 406 407 atomic_and(&dev->flags, 408 ~(PS2_FLAG_ACK | PS2_FLAG_NACK | PS2_FLAG_GETID | PS2_FLAG_RESEND)); 409 410 acquire_sem(gControllerSem); 411 412 if (!(atomic_get(&dev->flags) & PS2_FLAG_KEYB)) { 413 uint8 prefix_cmd; 414 if (gActiveMultiplexingEnabled) 415 prefix_cmd = 0x90 + dev->idx; 416 else 417 prefix_cmd = 0xd4; 418 res = ps2_wait_write(); 419 if (res == B_OK) 420 ps2_write_ctrl(prefix_cmd); 421 } 422 423 res = ps2_wait_write(); 424 if (res == B_OK) { 425 if (i == -1) { 426 if (cmd == PS2_CMD_GET_DEVICE_ID) 427 atomic_or(&dev->flags, PS2_FLAG_CMD | PS2_FLAG_GETID); 428 else if (cmd == PS2_CMD_RESEND) 429 atomic_or(&dev->flags, PS2_FLAG_CMD | PS2_FLAG_RESEND); 430 else 431 atomic_or(&dev->flags, PS2_FLAG_CMD); 432 ps2_write_data(cmd); 433 } else { 434 ps2_write_data(out[i]); 435 } 436 } 437 438 release_sem(gControllerSem); 439 #ifdef TRACE_PS2_DEV 440 start = system_time(); 441 #endif 442 res = acquire_sem_etc(dev->result_sem, 1, B_RELATIVE_TIMEOUT, timeout); 443 444 if (res != B_OK) 445 atomic_and(&dev->flags, ~PS2_FLAG_CMD); 446 447 #ifdef TRACE_PS2_DEV 448 TRACE("ps2: ps2_dev_command wait for ack res 0x%08" B_PRIx32 ", " 449 "wait-time %" B_PRId64 "\n", res, system_time() - start); 450 #endif 451 if (atomic_get(&dev->flags) & PS2_FLAG_ACK) { 452 TRACE("ps2: ps2_dev_command got ACK\n"); 453 } 454 455 if (atomic_get(&dev->flags) & PS2_FLAG_NACK) { 456 atomic_and(&dev->flags, ~PS2_FLAG_CMD); 457 res = B_IO_ERROR; 458 TRACE("ps2: ps2_dev_command got NACK\n"); 459 } 460 461 if (res != B_OK) 462 break; 463 } 464 465 if (res == B_OK) { 466 if (in_count == 0) { 467 atomic_and(&dev->flags, ~PS2_FLAG_CMD); 468 } else { 469 #ifdef TRACE_PS2_DEV 470 start = system_time(); 471 #endif 472 res = acquire_sem_etc(dev->result_sem, 1, B_RELATIVE_TIMEOUT, 473 timeout); 474 475 atomic_and(&dev->flags, ~PS2_FLAG_CMD); 476 477 if (dev->result_buf_cnt != 0) { 478 TRACE("ps2: ps2_dev_command error: %d rx bytes not received\n", 479 dev->result_buf_cnt); 480 in_count -= dev->result_buf_cnt; 481 dev->result_buf_cnt = 0; 482 res = B_IO_ERROR; 483 } 484 485 #ifdef TRACE_PS2_DEV 486 TRACE("ps2: ps2_dev_command wait for input res 0x%08" B_PRIx32 ", " 487 "wait-time %" B_PRId64 "\n", res, system_time() - start); 488 for (i = 0; i < in_count; i++) 489 TRACE("ps2: ps2_dev_command rx: 0x%02x\n", in[i]); 490 #endif 491 } 492 } 493 494 TRACE("ps2: ps2_dev_command result: %s\n", strerror(res)); 495 496 return res; 497 } 498 499 500 status_t 501 ps2_dev_command(ps2_dev* dev, uint8 cmd, const uint8* out, int out_count, 502 uint8* in, int in_count) 503 { 504 return ps2_dev_command_timeout(dev, cmd, out, out_count, in, in_count, 505 4000000); 506 } 507 508 509 status_t 510 ps2_dev_command_timeout(ps2_dev* dev, uint8 cmd, const uint8* out, 511 int out_count, uint8* in, int in_count, bigtime_t timeout) 512 { 513 return dev->command(dev, cmd, out, out_count, in, in_count, timeout); 514 } 515 516 517 status_t 518 ps2_dev_sliced_command(ps2_dev* dev, uint8 cmd) 519 { 520 uint8 val; 521 if (ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE11) != B_OK) 522 return B_ERROR; 523 524 val = (cmd >> 6) & 3; 525 if (ps2_dev_command(dev, PS2_CMD_MOUSE_SET_RES, &val, 1) != B_OK) 526 return B_ERROR; 527 528 val = (cmd >> 4) & 3; 529 if (ps2_dev_command(dev, PS2_CMD_MOUSE_SET_RES, &val, 1) != B_OK) 530 return B_ERROR; 531 532 val = (cmd >> 2) & 3; 533 if (ps2_dev_command(dev, PS2_CMD_MOUSE_SET_RES, &val, 1) != B_OK) 534 return B_ERROR; 535 536 val = cmd & 3; 537 if (ps2_dev_command(dev, PS2_CMD_MOUSE_SET_RES, &val, 1) != B_OK) 538 return B_ERROR; 539 540 return B_OK; 541 } 542