Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * BYD TouchPad PS/2 mouse driver
0004  *
0005  * Copyright (C) 2015 Chris Diamand <chris@diamand.org>
0006  * Copyright (C) 2015 Richard Pospesel
0007  * Copyright (C) 2015 Tai Chi Minh Ralph Eastwood
0008  * Copyright (C) 2015 Martin Wimpress
0009  * Copyright (C) 2015 Jay Kuri
0010  */
0011 
0012 #include <linux/delay.h>
0013 #include <linux/input.h>
0014 #include <linux/libps2.h>
0015 #include <linux/serio.h>
0016 #include <linux/slab.h>
0017 
0018 #include "psmouse.h"
0019 #include "byd.h"
0020 
0021 /* PS2 Bits */
0022 #define PS2_Y_OVERFLOW  BIT_MASK(7)
0023 #define PS2_X_OVERFLOW  BIT_MASK(6)
0024 #define PS2_Y_SIGN  BIT_MASK(5)
0025 #define PS2_X_SIGN  BIT_MASK(4)
0026 #define PS2_ALWAYS_1    BIT_MASK(3)
0027 #define PS2_MIDDLE  BIT_MASK(2)
0028 #define PS2_RIGHT   BIT_MASK(1)
0029 #define PS2_LEFT    BIT_MASK(0)
0030 
0031 /*
0032  * BYD pad constants
0033  */
0034 
0035 /*
0036  * True device resolution is unknown, however experiments show the
0037  * resolution is about 111 units/mm.
0038  * Absolute coordinate packets are in the range 0-255 for both X and Y
0039  * we pick ABS_X/ABS_Y dimensions which are multiples of 256 and in
0040  * the right ballpark given the touchpad's physical dimensions and estimate
0041  * resolution per spec sheet, device active area dimensions are
0042  * 101.6 x 60.1 mm.
0043  */
0044 #define BYD_PAD_WIDTH       11264
0045 #define BYD_PAD_HEIGHT      6656
0046 #define BYD_PAD_RESOLUTION  111
0047 
0048 /*
0049  * Given the above dimensions, relative packets velocity is in multiples of
0050  * 1 unit / 11 milliseconds.  We use this dt to estimate distance traveled
0051  */
0052 #define BYD_DT          11
0053 /* Time in jiffies used to timeout various touch events (64 ms) */
0054 #define BYD_TOUCH_TIMEOUT   msecs_to_jiffies(64)
0055 
0056 /* BYD commands reverse engineered from windows driver */
0057 
0058 /*
0059  * Swipe gesture from off-pad to on-pad
0060  *  0 : disable
0061  *  1 : enable
0062  */
0063 #define BYD_CMD_SET_OFFSCREEN_SWIPE     0x10cc
0064 /*
0065  * Tap and drag delay time
0066  *  0 : disable
0067  *  1 - 8 : least to most delay
0068  */
0069 #define BYD_CMD_SET_TAP_DRAG_DELAY_TIME     0x10cf
0070 /*
0071  * Physical buttons function mapping
0072  *  0 : enable
0073  *  4 : normal
0074  *  5 : left button custom command
0075  *  6 : right button custom command
0076  *  8 : disable
0077  */
0078 #define BYD_CMD_SET_PHYSICAL_BUTTONS        0x10d0
0079 /*
0080  * Absolute mode (1 byte X/Y resolution)
0081  *  0 : disable
0082  *  2 : enable
0083  */
0084 #define BYD_CMD_SET_ABSOLUTE_MODE       0x10d1
0085 /*
0086  * Two finger scrolling
0087  *  1 : vertical
0088  *  2 : horizontal
0089  *  3 : vertical + horizontal
0090  *  4 : disable
0091  */
0092 #define BYD_CMD_SET_TWO_FINGER_SCROLL       0x10d2
0093 /*
0094  * Handedness
0095  *  1 : right handed
0096  *  2 : left handed
0097  */
0098 #define BYD_CMD_SET_HANDEDNESS          0x10d3
0099 /*
0100  * Tap to click
0101  *  1 : enable
0102  *  2 : disable
0103  */
0104 #define BYD_CMD_SET_TAP             0x10d4
0105 /*
0106  * Tap and drag
0107  *  1 : tap and hold to drag
0108  *  2 : tap and hold to drag + lock
0109  *  3 : disable
0110  */
0111 #define BYD_CMD_SET_TAP_DRAG            0x10d5
0112 /*
0113  * Touch sensitivity
0114  *  1 - 7 : least to most sensitive
0115  */
0116 #define BYD_CMD_SET_TOUCH_SENSITIVITY       0x10d6
0117 /*
0118  * One finger scrolling
0119  *  1 : vertical
0120  *  2 : horizontal
0121  *  3 : vertical + horizontal
0122  *  4 : disable
0123  */
0124 #define BYD_CMD_SET_ONE_FINGER_SCROLL       0x10d7
0125 /*
0126  * One finger scrolling function
0127  *  1 : free scrolling
0128  *  2 : edge motion
0129  *  3 : free scrolling + edge motion
0130  *  4 : disable
0131  */
0132 #define BYD_CMD_SET_ONE_FINGER_SCROLL_FUNC  0x10d8
0133 /*
0134  * Sliding speed
0135  *  1 - 5 : slowest to fastest
0136  */
0137 #define BYD_CMD_SET_SLIDING_SPEED       0x10da
0138 /*
0139  * Edge motion
0140  *  1 : disable
0141  *  2 : enable when dragging
0142  *  3 : enable when dragging and pointing
0143  */
0144 #define BYD_CMD_SET_EDGE_MOTION         0x10db
0145 /*
0146  * Left edge region size
0147  *  0 - 7 : smallest to largest width
0148  */
0149 #define BYD_CMD_SET_LEFT_EDGE_REGION        0x10dc
0150 /*
0151  * Top edge region size
0152  *  0 - 9 : smallest to largest height
0153  */
0154 #define BYD_CMD_SET_TOP_EDGE_REGION     0x10dd
0155 /*
0156  * Disregard palm press as clicks
0157  *  1 - 6 : smallest to largest
0158  */
0159 #define BYD_CMD_SET_PALM_CHECK          0x10de
0160 /*
0161  * Right edge region size
0162  *  0 - 7 : smallest to largest width
0163  */
0164 #define BYD_CMD_SET_RIGHT_EDGE_REGION       0x10df
0165 /*
0166  * Bottom edge region size
0167  *  0 - 9 : smallest to largest height
0168  */
0169 #define BYD_CMD_SET_BOTTOM_EDGE_REGION      0x10e1
0170 /*
0171  * Multitouch gestures
0172  *  1 : enable
0173  *  2 : disable
0174  */
0175 #define BYD_CMD_SET_MULTITOUCH          0x10e3
0176 /*
0177  * Edge motion speed
0178  *  0 : control with finger pressure
0179  *  1 - 9 : slowest to fastest
0180  */
0181 #define BYD_CMD_SET_EDGE_MOTION_SPEED       0x10e4
0182 /*
0183  * Two finger scolling function
0184  *  0 : free scrolling
0185  *  1 : free scrolling (with momentum)
0186  *  2 : edge motion
0187  *  3 : free scrolling (with momentum) + edge motion
0188  *  4 : disable
0189  */
0190 #define BYD_CMD_SET_TWO_FINGER_SCROLL_FUNC  0x10e5
0191 
0192 /*
0193  * The touchpad generates a mixture of absolute and relative packets, indicated
0194  * by the last byte of each packet being set to one of the following:
0195  */
0196 #define BYD_PACKET_ABSOLUTE         0xf8
0197 #define BYD_PACKET_RELATIVE         0x00
0198 /* Multitouch gesture packets */
0199 #define BYD_PACKET_PINCH_IN         0xd8
0200 #define BYD_PACKET_PINCH_OUT            0x28
0201 #define BYD_PACKET_ROTATE_CLOCKWISE     0x29
0202 #define BYD_PACKET_ROTATE_ANTICLOCKWISE     0xd7
0203 #define BYD_PACKET_TWO_FINGER_SCROLL_RIGHT  0x2a
0204 #define BYD_PACKET_TWO_FINGER_SCROLL_DOWN   0x2b
0205 #define BYD_PACKET_TWO_FINGER_SCROLL_UP     0xd5
0206 #define BYD_PACKET_TWO_FINGER_SCROLL_LEFT   0xd6
0207 #define BYD_PACKET_THREE_FINGER_SWIPE_RIGHT 0x2c
0208 #define BYD_PACKET_THREE_FINGER_SWIPE_DOWN  0x2d
0209 #define BYD_PACKET_THREE_FINGER_SWIPE_UP    0xd3
0210 #define BYD_PACKET_THREE_FINGER_SWIPE_LEFT  0xd4
0211 #define BYD_PACKET_FOUR_FINGER_DOWN     0x33
0212 #define BYD_PACKET_FOUR_FINGER_UP       0xcd
0213 #define BYD_PACKET_REGION_SCROLL_RIGHT      0x35
0214 #define BYD_PACKET_REGION_SCROLL_DOWN       0x36
0215 #define BYD_PACKET_REGION_SCROLL_UP     0xca
0216 #define BYD_PACKET_REGION_SCROLL_LEFT       0xcb
0217 #define BYD_PACKET_RIGHT_CORNER_CLICK       0xd2
0218 #define BYD_PACKET_LEFT_CORNER_CLICK        0x2e
0219 #define BYD_PACKET_LEFT_AND_RIGHT_CORNER_CLICK  0x2f
0220 #define BYD_PACKET_ONTO_PAD_SWIPE_RIGHT     0x37
0221 #define BYD_PACKET_ONTO_PAD_SWIPE_DOWN      0x30
0222 #define BYD_PACKET_ONTO_PAD_SWIPE_UP        0xd0
0223 #define BYD_PACKET_ONTO_PAD_SWIPE_LEFT      0xc9
0224 
0225 struct byd_data {
0226     struct timer_list timer;
0227     struct psmouse *psmouse;
0228     s32 abs_x;
0229     s32 abs_y;
0230     typeof(jiffies) last_touch_time;
0231     bool btn_left;
0232     bool btn_right;
0233     bool touch;
0234 };
0235 
0236 static void byd_report_input(struct psmouse *psmouse)
0237 {
0238     struct byd_data *priv = psmouse->private;
0239     struct input_dev *dev = psmouse->dev;
0240 
0241     input_report_key(dev, BTN_TOUCH, priv->touch);
0242     input_report_key(dev, BTN_TOOL_FINGER, priv->touch);
0243 
0244     input_report_abs(dev, ABS_X, priv->abs_x);
0245     input_report_abs(dev, ABS_Y, priv->abs_y);
0246     input_report_key(dev, BTN_LEFT, priv->btn_left);
0247     input_report_key(dev, BTN_RIGHT, priv->btn_right);
0248 
0249     input_sync(dev);
0250 }
0251 
0252 static void byd_clear_touch(struct timer_list *t)
0253 {
0254     struct byd_data *priv = from_timer(priv, t, timer);
0255     struct psmouse *psmouse = priv->psmouse;
0256 
0257     serio_pause_rx(psmouse->ps2dev.serio);
0258     priv->touch = false;
0259 
0260     byd_report_input(psmouse);
0261 
0262     serio_continue_rx(psmouse->ps2dev.serio);
0263 
0264     /*
0265      * Move cursor back to center of pad when we lose touch - this
0266      * specifically improves user experience when moving cursor with one
0267      * finger, and pressing a button with another.
0268      */
0269     priv->abs_x = BYD_PAD_WIDTH / 2;
0270     priv->abs_y = BYD_PAD_HEIGHT / 2;
0271 }
0272 
0273 static psmouse_ret_t byd_process_byte(struct psmouse *psmouse)
0274 {
0275     struct byd_data *priv = psmouse->private;
0276     u8 *pkt = psmouse->packet;
0277 
0278     if (psmouse->pktcnt > 0 && !(pkt[0] & PS2_ALWAYS_1)) {
0279         psmouse_warn(psmouse, "Always_1 bit not 1. pkt[0] = %02x\n",
0280                  pkt[0]);
0281         return PSMOUSE_BAD_DATA;
0282     }
0283 
0284     if (psmouse->pktcnt < psmouse->pktsize)
0285         return PSMOUSE_GOOD_DATA;
0286 
0287     /* Otherwise, a full packet has been received */
0288     switch (pkt[3]) {
0289     case BYD_PACKET_ABSOLUTE:
0290         /* Only use absolute packets for the start of movement. */
0291         if (!priv->touch) {
0292             /* needed to detect tap */
0293             typeof(jiffies) tap_time =
0294                 priv->last_touch_time + BYD_TOUCH_TIMEOUT;
0295             priv->touch = time_after(jiffies, tap_time);
0296 
0297             /* init abs position */
0298             priv->abs_x = pkt[1] * (BYD_PAD_WIDTH / 256);
0299             priv->abs_y = (255 - pkt[2]) * (BYD_PAD_HEIGHT / 256);
0300         }
0301         break;
0302     case BYD_PACKET_RELATIVE: {
0303         /* Standard packet */
0304         /* Sign-extend if a sign bit is set. */
0305         u32 signx = pkt[0] & PS2_X_SIGN ? ~0xFF : 0;
0306         u32 signy = pkt[0] & PS2_Y_SIGN ? ~0xFF : 0;
0307         s32 dx = signx | (int) pkt[1];
0308         s32 dy = signy | (int) pkt[2];
0309 
0310         /* Update position based on velocity */
0311         priv->abs_x += dx * BYD_DT;
0312         priv->abs_y -= dy * BYD_DT;
0313 
0314         priv->touch = true;
0315         break;
0316     }
0317     default:
0318         psmouse_warn(psmouse,
0319                  "Unrecognized Z: pkt = %02x %02x %02x %02x\n",
0320                  psmouse->packet[0], psmouse->packet[1],
0321                  psmouse->packet[2], psmouse->packet[3]);
0322         return PSMOUSE_BAD_DATA;
0323     }
0324 
0325     priv->btn_left = pkt[0] & PS2_LEFT;
0326     priv->btn_right = pkt[0] & PS2_RIGHT;
0327 
0328     byd_report_input(psmouse);
0329 
0330     /* Reset time since last touch. */
0331     if (priv->touch) {
0332         priv->last_touch_time = jiffies;
0333         mod_timer(&priv->timer, jiffies + BYD_TOUCH_TIMEOUT);
0334     }
0335 
0336     return PSMOUSE_FULL_PACKET;
0337 }
0338 
0339 static int byd_reset_touchpad(struct psmouse *psmouse)
0340 {
0341     struct ps2dev *ps2dev = &psmouse->ps2dev;
0342     u8 param[4];
0343     size_t i;
0344 
0345     static const struct {
0346         u16 command;
0347         u8 arg;
0348     } seq[] = {
0349         /*
0350          * Intellimouse initialization sequence, to get 4-byte instead
0351          * of 3-byte packets.
0352          */
0353         { PSMOUSE_CMD_SETRATE, 0xC8 },
0354         { PSMOUSE_CMD_SETRATE, 0x64 },
0355         { PSMOUSE_CMD_SETRATE, 0x50 },
0356         { PSMOUSE_CMD_GETID, 0 },
0357         { PSMOUSE_CMD_ENABLE, 0 },
0358         /*
0359          * BYD-specific initialization, which enables absolute mode and
0360          * (if desired), the touchpad's built-in gesture detection.
0361          */
0362         { 0x10E2, 0x00 },
0363         { 0x10E0, 0x02 },
0364         /* The touchpad should reply with 4 seemingly-random bytes */
0365         { 0x14E0, 0x01 },
0366         /* Pairs of parameters and values. */
0367         { BYD_CMD_SET_HANDEDNESS, 0x01 },
0368         { BYD_CMD_SET_PHYSICAL_BUTTONS, 0x04 },
0369         { BYD_CMD_SET_TAP, 0x02 },
0370         { BYD_CMD_SET_ONE_FINGER_SCROLL, 0x04 },
0371         { BYD_CMD_SET_ONE_FINGER_SCROLL_FUNC, 0x04 },
0372         { BYD_CMD_SET_EDGE_MOTION, 0x01 },
0373         { BYD_CMD_SET_PALM_CHECK, 0x00 },
0374         { BYD_CMD_SET_MULTITOUCH, 0x02 },
0375         { BYD_CMD_SET_TWO_FINGER_SCROLL, 0x04 },
0376         { BYD_CMD_SET_TWO_FINGER_SCROLL_FUNC, 0x04 },
0377         { BYD_CMD_SET_LEFT_EDGE_REGION, 0x00 },
0378         { BYD_CMD_SET_TOP_EDGE_REGION, 0x00 },
0379         { BYD_CMD_SET_RIGHT_EDGE_REGION, 0x00 },
0380         { BYD_CMD_SET_BOTTOM_EDGE_REGION, 0x00 },
0381         { BYD_CMD_SET_ABSOLUTE_MODE, 0x02 },
0382         /* Finalize initialization. */
0383         { 0x10E0, 0x00 },
0384         { 0x10E2, 0x01 },
0385     };
0386 
0387     for (i = 0; i < ARRAY_SIZE(seq); ++i) {
0388         memset(param, 0, sizeof(param));
0389         param[0] = seq[i].arg;
0390         if (ps2_command(ps2dev, param, seq[i].command))
0391             return -EIO;
0392     }
0393 
0394     psmouse_set_state(psmouse, PSMOUSE_ACTIVATED);
0395     return 0;
0396 }
0397 
0398 static int byd_reconnect(struct psmouse *psmouse)
0399 {
0400     int retry = 0, error = 0;
0401 
0402     psmouse_dbg(psmouse, "Reconnect\n");
0403     do {
0404         psmouse_reset(psmouse);
0405         if (retry)
0406             ssleep(1);
0407         error = byd_detect(psmouse, 0);
0408     } while (error && ++retry < 3);
0409 
0410     if (error)
0411         return error;
0412 
0413     psmouse_dbg(psmouse, "Reconnected after %d attempts\n", retry);
0414 
0415     error = byd_reset_touchpad(psmouse);
0416     if (error) {
0417         psmouse_err(psmouse, "Unable to initialize device\n");
0418         return error;
0419     }
0420 
0421     return 0;
0422 }
0423 
0424 static void byd_disconnect(struct psmouse *psmouse)
0425 {
0426     struct byd_data *priv = psmouse->private;
0427 
0428     if (priv) {
0429         del_timer(&priv->timer);
0430         kfree(psmouse->private);
0431         psmouse->private = NULL;
0432     }
0433 }
0434 
0435 int byd_detect(struct psmouse *psmouse, bool set_properties)
0436 {
0437     struct ps2dev *ps2dev = &psmouse->ps2dev;
0438     u8 param[4] = {0x03, 0x00, 0x00, 0x00};
0439 
0440     if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
0441         return -1;
0442     if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
0443         return -1;
0444     if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
0445         return -1;
0446     if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
0447         return -1;
0448     if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
0449         return -1;
0450 
0451     if (param[1] != 0x03 || param[2] != 0x64)
0452         return -ENODEV;
0453 
0454     psmouse_dbg(psmouse, "BYD touchpad detected\n");
0455 
0456     if (set_properties) {
0457         psmouse->vendor = "BYD";
0458         psmouse->name = "TouchPad";
0459     }
0460 
0461     return 0;
0462 }
0463 
0464 int byd_init(struct psmouse *psmouse)
0465 {
0466     struct input_dev *dev = psmouse->dev;
0467     struct byd_data *priv;
0468 
0469     if (psmouse_reset(psmouse))
0470         return -EIO;
0471 
0472     if (byd_reset_touchpad(psmouse))
0473         return -EIO;
0474 
0475     priv = kzalloc(sizeof(*priv), GFP_KERNEL);
0476     if (!priv)
0477         return -ENOMEM;
0478 
0479     priv->psmouse = psmouse;
0480     timer_setup(&priv->timer, byd_clear_touch, 0);
0481 
0482     psmouse->private = priv;
0483     psmouse->disconnect = byd_disconnect;
0484     psmouse->reconnect = byd_reconnect;
0485     psmouse->protocol_handler = byd_process_byte;
0486     psmouse->pktsize = 4;
0487     psmouse->resync_time = 0;
0488 
0489     __set_bit(INPUT_PROP_POINTER, dev->propbit);
0490     /* Touchpad */
0491     __set_bit(BTN_TOUCH, dev->keybit);
0492     __set_bit(BTN_TOOL_FINGER, dev->keybit);
0493     /* Buttons */
0494     __set_bit(BTN_LEFT, dev->keybit);
0495     __set_bit(BTN_RIGHT, dev->keybit);
0496     __clear_bit(BTN_MIDDLE, dev->keybit);
0497 
0498     /* Absolute position */
0499     __set_bit(EV_ABS, dev->evbit);
0500     input_set_abs_params(dev, ABS_X, 0, BYD_PAD_WIDTH, 0, 0);
0501     input_set_abs_params(dev, ABS_Y, 0, BYD_PAD_HEIGHT, 0, 0);
0502     input_abs_set_res(dev, ABS_X, BYD_PAD_RESOLUTION);
0503     input_abs_set_res(dev, ABS_Y, BYD_PAD_RESOLUTION);
0504     /* No relative support */
0505     __clear_bit(EV_REL, dev->evbit);
0506     __clear_bit(REL_X, dev->relbit);
0507     __clear_bit(REL_Y, dev->relbit);
0508 
0509     return 0;
0510 }