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