Message ID | 1354846216-19707-1-git-send-email-kamal@canonical.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Hi Kamal, > This driver, submitted on behalf of Cypress Semiconductor Corporation and > additional contributors, provides support for the Cypress PS/2 Trackpad. > > Note that the patch "increase struct ps2dev cmdbuf[] to 8 bytes" [5] is a > PREREQUISITE for this patch. > > This [PATCH v6] version differs from my previous submitted version[4]: > > Changes as recommended by Dmitry Torokhov: > > - patches #2 (main driver) and #3 (link in driver) have been merged. > - eliminated cytp_dbg(), is_cypress_key(), and cypress_set_abs_rel_mode(). > - restructured cypress_validate_byte() and cypress_reset(). > - stopped unnecessary fiddling with psmouse->pktsize. > - fixed cypress_read_cmd_status() printf. > - removed unused members from struct cytp_data. > - misc code and comment style cleanups. > > Known problems: None. > > -Kamal Mostafa <kamal@canonical.com> > > [0] PATCH v1: http://www.spinics.net/lists/linux-input/msg23690.html > [1] PATCH v2: http://www.spinics.net/lists/linux-input/msg23718.html > [2] PATCH v3: http://www.spinics.net/lists/linux-input/msg23943.html > [3] PATCH v4: http://www.spinics.net/lists/linux-input/msg24047.html > [4] PATCH v5: http://www.spinics.net/lists/linux-input/msg24096.html > [5] cmdbuf patch: http://www.spinics.net/lists/linux-input/msg24095.html > > -- >8 -- > From: Dudley Du <dudl@cypress.com> > Subject: [PATCH v6] input: Cypress PS/2 Trackpad psmouse driver > > Input/mouse driver for Cypress PS/2 Trackpad. > > Original code contributed by Dudley Du (Cypress Semiconductor Corporation), > modified by Kamal Mostafa and Kyle Fazzari. > > BugLink: http://launchpad.net/bugs/978807 > > Signed-off-by: Dudley Du <dudl@cypress.com> > Signed-off-by: Kamal Mostafa <kamal@canonical.com> > Signed-off-by: Kyle Fazzari <git@status.e4ward.com> > Signed-off-by: Mario Limonciello <mario_limonciello@dell.com> > Signed-off-by: Tim Gardner <tim.gardner@canonical.com> > Acked-by: Herton Krzesinski <herton.krzesinski@canonical.com> > --- > drivers/input/mouse/Kconfig | 10 + > drivers/input/mouse/Makefile | 1 + > drivers/input/mouse/cypress_ps2.c | 719 ++++++++++++++++++++++++++++++++++++ > drivers/input/mouse/cypress_ps2.h | 191 ++++++++++ > drivers/input/mouse/psmouse-base.c | 32 ++ > drivers/input/mouse/psmouse.h | 1 + > 6 files changed, 954 insertions(+) > create mode 100644 drivers/input/mouse/cypress_ps2.c > create mode 100644 drivers/input/mouse/cypress_ps2.h > > diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig > index cd6268c..88954dd 100644 > --- a/drivers/input/mouse/Kconfig > +++ b/drivers/input/mouse/Kconfig > @@ -68,6 +68,16 @@ config MOUSE_PS2_SYNAPTICS > > If unsure, say Y. > > +config MOUSE_PS2_CYPRESS > + bool "Cypress PS/2 mouse protocol extension" if EXPERT Why EXPERT here? > + default y Should it really be default y here? > + depends on MOUSE_PS2 > + help > + Say Y here if you have a Cypress PS/2 Trackpad connected to > + your system. > + > + If unsure, say Y. > + > config MOUSE_PS2_LIFEBOOK > bool "Fujitsu Lifebook PS/2 mouse protocol extension" if EXPERT > default y > diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile > index 46ba755..323e352 100644 > --- a/drivers/input/mouse/Makefile > +++ b/drivers/input/mouse/Makefile > @@ -32,3 +32,4 @@ psmouse-$(CONFIG_MOUSE_PS2_LIFEBOOK) += lifebook.o > psmouse-$(CONFIG_MOUSE_PS2_SENTELIC) += sentelic.o > psmouse-$(CONFIG_MOUSE_PS2_TRACKPOINT) += trackpoint.o > psmouse-$(CONFIG_MOUSE_PS2_TOUCHKIT) += touchkit_ps2.o > +psmouse-$(CONFIG_MOUSE_PS2_CYPRESS) += cypress_ps2.o > diff --git a/drivers/input/mouse/cypress_ps2.c b/drivers/input/mouse/cypress_ps2.c > new file mode 100644 > index 0000000..310cb49 > --- /dev/null > +++ b/drivers/input/mouse/cypress_ps2.c > @@ -0,0 +1,719 @@ > +/* > + * Cypress Trackpad PS/2 mouse driver > + * > + * Copyright (c) 2012 Cypress Semiconductor Corporation. > + * > + * Author: > + * Dudley Du <dudl@cypress.com> > + * > + * Additional contributors include: > + * Kamal Mostafa <kamal@canonical.com> > + * Kyle Fazzari <git@status.e4ward.com> > + * > + * 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. > + */ > + > +#include <linux/init.h> > +#include <linux/module.h> > +#include <linux/kernel.h> > +#include <linux/slab.h> > +#include <linux/serio.h> > +#include <linux/libps2.h> > +#include <linux/input.h> > +#include <linux/input/mt.h> > +#include <linux/sched.h> > +#include <linux/wait.h> > + > +#include "cypress_ps2.h" > + > +#undef CYTP_DEBUG_VERBOSE /* define this and DEBUG for more verbose dump */ > + > +static void cypress_set_packet_size(struct psmouse *psmouse, unsigned int n) > +{ > + struct cytp_data *cytp = psmouse->private; > + cytp->pkt_size = n; > +} > + > +static const unsigned char cytp_rate[] = {10, 20, 40, 60, 100, 200}; > +static const unsigned char cytp_resolution[] = {0x00, 0x01, 0x02, 0x03}; > + > +static int cypress_ps2_sendbyte(struct psmouse *psmouse, int value) > +{ > + struct ps2dev *ps2dev = &psmouse->ps2dev; > + > + if (ps2_sendbyte(ps2dev, value & 0xff, CYTP_CMD_TIMEOUT) < 0) { > + psmouse_dbg(psmouse, > + "send command 0x%02x failed, resp 0x%02x\n", > + value & 0xff, ps2dev->nak); > + if (ps2dev->nak == CYTP_PS2_RETRY) > + return CYTP_PS2_RETRY; > + else > + return CYTP_PS2_ERROR; > + } > + > +#ifdef CYTP_DEBUG_VERBOSE > + psmouse_dbg(psmouse, "send command 0x%02x success, resp 0xfa\n", > + value & 0xff); > +#endif > + > + return 0; > +} > + > +static int cypress_ps2_ext_cmd(struct psmouse *psmouse, unsigned short cmd, > + unsigned char data) > +{ > + struct ps2dev *ps2dev = &psmouse->ps2dev; > + int tries = CYTP_PS2_CMD_TRIES; > + int rc; > + > + ps2_begin_command(ps2dev); > + > + do { > + /* > + * Send extension command byte (0xE8 or 0xF3). > + * If sending the command fails, send recovery command > + * to make the device return to the ready state. > + */ > + rc = cypress_ps2_sendbyte(psmouse, cmd & 0xff); > + if (rc == CYTP_PS2_RETRY) { > + rc = cypress_ps2_sendbyte(psmouse, 0x00); > + if (rc == CYTP_PS2_RETRY) > + rc = cypress_ps2_sendbyte(psmouse, 0x0a); > + } > + if (rc == CYTP_PS2_ERROR) > + continue; > + > + rc = cypress_ps2_sendbyte(psmouse, data); > + if (rc == CYTP_PS2_RETRY) > + rc = cypress_ps2_sendbyte(psmouse, data); > + if (rc == CYTP_PS2_ERROR) > + continue; > + else > + break; > + } while (--tries > 0); > + > + ps2_end_command(ps2dev); > + > + return rc; > +} > + > +static int cypress_ps2_read_cmd_status(struct psmouse *psmouse, > + unsigned char cmd, > + unsigned char *param) > +{ > + int rc; > + struct ps2dev *ps2dev = &psmouse->ps2dev; > + enum psmouse_state old_state; > + int pktsize; > + > + ps2_begin_command(&psmouse->ps2dev); > + > + old_state = psmouse->state; > + psmouse->state = PSMOUSE_CMD_MODE; > + psmouse->pktcnt = 0; > + > + pktsize = (cmd == CYTP_CMD_READ_TP_METRICS) ? 8 : 3; > + memset(param, 0, pktsize); > + > + rc = cypress_ps2_sendbyte(psmouse, 0xe9); > + if (rc < 0) > + goto out; > + > + wait_event_timeout(ps2dev->wait, > + (psmouse->pktcnt >= pktsize), > + msecs_to_jiffies(CYTP_CMD_TIMEOUT)); > + > + memcpy(param, psmouse->packet, pktsize); > + > + psmouse_dbg(psmouse, "Command 0x%02x response data (0x): %*ph\n", > + cmd, pktsize, param); > + > +out: > + psmouse->state = old_state; > + psmouse->pktcnt = 0; > + > + ps2_end_command(&psmouse->ps2dev); > + > + return rc; > +} > + > +static int cypress_verify_cmd_state(struct psmouse *psmouse, > + unsigned char cmd, unsigned char *param) > +{ > + bool rate_match = 0; > + bool resolution_match = 0; > + int i; > + > + /* callers will do further checking. */ > + if (cmd == CYTP_CMD_READ_CYPRESS_ID || > + cmd == CYTP_CMD_STANDARD_MODE || > + cmd == CYTP_CMD_READ_TP_METRICS) > + return 0; > + if ((~param[0] & DFLT_RESP_BITS_VALID) == DFLT_RESP_BITS_VALID && > + (param[0] & DFLT_RESP_BIT_MODE) == DFLT_RESP_STREAM_MODE) { > + for (i = 0; i < sizeof(cytp_resolution); i++) > + if (cytp_resolution[i] == param[1]) > + resolution_match = 1; > + > + for (i = 0; i < sizeof(cytp_rate); i++) > + if (cytp_rate[i] == param[2]) > + rate_match = 1; > + > + if (resolution_match && rate_match) > + return 0; > + } > + > + psmouse_dbg(psmouse, "verify cmd state failed.\n"); > + return -1; > +} > + > +static int cypress_send_ext_cmd(struct psmouse *psmouse, unsigned char cmd, > + unsigned char *param) > +{ > + int tries = CYTP_PS2_CMD_TRIES; > + int rc; > + > + psmouse_dbg(psmouse, "send extension cmd 0x%02x, [%d %d %d %d]\n", > + cmd, DECODE_CMD_AA(cmd), DECODE_CMD_BB(cmd), > + DECODE_CMD_CC(cmd), DECODE_CMD_DD(cmd)); > + do { > + cypress_ps2_ext_cmd(psmouse, > + PSMOUSE_CMD_SETRES, DECODE_CMD_DD(cmd)); > + cypress_ps2_ext_cmd(psmouse, > + PSMOUSE_CMD_SETRES, DECODE_CMD_CC(cmd)); > + cypress_ps2_ext_cmd(psmouse, > + PSMOUSE_CMD_SETRES, DECODE_CMD_BB(cmd)); > + cypress_ps2_ext_cmd(psmouse, > + PSMOUSE_CMD_SETRES, DECODE_CMD_AA(cmd)); > + > + rc = cypress_ps2_read_cmd_status(psmouse, cmd, param); > + if (rc) > + continue; > + > + rc = cypress_verify_cmd_state(psmouse, cmd, param); > + if (rc == 0) > + return 0; > + } while (--tries > 0); > + > + return -1; > +} > + > +int cypress_detect(struct psmouse *psmouse, bool set_properties) > +{ > + unsigned char param[3]; > + > + if (cypress_send_ext_cmd(psmouse, CYTP_CMD_READ_CYPRESS_ID, param)) > + return -ENODEV; > + > + /* Check for Cypress Trackpad signature bytes: 0x33 0xCC */ > + if (param[0] != 0x33 || param[1] != 0xCC) > + return -ENODEV; > + > + if (set_properties) { > + psmouse->vendor = "Cypress"; > + psmouse->name = "Trackpad"; > + } > + > + return 0; > +} > + > +static int cypress_read_fw_version(struct psmouse *psmouse) > +{ > + struct cytp_data *cytp = psmouse->private; > + unsigned char param[3]; > + > + if (cypress_send_ext_cmd(psmouse, CYTP_CMD_READ_CYPRESS_ID, param)) > + return -ENODEV; > + > + /* Check for Cypress Trackpad signature bytes: 0x33 0xCC */ > + if (param[0] != 0x33 || param[1] != 0xCC) > + return -ENODEV; > + > + cytp->fw_version = param[2] & FW_VERSION_MASX; > + cytp->tp_metrics_supported = (param[2] & TP_METRICS_MASK) ? 1 : 0; > + > + psmouse_dbg(psmouse, "cytp->fw_version = %d\n", cytp->fw_version); > + psmouse_dbg(psmouse, "cytp->tp_metrics_supported = %d\n", > + cytp->tp_metrics_supported); > + return 0; > +} > + > +static int cypress_read_tp_metrics(struct psmouse *psmouse) > +{ > + struct cytp_data *cytp = psmouse->private; > + unsigned char param[8]; > + > + /* set default values for tp metrics. */ > + cytp->tp_width = CYTP_DEFAULT_WIDTH; > + cytp->tp_high = CYTP_DEFAULT_HIGH; > + cytp->tp_max_abs_x = CYTP_ABS_MAX_X; > + cytp->tp_max_abs_y = CYTP_ABS_MAX_Y; > + cytp->tp_min_pressure = CYTP_MIN_PRESSURE; > + cytp->tp_max_pressure = CYTP_MAX_PRESSURE; > + cytp->tp_res_x = cytp->tp_max_abs_x / cytp->tp_width; > + cytp->tp_res_y = cytp->tp_max_abs_y / cytp->tp_high; > + > + memset(param, 0, sizeof(param)); > + if (cypress_send_ext_cmd(psmouse, CYTP_CMD_READ_TP_METRICS, param) == 0) { > + /* Update trackpad parameters. */ > + cytp->tp_max_abs_x = (param[1] << 8) | param[0]; > + cytp->tp_max_abs_y = (param[3] << 8) | param[2]; > + cytp->tp_min_pressure = param[4]; > + cytp->tp_max_pressure = param[5]; > + } > + > + if (!cytp->tp_max_pressure || > + cytp->tp_max_pressure < cytp->tp_min_pressure || > + !cytp->tp_width || !cytp->tp_high || > + !cytp->tp_max_abs_x || > + cytp->tp_max_abs_x < cytp->tp_width || > + !cytp->tp_max_abs_y || > + cytp->tp_max_abs_y < cytp->tp_high) > + return -1; > + > + cytp->tp_res_x = cytp->tp_max_abs_x / cytp->tp_width; > + cytp->tp_res_y = cytp->tp_max_abs_y / cytp->tp_high; > + > +#ifdef CYTP_DEBUG_VERBOSE > + psmouse_dbg(psmouse, "Dump trackpad hardware configuration as below:\n"); > + psmouse_dbg(psmouse, "cytp->tp_width = %d\n", cytp->tp_width); > + psmouse_dbg(psmouse, "cytp->tp_high = %d\n", cytp->tp_high); > + psmouse_dbg(psmouse, "cytp->tp_max_abs_x = %d\n", cytp->tp_max_abs_x); > + psmouse_dbg(psmouse, "cytp->tp_max_abs_y = %d\n", cytp->tp_max_abs_y); > + psmouse_dbg(psmouse, "cytp->tp_min_pressure = %d\n", cytp->tp_min_pressure); > + psmouse_dbg(psmouse, "cytp->tp_max_pressure = %d\n", cytp->tp_max_pressure); > + psmouse_dbg(psmouse, "cytp->tp_res_x = %d\n", cytp->tp_res_x); > + psmouse_dbg(psmouse, "cytp->tp_res_y = %d\n", cytp->tp_res_y); > + > + psmouse_dbg(psmouse, "tp_type_APA = %d\n", > + (param[6] & TP_METRICS_BIT_APA) ? 1 : 0); > + psmouse_dbg(psmouse, "tp_type_MTG = %d\n", > + (param[6] & TP_METRICS_BIT_MTG) ? 1 : 0); > + psmouse_dbg(psmouse, "tp_palm = %d\n", > + (param[6] & TP_METRICS_BIT_PALM) ? 1 : 0); > + psmouse_dbg(psmouse, "tp_stubborn = %d\n", > + (param[6] & TP_METRICS_BIT_STUBBORN) ? 1 : 0); > + psmouse_dbg(psmouse, "tp_1f_jitter = %d\n", > + (param[6] & TP_METRICS_BIT_1F_JITTER) >> 2); > + psmouse_dbg(psmouse, "tp_2f_jitter = %d\n", > + (param[6] & TP_METRICS_BIT_2F_JITTER) >> 4); > + psmouse_dbg(psmouse, "tp_1f_spike = %d\n", > + param[7] & TP_METRICS_BIT_1F_SPIKE); > + psmouse_dbg(psmouse, "tp_2f_spike = %d\n", > + (param[7] & TP_METRICS_BIT_2F_SPIKE) >> 2); > + psmouse_dbg(psmouse, "tp_abs_packet_format_set = %d\n", > + (param[7] & TP_METRICS_BIT_ABS_PKT_FORMAT_SET) >> 4); > +#endif > + > + return 0; > +} > + > +static int cypress_query_hardware(struct psmouse *psmouse) > +{ > + struct cytp_data *cytp = psmouse->private; > + int ret; > + > + ret = cypress_read_fw_version(psmouse); > + if (ret) > + return ret; > + > + if (cytp->tp_metrics_supported) { > + ret = cypress_read_tp_metrics(psmouse); > + if (ret) > + return ret; > + } > + > + return 0; > +} > + > +static int cypress_set_absolute_mode(struct psmouse *psmouse) > +{ > + struct cytp_data *cytp = psmouse->private; > + unsigned char param[3]; > + > + if (cypress_send_ext_cmd(psmouse, CYTP_CMD_ABS_WITH_PRESSURE_MODE, param) < 0) > + return -1; > + > + cytp->mode = (cytp->mode & ~CYTP_BIT_ABS_REL_MASK) > + | CYTP_BIT_ABS_PRESSURE; > + cypress_set_packet_size(psmouse, 5); > + > + return 0; > +} > + > +/* > + * Reset trackpad device. > + * This is also the default mode when trackpad powered on. > + */ > +static void cypress_reset(struct psmouse *psmouse) > +{ > + struct cytp_data *cytp = psmouse->private; > + > + cytp->mode = 0; > + > + psmouse_reset(psmouse); > +} > + > +static int cypress_set_input_params(struct input_dev *input, > + struct cytp_data *cytp) > +{ > + int ret; > + > + if (!cytp->tp_res_x || !cytp->tp_res_y) > + return -EINVAL; > + > + __set_bit(EV_ABS, input->evbit); > + input_set_abs_params(input, ABS_X, 0, cytp->tp_max_abs_x, 0, 0); > + input_set_abs_params(input, ABS_Y, 0, cytp->tp_max_abs_y, 0, 0); > + input_set_abs_params(input, ABS_PRESSURE, > + cytp->tp_min_pressure, cytp->tp_max_pressure, 0, 0); > + input_set_abs_params(input, ABS_TOOL_WIDTH, 0, 255, 0, 0); > + > + /* finger position */ > + input_set_abs_params(input, ABS_MT_POSITION_X, 0, cytp->tp_max_abs_x, 0, 0); > + input_set_abs_params(input, ABS_MT_POSITION_Y, 0, cytp->tp_max_abs_y, 0, 0); > + input_set_abs_params(input, ABS_MT_PRESSURE, 0, 255, 0, 0); > + > + ret = input_mt_init_slots(input, CYTP_MAX_MT_SLOTS, > + INPUT_MT_DROP_UNUSED|INPUT_MT_TRACK); > + if (ret < 0) > + return ret; > + > + __set_bit(INPUT_PROP_SEMI_MT, input->propbit); > + > + input_abs_set_res(input, ABS_X, cytp->tp_res_x); > + input_abs_set_res(input, ABS_Y, cytp->tp_res_y); > + > + input_abs_set_res(input, ABS_MT_POSITION_X, cytp->tp_res_x); > + input_abs_set_res(input, ABS_MT_POSITION_Y, cytp->tp_res_y); > + > + __set_bit(BTN_TOUCH, input->keybit); > + __set_bit(BTN_TOOL_FINGER, input->keybit); > + __set_bit(BTN_TOOL_DOUBLETAP, input->keybit); > + __set_bit(BTN_TOOL_TRIPLETAP, input->keybit); > + __set_bit(BTN_TOOL_QUADTAP, input->keybit); > + __set_bit(BTN_TOOL_QUINTTAP, input->keybit); > + > + __clear_bit(EV_REL, input->evbit); > + __clear_bit(REL_X, input->relbit); > + __clear_bit(REL_Y, input->relbit); > + > + __set_bit(INPUT_PROP_BUTTONPAD, input->propbit); > + __set_bit(EV_KEY, input->evbit); > + __set_bit(BTN_LEFT, input->keybit); > + __set_bit(BTN_RIGHT, input->keybit); > + __set_bit(BTN_MIDDLE, input->keybit); > + > + input_set_drvdata(input, cytp); > + > + return 0; > +} > + > +static int cypress_get_finger_count(unsigned char header_byte) > +{ > + unsigned char bits6_7; > + int finger_count; > + > + bits6_7 = header_byte >> 6; > + finger_count = bits6_7 & 0x03; > + > + if (finger_count == 1) > + return 1; > + > + if (header_byte & ABS_HSCROLL_BIT) { > + /* HSCROLL gets added on to 0 finger count. */ > + switch (finger_count) { > + case 0: return(4); No parenthesis on return. > + case 2: return(5); > + default: > + /* Invalid contact (e.g. palm). Ignore it. */ > + return(0); > + } > + } > + > + return finger_count; > +} > + > + > +static int cypress_parse_packet(struct psmouse *psmouse, > + struct cytp_data *cytp, struct cytp_report_data *report_data) > +{ > + unsigned char *packet = psmouse->packet; > + unsigned char header_byte = packet[0]; > + > + memset(report_data, 0, sizeof(struct cytp_report_data)); > + > + report_data->contact_cnt = cypress_get_finger_count(header_byte); > + > + if (report_data->contact_cnt == 0) /* e.g. palm detect */ > + return 0; > + > + report_data->tap = (header_byte & ABS_MULTIFINGER_TAP) ? 1 : 0; > + > + if (report_data->contact_cnt == 1) { > + report_data->contacts[0].x = > + ((packet[1] & 0x70) << 4) | packet[2]; > + report_data->contacts[0].y = > + ((packet[1] & 0x07) << 8) | packet[3]; > + if (cytp->mode & CYTP_BIT_ABS_PRESSURE) > + report_data->contacts[0].z = packet[4]; > + > + } else if (report_data->contact_cnt >= 2) { > + report_data->contacts[0].x = > + ((packet[1] & 0x70) << 4) | packet[2]; > + report_data->contacts[0].y = > + ((packet[1] & 0x07) << 8) | packet[3]; > + if (cytp->mode & CYTP_BIT_ABS_PRESSURE) > + report_data->contacts[0].z = packet[4]; > + > + report_data->contacts[1].x = > + ((packet[5] & 0xf0) << 4) | packet[6]; > + report_data->contacts[1].y = > + ((packet[5] & 0x0f) << 8) | packet[7]; > + if (cytp->mode & CYTP_BIT_ABS_PRESSURE) > + report_data->contacts[1].z = report_data->contacts[0].z; > + } > + > + report_data->left = (header_byte & BTN_LEFT_BIT) ? 1 : 0; > + report_data->right = (header_byte & BTN_RIGHT_BIT) ? 1 : 0; > + > + /* > + * This is only true if one of the mouse buttons were tapped. Make > + * sure it doesn't turn into a click. The regular tap-to-click > + * functionality will handle that on its own. If we don't do this, > + * disabling tap-to-click won't affect the mouse button zones. > + */ > + if (report_data->tap) > + report_data->left = 0; > + > +#ifdef CYTP_DEBUG_VERBOSE > + { > + int i; > + int n = report_data->contact_cnt; > + psmouse_dbg(psmouse, "Dump parsed report data as below:\n"); > + psmouse_dbg(psmouse, "contact_cnt = %d\n", > + report_data->contact_cnt); > + if (n > CYTP_MAX_MT_SLOTS) > + n = CYTP_MAX_MT_SLOTS; > + for (i = 0; i < n; i++) > + psmouse_dbg(psmouse, "contacts[%d] = {%d, %d, %d}\n", i, > + report_data->contacts[i].x, > + report_data->contacts[i].y, > + report_data->contacts[i].z); > + psmouse_dbg(psmouse, "left = %d\n", report_data->left); > + psmouse_dbg(psmouse, "right = %d\n", report_data->right); > + psmouse_dbg(psmouse, "middle = %d\n", report_data->middle); > + } > +#endif > + > + return 0; > +} > + > +static void cypress_process_packet(struct psmouse *psmouse, bool zero_pkt) > +{ > + int i; > + struct input_dev *input = psmouse->dev; > + struct cytp_data *cytp = psmouse->private; > + struct cytp_report_data report_data; > + struct cytp_contact *contact; > + struct input_mt_pos pos[CYTP_MAX_MT_SLOTS]; > + int slots[CYTP_MAX_MT_SLOTS]; > + int n; > + > + if (cypress_parse_packet(psmouse, cytp, &report_data)) > + return; > + > + n = report_data.contact_cnt; > + > + if (n > CYTP_MAX_MT_SLOTS) > + n = CYTP_MAX_MT_SLOTS; > + > + for (i = 0; i < n; i++) { > + contact = &report_data.contacts[i]; > + pos[i].x = contact->x; > + pos[i].y = contact->y; > + } > + > + input_mt_assign_slots(input, slots, pos, n); > + > + for (i = 0; i < n; i++) { > + contact = &report_data.contacts[i]; > + input_mt_slot(input, slots[i]); > + input_mt_report_slot_state(input, MT_TOOL_FINGER, true); > + input_report_abs(input, ABS_MT_POSITION_X, contact->x); > + input_report_abs(input, ABS_MT_POSITION_Y, contact->y); > + input_report_abs(input, ABS_MT_PRESSURE, contact->z); > + } > + > + input_mt_sync_frame(input); > + > + input_mt_report_finger_count(input, report_data.contact_cnt); > + > + input_report_key(input, BTN_LEFT, report_data.left); > + input_report_key(input, BTN_RIGHT, report_data.right); > + input_report_key(input, BTN_MIDDLE, report_data.middle); > + > + input_sync(input); > +} > + > +static psmouse_ret_t cypress_validate_byte(struct psmouse *psmouse) > +{ > + int contact_cnt; > + int index = psmouse->pktcnt - 1; > + unsigned char *packet = psmouse->packet; > + struct cytp_data *cytp = psmouse->private; > + > + if (index < 0 || index > cytp->pkt_size) > + return PSMOUSE_BAD_DATA; > + > + if (index == 0 && (packet[0] & 0xfc) == 0) { > + /* call packet process for reporting finger leave. */ > + cypress_process_packet(psmouse, 1); > + return PSMOUSE_FULL_PACKET; > + } > + > + /* > + * Perform validation (and adjust packet size) based only on the > + * first byte; allow all further bytes through. > + */ > + if (index != 0) > + return PSMOUSE_GOOD_DATA; > + > + /* > + * If absolute/relative mode bit has not been set yet, just pass > + * the byte through. > + */ > + if ((cytp->mode & CYTP_BIT_ABS_REL_MASK) == 0) > + return PSMOUSE_GOOD_DATA; > + > + if ((packet[0] & 0x08) == 0x08) > + return PSMOUSE_BAD_DATA; > + > + contact_cnt = cypress_get_finger_count(packet[0]); > + > + if (contact_cnt > 5) > + return PSMOUSE_BAD_DATA; > + > + if (cytp->mode & CYTP_BIT_ABS_NO_PRESSURE) > + cypress_set_packet_size(psmouse, contact_cnt == 2 ? 7 : 4); > + else > + cypress_set_packet_size(psmouse, contact_cnt == 2 ? 8 : 5); > + > + return PSMOUSE_GOOD_DATA; > +} > + > +static psmouse_ret_t cypress_protocol_handler(struct psmouse *psmouse) > +{ > + struct cytp_data *cytp = psmouse->private; > + > + if (psmouse->pktcnt >= cytp->pkt_size) { > + cypress_process_packet(psmouse, 0); > + return PSMOUSE_FULL_PACKET; > + } > + > + return cypress_validate_byte(psmouse); > +} > + > +static void cypress_set_rate(struct psmouse *psmouse, unsigned int rate) > +{ > + struct cytp_data *cytp = psmouse->private; > + > + if (rate >= 80) { > + psmouse->rate = 80; > + cytp->mode |= CYTP_BIT_HIGH_RATE; > + } else { > + psmouse->rate = 40; > + cytp->mode &= ~CYTP_BIT_HIGH_RATE; > + } > + > + ps2_command(&psmouse->ps2dev, (unsigned char *)&psmouse->rate, > + PSMOUSE_CMD_SETRATE); > +} > + > +static void cypress_disconnect(struct psmouse *psmouse) > +{ > + cypress_reset(psmouse); > + kfree(psmouse->private); > + psmouse->private = NULL; > +} > + > +static int cypress_reconnect(struct psmouse *psmouse) > +{ > + int tries = CYTP_PS2_CMD_TRIES; > + int rc; > + > + do { > + cypress_reset(psmouse); > + rc = cypress_detect(psmouse, false); > + } while (rc && (--tries > 0)); > + > + if (rc) { > + psmouse_err(psmouse, "Reconnect: unable to detect trackpad.\n"); > + return -1; > + } > + > + if (cypress_set_absolute_mode(psmouse)) { > + psmouse_err(psmouse, "Reconnect: Unable to initialize Cypress absolute mode.\n"); > + return -1; > + } > + > + return 0; > +} > + > +int cypress_init(struct psmouse *psmouse) > +{ > + struct cytp_data *cytp; > + > + cytp = (struct cytp_data *)kzalloc(sizeof(struct cytp_data), GFP_KERNEL); > + psmouse->private = (void *)cytp; > + if (cytp == NULL) > + return -ENOMEM; > + > + cypress_reset(psmouse); > + > + psmouse->pktsize = 8; > + > + if (cypress_query_hardware(psmouse)) { > + psmouse_err(psmouse, "Unable to query Trackpad hardware.\n"); > + goto err_exit; > + } > + > + if (cypress_set_absolute_mode(psmouse)) { > + psmouse_err(psmouse, "init: Unable to initialize Cypress absolute mode.\n"); > + goto err_exit; > + } > + > + if (cypress_set_input_params(psmouse->dev, cytp) < 0) { > + psmouse_err(psmouse, "init: Unable to set input params.\n"); > + goto err_exit; > + } > + > + psmouse->model = 1; > + psmouse->protocol_handler = cypress_protocol_handler; > + psmouse->set_rate = cypress_set_rate; > + psmouse->disconnect = cypress_disconnect; > + psmouse->reconnect = cypress_reconnect; > + psmouse->cleanup = cypress_reset; > + psmouse->resync_time = 0; > + > + return 0; > + > +err_exit: > + /* > + * Reset Cypress Trackpad as a standard mouse. Then > + * let psmouse driver commmunicating with it as default PS2 mouse. > + */ > + cypress_reset(psmouse); > + > + psmouse->private = NULL; > + kfree(cytp); > + > + return -1; > +} > + > +bool cypress_supported(void) > +{ > + return true; > +} > diff --git a/drivers/input/mouse/cypress_ps2.h b/drivers/input/mouse/cypress_ps2.h > new file mode 100644 > index 0000000..4720f21 > --- /dev/null > +++ b/drivers/input/mouse/cypress_ps2.h > @@ -0,0 +1,191 @@ > +#ifndef _CYPRESS_PS2_H > +#define _CYPRESS_PS2_H > + > +#include "psmouse.h" > + > +#define CMD_BITS_MASK 0x03 > +#define COMPOSIT(x, s) (((x) & CMD_BITS_MASK) << (s)) > + > +#define ENCODE_CMD(aa, bb, cc, dd) \ > + (COMPOSIT((aa), 6) | COMPOSIT((bb), 4) | COMPOSIT((cc), 2) | COMPOSIT((dd), 0)) > +#define CYTP_CMD_ABS_NO_PRESSURE_MODE ENCODE_CMD(0, 1, 0, 0) > +#define CYTP_CMD_ABS_WITH_PRESSURE_MODE ENCODE_CMD(0, 1, 0, 1) > +#define CYTP_CMD_SMBUS_MODE ENCODE_CMD(0, 1, 1, 0) > +#define CYTP_CMD_STANDARD_MODE ENCODE_CMD(0, 2, 0, 0) /* not implemented yet. */ > +#define CYTP_CMD_CYPRESS_REL_MODE ENCODE_CMD(1, 1, 1, 1) /* not implemented yet. */ > +#define CYTP_CMD_READ_CYPRESS_ID ENCODE_CMD(0, 0, 0, 0) > +#define CYTP_CMD_READ_TP_METRICS ENCODE_CMD(0, 0, 0, 1) > +#define CYTP_CMD_SET_HSCROLL_WIDTH(w) ENCODE_CMD(1, 1, 0, (w)) > +#define CYTP_CMD_SET_HSCROLL_MASK ENCODE_CMD(1, 1, 0, 0) > +#define CYTP_CMD_SET_VSCROLL_WIDTH(w) ENCODE_CMD(1, 2, 0, (w)) > +#define CYTP_CMD_SET_VSCROLL_MASK ENCODE_CMD(1, 2, 0, 0) > +#define CYTP_CMD_SET_PALM_GEOMETRY(e) ENCODE_CMD(1, 2, 1, (e)) > +#define CYTP_CMD_PALM_GEMMETRY_MASK ENCODE_CMD(1, 2, 1, 0) > +#define CYTP_CMD_SET_PALM_SENSITIVITY(s) ENCODE_CMD(1, 2, 2, (s)) > +#define CYTP_CMD_PALM_SENSITIVITY_MASK ENCODE_CMD(1, 2, 2, 0) > +#define CYTP_CMD_SET_MOUSE_SENSITIVITY(s) ENCODE_CMD(1, 3, ((s) >> 2), (s)) > +#define CYTP_CMD_MOUSE_SENSITIVITY_MASK ENCODE_CMD(1, 3, 0, 0) > +#define CYTP_CMD_REQUEST_BASELINE_STATUS ENCODE_CMD(2, 0, 0, 1) > +#define CYTP_CMD_REQUEST_RECALIBRATION ENCODE_CMD(2, 0, 0, 3) > + > +#define DECODE_CMD_AA(x) (((x) >> 6) & CMD_BITS_MASK) > +#define DECODE_CMD_BB(x) (((x) >> 4) & CMD_BITS_MASK) > +#define DECODE_CMD_CC(x) (((x) >> 2) & CMD_BITS_MASK) > +#define DECODE_CMD_DD(x) ((x) & CMD_BITS_MASK) > + > +/* Cypress trackpad working mode. */ > +#define CYTP_BIT_ABS_PRESSURE (1 << 3) > +#define CYTP_BIT_ABS_NO_PRESSURE (1 << 2) > +#define CYTP_BIT_CYPRESS_REL (1 << 1) > +#define CYTP_BIT_STANDARD_REL (1 << 0) > +#define CYTP_BIT_REL_MASK (CYTP_BIT_CYPRESS_REL | CYTP_BIT_STANDARD_REL) > +#define CYTP_BIT_ABS_MASK (CYTP_BIT_ABS_PRESSURE | CYTP_BIT_ABS_NO_PRESSURE) > +#define CYTP_BIT_ABS_REL_MASK (CYTP_BIT_ABS_MASK | CYTP_BIT_REL_MASK) > + > +#define CYTP_BIT_HIGH_RATE (1 << 4) > +/* > + * report mode bit is set, firmware working in Remote Mode. > + * report mode bit is cleared, firmware working in Stream Mode. > + */ > +#define CYTP_BIT_REPORT_MODE (1 << 5) > + > +/* scrolling width values for set HSCROLL and VSCROLL width command. */ > +#define SCROLL_WIDTH_NARROW 1 > +#define SCROLL_WIDTH_NORMAL 2 > +#define SCROLL_WIDTH_WIDE 3 > + > +#define PALM_GEOMETRY_ENABLE 1 > +#define PALM_GEOMETRY_DISABLE 0 > + > +#define TP_METRICS_MASK 0x80 > +#define FW_VERSION_MASX 0x7f > +#define FW_VER_HIGH_MASK 0x70 > +#define FW_VER_LOW_MASK 0x0f > + > +/* Times to retry a ps2_command and millisecond delay between tries. */ > +#define CYTP_PS2_CMD_TRIES 3 > +#define CYTP_PS2_CMD_DELAY 500 > + > +/* time out for PS/2 command only in milliseconds. */ > +#define CYTP_CMD_TIMEOUT 200 > +#define CYTP_DATA_TIMEOUT 30 > + > +#define CYTP_EXT_CMD 0xe8 > +#define CYTP_PS2_RETRY 0xfe > +#define CYTP_PS2_ERROR 0xfc > + > +#define CYTP_RESP_RETRY 0x01 > +#define CYTP_RESP_ERROR 0xfe > + > + > +#define CYTP_105001_WIDTH 97 /* Dell XPS 13 */ > +#define CYTP_105001_HIGH 59 > +#define CYTP_DEFAULT_WIDTH (CYTP_105001_WIDTH) > +#define CYTP_DEFAULT_HIGH (CYTP_105001_HIGH) > + > +#define CYTP_ABS_MAX_X 1600 > +#define CYTP_ABS_MAX_Y 900 > +#define CYTP_MAX_PRESSURE 255 > +#define CYTP_MIN_PRESSURE 0 > + > +/* header byte bits of relative package. */ > +#define BTN_LEFT_BIT 0x01 > +#define BTN_RIGHT_BIT 0x02 > +#define BTN_MIDDLE_BIT 0x04 > +#define REL_X_SIGN_BIT 0x10 > +#define REL_Y_SIGN_BIT 0x20 > + > +/* header byte bits of absolute package. */ > +#define ABS_VSCROLL_BIT 0x10 > +#define ABS_HSCROLL_BIT 0x20 > +#define ABS_MULTIFINGER_TAP 0x04 > +#define ABS_EDGE_MOTION_MASK 0x80 > + > +#define DFLT_RESP_BITS_VALID 0x88 /* SMBus bit should not be set. */ > +#define DFLT_RESP_SMBUS_BIT 0x80 > +#define DFLT_SMBUS_MODE 0x80 > +#define DFLT_PS2_MODE 0x00 > +#define DFLT_RESP_BIT_MODE 0x40 > +#define DFLT_RESP_REMOTE_MODE 0x40 > +#define DFLT_RESP_STREAM_MODE 0x00 > +#define DFLT_RESP_BIT_REPORTING 0x20 > +#define DFLT_RESP_BIT_SCALING 0x10 > + > +#define TP_METRICS_BIT_PALM 0x80 > +#define TP_METRICS_BIT_STUBBORN 0x40 > +#define TP_METRICS_BIT_2F_JITTER 0x30 > +#define TP_METRICS_BIT_1F_JITTER 0x0c > +#define TP_METRICS_BIT_APA 0x02 > +#define TP_METRICS_BIT_MTG 0x01 > +#define TP_METRICS_BIT_ABS_PKT_FORMAT_SET 0xf0 > +#define TP_METRICS_BIT_2F_SPIKE 0x0c > +#define TP_METRICS_BIT_1F_SPIKE 0x03 > + > +/* bits of first byte response of E9h-Status Request command. */ > +#define RESP_BTN_RIGHT_BIT 0x01 > +#define RESP_BTN_MIDDLE_BIT 0x02 > +#define RESP_BTN_LEFT_BIT 0x04 > +#define RESP_SCALING_BIT 0x10 > +#define RESP_ENABLE_BIT 0x20 > +#define RESP_REMOTE_BIT 0x40 > +#define RESP_SMBUS_BIT 0x80 > + > +#define CYTP_MAX_MT_SLOTS 2 > + > +struct cytp_contact { > + int x; > + int y; > + int z; /* also named as touch pressure. */ > +}; > + > +/* The structure of Cypress Trackpad event data. */ > +struct cytp_report_data { > + int contact_cnt; > + struct cytp_contact contacts[CYTP_MAX_MT_SLOTS]; > + unsigned int left:1; > + unsigned int right:1; > + unsigned int middle:1; > + unsigned int tap:1; /* multi-finger tap detected. */ > +}; > + > +/* The structure of Cypress Trackpad device private data. */ > +struct cytp_data { > + int fw_version; > + > + int pkt_size; > + int mode; > + > + int tp_min_pressure; > + int tp_max_pressure; > + int tp_width; /* X direction physical size in mm. */ > + int tp_high; /* Y direction physical size in mm. */ > + int tp_max_abs_x; /* Max X absolute units that can be reported. */ > + int tp_max_abs_y; /* Max Y absolute units that can be reported. */ > + > + int tp_res_x; /* X resolution in units/mm. */ > + int tp_res_y; /* Y resolution in units/mm. */ > + > + int tp_metrics_supported; > +}; > + > + > +#ifdef CONFIG_MOUSE_PS2_CYPRESS > +int cypress_detect(struct psmouse *psmouse, bool set_properties); > +int cypress_init(struct psmouse *psmouse); > +bool cypress_supported(void); > +#else > +inline int cypress_detect(struct psmouse *psmouse, bool set_properties) > +{ > + return -ENOSYS; > +} > +inline int cypress_init(struct psmouse *psmouse) > +{ > + return -ENOSYS; > +} > +inline bool cypress_supported(void) > +{ > + return 0; > +} > +#endif /* CONFIG_MOUSE_PS2_CYPRESS */ > + > +#endif /* _CYPRESS_PS2_H */ > diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c > index 22fe254..cff065f 100644 > --- a/drivers/input/mouse/psmouse-base.c > +++ b/drivers/input/mouse/psmouse-base.c > @@ -34,6 +34,7 @@ > #include "touchkit_ps2.h" > #include "elantech.h" > #include "sentelic.h" > +#include "cypress_ps2.h" > > #define DRIVER_DESC "PS/2 mouse driver" > > @@ -759,6 +760,28 @@ static int psmouse_extensions(struct psmouse *psmouse, > } > > /* > + * Try Cypress Trackpad. > + * Must try it before Finger Sensing Pad because Finger Sensing Pad probe > + * upsets some modules of Cypress Trackpads. > + */ > + if (max_proto > PSMOUSE_IMEX && > + cypress_detect(psmouse, set_properties) == 0) { > + if (cypress_supported()) { > + if (cypress_init(psmouse) == 0) > + return PSMOUSE_CYPRESS; > + > + /* > + * Finger Sensing Pad probe upsets some modules of > + * Cypress Trackpad, must avoid Finger Sensing Pad > + * probe if Cypress Trackpad device detected. > + */ > + return PSMOUSE_PS2; > + } > + > + max_proto = PSMOUSE_IMEX; > + } > + > +/* > * Try ALPS TouchPad > */ > if (max_proto > PSMOUSE_IMEX) { > @@ -896,6 +919,15 @@ static const struct psmouse_protocol psmouse_protocols[] = { > .alias = "thinkps", > .detect = thinking_detect, > }, > +#ifdef CONFIG_MOUSE_PS2_CYPRESS > + { > + .type = PSMOUSE_CYPRESS, > + .name = "CyPS/2", > + .alias = "cypress", > + .detect = cypress_detect, > + .init = cypress_init, > + }, > +#endif > { > .type = PSMOUSE_GENPS, > .name = "GenPS/2", > diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h > index fe1df23..2f0b39d 100644 > --- a/drivers/input/mouse/psmouse.h > +++ b/drivers/input/mouse/psmouse.h > @@ -95,6 +95,7 @@ enum psmouse_type { > PSMOUSE_ELANTECH, > PSMOUSE_FSP, > PSMOUSE_SYNAPTICS_RELATIVE, > + PSMOUSE_CYPRESS, > PSMOUSE_AUTO /* This one should always be last */ > }; > > -- > 1.7.10.4 > Reviewed-by: Henrik Rydberg <rydberg@euromail.se> Thanks, Henrik -- To unsubscribe from this list: send the line "unsubscribe linux-input" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Hi Henrik- On Sat, 2012-12-08 at 13:54 +0100, Henrik Rydberg wrote: > [...] > > --- a/drivers/input/mouse/Kconfig > > +++ b/drivers/input/mouse/Kconfig > > @@ -68,6 +68,16 @@ config MOUSE_PS2_SYNAPTICS > > > > If unsure, say Y. > > > > +config MOUSE_PS2_CYPRESS > > + bool "Cypress PS/2 mouse protocol extension" if EXPERT > > Why EXPERT here? > > > + default y > > Should it really be default y here? This config entry (with phrases "if EXPERT" and "default y") was simply cloned from similar devices (e.g. SYNAPTICS, ALPS, LIFEBOOK). If your preference is that CYPRESS should be disabled by default, I'll omit those phrases. Please advise. > > + case 0: return(4); > > No parenthesis on return. I'll fix that. > Reviewed-by: Henrik Rydberg <rydberg@euromail.se> Thanks! -Kamal -- To unsubscribe from this list: send the line "unsubscribe linux-input" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Hi Kamal, I think in default branch in cypress_get_finger_count() function return 0 is not suitable, We should return a value bigger than 5 to indicate invalid package to avoid unable to distinguish really 0 contact_cnt packages. 0 contact_cnt packages may still have valid left/right mechanical button events data, so cannot be mixed together. otherwise cypress_validate_byte() function cannot filter out invalid package data. Could you help double check with it, Thanks. > > This driver, submitted on behalf of Cypress Semiconductor Corporation > > and additional contributors, provides support for the Cypress PS/2 Trackpad. > > > > Note that the patch "increase struct ps2dev cmdbuf[] to 8 bytes" [5] > > is a PREREQUISITE for this patch. > > > > This [PATCH v6] version differs from my previous submitted version[4]: > > > > Changes as recommended by Dmitry Torokhov: > > > > - patches #2 (main driver) and #3 (link in driver) have been merged. > > - eliminated cytp_dbg(), is_cypress_key(), and cypress_set_abs_rel_mode(). > > - restructured cypress_validate_byte() and cypress_reset(). > > - stopped unnecessary fiddling with psmouse->pktsize. > > - fixed cypress_read_cmd_status() printf. > > - removed unused members from struct cytp_data. > > - misc code and comment style cleanups. > > > > Known problems: None. > > > > -Kamal Mostafa <kamal@canonical.com> > > > > [0] PATCH v1: http://www.spinics.net/lists/linux-input/msg23690.html > > [1] PATCH v2: http://www.spinics.net/lists/linux-input/msg23718.html > > [2] PATCH v3: http://www.spinics.net/lists/linux-input/msg23943.html > > [3] PATCH v4: http://www.spinics.net/lists/linux-input/msg24047.html > > [4] PATCH v5: http://www.spinics.net/lists/linux-input/msg24096.html > > [5] cmdbuf patch: > > http://www.spinics.net/lists/linux-input/msg24095.html > > > > -- >8 -- > > From: Dudley Du <dudl@cypress.com> > > Subject: [PATCH v6] input: Cypress PS/2 Trackpad psmouse driver > > > > Input/mouse driver for Cypress PS/2 Trackpad. > > > > Original code contributed by Dudley Du (Cypress Semiconductor > > Corporation), modified by Kamal Mostafa and Kyle Fazzari. > > > > BugLink: http://launchpad.net/bugs/978807 > > > > Signed-off-by: Dudley Du <dudl@cypress.com> > > Signed-off-by: Kamal Mostafa <kamal@canonical.com> > > Signed-off-by: Kyle Fazzari <git@status.e4ward.com> > > Signed-off-by: Mario Limonciello <mario_limonciello@dell.com> > > Signed-off-by: Tim Gardner <tim.gardner@canonical.com> > > Acked-by: Herton Krzesinski <herton.krzesinski@canonical.com> > > --- > > drivers/input/mouse/Kconfig | 10 + > > drivers/input/mouse/Makefile | 1 + > > drivers/input/mouse/cypress_ps2.c | 719 > > ++++++++++++++++++++++++++++++++++++ > > drivers/input/mouse/cypress_ps2.h | 191 ++++++++++ > > drivers/input/mouse/psmouse-base.c | 32 ++ > > drivers/input/mouse/psmouse.h | 1 + > > 6 files changed, 954 insertions(+) > > create mode 100644 drivers/input/mouse/cypress_ps2.c create mode > > 100644 drivers/input/mouse/cypress_ps2.h > > > > diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig > > index cd6268c..88954dd 100644 > > --- a/drivers/input/mouse/Kconfig > > +++ b/drivers/input/mouse/Kconfig > > @@ -68,6 +68,16 @@ config MOUSE_PS2_SYNAPTICS > > > > If unsure, say Y. > > > > +config MOUSE_PS2_CYPRESS > > + bool "Cypress PS/2 mouse protocol extension" if EXPERT > > Why EXPERT here? > > > + default y > > Should it really be default y here? > > > + depends on MOUSE_PS2 > > + help > > + Say Y here if you have a Cypress PS/2 Trackpad connected to > > + your system. > > + > > + If unsure, say Y. > > + > > config MOUSE_PS2_LIFEBOOK > > bool "Fujitsu Lifebook PS/2 mouse protocol extension" if EXPERT > > default y > > diff --git a/drivers/input/mouse/Makefile > > b/drivers/input/mouse/Makefile index 46ba755..323e352 100644 > > --- a/drivers/input/mouse/Makefile > > +++ b/drivers/input/mouse/Makefile > > @@ -32,3 +32,4 @@ psmouse-$(CONFIG_MOUSE_PS2_LIFEBOOK) += lifebook.o > > psmouse-$(CONFIG_MOUSE_PS2_SENTELIC) += sentelic.o > > psmouse-$(CONFIG_MOUSE_PS2_TRACKPOINT) += trackpoint.o > > psmouse-$(CONFIG_MOUSE_PS2_TOUCHKIT) += touchkit_ps2.o > > +psmouse-$(CONFIG_MOUSE_PS2_CYPRESS) += cypress_ps2.o > > diff --git a/drivers/input/mouse/cypress_ps2.c > > b/drivers/input/mouse/cypress_ps2.c > > new file mode 100644 > > index 0000000..310cb49 > > --- /dev/null > > +++ b/drivers/input/mouse/cypress_ps2.c > > @@ -0,0 +1,719 @@ > > +/* > > + * Cypress Trackpad PS/2 mouse driver > > + * > > + * Copyright (c) 2012 Cypress Semiconductor Corporation. > > + * > > + * Author: > > + * Dudley Du <dudl@cypress.com> > > + * > > + * Additional contributors include: > > + * Kamal Mostafa <kamal@canonical.com> > > + * Kyle Fazzari <git@status.e4ward.com> > > + * > > + * 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. > > + */ > > + > > +#include <linux/init.h> > > +#include <linux/module.h> > > +#include <linux/kernel.h> > > +#include <linux/slab.h> > > +#include <linux/serio.h> > > +#include <linux/libps2.h> > > +#include <linux/input.h> > > +#include <linux/input/mt.h> > > +#include <linux/sched.h> > > +#include <linux/wait.h> > > + > > +#include "cypress_ps2.h" > > + > > +#undef CYTP_DEBUG_VERBOSE /* define this and DEBUG for more verbose > > +dump */ > > + > > +static void cypress_set_packet_size(struct psmouse *psmouse, unsigned > > +int n) { > > + struct cytp_data *cytp = psmouse->private; > > + cytp->pkt_size = n; > > +} > > + > > +static const unsigned char cytp_rate[] = {10, 20, 40, 60, 100, 200}; > > +static const unsigned char cytp_resolution[] = {0x00, 0x01, 0x02, > > +0x03}; > > + > > +static int cypress_ps2_sendbyte(struct psmouse *psmouse, int value) { > > + struct ps2dev *ps2dev = &psmouse->ps2dev; > > + > > + if (ps2_sendbyte(ps2dev, value & 0xff, CYTP_CMD_TIMEOUT) < 0) { > > + psmouse_dbg(psmouse, > > + "send command 0x%02x failed, resp 0x%02x\n", > > + value & 0xff, ps2dev->nak); > > + if (ps2dev->nak == CYTP_PS2_RETRY) > > + return CYTP_PS2_RETRY; > > + else > > + return CYTP_PS2_ERROR; > > + } > > + > > +#ifdef CYTP_DEBUG_VERBOSE > > + psmouse_dbg(psmouse, "send command 0x%02x success, resp 0xfa\n", > > + value & 0xff); > > +#endif > > + > > + return 0; > > +} > > + > > +static int cypress_ps2_ext_cmd(struct psmouse *psmouse, unsigned short cmd, > > + unsigned char data) > > +{ > > + struct ps2dev *ps2dev = &psmouse->ps2dev; > > + int tries = CYTP_PS2_CMD_TRIES; > > + int rc; > > + > > + ps2_begin_command(ps2dev); > > + > > + do { > > + /* > > + * Send extension command byte (0xE8 or 0xF3). > > + * If sending the command fails, send recovery command > > + * to make the device return to the ready state. > > + */ > > + rc = cypress_ps2_sendbyte(psmouse, cmd & 0xff); > > + if (rc == CYTP_PS2_RETRY) { > > + rc = cypress_ps2_sendbyte(psmouse, 0x00); > > + if (rc == CYTP_PS2_RETRY) > > + rc = cypress_ps2_sendbyte(psmouse, 0x0a); > > + } > > + if (rc == CYTP_PS2_ERROR) > > + continue; > > + > > + rc = cypress_ps2_sendbyte(psmouse, data); > > + if (rc == CYTP_PS2_RETRY) > > + rc = cypress_ps2_sendbyte(psmouse, data); > > + if (rc == CYTP_PS2_ERROR) > > + continue; > > + else > > + break; > > + } while (--tries > 0); > > + > > + ps2_end_command(ps2dev); > > + > > + return rc; > > +} > > + > > +static int cypress_ps2_read_cmd_status(struct psmouse *psmouse, > > + unsigned char cmd, > > + unsigned char *param) > > +{ > > + int rc; > > + struct ps2dev *ps2dev = &psmouse->ps2dev; > > + enum psmouse_state old_state; > > + int pktsize; > > + > > + ps2_begin_command(&psmouse->ps2dev); > > + > > + old_state = psmouse->state; > > + psmouse->state = PSMOUSE_CMD_MODE; > > + psmouse->pktcnt = 0; > > + > > + pktsize = (cmd == CYTP_CMD_READ_TP_METRICS) ? 8 : 3; > > + memset(param, 0, pktsize); > > + > > + rc = cypress_ps2_sendbyte(psmouse, 0xe9); > > + if (rc < 0) > > + goto out; > > + > > + wait_event_timeout(ps2dev->wait, > > + (psmouse->pktcnt >= pktsize), > > + msecs_to_jiffies(CYTP_CMD_TIMEOUT)); > > + > > + memcpy(param, psmouse->packet, pktsize); > > + > > + psmouse_dbg(psmouse, "Command 0x%02x response data (0x): %*ph\n", > > + cmd, pktsize, param); > > + > > +out: > > + psmouse->state = old_state; > > + psmouse->pktcnt = 0; > > + > > + ps2_end_command(&psmouse->ps2dev); > > + > > + return rc; > > +} > > + > > +static int cypress_verify_cmd_state(struct psmouse *psmouse, > > + unsigned char cmd, unsigned char *param) { > > + bool rate_match = 0; > > + bool resolution_match = 0; > > + int i; > > + > > + /* callers will do further checking. */ > > + if (cmd == CYTP_CMD_READ_CYPRESS_ID || > > + cmd == CYTP_CMD_STANDARD_MODE || > > + cmd == CYTP_CMD_READ_TP_METRICS) > > + return 0; > > + if ((~param[0] & DFLT_RESP_BITS_VALID) == DFLT_RESP_BITS_VALID && > > + (param[0] & DFLT_RESP_BIT_MODE) == DFLT_RESP_STREAM_MODE) { > > + for (i = 0; i < sizeof(cytp_resolution); i++) > > + if (cytp_resolution[i] == param[1]) > > + resolution_match = 1; > > + > > + for (i = 0; i < sizeof(cytp_rate); i++) > > + if (cytp_rate[i] == param[2]) > > + rate_match = 1; > > + > > + if (resolution_match && rate_match) > > + return 0; > > + } > > + > > + psmouse_dbg(psmouse, "verify cmd state failed.\n"); > > + return -1; > > +} > > + > > +static int cypress_send_ext_cmd(struct psmouse *psmouse, unsigned char cmd, > > + unsigned char *param) > > +{ > > + int tries = CYTP_PS2_CMD_TRIES; > > + int rc; > > + > > + psmouse_dbg(psmouse, "send extension cmd 0x%02x, [%d %d %d %d]\n", > > + cmd, DECODE_CMD_AA(cmd), DECODE_CMD_BB(cmd), > > + DECODE_CMD_CC(cmd), DECODE_CMD_DD(cmd)); > > + do { > > + cypress_ps2_ext_cmd(psmouse, > > + PSMOUSE_CMD_SETRES, DECODE_CMD_DD(cmd)); > > + cypress_ps2_ext_cmd(psmouse, > > + PSMOUSE_CMD_SETRES, DECODE_CMD_CC(cmd)); > > + cypress_ps2_ext_cmd(psmouse, > > + PSMOUSE_CMD_SETRES, DECODE_CMD_BB(cmd)); > > + cypress_ps2_ext_cmd(psmouse, > > + PSMOUSE_CMD_SETRES, DECODE_CMD_AA(cmd)); > > + > > + rc = cypress_ps2_read_cmd_status(psmouse, cmd, param); > > + if (rc) > > + continue; > > + > > + rc = cypress_verify_cmd_state(psmouse, cmd, param); > > + if (rc == 0) > > + return 0; > > + } while (--tries > 0); > > + > > + return -1; > > +} > > + > > +int cypress_detect(struct psmouse *psmouse, bool set_properties) { > > + unsigned char param[3]; > > + > > + if (cypress_send_ext_cmd(psmouse, CYTP_CMD_READ_CYPRESS_ID, param)) > > + return -ENODEV; > > + > > + /* Check for Cypress Trackpad signature bytes: 0x33 0xCC */ > > + if (param[0] != 0x33 || param[1] != 0xCC) > > + return -ENODEV; > > + > > + if (set_properties) { > > + psmouse->vendor = "Cypress"; > > + psmouse->name = "Trackpad"; > > + } > > + > > + return 0; > > +} > > + > > +static int cypress_read_fw_version(struct psmouse *psmouse) { > > + struct cytp_data *cytp = psmouse->private; > > + unsigned char param[3]; > > + > > + if (cypress_send_ext_cmd(psmouse, CYTP_CMD_READ_CYPRESS_ID, param)) > > + return -ENODEV; > > + > > + /* Check for Cypress Trackpad signature bytes: 0x33 0xCC */ > > + if (param[0] != 0x33 || param[1] != 0xCC) > > + return -ENODEV; > > + > > + cytp->fw_version = param[2] & FW_VERSION_MASX; > > + cytp->tp_metrics_supported = (param[2] & TP_METRICS_MASK) ? 1 : 0; > > + > > + psmouse_dbg(psmouse, "cytp->fw_version = %d\n", cytp->fw_version); > > + psmouse_dbg(psmouse, "cytp->tp_metrics_supported = %d\n", > > + cytp->tp_metrics_supported); > > + return 0; > > +} > > + > > +static int cypress_read_tp_metrics(struct psmouse *psmouse) { > > + struct cytp_data *cytp = psmouse->private; > > + unsigned char param[8]; > > + > > + /* set default values for tp metrics. */ > > + cytp->tp_width = CYTP_DEFAULT_WIDTH; > > + cytp->tp_high = CYTP_DEFAULT_HIGH; > > + cytp->tp_max_abs_x = CYTP_ABS_MAX_X; > > + cytp->tp_max_abs_y = CYTP_ABS_MAX_Y; > > + cytp->tp_min_pressure = CYTP_MIN_PRESSURE; > > + cytp->tp_max_pressure = CYTP_MAX_PRESSURE; > > + cytp->tp_res_x = cytp->tp_max_abs_x / cytp->tp_width; > > + cytp->tp_res_y = cytp->tp_max_abs_y / cytp->tp_high; > > + > > + memset(param, 0, sizeof(param)); > > + if (cypress_send_ext_cmd(psmouse, CYTP_CMD_READ_TP_METRICS, param) == 0) { > > + /* Update trackpad parameters. */ > > + cytp->tp_max_abs_x = (param[1] << 8) | param[0]; > > + cytp->tp_max_abs_y = (param[3] << 8) | param[2]; > > + cytp->tp_min_pressure = param[4]; > > + cytp->tp_max_pressure = param[5]; > > + } > > + > > + if (!cytp->tp_max_pressure || > > + cytp->tp_max_pressure < cytp->tp_min_pressure || > > + !cytp->tp_width || !cytp->tp_high || > > + !cytp->tp_max_abs_x || > > + cytp->tp_max_abs_x < cytp->tp_width || > > + !cytp->tp_max_abs_y || > > + cytp->tp_max_abs_y < cytp->tp_high) > > + return -1; > > + > > + cytp->tp_res_x = cytp->tp_max_abs_x / cytp->tp_width; > > + cytp->tp_res_y = cytp->tp_max_abs_y / cytp->tp_high; > > + > > +#ifdef CYTP_DEBUG_VERBOSE > > + psmouse_dbg(psmouse, "Dump trackpad hardware configuration as below:\n"); > > + psmouse_dbg(psmouse, "cytp->tp_width = %d\n", cytp->tp_width); > > + psmouse_dbg(psmouse, "cytp->tp_high = %d\n", cytp->tp_high); > > + psmouse_dbg(psmouse, "cytp->tp_max_abs_x = %d\n", cytp->tp_max_abs_x); > > + psmouse_dbg(psmouse, "cytp->tp_max_abs_y = %d\n", cytp->tp_max_abs_y); > > + psmouse_dbg(psmouse, "cytp->tp_min_pressure = %d\n", cytp->tp_min_pressure); > > + psmouse_dbg(psmouse, "cytp->tp_max_pressure = %d\n", cytp->tp_max_pressure); > > + psmouse_dbg(psmouse, "cytp->tp_res_x = %d\n", cytp->tp_res_x); > > + psmouse_dbg(psmouse, "cytp->tp_res_y = %d\n", cytp->tp_res_y); > > + > > + psmouse_dbg(psmouse, "tp_type_APA = %d\n", > > + (param[6] & TP_METRICS_BIT_APA) ? 1 : 0); > > + psmouse_dbg(psmouse, "tp_type_MTG = %d\n", > > + (param[6] & TP_METRICS_BIT_MTG) ? 1 : 0); > > + psmouse_dbg(psmouse, "tp_palm = %d\n", > > + (param[6] & TP_METRICS_BIT_PALM) ? 1 : 0); > > + psmouse_dbg(psmouse, "tp_stubborn = %d\n", > > + (param[6] & TP_METRICS_BIT_STUBBORN) ? 1 : 0); > > + psmouse_dbg(psmouse, "tp_1f_jitter = %d\n", > > + (param[6] & TP_METRICS_BIT_1F_JITTER) >> 2); > > + psmouse_dbg(psmouse, "tp_2f_jitter = %d\n", > > + (param[6] & TP_METRICS_BIT_2F_JITTER) >> 4); > > + psmouse_dbg(psmouse, "tp_1f_spike = %d\n", > > + param[7] & TP_METRICS_BIT_1F_SPIKE); > > + psmouse_dbg(psmouse, "tp_2f_spike = %d\n", > > + (param[7] & TP_METRICS_BIT_2F_SPIKE) >> 2); > > + psmouse_dbg(psmouse, "tp_abs_packet_format_set = %d\n", > > + (param[7] & TP_METRICS_BIT_ABS_PKT_FORMAT_SET) >> 4); #endif > > + > > + return 0; > > +} > > + > > +static int cypress_query_hardware(struct psmouse *psmouse) { > > + struct cytp_data *cytp = psmouse->private; > > + int ret; > > + > > + ret = cypress_read_fw_version(psmouse); > > + if (ret) > > + return ret; > > + > > + if (cytp->tp_metrics_supported) { > > + ret = cypress_read_tp_metrics(psmouse); > > + if (ret) > > + return ret; > > + } > > + > > + return 0; > > +} > > + > > +static int cypress_set_absolute_mode(struct psmouse *psmouse) { > > + struct cytp_data *cytp = psmouse->private; > > + unsigned char param[3]; > > + > > + if (cypress_send_ext_cmd(psmouse, CYTP_CMD_ABS_WITH_PRESSURE_MODE, param) < 0) > > + return -1; > > + > > + cytp->mode = (cytp->mode & ~CYTP_BIT_ABS_REL_MASK) > > + | CYTP_BIT_ABS_PRESSURE; > > + cypress_set_packet_size(psmouse, 5); > > + > > + return 0; > > +} > > + > > +/* > > + * Reset trackpad device. > > + * This is also the default mode when trackpad powered on. > > + */ > > +static void cypress_reset(struct psmouse *psmouse) { > > + struct cytp_data *cytp = psmouse->private; > > + > > + cytp->mode = 0; > > + > > + psmouse_reset(psmouse); > > +} > > + > > +static int cypress_set_input_params(struct input_dev *input, > > + struct cytp_data *cytp) > > +{ > > + int ret; > > + > > + if (!cytp->tp_res_x || !cytp->tp_res_y) > > + return -EINVAL; > > + > > + __set_bit(EV_ABS, input->evbit); > > + input_set_abs_params(input, ABS_X, 0, cytp->tp_max_abs_x, 0, 0); > > + input_set_abs_params(input, ABS_Y, 0, cytp->tp_max_abs_y, 0, 0); > > + input_set_abs_params(input, ABS_PRESSURE, > > + cytp->tp_min_pressure, cytp->tp_max_pressure, 0, 0); > > + input_set_abs_params(input, ABS_TOOL_WIDTH, 0, 255, 0, 0); > > + > > + /* finger position */ > > + input_set_abs_params(input, ABS_MT_POSITION_X, 0, cytp->tp_max_abs_x, 0, 0); > > + input_set_abs_params(input, ABS_MT_POSITION_Y, 0, cytp->tp_max_abs_y, 0, 0); > > + input_set_abs_params(input, ABS_MT_PRESSURE, 0, 255, 0, 0); > > + > > + ret = input_mt_init_slots(input, CYTP_MAX_MT_SLOTS, > > + INPUT_MT_DROP_UNUSED|INPUT_MT_TRACK); > > + if (ret < 0) > > + return ret; > > + > > + __set_bit(INPUT_PROP_SEMI_MT, input->propbit); > > + > > + input_abs_set_res(input, ABS_X, cytp->tp_res_x); > > + input_abs_set_res(input, ABS_Y, cytp->tp_res_y); > > + > > + input_abs_set_res(input, ABS_MT_POSITION_X, cytp->tp_res_x); > > + input_abs_set_res(input, ABS_MT_POSITION_Y, cytp->tp_res_y); > > + > > + __set_bit(BTN_TOUCH, input->keybit); > > + __set_bit(BTN_TOOL_FINGER, input->keybit); > > + __set_bit(BTN_TOOL_DOUBLETAP, input->keybit); > > + __set_bit(BTN_TOOL_TRIPLETAP, input->keybit); > > + __set_bit(BTN_TOOL_QUADTAP, input->keybit); > > + __set_bit(BTN_TOOL_QUINTTAP, input->keybit); > > + > > + __clear_bit(EV_REL, input->evbit); > > + __clear_bit(REL_X, input->relbit); > > + __clear_bit(REL_Y, input->relbit); > > + > > + __set_bit(INPUT_PROP_BUTTONPAD, input->propbit); > > + __set_bit(EV_KEY, input->evbit); > > + __set_bit(BTN_LEFT, input->keybit); > > + __set_bit(BTN_RIGHT, input->keybit); > > + __set_bit(BTN_MIDDLE, input->keybit); > > + > > + input_set_drvdata(input, cytp); > > + > > + return 0; > > +} > > + > > +static int cypress_get_finger_count(unsigned char header_byte) { > > + unsigned char bits6_7; > > + int finger_count; > > + > > + bits6_7 = header_byte >> 6; > > + finger_count = bits6_7 & 0x03; > > + > > + if (finger_count == 1) > > + return 1; > > + > > + if (header_byte & ABS_HSCROLL_BIT) { > > + /* HSCROLL gets added on to 0 finger count. */ > > + switch (finger_count) { > > + case 0: return(4); > > No parenthesis on return. > > > + case 2: return(5); > > + default: > > + /* Invalid contact (e.g. palm). Ignore it. */ > > + return(0); I suggest to return 6 (bigger than 5) to indicate invalid header byte value detected. > > + } > > + } > > + > > + return finger_count; > > +} > > + > > + > > +static int cypress_parse_packet(struct psmouse *psmouse, > > + struct cytp_data *cytp, struct cytp_report_data *report_data) { > > + unsigned char *packet = psmouse->packet; > > + unsigned char header_byte = packet[0]; > > + > > + memset(report_data, 0, sizeof(struct cytp_report_data)); > > + > > + report_data->contact_cnt = cypress_get_finger_count(header_byte); > > + > > + if (report_data->contact_cnt == 0) /* e.g. palm detect */ > > + return 0; Depending on my suggestion, should add contact_cnt > 5 check here, and equal to 0 checking should be removed. When invalid contact_cnt is checked, we can ignore these data. But for though really contact_cnt value is 0 packages, we should still go on to process it, cannot return immediately, Because 0 contact_cnt package still can report left/right mechanical button events. if (report_data->contact_cnt > 5) { report_data->contact_cnt = 0; return 0; } > > + report_data->tap = (header_byte & ABS_MULTIFINGER_TAP) ? 1 : 0; > > + > > + if (report_data->contact_cnt == 1) { > > + report_data->contacts[0].x = > > + ((packet[1] & 0x70) << 4) | packet[2]; > > + report_data->contacts[0].y = > > + ((packet[1] & 0x07) << 8) | packet[3]; > > + if (cytp->mode & CYTP_BIT_ABS_PRESSURE) > > + report_data->contacts[0].z = packet[4]; > > + > > + } else if (report_data->contact_cnt >= 2) { > > + report_data->contacts[0].x = > > + ((packet[1] & 0x70) << 4) | packet[2]; > > + report_data->contacts[0].y = > > + ((packet[1] & 0x07) << 8) | packet[3]; > > + if (cytp->mode & CYTP_BIT_ABS_PRESSURE) > > + report_data->contacts[0].z = packet[4]; > > + > > + report_data->contacts[1].x = > > + ((packet[5] & 0xf0) << 4) | packet[6]; > > + report_data->contacts[1].y = > > + ((packet[5] & 0x0f) << 8) | packet[7]; > > + if (cytp->mode & CYTP_BIT_ABS_PRESSURE) > > + report_data->contacts[1].z = report_data->contacts[0].z; > > + } > > + > > + report_data->left = (header_byte & BTN_LEFT_BIT) ? 1 : 0; > > + report_data->right = (header_byte & BTN_RIGHT_BIT) ? 1 : 0; > > + > > + /* > > + * This is only true if one of the mouse buttons were tapped. Make > > + * sure it doesn't turn into a click. The regular tap-to-click > > + * functionality will handle that on its own. If we don't do this, > > + * disabling tap-to-click won't affect the mouse button zones. > > + */ > > + if (report_data->tap) > > + report_data->left = 0; > > + > > +#ifdef CYTP_DEBUG_VERBOSE > > + { > > + int i; > > + int n = report_data->contact_cnt; > > + psmouse_dbg(psmouse, "Dump parsed report data as below:\n"); > > + psmouse_dbg(psmouse, "contact_cnt = %d\n", > > + report_data->contact_cnt); > > + if (n > CYTP_MAX_MT_SLOTS) > > + n = CYTP_MAX_MT_SLOTS; > > + for (i = 0; i < n; i++) > > + psmouse_dbg(psmouse, "contacts[%d] = {%d, %d, %d}\n", i, > > + report_data->contacts[i].x, > > + report_data->contacts[i].y, > > + report_data->contacts[i].z); > > + psmouse_dbg(psmouse, "left = %d\n", report_data->left); > > + psmouse_dbg(psmouse, "right = %d\n", report_data->right); > > + psmouse_dbg(psmouse, "middle = %d\n", report_data->middle); > > + } > > +#endif > > + > > + return 0; > > +} > > + > > +static void cypress_process_packet(struct psmouse *psmouse, bool > > +zero_pkt) { > > + int i; > > + struct input_dev *input = psmouse->dev; > > + struct cytp_data *cytp = psmouse->private; > > + struct cytp_report_data report_data; > > + struct cytp_contact *contact; > > + struct input_mt_pos pos[CYTP_MAX_MT_SLOTS]; > > + int slots[CYTP_MAX_MT_SLOTS]; > > + int n; > > + > > + if (cypress_parse_packet(psmouse, cytp, &report_data)) > > + return; > > + > > + n = report_data.contact_cnt; > > + > > + if (n > CYTP_MAX_MT_SLOTS) > > + n = CYTP_MAX_MT_SLOTS; > > + > > + for (i = 0; i < n; i++) { > > + contact = &report_data.contacts[i]; > > + pos[i].x = contact->x; > > + pos[i].y = contact->y; > > + } > > + > > + input_mt_assign_slots(input, slots, pos, n); > > + > > + for (i = 0; i < n; i++) { > > + contact = &report_data.contacts[i]; > > + input_mt_slot(input, slots[i]); > > + input_mt_report_slot_state(input, MT_TOOL_FINGER, true); > > + input_report_abs(input, ABS_MT_POSITION_X, contact->x); > > + input_report_abs(input, ABS_MT_POSITION_Y, contact->y); > > + input_report_abs(input, ABS_MT_PRESSURE, contact->z); > > + } > > + > > + input_mt_sync_frame(input); > > + > > + input_mt_report_finger_count(input, report_data.contact_cnt); > > + > > + input_report_key(input, BTN_LEFT, report_data.left); > > + input_report_key(input, BTN_RIGHT, report_data.right); > > + input_report_key(input, BTN_MIDDLE, report_data.middle); > > + > > + input_sync(input); > > +} > > + > > +static psmouse_ret_t cypress_validate_byte(struct psmouse *psmouse) { > > + int contact_cnt; > > + int index = psmouse->pktcnt - 1; > > + unsigned char *packet = psmouse->packet; > > + struct cytp_data *cytp = psmouse->private; > > + > > + if (index < 0 || index > cytp->pkt_size) > > + return PSMOUSE_BAD_DATA; > > + > > + if (index == 0 && (packet[0] & 0xfc) == 0) { > > + /* call packet process for reporting finger leave. */ > > + cypress_process_packet(psmouse, 1); > > + return PSMOUSE_FULL_PACKET; > > + } > > + > > + /* > > + * Perform validation (and adjust packet size) based only on the > > + * first byte; allow all further bytes through. > > + */ > > + if (index != 0) > > + return PSMOUSE_GOOD_DATA; > > + > > + /* > > + * If absolute/relative mode bit has not been set yet, just pass > > + * the byte through. > > + */ > > + if ((cytp->mode & CYTP_BIT_ABS_REL_MASK) == 0) > > + return PSMOUSE_GOOD_DATA; > > + > > + if ((packet[0] & 0x08) == 0x08) > > + return PSMOUSE_BAD_DATA; > > + > > + contact_cnt = cypress_get_finger_count(packet[0]); > > + > > + if (contact_cnt > 5) > > + return PSMOUSE_BAD_DATA; > > + > > + if (cytp->mode & CYTP_BIT_ABS_NO_PRESSURE) > > + cypress_set_packet_size(psmouse, contact_cnt == 2 ? 7 : 4); > > + else > > + cypress_set_packet_size(psmouse, contact_cnt == 2 ? 8 : 5); > > + > > + return PSMOUSE_GOOD_DATA; > > +} > > + > > +static psmouse_ret_t cypress_protocol_handler(struct psmouse > > +*psmouse) { > > + struct cytp_data *cytp = psmouse->private; > > + > > + if (psmouse->pktcnt >= cytp->pkt_size) { > > + cypress_process_packet(psmouse, 0); > > + return PSMOUSE_FULL_PACKET; > > + } > > + > > + return cypress_validate_byte(psmouse); } > > + > > +static void cypress_set_rate(struct psmouse *psmouse, unsigned int > > +rate) { > > + struct cytp_data *cytp = psmouse->private; > > + > > + if (rate >= 80) { > > + psmouse->rate = 80; > > + cytp->mode |= CYTP_BIT_HIGH_RATE; > > + } else { > > + psmouse->rate = 40; > > + cytp->mode &= ~CYTP_BIT_HIGH_RATE; > > + } > > + > > + ps2_command(&psmouse->ps2dev, (unsigned char *)&psmouse->rate, > > + PSMOUSE_CMD_SETRATE); > > +} > > + > > +static void cypress_disconnect(struct psmouse *psmouse) { > > + cypress_reset(psmouse); > > + kfree(psmouse->private); > > + psmouse->private = NULL; > > +} > > + > > +static int cypress_reconnect(struct psmouse *psmouse) { > > + int tries = CYTP_PS2_CMD_TRIES; > > + int rc; > > + > > + do { > > + cypress_reset(psmouse); > > + rc = cypress_detect(psmouse, false); > > + } while (rc && (--tries > 0)); > > + > > + if (rc) { > > + psmouse_err(psmouse, "Reconnect: unable to detect trackpad.\n"); > > + return -1; > > + } > > + > > + if (cypress_set_absolute_mode(psmouse)) { > > + psmouse_err(psmouse, "Reconnect: Unable to initialize Cypress absolute mode.\n"); > > + return -1; > > + } > > + > > + return 0; > > +} > > + > > +int cypress_init(struct psmouse *psmouse) { > > + struct cytp_data *cytp; > > + > > + cytp = (struct cytp_data *)kzalloc(sizeof(struct cytp_data), GFP_KERNEL); > > + psmouse->private = (void *)cytp; > > + if (cytp == NULL) > > + return -ENOMEM; > > + > > + cypress_reset(psmouse); > > + > > + psmouse->pktsize = 8; > > + > > + if (cypress_query_hardware(psmouse)) { > > + psmouse_err(psmouse, "Unable to query Trackpad hardware.\n"); > > + goto err_exit; > > + } > > + > > + if (cypress_set_absolute_mode(psmouse)) { > > + psmouse_err(psmouse, "init: Unable to initialize Cypress absolute mode.\n"); > > + goto err_exit; > > + } > > + > > + if (cypress_set_input_params(psmouse->dev, cytp) < 0) { > > + psmouse_err(psmouse, "init: Unable to set input params.\n"); > > + goto err_exit; > > + } > > + > > + psmouse->model = 1; > > + psmouse->protocol_handler = cypress_protocol_handler; > > + psmouse->set_rate = cypress_set_rate; > > + psmouse->disconnect = cypress_disconnect; > > + psmouse->reconnect = cypress_reconnect; > > + psmouse->cleanup = cypress_reset; > > + psmouse->resync_time = 0; > > + > > + return 0; > > + > > +err_exit: > > + /* > > + * Reset Cypress Trackpad as a standard mouse. Then > > + * let psmouse driver commmunicating with it as default PS2 mouse. > > + */ > > + cypress_reset(psmouse); > > + > > + psmouse->private = NULL; > > + kfree(cytp); > > + > > + return -1; > > +} > > + > > +bool cypress_supported(void) > > +{ > > + return true; > > +} > > diff --git a/drivers/input/mouse/cypress_ps2.h > > b/drivers/input/mouse/cypress_ps2.h > > new file mode 100644 > > index 0000000..4720f21 > > --- /dev/null > > +++ b/drivers/input/mouse/cypress_ps2.h > > @@ -0,0 +1,191 @@ > > +#ifndef _CYPRESS_PS2_H > > +#define _CYPRESS_PS2_H > > + > > +#include "psmouse.h" > > + > > +#define CMD_BITS_MASK 0x03 > > +#define COMPOSIT(x, s) (((x) & CMD_BITS_MASK) << (s)) > > + > > +#define ENCODE_CMD(aa, bb, cc, dd) \ > > + (COMPOSIT((aa), 6) | COMPOSIT((bb), 4) | COMPOSIT((cc), 2) | COMPOSIT((dd), 0)) > > +#define CYTP_CMD_ABS_NO_PRESSURE_MODE ENCODE_CMD(0, 1, 0, 0) > > +#define CYTP_CMD_ABS_WITH_PRESSURE_MODE ENCODE_CMD(0, 1, 0, 1) > > +#define CYTP_CMD_SMBUS_MODE ENCODE_CMD(0, 1, 1, 0) > > +#define CYTP_CMD_STANDARD_MODE ENCODE_CMD(0, 2, 0, 0) /* not implemented yet. */ > > +#define CYTP_CMD_CYPRESS_REL_MODE ENCODE_CMD(1, 1, 1, 1) /* not implemented yet. */ > > +#define CYTP_CMD_READ_CYPRESS_ID ENCODE_CMD(0, 0, 0, 0) > > +#define CYTP_CMD_READ_TP_METRICS ENCODE_CMD(0, 0, 0, 1) > > +#define CYTP_CMD_SET_HSCROLL_WIDTH(w) ENCODE_CMD(1, 1, 0, (w)) > > +#define CYTP_CMD_SET_HSCROLL_MASK ENCODE_CMD(1, 1, 0, 0) > > +#define CYTP_CMD_SET_VSCROLL_WIDTH(w) ENCODE_CMD(1, 2, 0, (w)) > > +#define CYTP_CMD_SET_VSCROLL_MASK ENCODE_CMD(1, 2, 0, 0) > > +#define CYTP_CMD_SET_PALM_GEOMETRY(e) ENCODE_CMD(1, 2, 1, (e)) > > +#define CYTP_CMD_PALM_GEMMETRY_MASK ENCODE_CMD(1, 2, 1, 0) > > +#define CYTP_CMD_SET_PALM_SENSITIVITY(s) ENCODE_CMD(1, 2, 2, (s)) > > +#define CYTP_CMD_PALM_SENSITIVITY_MASK ENCODE_CMD(1, 2, 2, 0) > > +#define CYTP_CMD_SET_MOUSE_SENSITIVITY(s) ENCODE_CMD(1, 3, ((s) >> 2), (s)) > > +#define CYTP_CMD_MOUSE_SENSITIVITY_MASK ENCODE_CMD(1, 3, 0, 0) > > +#define CYTP_CMD_REQUEST_BASELINE_STATUS ENCODE_CMD(2, 0, 0, 1) > > +#define CYTP_CMD_REQUEST_RECALIBRATION ENCODE_CMD(2, 0, 0, 3) > > + > > +#define DECODE_CMD_AA(x) (((x) >> 6) & CMD_BITS_MASK) #define > > +DECODE_CMD_BB(x) (((x) >> 4) & CMD_BITS_MASK) #define > > +DECODE_CMD_CC(x) (((x) >> 2) & CMD_BITS_MASK) #define > > +DECODE_CMD_DD(x) ((x) & CMD_BITS_MASK) > > + > > +/* Cypress trackpad working mode. */ > > +#define CYTP_BIT_ABS_PRESSURE (1 << 3) > > +#define CYTP_BIT_ABS_NO_PRESSURE (1 << 2) > > +#define CYTP_BIT_CYPRESS_REL (1 << 1) > > +#define CYTP_BIT_STANDARD_REL (1 << 0) > > +#define CYTP_BIT_REL_MASK (CYTP_BIT_CYPRESS_REL | > > +CYTP_BIT_STANDARD_REL) #define CYTP_BIT_ABS_MASK > > +(CYTP_BIT_ABS_PRESSURE | CYTP_BIT_ABS_NO_PRESSURE) #define > > +CYTP_BIT_ABS_REL_MASK (CYTP_BIT_ABS_MASK | CYTP_BIT_REL_MASK) > > + > > +#define CYTP_BIT_HIGH_RATE (1 << 4) > > +/* > > + * report mode bit is set, firmware working in Remote Mode. > > + * report mode bit is cleared, firmware working in Stream Mode. > > + */ > > +#define CYTP_BIT_REPORT_MODE (1 << 5) > > + > > +/* scrolling width values for set HSCROLL and VSCROLL width command. > > +*/ #define SCROLL_WIDTH_NARROW 1 #define SCROLL_WIDTH_NORMAL 2 > > +#define SCROLL_WIDTH_WIDE 3 > > + > > +#define PALM_GEOMETRY_ENABLE 1 > > +#define PALM_GEOMETRY_DISABLE 0 > > + > > +#define TP_METRICS_MASK 0x80 > > +#define FW_VERSION_MASX 0x7f > > +#define FW_VER_HIGH_MASK 0x70 > > +#define FW_VER_LOW_MASK 0x0f > > + > > +/* Times to retry a ps2_command and millisecond delay between tries. > > +*/ #define CYTP_PS2_CMD_TRIES 3 #define CYTP_PS2_CMD_DELAY 500 > > + > > +/* time out for PS/2 command only in milliseconds. */ #define > > +CYTP_CMD_TIMEOUT 200 #define CYTP_DATA_TIMEOUT 30 > > + > > +#define CYTP_EXT_CMD 0xe8 > > +#define CYTP_PS2_RETRY 0xfe > > +#define CYTP_PS2_ERROR 0xfc > > + > > +#define CYTP_RESP_RETRY 0x01 > > +#define CYTP_RESP_ERROR 0xfe > > + > > + > > +#define CYTP_105001_WIDTH 97 /* Dell XPS 13 */ > > +#define CYTP_105001_HIGH 59 > > +#define CYTP_DEFAULT_WIDTH (CYTP_105001_WIDTH) #define > > +CYTP_DEFAULT_HIGH (CYTP_105001_HIGH) > > + > > +#define CYTP_ABS_MAX_X 1600 > > +#define CYTP_ABS_MAX_Y 900 > > +#define CYTP_MAX_PRESSURE 255 > > +#define CYTP_MIN_PRESSURE 0 > > + > > +/* header byte bits of relative package. */ > > +#define BTN_LEFT_BIT 0x01 > > +#define BTN_RIGHT_BIT 0x02 > > +#define BTN_MIDDLE_BIT 0x04 > > +#define REL_X_SIGN_BIT 0x10 > > +#define REL_Y_SIGN_BIT 0x20 > > + > > +/* header byte bits of absolute package. */ #define ABS_VSCROLL_BIT > > +0x10 #define ABS_HSCROLL_BIT 0x20 #define ABS_MULTIFINGER_TAP 0x04 > > +#define ABS_EDGE_MOTION_MASK 0x80 > > + > > +#define DFLT_RESP_BITS_VALID 0x88 /* SMBus bit should not be set. */ > > +#define DFLT_RESP_SMBUS_BIT 0x80 > > +#define DFLT_SMBUS_MODE 0x80 > > +#define DFLT_PS2_MODE 0x00 > > +#define DFLT_RESP_BIT_MODE 0x40 > > +#define DFLT_RESP_REMOTE_MODE 0x40 > > +#define DFLT_RESP_STREAM_MODE 0x00 > > +#define DFLT_RESP_BIT_REPORTING 0x20 > > +#define DFLT_RESP_BIT_SCALING 0x10 > > + > > +#define TP_METRICS_BIT_PALM 0x80 > > +#define TP_METRICS_BIT_STUBBORN 0x40 > > +#define TP_METRICS_BIT_2F_JITTER 0x30 > > +#define TP_METRICS_BIT_1F_JITTER 0x0c > > +#define TP_METRICS_BIT_APA 0x02 > > +#define TP_METRICS_BIT_MTG 0x01 > > +#define TP_METRICS_BIT_ABS_PKT_FORMAT_SET 0xf0 > > +#define TP_METRICS_BIT_2F_SPIKE 0x0c > > +#define TP_METRICS_BIT_1F_SPIKE 0x03 > > + > > +/* bits of first byte response of E9h-Status Request command. */ > > +#define RESP_BTN_RIGHT_BIT 0x01 #define RESP_BTN_MIDDLE_BIT 0x02 > > +#define RESP_BTN_LEFT_BIT 0x04 > > +#define RESP_SCALING_BIT 0x10 > > +#define RESP_ENABLE_BIT 0x20 > > +#define RESP_REMOTE_BIT 0x40 > > +#define RESP_SMBUS_BIT 0x80 > > + > > +#define CYTP_MAX_MT_SLOTS 2 > > + > > +struct cytp_contact { > > + int x; > > + int y; > > + int z; /* also named as touch pressure. */ }; > > + > > +/* The structure of Cypress Trackpad event data. */ struct > > +cytp_report_data { > > + int contact_cnt; > > + struct cytp_contact contacts[CYTP_MAX_MT_SLOTS]; > > + unsigned int left:1; > > + unsigned int right:1; > > + unsigned int middle:1; > > + unsigned int tap:1; /* multi-finger tap detected. */ }; > > + > > +/* The structure of Cypress Trackpad device private data. */ struct > > +cytp_data { > > + int fw_version; > > + > > + int pkt_size; > > + int mode; > > + > > + int tp_min_pressure; > > + int tp_max_pressure; > > + int tp_width; /* X direction physical size in mm. */ > > + int tp_high; /* Y direction physical size in mm. */ > > + int tp_max_abs_x; /* Max X absolute units that can be reported. */ > > + int tp_max_abs_y; /* Max Y absolute units that can be reported. */ > > + > > + int tp_res_x; /* X resolution in units/mm. */ > > + int tp_res_y; /* Y resolution in units/mm. */ > > + > > + int tp_metrics_supported; > > +}; > > + > > + > > +#ifdef CONFIG_MOUSE_PS2_CYPRESS > > +int cypress_detect(struct psmouse *psmouse, bool set_properties); int > > +cypress_init(struct psmouse *psmouse); bool cypress_supported(void); > > +#else inline int cypress_detect(struct psmouse *psmouse, bool > > +set_properties) { > > + return -ENOSYS; > > +} > > +inline int cypress_init(struct psmouse *psmouse) { > > + return -ENOSYS; > > +} > > +inline bool cypress_supported(void) > > +{ > > + return 0; > > +} > > +#endif /* CONFIG_MOUSE_PS2_CYPRESS */ > > + > > +#endif /* _CYPRESS_PS2_H */ > > diff --git a/drivers/input/mouse/psmouse-base.c > > b/drivers/input/mouse/psmouse-base.c > > index 22fe254..cff065f 100644 > > --- a/drivers/input/mouse/psmouse-base.c > > +++ b/drivers/input/mouse/psmouse-base.c > > @@ -34,6 +34,7 @@ > > #include "touchkit_ps2.h" > > #include "elantech.h" > > #include "sentelic.h" > > +#include "cypress_ps2.h" > > > > #define DRIVER_DESC "PS/2 mouse driver" > > > > @@ -759,6 +760,28 @@ static int psmouse_extensions(struct psmouse *psmouse, > > } > > > > /* > > + * Try Cypress Trackpad. > > + * Must try it before Finger Sensing Pad because Finger Sensing Pad > > +probe > > + * upsets some modules of Cypress Trackpads. > > + */ > > + if (max_proto > PSMOUSE_IMEX && > > + cypress_detect(psmouse, set_properties) == 0) { > > + if (cypress_supported()) { > > + if (cypress_init(psmouse) == 0) > > + return PSMOUSE_CYPRESS; > > + > > + /* > > + * Finger Sensing Pad probe upsets some modules of > > + * Cypress Trackpad, must avoid Finger Sensing Pad > > + * probe if Cypress Trackpad device detected. > > + */ > > + return PSMOUSE_PS2; > > + } > > + > > + max_proto = PSMOUSE_IMEX; > > + } > > + > > +/* > > * Try ALPS TouchPad > > */ > > if (max_proto > PSMOUSE_IMEX) { > > @@ -896,6 +919,15 @@ static const struct psmouse_protocol psmouse_protocols[] = { > > .alias = "thinkps", > > .detect = thinking_detect, > > }, > > +#ifdef CONFIG_MOUSE_PS2_CYPRESS > > + { > > + .type = PSMOUSE_CYPRESS, > > + .name = "CyPS/2", > > + .alias = "cypress", > > + .detect = cypress_detect, > > + .init = cypress_init, > > + }, > > +#endif > > { > > .type = PSMOUSE_GENPS, > > .name = "GenPS/2", > > diff --git a/drivers/input/mouse/psmouse.h > > b/drivers/input/mouse/psmouse.h index fe1df23..2f0b39d 100644 > > --- a/drivers/input/mouse/psmouse.h > > +++ b/drivers/input/mouse/psmouse.h > > @@ -95,6 +95,7 @@ enum psmouse_type { > > PSMOUSE_ELANTECH, > > PSMOUSE_FSP, > > PSMOUSE_SYNAPTICS_RELATIVE, > > + PSMOUSE_CYPRESS, > > PSMOUSE_AUTO /* This one should always be last */ > > }; > > > > -- > > 1.7.10.4 > > Reviewed-by: Dudley Du <dudl@cypress.com> Thanks, Dudley Du This message and any attachments may contain Cypress (or its subsidiaries) confidential information. If it has been received in error, please advise the sender and immediately delete this message. -- To unsubscribe from this list: send the line "unsubscribe linux-input" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
> > > +config MOUSE_PS2_CYPRESS > > > + bool "Cypress PS/2 mouse protocol extension" if EXPERT > > > > Why EXPERT here? > > > > > + default y > > > > Should it really be default y here? > > > This config entry (with phrases "if EXPERT" and "default y") was simply > cloned from similar devices (e.g. SYNAPTICS, ALPS, LIFEBOOK). > > If your preference is that CYPRESS should be disabled by default, I'll > omit those phrases. Please advise. Dmitry, which default do you prefer here? Henrik -- To unsubscribe from this list: send the line "unsubscribe linux-input" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Mon, Dec 10, 2012 at 06:13:59PM +0100, Henrik Rydberg wrote: > > > > +config MOUSE_PS2_CYPRESS > > > > + bool "Cypress PS/2 mouse protocol extension" if EXPERT > > > > > > Why EXPERT here? > > > > > > > + default y > > > > > > Should it really be default y here? > > > > > > This config entry (with phrases "if EXPERT" and "default y") was simply > > cloned from similar devices (e.g. SYNAPTICS, ALPS, LIFEBOOK). > > > > If your preference is that CYPRESS should be disabled by default, I'll > > omit those phrases. Please advise. > > Dmitry, which default do you prefer here? Given that psmouse is monolithic and can't be [easily] split into separate drivers (because they each protocol has to be actively probed), I believe that allprotocols should be enabled by default. People who really know what they are doing can reduce the size by compiling parts of it out. So copying what other protocols did was right ;) Thanks.
diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig index cd6268c..88954dd 100644 --- a/drivers/input/mouse/Kconfig +++ b/drivers/input/mouse/Kconfig @@ -68,6 +68,16 @@ config MOUSE_PS2_SYNAPTICS If unsure, say Y. +config MOUSE_PS2_CYPRESS + bool "Cypress PS/2 mouse protocol extension" if EXPERT + default y + depends on MOUSE_PS2 + help + Say Y here if you have a Cypress PS/2 Trackpad connected to + your system. + + If unsure, say Y. + config MOUSE_PS2_LIFEBOOK bool "Fujitsu Lifebook PS/2 mouse protocol extension" if EXPERT default y diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile index 46ba755..323e352 100644 --- a/drivers/input/mouse/Makefile +++ b/drivers/input/mouse/Makefile @@ -32,3 +32,4 @@ psmouse-$(CONFIG_MOUSE_PS2_LIFEBOOK) += lifebook.o psmouse-$(CONFIG_MOUSE_PS2_SENTELIC) += sentelic.o psmouse-$(CONFIG_MOUSE_PS2_TRACKPOINT) += trackpoint.o psmouse-$(CONFIG_MOUSE_PS2_TOUCHKIT) += touchkit_ps2.o +psmouse-$(CONFIG_MOUSE_PS2_CYPRESS) += cypress_ps2.o diff --git a/drivers/input/mouse/cypress_ps2.c b/drivers/input/mouse/cypress_ps2.c new file mode 100644 index 0000000..310cb49 --- /dev/null +++ b/drivers/input/mouse/cypress_ps2.c @@ -0,0 +1,719 @@ +/* + * Cypress Trackpad PS/2 mouse driver + * + * Copyright (c) 2012 Cypress Semiconductor Corporation. + * + * Author: + * Dudley Du <dudl@cypress.com> + * + * Additional contributors include: + * Kamal Mostafa <kamal@canonical.com> + * Kyle Fazzari <git@status.e4ward.com> + * + * 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. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/serio.h> +#include <linux/libps2.h> +#include <linux/input.h> +#include <linux/input/mt.h> +#include <linux/sched.h> +#include <linux/wait.h> + +#include "cypress_ps2.h" + +#undef CYTP_DEBUG_VERBOSE /* define this and DEBUG for more verbose dump */ + +static void cypress_set_packet_size(struct psmouse *psmouse, unsigned int n) +{ + struct cytp_data *cytp = psmouse->private; + cytp->pkt_size = n; +} + +static const unsigned char cytp_rate[] = {10, 20, 40, 60, 100, 200}; +static const unsigned char cytp_resolution[] = {0x00, 0x01, 0x02, 0x03}; + +static int cypress_ps2_sendbyte(struct psmouse *psmouse, int value) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + + if (ps2_sendbyte(ps2dev, value & 0xff, CYTP_CMD_TIMEOUT) < 0) { + psmouse_dbg(psmouse, + "send command 0x%02x failed, resp 0x%02x\n", + value & 0xff, ps2dev->nak); + if (ps2dev->nak == CYTP_PS2_RETRY) + return CYTP_PS2_RETRY; + else + return CYTP_PS2_ERROR; + } + +#ifdef CYTP_DEBUG_VERBOSE + psmouse_dbg(psmouse, "send command 0x%02x success, resp 0xfa\n", + value & 0xff); +#endif + + return 0; +} + +static int cypress_ps2_ext_cmd(struct psmouse *psmouse, unsigned short cmd, + unsigned char data) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + int tries = CYTP_PS2_CMD_TRIES; + int rc; + + ps2_begin_command(ps2dev); + + do { + /* + * Send extension command byte (0xE8 or 0xF3). + * If sending the command fails, send recovery command + * to make the device return to the ready state. + */ + rc = cypress_ps2_sendbyte(psmouse, cmd & 0xff); + if (rc == CYTP_PS2_RETRY) { + rc = cypress_ps2_sendbyte(psmouse, 0x00); + if (rc == CYTP_PS2_RETRY) + rc = cypress_ps2_sendbyte(psmouse, 0x0a); + } + if (rc == CYTP_PS2_ERROR) + continue; + + rc = cypress_ps2_sendbyte(psmouse, data); + if (rc == CYTP_PS2_RETRY) + rc = cypress_ps2_sendbyte(psmouse, data); + if (rc == CYTP_PS2_ERROR) + continue; + else + break; + } while (--tries > 0); + + ps2_end_command(ps2dev); + + return rc; +} + +static int cypress_ps2_read_cmd_status(struct psmouse *psmouse, + unsigned char cmd, + unsigned char *param) +{ + int rc; + struct ps2dev *ps2dev = &psmouse->ps2dev; + enum psmouse_state old_state; + int pktsize; + + ps2_begin_command(&psmouse->ps2dev); + + old_state = psmouse->state; + psmouse->state = PSMOUSE_CMD_MODE; + psmouse->pktcnt = 0; + + pktsize = (cmd == CYTP_CMD_READ_TP_METRICS) ? 8 : 3; + memset(param, 0, pktsize); + + rc = cypress_ps2_sendbyte(psmouse, 0xe9); + if (rc < 0) + goto out; + + wait_event_timeout(ps2dev->wait, + (psmouse->pktcnt >= pktsize), + msecs_to_jiffies(CYTP_CMD_TIMEOUT)); + + memcpy(param, psmouse->packet, pktsize); + + psmouse_dbg(psmouse, "Command 0x%02x response data (0x): %*ph\n", + cmd, pktsize, param); + +out: + psmouse->state = old_state; + psmouse->pktcnt = 0; + + ps2_end_command(&psmouse->ps2dev); + + return rc; +} + +static int cypress_verify_cmd_state(struct psmouse *psmouse, + unsigned char cmd, unsigned char *param) +{ + bool rate_match = 0; + bool resolution_match = 0; + int i; + + /* callers will do further checking. */ + if (cmd == CYTP_CMD_READ_CYPRESS_ID || + cmd == CYTP_CMD_STANDARD_MODE || + cmd == CYTP_CMD_READ_TP_METRICS) + return 0; + if ((~param[0] & DFLT_RESP_BITS_VALID) == DFLT_RESP_BITS_VALID && + (param[0] & DFLT_RESP_BIT_MODE) == DFLT_RESP_STREAM_MODE) { + for (i = 0; i < sizeof(cytp_resolution); i++) + if (cytp_resolution[i] == param[1]) + resolution_match = 1; + + for (i = 0; i < sizeof(cytp_rate); i++) + if (cytp_rate[i] == param[2]) + rate_match = 1; + + if (resolution_match && rate_match) + return 0; + } + + psmouse_dbg(psmouse, "verify cmd state failed.\n"); + return -1; +} + +static int cypress_send_ext_cmd(struct psmouse *psmouse, unsigned char cmd, + unsigned char *param) +{ + int tries = CYTP_PS2_CMD_TRIES; + int rc; + + psmouse_dbg(psmouse, "send extension cmd 0x%02x, [%d %d %d %d]\n", + cmd, DECODE_CMD_AA(cmd), DECODE_CMD_BB(cmd), + DECODE_CMD_CC(cmd), DECODE_CMD_DD(cmd)); + do { + cypress_ps2_ext_cmd(psmouse, + PSMOUSE_CMD_SETRES, DECODE_CMD_DD(cmd)); + cypress_ps2_ext_cmd(psmouse, + PSMOUSE_CMD_SETRES, DECODE_CMD_CC(cmd)); + cypress_ps2_ext_cmd(psmouse, + PSMOUSE_CMD_SETRES, DECODE_CMD_BB(cmd)); + cypress_ps2_ext_cmd(psmouse, + PSMOUSE_CMD_SETRES, DECODE_CMD_AA(cmd)); + + rc = cypress_ps2_read_cmd_status(psmouse, cmd, param); + if (rc) + continue; + + rc = cypress_verify_cmd_state(psmouse, cmd, param); + if (rc == 0) + return 0; + } while (--tries > 0); + + return -1; +} + +int cypress_detect(struct psmouse *psmouse, bool set_properties) +{ + unsigned char param[3]; + + if (cypress_send_ext_cmd(psmouse, CYTP_CMD_READ_CYPRESS_ID, param)) + return -ENODEV; + + /* Check for Cypress Trackpad signature bytes: 0x33 0xCC */ + if (param[0] != 0x33 || param[1] != 0xCC) + return -ENODEV; + + if (set_properties) { + psmouse->vendor = "Cypress"; + psmouse->name = "Trackpad"; + } + + return 0; +} + +static int cypress_read_fw_version(struct psmouse *psmouse) +{ + struct cytp_data *cytp = psmouse->private; + unsigned char param[3]; + + if (cypress_send_ext_cmd(psmouse, CYTP_CMD_READ_CYPRESS_ID, param)) + return -ENODEV; + + /* Check for Cypress Trackpad signature bytes: 0x33 0xCC */ + if (param[0] != 0x33 || param[1] != 0xCC) + return -ENODEV; + + cytp->fw_version = param[2] & FW_VERSION_MASX; + cytp->tp_metrics_supported = (param[2] & TP_METRICS_MASK) ? 1 : 0; + + psmouse_dbg(psmouse, "cytp->fw_version = %d\n", cytp->fw_version); + psmouse_dbg(psmouse, "cytp->tp_metrics_supported = %d\n", + cytp->tp_metrics_supported); + return 0; +} + +static int cypress_read_tp_metrics(struct psmouse *psmouse) +{ + struct cytp_data *cytp = psmouse->private; + unsigned char param[8]; + + /* set default values for tp metrics. */ + cytp->tp_width = CYTP_DEFAULT_WIDTH; + cytp->tp_high = CYTP_DEFAULT_HIGH; + cytp->tp_max_abs_x = CYTP_ABS_MAX_X; + cytp->tp_max_abs_y = CYTP_ABS_MAX_Y; + cytp->tp_min_pressure = CYTP_MIN_PRESSURE; + cytp->tp_max_pressure = CYTP_MAX_PRESSURE; + cytp->tp_res_x = cytp->tp_max_abs_x / cytp->tp_width; + cytp->tp_res_y = cytp->tp_max_abs_y / cytp->tp_high; + + memset(param, 0, sizeof(param)); + if (cypress_send_ext_cmd(psmouse, CYTP_CMD_READ_TP_METRICS, param) == 0) { + /* Update trackpad parameters. */ + cytp->tp_max_abs_x = (param[1] << 8) | param[0]; + cytp->tp_max_abs_y = (param[3] << 8) | param[2]; + cytp->tp_min_pressure = param[4]; + cytp->tp_max_pressure = param[5]; + } + + if (!cytp->tp_max_pressure || + cytp->tp_max_pressure < cytp->tp_min_pressure || + !cytp->tp_width || !cytp->tp_high || + !cytp->tp_max_abs_x || + cytp->tp_max_abs_x < cytp->tp_width || + !cytp->tp_max_abs_y || + cytp->tp_max_abs_y < cytp->tp_high) + return -1; + + cytp->tp_res_x = cytp->tp_max_abs_x / cytp->tp_width; + cytp->tp_res_y = cytp->tp_max_abs_y / cytp->tp_high; + +#ifdef CYTP_DEBUG_VERBOSE + psmouse_dbg(psmouse, "Dump trackpad hardware configuration as below:\n"); + psmouse_dbg(psmouse, "cytp->tp_width = %d\n", cytp->tp_width); + psmouse_dbg(psmouse, "cytp->tp_high = %d\n", cytp->tp_high); + psmouse_dbg(psmouse, "cytp->tp_max_abs_x = %d\n", cytp->tp_max_abs_x); + psmouse_dbg(psmouse, "cytp->tp_max_abs_y = %d\n", cytp->tp_max_abs_y); + psmouse_dbg(psmouse, "cytp->tp_min_pressure = %d\n", cytp->tp_min_pressure); + psmouse_dbg(psmouse, "cytp->tp_max_pressure = %d\n", cytp->tp_max_pressure); + psmouse_dbg(psmouse, "cytp->tp_res_x = %d\n", cytp->tp_res_x); + psmouse_dbg(psmouse, "cytp->tp_res_y = %d\n", cytp->tp_res_y); + + psmouse_dbg(psmouse, "tp_type_APA = %d\n", + (param[6] & TP_METRICS_BIT_APA) ? 1 : 0); + psmouse_dbg(psmouse, "tp_type_MTG = %d\n", + (param[6] & TP_METRICS_BIT_MTG) ? 1 : 0); + psmouse_dbg(psmouse, "tp_palm = %d\n", + (param[6] & TP_METRICS_BIT_PALM) ? 1 : 0); + psmouse_dbg(psmouse, "tp_stubborn = %d\n", + (param[6] & TP_METRICS_BIT_STUBBORN) ? 1 : 0); + psmouse_dbg(psmouse, "tp_1f_jitter = %d\n", + (param[6] & TP_METRICS_BIT_1F_JITTER) >> 2); + psmouse_dbg(psmouse, "tp_2f_jitter = %d\n", + (param[6] & TP_METRICS_BIT_2F_JITTER) >> 4); + psmouse_dbg(psmouse, "tp_1f_spike = %d\n", + param[7] & TP_METRICS_BIT_1F_SPIKE); + psmouse_dbg(psmouse, "tp_2f_spike = %d\n", + (param[7] & TP_METRICS_BIT_2F_SPIKE) >> 2); + psmouse_dbg(psmouse, "tp_abs_packet_format_set = %d\n", + (param[7] & TP_METRICS_BIT_ABS_PKT_FORMAT_SET) >> 4); +#endif + + return 0; +} + +static int cypress_query_hardware(struct psmouse *psmouse) +{ + struct cytp_data *cytp = psmouse->private; + int ret; + + ret = cypress_read_fw_version(psmouse); + if (ret) + return ret; + + if (cytp->tp_metrics_supported) { + ret = cypress_read_tp_metrics(psmouse); + if (ret) + return ret; + } + + return 0; +} + +static int cypress_set_absolute_mode(struct psmouse *psmouse) +{ + struct cytp_data *cytp = psmouse->private; + unsigned char param[3]; + + if (cypress_send_ext_cmd(psmouse, CYTP_CMD_ABS_WITH_PRESSURE_MODE, param) < 0) + return -1; + + cytp->mode = (cytp->mode & ~CYTP_BIT_ABS_REL_MASK) + | CYTP_BIT_ABS_PRESSURE; + cypress_set_packet_size(psmouse, 5); + + return 0; +} + +/* + * Reset trackpad device. + * This is also the default mode when trackpad powered on. + */ +static void cypress_reset(struct psmouse *psmouse) +{ + struct cytp_data *cytp = psmouse->private; + + cytp->mode = 0; + + psmouse_reset(psmouse); +} + +static int cypress_set_input_params(struct input_dev *input, + struct cytp_data *cytp) +{ + int ret; + + if (!cytp->tp_res_x || !cytp->tp_res_y) + return -EINVAL; + + __set_bit(EV_ABS, input->evbit); + input_set_abs_params(input, ABS_X, 0, cytp->tp_max_abs_x, 0, 0); + input_set_abs_params(input, ABS_Y, 0, cytp->tp_max_abs_y, 0, 0); + input_set_abs_params(input, ABS_PRESSURE, + cytp->tp_min_pressure, cytp->tp_max_pressure, 0, 0); + input_set_abs_params(input, ABS_TOOL_WIDTH, 0, 255, 0, 0); + + /* finger position */ + input_set_abs_params(input, ABS_MT_POSITION_X, 0, cytp->tp_max_abs_x, 0, 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, 0, cytp->tp_max_abs_y, 0, 0); + input_set_abs_params(input, ABS_MT_PRESSURE, 0, 255, 0, 0); + + ret = input_mt_init_slots(input, CYTP_MAX_MT_SLOTS, + INPUT_MT_DROP_UNUSED|INPUT_MT_TRACK); + if (ret < 0) + return ret; + + __set_bit(INPUT_PROP_SEMI_MT, input->propbit); + + input_abs_set_res(input, ABS_X, cytp->tp_res_x); + input_abs_set_res(input, ABS_Y, cytp->tp_res_y); + + input_abs_set_res(input, ABS_MT_POSITION_X, cytp->tp_res_x); + input_abs_set_res(input, ABS_MT_POSITION_Y, cytp->tp_res_y); + + __set_bit(BTN_TOUCH, input->keybit); + __set_bit(BTN_TOOL_FINGER, input->keybit); + __set_bit(BTN_TOOL_DOUBLETAP, input->keybit); + __set_bit(BTN_TOOL_TRIPLETAP, input->keybit); + __set_bit(BTN_TOOL_QUADTAP, input->keybit); + __set_bit(BTN_TOOL_QUINTTAP, input->keybit); + + __clear_bit(EV_REL, input->evbit); + __clear_bit(REL_X, input->relbit); + __clear_bit(REL_Y, input->relbit); + + __set_bit(INPUT_PROP_BUTTONPAD, input->propbit); + __set_bit(EV_KEY, input->evbit); + __set_bit(BTN_LEFT, input->keybit); + __set_bit(BTN_RIGHT, input->keybit); + __set_bit(BTN_MIDDLE, input->keybit); + + input_set_drvdata(input, cytp); + + return 0; +} + +static int cypress_get_finger_count(unsigned char header_byte) +{ + unsigned char bits6_7; + int finger_count; + + bits6_7 = header_byte >> 6; + finger_count = bits6_7 & 0x03; + + if (finger_count == 1) + return 1; + + if (header_byte & ABS_HSCROLL_BIT) { + /* HSCROLL gets added on to 0 finger count. */ + switch (finger_count) { + case 0: return(4); + case 2: return(5); + default: + /* Invalid contact (e.g. palm). Ignore it. */ + return(0); + } + } + + return finger_count; +} + + +static int cypress_parse_packet(struct psmouse *psmouse, + struct cytp_data *cytp, struct cytp_report_data *report_data) +{ + unsigned char *packet = psmouse->packet; + unsigned char header_byte = packet[0]; + + memset(report_data, 0, sizeof(struct cytp_report_data)); + + report_data->contact_cnt = cypress_get_finger_count(header_byte); + + if (report_data->contact_cnt == 0) /* e.g. palm detect */ + return 0; + + report_data->tap = (header_byte & ABS_MULTIFINGER_TAP) ? 1 : 0; + + if (report_data->contact_cnt == 1) { + report_data->contacts[0].x = + ((packet[1] & 0x70) << 4) | packet[2]; + report_data->contacts[0].y = + ((packet[1] & 0x07) << 8) | packet[3]; + if (cytp->mode & CYTP_BIT_ABS_PRESSURE) + report_data->contacts[0].z = packet[4]; + + } else if (report_data->contact_cnt >= 2) { + report_data->contacts[0].x = + ((packet[1] & 0x70) << 4) | packet[2]; + report_data->contacts[0].y = + ((packet[1] & 0x07) << 8) | packet[3]; + if (cytp->mode & CYTP_BIT_ABS_PRESSURE) + report_data->contacts[0].z = packet[4]; + + report_data->contacts[1].x = + ((packet[5] & 0xf0) << 4) | packet[6]; + report_data->contacts[1].y = + ((packet[5] & 0x0f) << 8) | packet[7]; + if (cytp->mode & CYTP_BIT_ABS_PRESSURE) + report_data->contacts[1].z = report_data->contacts[0].z; + } + + report_data->left = (header_byte & BTN_LEFT_BIT) ? 1 : 0; + report_data->right = (header_byte & BTN_RIGHT_BIT) ? 1 : 0; + + /* + * This is only true if one of the mouse buttons were tapped. Make + * sure it doesn't turn into a click. The regular tap-to-click + * functionality will handle that on its own. If we don't do this, + * disabling tap-to-click won't affect the mouse button zones. + */ + if (report_data->tap) + report_data->left = 0; + +#ifdef CYTP_DEBUG_VERBOSE + { + int i; + int n = report_data->contact_cnt; + psmouse_dbg(psmouse, "Dump parsed report data as below:\n"); + psmouse_dbg(psmouse, "contact_cnt = %d\n", + report_data->contact_cnt); + if (n > CYTP_MAX_MT_SLOTS) + n = CYTP_MAX_MT_SLOTS; + for (i = 0; i < n; i++) + psmouse_dbg(psmouse, "contacts[%d] = {%d, %d, %d}\n", i, + report_data->contacts[i].x, + report_data->contacts[i].y, + report_data->contacts[i].z); + psmouse_dbg(psmouse, "left = %d\n", report_data->left); + psmouse_dbg(psmouse, "right = %d\n", report_data->right); + psmouse_dbg(psmouse, "middle = %d\n", report_data->middle); + } +#endif + + return 0; +} + +static void cypress_process_packet(struct psmouse *psmouse, bool zero_pkt) +{ + int i; + struct input_dev *input = psmouse->dev; + struct cytp_data *cytp = psmouse->private; + struct cytp_report_data report_data; + struct cytp_contact *contact; + struct input_mt_pos pos[CYTP_MAX_MT_SLOTS]; + int slots[CYTP_MAX_MT_SLOTS]; + int n; + + if (cypress_parse_packet(psmouse, cytp, &report_data)) + return; + + n = report_data.contact_cnt; + + if (n > CYTP_MAX_MT_SLOTS) + n = CYTP_MAX_MT_SLOTS; + + for (i = 0; i < n; i++) { + contact = &report_data.contacts[i]; + pos[i].x = contact->x; + pos[i].y = contact->y; + } + + input_mt_assign_slots(input, slots, pos, n); + + for (i = 0; i < n; i++) { + contact = &report_data.contacts[i]; + input_mt_slot(input, slots[i]); + input_mt_report_slot_state(input, MT_TOOL_FINGER, true); + input_report_abs(input, ABS_MT_POSITION_X, contact->x); + input_report_abs(input, ABS_MT_POSITION_Y, contact->y); + input_report_abs(input, ABS_MT_PRESSURE, contact->z); + } + + input_mt_sync_frame(input); + + input_mt_report_finger_count(input, report_data.contact_cnt); + + input_report_key(input, BTN_LEFT, report_data.left); + input_report_key(input, BTN_RIGHT, report_data.right); + input_report_key(input, BTN_MIDDLE, report_data.middle); + + input_sync(input); +} + +static psmouse_ret_t cypress_validate_byte(struct psmouse *psmouse) +{ + int contact_cnt; + int index = psmouse->pktcnt - 1; + unsigned char *packet = psmouse->packet; + struct cytp_data *cytp = psmouse->private; + + if (index < 0 || index > cytp->pkt_size) + return PSMOUSE_BAD_DATA; + + if (index == 0 && (packet[0] & 0xfc) == 0) { + /* call packet process for reporting finger leave. */ + cypress_process_packet(psmouse, 1); + return PSMOUSE_FULL_PACKET; + } + + /* + * Perform validation (and adjust packet size) based only on the + * first byte; allow all further bytes through. + */ + if (index != 0) + return PSMOUSE_GOOD_DATA; + + /* + * If absolute/relative mode bit has not been set yet, just pass + * the byte through. + */ + if ((cytp->mode & CYTP_BIT_ABS_REL_MASK) == 0) + return PSMOUSE_GOOD_DATA; + + if ((packet[0] & 0x08) == 0x08) + return PSMOUSE_BAD_DATA; + + contact_cnt = cypress_get_finger_count(packet[0]); + + if (contact_cnt > 5) + return PSMOUSE_BAD_DATA; + + if (cytp->mode & CYTP_BIT_ABS_NO_PRESSURE) + cypress_set_packet_size(psmouse, contact_cnt == 2 ? 7 : 4); + else + cypress_set_packet_size(psmouse, contact_cnt == 2 ? 8 : 5); + + return PSMOUSE_GOOD_DATA; +} + +static psmouse_ret_t cypress_protocol_handler(struct psmouse *psmouse) +{ + struct cytp_data *cytp = psmouse->private; + + if (psmouse->pktcnt >= cytp->pkt_size) { + cypress_process_packet(psmouse, 0); + return PSMOUSE_FULL_PACKET; + } + + return cypress_validate_byte(psmouse); +} + +static void cypress_set_rate(struct psmouse *psmouse, unsigned int rate) +{ + struct cytp_data *cytp = psmouse->private; + + if (rate >= 80) { + psmouse->rate = 80; + cytp->mode |= CYTP_BIT_HIGH_RATE; + } else { + psmouse->rate = 40; + cytp->mode &= ~CYTP_BIT_HIGH_RATE; + } + + ps2_command(&psmouse->ps2dev, (unsigned char *)&psmouse->rate, + PSMOUSE_CMD_SETRATE); +} + +static void cypress_disconnect(struct psmouse *psmouse) +{ + cypress_reset(psmouse); + kfree(psmouse->private); + psmouse->private = NULL; +} + +static int cypress_reconnect(struct psmouse *psmouse) +{ + int tries = CYTP_PS2_CMD_TRIES; + int rc; + + do { + cypress_reset(psmouse); + rc = cypress_detect(psmouse, false); + } while (rc && (--tries > 0)); + + if (rc) { + psmouse_err(psmouse, "Reconnect: unable to detect trackpad.\n"); + return -1; + } + + if (cypress_set_absolute_mode(psmouse)) { + psmouse_err(psmouse, "Reconnect: Unable to initialize Cypress absolute mode.\n"); + return -1; + } + + return 0; +} + +int cypress_init(struct psmouse *psmouse) +{ + struct cytp_data *cytp; + + cytp = (struct cytp_data *)kzalloc(sizeof(struct cytp_data), GFP_KERNEL); + psmouse->private = (void *)cytp; + if (cytp == NULL) + return -ENOMEM; + + cypress_reset(psmouse); + + psmouse->pktsize = 8; + + if (cypress_query_hardware(psmouse)) { + psmouse_err(psmouse, "Unable to query Trackpad hardware.\n"); + goto err_exit; + } + + if (cypress_set_absolute_mode(psmouse)) { + psmouse_err(psmouse, "init: Unable to initialize Cypress absolute mode.\n"); + goto err_exit; + } + + if (cypress_set_input_params(psmouse->dev, cytp) < 0) { + psmouse_err(psmouse, "init: Unable to set input params.\n"); + goto err_exit; + } + + psmouse->model = 1; + psmouse->protocol_handler = cypress_protocol_handler; + psmouse->set_rate = cypress_set_rate; + psmouse->disconnect = cypress_disconnect; + psmouse->reconnect = cypress_reconnect; + psmouse->cleanup = cypress_reset; + psmouse->resync_time = 0; + + return 0; + +err_exit: + /* + * Reset Cypress Trackpad as a standard mouse. Then + * let psmouse driver commmunicating with it as default PS2 mouse. + */ + cypress_reset(psmouse); + + psmouse->private = NULL; + kfree(cytp); + + return -1; +} + +bool cypress_supported(void) +{ + return true; +} diff --git a/drivers/input/mouse/cypress_ps2.h b/drivers/input/mouse/cypress_ps2.h new file mode 100644 index 0000000..4720f21 --- /dev/null +++ b/drivers/input/mouse/cypress_ps2.h @@ -0,0 +1,191 @@ +#ifndef _CYPRESS_PS2_H +#define _CYPRESS_PS2_H + +#include "psmouse.h" + +#define CMD_BITS_MASK 0x03 +#define COMPOSIT(x, s) (((x) & CMD_BITS_MASK) << (s)) + +#define ENCODE_CMD(aa, bb, cc, dd) \ + (COMPOSIT((aa), 6) | COMPOSIT((bb), 4) | COMPOSIT((cc), 2) | COMPOSIT((dd), 0)) +#define CYTP_CMD_ABS_NO_PRESSURE_MODE ENCODE_CMD(0, 1, 0, 0) +#define CYTP_CMD_ABS_WITH_PRESSURE_MODE ENCODE_CMD(0, 1, 0, 1) +#define CYTP_CMD_SMBUS_MODE ENCODE_CMD(0, 1, 1, 0) +#define CYTP_CMD_STANDARD_MODE ENCODE_CMD(0, 2, 0, 0) /* not implemented yet. */ +#define CYTP_CMD_CYPRESS_REL_MODE ENCODE_CMD(1, 1, 1, 1) /* not implemented yet. */ +#define CYTP_CMD_READ_CYPRESS_ID ENCODE_CMD(0, 0, 0, 0) +#define CYTP_CMD_READ_TP_METRICS ENCODE_CMD(0, 0, 0, 1) +#define CYTP_CMD_SET_HSCROLL_WIDTH(w) ENCODE_CMD(1, 1, 0, (w)) +#define CYTP_CMD_SET_HSCROLL_MASK ENCODE_CMD(1, 1, 0, 0) +#define CYTP_CMD_SET_VSCROLL_WIDTH(w) ENCODE_CMD(1, 2, 0, (w)) +#define CYTP_CMD_SET_VSCROLL_MASK ENCODE_CMD(1, 2, 0, 0) +#define CYTP_CMD_SET_PALM_GEOMETRY(e) ENCODE_CMD(1, 2, 1, (e)) +#define CYTP_CMD_PALM_GEMMETRY_MASK ENCODE_CMD(1, 2, 1, 0) +#define CYTP_CMD_SET_PALM_SENSITIVITY(s) ENCODE_CMD(1, 2, 2, (s)) +#define CYTP_CMD_PALM_SENSITIVITY_MASK ENCODE_CMD(1, 2, 2, 0) +#define CYTP_CMD_SET_MOUSE_SENSITIVITY(s) ENCODE_CMD(1, 3, ((s) >> 2), (s)) +#define CYTP_CMD_MOUSE_SENSITIVITY_MASK ENCODE_CMD(1, 3, 0, 0) +#define CYTP_CMD_REQUEST_BASELINE_STATUS ENCODE_CMD(2, 0, 0, 1) +#define CYTP_CMD_REQUEST_RECALIBRATION ENCODE_CMD(2, 0, 0, 3) + +#define DECODE_CMD_AA(x) (((x) >> 6) & CMD_BITS_MASK) +#define DECODE_CMD_BB(x) (((x) >> 4) & CMD_BITS_MASK) +#define DECODE_CMD_CC(x) (((x) >> 2) & CMD_BITS_MASK) +#define DECODE_CMD_DD(x) ((x) & CMD_BITS_MASK) + +/* Cypress trackpad working mode. */ +#define CYTP_BIT_ABS_PRESSURE (1 << 3) +#define CYTP_BIT_ABS_NO_PRESSURE (1 << 2) +#define CYTP_BIT_CYPRESS_REL (1 << 1) +#define CYTP_BIT_STANDARD_REL (1 << 0) +#define CYTP_BIT_REL_MASK (CYTP_BIT_CYPRESS_REL | CYTP_BIT_STANDARD_REL) +#define CYTP_BIT_ABS_MASK (CYTP_BIT_ABS_PRESSURE | CYTP_BIT_ABS_NO_PRESSURE) +#define CYTP_BIT_ABS_REL_MASK (CYTP_BIT_ABS_MASK | CYTP_BIT_REL_MASK) + +#define CYTP_BIT_HIGH_RATE (1 << 4) +/* + * report mode bit is set, firmware working in Remote Mode. + * report mode bit is cleared, firmware working in Stream Mode. + */ +#define CYTP_BIT_REPORT_MODE (1 << 5) + +/* scrolling width values for set HSCROLL and VSCROLL width command. */ +#define SCROLL_WIDTH_NARROW 1 +#define SCROLL_WIDTH_NORMAL 2 +#define SCROLL_WIDTH_WIDE 3 + +#define PALM_GEOMETRY_ENABLE 1 +#define PALM_GEOMETRY_DISABLE 0 + +#define TP_METRICS_MASK 0x80 +#define FW_VERSION_MASX 0x7f +#define FW_VER_HIGH_MASK 0x70 +#define FW_VER_LOW_MASK 0x0f + +/* Times to retry a ps2_command and millisecond delay between tries. */ +#define CYTP_PS2_CMD_TRIES 3 +#define CYTP_PS2_CMD_DELAY 500 + +/* time out for PS/2 command only in milliseconds. */ +#define CYTP_CMD_TIMEOUT 200 +#define CYTP_DATA_TIMEOUT 30 + +#define CYTP_EXT_CMD 0xe8 +#define CYTP_PS2_RETRY 0xfe +#define CYTP_PS2_ERROR 0xfc + +#define CYTP_RESP_RETRY 0x01 +#define CYTP_RESP_ERROR 0xfe + + +#define CYTP_105001_WIDTH 97 /* Dell XPS 13 */ +#define CYTP_105001_HIGH 59 +#define CYTP_DEFAULT_WIDTH (CYTP_105001_WIDTH) +#define CYTP_DEFAULT_HIGH (CYTP_105001_HIGH) + +#define CYTP_ABS_MAX_X 1600 +#define CYTP_ABS_MAX_Y 900 +#define CYTP_MAX_PRESSURE 255 +#define CYTP_MIN_PRESSURE 0 + +/* header byte bits of relative package. */ +#define BTN_LEFT_BIT 0x01 +#define BTN_RIGHT_BIT 0x02 +#define BTN_MIDDLE_BIT 0x04 +#define REL_X_SIGN_BIT 0x10 +#define REL_Y_SIGN_BIT 0x20 + +/* header byte bits of absolute package. */ +#define ABS_VSCROLL_BIT 0x10 +#define ABS_HSCROLL_BIT 0x20 +#define ABS_MULTIFINGER_TAP 0x04 +#define ABS_EDGE_MOTION_MASK 0x80 + +#define DFLT_RESP_BITS_VALID 0x88 /* SMBus bit should not be set. */ +#define DFLT_RESP_SMBUS_BIT 0x80 +#define DFLT_SMBUS_MODE 0x80 +#define DFLT_PS2_MODE 0x00 +#define DFLT_RESP_BIT_MODE 0x40 +#define DFLT_RESP_REMOTE_MODE 0x40 +#define DFLT_RESP_STREAM_MODE 0x00 +#define DFLT_RESP_BIT_REPORTING 0x20 +#define DFLT_RESP_BIT_SCALING 0x10 + +#define TP_METRICS_BIT_PALM 0x80 +#define TP_METRICS_BIT_STUBBORN 0x40 +#define TP_METRICS_BIT_2F_JITTER 0x30 +#define TP_METRICS_BIT_1F_JITTER 0x0c +#define TP_METRICS_BIT_APA 0x02 +#define TP_METRICS_BIT_MTG 0x01 +#define TP_METRICS_BIT_ABS_PKT_FORMAT_SET 0xf0 +#define TP_METRICS_BIT_2F_SPIKE 0x0c +#define TP_METRICS_BIT_1F_SPIKE 0x03 + +/* bits of first byte response of E9h-Status Request command. */ +#define RESP_BTN_RIGHT_BIT 0x01 +#define RESP_BTN_MIDDLE_BIT 0x02 +#define RESP_BTN_LEFT_BIT 0x04 +#define RESP_SCALING_BIT 0x10 +#define RESP_ENABLE_BIT 0x20 +#define RESP_REMOTE_BIT 0x40 +#define RESP_SMBUS_BIT 0x80 + +#define CYTP_MAX_MT_SLOTS 2 + +struct cytp_contact { + int x; + int y; + int z; /* also named as touch pressure. */ +}; + +/* The structure of Cypress Trackpad event data. */ +struct cytp_report_data { + int contact_cnt; + struct cytp_contact contacts[CYTP_MAX_MT_SLOTS]; + unsigned int left:1; + unsigned int right:1; + unsigned int middle:1; + unsigned int tap:1; /* multi-finger tap detected. */ +}; + +/* The structure of Cypress Trackpad device private data. */ +struct cytp_data { + int fw_version; + + int pkt_size; + int mode; + + int tp_min_pressure; + int tp_max_pressure; + int tp_width; /* X direction physical size in mm. */ + int tp_high; /* Y direction physical size in mm. */ + int tp_max_abs_x; /* Max X absolute units that can be reported. */ + int tp_max_abs_y; /* Max Y absolute units that can be reported. */ + + int tp_res_x; /* X resolution in units/mm. */ + int tp_res_y; /* Y resolution in units/mm. */ + + int tp_metrics_supported; +}; + + +#ifdef CONFIG_MOUSE_PS2_CYPRESS +int cypress_detect(struct psmouse *psmouse, bool set_properties); +int cypress_init(struct psmouse *psmouse); +bool cypress_supported(void); +#else +inline int cypress_detect(struct psmouse *psmouse, bool set_properties) +{ + return -ENOSYS; +} +inline int cypress_init(struct psmouse *psmouse) +{ + return -ENOSYS; +} +inline bool cypress_supported(void) +{ + return 0; +} +#endif /* CONFIG_MOUSE_PS2_CYPRESS */ + +#endif /* _CYPRESS_PS2_H */ diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 22fe254..cff065f 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -34,6 +34,7 @@ #include "touchkit_ps2.h" #include "elantech.h" #include "sentelic.h" +#include "cypress_ps2.h" #define DRIVER_DESC "PS/2 mouse driver" @@ -759,6 +760,28 @@ static int psmouse_extensions(struct psmouse *psmouse, } /* + * Try Cypress Trackpad. + * Must try it before Finger Sensing Pad because Finger Sensing Pad probe + * upsets some modules of Cypress Trackpads. + */ + if (max_proto > PSMOUSE_IMEX && + cypress_detect(psmouse, set_properties) == 0) { + if (cypress_supported()) { + if (cypress_init(psmouse) == 0) + return PSMOUSE_CYPRESS; + + /* + * Finger Sensing Pad probe upsets some modules of + * Cypress Trackpad, must avoid Finger Sensing Pad + * probe if Cypress Trackpad device detected. + */ + return PSMOUSE_PS2; + } + + max_proto = PSMOUSE_IMEX; + } + +/* * Try ALPS TouchPad */ if (max_proto > PSMOUSE_IMEX) { @@ -896,6 +919,15 @@ static const struct psmouse_protocol psmouse_protocols[] = { .alias = "thinkps", .detect = thinking_detect, }, +#ifdef CONFIG_MOUSE_PS2_CYPRESS + { + .type = PSMOUSE_CYPRESS, + .name = "CyPS/2", + .alias = "cypress", + .detect = cypress_detect, + .init = cypress_init, + }, +#endif { .type = PSMOUSE_GENPS, .name = "GenPS/2", diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h index fe1df23..2f0b39d 100644 --- a/drivers/input/mouse/psmouse.h +++ b/drivers/input/mouse/psmouse.h @@ -95,6 +95,7 @@ enum psmouse_type { PSMOUSE_ELANTECH, PSMOUSE_FSP, PSMOUSE_SYNAPTICS_RELATIVE, + PSMOUSE_CYPRESS, PSMOUSE_AUTO /* This one should always be last */ };