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