1 /* 2 * Copyright 2001-2010 Haiku, Inc. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors (in chronological order): 6 * Elad Lahav (elad@eldarshany.com) 7 * Stefano Ceccherini (burton666@libero.it) 8 * Axel Dörfler, axeld@pinc-software.de 9 * Marcus Overhagen <marcus@overhagen.de> 10 * Clemens Zeidler <czeidler@gmx.de> 11 */ 12 13 14 /*! PS/2 mouse device driver 15 16 A PS/2 mouse is connected to the IBM 8042 controller, and gets its 17 name from the IBM PS/2 personal computer, which was the first to 18 use this device. All resources are shared between the keyboard, and 19 the mouse, referred to as the "Auxiliary Device". 20 21 I/O: 22 ~~~ 23 The controller has 3 I/O registers: 24 1. Status (input), mapped to port 64h 25 2. Control (output), mapped to port 64h 26 3. Data (input/output), mapped to port 60h 27 28 Data: 29 ~~~~ 30 A packet read from the mouse data port is composed of 31 three bytes: 32 byte 0: status byte, where 33 - bit 7: Y overflow (1 = true) 34 - bit 6: X overflow (1 = true) 35 - bit 5: MSB of Y offset 36 - bit 4: MSB of X offset 37 - bit 3: Syncronization bit (always 1) 38 - bit 2: Middle button (1 = down) 39 - bit 1: Right button (1 = down) 40 - bit 0: Left button (1 = down) 41 byte 1: X position change, since last probed (-127 to +127) 42 byte 2: Y position change, since last probed (-127 to +127) 43 44 Intellimouse mice send a four byte packet, where the first three 45 bytes are the same as standard mice, and the last one reports the 46 Z position, which is, usually, the wheel movement. 47 48 Interrupts: 49 ~~~~~~~~~~ 50 The PS/2 mouse device is connected to interrupt 12. 51 The controller uses 3 consecutive interrupts to inform the computer 52 that it has new data. On the first the data register holds the status 53 byte, on the second the X offset, and on the 3rd the Y offset. 54 */ 55 56 57 #include <stdlib.h> 58 #include <string.h> 59 60 #include <keyboard_mouse_driver.h> 61 62 #include "ps2_service.h" 63 #include "ps2_standard_mouse.h" 64 65 66 const char* kStandardMousePath[4] = { 67 "input/mouse/ps2/standard_0", 68 "input/mouse/ps2/standard_1", 69 "input/mouse/ps2/standard_2", 70 "input/mouse/ps2/standard_3" 71 }; 72 const char* kIntelliMousePath[4] = { 73 "input/mouse/ps2/intelli_0", 74 "input/mouse/ps2/intelli_1", 75 "input/mouse/ps2/intelli_2", 76 "input/mouse/ps2/intelli_3" 77 }; 78 79 80 /*! Set sampling rate of the ps2 port. 81 */ 82 static inline status_t 83 ps2_set_sample_rate(ps2_dev *dev, uint8 rate) 84 { 85 return ps2_dev_command(dev, PS2_CMD_SET_SAMPLE_RATE, &rate, 1, NULL, 0); 86 } 87 88 89 /*! Converts a packet received by the mouse to a "movement". 90 */ 91 static void 92 ps2_packet_to_movement(standard_mouse_cookie *cookie, uint8 packet[], 93 mouse_movement *pos) 94 { 95 int buttons = packet[0] & 7; 96 int xDelta = ((packet[0] & 0x10) ? ~0xff : 0) | packet[1]; 97 int yDelta = ((packet[0] & 0x20) ? ~0xff : 0) | packet[2]; 98 int xDeltaWheel = 0; 99 int yDeltaWheel = 0; 100 bigtime_t currentTime = system_time(); 101 102 if (buttons != 0 && cookie->buttons_state == 0) { 103 if (cookie->click_last_time + cookie->click_speed > currentTime) 104 cookie->click_count++; 105 else 106 cookie->click_count = 1; 107 108 cookie->click_last_time = currentTime; 109 } 110 111 cookie->buttons_state = buttons; 112 113 if (cookie->flags & F_MOUSE_TYPE_INTELLIMOUSE) { 114 yDeltaWheel = packet[3] & 0x07; 115 if (packet[3] & 0x08) 116 yDeltaWheel |= ~0x07; 117 } 118 /* 119 if (cookie->flags & F_MOUSE_TYPE_2WHEELS) { 120 switch (packet[3] & 0x0F) { 121 case 0x01: yDeltaWheel = +1; break; // wheel 1 down 122 case 0x0F: yDeltaWheel = -1; break; // wheel 1 up 123 case 0x02: xDeltaWheel = +1; break; // wheel 2 down 124 case 0x0E: xDeltaWheel = -1; break; // wheel 2 up 125 } 126 } 127 */ 128 129 // TRACE("packet: %02x %02x %02x %02x: xd %d, yd %d, 0x%x (%d), w-xd %d, " 130 // "w-yd %d\n", packet[0], packet[1], packet[2], packet[3], 131 // xDelta, yDelta, buttons, cookie->click_count, xDeltaWheel, 132 // yDeltaWheel); 133 134 if (pos) { 135 pos->xdelta = xDelta; 136 pos->ydelta = yDelta; 137 pos->buttons = buttons; 138 pos->clicks = cookie->click_count; 139 pos->modifiers = 0; 140 pos->timestamp = currentTime; 141 pos->wheel_ydelta = yDeltaWheel; 142 pos->wheel_xdelta = xDeltaWheel; 143 144 TRACE("ps2: ps2_packet_to_movement xdelta: %d, ydelta: %d, buttons %x, " 145 "clicks: %d, timestamp %Ld\n", 146 xDelta, yDelta, buttons, cookie->click_count, currentTime); 147 } 148 } 149 150 151 /*! Read a mouse event from the mouse events chain buffer. 152 */ 153 static status_t 154 standard_mouse_read_event(standard_mouse_cookie *cookie, 155 mouse_movement *movement) 156 { 157 uint8 packet[PS2_MAX_PACKET_SIZE]; 158 status_t status; 159 160 TRACE("ps2: standard_mouse_read_event\n"); 161 162 status = acquire_sem_etc(cookie->standard_mouse_sem, 1, B_CAN_INTERRUPT, 0); 163 TRACE("ps2: standard_mouse_read_event acquired\n"); 164 if (status < B_OK) 165 return status; 166 167 if (!cookie->dev->active) { 168 TRACE("ps2: standard_mouse_read_event: Error device no longer " 169 "active\n"); 170 return B_ERROR; 171 } 172 173 if (packet_buffer_read(cookie->standard_mouse_buffer, packet, 174 cookie->dev->packet_size) != cookie->dev->packet_size) { 175 TRACE("ps2: error copying buffer\n"); 176 return B_ERROR; 177 } 178 179 if (!(packet[0] & 8)) 180 panic("ps2: got broken data from packet_buffer_read\n"); 181 182 ps2_packet_to_movement(cookie, packet, movement); 183 return B_OK; 184 } 185 186 187 // #pragma mark - 188 189 190 void 191 standard_mouse_disconnect(ps2_dev *dev) 192 { 193 // the mouse device might not be opened at this point 194 INFO("ps2: ps2_standard_mouse_disconnect %s\n", dev->name); 195 if (dev->flags & PS2_FLAG_OPEN) 196 release_sem(((standard_mouse_cookie *)dev->cookie)->standard_mouse_sem); 197 } 198 199 200 /*! Interrupt handler for the mouse device. Called whenever the I/O 201 controller generates an interrupt for the PS/2 mouse. Reads mouse 202 information from the data port, and stores it, so it can be accessed 203 by read() operations. The full data is obtained using 3 consecutive 204 calls to the handler, each holds a different byte on the data port. 205 */ 206 int32 207 standard_mouse_handle_int(ps2_dev *dev) 208 { 209 standard_mouse_cookie *cookie = (standard_mouse_cookie*)dev->cookie; 210 const uint8 data = dev->history[0].data; 211 212 TRACE("ps2: standard mouse: %1x\t%1x\t%1x\t%1x\t%1x\t%1x\t%1x\t%1x\n", 213 data >> 7 & 1, data >> 6 & 1, data >> 5 & 1, 214 data >> 4 & 1, data >> 3 & 1, data >> 2 & 1, 215 data >> 1 & 1, data >> 0 & 1); 216 217 if (cookie->packet_index == 0 && !(data & 8)) { 218 INFO("ps2: bad mouse data, trying resync\n"); 219 return B_HANDLED_INTERRUPT; 220 } 221 222 // Workarounds for active multiplexing keyboard controllers 223 // that lose data or send them to the wrong port. 224 if (cookie->packet_index == 0 && (data & 0xc0)) { 225 INFO("ps2: strange mouse data, x/y overflow, trying resync\n"); 226 return B_HANDLED_INTERRUPT; 227 } 228 if (cookie->packet_index == 1) { 229 int xDelta 230 = ((cookie->buffer[0] & 0x10) ? 0xFFFFFF00 : 0) | data; 231 if (xDelta < -100 || xDelta > 100) { 232 INFO("ps2: strange mouse data, x-delta %d, trying resync\n", 233 xDelta); 234 cookie->packet_index = 0; 235 return B_HANDLED_INTERRUPT; 236 } 237 } 238 if (cookie->packet_index == 2) { 239 int yDelta 240 = ((cookie->buffer[0] & 0x20) ? 0xFFFFFF00 : 0) | data; 241 if (yDelta < -100 || yDelta > 100) { 242 INFO("ps2: strange mouse data, y-delta %d, trying resync\n", 243 yDelta); 244 cookie->packet_index = 0; 245 return B_HANDLED_INTERRUPT; 246 } 247 } 248 249 cookie->buffer[cookie->packet_index++] = data; 250 251 if (cookie->packet_index != dev->packet_size) { 252 // packet not yet complete 253 return B_HANDLED_INTERRUPT; 254 } 255 256 // complete packet is assembled 257 258 cookie->packet_index = 0; 259 if (packet_buffer_write(cookie->standard_mouse_buffer, 260 cookie->buffer, dev->packet_size) != dev->packet_size) { 261 // buffer is full, drop new data 262 return B_HANDLED_INTERRUPT; 263 } 264 265 release_sem_etc(cookie->standard_mouse_sem, 1, B_DO_NOT_RESCHEDULE); 266 return B_INVOKE_SCHEDULER; 267 } 268 269 270 // #pragma mark - 271 272 273 status_t 274 probe_standard_mouse(ps2_dev * dev) 275 { 276 status_t status; 277 uint8 deviceId = 0; 278 279 // get device id 280 status = ps2_dev_command(dev, PS2_CMD_GET_DEVICE_ID, NULL, 0, 281 &deviceId, 1); 282 if (status != B_OK) { 283 INFO("ps2: probe_mouse get device id failed\n"); 284 return B_ERROR; 285 } 286 287 TRACE("ps2: probe_mouse device id: %2x\n", deviceId); 288 289 // check for MS Intellimouse 290 if (deviceId == 0) { 291 uint8 alternate_device_id; 292 status = ps2_set_sample_rate(dev, 200); 293 status |= ps2_set_sample_rate(dev, 100); 294 status |= ps2_set_sample_rate(dev, 80); 295 status |= ps2_dev_command(dev, PS2_CMD_GET_DEVICE_ID, NULL, 0, 296 &alternate_device_id, 1); 297 if (status == 0) { 298 TRACE("ps2: probe_mouse alternate device id: %2x\n", 299 alternate_device_id); 300 deviceId = alternate_device_id; 301 } 302 } 303 304 if (deviceId == PS2_DEV_ID_STANDARD 305 || deviceId == PS2_DEV_ID_TOUCHPAD_RICATECH) { 306 INFO("ps2: probe_mouse Standard PS/2 mouse found\n"); 307 dev->name = kStandardMousePath[dev->idx]; 308 dev->packet_size = PS2_PACKET_STANDARD; 309 } else if (deviceId == PS2_DEV_ID_INTELLIMOUSE) { 310 dev->name = kIntelliMousePath[dev->idx]; 311 dev->packet_size = PS2_PACKET_INTELLIMOUSE; 312 INFO("ps2: probe_mouse Extended PS/2 mouse found\n"); 313 } else { 314 INFO("ps2: probe_mouse Error unknown device id.\n"); 315 return B_ERROR; 316 } 317 318 return B_OK; 319 } 320 321 322 // #pragma mark - Device functions 323 324 325 status_t 326 standard_mouse_open(const char *name, uint32 flags, void **_cookie) 327 { 328 standard_mouse_cookie *cookie; 329 status_t status; 330 ps2_dev *dev = NULL; 331 int i; 332 333 TRACE("ps2: standard_mouse_open %s\n", name); 334 335 for (dev = NULL, i = 0; i < PS2_DEVICE_COUNT; i++) { 336 if (0 == strcmp(ps2_device[i].name, name)) { 337 dev = &ps2_device[i]; 338 break; 339 } 340 /*if (0 == strcmp(g_passthrough_dev.name, name)) { 341 dev = &g_passthrough_dev; 342 isSynapticsPTDevice = true; 343 break; 344 }*/ 345 } 346 347 if (dev == NULL) { 348 TRACE("ps2: dev = NULL\n"); 349 return B_ERROR; 350 } 351 352 if (atomic_or(&dev->flags, PS2_FLAG_OPEN) & PS2_FLAG_OPEN) 353 return B_BUSY; 354 355 cookie = (standard_mouse_cookie*)malloc(sizeof(standard_mouse_cookie)); 356 if (cookie == NULL) 357 goto err1; 358 359 *_cookie = cookie; 360 memset(cookie, 0, sizeof(*cookie)); 361 362 cookie->dev = dev; 363 dev->cookie = cookie; 364 dev->disconnect = &standard_mouse_disconnect; 365 dev->handle_int = &standard_mouse_handle_int; 366 367 if (strstr(dev->name, "standard") != NULL) 368 cookie->flags = F_MOUSE_TYPE_STANDARD; 369 370 if (strstr(dev->name, "intelli") != NULL) 371 cookie->flags = F_MOUSE_TYPE_INTELLIMOUSE; 372 373 cookie->standard_mouse_buffer 374 = create_packet_buffer(MOUSE_HISTORY_SIZE * dev->packet_size); 375 if (cookie->standard_mouse_buffer == NULL) { 376 TRACE("ps2: can't allocate mouse actions buffer\n"); 377 goto err2; 378 } 379 380 // create the mouse semaphore, used for synchronization between 381 // the interrupt handler and the read operation 382 cookie->standard_mouse_sem = create_sem(0, "ps2_standard_mouse_sem"); 383 if (cookie->standard_mouse_sem < 0) { 384 TRACE("ps2: failed creating PS/2 mouse semaphore!\n"); 385 goto err3; 386 } 387 388 status = ps2_dev_command(dev, PS2_CMD_ENABLE, NULL, 0, NULL, 0); 389 if (status < B_OK) { 390 INFO("ps2: cannot enable mouse %s\n", name); 391 goto err4; 392 } 393 394 atomic_or(&dev->flags, PS2_FLAG_ENABLED); 395 396 397 TRACE("ps2: standard_mouse_open %s success\n", name); 398 return B_OK; 399 400 err4: 401 delete_sem(cookie->standard_mouse_sem); 402 err3: 403 delete_packet_buffer(cookie->standard_mouse_buffer); 404 err2: 405 free(cookie); 406 err1: 407 atomic_and(&dev->flags, ~PS2_FLAG_OPEN); 408 409 TRACE("ps2: standard_mouse_open %s failed\n", name); 410 return B_ERROR; 411 } 412 413 414 status_t 415 standard_mouse_close(void *_cookie) 416 { 417 standard_mouse_cookie *cookie = (standard_mouse_cookie*)_cookie; 418 419 TRACE("ps2: standard_mouse_close %s enter\n", cookie->dev->name); 420 421 ps2_dev_command_timeout(cookie->dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0, 422 150000); 423 424 delete_packet_buffer(cookie->standard_mouse_buffer); 425 delete_sem(cookie->standard_mouse_sem); 426 427 atomic_and(&cookie->dev->flags, ~PS2_FLAG_OPEN); 428 atomic_and(&cookie->dev->flags, ~PS2_FLAG_ENABLED); 429 430 TRACE("ps2: standard_mouse_close %s done\n", cookie->dev->name); 431 return B_OK; 432 } 433 434 435 status_t 436 standard_mouse_freecookie(void *_cookie) 437 { 438 standard_mouse_cookie *cookie = (standard_mouse_cookie*)_cookie; 439 free(cookie); 440 return B_OK; 441 } 442 443 444 static status_t 445 standard_mouse_read(void *cookie, off_t pos, void *buffer, size_t *_length) 446 { 447 *_length = 0; 448 return B_NOT_ALLOWED; 449 } 450 451 452 static status_t 453 standard_mouse_write(void *cookie, off_t pos, const void *buffer, 454 size_t *_length) 455 { 456 *_length = 0; 457 return B_NOT_ALLOWED; 458 } 459 460 461 status_t 462 standard_mouse_ioctl(void *_cookie, uint32 op, void *buffer, size_t length) 463 { 464 standard_mouse_cookie *cookie = (standard_mouse_cookie*)_cookie; 465 466 switch (op) { 467 case MS_NUM_EVENTS: 468 { 469 int32 count; 470 TRACE("ps2: ioctl MS_NUM_EVENTS\n"); 471 get_sem_count(cookie->standard_mouse_sem, &count); 472 return count; 473 } 474 475 case MS_READ: 476 { 477 mouse_movement movement; 478 status_t status; 479 TRACE("ps2: ioctl MS_READ\n"); 480 if ((status = standard_mouse_read_event(cookie, &movement)) < B_OK) 481 return status; 482 // TRACE("%s %d %d %d %d\n", cookie->dev->name, 483 // movement.xdelta, movement.ydelta, movement.buttons, 484 // movement.clicks); 485 return user_memcpy(buffer, &movement, sizeof(movement)); 486 } 487 488 case MS_SET_TYPE: 489 TRACE("ps2: ioctl MS_SET_TYPE not implemented\n"); 490 return B_BAD_VALUE; 491 492 case MS_SET_MAP: 493 TRACE("ps2: ioctl MS_SET_MAP (set mouse mapping) not " 494 "implemented\n"); 495 return B_BAD_VALUE; 496 497 case MS_GET_ACCEL: 498 TRACE("ps2: ioctl MS_GET_ACCEL (get mouse acceleration) not " 499 "implemented\n"); 500 return B_BAD_VALUE; 501 502 case MS_SET_ACCEL: 503 TRACE("ps2: ioctl MS_SET_ACCEL (set mouse acceleration) not " 504 "implemented\n"); 505 return B_BAD_VALUE; 506 507 case MS_SET_CLICKSPEED: 508 TRACE("ps2: ioctl MS_SETCLICK (set click speed)\n"); 509 return user_memcpy(&cookie->click_speed, buffer, sizeof(bigtime_t)); 510 511 default: 512 TRACE("ps2: ioctl unknown mouse opcode: %ld\n", op); 513 return B_DEV_INVALID_IOCTL; 514 } 515 } 516 517 518 device_hooks gStandardMouseDeviceHooks = { 519 standard_mouse_open, 520 standard_mouse_close, 521 standard_mouse_freecookie, 522 standard_mouse_ioctl, 523 standard_mouse_read, 524 standard_mouse_write, 525 }; 526