cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

cypress_ps2.c (18898B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Cypress Trackpad PS/2 mouse driver
      4 *
      5 * Copyright (c) 2012 Cypress Semiconductor Corporation.
      6 *
      7 * Author:
      8 *   Dudley Du <dudl@cypress.com>
      9 *
     10 * Additional contributors include:
     11 *   Kamal Mostafa <kamal@canonical.com>
     12 *   Kyle Fazzari <git@status.e4ward.com>
     13 */
     14
     15#include <linux/module.h>
     16#include <linux/kernel.h>
     17#include <linux/slab.h>
     18#include <linux/serio.h>
     19#include <linux/libps2.h>
     20#include <linux/input.h>
     21#include <linux/input/mt.h>
     22#include <linux/sched.h>
     23#include <linux/wait.h>
     24
     25#include "cypress_ps2.h"
     26
     27#undef CYTP_DEBUG_VERBOSE  /* define this and DEBUG for more verbose dump */
     28
     29static void cypress_set_packet_size(struct psmouse *psmouse, unsigned int n)
     30{
     31	struct cytp_data *cytp = psmouse->private;
     32	cytp->pkt_size = n;
     33}
     34
     35static const unsigned char cytp_rate[] = {10, 20, 40, 60, 100, 200};
     36static const unsigned char cytp_resolution[] = {0x00, 0x01, 0x02, 0x03};
     37
     38static int cypress_ps2_sendbyte(struct psmouse *psmouse, int value)
     39{
     40	struct ps2dev *ps2dev = &psmouse->ps2dev;
     41
     42	if (ps2_sendbyte(ps2dev, value & 0xff, CYTP_CMD_TIMEOUT) < 0) {
     43		psmouse_dbg(psmouse,
     44				"sending command 0x%02x failed, resp 0x%02x\n",
     45				value & 0xff, ps2dev->nak);
     46		if (ps2dev->nak == CYTP_PS2_RETRY)
     47			return CYTP_PS2_RETRY;
     48		else
     49			return CYTP_PS2_ERROR;
     50	}
     51
     52#ifdef CYTP_DEBUG_VERBOSE
     53	psmouse_dbg(psmouse, "sending command 0x%02x succeeded, resp 0xfa\n",
     54			value & 0xff);
     55#endif
     56
     57	return 0;
     58}
     59
     60static int cypress_ps2_ext_cmd(struct psmouse *psmouse, unsigned short cmd,
     61			       unsigned char data)
     62{
     63	struct ps2dev *ps2dev = &psmouse->ps2dev;
     64	int tries = CYTP_PS2_CMD_TRIES;
     65	int rc;
     66
     67	ps2_begin_command(ps2dev);
     68
     69	do {
     70		/*
     71		 * Send extension command byte (0xE8 or 0xF3).
     72		 * If sending the command fails, send recovery command
     73		 * to make the device return to the ready state.
     74		 */
     75		rc = cypress_ps2_sendbyte(psmouse, cmd & 0xff);
     76		if (rc == CYTP_PS2_RETRY) {
     77			rc = cypress_ps2_sendbyte(psmouse, 0x00);
     78			if (rc == CYTP_PS2_RETRY)
     79				rc = cypress_ps2_sendbyte(psmouse, 0x0a);
     80		}
     81		if (rc == CYTP_PS2_ERROR)
     82			continue;
     83
     84		rc = cypress_ps2_sendbyte(psmouse, data);
     85		if (rc == CYTP_PS2_RETRY)
     86			rc = cypress_ps2_sendbyte(psmouse, data);
     87		if (rc == CYTP_PS2_ERROR)
     88			continue;
     89		else
     90			break;
     91	} while (--tries > 0);
     92
     93	ps2_end_command(ps2dev);
     94
     95	return rc;
     96}
     97
     98static int cypress_ps2_read_cmd_status(struct psmouse *psmouse,
     99				       unsigned char cmd,
    100				       unsigned char *param)
    101{
    102	int rc;
    103	struct ps2dev *ps2dev = &psmouse->ps2dev;
    104	enum psmouse_state old_state;
    105	int pktsize;
    106
    107	ps2_begin_command(ps2dev);
    108
    109	old_state = psmouse->state;
    110	psmouse->state = PSMOUSE_CMD_MODE;
    111	psmouse->pktcnt = 0;
    112
    113	pktsize = (cmd == CYTP_CMD_READ_TP_METRICS) ? 8 : 3;
    114	memset(param, 0, pktsize);
    115
    116	rc = cypress_ps2_sendbyte(psmouse, 0xe9);
    117	if (rc < 0)
    118		goto out;
    119
    120	wait_event_timeout(ps2dev->wait,
    121			(psmouse->pktcnt >= pktsize),
    122			msecs_to_jiffies(CYTP_CMD_TIMEOUT));
    123
    124	memcpy(param, psmouse->packet, pktsize);
    125
    126	psmouse_dbg(psmouse, "Command 0x%02x response data (0x): %*ph\n",
    127			cmd, pktsize, param);
    128
    129out:
    130	psmouse->state = old_state;
    131	psmouse->pktcnt = 0;
    132
    133	ps2_end_command(ps2dev);
    134
    135	return rc;
    136}
    137
    138static bool cypress_verify_cmd_state(struct psmouse *psmouse,
    139				     unsigned char cmd, unsigned char *param)
    140{
    141	bool rate_match = false;
    142	bool resolution_match = false;
    143	int i;
    144
    145	/* callers will do further checking. */
    146	if (cmd == CYTP_CMD_READ_CYPRESS_ID ||
    147	    cmd == CYTP_CMD_STANDARD_MODE ||
    148	    cmd == CYTP_CMD_READ_TP_METRICS)
    149		return true;
    150
    151	if ((~param[0] & DFLT_RESP_BITS_VALID) == DFLT_RESP_BITS_VALID &&
    152	    (param[0] & DFLT_RESP_BIT_MODE) == DFLT_RESP_STREAM_MODE) {
    153		for (i = 0; i < sizeof(cytp_resolution); i++)
    154			if (cytp_resolution[i] == param[1])
    155				resolution_match = true;
    156
    157		for (i = 0; i < sizeof(cytp_rate); i++)
    158			if (cytp_rate[i] == param[2])
    159				rate_match = true;
    160
    161		if (resolution_match && rate_match)
    162			return true;
    163	}
    164
    165	psmouse_dbg(psmouse, "verify cmd state failed.\n");
    166	return false;
    167}
    168
    169static int cypress_send_ext_cmd(struct psmouse *psmouse, unsigned char cmd,
    170				unsigned char *param)
    171{
    172	int tries = CYTP_PS2_CMD_TRIES;
    173	int rc;
    174
    175	psmouse_dbg(psmouse, "send extension cmd 0x%02x, [%d %d %d %d]\n",
    176		 cmd, DECODE_CMD_AA(cmd), DECODE_CMD_BB(cmd),
    177		 DECODE_CMD_CC(cmd), DECODE_CMD_DD(cmd));
    178
    179	do {
    180		cypress_ps2_ext_cmd(psmouse,
    181				    PSMOUSE_CMD_SETRES, DECODE_CMD_DD(cmd));
    182		cypress_ps2_ext_cmd(psmouse,
    183				    PSMOUSE_CMD_SETRES, DECODE_CMD_CC(cmd));
    184		cypress_ps2_ext_cmd(psmouse,
    185				    PSMOUSE_CMD_SETRES, DECODE_CMD_BB(cmd));
    186		cypress_ps2_ext_cmd(psmouse,
    187				    PSMOUSE_CMD_SETRES, DECODE_CMD_AA(cmd));
    188
    189		rc = cypress_ps2_read_cmd_status(psmouse, cmd, param);
    190		if (rc)
    191			continue;
    192
    193		if (cypress_verify_cmd_state(psmouse, cmd, param))
    194			return 0;
    195
    196	} while (--tries > 0);
    197
    198	return -EIO;
    199}
    200
    201int cypress_detect(struct psmouse *psmouse, bool set_properties)
    202{
    203	unsigned char param[3];
    204
    205	if (cypress_send_ext_cmd(psmouse, CYTP_CMD_READ_CYPRESS_ID, param))
    206		return -ENODEV;
    207
    208	/* Check for Cypress Trackpad signature bytes: 0x33 0xCC */
    209	if (param[0] != 0x33 || param[1] != 0xCC)
    210		return -ENODEV;
    211
    212	if (set_properties) {
    213		psmouse->vendor = "Cypress";
    214		psmouse->name = "Trackpad";
    215	}
    216
    217	return 0;
    218}
    219
    220static int cypress_read_fw_version(struct psmouse *psmouse)
    221{
    222	struct cytp_data *cytp = psmouse->private;
    223	unsigned char param[3];
    224
    225	if (cypress_send_ext_cmd(psmouse, CYTP_CMD_READ_CYPRESS_ID, param))
    226		return -ENODEV;
    227
    228	/* Check for Cypress Trackpad signature bytes: 0x33 0xCC */
    229	if (param[0] != 0x33 || param[1] != 0xCC)
    230		return -ENODEV;
    231
    232	cytp->fw_version = param[2] & FW_VERSION_MASX;
    233	cytp->tp_metrics_supported = (param[2] & TP_METRICS_MASK) ? 1 : 0;
    234
    235	/*
    236	 * Trackpad fw_version 11 (in Dell XPS12) yields a bogus response to
    237	 * CYTP_CMD_READ_TP_METRICS so do not try to use it. LP: #1103594.
    238	 */
    239	if (cytp->fw_version >= 11)
    240		cytp->tp_metrics_supported = 0;
    241
    242	psmouse_dbg(psmouse, "cytp->fw_version = %d\n", cytp->fw_version);
    243	psmouse_dbg(psmouse, "cytp->tp_metrics_supported = %d\n",
    244		 cytp->tp_metrics_supported);
    245
    246	return 0;
    247}
    248
    249static int cypress_read_tp_metrics(struct psmouse *psmouse)
    250{
    251	struct cytp_data *cytp = psmouse->private;
    252	unsigned char param[8];
    253
    254	/* set default values for tp metrics. */
    255	cytp->tp_width = CYTP_DEFAULT_WIDTH;
    256	cytp->tp_high = CYTP_DEFAULT_HIGH;
    257	cytp->tp_max_abs_x = CYTP_ABS_MAX_X;
    258	cytp->tp_max_abs_y = CYTP_ABS_MAX_Y;
    259	cytp->tp_min_pressure = CYTP_MIN_PRESSURE;
    260	cytp->tp_max_pressure = CYTP_MAX_PRESSURE;
    261	cytp->tp_res_x = cytp->tp_max_abs_x / cytp->tp_width;
    262	cytp->tp_res_y = cytp->tp_max_abs_y / cytp->tp_high;
    263
    264	if (!cytp->tp_metrics_supported)
    265		return 0;
    266
    267	memset(param, 0, sizeof(param));
    268	if (cypress_send_ext_cmd(psmouse, CYTP_CMD_READ_TP_METRICS, param) == 0) {
    269		/* Update trackpad parameters. */
    270		cytp->tp_max_abs_x = (param[1] << 8) | param[0];
    271		cytp->tp_max_abs_y = (param[3] << 8) | param[2];
    272		cytp->tp_min_pressure = param[4];
    273		cytp->tp_max_pressure = param[5];
    274	}
    275
    276	if (!cytp->tp_max_pressure ||
    277	    cytp->tp_max_pressure < cytp->tp_min_pressure ||
    278	    !cytp->tp_width || !cytp->tp_high ||
    279	    !cytp->tp_max_abs_x ||
    280	    cytp->tp_max_abs_x < cytp->tp_width ||
    281	    !cytp->tp_max_abs_y ||
    282	    cytp->tp_max_abs_y < cytp->tp_high)
    283		return -EINVAL;
    284
    285	cytp->tp_res_x = cytp->tp_max_abs_x / cytp->tp_width;
    286	cytp->tp_res_y = cytp->tp_max_abs_y / cytp->tp_high;
    287
    288#ifdef CYTP_DEBUG_VERBOSE
    289	psmouse_dbg(psmouse, "Dump trackpad hardware configuration as below:\n");
    290	psmouse_dbg(psmouse, "cytp->tp_width = %d\n", cytp->tp_width);
    291	psmouse_dbg(psmouse, "cytp->tp_high = %d\n", cytp->tp_high);
    292	psmouse_dbg(psmouse, "cytp->tp_max_abs_x = %d\n", cytp->tp_max_abs_x);
    293	psmouse_dbg(psmouse, "cytp->tp_max_abs_y = %d\n", cytp->tp_max_abs_y);
    294	psmouse_dbg(psmouse, "cytp->tp_min_pressure = %d\n", cytp->tp_min_pressure);
    295	psmouse_dbg(psmouse, "cytp->tp_max_pressure = %d\n", cytp->tp_max_pressure);
    296	psmouse_dbg(psmouse, "cytp->tp_res_x = %d\n", cytp->tp_res_x);
    297	psmouse_dbg(psmouse, "cytp->tp_res_y = %d\n", cytp->tp_res_y);
    298
    299	psmouse_dbg(psmouse, "tp_type_APA = %d\n",
    300			(param[6] & TP_METRICS_BIT_APA) ? 1 : 0);
    301	psmouse_dbg(psmouse, "tp_type_MTG = %d\n",
    302			(param[6] & TP_METRICS_BIT_MTG) ? 1 : 0);
    303	psmouse_dbg(psmouse, "tp_palm = %d\n",
    304			(param[6] & TP_METRICS_BIT_PALM) ? 1 : 0);
    305	psmouse_dbg(psmouse, "tp_stubborn = %d\n",
    306			(param[6] & TP_METRICS_BIT_STUBBORN) ? 1 : 0);
    307	psmouse_dbg(psmouse, "tp_1f_jitter = %d\n",
    308			(param[6] & TP_METRICS_BIT_1F_JITTER) >> 2);
    309	psmouse_dbg(psmouse, "tp_2f_jitter = %d\n",
    310			(param[6] & TP_METRICS_BIT_2F_JITTER) >> 4);
    311	psmouse_dbg(psmouse, "tp_1f_spike = %d\n",
    312			param[7] & TP_METRICS_BIT_1F_SPIKE);
    313	psmouse_dbg(psmouse, "tp_2f_spike = %d\n",
    314			(param[7] & TP_METRICS_BIT_2F_SPIKE) >> 2);
    315	psmouse_dbg(psmouse, "tp_abs_packet_format_set = %d\n",
    316			(param[7] & TP_METRICS_BIT_ABS_PKT_FORMAT_SET) >> 4);
    317#endif
    318
    319	return 0;
    320}
    321
    322static int cypress_query_hardware(struct psmouse *psmouse)
    323{
    324	int ret;
    325
    326	ret = cypress_read_fw_version(psmouse);
    327	if (ret)
    328		return ret;
    329
    330	ret = cypress_read_tp_metrics(psmouse);
    331	if (ret)
    332		return ret;
    333
    334	return 0;
    335}
    336
    337static int cypress_set_absolute_mode(struct psmouse *psmouse)
    338{
    339	struct cytp_data *cytp = psmouse->private;
    340	unsigned char param[3];
    341
    342	if (cypress_send_ext_cmd(psmouse, CYTP_CMD_ABS_WITH_PRESSURE_MODE, param) < 0)
    343		return -1;
    344
    345	cytp->mode = (cytp->mode & ~CYTP_BIT_ABS_REL_MASK)
    346			| CYTP_BIT_ABS_PRESSURE;
    347	cypress_set_packet_size(psmouse, 5);
    348
    349	return 0;
    350}
    351
    352/*
    353 * Reset trackpad device.
    354 * This is also the default mode when trackpad powered on.
    355 */
    356static void cypress_reset(struct psmouse *psmouse)
    357{
    358	struct cytp_data *cytp = psmouse->private;
    359
    360	cytp->mode = 0;
    361
    362	psmouse_reset(psmouse);
    363}
    364
    365static int cypress_set_input_params(struct input_dev *input,
    366				    struct cytp_data *cytp)
    367{
    368	int ret;
    369
    370	if (!cytp->tp_res_x || !cytp->tp_res_y)
    371		return -EINVAL;
    372
    373	__set_bit(EV_ABS, input->evbit);
    374	input_set_abs_params(input, ABS_X, 0, cytp->tp_max_abs_x, 0, 0);
    375	input_set_abs_params(input, ABS_Y, 0, cytp->tp_max_abs_y, 0, 0);
    376	input_set_abs_params(input, ABS_PRESSURE,
    377			     cytp->tp_min_pressure, cytp->tp_max_pressure, 0, 0);
    378	input_set_abs_params(input, ABS_TOOL_WIDTH, 0, 255, 0, 0);
    379
    380	/* finger position */
    381	input_set_abs_params(input, ABS_MT_POSITION_X, 0, cytp->tp_max_abs_x, 0, 0);
    382	input_set_abs_params(input, ABS_MT_POSITION_Y, 0, cytp->tp_max_abs_y, 0, 0);
    383	input_set_abs_params(input, ABS_MT_PRESSURE, 0, 255, 0, 0);
    384
    385	ret = input_mt_init_slots(input, CYTP_MAX_MT_SLOTS,
    386			INPUT_MT_DROP_UNUSED|INPUT_MT_TRACK);
    387	if (ret < 0)
    388		return ret;
    389
    390	__set_bit(INPUT_PROP_SEMI_MT, input->propbit);
    391
    392	input_abs_set_res(input, ABS_X, cytp->tp_res_x);
    393	input_abs_set_res(input, ABS_Y, cytp->tp_res_y);
    394
    395	input_abs_set_res(input, ABS_MT_POSITION_X, cytp->tp_res_x);
    396	input_abs_set_res(input, ABS_MT_POSITION_Y, cytp->tp_res_y);
    397
    398	__set_bit(BTN_TOUCH, input->keybit);
    399	__set_bit(BTN_TOOL_FINGER, input->keybit);
    400	__set_bit(BTN_TOOL_DOUBLETAP, input->keybit);
    401	__set_bit(BTN_TOOL_TRIPLETAP, input->keybit);
    402	__set_bit(BTN_TOOL_QUADTAP, input->keybit);
    403	__set_bit(BTN_TOOL_QUINTTAP, input->keybit);
    404
    405	__clear_bit(EV_REL, input->evbit);
    406	__clear_bit(REL_X, input->relbit);
    407	__clear_bit(REL_Y, input->relbit);
    408
    409	__set_bit(EV_KEY, input->evbit);
    410	__set_bit(BTN_LEFT, input->keybit);
    411	__set_bit(BTN_RIGHT, input->keybit);
    412	__set_bit(BTN_MIDDLE, input->keybit);
    413
    414	return 0;
    415}
    416
    417static int cypress_get_finger_count(unsigned char header_byte)
    418{
    419	unsigned char bits6_7;
    420	int finger_count;
    421
    422	bits6_7 = header_byte >> 6;
    423	finger_count = bits6_7 & 0x03;
    424
    425	if (finger_count == 1)
    426		return 1;
    427
    428	if (header_byte & ABS_HSCROLL_BIT) {
    429		/* HSCROLL gets added on to 0 finger count. */
    430		switch (finger_count) {
    431			case 0:	return 4;
    432			case 2: return 5;
    433			default:
    434				/* Invalid contact (e.g. palm). Ignore it. */
    435				return 0;
    436		}
    437	}
    438
    439	return finger_count;
    440}
    441
    442
    443static int cypress_parse_packet(struct psmouse *psmouse,
    444				struct cytp_data *cytp, struct cytp_report_data *report_data)
    445{
    446	unsigned char *packet = psmouse->packet;
    447	unsigned char header_byte = packet[0];
    448
    449	memset(report_data, 0, sizeof(struct cytp_report_data));
    450
    451	report_data->contact_cnt = cypress_get_finger_count(header_byte);
    452	report_data->tap = (header_byte & ABS_MULTIFINGER_TAP) ? 1 : 0;
    453
    454	if (report_data->contact_cnt == 1) {
    455		report_data->contacts[0].x =
    456			((packet[1] & 0x70) << 4) | packet[2];
    457		report_data->contacts[0].y =
    458			((packet[1] & 0x07) << 8) | packet[3];
    459		if (cytp->mode & CYTP_BIT_ABS_PRESSURE)
    460			report_data->contacts[0].z = packet[4];
    461
    462	} else if (report_data->contact_cnt >= 2) {
    463		report_data->contacts[0].x =
    464			((packet[1] & 0x70) << 4) | packet[2];
    465		report_data->contacts[0].y =
    466			((packet[1] & 0x07) << 8) | packet[3];
    467		if (cytp->mode & CYTP_BIT_ABS_PRESSURE)
    468			report_data->contacts[0].z = packet[4];
    469
    470		report_data->contacts[1].x =
    471			((packet[5] & 0xf0) << 4) | packet[6];
    472		report_data->contacts[1].y =
    473			((packet[5] & 0x0f) << 8) | packet[7];
    474		if (cytp->mode & CYTP_BIT_ABS_PRESSURE)
    475			report_data->contacts[1].z = report_data->contacts[0].z;
    476	}
    477
    478	report_data->left = (header_byte & BTN_LEFT_BIT) ? 1 : 0;
    479	report_data->right = (header_byte & BTN_RIGHT_BIT) ? 1 : 0;
    480
    481	/*
    482	 * This is only true if one of the mouse buttons were tapped.  Make
    483	 * sure it doesn't turn into a click. The regular tap-to-click
    484	 * functionality will handle that on its own. If we don't do this,
    485	 * disabling tap-to-click won't affect the mouse button zones.
    486	 */
    487	if (report_data->tap)
    488		report_data->left = 0;
    489
    490#ifdef CYTP_DEBUG_VERBOSE
    491	{
    492		int i;
    493		int n = report_data->contact_cnt;
    494		psmouse_dbg(psmouse, "Dump parsed report data as below:\n");
    495		psmouse_dbg(psmouse, "contact_cnt = %d\n",
    496			report_data->contact_cnt);
    497		if (n > CYTP_MAX_MT_SLOTS)
    498		    n = CYTP_MAX_MT_SLOTS;
    499		for (i = 0; i < n; i++)
    500			psmouse_dbg(psmouse, "contacts[%d] = {%d, %d, %d}\n", i,
    501					report_data->contacts[i].x,
    502					report_data->contacts[i].y,
    503					report_data->contacts[i].z);
    504		psmouse_dbg(psmouse, "left = %d\n", report_data->left);
    505		psmouse_dbg(psmouse, "right = %d\n", report_data->right);
    506		psmouse_dbg(psmouse, "middle = %d\n", report_data->middle);
    507	}
    508#endif
    509
    510	return 0;
    511}
    512
    513static void cypress_process_packet(struct psmouse *psmouse, bool zero_pkt)
    514{
    515	int i;
    516	struct input_dev *input = psmouse->dev;
    517	struct cytp_data *cytp = psmouse->private;
    518	struct cytp_report_data report_data;
    519	struct cytp_contact *contact;
    520	struct input_mt_pos pos[CYTP_MAX_MT_SLOTS];
    521	int slots[CYTP_MAX_MT_SLOTS];
    522	int n;
    523
    524	cypress_parse_packet(psmouse, cytp, &report_data);
    525
    526	n = report_data.contact_cnt;
    527	if (n > CYTP_MAX_MT_SLOTS)
    528		n = CYTP_MAX_MT_SLOTS;
    529
    530	for (i = 0; i < n; i++) {
    531		contact = &report_data.contacts[i];
    532		pos[i].x = contact->x;
    533		pos[i].y = contact->y;
    534	}
    535
    536	input_mt_assign_slots(input, slots, pos, n, 0);
    537
    538	for (i = 0; i < n; i++) {
    539		contact = &report_data.contacts[i];
    540		input_mt_slot(input, slots[i]);
    541		input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
    542		input_report_abs(input, ABS_MT_POSITION_X, contact->x);
    543		input_report_abs(input, ABS_MT_POSITION_Y, contact->y);
    544		input_report_abs(input, ABS_MT_PRESSURE, contact->z);
    545	}
    546
    547	input_mt_sync_frame(input);
    548
    549	input_mt_report_finger_count(input, report_data.contact_cnt);
    550
    551	input_report_key(input, BTN_LEFT, report_data.left);
    552	input_report_key(input, BTN_RIGHT, report_data.right);
    553	input_report_key(input, BTN_MIDDLE, report_data.middle);
    554
    555	input_sync(input);
    556}
    557
    558static psmouse_ret_t cypress_validate_byte(struct psmouse *psmouse)
    559{
    560	int contact_cnt;
    561	int index = psmouse->pktcnt - 1;
    562	unsigned char *packet = psmouse->packet;
    563	struct cytp_data *cytp = psmouse->private;
    564
    565	if (index < 0 || index > cytp->pkt_size)
    566		return PSMOUSE_BAD_DATA;
    567
    568	if (index == 0 && (packet[0] & 0xfc) == 0) {
    569		/* call packet process for reporting finger leave. */
    570		cypress_process_packet(psmouse, 1);
    571		return PSMOUSE_FULL_PACKET;
    572	}
    573
    574	/*
    575	 * Perform validation (and adjust packet size) based only on the
    576	 * first byte; allow all further bytes through.
    577	 */
    578	if (index != 0)
    579		return PSMOUSE_GOOD_DATA;
    580
    581	/*
    582	 * If absolute/relative mode bit has not been set yet, just pass
    583	 * the byte through.
    584	 */
    585	if ((cytp->mode & CYTP_BIT_ABS_REL_MASK) == 0)
    586		return PSMOUSE_GOOD_DATA;
    587
    588	if ((packet[0] & 0x08) == 0x08)
    589		return PSMOUSE_BAD_DATA;
    590
    591	contact_cnt = cypress_get_finger_count(packet[0]);
    592	if (cytp->mode & CYTP_BIT_ABS_NO_PRESSURE)
    593		cypress_set_packet_size(psmouse, contact_cnt == 2 ? 7 : 4);
    594	else
    595		cypress_set_packet_size(psmouse, contact_cnt == 2 ? 8 : 5);
    596
    597	return PSMOUSE_GOOD_DATA;
    598}
    599
    600static psmouse_ret_t cypress_protocol_handler(struct psmouse *psmouse)
    601{
    602	struct cytp_data *cytp = psmouse->private;
    603
    604	if (psmouse->pktcnt >= cytp->pkt_size) {
    605		cypress_process_packet(psmouse, 0);
    606		return PSMOUSE_FULL_PACKET;
    607	}
    608
    609	return cypress_validate_byte(psmouse);
    610}
    611
    612static void cypress_set_rate(struct psmouse *psmouse, unsigned int rate)
    613{
    614	struct cytp_data *cytp = psmouse->private;
    615
    616	if (rate >= 80) {
    617		psmouse->rate = 80;
    618		cytp->mode |= CYTP_BIT_HIGH_RATE;
    619	} else {
    620		psmouse->rate = 40;
    621		cytp->mode &= ~CYTP_BIT_HIGH_RATE;
    622	}
    623
    624	ps2_command(&psmouse->ps2dev, (unsigned char *)&psmouse->rate,
    625		    PSMOUSE_CMD_SETRATE);
    626}
    627
    628static void cypress_disconnect(struct psmouse *psmouse)
    629{
    630	cypress_reset(psmouse);
    631	kfree(psmouse->private);
    632	psmouse->private = NULL;
    633}
    634
    635static int cypress_reconnect(struct psmouse *psmouse)
    636{
    637	int tries = CYTP_PS2_CMD_TRIES;
    638	int rc;
    639
    640	do {
    641		cypress_reset(psmouse);
    642		rc = cypress_detect(psmouse, false);
    643	} while (rc && (--tries > 0));
    644
    645	if (rc) {
    646		psmouse_err(psmouse, "Reconnect: unable to detect trackpad.\n");
    647		return -1;
    648	}
    649
    650	if (cypress_set_absolute_mode(psmouse)) {
    651		psmouse_err(psmouse, "Reconnect: Unable to initialize Cypress absolute mode.\n");
    652		return -1;
    653	}
    654
    655	return 0;
    656}
    657
    658int cypress_init(struct psmouse *psmouse)
    659{
    660	struct cytp_data *cytp;
    661
    662	cytp = kzalloc(sizeof(struct cytp_data), GFP_KERNEL);
    663	if (!cytp)
    664		return -ENOMEM;
    665
    666	psmouse->private = cytp;
    667	psmouse->pktsize = 8;
    668
    669	cypress_reset(psmouse);
    670
    671	if (cypress_query_hardware(psmouse)) {
    672		psmouse_err(psmouse, "Unable to query Trackpad hardware.\n");
    673		goto err_exit;
    674	}
    675
    676	if (cypress_set_absolute_mode(psmouse)) {
    677		psmouse_err(psmouse, "init: Unable to initialize Cypress absolute mode.\n");
    678		goto err_exit;
    679	}
    680
    681	if (cypress_set_input_params(psmouse->dev, cytp) < 0) {
    682		psmouse_err(psmouse, "init: Unable to set input params.\n");
    683		goto err_exit;
    684	}
    685
    686	psmouse->model = 1;
    687	psmouse->protocol_handler = cypress_protocol_handler;
    688	psmouse->set_rate = cypress_set_rate;
    689	psmouse->disconnect = cypress_disconnect;
    690	psmouse->reconnect = cypress_reconnect;
    691	psmouse->cleanup = cypress_reset;
    692	psmouse->resync_time = 0;
    693
    694	return 0;
    695
    696err_exit:
    697	/*
    698	 * Reset Cypress Trackpad as a standard mouse. Then
    699	 * let psmouse driver communicating with it as default PS2 mouse.
    700	 */
    701	cypress_reset(psmouse);
    702
    703	psmouse->private = NULL;
    704	kfree(cytp);
    705
    706	return -1;
    707}