1 /* 2 * Copyright 2011, Haiku, Inc. 3 * Distributed under the terms of the MIT License. 4 * 5 * The alps_model_info struct and all the hardware specs are taken from the 6 * linux driver, thanks a lot! 7 * 8 * Authors: 9 * Clemens Zeidler (haiku@Clemens-Zeidler.de) 10 */ 11 12 13 #include "ps2_alps.h" 14 15 #include <stdlib.h> 16 #include <string.h> 17 18 #include "ps2_service.h" 19 20 21 //#define TRACE_PS2_ALPS 22 #ifdef TRACE_PS2_APLS 23 # define TRACE(x...) dprintf(x) 24 #else 25 # define TRACE(x...) 26 #endif 27 28 29 static int32 generate_event(timer* timer); 30 31 32 const bigtime_t kEventInterval = 1000 * 50; 33 34 35 class EventProducer { 36 public: 37 EventProducer() 38 { 39 fFired = false; 40 } 41 42 status_t 43 FireEvent(alps_cookie* cookie, uint8* package) 44 { 45 fCookie = cookie; 46 memcpy(fLastPackage, package, sizeof(uint8) * PS2_PACKET_ALPS); 47 48 status_t status = add_timer(&fEventTimer, &generate_event, 49 kEventInterval, B_ONE_SHOT_RELATIVE_TIMER); 50 if (status == B_OK) 51 fFired = true; 52 return status; 53 } 54 55 bool 56 CancelEvent() 57 { 58 if (!fFired) 59 return false; 60 fFired = false; 61 return cancel_timer(&fEventTimer); 62 } 63 64 void 65 InjectEvent() 66 { 67 if (packet_buffer_write(fCookie->ring_buffer, fLastPackage, 68 PS2_PACKET_ALPS) != PS2_PACKET_ALPS) { 69 // buffer is full, drop new data 70 return; 71 } 72 release_sem_etc(fCookie->sem, 1, B_DO_NOT_RESCHEDULE); 73 } 74 75 private: 76 bool fFired; 77 uint8 fLastPackage[PS2_PACKET_ALPS]; 78 timer fEventTimer; 79 alps_cookie* fCookie; 80 }; 81 82 83 static EventProducer gEventProducer; 84 85 86 static int32 87 generate_event(timer* timer) 88 { 89 gEventProducer.InjectEvent(); 90 return B_HANDLED_INTERRUPT; 91 } 92 93 94 const char* kALPSPath[4] = { 95 "input/touchpad/ps2/alps_0", 96 "input/touchpad/ps2/alps_1", 97 "input/touchpad/ps2/alps_2", 98 "input/touchpad/ps2/alps_3" 99 }; 100 101 102 typedef struct alps_model_info { 103 uint8 id[3]; 104 uint8 firstByte; 105 uint8 maskFirstByte; 106 uint8 flags; 107 } alps_model_info; 108 109 110 #define ALPS_OLDPROTO 0x01 // old style input 111 #define ALPS_DUALPOINT 0x02 // touchpad has trackstick 112 #define ALPS_PASS 0x04 // device has a pass-through port 113 114 #define ALPS_WHEEL 0x08 // hardware wheel present 115 #define ALPS_FW_BK_1 0x10 // front & back buttons present 116 #define ALPS_FW_BK_2 0x20 // front & back buttons present 117 #define ALPS_FOUR_BUTTONS 0x40 // 4 direction button present 118 #define ALPS_PS2_INTERLEAVED 0x80 // 3-byte PS/2 packet interleaved with 119 // 6-byte ALPS packet 120 121 static const struct alps_model_info gALPSModelInfos[] = { 122 {{0x32, 0x02, 0x14}, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT}, 123 // Toshiba Salellite Pro M10 124 // {{0x33, 0x02, 0x0a}, 0x88, 0xf8, ALPS_OLDPROTO}, 125 // UMAX-530T 126 {{0x53, 0x02, 0x0a}, 0xf8, 0xf8, 0}, 127 {{0x53, 0x02, 0x14}, 0xf8, 0xf8, 0}, 128 {{0x60, 0x03, 0xc8}, 0xf8, 0xf8, 0}, 129 // HP ze1115 130 {{0x63, 0x02, 0x0a}, 0xf8, 0xf8, 0}, 131 {{0x63, 0x02, 0x14}, 0xf8, 0xf8, 0}, 132 {{0x63, 0x02, 0x28}, 0xf8, 0xf8, ALPS_FW_BK_2}, 133 // Fujitsu Siemens S6010 134 // {{0x63, 0x02, 0x3c}, 0x8f, 0x8f, ALPS_WHEEL}, 135 // Toshiba Satellite S2400-103 136 {{0x63, 0x02, 0x50}, 0xef, 0xef, ALPS_FW_BK_1}, 137 // NEC Versa L320 138 {{0x63, 0x02, 0x64}, 0xf8, 0xf8, 0}, 139 {{0x63, 0x03, 0xc8}, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT}, 140 // Dell Latitude D800 141 {{0x73, 0x00, 0x0a}, 0xf8, 0xf8, ALPS_DUALPOINT}, 142 // ThinkPad R61 8918-5QG, x301 143 {{0x73, 0x02, 0x0a}, 0xf8, 0xf8, 0}, 144 {{0x73, 0x02, 0x14}, 0xf8, 0xf8, ALPS_FW_BK_2}, 145 // Ahtec Laptop 146 {{0x20, 0x02, 0x0e}, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT}, 147 // XXX 148 {{0x22, 0x02, 0x0a}, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT}, 149 {{0x22, 0x02, 0x14}, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT}, 150 // Dell Latitude D600 151 // {{0x62, 0x02, 0x14}, 0xcf, 0xcf, ALPS_PASS | ALPS_DUALPOINT 152 // | ALPS_PS2_INTERLEAVED}, 153 // Dell Latitude E5500, E6400, E6500, Precision M4400 154 {{0x73, 0x02, 0x50}, 0xcf, 0xcf, ALPS_FOUR_BUTTONS}, 155 // Dell Vostro 1400 156 // {{0x52, 0x01, 0x14}, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT 157 // | ALPS_PS2_INTERLEAVED}, 158 // Toshiba Tecra A11-11L 159 {{0, 0, 0}, 0, 0, 0} 160 }; 161 162 163 static alps_model_info* sFoundModel = NULL; 164 165 166 // touchpad proportions 167 #define EDGE_MOTION_WIDTH 55 168 // increase the touchpad size a little bit 169 #define AREA_START_X 40 170 #define AREA_END_X 987 171 #define AREA_START_Y 40 172 #define AREA_END_Y 734 173 174 #define MIN_PRESSURE 15 175 #define REAL_MAX_PRESSURE 70 176 #define MAX_PRESSURE 115 177 178 179 #define ALPS_HISTORY_SIZE 256 180 181 182 static hardware_specs gHardwareSpecs; 183 184 185 /* Data taken from linux driver: 186 ALPS absolute Mode - new format 187 byte 0: 1 ? ? ? 1 ? ? ? 188 byte 1: 0 x6 x5 x4 x3 x2 x1 x0 189 byte 2: 0 x10 x9 x8 x7 ? fin ges 190 byte 3: 0 y9 y8 y7 1 M R L 191 byte 4: 0 y6 y5 y4 y3 y2 y1 y0 192 byte 5: 0 z6 z5 z4 z3 z2 z1 z0 193 */ 194 static status_t 195 get_alps_movment(alps_cookie *cookie, mouse_movement *movement) 196 { 197 status_t status; 198 touch_event event; 199 uint8 event_buffer[PS2_PACKET_ALPS]; 200 201 status = acquire_sem_etc(cookie->sem, 1, B_CAN_INTERRUPT, 0); 202 if (status < B_OK) 203 return status; 204 205 if (!cookie->dev->active) { 206 TRACE("ALPS: read_event: Error device no longer active\n"); 207 return B_ERROR; 208 } 209 210 if (packet_buffer_read(cookie->ring_buffer, event_buffer, 211 cookie->dev->packet_size) != cookie->dev->packet_size) { 212 TRACE("ALPS: error copying buffer\n"); 213 return B_ERROR; 214 } 215 216 event.buttons = event_buffer[3] & 7; 217 event.zPressure = event_buffer[5]; 218 219 // finger on touchpad 220 if (event_buffer[2] & 0x2) { 221 // finger with normal width 222 event.wValue = 4; 223 } else { 224 event.wValue = 3; 225 } 226 227 // tab gesture 228 if (event_buffer[2] & 0x1) { 229 event.zPressure = 60; 230 event.wValue = 4; 231 } 232 // if hardware tab gesture is off a z pressure of 16 is reported 233 if (cookie->previousZ == 0 && event.wValue == 4 && event.zPressure == 16) 234 event.zPressure = 60; 235 236 cookie->previousZ = event.zPressure; 237 238 event.xPosition = event_buffer[1] | ((event_buffer[2] & 0x78) << 4); 239 event.yPosition = event_buffer[4] | ((event_buffer[3] & 0x70) << 3); 240 241 // check for trackpoint even (z pressure 127) 242 if (sFoundModel->flags & ALPS_DUALPOINT && event.zPressure == 127) { 243 movement->xdelta = event.xPosition > 383 ? event.xPosition - 768 244 : event.xPosition; 245 movement->ydelta = event.yPosition > 255 246 ? event.yPosition - 512 : event.yPosition; 247 movement->wheel_xdelta = 0; 248 movement->wheel_ydelta = 0; 249 movement->buttons = event.buttons; 250 movement->timestamp = system_time(); 251 cookie->movementMaker.UpdateButtons(movement); 252 } else { 253 event.yPosition = AREA_END_Y - (event.yPosition - AREA_START_Y); 254 status = cookie->movementMaker.EventToMovement(&event, movement); 255 } 256 257 if (cookie->movementMaker.WasEdgeMotion() 258 || cookie->movementMaker.TapDragStarted()) { 259 gEventProducer.FireEvent(cookie, event_buffer); 260 } 261 262 return status; 263 } 264 265 266 static void 267 default_settings(touchpad_settings *set) 268 { 269 memcpy(set, &kDefaultTouchpadSettings, sizeof(touchpad_settings)); 270 } 271 272 273 status_t 274 probe_alps(ps2_dev* dev) 275 { 276 int i; 277 uint8 val[3]; 278 TRACE("ALPS: probe\n"); 279 280 val[0] = 0; 281 if (ps2_dev_command(dev, PS2_CMD_MOUSE_SET_RES, val, 1, NULL, 0) != B_OK 282 || ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE11, NULL, 0, NULL, 0) 283 != B_OK 284 || ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE11, NULL, 0, NULL, 0) 285 != B_OK 286 || ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE11, NULL, 0, NULL, 0) 287 != B_OK) 288 return B_ERROR; 289 290 if (ps2_dev_command(dev, PS2_CMD_MOUSE_GET_INFO, NULL, 0, val, 3) 291 != B_OK) 292 return B_ERROR; 293 294 if (val[0] != 0 || val[1] != 0 || (val[2] != 10 && val[2] != 100)) 295 return B_ERROR; 296 297 val[0] = 0; 298 if (ps2_dev_command(dev, PS2_CMD_MOUSE_SET_RES, val, 1, NULL, 0) != B_OK 299 || ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE21, NULL, 0, NULL, 0) 300 != B_OK 301 || ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE21, NULL, 0, NULL, 0) 302 != B_OK 303 || ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE21, NULL, 0, NULL, 0) 304 != B_OK) 305 return B_ERROR; 306 307 if (ps2_dev_command(dev, PS2_CMD_MOUSE_GET_INFO, NULL, 0, val, 3) 308 != B_OK) 309 return B_ERROR; 310 311 for (i = 0; ; i++) { 312 const alps_model_info* info = &gALPSModelInfos[i]; 313 if (info->id[0] == 0) { 314 INFO("ALPS not supported: %2.2x %2.2x %2.2x\n", val[0], val[1], 315 val[2]); 316 return B_ERROR; 317 } 318 319 if (info->id[0] == val[0] && info->id[1] == val[1] 320 && info->id[2] == val[2]) { 321 sFoundModel = (alps_model_info*)info; 322 INFO("ALPS found: %2.2x %2.2x %2.2x\n", val[0], val[1], val[2]); 323 break; 324 } 325 } 326 327 dev->name = kALPSPath[dev->idx]; 328 dev->packet_size = PS2_PACKET_ALPS; 329 330 return B_OK; 331 } 332 333 334 status_t 335 switch_hardware_tab(ps2_dev* dev, bool on) 336 { 337 uint8 val[3]; 338 uint8 arg = 0x00; 339 uint8 command = PS2_CMD_MOUSE_SET_RES; 340 if (on) { 341 arg = 0x0A; 342 command = PS2_CMD_SET_TYPEMATIC; 343 } 344 if (ps2_dev_command(dev, PS2_CMD_MOUSE_GET_INFO, NULL, 0, val, 3) != B_OK 345 || ps2_dev_command(dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0) != B_OK 346 || ps2_dev_command(dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0) != B_OK 347 || ps2_dev_command(dev, command, &arg, 1, NULL, 0) != B_OK) 348 return B_ERROR; 349 350 return B_OK; 351 } 352 353 354 status_t 355 enable_passthrough(ps2_dev* dev, bool on) 356 { 357 uint8 command = PS2_CMD_MOUSE_SET_SCALE11; 358 if (on) 359 command = PS2_CMD_MOUSE_SET_SCALE21; 360 361 if (ps2_dev_command(dev, command, NULL, 0, NULL, 0) != B_OK 362 || ps2_dev_command(dev, command, NULL, 0, NULL, 0) != B_OK 363 || ps2_dev_command(dev, command, NULL, 0, NULL, 0) != B_OK 364 || ps2_dev_command(dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0) != B_OK) 365 return B_ERROR; 366 367 return B_OK; 368 } 369 370 371 status_t 372 alps_open(const char *name, uint32 flags, void **_cookie) 373 { 374 ps2_dev* dev; 375 int i; 376 for (dev = NULL, i = 0; i < PS2_DEVICE_COUNT; i++) { 377 if (0 == strcmp(ps2_device[i].name, name)) { 378 dev = &ps2_device[i]; 379 break; 380 } 381 } 382 383 if (dev == NULL) { 384 TRACE("ps2: dev = NULL\n"); 385 return B_ERROR; 386 } 387 388 if (atomic_or(&dev->flags, PS2_FLAG_OPEN) & PS2_FLAG_OPEN) 389 return B_BUSY; 390 391 alps_cookie* cookie = (alps_cookie*)malloc(sizeof(alps_cookie)); 392 if (cookie == NULL) 393 goto err1; 394 memset(cookie, 0, sizeof(*cookie)); 395 396 cookie->movementMaker.Init(); 397 cookie->previousZ = 0; 398 *_cookie = cookie; 399 400 cookie->dev = dev; 401 dev->cookie = cookie; 402 dev->disconnect = &alps_disconnect; 403 dev->handle_int = &alps_handle_int; 404 405 default_settings(&cookie->settings); 406 407 gHardwareSpecs.edgeMotionWidth = EDGE_MOTION_WIDTH; 408 409 gHardwareSpecs.areaStartX = AREA_START_X; 410 gHardwareSpecs.areaEndX = AREA_END_X; 411 gHardwareSpecs.areaStartY = AREA_START_Y; 412 gHardwareSpecs.areaEndY = AREA_END_Y; 413 414 gHardwareSpecs.minPressure = MIN_PRESSURE; 415 gHardwareSpecs.realMaxPressure = REAL_MAX_PRESSURE; 416 gHardwareSpecs.maxPressure = MAX_PRESSURE; 417 418 cookie->movementMaker.SetSettings(&cookie->settings); 419 cookie->movementMaker.SetSpecs(&gHardwareSpecs); 420 421 dev->packet_size = PS2_PACKET_ALPS; 422 423 cookie->ring_buffer = create_packet_buffer( 424 ALPS_HISTORY_SIZE * dev->packet_size); 425 if (cookie->ring_buffer == NULL) { 426 TRACE("ALPS: can't allocate mouse actions buffer\n"); 427 goto err2; 428 } 429 // create the mouse semaphore, used for synchronization between 430 // the interrupt handler and the read operation 431 cookie->sem = create_sem(0, "ps2_alps_sem"); 432 if (cookie->sem < 0) { 433 TRACE("ALPS: failed creating semaphore!\n"); 434 goto err3; 435 } 436 437 if ((sFoundModel->flags & ALPS_PASS) != 0 438 && enable_passthrough(dev, true) != B_OK) 439 goto err4; 440 441 // switch tap mode off 442 if (switch_hardware_tab(dev, false) != B_OK) 443 goto err4; 444 445 // init the alps device to absolut mode 446 if (ps2_dev_command(dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0) != B_OK 447 || ps2_dev_command(dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0) != B_OK 448 || ps2_dev_command(dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0) != B_OK 449 || ps2_dev_command(dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0) != B_OK 450 || ps2_dev_command(dev, PS2_CMD_ENABLE, NULL, 0, NULL, 0) != B_OK) 451 goto err4; 452 453 if ((sFoundModel->flags & ALPS_PASS) != 0 454 && enable_passthrough(dev, false) != B_OK) 455 goto err4; 456 457 if (ps2_dev_command(dev, PS2_CMD_MOUSE_SET_STREAM, NULL, 0, NULL, 0) != B_OK) 458 goto err4; 459 460 if (ps2_dev_command(dev, PS2_CMD_ENABLE, NULL, 0, NULL, 0) != B_OK) 461 goto err4; 462 463 atomic_or(&dev->flags, PS2_FLAG_ENABLED); 464 465 TRACE("ALPS: open %s success\n", name); 466 return B_OK; 467 468 err4: 469 delete_sem(cookie->sem); 470 err3: 471 delete_packet_buffer(cookie->ring_buffer); 472 err2: 473 free(cookie); 474 err1: 475 atomic_and(&dev->flags, ~PS2_FLAG_OPEN); 476 477 TRACE("ALPS: open %s failed\n", name); 478 return B_ERROR; 479 } 480 481 482 status_t 483 alps_close(void *_cookie) 484 { 485 gEventProducer.CancelEvent(); 486 487 alps_cookie *cookie = (alps_cookie*)_cookie; 488 489 ps2_dev_command_timeout(cookie->dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0, 490 150000); 491 492 delete_packet_buffer(cookie->ring_buffer); 493 delete_sem(cookie->sem); 494 495 atomic_and(&cookie->dev->flags, ~PS2_FLAG_OPEN); 496 atomic_and(&cookie->dev->flags, ~PS2_FLAG_ENABLED); 497 498 // Reset the touchpad so it generate standard ps2 packets instead of 499 // extended ones. If not, BeOS is confused with such packets when rebooting 500 // without a complete shutdown. 501 status_t status = ps2_reset_mouse(cookie->dev); 502 if (status != B_OK) { 503 INFO("ps2: reset failed\n"); 504 return B_ERROR; 505 } 506 507 TRACE("ALPS: close %s done\n", cookie->dev->name); 508 return B_OK; 509 } 510 511 512 status_t 513 alps_freecookie(void *_cookie) 514 { 515 free(_cookie); 516 return B_OK; 517 } 518 519 520 status_t 521 alps_ioctl(void *_cookie, uint32 op, void *buffer, size_t length) 522 { 523 alps_cookie *cookie = (alps_cookie*)_cookie; 524 mouse_movement movement; 525 status_t status; 526 527 switch (op) { 528 case MS_READ: 529 TRACE("ALPS: MS_READ get event\n"); 530 if ((status = get_alps_movment(cookie, &movement)) != B_OK) 531 return status; 532 return user_memcpy(buffer, &movement, sizeof(movement)); 533 534 case MS_IS_TOUCHPAD: 535 TRACE("ALPS: MS_IS_TOUCHPAD\n"); 536 return B_OK; 537 538 case MS_SET_TOUCHPAD_SETTINGS: 539 TRACE("ALPS: MS_SET_TOUCHPAD_SETTINGS"); 540 user_memcpy(&cookie->settings, buffer, sizeof(touchpad_settings)); 541 return B_OK; 542 543 case MS_SET_CLICKSPEED: 544 TRACE("ALPS: ioctl MS_SETCLICK (set click speed)\n"); 545 return user_memcpy(&cookie->movementMaker.click_speed, buffer, 546 sizeof(bigtime_t)); 547 548 default: 549 TRACE("ALPS: unknown opcode: %" B_PRIu32 "\n", op); 550 return B_BAD_VALUE; 551 } 552 } 553 554 555 static status_t 556 alps_read(void* cookie, off_t pos, void* buffer, size_t* _length) 557 { 558 *_length = 0; 559 return B_NOT_ALLOWED; 560 } 561 562 563 static status_t 564 alps_write(void* cookie, off_t pos, const void* buffer, size_t* _length) 565 { 566 *_length = 0; 567 return B_NOT_ALLOWED; 568 } 569 570 571 int32 572 alps_handle_int(ps2_dev* dev) 573 { 574 alps_cookie* cookie = (alps_cookie*)dev->cookie; 575 576 // we got a real event cancel the fake event 577 gEventProducer.CancelEvent(); 578 579 uint8 val; 580 val = cookie->dev->history[0].data; 581 if (cookie->packet_index == 0 582 && (val & sFoundModel->maskFirstByte) != sFoundModel->firstByte) { 583 INFO("ALPS: bad header, trying resync\n"); 584 cookie->packet_index = 0; 585 return B_UNHANDLED_INTERRUPT; 586 } 587 588 // data packages starting with a 0 589 if (cookie->packet_index > 1 && (val & 0x80)) { 590 INFO("ALPS: bad package data, trying resync\n"); 591 cookie->packet_index = 0; 592 return B_UNHANDLED_INTERRUPT; 593 } 594 595 cookie->buffer[cookie->packet_index] = val; 596 597 cookie->packet_index++; 598 if (cookie->packet_index >= 6) { 599 cookie->packet_index = 0; 600 601 if (packet_buffer_write(cookie->ring_buffer, 602 cookie->buffer, cookie->dev->packet_size) 603 != cookie->dev->packet_size) { 604 // buffer is full, drop new data 605 return B_HANDLED_INTERRUPT; 606 } 607 release_sem_etc(cookie->sem, 1, B_DO_NOT_RESCHEDULE); 608 609 return B_INVOKE_SCHEDULER; 610 } 611 612 return B_HANDLED_INTERRUPT; 613 } 614 615 616 void 617 alps_disconnect(ps2_dev *dev) 618 { 619 alps_cookie *cookie = (alps_cookie*)dev->cookie; 620 // the mouse device might not be opened at this point 621 INFO("ALPS: alps_disconnect %s\n", dev->name); 622 if ((dev->flags & PS2_FLAG_OPEN) != 0) 623 release_sem(cookie->sem); 624 } 625 626 627 device_hooks gALPSDeviceHooks = { 628 alps_open, 629 alps_close, 630 alps_freecookie, 631 alps_ioctl, 632 alps_read, 633 alps_write, 634 }; 635