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