Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Cypress Trackpad PS/2 mouse driver
0004  *
0005  * Copyright (c) 2012 Cypress Semiconductor Corporation.
0006  *
0007  * Author:
0008  *   Dudley Du <dudl@cypress.com>
0009  *
0010  * Additional contributors include:
0011  *   Kamal Mostafa <kamal@canonical.com>
0012  *   Kyle Fazzari <git@status.e4ward.com>
0013  */
0014 
0015 #include <linux/module.h>
0016 #include <linux/kernel.h>
0017 #include <linux/slab.h>
0018 #include <linux/serio.h>
0019 #include <linux/libps2.h>
0020 #include <linux/input.h>
0021 #include <linux/input/mt.h>
0022 #include <linux/sched.h>
0023 #include <linux/wait.h>
0024 
0025 #include "cypress_ps2.h"
0026 
0027 #undef CYTP_DEBUG_VERBOSE  /* define this and DEBUG for more verbose dump */
0028 
0029 static void cypress_set_packet_size(struct psmouse *psmouse, unsigned int n)
0030 {
0031     struct cytp_data *cytp = psmouse->private;
0032     cytp->pkt_size = n;
0033 }
0034 
0035 static const unsigned char cytp_rate[] = {10, 20, 40, 60, 100, 200};
0036 static const unsigned char cytp_resolution[] = {0x00, 0x01, 0x02, 0x03};
0037 
0038 static int cypress_ps2_sendbyte(struct psmouse *psmouse, int value)
0039 {
0040     struct ps2dev *ps2dev = &psmouse->ps2dev;
0041 
0042     if (ps2_sendbyte(ps2dev, value & 0xff, CYTP_CMD_TIMEOUT) < 0) {
0043         psmouse_dbg(psmouse,
0044                 "sending command 0x%02x failed, resp 0x%02x\n",
0045                 value & 0xff, ps2dev->nak);
0046         if (ps2dev->nak == CYTP_PS2_RETRY)
0047             return CYTP_PS2_RETRY;
0048         else
0049             return CYTP_PS2_ERROR;
0050     }
0051 
0052 #ifdef CYTP_DEBUG_VERBOSE
0053     psmouse_dbg(psmouse, "sending command 0x%02x succeeded, resp 0xfa\n",
0054             value & 0xff);
0055 #endif
0056 
0057     return 0;
0058 }
0059 
0060 static int cypress_ps2_ext_cmd(struct psmouse *psmouse, unsigned short cmd,
0061                    unsigned char data)
0062 {
0063     struct ps2dev *ps2dev = &psmouse->ps2dev;
0064     int tries = CYTP_PS2_CMD_TRIES;
0065     int rc;
0066 
0067     ps2_begin_command(ps2dev);
0068 
0069     do {
0070         /*
0071          * Send extension command byte (0xE8 or 0xF3).
0072          * If sending the command fails, send recovery command
0073          * to make the device return to the ready state.
0074          */
0075         rc = cypress_ps2_sendbyte(psmouse, cmd & 0xff);
0076         if (rc == CYTP_PS2_RETRY) {
0077             rc = cypress_ps2_sendbyte(psmouse, 0x00);
0078             if (rc == CYTP_PS2_RETRY)
0079                 rc = cypress_ps2_sendbyte(psmouse, 0x0a);
0080         }
0081         if (rc == CYTP_PS2_ERROR)
0082             continue;
0083 
0084         rc = cypress_ps2_sendbyte(psmouse, data);
0085         if (rc == CYTP_PS2_RETRY)
0086             rc = cypress_ps2_sendbyte(psmouse, data);
0087         if (rc == CYTP_PS2_ERROR)
0088             continue;
0089         else
0090             break;
0091     } while (--tries > 0);
0092 
0093     ps2_end_command(ps2dev);
0094 
0095     return rc;
0096 }
0097 
0098 static int cypress_ps2_read_cmd_status(struct psmouse *psmouse,
0099                        unsigned char cmd,
0100                        unsigned char *param)
0101 {
0102     int rc;
0103     struct ps2dev *ps2dev = &psmouse->ps2dev;
0104     enum psmouse_state old_state;
0105     int pktsize;
0106 
0107     ps2_begin_command(ps2dev);
0108 
0109     old_state = psmouse->state;
0110     psmouse->state = PSMOUSE_CMD_MODE;
0111     psmouse->pktcnt = 0;
0112 
0113     pktsize = (cmd == CYTP_CMD_READ_TP_METRICS) ? 8 : 3;
0114     memset(param, 0, pktsize);
0115 
0116     rc = cypress_ps2_sendbyte(psmouse, 0xe9);
0117     if (rc < 0)
0118         goto out;
0119 
0120     wait_event_timeout(ps2dev->wait,
0121             (psmouse->pktcnt >= pktsize),
0122             msecs_to_jiffies(CYTP_CMD_TIMEOUT));
0123 
0124     memcpy(param, psmouse->packet, pktsize);
0125 
0126     psmouse_dbg(psmouse, "Command 0x%02x response data (0x): %*ph\n",
0127             cmd, pktsize, param);
0128 
0129 out:
0130     psmouse->state = old_state;
0131     psmouse->pktcnt = 0;
0132 
0133     ps2_end_command(ps2dev);
0134 
0135     return rc;
0136 }
0137 
0138 static bool cypress_verify_cmd_state(struct psmouse *psmouse,
0139                      unsigned char cmd, unsigned char *param)
0140 {
0141     bool rate_match = false;
0142     bool resolution_match = false;
0143     int i;
0144 
0145     /* callers will do further checking. */
0146     if (cmd == CYTP_CMD_READ_CYPRESS_ID ||
0147         cmd == CYTP_CMD_STANDARD_MODE ||
0148         cmd == CYTP_CMD_READ_TP_METRICS)
0149         return true;
0150 
0151     if ((~param[0] & DFLT_RESP_BITS_VALID) == DFLT_RESP_BITS_VALID &&
0152         (param[0] & DFLT_RESP_BIT_MODE) == DFLT_RESP_STREAM_MODE) {
0153         for (i = 0; i < sizeof(cytp_resolution); i++)
0154             if (cytp_resolution[i] == param[1])
0155                 resolution_match = true;
0156 
0157         for (i = 0; i < sizeof(cytp_rate); i++)
0158             if (cytp_rate[i] == param[2])
0159                 rate_match = true;
0160 
0161         if (resolution_match && rate_match)
0162             return true;
0163     }
0164 
0165     psmouse_dbg(psmouse, "verify cmd state failed.\n");
0166     return false;
0167 }
0168 
0169 static int cypress_send_ext_cmd(struct psmouse *psmouse, unsigned char cmd,
0170                 unsigned char *param)
0171 {
0172     int tries = CYTP_PS2_CMD_TRIES;
0173     int rc;
0174 
0175     psmouse_dbg(psmouse, "send extension cmd 0x%02x, [%d %d %d %d]\n",
0176          cmd, DECODE_CMD_AA(cmd), DECODE_CMD_BB(cmd),
0177          DECODE_CMD_CC(cmd), DECODE_CMD_DD(cmd));
0178 
0179     do {
0180         cypress_ps2_ext_cmd(psmouse,
0181                     PSMOUSE_CMD_SETRES, DECODE_CMD_DD(cmd));
0182         cypress_ps2_ext_cmd(psmouse,
0183                     PSMOUSE_CMD_SETRES, DECODE_CMD_CC(cmd));
0184         cypress_ps2_ext_cmd(psmouse,
0185                     PSMOUSE_CMD_SETRES, DECODE_CMD_BB(cmd));
0186         cypress_ps2_ext_cmd(psmouse,
0187                     PSMOUSE_CMD_SETRES, DECODE_CMD_AA(cmd));
0188 
0189         rc = cypress_ps2_read_cmd_status(psmouse, cmd, param);
0190         if (rc)
0191             continue;
0192 
0193         if (cypress_verify_cmd_state(psmouse, cmd, param))
0194             return 0;
0195 
0196     } while (--tries > 0);
0197 
0198     return -EIO;
0199 }
0200 
0201 int cypress_detect(struct psmouse *psmouse, bool set_properties)
0202 {
0203     unsigned char param[3];
0204 
0205     if (cypress_send_ext_cmd(psmouse, CYTP_CMD_READ_CYPRESS_ID, param))
0206         return -ENODEV;
0207 
0208     /* Check for Cypress Trackpad signature bytes: 0x33 0xCC */
0209     if (param[0] != 0x33 || param[1] != 0xCC)
0210         return -ENODEV;
0211 
0212     if (set_properties) {
0213         psmouse->vendor = "Cypress";
0214         psmouse->name = "Trackpad";
0215     }
0216 
0217     return 0;
0218 }
0219 
0220 static int cypress_read_fw_version(struct psmouse *psmouse)
0221 {
0222     struct cytp_data *cytp = psmouse->private;
0223     unsigned char param[3];
0224 
0225     if (cypress_send_ext_cmd(psmouse, CYTP_CMD_READ_CYPRESS_ID, param))
0226         return -ENODEV;
0227 
0228     /* Check for Cypress Trackpad signature bytes: 0x33 0xCC */
0229     if (param[0] != 0x33 || param[1] != 0xCC)
0230         return -ENODEV;
0231 
0232     cytp->fw_version = param[2] & FW_VERSION_MASX;
0233     cytp->tp_metrics_supported = (param[2] & TP_METRICS_MASK) ? 1 : 0;
0234 
0235     /*
0236      * Trackpad fw_version 11 (in Dell XPS12) yields a bogus response to
0237      * CYTP_CMD_READ_TP_METRICS so do not try to use it. LP: #1103594.
0238      */
0239     if (cytp->fw_version >= 11)
0240         cytp->tp_metrics_supported = 0;
0241 
0242     psmouse_dbg(psmouse, "cytp->fw_version = %d\n", cytp->fw_version);
0243     psmouse_dbg(psmouse, "cytp->tp_metrics_supported = %d\n",
0244          cytp->tp_metrics_supported);
0245 
0246     return 0;
0247 }
0248 
0249 static int cypress_read_tp_metrics(struct psmouse *psmouse)
0250 {
0251     struct cytp_data *cytp = psmouse->private;
0252     unsigned char param[8];
0253 
0254     /* set default values for tp metrics. */
0255     cytp->tp_width = CYTP_DEFAULT_WIDTH;
0256     cytp->tp_high = CYTP_DEFAULT_HIGH;
0257     cytp->tp_max_abs_x = CYTP_ABS_MAX_X;
0258     cytp->tp_max_abs_y = CYTP_ABS_MAX_Y;
0259     cytp->tp_min_pressure = CYTP_MIN_PRESSURE;
0260     cytp->tp_max_pressure = CYTP_MAX_PRESSURE;
0261     cytp->tp_res_x = cytp->tp_max_abs_x / cytp->tp_width;
0262     cytp->tp_res_y = cytp->tp_max_abs_y / cytp->tp_high;
0263 
0264     if (!cytp->tp_metrics_supported)
0265         return 0;
0266 
0267     memset(param, 0, sizeof(param));
0268     if (cypress_send_ext_cmd(psmouse, CYTP_CMD_READ_TP_METRICS, param) == 0) {
0269         /* Update trackpad parameters. */
0270         cytp->tp_max_abs_x = (param[1] << 8) | param[0];
0271         cytp->tp_max_abs_y = (param[3] << 8) | param[2];
0272         cytp->tp_min_pressure = param[4];
0273         cytp->tp_max_pressure = param[5];
0274     }
0275 
0276     if (!cytp->tp_max_pressure ||
0277         cytp->tp_max_pressure < cytp->tp_min_pressure ||
0278         !cytp->tp_width || !cytp->tp_high ||
0279         !cytp->tp_max_abs_x ||
0280         cytp->tp_max_abs_x < cytp->tp_width ||
0281         !cytp->tp_max_abs_y ||
0282         cytp->tp_max_abs_y < cytp->tp_high)
0283         return -EINVAL;
0284 
0285     cytp->tp_res_x = cytp->tp_max_abs_x / cytp->tp_width;
0286     cytp->tp_res_y = cytp->tp_max_abs_y / cytp->tp_high;
0287 
0288 #ifdef CYTP_DEBUG_VERBOSE
0289     psmouse_dbg(psmouse, "Dump trackpad hardware configuration as below:\n");
0290     psmouse_dbg(psmouse, "cytp->tp_width = %d\n", cytp->tp_width);
0291     psmouse_dbg(psmouse, "cytp->tp_high = %d\n", cytp->tp_high);
0292     psmouse_dbg(psmouse, "cytp->tp_max_abs_x = %d\n", cytp->tp_max_abs_x);
0293     psmouse_dbg(psmouse, "cytp->tp_max_abs_y = %d\n", cytp->tp_max_abs_y);
0294     psmouse_dbg(psmouse, "cytp->tp_min_pressure = %d\n", cytp->tp_min_pressure);
0295     psmouse_dbg(psmouse, "cytp->tp_max_pressure = %d\n", cytp->tp_max_pressure);
0296     psmouse_dbg(psmouse, "cytp->tp_res_x = %d\n", cytp->tp_res_x);
0297     psmouse_dbg(psmouse, "cytp->tp_res_y = %d\n", cytp->tp_res_y);
0298 
0299     psmouse_dbg(psmouse, "tp_type_APA = %d\n",
0300             (param[6] & TP_METRICS_BIT_APA) ? 1 : 0);
0301     psmouse_dbg(psmouse, "tp_type_MTG = %d\n",
0302             (param[6] & TP_METRICS_BIT_MTG) ? 1 : 0);
0303     psmouse_dbg(psmouse, "tp_palm = %d\n",
0304             (param[6] & TP_METRICS_BIT_PALM) ? 1 : 0);
0305     psmouse_dbg(psmouse, "tp_stubborn = %d\n",
0306             (param[6] & TP_METRICS_BIT_STUBBORN) ? 1 : 0);
0307     psmouse_dbg(psmouse, "tp_1f_jitter = %d\n",
0308             (param[6] & TP_METRICS_BIT_1F_JITTER) >> 2);
0309     psmouse_dbg(psmouse, "tp_2f_jitter = %d\n",
0310             (param[6] & TP_METRICS_BIT_2F_JITTER) >> 4);
0311     psmouse_dbg(psmouse, "tp_1f_spike = %d\n",
0312             param[7] & TP_METRICS_BIT_1F_SPIKE);
0313     psmouse_dbg(psmouse, "tp_2f_spike = %d\n",
0314             (param[7] & TP_METRICS_BIT_2F_SPIKE) >> 2);
0315     psmouse_dbg(psmouse, "tp_abs_packet_format_set = %d\n",
0316             (param[7] & TP_METRICS_BIT_ABS_PKT_FORMAT_SET) >> 4);
0317 #endif
0318 
0319     return 0;
0320 }
0321 
0322 static int cypress_query_hardware(struct psmouse *psmouse)
0323 {
0324     int ret;
0325 
0326     ret = cypress_read_fw_version(psmouse);
0327     if (ret)
0328         return ret;
0329 
0330     ret = cypress_read_tp_metrics(psmouse);
0331     if (ret)
0332         return ret;
0333 
0334     return 0;
0335 }
0336 
0337 static int cypress_set_absolute_mode(struct psmouse *psmouse)
0338 {
0339     struct cytp_data *cytp = psmouse->private;
0340     unsigned char param[3];
0341 
0342     if (cypress_send_ext_cmd(psmouse, CYTP_CMD_ABS_WITH_PRESSURE_MODE, param) < 0)
0343         return -1;
0344 
0345     cytp->mode = (cytp->mode & ~CYTP_BIT_ABS_REL_MASK)
0346             | CYTP_BIT_ABS_PRESSURE;
0347     cypress_set_packet_size(psmouse, 5);
0348 
0349     return 0;
0350 }
0351 
0352 /*
0353  * Reset trackpad device.
0354  * This is also the default mode when trackpad powered on.
0355  */
0356 static void cypress_reset(struct psmouse *psmouse)
0357 {
0358     struct cytp_data *cytp = psmouse->private;
0359 
0360     cytp->mode = 0;
0361 
0362     psmouse_reset(psmouse);
0363 }
0364 
0365 static int cypress_set_input_params(struct input_dev *input,
0366                     struct cytp_data *cytp)
0367 {
0368     int ret;
0369 
0370     if (!cytp->tp_res_x || !cytp->tp_res_y)
0371         return -EINVAL;
0372 
0373     __set_bit(EV_ABS, input->evbit);
0374     input_set_abs_params(input, ABS_X, 0, cytp->tp_max_abs_x, 0, 0);
0375     input_set_abs_params(input, ABS_Y, 0, cytp->tp_max_abs_y, 0, 0);
0376     input_set_abs_params(input, ABS_PRESSURE,
0377                  cytp->tp_min_pressure, cytp->tp_max_pressure, 0, 0);
0378     input_set_abs_params(input, ABS_TOOL_WIDTH, 0, 255, 0, 0);
0379 
0380     /* finger position */
0381     input_set_abs_params(input, ABS_MT_POSITION_X, 0, cytp->tp_max_abs_x, 0, 0);
0382     input_set_abs_params(input, ABS_MT_POSITION_Y, 0, cytp->tp_max_abs_y, 0, 0);
0383     input_set_abs_params(input, ABS_MT_PRESSURE, 0, 255, 0, 0);
0384 
0385     ret = input_mt_init_slots(input, CYTP_MAX_MT_SLOTS,
0386             INPUT_MT_DROP_UNUSED|INPUT_MT_TRACK);
0387     if (ret < 0)
0388         return ret;
0389 
0390     __set_bit(INPUT_PROP_SEMI_MT, input->propbit);
0391 
0392     input_abs_set_res(input, ABS_X, cytp->tp_res_x);
0393     input_abs_set_res(input, ABS_Y, cytp->tp_res_y);
0394 
0395     input_abs_set_res(input, ABS_MT_POSITION_X, cytp->tp_res_x);
0396     input_abs_set_res(input, ABS_MT_POSITION_Y, cytp->tp_res_y);
0397 
0398     __set_bit(BTN_TOUCH, input->keybit);
0399     __set_bit(BTN_TOOL_FINGER, input->keybit);
0400     __set_bit(BTN_TOOL_DOUBLETAP, input->keybit);
0401     __set_bit(BTN_TOOL_TRIPLETAP, input->keybit);
0402     __set_bit(BTN_TOOL_QUADTAP, input->keybit);
0403     __set_bit(BTN_TOOL_QUINTTAP, input->keybit);
0404 
0405     __clear_bit(EV_REL, input->evbit);
0406     __clear_bit(REL_X, input->relbit);
0407     __clear_bit(REL_Y, input->relbit);
0408 
0409     __set_bit(EV_KEY, input->evbit);
0410     __set_bit(BTN_LEFT, input->keybit);
0411     __set_bit(BTN_RIGHT, input->keybit);
0412     __set_bit(BTN_MIDDLE, input->keybit);
0413 
0414     return 0;
0415 }
0416 
0417 static int cypress_get_finger_count(unsigned char header_byte)
0418 {
0419     unsigned char bits6_7;
0420     int finger_count;
0421 
0422     bits6_7 = header_byte >> 6;
0423     finger_count = bits6_7 & 0x03;
0424 
0425     if (finger_count == 1)
0426         return 1;
0427 
0428     if (header_byte & ABS_HSCROLL_BIT) {
0429         /* HSCROLL gets added on to 0 finger count. */
0430         switch (finger_count) {
0431             case 0: return 4;
0432             case 2: return 5;
0433             default:
0434                 /* Invalid contact (e.g. palm). Ignore it. */
0435                 return 0;
0436         }
0437     }
0438 
0439     return finger_count;
0440 }
0441 
0442 
0443 static int cypress_parse_packet(struct psmouse *psmouse,
0444                 struct cytp_data *cytp, struct cytp_report_data *report_data)
0445 {
0446     unsigned char *packet = psmouse->packet;
0447     unsigned char header_byte = packet[0];
0448 
0449     memset(report_data, 0, sizeof(struct cytp_report_data));
0450 
0451     report_data->contact_cnt = cypress_get_finger_count(header_byte);
0452     report_data->tap = (header_byte & ABS_MULTIFINGER_TAP) ? 1 : 0;
0453 
0454     if (report_data->contact_cnt == 1) {
0455         report_data->contacts[0].x =
0456             ((packet[1] & 0x70) << 4) | packet[2];
0457         report_data->contacts[0].y =
0458             ((packet[1] & 0x07) << 8) | packet[3];
0459         if (cytp->mode & CYTP_BIT_ABS_PRESSURE)
0460             report_data->contacts[0].z = packet[4];
0461 
0462     } else if (report_data->contact_cnt >= 2) {
0463         report_data->contacts[0].x =
0464             ((packet[1] & 0x70) << 4) | packet[2];
0465         report_data->contacts[0].y =
0466             ((packet[1] & 0x07) << 8) | packet[3];
0467         if (cytp->mode & CYTP_BIT_ABS_PRESSURE)
0468             report_data->contacts[0].z = packet[4];
0469 
0470         report_data->contacts[1].x =
0471             ((packet[5] & 0xf0) << 4) | packet[6];
0472         report_data->contacts[1].y =
0473             ((packet[5] & 0x0f) << 8) | packet[7];
0474         if (cytp->mode & CYTP_BIT_ABS_PRESSURE)
0475             report_data->contacts[1].z = report_data->contacts[0].z;
0476     }
0477 
0478     report_data->left = (header_byte & BTN_LEFT_BIT) ? 1 : 0;
0479     report_data->right = (header_byte & BTN_RIGHT_BIT) ? 1 : 0;
0480 
0481     /*
0482      * This is only true if one of the mouse buttons were tapped.  Make
0483      * sure it doesn't turn into a click. The regular tap-to-click
0484      * functionality will handle that on its own. If we don't do this,
0485      * disabling tap-to-click won't affect the mouse button zones.
0486      */
0487     if (report_data->tap)
0488         report_data->left = 0;
0489 
0490 #ifdef CYTP_DEBUG_VERBOSE
0491     {
0492         int i;
0493         int n = report_data->contact_cnt;
0494         psmouse_dbg(psmouse, "Dump parsed report data as below:\n");
0495         psmouse_dbg(psmouse, "contact_cnt = %d\n",
0496             report_data->contact_cnt);
0497         if (n > CYTP_MAX_MT_SLOTS)
0498             n = CYTP_MAX_MT_SLOTS;
0499         for (i = 0; i < n; i++)
0500             psmouse_dbg(psmouse, "contacts[%d] = {%d, %d, %d}\n", i,
0501                     report_data->contacts[i].x,
0502                     report_data->contacts[i].y,
0503                     report_data->contacts[i].z);
0504         psmouse_dbg(psmouse, "left = %d\n", report_data->left);
0505         psmouse_dbg(psmouse, "right = %d\n", report_data->right);
0506         psmouse_dbg(psmouse, "middle = %d\n", report_data->middle);
0507     }
0508 #endif
0509 
0510     return 0;
0511 }
0512 
0513 static void cypress_process_packet(struct psmouse *psmouse, bool zero_pkt)
0514 {
0515     int i;
0516     struct input_dev *input = psmouse->dev;
0517     struct cytp_data *cytp = psmouse->private;
0518     struct cytp_report_data report_data;
0519     struct cytp_contact *contact;
0520     struct input_mt_pos pos[CYTP_MAX_MT_SLOTS];
0521     int slots[CYTP_MAX_MT_SLOTS];
0522     int n;
0523 
0524     cypress_parse_packet(psmouse, cytp, &report_data);
0525 
0526     n = report_data.contact_cnt;
0527     if (n > CYTP_MAX_MT_SLOTS)
0528         n = CYTP_MAX_MT_SLOTS;
0529 
0530     for (i = 0; i < n; i++) {
0531         contact = &report_data.contacts[i];
0532         pos[i].x = contact->x;
0533         pos[i].y = contact->y;
0534     }
0535 
0536     input_mt_assign_slots(input, slots, pos, n, 0);
0537 
0538     for (i = 0; i < n; i++) {
0539         contact = &report_data.contacts[i];
0540         input_mt_slot(input, slots[i]);
0541         input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
0542         input_report_abs(input, ABS_MT_POSITION_X, contact->x);
0543         input_report_abs(input, ABS_MT_POSITION_Y, contact->y);
0544         input_report_abs(input, ABS_MT_PRESSURE, contact->z);
0545     }
0546 
0547     input_mt_sync_frame(input);
0548 
0549     input_mt_report_finger_count(input, report_data.contact_cnt);
0550 
0551     input_report_key(input, BTN_LEFT, report_data.left);
0552     input_report_key(input, BTN_RIGHT, report_data.right);
0553     input_report_key(input, BTN_MIDDLE, report_data.middle);
0554 
0555     input_sync(input);
0556 }
0557 
0558 static psmouse_ret_t cypress_validate_byte(struct psmouse *psmouse)
0559 {
0560     int contact_cnt;
0561     int index = psmouse->pktcnt - 1;
0562     unsigned char *packet = psmouse->packet;
0563     struct cytp_data *cytp = psmouse->private;
0564 
0565     if (index < 0 || index > cytp->pkt_size)
0566         return PSMOUSE_BAD_DATA;
0567 
0568     if (index == 0 && (packet[0] & 0xfc) == 0) {
0569         /* call packet process for reporting finger leave. */
0570         cypress_process_packet(psmouse, 1);
0571         return PSMOUSE_FULL_PACKET;
0572     }
0573 
0574     /*
0575      * Perform validation (and adjust packet size) based only on the
0576      * first byte; allow all further bytes through.
0577      */
0578     if (index != 0)
0579         return PSMOUSE_GOOD_DATA;
0580 
0581     /*
0582      * If absolute/relative mode bit has not been set yet, just pass
0583      * the byte through.
0584      */
0585     if ((cytp->mode & CYTP_BIT_ABS_REL_MASK) == 0)
0586         return PSMOUSE_GOOD_DATA;
0587 
0588     if ((packet[0] & 0x08) == 0x08)
0589         return PSMOUSE_BAD_DATA;
0590 
0591     contact_cnt = cypress_get_finger_count(packet[0]);
0592     if (cytp->mode & CYTP_BIT_ABS_NO_PRESSURE)
0593         cypress_set_packet_size(psmouse, contact_cnt == 2 ? 7 : 4);
0594     else
0595         cypress_set_packet_size(psmouse, contact_cnt == 2 ? 8 : 5);
0596 
0597     return PSMOUSE_GOOD_DATA;
0598 }
0599 
0600 static psmouse_ret_t cypress_protocol_handler(struct psmouse *psmouse)
0601 {
0602     struct cytp_data *cytp = psmouse->private;
0603 
0604     if (psmouse->pktcnt >= cytp->pkt_size) {
0605         cypress_process_packet(psmouse, 0);
0606         return PSMOUSE_FULL_PACKET;
0607     }
0608 
0609     return cypress_validate_byte(psmouse);
0610 }
0611 
0612 static void cypress_set_rate(struct psmouse *psmouse, unsigned int rate)
0613 {
0614     struct cytp_data *cytp = psmouse->private;
0615 
0616     if (rate >= 80) {
0617         psmouse->rate = 80;
0618         cytp->mode |= CYTP_BIT_HIGH_RATE;
0619     } else {
0620         psmouse->rate = 40;
0621         cytp->mode &= ~CYTP_BIT_HIGH_RATE;
0622     }
0623 
0624     ps2_command(&psmouse->ps2dev, (unsigned char *)&psmouse->rate,
0625             PSMOUSE_CMD_SETRATE);
0626 }
0627 
0628 static void cypress_disconnect(struct psmouse *psmouse)
0629 {
0630     cypress_reset(psmouse);
0631     kfree(psmouse->private);
0632     psmouse->private = NULL;
0633 }
0634 
0635 static int cypress_reconnect(struct psmouse *psmouse)
0636 {
0637     int tries = CYTP_PS2_CMD_TRIES;
0638     int rc;
0639 
0640     do {
0641         cypress_reset(psmouse);
0642         rc = cypress_detect(psmouse, false);
0643     } while (rc && (--tries > 0));
0644 
0645     if (rc) {
0646         psmouse_err(psmouse, "Reconnect: unable to detect trackpad.\n");
0647         return -1;
0648     }
0649 
0650     if (cypress_set_absolute_mode(psmouse)) {
0651         psmouse_err(psmouse, "Reconnect: Unable to initialize Cypress absolute mode.\n");
0652         return -1;
0653     }
0654 
0655     return 0;
0656 }
0657 
0658 int cypress_init(struct psmouse *psmouse)
0659 {
0660     struct cytp_data *cytp;
0661 
0662     cytp = kzalloc(sizeof(struct cytp_data), GFP_KERNEL);
0663     if (!cytp)
0664         return -ENOMEM;
0665 
0666     psmouse->private = cytp;
0667     psmouse->pktsize = 8;
0668 
0669     cypress_reset(psmouse);
0670 
0671     if (cypress_query_hardware(psmouse)) {
0672         psmouse_err(psmouse, "Unable to query Trackpad hardware.\n");
0673         goto err_exit;
0674     }
0675 
0676     if (cypress_set_absolute_mode(psmouse)) {
0677         psmouse_err(psmouse, "init: Unable to initialize Cypress absolute mode.\n");
0678         goto err_exit;
0679     }
0680 
0681     if (cypress_set_input_params(psmouse->dev, cytp) < 0) {
0682         psmouse_err(psmouse, "init: Unable to set input params.\n");
0683         goto err_exit;
0684     }
0685 
0686     psmouse->model = 1;
0687     psmouse->protocol_handler = cypress_protocol_handler;
0688     psmouse->set_rate = cypress_set_rate;
0689     psmouse->disconnect = cypress_disconnect;
0690     psmouse->reconnect = cypress_reconnect;
0691     psmouse->cleanup = cypress_reset;
0692     psmouse->resync_time = 0;
0693 
0694     return 0;
0695 
0696 err_exit:
0697     /*
0698      * Reset Cypress Trackpad as a standard mouse. Then
0699      * let psmouse driver communicating with it as default PS2 mouse.
0700      */
0701     cypress_reset(psmouse);
0702 
0703     psmouse->private = NULL;
0704     kfree(cytp);
0705 
0706     return -1;
0707 }