1394 lines · 36.9 KB · C
RawDownload
/* * Elantech Touchpad driver (v6) * * Copyright (C) 2007-2009 Arjan Opmeer <arjan@opmeer.net> * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Trademarks are the property of their respective owners. */ #include <linux/delay.h>#include <linux/slab.h>#include <linux/module.h>#include <linux/input.h>#include <linux/input/mt.h>#include <linux/serio.h>#include <linux/libps2.h>#include "psmouse.h"#include "elantech.h" #define elantech_debug(fmt, ...)					\	do {								\		if (etd->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 (psmouse_sliced_command(psmouse, 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 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->hw_version) {	case 1:		if (psmouse_sliced_command(psmouse, ETP_REGISTER_READ) ||		    psmouse_sliced_command(psmouse, 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->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->hw_version) {	case 1:		if (psmouse_sliced_command(psmouse, ETP_REGISTER_WRITE) ||		    psmouse_sliced_command(psmouse, reg) ||		    psmouse_sliced_command(psmouse, 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){	int	i; 	psmouse_printk(KERN_DEBUG, psmouse, "PS/2 packet [");	for (i = 0; i < psmouse->pktsize; i++)		printk("%s0x%02x ", i ? ", " : " ", psmouse->packet[i]);	printk("]\n");} /* * 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->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->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);	input_report_key(dev, BTN_LEFT, packet[0] & 0x01);	input_report_key(dev, BTN_RIGHT, packet[0] & 0x02); 	if (etd->fw_version < 0x020000 &&	    (etd->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;		/* pass through... */	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);	input_report_key(dev, BTN_LEFT, packet[0] & 0x01);	input_report_key(dev, BTN_RIGHT, packet[0] & 0x02);	if (etd->reports_pressure) {		input_report_abs(dev, ABS_PRESSURE, pres);		input_report_abs(dev, ABS_TOOL_WIDTH, width);	} 	input_sync(dev);} /* * Interpret complete data packets and report absolute mode input events for * hardware version 3. (12 byte packets for two fingers) */static void elantech_report_absolute_v3(struct psmouse *psmouse,					int packet_type){	struct input_dev *dev = psmouse->dev;	struct elantech_data *etd = psmouse->private;	unsigned char *packet = psmouse->packet;	unsigned int fingers = 0, 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:	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]);		break; 	case 2:		if (packet_type == PACKET_V3_HEAD) {			/*			 * byte 1:   .    .    .    .  ax11 ax10 ax9  ax8			 * byte 2: ax7  ax6  ax5  ax4  ax3  ax2  ax1  ax0			 */			etd->mt[0].x = ((packet[1] & 0x0f) << 8) | packet[2];			/*			 * byte 4:   .    .    .    .  ay11 ay10 ay9  ay8			 * byte 5: ay7  ay6  ay5  ay4  ay3  ay2  ay1  ay0			 */			etd->mt[0].y = etd->y_max -				(((packet[4] & 0x0f) << 8) | packet[5]);			/*			 * wait for next packet			 */			return;		} 		/* packet_type == PACKET_V3_TAIL */		x1 = etd->mt[0].x;		y1 = etd->mt[0].y;		x2 = ((packet[1] & 0x0f) << 8) | packet[2];		y2 = etd->y_max - (((packet[4] & 0x0f) << 8) | packet[5]);		break;	} 	pres = (packet[1] & 0xf0) | ((packet[4] & 0xf0) >> 4);	width = ((packet[0] & 0x30) >> 2) | ((packet[3] & 0x30) >> 4); 	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_LEFT, packet[0] & 0x01);	input_report_key(dev, BTN_RIGHT, packet[0] & 0x02);	input_report_abs(dev, ABS_PRESSURE, pres);	input_report_abs(dev, ABS_TOOL_WIDTH, width); 	input_sync(dev);} static void elantech_input_sync_v4(struct psmouse *psmouse){	struct input_dev *dev = psmouse->dev;	unsigned char *packet = psmouse->packet; 	input_report_key(dev, BTN_LEFT, packet[0] & 0x01);	input_mt_report_pointer_emulation(dev, true);	input_sync(dev);} static void process_packet_status_v4(struct psmouse *psmouse){	struct input_dev *dev = psmouse->dev;	unsigned char *packet = psmouse->packet;	unsigned fingers;	int i; 	/* notify finger state change */

Lines 1–500 of 1394.