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