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