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