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