LCOV - code coverage report
Current view: top level - drivers/input/mouse - focaltech.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 165 0.0 %
Date: 2023-04-06 08:38:28 Functions: 0 17 0.0 %

          Line data    Source code
       1             : // SPDX-License-Identifier: GPL-2.0-or-later
       2             : /*
       3             :  * Focaltech TouchPad PS/2 mouse driver
       4             :  *
       5             :  * Copyright (c) 2014 Red Hat Inc.
       6             :  * Copyright (c) 2014 Mathias Gottschlag <mgottschlag@gmail.com>
       7             :  *
       8             :  * Red Hat authors:
       9             :  *
      10             :  * Hans de Goede <hdegoede@redhat.com>
      11             :  */
      12             : 
      13             : 
      14             : #include <linux/device.h>
      15             : #include <linux/libps2.h>
      16             : #include <linux/input/mt.h>
      17             : #include <linux/serio.h>
      18             : #include <linux/slab.h>
      19             : #include "psmouse.h"
      20             : #include "focaltech.h"
      21             : 
      22             : static const char * const focaltech_pnp_ids[] = {
      23             :         "FLT0101",
      24             :         "FLT0102",
      25             :         "FLT0103",
      26             :         NULL
      27             : };
      28             : 
      29             : /*
      30             :  * Even if the kernel is built without support for Focaltech PS/2 touchpads (or
      31             :  * when the real driver fails to recognize the device), we still have to detect
      32             :  * them in order to avoid further detection attempts confusing the touchpad.
      33             :  * This way it at least works in PS/2 mouse compatibility mode.
      34             :  */
      35           0 : int focaltech_detect(struct psmouse *psmouse, bool set_properties)
      36             : {
      37           0 :         if (!psmouse_matches_pnp_id(psmouse, focaltech_pnp_ids))
      38             :                 return -ENODEV;
      39             : 
      40           0 :         if (set_properties) {
      41           0 :                 psmouse->vendor = "FocalTech";
      42           0 :                 psmouse->name = "Touchpad";
      43             :         }
      44             : 
      45             :         return 0;
      46             : }
      47             : 
      48             : #ifdef CONFIG_MOUSE_PS2_FOCALTECH
      49             : 
      50             : /*
      51             :  * Packet types - the numbers are not consecutive, so we might be missing
      52             :  * something here.
      53             :  */
      54             : #define FOC_TOUCH 0x3 /* bitmap of active fingers */
      55             : #define FOC_ABS 0x6 /* absolute position of one finger */
      56             : #define FOC_REL 0x9 /* relative position of 1-2 fingers */
      57             : 
      58             : #define FOC_MAX_FINGERS 5
      59             : 
      60             : /*
      61             :  * Current state of a single finger on the touchpad.
      62             :  */
      63             : struct focaltech_finger_state {
      64             :         /* The touchpad has generated a touch event for the finger */
      65             :         bool active;
      66             : 
      67             :         /*
      68             :          * The touchpad has sent position data for the finger. The
      69             :          * flag is 0 when the finger is not active, and there is a
      70             :          * time between the first touch event for the finger and the
      71             :          * following absolute position packet for the finger where the
      72             :          * touchpad has declared the finger to be valid, but we do not
      73             :          * have any valid position yet.
      74             :          */
      75             :         bool valid;
      76             : 
      77             :         /*
      78             :          * Absolute position (from the bottom left corner) of the
      79             :          * finger.
      80             :          */
      81             :         unsigned int x;
      82             :         unsigned int y;
      83             : };
      84             : 
      85             : /*
      86             :  * Description of the current state of the touchpad hardware.
      87             :  */
      88             : struct focaltech_hw_state {
      89             :         /*
      90             :          * The touchpad tracks the positions of the fingers for us,
      91             :          * the array indices correspond to the finger indices returned
      92             :          * in the report packages.
      93             :          */
      94             :         struct focaltech_finger_state fingers[FOC_MAX_FINGERS];
      95             : 
      96             :         /*
      97             :          * Finger width 0-7 and 15 for a very big contact area.
      98             :          * 15 value stays until the finger is released.
      99             :          * Width is reported only in absolute packets.
     100             :          * Since hardware reports width only for last touching finger,
     101             :          * there is no need to store width for every specific finger,
     102             :          * so we keep only last value reported.
     103             :          */
     104             :         unsigned int width;
     105             : 
     106             :         /* True if the clickpad has been pressed. */
     107             :         bool pressed;
     108             : };
     109             : 
     110             : struct focaltech_data {
     111             :         unsigned int x_max, y_max;
     112             :         struct focaltech_hw_state state;
     113             : };
     114             : 
     115           0 : static void focaltech_report_state(struct psmouse *psmouse)
     116             : {
     117           0 :         struct focaltech_data *priv = psmouse->private;
     118           0 :         struct focaltech_hw_state *state = &priv->state;
     119           0 :         struct input_dev *dev = psmouse->dev;
     120             :         int i;
     121             : 
     122           0 :         for (i = 0; i < FOC_MAX_FINGERS; i++) {
     123           0 :                 struct focaltech_finger_state *finger = &state->fingers[i];
     124           0 :                 bool active = finger->active && finger->valid;
     125             : 
     126           0 :                 input_mt_slot(dev, i);
     127           0 :                 input_mt_report_slot_state(dev, MT_TOOL_FINGER, active);
     128           0 :                 if (active) {
     129             :                         unsigned int clamped_x, clamped_y;
     130             :                         /*
     131             :                          * The touchpad might report invalid data, so we clamp
     132             :                          * the resulting values so that we do not confuse
     133             :                          * userspace.
     134             :                          */
     135           0 :                         clamped_x = clamp(finger->x, 0U, priv->x_max);
     136           0 :                         clamped_y = clamp(finger->y, 0U, priv->y_max);
     137           0 :                         input_report_abs(dev, ABS_MT_POSITION_X, clamped_x);
     138           0 :                         input_report_abs(dev, ABS_MT_POSITION_Y,
     139           0 :                                          priv->y_max - clamped_y);
     140           0 :                         input_report_abs(dev, ABS_TOOL_WIDTH, state->width);
     141             :                 }
     142             :         }
     143           0 :         input_mt_report_pointer_emulation(dev, true);
     144             : 
     145           0 :         input_report_key(dev, BTN_LEFT, state->pressed);
     146           0 :         input_sync(dev);
     147           0 : }
     148             : 
     149             : static void focaltech_process_touch_packet(struct psmouse *psmouse,
     150             :                                            unsigned char *packet)
     151             : {
     152           0 :         struct focaltech_data *priv = psmouse->private;
     153           0 :         struct focaltech_hw_state *state = &priv->state;
     154           0 :         unsigned char fingers = packet[1];
     155             :         int i;
     156             : 
     157           0 :         state->pressed = (packet[0] >> 4) & 1;
     158             : 
     159             :         /* the second byte contains a bitmap of all fingers touching the pad */
     160           0 :         for (i = 0; i < FOC_MAX_FINGERS; i++) {
     161           0 :                 state->fingers[i].active = fingers & 0x1;
     162           0 :                 if (!state->fingers[i].active) {
     163             :                         /*
     164             :                          * Even when the finger becomes active again, we still
     165             :                          * will have to wait for the first valid position.
     166             :                          */
     167           0 :                         state->fingers[i].valid = false;
     168             :                 }
     169           0 :                 fingers >>= 1;
     170             :         }
     171             : }
     172             : 
     173           0 : static void focaltech_process_abs_packet(struct psmouse *psmouse,
     174             :                                          unsigned char *packet)
     175             : {
     176           0 :         struct focaltech_data *priv = psmouse->private;
     177           0 :         struct focaltech_hw_state *state = &priv->state;
     178             :         unsigned int finger;
     179             : 
     180           0 :         finger = (packet[1] >> 4) - 1;
     181           0 :         if (finger >= FOC_MAX_FINGERS) {
     182           0 :                 psmouse_err(psmouse, "Invalid finger in abs packet: %d\n",
     183             :                             finger);
     184             :                 return;
     185             :         }
     186             : 
     187           0 :         state->pressed = (packet[0] >> 4) & 1;
     188             : 
     189           0 :         state->fingers[finger].x = ((packet[1] & 0xf) << 8) | packet[2];
     190           0 :         state->fingers[finger].y = (packet[3] << 8) | packet[4];
     191           0 :         state->width = packet[5] >> 4;
     192           0 :         state->fingers[finger].valid = true;
     193             : }
     194             : 
     195           0 : static void focaltech_process_rel_packet(struct psmouse *psmouse,
     196             :                                          unsigned char *packet)
     197             : {
     198           0 :         struct focaltech_data *priv = psmouse->private;
     199           0 :         struct focaltech_hw_state *state = &priv->state;
     200             :         int finger1, finger2;
     201             : 
     202           0 :         state->pressed = packet[0] >> 7;
     203           0 :         finger1 = ((packet[0] >> 4) & 0x7) - 1;
     204           0 :         if (finger1 < FOC_MAX_FINGERS) {
     205           0 :                 state->fingers[finger1].x += (char)packet[1];
     206           0 :                 state->fingers[finger1].y += (char)packet[2];
     207             :         } else {
     208           0 :                 psmouse_err(psmouse, "First finger in rel packet invalid: %d\n",
     209             :                             finger1);
     210             :         }
     211             : 
     212             :         /*
     213             :          * If there is an odd number of fingers, the last relative
     214             :          * packet only contains one finger. In this case, the second
     215             :          * finger index in the packet is 0 (we subtract 1 in the lines
     216             :          * above to create array indices, so the finger will overflow
     217             :          * and be above FOC_MAX_FINGERS).
     218             :          */
     219           0 :         finger2 = ((packet[3] >> 4) & 0x7) - 1;
     220           0 :         if (finger2 < FOC_MAX_FINGERS) {
     221           0 :                 state->fingers[finger2].x += (char)packet[4];
     222           0 :                 state->fingers[finger2].y += (char)packet[5];
     223             :         }
     224           0 : }
     225             : 
     226           0 : static void focaltech_process_packet(struct psmouse *psmouse)
     227             : {
     228           0 :         unsigned char *packet = psmouse->packet;
     229             : 
     230           0 :         switch (packet[0] & 0xf) {
     231             :         case FOC_TOUCH:
     232           0 :                 focaltech_process_touch_packet(psmouse, packet);
     233             :                 break;
     234             : 
     235             :         case FOC_ABS:
     236           0 :                 focaltech_process_abs_packet(psmouse, packet);
     237           0 :                 break;
     238             : 
     239             :         case FOC_REL:
     240           0 :                 focaltech_process_rel_packet(psmouse, packet);
     241           0 :                 break;
     242             : 
     243             :         default:
     244           0 :                 psmouse_err(psmouse, "Unknown packet type: %02x\n", packet[0]);
     245           0 :                 break;
     246             :         }
     247             : 
     248           0 :         focaltech_report_state(psmouse);
     249           0 : }
     250             : 
     251           0 : static psmouse_ret_t focaltech_process_byte(struct psmouse *psmouse)
     252             : {
     253           0 :         if (psmouse->pktcnt >= 6) { /* Full packet received */
     254           0 :                 focaltech_process_packet(psmouse);
     255           0 :                 return PSMOUSE_FULL_PACKET;
     256             :         }
     257             : 
     258             :         /*
     259             :          * We might want to do some validation of the data here, but
     260             :          * we do not know the protocol well enough
     261             :          */
     262             :         return PSMOUSE_GOOD_DATA;
     263             : }
     264             : 
     265           0 : static int focaltech_switch_protocol(struct psmouse *psmouse)
     266             : {
     267           0 :         struct ps2dev *ps2dev = &psmouse->ps2dev;
     268             :         unsigned char param[3];
     269             : 
     270           0 :         param[0] = 0;
     271           0 :         if (ps2_command(ps2dev, param, 0x10f8))
     272             :                 return -EIO;
     273             : 
     274           0 :         if (ps2_command(ps2dev, param, 0x10f8))
     275             :                 return -EIO;
     276             : 
     277           0 :         if (ps2_command(ps2dev, param, 0x10f8))
     278             :                 return -EIO;
     279             : 
     280           0 :         param[0] = 1;
     281           0 :         if (ps2_command(ps2dev, param, 0x10f8))
     282             :                 return -EIO;
     283             : 
     284           0 :         if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETSCALE11))
     285             :                 return -EIO;
     286             : 
     287           0 :         if (ps2_command(ps2dev, param, PSMOUSE_CMD_ENABLE))
     288             :                 return -EIO;
     289             : 
     290           0 :         return 0;
     291             : }
     292             : 
     293           0 : static void focaltech_reset(struct psmouse *psmouse)
     294             : {
     295           0 :         ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
     296           0 :         psmouse_reset(psmouse);
     297           0 : }
     298             : 
     299           0 : static void focaltech_disconnect(struct psmouse *psmouse)
     300             : {
     301           0 :         focaltech_reset(psmouse);
     302           0 :         kfree(psmouse->private);
     303           0 :         psmouse->private = NULL;
     304           0 : }
     305             : 
     306           0 : static int focaltech_reconnect(struct psmouse *psmouse)
     307             : {
     308             :         int error;
     309             : 
     310           0 :         focaltech_reset(psmouse);
     311             : 
     312           0 :         error = focaltech_switch_protocol(psmouse);
     313           0 :         if (error) {
     314           0 :                 psmouse_err(psmouse, "Unable to initialize the device\n");
     315           0 :                 return error;
     316             :         }
     317             : 
     318             :         return 0;
     319             : }
     320             : 
     321           0 : static void focaltech_set_input_params(struct psmouse *psmouse)
     322             : {
     323           0 :         struct input_dev *dev = psmouse->dev;
     324           0 :         struct focaltech_data *priv = psmouse->private;
     325             : 
     326             :         /*
     327             :          * Undo part of setup done for us by psmouse core since touchpad
     328             :          * is not a relative device.
     329             :          */
     330           0 :         __clear_bit(EV_REL, dev->evbit);
     331           0 :         __clear_bit(REL_X, dev->relbit);
     332           0 :         __clear_bit(REL_Y, dev->relbit);
     333           0 :         __clear_bit(BTN_RIGHT, dev->keybit);
     334           0 :         __clear_bit(BTN_MIDDLE, dev->keybit);
     335             : 
     336             :         /*
     337             :          * Now set up our capabilities.
     338             :          */
     339           0 :         __set_bit(EV_ABS, dev->evbit);
     340           0 :         input_set_abs_params(dev, ABS_MT_POSITION_X, 0, priv->x_max, 0, 0);
     341           0 :         input_set_abs_params(dev, ABS_MT_POSITION_Y, 0, priv->y_max, 0, 0);
     342           0 :         input_set_abs_params(dev, ABS_TOOL_WIDTH, 0, 15, 0, 0);
     343           0 :         input_mt_init_slots(dev, 5, INPUT_MT_POINTER);
     344           0 :         __set_bit(INPUT_PROP_BUTTONPAD, dev->propbit);
     345           0 : }
     346             : 
     347           0 : static int focaltech_read_register(struct ps2dev *ps2dev, int reg,
     348             :                                    unsigned char *param)
     349             : {
     350           0 :         if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETSCALE11))
     351             :                 return -EIO;
     352             : 
     353           0 :         param[0] = 0;
     354           0 :         if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
     355             :                 return -EIO;
     356             : 
     357           0 :         if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
     358             :                 return -EIO;
     359             : 
     360           0 :         if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
     361             :                 return -EIO;
     362             : 
     363           0 :         param[0] = reg;
     364           0 :         if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
     365             :                 return -EIO;
     366             : 
     367           0 :         if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
     368             :                 return -EIO;
     369             : 
     370           0 :         return 0;
     371             : }
     372             : 
     373           0 : static int focaltech_read_size(struct psmouse *psmouse)
     374             : {
     375           0 :         struct ps2dev *ps2dev = &psmouse->ps2dev;
     376           0 :         struct focaltech_data *priv = psmouse->private;
     377             :         char param[3];
     378             : 
     379           0 :         if (focaltech_read_register(ps2dev, 2, param))
     380             :                 return -EIO;
     381             : 
     382             :         /* not sure whether this is 100% correct */
     383           0 :         priv->x_max = (unsigned char)param[1] * 128;
     384           0 :         priv->y_max = (unsigned char)param[2] * 128;
     385             : 
     386           0 :         return 0;
     387             : }
     388             : 
     389           0 : static void focaltech_set_resolution(struct psmouse *psmouse,
     390             :                                      unsigned int resolution)
     391             : {
     392             :         /* not supported yet */
     393           0 : }
     394             : 
     395           0 : static void focaltech_set_rate(struct psmouse *psmouse, unsigned int rate)
     396             : {
     397             :         /* not supported yet */
     398           0 : }
     399             : 
     400           0 : static void focaltech_set_scale(struct psmouse *psmouse,
     401             :                                 enum psmouse_scale scale)
     402             : {
     403             :         /* not supported yet */
     404           0 : }
     405             : 
     406           0 : int focaltech_init(struct psmouse *psmouse)
     407             : {
     408             :         struct focaltech_data *priv;
     409             :         int error;
     410             : 
     411           0 :         psmouse->private = priv = kzalloc(sizeof(struct focaltech_data),
     412             :                                           GFP_KERNEL);
     413           0 :         if (!priv)
     414             :                 return -ENOMEM;
     415             : 
     416           0 :         focaltech_reset(psmouse);
     417             : 
     418           0 :         error = focaltech_read_size(psmouse);
     419           0 :         if (error) {
     420           0 :                 psmouse_err(psmouse,
     421             :                             "Unable to read the size of the touchpad\n");
     422           0 :                 goto fail;
     423             :         }
     424             : 
     425           0 :         error = focaltech_switch_protocol(psmouse);
     426           0 :         if (error) {
     427           0 :                 psmouse_err(psmouse, "Unable to initialize the device\n");
     428           0 :                 goto fail;
     429             :         }
     430             : 
     431           0 :         focaltech_set_input_params(psmouse);
     432             : 
     433           0 :         psmouse->protocol_handler = focaltech_process_byte;
     434           0 :         psmouse->pktsize = 6;
     435           0 :         psmouse->disconnect = focaltech_disconnect;
     436           0 :         psmouse->reconnect = focaltech_reconnect;
     437           0 :         psmouse->cleanup = focaltech_reset;
     438             :         /* resync is not supported yet */
     439           0 :         psmouse->resync_time = 0;
     440             :         /*
     441             :          * rate/resolution/scale changes are not supported yet, and
     442             :          * the generic implementations of these functions seem to
     443             :          * confuse some touchpads
     444             :          */
     445           0 :         psmouse->set_resolution = focaltech_set_resolution;
     446           0 :         psmouse->set_rate = focaltech_set_rate;
     447           0 :         psmouse->set_scale = focaltech_set_scale;
     448             : 
     449           0 :         return 0;
     450             : 
     451             : fail:
     452           0 :         focaltech_reset(psmouse);
     453           0 :         kfree(priv);
     454           0 :         return error;
     455             : }
     456             : #endif /* CONFIG_MOUSE_PS2_FOCALTECH */

Generated by: LCOV version 1.14