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