1 /* 2 * Copyright 2013, Haiku, Inc. 3 * Distributed under the terms of the MIT License. 4 * 5 * Hardware specs taken from the linux driver, thanks a lot! 6 * Based on ps2_alps.c 7 * 8 * Authors: 9 * Jérôme Duval <korli@users.berlios.de> 10 */ 11 12 13 #include "ps2_elantech.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(elantech_cookie* cookie, uint8* package) 36 { 37 fCookie = cookie; 38 memcpy(fLastPackage, package, sizeof(uint8) * PS2_PACKET_ELANTECH); 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_ELANTECH) != PS2_PACKET_ELANTECH) { 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_ELANTECH]; 70 timer fEventTimer; 71 elantech_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* kElantechPath[4] = { 87 "input/touchpad/ps2/elantech_0", 88 "input/touchpad/ps2/elantech_1", 89 "input/touchpad/ps2/elantech_2", 90 "input/touchpad/ps2/elantech_3" 91 }; 92 93 94 #define ELANTECH_CMD_GET_ID 0x00 95 #define ELANTECH_CMD_GET_VERSION 0x01 96 #define ELANTECH_CMD_GET_CAPABILITIES 0x02 97 #define ELANTECH_CMD_GET_SAMPLE 0x03 98 #define ELANTECH_CMD_GET_RESOLUTION 0x04 99 100 #define ELANTECH_CMD_REGISTER_READ 0x10 101 #define ELANTECH_CMD_REGISTER_WRITE 0x11 102 #define ELANTECH_CMD_REGISTER_READWRITE 0x00 103 #define ELANTECH_CMD_PS2_CUSTOM_CMD 0xf8 104 105 106 // touchpad proportions 107 #define EDGE_MOTION_WIDTH 55 108 109 #define MIN_PRESSURE 0 110 #define REAL_MAX_PRESSURE 50 111 #define MAX_PRESSURE 255 112 113 #define ELANTECH_HISTORY_SIZE 256 114 115 116 static hardware_specs gHardwareSpecs; 117 118 119 static status_t 120 get_elantech_movement(elantech_cookie *cookie, mouse_movement *movement) 121 { 122 touch_event event; 123 uint8 event_buffer[PS2_PACKET_ELANTECH]; 124 125 status_t status = acquire_sem_etc(cookie->sem, 1, B_CAN_INTERRUPT, 0); 126 if (status < B_OK) 127 return status; 128 129 if (!cookie->dev->active) { 130 TRACE("ELANTECH: read_event: Error device no longer active\n"); 131 return B_ERROR; 132 } 133 134 if (packet_buffer_read(cookie->ring_buffer, event_buffer, 135 cookie->dev->packet_size) != cookie->dev->packet_size) { 136 TRACE("ELANTECH: error copying buffer\n"); 137 return B_ERROR; 138 } 139 140 if (cookie->crcEnabled && (event_buffer[3] & 0x08) != 0) { 141 TRACE("ELANTECH: bad crc buffer\n"); 142 return B_ERROR; 143 } else if (!cookie->crcEnabled && ((event_buffer[0] & 0x0c) != 0x04 144 || (event_buffer[3] & 0x1c) != 0x10)) { 145 TRACE("ELANTECH: bad crc buffer\n"); 146 return B_ERROR; 147 } 148 uint8 type = event_buffer[3] & 3; 149 TRACE("ELANTECH: packet type %d\n", type); 150 TRACE("ELANTECH: packet content 0x%02x%02x%02x%02x%02x%02x\n", 151 event_buffer[0], event_buffer[1], event_buffer[2], event_buffer[3], 152 event_buffer[4], event_buffer[5]); 153 switch (type) { 154 case 0: 155 // fingers 156 cookie->fingers = event_buffer[1] & 0x1f; 157 break; 158 case 1: 159 if ((((event_buffer[3] & 0xe0) >> 5) - 1) != 0) { 160 // only process first finger 161 return B_OK; 162 } 163 event.zPressure = (event_buffer[1] & 0xf0) 164 | ((event_buffer[4] & 0xf0) >> 4); 165 166 cookie->previousZ = event.zPressure; 167 168 event.xPosition = ((event_buffer[1] & 0xf) << 8) | event_buffer[2]; 169 event.yPosition = (((event_buffer[4] & 0xf) << 8) 170 | event_buffer[5]); 171 TRACE("ELANTECH: buttons 0x%x x %ld y %ld z %d\n", 172 event.buttons, event.xPosition, event.yPosition, 173 event.zPressure); 174 break; 175 case 2: 176 TRACE("ELANTECH: packet type motion\n"); 177 // TODO 178 return B_OK; 179 default: 180 TRACE("ELANTECH: unknown packet type %d\n", type); 181 return B_ERROR; 182 } 183 184 event.buttons = 0; 185 // finger on touchpad 186 if ((cookie->fingers & 1) != 0) { 187 // finger with normal width 188 event.wValue = 4; 189 } else { 190 event.wValue = 3; 191 } 192 status = cookie->movementMaker.EventToMovement(&event, movement); 193 194 if (cookie->movementMaker.WasEdgeMotion() 195 || cookie->movementMaker.TapDragStarted()) { 196 gEventProducer.FireEvent(cookie, event_buffer); 197 } 198 199 return status; 200 } 201 202 203 static void 204 default_settings(touchpad_settings *set) 205 { 206 memcpy(set, &kDefaultTouchpadSettings, sizeof(touchpad_settings)); 207 } 208 209 210 static status_t 211 synaptics_dev_send_command(ps2_dev* dev, uint8 cmd, uint8 *in, int in_count) 212 { 213 uint8 val; 214 if (ps2_dev_sliced_command(dev, cmd) != B_OK 215 || ps2_dev_command(dev, PS2_CMD_MOUSE_GET_INFO, NULL, 0, in, in_count) 216 != B_OK) { 217 TRACE("ELANTECH: synaptics_dev_send_command failed\n"); 218 return B_ERROR; 219 } 220 return B_OK; 221 } 222 223 224 static status_t 225 elantech_dev_send_command(ps2_dev* dev, uint8 cmd, uint8 *in, int in_count) 226 { 227 uint8 val; 228 if (ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK 229 || ps2_dev_command(dev, cmd) != B_OK 230 || ps2_dev_command(dev, PS2_CMD_MOUSE_GET_INFO, NULL, 0, in, in_count) 231 != B_OK) { 232 TRACE("ELANTECH: elantech_dev_send_command failed\n"); 233 return B_ERROR; 234 } 235 return B_OK; 236 } 237 238 239 status_t 240 probe_elantech(ps2_dev* dev) 241 { 242 int i; 243 uint8 val[3]; 244 TRACE("ELANTECH: probe\n"); 245 246 ps2_dev_command(dev, PS2_CMD_MOUSE_RESET_DIS); 247 248 if (ps2_dev_command(dev, PS2_CMD_DISABLE) != B_OK 249 || ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE11) != B_OK 250 || ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE11) != B_OK 251 || ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE11) != B_OK) { 252 TRACE("ELANTECH: not found (1)\n"); 253 return B_ERROR; 254 } 255 256 if (ps2_dev_command(dev, PS2_CMD_MOUSE_GET_INFO, NULL, 0, val, 3) 257 != B_OK) { 258 TRACE("ELANTECH: not found (2)\n"); 259 return B_ERROR; 260 } 261 262 if (val[0] != 0x3c || val[1] != 0x3 || (val[2] != 0xc8 && val[2] != 0x0)) { 263 TRACE("ELANTECH: not found (3)\n"); 264 return B_ERROR; 265 } 266 267 val[0] = 0; 268 if (synaptics_dev_send_command(dev, ELANTECH_CMD_GET_VERSION, val, 3) 269 != B_OK) { 270 TRACE("ELANTECH: not found (4)\n"); 271 return B_ERROR; 272 } 273 274 if (val[0] == 0x0 || val[2] == 10 || val[2] == 20 || val[2] == 40 275 || val[2] == 60 || val[2] == 80 || val[2] == 100 || val[2] == 200) { 276 TRACE("ELANTECH: not found (5)\n"); 277 return B_ERROR; 278 } 279 280 INFO("Elantech found\n"); 281 282 dev->name = kElantechPath[dev->idx]; 283 dev->packet_size = PS2_PACKET_ELANTECH; 284 285 return B_OK; 286 } 287 288 289 static status_t 290 elantech_write_reg(elantech_cookie* cookie, uint8 reg, uint8 value) 291 { 292 if (reg < 0x7 || reg > 0x26) 293 return B_BAD_VALUE; 294 if (reg > 0x11 && reg < 0x20) 295 return B_BAD_VALUE; 296 297 ps2_dev* dev = cookie->dev; 298 switch (cookie->version) { 299 case 1: 300 // TODO 301 return B_ERROR; 302 break; 303 case 2: 304 if (ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK 305 || ps2_dev_command(dev, ELANTECH_CMD_REGISTER_WRITE) != B_OK 306 || ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK 307 || ps2_dev_command(dev, reg) != B_OK 308 || ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK 309 || ps2_dev_command(dev, value) != B_OK 310 || ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE11) != B_OK) 311 return B_ERROR; 312 break; 313 case 3: 314 if (ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK 315 || ps2_dev_command(dev, ELANTECH_CMD_REGISTER_READWRITE) != B_OK 316 || ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK 317 || ps2_dev_command(dev, reg) != B_OK 318 || ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK 319 || ps2_dev_command(dev, value) != B_OK 320 || ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE11) != B_OK) 321 return B_ERROR; 322 break; 323 case 4: 324 if (ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK 325 || ps2_dev_command(dev, ELANTECH_CMD_REGISTER_READWRITE) != B_OK 326 || ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK 327 || ps2_dev_command(dev, reg) != B_OK 328 || ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK 329 || ps2_dev_command(dev, ELANTECH_CMD_REGISTER_READWRITE) != B_OK 330 || ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK 331 || ps2_dev_command(dev, value) != B_OK 332 || ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE11) != B_OK) 333 return B_ERROR; 334 break; 335 default: 336 TRACE("ELANTECH: read_write_reg: unknown version\n"); 337 return B_ERROR; 338 } 339 return B_OK; 340 } 341 342 343 static status_t 344 elantech_read_reg(elantech_cookie* cookie, uint8 reg, uint8 *value) 345 { 346 if (reg < 0x7 || reg > 0x26) 347 return B_BAD_VALUE; 348 if (reg > 0x11 && reg < 0x20) 349 return B_BAD_VALUE; 350 351 ps2_dev* dev = cookie->dev; 352 uint8 val[3]; 353 switch (cookie->version) { 354 case 1: 355 // TODO 356 return B_ERROR; 357 break; 358 case 2: 359 if (ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK 360 || ps2_dev_command(dev, ELANTECH_CMD_REGISTER_READ) != B_OK 361 || ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK 362 || ps2_dev_command(dev, reg) != B_OK 363 || ps2_dev_command(dev, PS2_CMD_MOUSE_GET_INFO, NULL, 0, val, 364 3) != B_OK) 365 return B_ERROR; 366 break; 367 case 3: 368 case 4: 369 if (ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK 370 || ps2_dev_command(dev, ELANTECH_CMD_REGISTER_READWRITE) 371 != B_OK 372 || ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK 373 || ps2_dev_command(dev, reg) != B_OK 374 || ps2_dev_command(dev, PS2_CMD_MOUSE_GET_INFO, NULL, 0, val, 375 3) != B_OK) 376 return B_ERROR; 377 break; 378 default: 379 TRACE("ELANTECH: read_write_reg: unknown version\n"); 380 return B_ERROR; 381 } 382 if (cookie->version == 4) 383 *value = val[1]; 384 else 385 *value = val[0]; 386 387 return B_OK; 388 } 389 390 391 392 static status_t 393 switch_hardware_tab(ps2_dev* dev, bool on) 394 { 395 uint8 val[3]; 396 uint8 arg = 0x00; 397 uint8 command = PS2_CMD_MOUSE_SET_RES; 398 if (on) { 399 arg = 0x0A; 400 command = PS2_CMD_SET_SAMPLE_RATE; 401 } 402 if (ps2_dev_command(dev, PS2_CMD_MOUSE_GET_INFO, NULL, 0, val, 3) != B_OK 403 || ps2_dev_command(dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0) != B_OK 404 || ps2_dev_command(dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0) != B_OK 405 || ps2_dev_command(dev, command, &arg, 1, NULL, 0) != B_OK) 406 return B_ERROR; 407 408 return B_OK; 409 } 410 411 412 static status_t 413 get_resolution_v4(elantech_cookie* cookie, uint32* x, uint32* y) 414 { 415 uint8 val[3]; 416 if (elantech_dev_send_command(cookie->dev, ELANTECH_CMD_GET_RESOLUTION, 417 val, 3) != B_OK) 418 return B_ERROR; 419 *x = (val[1] & 0xf) * 10 + 790; 420 *y = ((val[1] & 0xf) >> 4) * 10 + 790; 421 return B_OK; 422 } 423 424 425 static status_t 426 get_range(elantech_cookie* cookie, uint32* x_min, uint32* y_min, uint32* x_max, 427 uint32* y_max, uint32 *width) 428 { 429 status_t status = B_OK; 430 uint8 val[3]; 431 switch (cookie->version) { 432 case 1: 433 *x_min = 32; 434 *y_min = 32; 435 *x_max = 544; 436 *y_max = 344; 437 *width = 0; 438 break; 439 case 2: 440 // TODO 441 break; 442 case 3: 443 if ((cookie->send_command)(cookie->dev, ELANTECH_CMD_GET_ID, val, 3) 444 != B_OK) { 445 return B_ERROR; 446 } 447 *x_min = 0; 448 *y_min = 0; 449 *x_max = ((val[0] & 0xf) << 8) | val[1]; 450 *y_max = ((val[0] & 0xf0) << 4) | val[2]; 451 *width = 0; 452 break; 453 case 4: 454 if ((cookie->send_command)(cookie->dev, ELANTECH_CMD_GET_ID, val, 3) 455 != B_OK) { 456 return B_ERROR; 457 } 458 *x_min = 0; 459 *y_min = 0; 460 *x_max = ((val[0] & 0xf) << 8) | val[1]; 461 *y_max = ((val[0] & 0xf0) << 4) | val[2]; 462 if (cookie->capabilities[1] < 2 || cookie->capabilities[1] > *x_max) 463 return B_ERROR; 464 *width = *x_max / (cookie->capabilities[1] - 1); 465 break; 466 } 467 return B_OK; 468 } 469 470 471 static status_t 472 enable_absolute_mode(elantech_cookie* cookie) 473 { 474 status_t status = B_OK; 475 switch (cookie->version) { 476 case 1: 477 status = elantech_write_reg(cookie, 0x10, 0x16); 478 if (status == B_OK) 479 status = elantech_write_reg(cookie, 0x11, 0x8f); 480 break; 481 case 2: 482 status = elantech_write_reg(cookie, 0x10, 0x54); 483 if (status == B_OK) 484 status = elantech_write_reg(cookie, 0x11, 0x88); 485 if (status == B_OK) 486 status = elantech_write_reg(cookie, 0x12, 0x60); 487 break; 488 case 3: 489 status = elantech_write_reg(cookie, 0x10, 0xb); 490 break; 491 case 4: 492 status = elantech_write_reg(cookie, 0x7, 0x1); 493 break; 494 495 } 496 497 if (cookie->version < 4) { 498 uint8 val; 499 500 for (uint8 retry = 0; retry < 5; retry++) { 501 status = elantech_read_reg(cookie, 0x10, &val); 502 if (status != B_OK) 503 break; 504 snooze(100); 505 } 506 } 507 508 return status; 509 } 510 511 512 status_t 513 elantech_open(const char *name, uint32 flags, void **_cookie) 514 { 515 TRACE("ELANTECH: open %s\n", name); 516 ps2_dev* dev; 517 int i; 518 for (dev = NULL, i = 0; i < PS2_DEVICE_COUNT; i++) { 519 if (0 == strcmp(ps2_device[i].name, name)) { 520 dev = &ps2_device[i]; 521 break; 522 } 523 } 524 525 if (dev == NULL) { 526 TRACE("ps2: dev = NULL\n"); 527 return B_ERROR; 528 } 529 530 if (atomic_or(&dev->flags, PS2_FLAG_OPEN) & PS2_FLAG_OPEN) 531 return B_BUSY; 532 533 elantech_cookie* cookie = (elantech_cookie*)malloc( 534 sizeof(elantech_cookie)); 535 if (cookie == NULL) 536 goto err1; 537 memset(cookie, 0, sizeof(*cookie)); 538 539 cookie->movementMaker.Init(); 540 cookie->previousZ = 0; 541 *_cookie = cookie; 542 543 cookie->dev = dev; 544 dev->cookie = cookie; 545 dev->disconnect = &elantech_disconnect; 546 dev->handle_int = &elantech_handle_int; 547 548 default_settings(&cookie->settings); 549 550 dev->packet_size = PS2_PACKET_ELANTECH; 551 552 cookie->ring_buffer = create_packet_buffer( 553 ELANTECH_HISTORY_SIZE * dev->packet_size); 554 if (cookie->ring_buffer == NULL) { 555 TRACE("ELANTECH: can't allocate mouse actions buffer\n"); 556 goto err2; 557 } 558 // create the mouse semaphore, used for synchronization between 559 // the interrupt handler and the read operation 560 cookie->sem = create_sem(0, "ps2_elantech_sem"); 561 if (cookie->sem < 0) { 562 TRACE("ELANTECH: failed creating semaphore!\n"); 563 goto err3; 564 } 565 566 uint8 val[3]; 567 if (synaptics_dev_send_command(dev, ELANTECH_CMD_GET_VERSION, val, 3) 568 != B_OK) { 569 TRACE("ELANTECH: get version failed!\n"); 570 goto err4; 571 } 572 cookie->fwVersion = (val[0] << 16) | (val[1] << 8) | val[2]; 573 if (cookie->fwVersion < 0x020030 || cookie->fwVersion == 0x020600) 574 cookie->version = 1; 575 else { 576 switch (val[0] & 0xf) { 577 case 2: 578 case 4: 579 cookie->version = 2; 580 break; 581 case 5: 582 cookie->version = 3; 583 case 6: 584 case 7: 585 cookie->version = 4; 586 break; 587 default: 588 TRACE("ELANTECH: unknown version!\n"); 589 goto err4; 590 } 591 } 592 TRACE("ELANTECH: version 0x%lx (0x%lx)\n", cookie->version, 593 cookie->fwVersion); 594 595 if (cookie->version >= 3) 596 cookie->send_command = &elantech_dev_send_command; 597 else 598 cookie->send_command = &synaptics_dev_send_command; 599 cookie->crcEnabled = (cookie->fwVersion & 0x4000) == 0x4000; 600 601 if ((cookie->send_command)(cookie->dev, ELANTECH_CMD_GET_CAPABILITIES, 602 cookie->capabilities, 3) != B_OK) { 603 TRACE("ELANTECH: get capabilities failed!\n"); 604 return B_ERROR; 605 } 606 607 if (enable_absolute_mode(cookie) != B_OK) { 608 TRACE("ELANTECH: failed enabling absolute mode!\n"); 609 goto err4; 610 } 611 TRACE("ELANTECH: enabled absolute mode!\n"); 612 613 uint32 x_min, x_max, y_min, y_max, width; 614 if (get_range(cookie, &x_min, &y_min, &x_max, &y_max, &width) != B_OK) { 615 TRACE("ELANTECH: get range failed!\n"); 616 goto err4; 617 } 618 619 TRACE("ELANTECH: range x %ld-%ld y %ld-%ld (%ld)\n", x_min, x_max, 620 y_min, y_max, width); 621 622 uint32 x_res, y_res; 623 if (get_resolution_v4(cookie, &x_res, &y_res) != B_OK) { 624 TRACE("ELANTECH: get resolution failed!\n"); 625 goto err4; 626 } 627 628 TRACE("ELANTECH: resolution x %ld y %ld (dpi)\n", x_res, y_res); 629 630 gHardwareSpecs.edgeMotionWidth = EDGE_MOTION_WIDTH; 631 632 gHardwareSpecs.areaStartX = x_min; 633 gHardwareSpecs.areaEndX = x_max; 634 gHardwareSpecs.areaStartY = y_min; 635 gHardwareSpecs.areaEndY = y_max; 636 637 gHardwareSpecs.minPressure = MIN_PRESSURE; 638 gHardwareSpecs.realMaxPressure = REAL_MAX_PRESSURE; 639 gHardwareSpecs.maxPressure = MAX_PRESSURE; 640 641 cookie->movementMaker.SetSettings(&cookie->settings); 642 cookie->movementMaker.SetSpecs(&gHardwareSpecs); 643 644 if (ps2_dev_command(dev, PS2_CMD_ENABLE, NULL, 0, NULL, 0) != B_OK) 645 goto err4; 646 647 atomic_or(&dev->flags, PS2_FLAG_ENABLED); 648 649 TRACE("ELANTECH: open %s success\n", name); 650 return B_OK; 651 652 err4: 653 delete_sem(cookie->sem); 654 err3: 655 delete_packet_buffer(cookie->ring_buffer); 656 err2: 657 free(cookie); 658 err1: 659 atomic_and(&dev->flags, ~PS2_FLAG_OPEN); 660 661 TRACE("ELANTECH: open %s failed\n", name); 662 return B_ERROR; 663 } 664 665 666 status_t 667 elantech_close(void *_cookie) 668 { 669 gEventProducer.CancelEvent(); 670 671 elantech_cookie *cookie = (elantech_cookie*)_cookie; 672 673 ps2_dev_command_timeout(cookie->dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0, 674 150000); 675 676 delete_packet_buffer(cookie->ring_buffer); 677 delete_sem(cookie->sem); 678 679 atomic_and(&cookie->dev->flags, ~PS2_FLAG_OPEN); 680 atomic_and(&cookie->dev->flags, ~PS2_FLAG_ENABLED); 681 682 // Reset the touchpad so it generate standard ps2 packets instead of 683 // extended ones. If not, BeOS is confused with such packets when rebooting 684 // without a complete shutdown. 685 status_t status = ps2_reset_mouse(cookie->dev); 686 if (status != B_OK) { 687 INFO("ps2: reset failed\n"); 688 return B_ERROR; 689 } 690 691 TRACE("ELANTECH: close %s done\n", cookie->dev->name); 692 return B_OK; 693 } 694 695 696 status_t 697 elantech_freecookie(void *_cookie) 698 { 699 free(_cookie); 700 return B_OK; 701 } 702 703 704 status_t 705 elantech_ioctl(void *_cookie, uint32 op, void *buffer, size_t length) 706 { 707 elantech_cookie *cookie = (elantech_cookie*)_cookie; 708 mouse_movement movement; 709 status_t status; 710 711 switch (op) { 712 case MS_READ: 713 TRACE("ELANTECH: MS_READ get event\n"); 714 if ((status = get_elantech_movement(cookie, &movement)) != B_OK) 715 return status; 716 return user_memcpy(buffer, &movement, sizeof(movement)); 717 718 case MS_IS_TOUCHPAD: 719 TRACE("ELANTECH: MS_IS_TOUCHPAD\n"); 720 return B_OK; 721 722 case MS_SET_TOUCHPAD_SETTINGS: 723 TRACE("ELANTECH: MS_SET_TOUCHPAD_SETTINGS"); 724 user_memcpy(&cookie->settings, buffer, sizeof(touchpad_settings)); 725 return B_OK; 726 727 case MS_SET_CLICKSPEED: 728 TRACE("ELANTECH: ioctl MS_SETCLICK (set click speed)\n"); 729 return user_memcpy(&cookie->movementMaker.click_speed, buffer, 730 sizeof(bigtime_t)); 731 732 default: 733 TRACE("ELANTECH: unknown opcode: %ld\n", op); 734 return B_BAD_VALUE; 735 } 736 } 737 738 739 static status_t 740 elantech_read(void* cookie, off_t pos, void* buffer, size_t* _length) 741 { 742 *_length = 0; 743 return B_NOT_ALLOWED; 744 } 745 746 747 static status_t 748 elantech_write(void* cookie, off_t pos, const void* buffer, size_t* _length) 749 { 750 *_length = 0; 751 return B_NOT_ALLOWED; 752 } 753 754 755 int32 756 elantech_handle_int(ps2_dev* dev) 757 { 758 elantech_cookie* cookie = (elantech_cookie*)dev->cookie; 759 760 // we got a real event cancel the fake event 761 gEventProducer.CancelEvent(); 762 763 uint8 val; 764 val = cookie->dev->history[0].data; 765 cookie->buffer[cookie->packet_index] = val; 766 cookie->packet_index++; 767 768 if (cookie->packet_index < PS2_PACKET_ELANTECH) 769 return B_HANDLED_INTERRUPT; 770 771 cookie->packet_index = 0; 772 if (packet_buffer_write(cookie->ring_buffer, 773 cookie->buffer, cookie->dev->packet_size) 774 != cookie->dev->packet_size) { 775 // buffer is full, drop new data 776 return B_HANDLED_INTERRUPT; 777 } 778 release_sem_etc(cookie->sem, 1, B_DO_NOT_RESCHEDULE); 779 return B_INVOKE_SCHEDULER; 780 } 781 782 783 void 784 elantech_disconnect(ps2_dev *dev) 785 { 786 elantech_cookie *cookie = (elantech_cookie*)dev->cookie; 787 // the mouse device might not be opened at this point 788 INFO("ELANTECH: elantech_disconnect %s\n", dev->name); 789 if ((dev->flags & PS2_FLAG_OPEN) != 0) 790 release_sem(cookie->sem); 791 } 792 793 794 device_hooks gElantechDeviceHooks = { 795 elantech_open, 796 elantech_close, 797 elantech_freecookie, 798 elantech_ioctl, 799 elantech_read, 800 elantech_write, 801 }; 802