2224 lines · 60.4 KB · C
RawDownload
// 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

Lines 1–500 of 2224.