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