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