// SPDX-License-Identifier: GPL-2.0-only/* * Elantech Touchpad driver (v6) * * Copyright (C) 2007-2009 Arjan Opmeer <arjan@opmeer.net> * * Trademarks are the property of their respective owners. */ #include <linux/delay.h>#include <linux/dmi.h>#include <linux/slab.h>#include <linux/module.h>#include <linux/i2c.h>#include <linux/input.h>#include <linux/input/mt.h>#include <linux/platform_device.h>#include <linux/serio.h>#include <linux/libps2.h>#include <linux/unaligned.h>#include "psmouse.h"#include "elantech.h"#include "elan_i2c.h" #define elantech_debug(fmt, ...) \ do { \ if (etd->info.debug) \ psmouse_printk(KERN_DEBUG, psmouse, \ fmt, ##__VA_ARGS__); \ } while (0) /* * Send a Synaptics style sliced query command */static int synaptics_send_cmd(struct psmouse *psmouse, unsigned char c, unsigned char *param){ if (ps2_sliced_command(&psmouse->ps2dev, c) || ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO)) { psmouse_err(psmouse, "%s query 0x%02x failed.\n", __func__, c); return -1; } return 0;} /* * V3 and later support this fast command */static int elantech_send_cmd(struct psmouse *psmouse, unsigned char c, unsigned char *param){ struct ps2dev *ps2dev = &psmouse->ps2dev; if (ps2_command(ps2dev, NULL, ETP_PS2_CUSTOM_COMMAND) || ps2_command(ps2dev, NULL, c) || ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) { psmouse_err(psmouse, "%s query 0x%02x failed.\n", __func__, c); return -1; } return 0;} /* * A retrying version of ps2_command */static int elantech_ps2_command(struct psmouse *psmouse, unsigned char *param, int command){ struct ps2dev *ps2dev = &psmouse->ps2dev; struct elantech_data *etd = psmouse->private; int rc; int tries = ETP_PS2_COMMAND_TRIES; do { rc = ps2_command(ps2dev, param, command); if (rc == 0) break; tries--; elantech_debug("retrying ps2 command 0x%02x (%d).\n", command, tries); msleep(ETP_PS2_COMMAND_DELAY); } while (tries > 0); if (rc) psmouse_err(psmouse, "ps2 command 0x%02x failed.\n", command); return rc;} /* * Send an Elantech style special command to read 3 bytes from a register */static int elantech_read_reg_params(struct psmouse *psmouse, u8 reg, u8 *param){ if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READWRITE) || elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || elantech_ps2_command(psmouse, NULL, reg) || elantech_ps2_command(psmouse, param, PSMOUSE_CMD_GETINFO)) { psmouse_err(psmouse, "failed to read register %#02x\n", reg); return -EIO; } return 0;} /* * Send an Elantech style special command to write a register with a parameter */static int elantech_write_reg_params(struct psmouse *psmouse, u8 reg, u8 *param){ if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READWRITE) || elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || elantech_ps2_command(psmouse, NULL, reg) || elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || elantech_ps2_command(psmouse, NULL, param[0]) || elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || elantech_ps2_command(psmouse, NULL, param[1]) || elantech_ps2_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11)) { psmouse_err(psmouse, "failed to write register %#02x with value %#02x%#02x\n", reg, param[0], param[1]); return -EIO; } return 0;} /* * Send an Elantech style special command to read a value from a register */static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg, unsigned char *val){ struct elantech_data *etd = psmouse->private; unsigned char param[3]; int rc = 0; if (reg < 0x07 || reg > 0x26) return -1; if (reg > 0x11 && reg < 0x20) return -1; switch (etd->info.hw_version) { case 1: if (ps2_sliced_command(&psmouse->ps2dev, ETP_REGISTER_READ) || ps2_sliced_command(&psmouse->ps2dev, reg) || ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO)) { rc = -1; } break; case 2: if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READ) || elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || elantech_ps2_command(psmouse, NULL, reg) || elantech_ps2_command(psmouse, param, PSMOUSE_CMD_GETINFO)) { rc = -1; } break; case 3 ... 4: if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READWRITE) || elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || elantech_ps2_command(psmouse, NULL, reg) || elantech_ps2_command(psmouse, param, PSMOUSE_CMD_GETINFO)) { rc = -1; } break; } if (rc) psmouse_err(psmouse, "failed to read register 0x%02x.\n", reg); else if (etd->info.hw_version != 4) *val = param[0]; else *val = param[1]; return rc;} /* * Send an Elantech style special command to write a register with a value */static int elantech_write_reg(struct psmouse *psmouse, unsigned char reg, unsigned char val){ struct elantech_data *etd = psmouse->private; int rc = 0; if (reg < 0x07 || reg > 0x26) return -1; if (reg > 0x11 && reg < 0x20) return -1; switch (etd->info.hw_version) { case 1: if (ps2_sliced_command(&psmouse->ps2dev, ETP_REGISTER_WRITE) || ps2_sliced_command(&psmouse->ps2dev, reg) || ps2_sliced_command(&psmouse->ps2dev, val) || ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11)) { rc = -1; } break; case 2: if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || elantech_ps2_command(psmouse, NULL, ETP_REGISTER_WRITE) || elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || elantech_ps2_command(psmouse, NULL, reg) || elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || elantech_ps2_command(psmouse, NULL, val) || elantech_ps2_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11)) { rc = -1; } break; case 3: if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READWRITE) || elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || elantech_ps2_command(psmouse, NULL, reg) || elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || elantech_ps2_command(psmouse, NULL, val) || elantech_ps2_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11)) { rc = -1; } break; case 4: if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READWRITE) || elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || elantech_ps2_command(psmouse, NULL, reg) || elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READWRITE) || elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || elantech_ps2_command(psmouse, NULL, val) || elantech_ps2_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11)) { rc = -1; } break; } if (rc) psmouse_err(psmouse, "failed to write register 0x%02x with value 0x%02x.\n", reg, val); return rc;} /* * Dump a complete mouse movement packet to the syslog */static void elantech_packet_dump(struct psmouse *psmouse){ psmouse_printk(KERN_DEBUG, psmouse, "PS/2 packet [%*ph]\n", psmouse->pktsize, psmouse->packet);} /* * Advertise INPUT_PROP_BUTTONPAD for clickpads. The testing of bit 12 in * fw_version for this is based on the following fw_version & caps table: * * Laptop-model: fw_version: caps: buttons: * Acer S3 0x461f00 10, 13, 0e clickpad * Acer S7-392 0x581f01 50, 17, 0d clickpad * Acer V5-131 0x461f02 01, 16, 0c clickpad * Acer V5-551 0x461f00 ? clickpad * Asus K53SV 0x450f01 78, 15, 0c 2 hw buttons * Asus G46VW 0x460f02 00, 18, 0c 2 hw buttons * Asus G750JX 0x360f00 00, 16, 0c 2 hw buttons * Asus TP500LN 0x381f17 10, 14, 0e clickpad * Asus X750JN 0x381f17 10, 14, 0e clickpad * Asus UX31 0x361f00 20, 15, 0e clickpad * Asus UX32VD 0x361f02 00, 15, 0e clickpad * Avatar AVIU-145A2 0x361f00 ? clickpad * Fujitsu CELSIUS H760 0x570f02 40, 14, 0c 3 hw buttons (**) * Fujitsu CELSIUS H780 0x5d0f02 41, 16, 0d 3 hw buttons (**) * Fujitsu LIFEBOOK E544 0x470f00 d0, 12, 09 2 hw buttons * Fujitsu LIFEBOOK E546 0x470f00 50, 12, 09 2 hw buttons * Fujitsu LIFEBOOK E547 0x470f00 50, 12, 09 2 hw buttons * Fujitsu LIFEBOOK E554 0x570f01 40, 14, 0c 2 hw buttons * Fujitsu LIFEBOOK E557 0x570f01 40, 14, 0c 2 hw buttons * Fujitsu T725 0x470f01 05, 12, 09 2 hw buttons * Fujitsu H730 0x570f00 c0, 14, 0c 3 hw buttons (**) * Gigabyte U2442 0x450f01 58, 17, 0c 2 hw buttons * Lenovo L430 0x350f02 b9, 15, 0c 2 hw buttons (*) * Lenovo L530 0x350f02 b9, 15, 0c 2 hw buttons (*) * Samsung NF210 0x150b00 78, 14, 0a 2 hw buttons * Samsung NP770Z5E 0x575f01 10, 15, 0f clickpad * Samsung NP700Z5B 0x361f06 21, 15, 0f clickpad * Samsung NP900X3E-A02 0x575f03 ? clickpad * Samsung NP-QX410 0x851b00 19, 14, 0c clickpad * Samsung RC512 0x450f00 08, 15, 0c 2 hw buttons * Samsung RF710 0x450f00 ? 2 hw buttons * System76 Pangolin 0x250f01 ? 2 hw buttons * (*) + 3 trackpoint buttons * (**) + 0 trackpoint buttons * Note: Lenovo L430 and Lenovo L530 have the same fw_version/caps */static inline int elantech_is_buttonpad(struct elantech_device_info *info){ return info->fw_version & 0x001000;} /* * Interpret complete data packets and report absolute mode input events for * hardware version 1. (4 byte packets) */static void elantech_report_absolute_v1(struct psmouse *psmouse){ struct input_dev *dev = psmouse->dev; struct elantech_data *etd = psmouse->private; unsigned char *packet = psmouse->packet; int fingers; if (etd->info.fw_version < 0x020000) { /* * byte 0: D U p1 p2 1 p3 R L * byte 1: f 0 th tw x9 x8 y9 y8 */ fingers = ((packet[1] & 0x80) >> 7) + ((packet[1] & 0x30) >> 4); } else { /* * byte 0: n1 n0 p2 p1 1 p3 R L * byte 1: 0 0 0 0 x9 x8 y9 y8 */ fingers = (packet[0] & 0xc0) >> 6; } if (etd->info.jumpy_cursor) { if (fingers != 1) { etd->single_finger_reports = 0; } else if (etd->single_finger_reports < 2) { /* Discard first 2 reports of one finger, bogus */ etd->single_finger_reports++; elantech_debug("discarding packet\n"); return; } } input_report_key(dev, BTN_TOUCH, fingers != 0); /* * byte 2: x7 x6 x5 x4 x3 x2 x1 x0 * byte 3: y7 y6 y5 y4 y3 y2 y1 y0 */ if (fingers) { input_report_abs(dev, ABS_X, ((packet[1] & 0x0c) << 6) | packet[2]); input_report_abs(dev, ABS_Y, etd->y_max - (((packet[1] & 0x03) << 8) | packet[3])); } input_report_key(dev, BTN_TOOL_FINGER, fingers == 1); input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2); input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3); psmouse_report_standard_buttons(dev, packet[0]); if (etd->info.fw_version < 0x020000 && (etd->info.capabilities[0] & ETP_CAP_HAS_ROCKER)) { /* rocker up */ input_report_key(dev, BTN_FORWARD, packet[0] & 0x40); /* rocker down */ input_report_key(dev, BTN_BACK, packet[0] & 0x80); } input_sync(dev);} static void elantech_set_slot(struct input_dev *dev, int slot, bool active, unsigned int x, unsigned int y){ input_mt_slot(dev, slot); input_mt_report_slot_state(dev, MT_TOOL_FINGER, active); if (active) { input_report_abs(dev, ABS_MT_POSITION_X, x); input_report_abs(dev, ABS_MT_POSITION_Y, y); }} /* x1 < x2 and y1 < y2 when two fingers, x = y = 0 when not pressed */static void elantech_report_semi_mt_data(struct input_dev *dev, unsigned int num_fingers, unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2){ elantech_set_slot(dev, 0, num_fingers != 0, x1, y1); elantech_set_slot(dev, 1, num_fingers >= 2, x2, y2);} /* * Interpret complete data packets and report absolute mode input events for * hardware version 2. (6 byte packets) */static void elantech_report_absolute_v2(struct psmouse *psmouse){ struct elantech_data *etd = psmouse->private; struct input_dev *dev = psmouse->dev; unsigned char *packet = psmouse->packet; unsigned int fingers, x1 = 0, y1 = 0, x2 = 0, y2 = 0; unsigned int width = 0, pres = 0; /* byte 0: n1 n0 . . . . R L */ fingers = (packet[0] & 0xc0) >> 6; switch (fingers) { case 3: /* * Same as one finger, except report of more than 3 fingers: * byte 3: n4 . w1 w0 . . . . */ if (packet[3] & 0x80) fingers = 4; fallthrough; case 1: /* * byte 1: . . . . x11 x10 x9 x8 * byte 2: x7 x6 x5 x4 x4 x2 x1 x0 */ x1 = ((packet[1] & 0x0f) << 8) | packet[2]; /* * byte 4: . . . . y11 y10 y9 y8 * byte 5: y7 y6 y5 y4 y3 y2 y1 y0 */ y1 = etd->y_max - (((packet[4] & 0x0f) << 8) | packet[5]); pres = (packet[1] & 0xf0) | ((packet[4] & 0xf0) >> 4); width = ((packet[0] & 0x30) >> 2) | ((packet[3] & 0x30) >> 4); break; case 2: /* * The coordinate of each finger is reported separately * with a lower resolution for two finger touches: * byte 0: . . ay8 ax8 . . . . * byte 1: ax7 ax6 ax5 ax4 ax3 ax2 ax1 ax0 */ x1 = (((packet[0] & 0x10) << 4) | packet[1]) << 2; /* byte 2: ay7 ay6 ay5 ay4 ay3 ay2 ay1 ay0 */ y1 = etd->y_max - ((((packet[0] & 0x20) << 3) | packet[2]) << 2); /* * byte 3: . . by8 bx8 . . . . * byte 4: bx7 bx6 bx5 bx4 bx3 bx2 bx1 bx0 */ x2 = (((packet[3] & 0x10) << 4) | packet[4]) << 2; /* byte 5: by7 by8 by5 by4 by3 by2 by1 by0 */ y2 = etd->y_max - ((((packet[3] & 0x20) << 3) | packet[5]) << 2); /* Unknown so just report sensible values */ pres = 127; width = 7; break; } input_report_key(dev, BTN_TOUCH, fingers != 0); if (fingers != 0) { input_report_abs(dev, ABS_X, x1); input_report_abs(dev, ABS_Y, y1); } elantech_report_semi_mt_data(dev, fingers, x1, y1, x2, y2); input_report_key(dev, BTN_TOOL_FINGER, fingers == 1); input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2); input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3); input_report_key(dev, BTN_TOOL_QUADTAP, fingers == 4); psmouse_report_standard_buttons(dev, packet[0]); if (etd->info.reports_pressure) { input_report_abs(dev, ABS_PRESSURE, pres); input_report_abs(dev, ABS_TOOL_WIDTH, width); } input_sync(dev);} static void elantech_report_trackpoint(struct psmouse *psmouse, int packet_type){ /* * byte 0: 0 0 sx sy 0 M R L * byte 1:~sx 0 0 0 0 0 0 0 * byte 2:~sy 0 0 0 0 0 0 0 * byte 3: 0 0 ~sy ~sx 0 1 1 0 * byte 4: x7 x6 x5 x4 x3 x2 x1 x0 * byte 5: y7 y6 y5 y4 y3 y2 y1 y0 * * x and y are written in two's complement spreadLines 1–500 of 2224. Load more
// SPDX-License-Identifier: GPL-2.0-only /* * Elantech Touchpad driver (v6) * * Copyright (C) 2007-2009 Arjan Opmeer <arjan@opmeer.net> * * Trademarks are the property of their respective owners. */ #include <linux/delay.h> #include <linux/dmi.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/i2c.h> #include <linux/input.h> #include <linux/input/mt.h> #include <linux/platform_device.h> #include <linux/serio.h> #include <linux/libps2.h> #include <linux/unaligned.h> #include "psmouse.h" #include "elantech.h" #include "elan_i2c.h" #define elantech_debug(fmt, ...) \ do { \ if (etd->info.debug) \ psmouse_printk(KERN_DEBUG, psmouse, \ fmt, ##__VA_ARGS__); \ } while (0) /* * Send a Synaptics style sliced query command */ static int synaptics_send_cmd(struct psmouse *psmouse, unsigned char c, unsigned char *param) { if (ps2_sliced_command(&psmouse->ps2dev, c) || ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO)) { psmouse_err(psmouse, "%s query 0x%02x failed.\n", __func__, c); return -1; } return 0; } /* * V3 and later support this fast command */ static int elantech_send_cmd(struct psmouse *psmouse, unsigned char c, unsigned char *param) { struct ps2dev *ps2dev = &psmouse->ps2dev; if (ps2_command(ps2dev, NULL, ETP_PS2_CUSTOM_COMMAND) || ps2_command(ps2dev, NULL, c) || ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) { psmouse_err(psmouse, "%s query 0x%02x failed.\n", __func__, c); return -1; } return 0; } /* * A retrying version of ps2_command */ static int elantech_ps2_command(struct psmouse *psmouse, unsigned char *param, int command) { struct ps2dev *ps2dev = &psmouse->ps2dev; struct elantech_data *etd = psmouse->private; int rc; int tries = ETP_PS2_COMMAND_TRIES; do { rc = ps2_command(ps2dev, param, command); if (rc == 0) break; tries--; elantech_debug("retrying ps2 command 0x%02x (%d).\n", command, tries); msleep(ETP_PS2_COMMAND_DELAY); } while (tries > 0); if (rc) psmouse_err(psmouse, "ps2 command 0x%02x failed.\n", command); return rc; } /* * Send an Elantech style special command to read 3 bytes from a register */ static int elantech_read_reg_params(struct psmouse *psmouse, u8 reg, u8 *param) { if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READWRITE) || elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || elantech_ps2_command(psmouse, NULL, reg) || elantech_ps2_command(psmouse, param, PSMOUSE_CMD_GETINFO)) { psmouse_err(psmouse, "failed to read register %#02x\n", reg); return -EIO; } return 0; } /* * Send an Elantech style special command to write a register with a parameter */ static int elantech_write_reg_params(struct psmouse *psmouse, u8 reg, u8 *param) { if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READWRITE) || elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || elantech_ps2_command(psmouse, NULL, reg) || elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || elantech_ps2_command(psmouse, NULL, param[0]) || elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || elantech_ps2_command(psmouse, NULL, param[1]) || elantech_ps2_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11)) { psmouse_err(psmouse, "failed to write register %#02x with value %#02x%#02x\n", reg, param[0], param[1]); return -EIO; } return 0; } /* * Send an Elantech style special command to read a value from a register */ static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg, unsigned char *val) { struct elantech_data *etd = psmouse->private; unsigned char param[3]; int rc = 0; if (reg < 0x07 || reg > 0x26) return -1; if (reg > 0x11 && reg < 0x20) return -1; switch (etd->info.hw_version) { case 1: if (ps2_sliced_command(&psmouse->ps2dev, ETP_REGISTER_READ) || ps2_sliced_command(&psmouse->ps2dev, reg) || ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO)) { rc = -1; } break; case 2: if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READ) || elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || elantech_ps2_command(psmouse, NULL, reg) || elantech_ps2_command(psmouse, param, PSMOUSE_CMD_GETINFO)) { rc = -1; } break; case 3 ... 4: if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READWRITE) || elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || elantech_ps2_command(psmouse, NULL, reg) || elantech_ps2_command(psmouse, param, PSMOUSE_CMD_GETINFO)) { rc = -1; } break; } if (rc) psmouse_err(psmouse, "failed to read register 0x%02x.\n", reg); else if (etd->info.hw_version != 4) *val = param[0]; else *val = param[1]; return rc; } /* * Send an Elantech style special command to write a register with a value */ static int elantech_write_reg(struct psmouse *psmouse, unsigned char reg, unsigned char val) { struct elantech_data *etd = psmouse->private; int rc = 0; if (reg < 0x07 || reg > 0x26) return -1; if (reg > 0x11 && reg < 0x20) return -1; switch (etd->info.hw_version) { case 1: if (ps2_sliced_command(&psmouse->ps2dev, ETP_REGISTER_WRITE) || ps2_sliced_command(&psmouse->ps2dev, reg) || ps2_sliced_command(&psmouse->ps2dev, val) || ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11)) { rc = -1; } break; case 2: if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || elantech_ps2_command(psmouse, NULL, ETP_REGISTER_WRITE) || elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || elantech_ps2_command(psmouse, NULL, reg) || elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || elantech_ps2_command(psmouse, NULL, val) || elantech_ps2_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11)) { rc = -1; } break; case 3: if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READWRITE) || elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || elantech_ps2_command(psmouse, NULL, reg) || elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || elantech_ps2_command(psmouse, NULL, val) || elantech_ps2_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11)) { rc = -1; } break; case 4: if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READWRITE) || elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || elantech_ps2_command(psmouse, NULL, reg) || elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READWRITE) || elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || elantech_ps2_command(psmouse, NULL, val) || elantech_ps2_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11)) { rc = -1; } break; } if (rc) psmouse_err(psmouse, "failed to write register 0x%02x with value 0x%02x.\n", reg, val); return rc; } /* * Dump a complete mouse movement packet to the syslog */ static void elantech_packet_dump(struct psmouse *psmouse) { psmouse_printk(KERN_DEBUG, psmouse, "PS/2 packet [%*ph]\n", psmouse->pktsize, psmouse->packet); } /* * Advertise INPUT_PROP_BUTTONPAD for clickpads. The testing of bit 12 in * fw_version for this is based on the following fw_version & caps table: * * Laptop-model: fw_version: caps: buttons: * Acer S3 0x461f00 10, 13, 0e clickpad * Acer S7-392 0x581f01 50, 17, 0d clickpad * Acer V5-131 0x461f02 01, 16, 0c clickpad * Acer V5-551 0x461f00 ? clickpad * Asus K53SV 0x450f01 78, 15, 0c 2 hw buttons * Asus G46VW 0x460f02 00, 18, 0c 2 hw buttons * Asus G750JX 0x360f00 00, 16, 0c 2 hw buttons * Asus TP500LN 0x381f17 10, 14, 0e clickpad * Asus X750JN 0x381f17 10, 14, 0e clickpad * Asus UX31 0x361f00 20, 15, 0e clickpad * Asus UX32VD 0x361f02 00, 15, 0e clickpad * Avatar AVIU-145A2 0x361f00 ? clickpad * Fujitsu CELSIUS H760 0x570f02 40, 14, 0c 3 hw buttons (**) * Fujitsu CELSIUS H780 0x5d0f02 41, 16, 0d 3 hw buttons (**) * Fujitsu LIFEBOOK E544 0x470f00 d0, 12, 09 2 hw buttons * Fujitsu LIFEBOOK E546 0x470f00 50, 12, 09 2 hw buttons * Fujitsu LIFEBOOK E547 0x470f00 50, 12, 09 2 hw buttons * Fujitsu LIFEBOOK E554 0x570f01 40, 14, 0c 2 hw buttons * Fujitsu LIFEBOOK E557 0x570f01 40, 14, 0c 2 hw buttons * Fujitsu T725 0x470f01 05, 12, 09 2 hw buttons * Fujitsu H730 0x570f00 c0, 14, 0c 3 hw buttons (**) * Gigabyte U2442 0x450f01 58, 17, 0c 2 hw buttons * Lenovo L430 0x350f02 b9, 15, 0c 2 hw buttons (*) * Lenovo L530 0x350f02 b9, 15, 0c 2 hw buttons (*) * Samsung NF210 0x150b00 78, 14, 0a 2 hw buttons * Samsung NP770Z5E 0x575f01 10, 15, 0f clickpad * Samsung NP700Z5B 0x361f06 21, 15, 0f clickpad * Samsung NP900X3E-A02 0x575f03 ? clickpad * Samsung NP-QX410 0x851b00 19, 14, 0c clickpad * Samsung RC512 0x450f00 08, 15, 0c 2 hw buttons * Samsung RF710 0x450f00 ? 2 hw buttons * System76 Pangolin 0x250f01 ? 2 hw buttons * (*) + 3 trackpoint buttons * (**) + 0 trackpoint buttons * Note: Lenovo L430 and Lenovo L530 have the same fw_version/caps */ static inline int elantech_is_buttonpad(struct elantech_device_info *info) { return info->fw_version & 0x001000; } /* * Interpret complete data packets and report absolute mode input events for * hardware version 1. (4 byte packets) */ static void elantech_report_absolute_v1(struct psmouse *psmouse) { struct input_dev *dev = psmouse->dev; struct elantech_data *etd = psmouse->private; unsigned char *packet = psmouse->packet; int fingers; if (etd->info.fw_version < 0x020000) { /* * byte 0: D U p1 p2 1 p3 R L * byte 1: f 0 th tw x9 x8 y9 y8 */ fingers = ((packet[1] & 0x80) >> 7) + ((packet[1] & 0x30) >> 4); } else { /* * byte 0: n1 n0 p2 p1 1 p3 R L * byte 1: 0 0 0 0 x9 x8 y9 y8 */ fingers = (packet[0] & 0xc0) >> 6; } if (etd->info.jumpy_cursor) { if (fingers != 1) { etd->single_finger_reports = 0; } else if (etd->single_finger_reports < 2) { /* Discard first 2 reports of one finger, bogus */ etd->single_finger_reports++; elantech_debug("discarding packet\n"); return; } } input_report_key(dev, BTN_TOUCH, fingers != 0); /* * byte 2: x7 x6 x5 x4 x3 x2 x1 x0 * byte 3: y7 y6 y5 y4 y3 y2 y1 y0 */ if (fingers) { input_report_abs(dev, ABS_X, ((packet[1] & 0x0c) << 6) | packet[2]); input_report_abs(dev, ABS_Y, etd->y_max - (((packet[1] & 0x03) << 8) | packet[3])); } input_report_key(dev, BTN_TOOL_FINGER, fingers == 1); input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2); input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3); psmouse_report_standard_buttons(dev, packet[0]); if (etd->info.fw_version < 0x020000 && (etd->info.capabilities[0] & ETP_CAP_HAS_ROCKER)) { /* rocker up */ input_report_key(dev, BTN_FORWARD, packet[0] & 0x40); /* rocker down */ input_report_key(dev, BTN_BACK, packet[0] & 0x80); } input_sync(dev); } static void elantech_set_slot(struct input_dev *dev, int slot, bool active, unsigned int x, unsigned int y) { input_mt_slot(dev, slot); input_mt_report_slot_state(dev, MT_TOOL_FINGER, active); if (active) { input_report_abs(dev, ABS_MT_POSITION_X, x); input_report_abs(dev, ABS_MT_POSITION_Y, y); } } /* x1 < x2 and y1 < y2 when two fingers, x = y = 0 when not pressed */ static void elantech_report_semi_mt_data(struct input_dev *dev, unsigned int num_fingers, unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2) { elantech_set_slot(dev, 0, num_fingers != 0, x1, y1); elantech_set_slot(dev, 1, num_fingers >= 2, x2, y2); } /* * Interpret complete data packets and report absolute mode input events for * hardware version 2. (6 byte packets) */ static void elantech_report_absolute_v2(struct psmouse *psmouse) { struct elantech_data *etd = psmouse->private; struct input_dev *dev = psmouse->dev; unsigned char *packet = psmouse->packet; unsigned int fingers, x1 = 0, y1 = 0, x2 = 0, y2 = 0; unsigned int width = 0, pres = 0; /* byte 0: n1 n0 . . . . R L */ fingers = (packet[0] & 0xc0) >> 6; switch (fingers) { case 3: /* * Same as one finger, except report of more than 3 fingers: * byte 3: n4 . w1 w0 . . . . */ if (packet[3] & 0x80) fingers = 4; fallthrough; case 1: /* * byte 1: . . . . x11 x10 x9 x8 * byte 2: x7 x6 x5 x4 x4 x2 x1 x0 */ x1 = ((packet[1] & 0x0f) << 8) | packet[2]; /* * byte 4: . . . . y11 y10 y9 y8 * byte 5: y7 y6 y5 y4 y3 y2 y1 y0 */ y1 = etd->y_max - (((packet[4] & 0x0f) << 8) | packet[5]); pres = (packet[1] & 0xf0) | ((packet[4] & 0xf0) >> 4); width = ((packet[0] & 0x30) >> 2) | ((packet[3] & 0x30) >> 4); break; case 2: /* * The coordinate of each finger is reported separately * with a lower resolution for two finger touches: * byte 0: . . ay8 ax8 . . . . * byte 1: ax7 ax6 ax5 ax4 ax3 ax2 ax1 ax0 */ x1 = (((packet[0] & 0x10) << 4) | packet[1]) << 2; /* byte 2: ay7 ay6 ay5 ay4 ay3 ay2 ay1 ay0 */ y1 = etd->y_max - ((((packet[0] & 0x20) << 3) | packet[2]) << 2); /* * byte 3: . . by8 bx8 . . . . * byte 4: bx7 bx6 bx5 bx4 bx3 bx2 bx1 bx0 */ x2 = (((packet[3] & 0x10) << 4) | packet[4]) << 2; /* byte 5: by7 by8 by5 by4 by3 by2 by1 by0 */ y2 = etd->y_max - ((((packet[3] & 0x20) << 3) | packet[5]) << 2); /* Unknown so just report sensible values */ pres = 127; width = 7; break; } input_report_key(dev, BTN_TOUCH, fingers != 0); if (fingers != 0) { input_report_abs(dev, ABS_X, x1); input_report_abs(dev, ABS_Y, y1); } elantech_report_semi_mt_data(dev, fingers, x1, y1, x2, y2); input_report_key(dev, BTN_TOOL_FINGER, fingers == 1); input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2); input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3); input_report_key(dev, BTN_TOOL_QUADTAP, fingers == 4); psmouse_report_standard_buttons(dev, packet[0]); if (etd->info.reports_pressure) { input_report_abs(dev, ABS_PRESSURE, pres); input_report_abs(dev, ABS_TOOL_WIDTH, width); } input_sync(dev); } static void elantech_report_trackpoint(struct psmouse *psmouse, int packet_type) { /* * byte 0: 0 0 sx sy 0 M R L * byte 1:~sx 0 0 0 0 0 0 0 * byte 2:~sy 0 0 0 0 0 0 0 * byte 3: 0 0 ~sy ~sx 0 1 1 0 * byte 4: x7 x6 x5 x4 x3 x2 x1 x0 * byte 5: y7 y6 y5 y4 y3 y2 y1 y0 * * x and y are written in two's complement spread