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 if (ps2_dev_sliced_command(dev, cmd) != B_OK 214 || ps2_dev_command(dev, PS2_CMD_MOUSE_GET_INFO, NULL, 0, in, in_count) 215 != B_OK) { 216 TRACE("ELANTECH: synaptics_dev_send_command failed\n"); 217 return B_ERROR; 218 } 219 return B_OK; 220 } 221 222 223 static status_t 224 elantech_dev_send_command(ps2_dev* dev, uint8 cmd, uint8 *in, int in_count) 225 { 226 if (ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK 227 || ps2_dev_command(dev, cmd) != B_OK 228 || ps2_dev_command(dev, PS2_CMD_MOUSE_GET_INFO, NULL, 0, in, in_count) 229 != B_OK) { 230 TRACE("ELANTECH: elantech_dev_send_command failed\n"); 231 return B_ERROR; 232 } 233 return B_OK; 234 } 235 236 237 status_t 238 probe_elantech(ps2_dev* dev) 239 { 240 uint8 val[3]; 241 TRACE("ELANTECH: probe\n"); 242 243 ps2_dev_command(dev, PS2_CMD_MOUSE_RESET_DIS); 244 245 if (ps2_dev_command(dev, PS2_CMD_DISABLE) != B_OK 246 || ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE11) != B_OK 247 || ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE11) != B_OK 248 || ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE11) != B_OK) { 249 TRACE("ELANTECH: not found (1)\n"); 250 return B_ERROR; 251 } 252 253 if (ps2_dev_command(dev, PS2_CMD_MOUSE_GET_INFO, NULL, 0, val, 3) 254 != B_OK) { 255 TRACE("ELANTECH: not found (2)\n"); 256 return B_ERROR; 257 } 258 259 if (val[0] != 0x3c || val[1] != 0x3 || (val[2] != 0xc8 && val[2] != 0x0)) { 260 TRACE("ELANTECH: not found (3)\n"); 261 return B_ERROR; 262 } 263 264 val[0] = 0; 265 if (synaptics_dev_send_command(dev, ELANTECH_CMD_GET_VERSION, val, 3) 266 != B_OK) { 267 TRACE("ELANTECH: not found (4)\n"); 268 return B_ERROR; 269 } 270 271 if (val[0] == 0x0 || val[2] == 10 || val[2] == 20 || val[2] == 40 272 || val[2] == 60 || val[2] == 80 || val[2] == 100 || val[2] == 200) { 273 TRACE("ELANTECH: not found (5)\n"); 274 return B_ERROR; 275 } 276 277 INFO("Elantech version %02X%02X%02X, under developement! Using fallback.\n", 278 val[0], val[1], val[2]); 279 280 dev->name = kElantechPath[dev->idx]; 281 dev->packet_size = PS2_PACKET_ELANTECH; 282 283 return B_ERROR; 284 } 285 286 287 static status_t 288 elantech_write_reg(elantech_cookie* cookie, uint8 reg, uint8 value) 289 { 290 if (reg < 0x7 || reg > 0x26) 291 return B_BAD_VALUE; 292 if (reg > 0x11 && reg < 0x20) 293 return B_BAD_VALUE; 294 295 ps2_dev* dev = cookie->dev; 296 switch (cookie->version) { 297 case 1: 298 // TODO 299 return B_ERROR; 300 break; 301 case 2: 302 if (ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK 303 || ps2_dev_command(dev, ELANTECH_CMD_REGISTER_WRITE) != B_OK 304 || ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK 305 || ps2_dev_command(dev, reg) != B_OK 306 || ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK 307 || ps2_dev_command(dev, value) != B_OK 308 || ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE11) != B_OK) 309 return B_ERROR; 310 break; 311 case 3: 312 if (ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK 313 || ps2_dev_command(dev, ELANTECH_CMD_REGISTER_READWRITE) != B_OK 314 || ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK 315 || ps2_dev_command(dev, reg) != B_OK 316 || ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK 317 || ps2_dev_command(dev, value) != B_OK 318 || ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE11) != B_OK) 319 return B_ERROR; 320 break; 321 case 4: 322 if (ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK 323 || ps2_dev_command(dev, ELANTECH_CMD_REGISTER_READWRITE) != B_OK 324 || ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK 325 || ps2_dev_command(dev, reg) != B_OK 326 || ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK 327 || ps2_dev_command(dev, ELANTECH_CMD_REGISTER_READWRITE) != B_OK 328 || ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK 329 || ps2_dev_command(dev, value) != B_OK 330 || ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE11) != B_OK) 331 return B_ERROR; 332 break; 333 default: 334 TRACE("ELANTECH: read_write_reg: unknown version\n"); 335 return B_ERROR; 336 } 337 return B_OK; 338 } 339 340 341 static status_t 342 elantech_read_reg(elantech_cookie* cookie, uint8 reg, uint8 *value) 343 { 344 if (reg < 0x7 || reg > 0x26) 345 return B_BAD_VALUE; 346 if (reg > 0x11 && reg < 0x20) 347 return B_BAD_VALUE; 348 349 ps2_dev* dev = cookie->dev; 350 uint8 val[3]; 351 switch (cookie->version) { 352 case 1: 353 // TODO 354 return B_ERROR; 355 break; 356 case 2: 357 if (ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK 358 || ps2_dev_command(dev, ELANTECH_CMD_REGISTER_READ) != B_OK 359 || ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK 360 || ps2_dev_command(dev, reg) != B_OK 361 || ps2_dev_command(dev, PS2_CMD_MOUSE_GET_INFO, NULL, 0, val, 362 3) != B_OK) 363 return B_ERROR; 364 break; 365 case 3: 366 case 4: 367 if (ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK 368 || ps2_dev_command(dev, ELANTECH_CMD_REGISTER_READWRITE) 369 != B_OK 370 || ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK 371 || ps2_dev_command(dev, reg) != B_OK 372 || ps2_dev_command(dev, PS2_CMD_MOUSE_GET_INFO, NULL, 0, val, 373 3) != B_OK) 374 return B_ERROR; 375 break; 376 default: 377 TRACE("ELANTECH: read_write_reg: unknown version\n"); 378 return B_ERROR; 379 } 380 if (cookie->version == 4) 381 *value = val[1]; 382 else 383 *value = val[0]; 384 385 return B_OK; 386 } 387 388 389 static status_t 390 get_resolution_v4(elantech_cookie* cookie, uint32* x, uint32* y) 391 { 392 uint8 val[3]; 393 if (elantech_dev_send_command(cookie->dev, ELANTECH_CMD_GET_RESOLUTION, 394 val, 3) != B_OK) 395 return B_ERROR; 396 *x = (val[1] & 0xf) * 10 + 790; 397 *y = ((val[1] & 0xf) >> 4) * 10 + 790; 398 return B_OK; 399 } 400 401 402 static status_t 403 get_range(elantech_cookie* cookie, uint32* x_min, uint32* y_min, uint32* x_max, 404 uint32* y_max, uint32 *width) 405 { 406 uint8 val[3]; 407 switch (cookie->version) { 408 case 1: 409 *x_min = 32; 410 *y_min = 32; 411 *x_max = 544; 412 *y_max = 344; 413 *width = 0; 414 break; 415 case 2: 416 // TODO 417 break; 418 case 3: 419 if ((cookie->send_command)(cookie->dev, ELANTECH_CMD_GET_ID, val, 3) 420 != B_OK) { 421 return B_ERROR; 422 } 423 *x_min = 0; 424 *y_min = 0; 425 *x_max = ((val[0] & 0xf) << 8) | val[1]; 426 *y_max = ((val[0] & 0xf0) << 4) | val[2]; 427 *width = 0; 428 break; 429 case 4: 430 if ((cookie->send_command)(cookie->dev, ELANTECH_CMD_GET_ID, val, 3) 431 != B_OK) { 432 return B_ERROR; 433 } 434 *x_min = 0; 435 *y_min = 0; 436 *x_max = ((val[0] & 0xf) << 8) | val[1]; 437 *y_max = ((val[0] & 0xf0) << 4) | val[2]; 438 if (cookie->capabilities[1] < 2 || cookie->capabilities[1] > *x_max) 439 return B_ERROR; 440 *width = *x_max / (cookie->capabilities[1] - 1); 441 break; 442 } 443 return B_OK; 444 } 445 446 447 static status_t 448 enable_absolute_mode(elantech_cookie* cookie) 449 { 450 status_t status = B_OK; 451 switch (cookie->version) { 452 case 1: 453 status = elantech_write_reg(cookie, 0x10, 0x16); 454 if (status == B_OK) 455 status = elantech_write_reg(cookie, 0x11, 0x8f); 456 break; 457 case 2: 458 status = elantech_write_reg(cookie, 0x10, 0x54); 459 if (status == B_OK) 460 status = elantech_write_reg(cookie, 0x11, 0x88); 461 if (status == B_OK) 462 status = elantech_write_reg(cookie, 0x12, 0x60); 463 break; 464 case 3: 465 status = elantech_write_reg(cookie, 0x10, 0xb); 466 break; 467 case 4: 468 status = elantech_write_reg(cookie, 0x7, 0x1); 469 break; 470 471 } 472 473 if (cookie->version < 4) { 474 uint8 val; 475 476 for (uint8 retry = 0; retry < 5; retry++) { 477 status = elantech_read_reg(cookie, 0x10, &val); 478 if (status != B_OK) 479 break; 480 snooze(100); 481 } 482 } 483 484 return status; 485 } 486 487 488 status_t 489 elantech_open(const char *name, uint32 flags, void **_cookie) 490 { 491 TRACE("ELANTECH: open %s\n", name); 492 ps2_dev* dev; 493 int i; 494 for (dev = NULL, i = 0; i < PS2_DEVICE_COUNT; i++) { 495 if (0 == strcmp(ps2_device[i].name, name)) { 496 dev = &ps2_device[i]; 497 break; 498 } 499 } 500 501 if (dev == NULL) { 502 TRACE("ps2: dev = NULL\n"); 503 return B_ERROR; 504 } 505 506 if (atomic_or(&dev->flags, PS2_FLAG_OPEN) & PS2_FLAG_OPEN) 507 return B_BUSY; 508 509 elantech_cookie* cookie = (elantech_cookie*)malloc( 510 sizeof(elantech_cookie)); 511 if (cookie == NULL) 512 goto err1; 513 memset(cookie, 0, sizeof(*cookie)); 514 515 cookie->movementMaker.Init(); 516 cookie->previousZ = 0; 517 *_cookie = cookie; 518 519 cookie->dev = dev; 520 dev->cookie = cookie; 521 dev->disconnect = &elantech_disconnect; 522 dev->handle_int = &elantech_handle_int; 523 524 default_settings(&cookie->settings); 525 526 dev->packet_size = PS2_PACKET_ELANTECH; 527 528 cookie->ring_buffer = create_packet_buffer( 529 ELANTECH_HISTORY_SIZE * dev->packet_size); 530 if (cookie->ring_buffer == NULL) { 531 TRACE("ELANTECH: can't allocate mouse actions buffer\n"); 532 goto err2; 533 } 534 // create the mouse semaphore, used for synchronization between 535 // the interrupt handler and the read operation 536 cookie->sem = create_sem(0, "ps2_elantech_sem"); 537 if (cookie->sem < 0) { 538 TRACE("ELANTECH: failed creating semaphore!\n"); 539 goto err3; 540 } 541 542 uint8 val[3]; 543 if (synaptics_dev_send_command(dev, ELANTECH_CMD_GET_VERSION, val, 3) 544 != B_OK) { 545 TRACE("ELANTECH: get version failed!\n"); 546 goto err4; 547 } 548 cookie->fwVersion = (val[0] << 16) | (val[1] << 8) | val[2]; 549 if (cookie->fwVersion < 0x020030 || cookie->fwVersion == 0x020600) 550 cookie->version = 1; 551 else { 552 switch (val[0] & 0xf) { 553 case 2: 554 case 4: 555 cookie->version = 2; 556 break; 557 case 5: 558 cookie->version = 3; 559 break; 560 case 6: 561 case 7: 562 cookie->version = 4; 563 break; 564 default: 565 TRACE("ELANTECH: unknown version!\n"); 566 goto err4; 567 } 568 } 569 TRACE("ELANTECH: version 0x%lx (0x%lx)\n", cookie->version, 570 cookie->fwVersion); 571 572 if (cookie->version >= 3) 573 cookie->send_command = &elantech_dev_send_command; 574 else 575 cookie->send_command = &synaptics_dev_send_command; 576 cookie->crcEnabled = (cookie->fwVersion & 0x4000) == 0x4000; 577 578 if ((cookie->send_command)(cookie->dev, ELANTECH_CMD_GET_CAPABILITIES, 579 cookie->capabilities, 3) != B_OK) { 580 TRACE("ELANTECH: get capabilities failed!\n"); 581 return B_ERROR; 582 } 583 584 if (enable_absolute_mode(cookie) != B_OK) { 585 TRACE("ELANTECH: failed enabling absolute mode!\n"); 586 goto err4; 587 } 588 TRACE("ELANTECH: enabled absolute mode!\n"); 589 590 uint32 x_min, x_max, y_min, y_max, width; 591 if (get_range(cookie, &x_min, &y_min, &x_max, &y_max, &width) != B_OK) { 592 TRACE("ELANTECH: get range failed!\n"); 593 goto err4; 594 } 595 596 TRACE("ELANTECH: range x %ld-%ld y %ld-%ld (%ld)\n", x_min, x_max, 597 y_min, y_max, width); 598 599 uint32 x_res, y_res; 600 if (get_resolution_v4(cookie, &x_res, &y_res) != B_OK) { 601 TRACE("ELANTECH: get resolution failed!\n"); 602 goto err4; 603 } 604 605 TRACE("ELANTECH: resolution x %ld y %ld (dpi)\n", x_res, y_res); 606 607 gHardwareSpecs.edgeMotionWidth = EDGE_MOTION_WIDTH; 608 609 gHardwareSpecs.areaStartX = x_min; 610 gHardwareSpecs.areaEndX = x_max; 611 gHardwareSpecs.areaStartY = y_min; 612 gHardwareSpecs.areaEndY = y_max; 613 614 gHardwareSpecs.minPressure = MIN_PRESSURE; 615 gHardwareSpecs.realMaxPressure = REAL_MAX_PRESSURE; 616 gHardwareSpecs.maxPressure = MAX_PRESSURE; 617 618 cookie->movementMaker.SetSettings(&cookie->settings); 619 cookie->movementMaker.SetSpecs(&gHardwareSpecs); 620 621 if (ps2_dev_command(dev, PS2_CMD_ENABLE, NULL, 0, NULL, 0) != B_OK) 622 goto err4; 623 624 atomic_or(&dev->flags, PS2_FLAG_ENABLED); 625 626 TRACE("ELANTECH: open %s success\n", name); 627 return B_OK; 628 629 err4: 630 delete_sem(cookie->sem); 631 err3: 632 delete_packet_buffer(cookie->ring_buffer); 633 err2: 634 free(cookie); 635 err1: 636 atomic_and(&dev->flags, ~PS2_FLAG_OPEN); 637 638 TRACE("ELANTECH: open %s failed\n", name); 639 return B_ERROR; 640 } 641 642 643 status_t 644 elantech_close(void *_cookie) 645 { 646 gEventProducer.CancelEvent(); 647 648 elantech_cookie *cookie = (elantech_cookie*)_cookie; 649 650 ps2_dev_command_timeout(cookie->dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0, 651 150000); 652 653 delete_packet_buffer(cookie->ring_buffer); 654 delete_sem(cookie->sem); 655 656 atomic_and(&cookie->dev->flags, ~PS2_FLAG_OPEN); 657 atomic_and(&cookie->dev->flags, ~PS2_FLAG_ENABLED); 658 659 // Reset the touchpad so it generate standard ps2 packets instead of 660 // extended ones. If not, BeOS is confused with such packets when rebooting 661 // without a complete shutdown. 662 status_t status = ps2_reset_mouse(cookie->dev); 663 if (status != B_OK) { 664 INFO("ps2: reset failed\n"); 665 return B_ERROR; 666 } 667 668 TRACE("ELANTECH: close %s done\n", cookie->dev->name); 669 return B_OK; 670 } 671 672 673 status_t 674 elantech_freecookie(void *_cookie) 675 { 676 free(_cookie); 677 return B_OK; 678 } 679 680 681 status_t 682 elantech_ioctl(void *_cookie, uint32 op, void *buffer, size_t length) 683 { 684 elantech_cookie *cookie = (elantech_cookie*)_cookie; 685 mouse_movement movement; 686 status_t status; 687 688 switch (op) { 689 case MS_READ: 690 TRACE("ELANTECH: MS_READ get event\n"); 691 if ((status = get_elantech_movement(cookie, &movement)) != B_OK) 692 return status; 693 return user_memcpy(buffer, &movement, sizeof(movement)); 694 695 case MS_IS_TOUCHPAD: 696 TRACE("ELANTECH: MS_IS_TOUCHPAD\n"); 697 return B_OK; 698 699 case MS_SET_TOUCHPAD_SETTINGS: 700 TRACE("ELANTECH: MS_SET_TOUCHPAD_SETTINGS"); 701 user_memcpy(&cookie->settings, buffer, sizeof(touchpad_settings)); 702 return B_OK; 703 704 case MS_SET_CLICKSPEED: 705 TRACE("ELANTECH: ioctl MS_SETCLICK (set click speed)\n"); 706 return user_memcpy(&cookie->movementMaker.click_speed, buffer, 707 sizeof(bigtime_t)); 708 709 default: 710 TRACE("ELANTECH: unknown opcode: %ld\n", op); 711 return B_BAD_VALUE; 712 } 713 } 714 715 716 static status_t 717 elantech_read(void* cookie, off_t pos, void* buffer, size_t* _length) 718 { 719 *_length = 0; 720 return B_NOT_ALLOWED; 721 } 722 723 724 static status_t 725 elantech_write(void* cookie, off_t pos, const void* buffer, size_t* _length) 726 { 727 *_length = 0; 728 return B_NOT_ALLOWED; 729 } 730 731 732 int32 733 elantech_handle_int(ps2_dev* dev) 734 { 735 elantech_cookie* cookie = (elantech_cookie*)dev->cookie; 736 737 // we got a real event cancel the fake event 738 gEventProducer.CancelEvent(); 739 740 uint8 val; 741 val = cookie->dev->history[0].data; 742 cookie->buffer[cookie->packet_index] = val; 743 cookie->packet_index++; 744 745 if (cookie->packet_index < PS2_PACKET_ELANTECH) 746 return B_HANDLED_INTERRUPT; 747 748 cookie->packet_index = 0; 749 if (packet_buffer_write(cookie->ring_buffer, 750 cookie->buffer, cookie->dev->packet_size) 751 != cookie->dev->packet_size) { 752 // buffer is full, drop new data 753 return B_HANDLED_INTERRUPT; 754 } 755 release_sem_etc(cookie->sem, 1, B_DO_NOT_RESCHEDULE); 756 return B_INVOKE_SCHEDULER; 757 } 758 759 760 void 761 elantech_disconnect(ps2_dev *dev) 762 { 763 elantech_cookie *cookie = (elantech_cookie*)dev->cookie; 764 // the mouse device might not be opened at this point 765 INFO("ELANTECH: elantech_disconnect %s\n", dev->name); 766 if ((dev->flags & PS2_FLAG_OPEN) != 0) 767 release_sem(cookie->sem); 768 } 769 770 771 device_hooks gElantechDeviceHooks = { 772 elantech_open, 773 elantech_close, 774 elantech_freecookie, 775 elantech_ioctl, 776 elantech_read, 777 elantech_write, 778 }; 779