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