1 /* 2 * Copyright 2008-2010, Haiku, Inc. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors (in chronological order): 6 * Clemens Zeidler (haiku@Clemens-Zeidler.de) 7 * Axel Dörfler, axeld@pinc-software.de 8 */ 9 10 11 //! PS/2 synaptics touchpad 12 13 14 #include "ps2_synaptics.h" 15 16 #include <string.h> 17 #include <stdlib.h> 18 19 #include <keyboard_mouse_driver.h> 20 21 #include "ps2_service.h" 22 23 24 // synaptics touchpad proportions 25 #define SYN_EDGE_MOTION_WIDTH 50 26 #define SYN_AREA_OFFSET 40 27 28 #define MIN_PRESSURE 30 29 #define REAL_MAX_PRESSURE 100 30 #define MAX_PRESSURE 200 31 32 33 static hardware_specs gHardwareSpecs; 34 35 36 const char* kSynapticsPath[4] = { 37 "input/touchpad/ps2/synaptics_0", 38 "input/touchpad/ps2/synaptics_1", 39 "input/touchpad/ps2/synaptics_2", 40 "input/touchpad/ps2/synaptics_3" 41 }; 42 43 44 static touchpad_info sTouchpadInfo; 45 static ps2_dev *sPassthroughDevice = &ps2_device[PS2_DEVICE_SYN_PASSTHROUGH]; 46 47 48 static void 49 default_settings(touchpad_settings *set) 50 { 51 memcpy(set, &kDefaultTouchpadSettings, sizeof(touchpad_settings)); 52 } 53 54 55 static status_t 56 send_touchpad_arg_timeout(ps2_dev *dev, uint8 arg, bigtime_t timeout) 57 { 58 int8 i; 59 uint8 val[8]; 60 61 for (i = 0; i < 4; i++) { 62 val[2 * i] = (arg >> (6 - 2 * i)) & 3; 63 val[2 * i + 1] = 0xE8; 64 } 65 return ps2_dev_command_timeout(dev, 0xE8, val, 7, NULL, 0, timeout); 66 } 67 68 69 static status_t 70 send_touchpad_arg(ps2_dev *dev, uint8 arg) 71 { 72 return send_touchpad_arg_timeout(dev, arg, 4000000); 73 } 74 75 76 static status_t 77 set_touchpad_mode(ps2_dev *dev, uint8 mode) 78 { 79 uint8 sample_rate = SYN_CHANGE_MODE; 80 send_touchpad_arg(dev, mode); 81 return ps2_dev_command(dev, PS2_CMD_SET_SAMPLE_RATE, &sample_rate, 1, 82 NULL, 0); 83 } 84 85 86 static status_t 87 get_synaptics_movment(synaptics_cookie *cookie, mouse_movement *movement) 88 { 89 status_t status; 90 touch_event event; 91 uint8 event_buffer[PS2_MAX_PACKET_SIZE]; 92 uint8 wValue0, wValue1, wValue2, wValue3, wValue; 93 uint32 val32; 94 uint32 xTwelfBit, yTwelfBit; 95 96 status = acquire_sem_etc(cookie->synaptics_sem, 1, B_CAN_INTERRUPT, 0); 97 if (status < B_OK) 98 return status; 99 100 if (!cookie->dev->active) { 101 TRACE("SYNAPTICS: read_event: Error device no longer active\n"); 102 return B_ERROR; 103 } 104 105 if (packet_buffer_read(cookie->synaptics_ring_buffer, event_buffer, 106 cookie->dev->packet_size) != cookie->dev->packet_size) { 107 TRACE("SYNAPTICS: error copying buffer\n"); 108 return B_ERROR; 109 } 110 111 event.buttons = event_buffer[0] & 3; 112 event.zPressure = event_buffer[2]; 113 114 if (sTouchpadInfo.capExtended) { 115 wValue0 = event_buffer[3] >> 2 & 1; 116 wValue1 = event_buffer[0] >> 2 & 1; 117 wValue2 = event_buffer[0] >> 4 & 1; 118 wValue3 = event_buffer[0] >> 5 & 1; 119 120 wValue = wValue0; 121 wValue = wValue | (wValue1 << 1); 122 wValue = wValue | (wValue2 << 2); 123 wValue = wValue | (wValue3 << 3); 124 125 event.wValue = wValue; 126 event.gesture = false; 127 128 if (sTouchpadInfo.capMiddleButton || sTouchpadInfo.capFourButtons) 129 event.buttons |= ((event_buffer[0] ^ event_buffer[3]) & 0x01) << 2; 130 } else { 131 bool finger = event_buffer[0] >> 5 & 1; 132 if (finger) { 133 // finger with normal width 134 event.wValue = 4; 135 } 136 event.gesture = event_buffer[0] >> 2 & 1; 137 } 138 139 event.xPosition = event_buffer[4]; 140 event.yPosition = event_buffer[5]; 141 142 val32 = event_buffer[1] & 0x0F; 143 event.xPosition += val32 << 8; 144 val32 = event_buffer[1] >> 4 & 0x0F; 145 event.yPosition += val32 << 8; 146 147 xTwelfBit = event_buffer[3] >> 4 & 1; 148 event.xPosition += xTwelfBit << 12; 149 yTwelfBit = event_buffer[3] >> 5 & 1; 150 event.yPosition += yTwelfBit << 12; 151 152 status = cookie->movementMaker.EventToMovement(&event, movement); 153 154 return status; 155 } 156 157 158 static void 159 query_capability(ps2_dev *dev) 160 { 161 uint8 val[3]; 162 send_touchpad_arg(dev, 0x02); 163 ps2_dev_command(dev, 0xE9, NULL, 0, val, 3); 164 165 sTouchpadInfo.capExtended = val[0] >> 7 & 1; 166 TRACE("SYNAPTICS: extended mode %2x\n", val[0] >> 7 & 1); 167 TRACE("SYNAPTICS: middle button %2x\n", val[0] >> 2 & 1); 168 sTouchpadInfo.capMiddleButton = val[0] >> 2 & 1; 169 TRACE("SYNAPTICS: sleep mode %2x\n", val[2] >> 4 & 1); 170 sTouchpadInfo.capSleep = val[2] >> 4 & 1; 171 TRACE("SYNAPTICS: four buttons %2x\n", val[2] >> 3 & 1); 172 sTouchpadInfo.capFourButtons = val[2] >> 3 & 1; 173 TRACE("SYNAPTICS: multi finger %2x\n", val[2] >> 1 & 1); 174 sTouchpadInfo.capMultiFinger = val[2] >> 1 & 1; 175 TRACE("SYNAPTICS: palm detection %2x\n", val[2] & 1); 176 sTouchpadInfo.capPalmDetection = val[2] & 1; 177 TRACE("SYNAPTICS: pass through %2x\n", val[2] >> 7 & 1); 178 sTouchpadInfo.capPassThrough = val[2] >> 7 & 1; 179 } 180 181 182 // #pragma mark - exported functions 183 184 185 status_t 186 synaptics_pass_through_set_packet_size(ps2_dev *dev, uint8 size) 187 { 188 synaptics_cookie *synapticsCookie 189 = (synaptics_cookie*)dev->parent_dev->cookie; 190 191 status_t status = ps2_dev_command(dev->parent_dev, PS2_CMD_DISABLE, NULL, 192 0, NULL, 0); 193 if (status < B_OK) { 194 INFO("SYNAPTICS: cannot disable touchpad %s\n", dev->parent_dev->name); 195 return B_ERROR; 196 } 197 198 synapticsCookie->packet_index = 0; 199 200 if (size == 4) 201 synapticsCookie->mode |= SYN_FOUR_BYTE_CHILD; 202 else 203 synapticsCookie->mode &= ~SYN_FOUR_BYTE_CHILD; 204 205 set_touchpad_mode(dev->parent_dev, synapticsCookie->mode); 206 207 status = ps2_dev_command(dev->parent_dev, PS2_CMD_ENABLE, NULL, 0, NULL, 0); 208 if (status < B_OK) { 209 INFO("SYNAPTICS: cannot enable touchpad %s\n", dev->parent_dev->name); 210 return B_ERROR; 211 } 212 return status; 213 } 214 215 216 status_t 217 passthrough_command(ps2_dev *dev, uint8 cmd, const uint8 *out, int outCount, 218 uint8 *in, int inCount, bigtime_t timeout) 219 { 220 status_t status; 221 uint8 passThroughCmd = SYN_PASSTHROUGH_CMD; 222 uint8 val; 223 uint32 passThroughInCount = (inCount + 1) * 6; 224 uint8 passThroughIn[passThroughInCount]; 225 int8 i; 226 227 TRACE("SYNAPTICS: passthrough command 0x%x\n", cmd); 228 229 status = ps2_dev_command(dev->parent_dev, PS2_CMD_DISABLE, NULL, 0, 230 NULL, 0); 231 if (status != B_OK) 232 return status; 233 234 for (i = -1; i < outCount; i++) { 235 if (i == -1) 236 val = cmd; 237 else 238 val = out[i]; 239 status = send_touchpad_arg_timeout(dev->parent_dev, val, timeout); 240 if (status != B_OK) 241 goto finalize; 242 if (i != outCount -1) { 243 status = ps2_dev_command_timeout(dev->parent_dev, 244 PS2_CMD_SET_SAMPLE_RATE, &passThroughCmd, 1, NULL, 0, timeout); 245 if (status != B_OK) 246 goto finalize; 247 } 248 } 249 status = ps2_dev_command_timeout(dev->parent_dev, PS2_CMD_SET_SAMPLE_RATE, 250 &passThroughCmd, 1, passThroughIn, passThroughInCount, timeout); 251 if (status != B_OK) 252 goto finalize; 253 254 for (i = 0; i < inCount + 1; i++) { 255 uint8 *inPointer = &passThroughIn[i * 6]; 256 if (!IS_SYN_PT_PACKAGE(inPointer)) { 257 TRACE("SYNAPTICS: not a pass throught package\n"); 258 goto finalize; 259 } 260 if (i == 0) 261 continue; 262 263 in[i - 1] = passThroughIn[i * 6 + 1]; 264 } 265 266 finalize: 267 status_t statusOfEnable = ps2_dev_command(dev->parent_dev, PS2_CMD_ENABLE, 268 NULL, 0, NULL, 0); 269 if (statusOfEnable != B_OK) 270 TRACE("SYNAPTICS: enabling of parent failed: 0x%lx.\n", statusOfEnable); 271 272 return status != B_OK ? status : statusOfEnable; 273 } 274 275 276 status_t 277 probe_synaptics(ps2_dev *dev) 278 { 279 uint8 val[3]; 280 uint8 deviceId; 281 status_t status; 282 TRACE("SYNAPTICS: probe\n"); 283 284 // We reset the device here because it may have been left in a confused 285 // state by a previous probing attempt. Some synaptics touchpads are known 286 // to lockup when we attempt to detect them as IBM trackpoints. 287 ps2_reset_mouse(dev); 288 289 // Request "Identify touchpad" 290 // The touchpad will delay this, until it's ready and calibrated. 291 status = send_touchpad_arg(dev, 0x00); 292 if (status != B_OK) 293 return status; 294 295 // "Status request" (executes "Identify touchpad") 296 status = ps2_dev_command(dev, 0xE9, NULL, 0, val, 3); 297 if (status != B_OK) 298 return status; 299 300 sTouchpadInfo.minorVersion = val[0]; 301 deviceId = val[1]; 302 if (deviceId != SYN_TOUCHPAD) { 303 TRACE("SYNAPTICS: not found\n"); 304 return B_ERROR; 305 } 306 307 TRACE("SYNAPTICS: Touchpad found id:l %2x\n", deviceId); 308 sTouchpadInfo.majorVersion = val[2] & 0x0F; 309 TRACE("SYNAPTICS: version %d.%d\n", sTouchpadInfo.majorVersion, 310 sTouchpadInfo.minorVersion); 311 // version >= 4.0? 312 if (sTouchpadInfo.minorVersion <= 2 313 && sTouchpadInfo.majorVersion <= 3) { 314 TRACE("SYNAPTICS: too old touchpad not supported\n"); 315 return B_ERROR; 316 } 317 dev->name = kSynapticsPath[dev->idx]; 318 return B_OK; 319 } 320 321 322 // #pragma mark - Device functions 323 324 325 status_t 326 synaptics_open(const char *name, uint32 flags, void **_cookie) 327 { 328 status_t status; 329 synaptics_cookie *cookie; 330 ps2_dev *dev; 331 int i; 332 333 for (dev = NULL, i = 0; i < PS2_DEVICE_COUNT; i++) { 334 if (0 == strcmp(ps2_device[i].name, name)) { 335 dev = &ps2_device[i]; 336 break; 337 } 338 } 339 340 if (dev == NULL) { 341 TRACE("ps2: dev = NULL\n"); 342 return B_ERROR; 343 } 344 345 if (atomic_or(&dev->flags, PS2_FLAG_OPEN) & PS2_FLAG_OPEN) 346 return B_BUSY; 347 348 cookie = (synaptics_cookie*)malloc(sizeof(synaptics_cookie)); 349 if (cookie == NULL) 350 goto err1; 351 memset(cookie, 0, sizeof(*cookie)); 352 353 cookie->movementMaker.Init(); 354 *_cookie = cookie; 355 356 cookie->dev = dev; 357 dev->cookie = cookie; 358 dev->disconnect = &synaptics_disconnect; 359 dev->handle_int = &synaptics_handle_int; 360 361 default_settings(&cookie->settings); 362 363 gHardwareSpecs.edgeMotionWidth = SYN_EDGE_MOTION_WIDTH; 364 365 gHardwareSpecs.areaStartX = SYN_AREA_START_X; 366 gHardwareSpecs.areaEndX = SYN_AREA_END_X; 367 gHardwareSpecs.areaStartY = SYN_AREA_START_Y; 368 gHardwareSpecs.areaEndY = SYN_AREA_END_Y; 369 370 gHardwareSpecs.minPressure = MIN_PRESSURE; 371 gHardwareSpecs.realMaxPressure = REAL_MAX_PRESSURE; 372 gHardwareSpecs.maxPressure = MAX_PRESSURE; 373 374 cookie->movementMaker.SetSettings(&cookie->settings); 375 cookie->movementMaker.SetSpecs(&gHardwareSpecs); 376 377 dev->packet_size = PS2_PACKET_SYNAPTICS; 378 379 cookie->synaptics_ring_buffer 380 = create_packet_buffer(SYNAPTICS_HISTORY_SIZE * dev->packet_size); 381 if (cookie->synaptics_ring_buffer == NULL) { 382 TRACE("ps2: can't allocate mouse actions buffer\n"); 383 goto err2; 384 } 385 386 // create the mouse semaphore, used for synchronization between 387 // the interrupt handler and the read operation 388 cookie->synaptics_sem = create_sem(0, "ps2_synaptics_sem"); 389 if (cookie->synaptics_sem < 0) { 390 TRACE("SYNAPTICS: failed creating semaphore!\n"); 391 goto err3; 392 } 393 query_capability(dev); 394 395 // create pass through dev 396 if (sTouchpadInfo.capPassThrough) { 397 TRACE("SYNAPTICS: pass through detected\n"); 398 sPassthroughDevice->parent_dev = dev; 399 sPassthroughDevice->idx = dev->idx; 400 ps2_service_notify_device_added(sPassthroughDevice); 401 } 402 403 // Set Mode 404 if (sTouchpadInfo.capExtended) 405 cookie->mode = SYN_ABSOLUTE_W_MODE; 406 else 407 cookie->mode = SYN_ABSOLUTE_MODE; 408 409 status = set_touchpad_mode(dev, cookie->mode); 410 if (status < B_OK) { 411 INFO("SYNAPTICS: cannot set mode %s\n", name); 412 goto err4; 413 } 414 415 status = ps2_dev_command(dev, PS2_CMD_ENABLE, NULL, 0, NULL, 0); 416 if (status < B_OK) { 417 INFO("SYNAPTICS: cannot enable touchpad %s\n", name); 418 goto err4; 419 } 420 421 atomic_or(&dev->flags, PS2_FLAG_ENABLED); 422 423 TRACE("SYNAPTICS: open %s success\n", name); 424 return B_OK; 425 426 err4: 427 delete_sem(cookie->synaptics_sem); 428 err3: 429 delete_packet_buffer(cookie->synaptics_ring_buffer); 430 err2: 431 free(cookie); 432 err1: 433 atomic_and(&dev->flags, ~PS2_FLAG_OPEN); 434 435 TRACE("SYNAPTICS: synaptics_open %s failed\n", name); 436 return B_ERROR; 437 } 438 439 440 status_t 441 synaptics_close(void *_cookie) 442 { 443 status_t status; 444 synaptics_cookie *cookie = (synaptics_cookie*)_cookie; 445 446 ps2_dev_command_timeout(cookie->dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0, 447 150000); 448 449 delete_packet_buffer(cookie->synaptics_ring_buffer); 450 delete_sem(cookie->synaptics_sem); 451 452 atomic_and(&cookie->dev->flags, ~PS2_FLAG_OPEN); 453 atomic_and(&cookie->dev->flags, ~PS2_FLAG_ENABLED); 454 455 // Reset the touchpad so it generate standard ps2 packets instead of 456 // extended ones. If not, BeOS is confused with such packets when rebooting 457 // without a complete shutdown. 458 status = ps2_reset_mouse(cookie->dev); 459 if (status != B_OK) { 460 INFO("ps2: reset failed\n"); 461 return B_ERROR; 462 } 463 464 if (sTouchpadInfo.capPassThrough) 465 ps2_service_notify_device_removed(sPassthroughDevice); 466 467 TRACE("SYNAPTICS: close %s done\n", cookie->dev->name); 468 return B_OK; 469 } 470 471 472 status_t 473 synaptics_freecookie(void *_cookie) 474 { 475 free(_cookie); 476 return B_OK; 477 } 478 479 480 static status_t 481 synaptics_read(void *cookie, off_t pos, void *buffer, size_t *_length) 482 { 483 *_length = 0; 484 return B_NOT_ALLOWED; 485 } 486 487 488 static status_t 489 synaptics_write(void *cookie, off_t pos, const void *buffer, size_t *_length) 490 { 491 *_length = 0; 492 return B_NOT_ALLOWED; 493 } 494 495 496 status_t 497 synaptics_ioctl(void *_cookie, uint32 op, void *buffer, size_t length) 498 { 499 synaptics_cookie *cookie = (synaptics_cookie*)_cookie; 500 mouse_movement movement; 501 status_t status; 502 503 switch (op) { 504 case MS_READ: 505 TRACE("SYNAPTICS: MS_READ get event\n"); 506 if ((status = get_synaptics_movment(cookie, &movement)) != B_OK) 507 return status; 508 return user_memcpy(buffer, &movement, sizeof(movement)); 509 510 case MS_IS_TOUCHPAD: 511 TRACE("SYNAPTICS: MS_IS_TOUCHPAD\n"); 512 return B_OK; 513 514 case MS_SET_TOUCHPAD_SETTINGS: 515 TRACE("SYNAPTICS: MS_SET_TOUCHPAD_SETTINGS"); 516 user_memcpy(&cookie->settings, buffer, sizeof(touchpad_settings)); 517 return B_OK; 518 519 case MS_SET_CLICKSPEED: 520 TRACE("SYNAPTICS: ioctl MS_SETCLICK (set click speed)\n"); 521 return user_memcpy(&cookie->movementMaker.click_speed, buffer, 522 sizeof(bigtime_t)); 523 524 default: 525 TRACE("SYNAPTICS: unknown opcode: %ld\n", op); 526 return B_DEV_INVALID_IOCTL; 527 } 528 } 529 530 531 int32 532 synaptics_handle_int(ps2_dev *dev) 533 { 534 synaptics_cookie *cookie = (synaptics_cookie*)dev->cookie; 535 uint8 val; 536 537 val = cookie->dev->history[0].data; 538 539 if ((cookie->packet_index == 0 || cookie->packet_index == 3) 540 && (val & 8) != 0) { 541 INFO("SYNAPTICS: bad mouse data, trying resync\n"); 542 cookie->packet_index = 0; 543 return B_UNHANDLED_INTERRUPT; 544 } 545 if (cookie->packet_index == 0 && val >> 6 != 0x02) { 546 TRACE("SYNAPTICS: first package begins not with bit 1, 0\n"); 547 return B_UNHANDLED_INTERRUPT; 548 } 549 if (cookie->packet_index == 3 && val >> 6 != 0x03) { 550 TRACE("SYNAPTICS: third package begins not with bit 1, 1\n"); 551 cookie->packet_index = 0; 552 return B_UNHANDLED_INTERRUPT; 553 } 554 cookie->buffer[cookie->packet_index] = val; 555 556 cookie->packet_index++; 557 if (cookie->packet_index >= 6) { 558 cookie->packet_index = 0; 559 560 // check if package is a pass through package if true pass it 561 // too the pass through interrupt handle 562 if (sPassthroughDevice->active 563 && sPassthroughDevice->handle_int != NULL 564 && IS_SYN_PT_PACKAGE(cookie->buffer)) { 565 status_t status; 566 567 sPassthroughDevice->history[0].data = cookie->buffer[1]; 568 sPassthroughDevice->handle_int(sPassthroughDevice); 569 sPassthroughDevice->history[0].data = cookie->buffer[4]; 570 sPassthroughDevice->handle_int(sPassthroughDevice); 571 sPassthroughDevice->history[0].data = cookie->buffer[5]; 572 status = sPassthroughDevice->handle_int(sPassthroughDevice); 573 574 if (cookie->dev->packet_size == 4) { 575 sPassthroughDevice->history[0].data = cookie->buffer[2]; 576 status = sPassthroughDevice->handle_int(sPassthroughDevice); 577 } 578 return status; 579 } 580 581 if (packet_buffer_write(cookie->synaptics_ring_buffer, 582 cookie->buffer, cookie->dev->packet_size) 583 != cookie->dev->packet_size) { 584 // buffer is full, drop new data 585 return B_HANDLED_INTERRUPT; 586 } 587 release_sem_etc(cookie->synaptics_sem, 1, B_DO_NOT_RESCHEDULE); 588 589 return B_INVOKE_SCHEDULER; 590 } 591 592 return B_HANDLED_INTERRUPT; 593 } 594 595 596 void 597 synaptics_disconnect(ps2_dev *dev) 598 { 599 synaptics_cookie *cookie = (synaptics_cookie*)dev->cookie; 600 // the mouse device might not be opened at this point 601 INFO("SYNAPTICS: synaptics_disconnect %s\n", dev->name); 602 if ((dev->flags & PS2_FLAG_OPEN) != 0) 603 release_sem(cookie->synaptics_sem); 604 } 605 606 607 device_hooks gSynapticsDeviceHooks = { 608 synaptics_open, 609 synaptics_close, 610 synaptics_freecookie, 611 synaptics_ioctl, 612 synaptics_read, 613 synaptics_write, 614 }; 615