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