cachepc-linux

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

byd.c (13034B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * BYD TouchPad PS/2 mouse driver
      4 *
      5 * Copyright (C) 2015 Chris Diamand <chris@diamand.org>
      6 * Copyright (C) 2015 Richard Pospesel
      7 * Copyright (C) 2015 Tai Chi Minh Ralph Eastwood
      8 * Copyright (C) 2015 Martin Wimpress
      9 * Copyright (C) 2015 Jay Kuri
     10 */
     11
     12#include <linux/delay.h>
     13#include <linux/input.h>
     14#include <linux/libps2.h>
     15#include <linux/serio.h>
     16#include <linux/slab.h>
     17
     18#include "psmouse.h"
     19#include "byd.h"
     20
     21/* PS2 Bits */
     22#define PS2_Y_OVERFLOW	BIT_MASK(7)
     23#define PS2_X_OVERFLOW	BIT_MASK(6)
     24#define PS2_Y_SIGN	BIT_MASK(5)
     25#define PS2_X_SIGN	BIT_MASK(4)
     26#define PS2_ALWAYS_1	BIT_MASK(3)
     27#define PS2_MIDDLE	BIT_MASK(2)
     28#define PS2_RIGHT	BIT_MASK(1)
     29#define PS2_LEFT	BIT_MASK(0)
     30
     31/*
     32 * BYD pad constants
     33 */
     34
     35/*
     36 * True device resolution is unknown, however experiments show the
     37 * resolution is about 111 units/mm.
     38 * Absolute coordinate packets are in the range 0-255 for both X and Y
     39 * we pick ABS_X/ABS_Y dimensions which are multiples of 256 and in
     40 * the right ballpark given the touchpad's physical dimensions and estimate
     41 * resolution per spec sheet, device active area dimensions are
     42 * 101.6 x 60.1 mm.
     43 */
     44#define BYD_PAD_WIDTH		11264
     45#define BYD_PAD_HEIGHT		6656
     46#define BYD_PAD_RESOLUTION	111
     47
     48/*
     49 * Given the above dimensions, relative packets velocity is in multiples of
     50 * 1 unit / 11 milliseconds.  We use this dt to estimate distance traveled
     51 */
     52#define BYD_DT			11
     53/* Time in jiffies used to timeout various touch events (64 ms) */
     54#define BYD_TOUCH_TIMEOUT	msecs_to_jiffies(64)
     55
     56/* BYD commands reverse engineered from windows driver */
     57
     58/*
     59 * Swipe gesture from off-pad to on-pad
     60 *  0 : disable
     61 *  1 : enable
     62 */
     63#define BYD_CMD_SET_OFFSCREEN_SWIPE		0x10cc
     64/*
     65 * Tap and drag delay time
     66 *  0 : disable
     67 *  1 - 8 : least to most delay
     68 */
     69#define BYD_CMD_SET_TAP_DRAG_DELAY_TIME		0x10cf
     70/*
     71 * Physical buttons function mapping
     72 *  0 : enable
     73 *  4 : normal
     74 *  5 : left button custom command
     75 *  6 : right button custom command
     76 *  8 : disable
     77 */
     78#define BYD_CMD_SET_PHYSICAL_BUTTONS		0x10d0
     79/*
     80 * Absolute mode (1 byte X/Y resolution)
     81 *  0 : disable
     82 *  2 : enable
     83 */
     84#define BYD_CMD_SET_ABSOLUTE_MODE		0x10d1
     85/*
     86 * Two finger scrolling
     87 *  1 : vertical
     88 *  2 : horizontal
     89 *  3 : vertical + horizontal
     90 *  4 : disable
     91 */
     92#define BYD_CMD_SET_TWO_FINGER_SCROLL		0x10d2
     93/*
     94 * Handedness
     95 *  1 : right handed
     96 *  2 : left handed
     97 */
     98#define BYD_CMD_SET_HANDEDNESS			0x10d3
     99/*
    100 * Tap to click
    101 *  1 : enable
    102 *  2 : disable
    103 */
    104#define BYD_CMD_SET_TAP				0x10d4
    105/*
    106 * Tap and drag
    107 *  1 : tap and hold to drag
    108 *  2 : tap and hold to drag + lock
    109 *  3 : disable
    110 */
    111#define BYD_CMD_SET_TAP_DRAG			0x10d5
    112/*
    113 * Touch sensitivity
    114 *  1 - 7 : least to most sensitive
    115 */
    116#define BYD_CMD_SET_TOUCH_SENSITIVITY		0x10d6
    117/*
    118 * One finger scrolling
    119 *  1 : vertical
    120 *  2 : horizontal
    121 *  3 : vertical + horizontal
    122 *  4 : disable
    123 */
    124#define BYD_CMD_SET_ONE_FINGER_SCROLL		0x10d7
    125/*
    126 * One finger scrolling function
    127 *  1 : free scrolling
    128 *  2 : edge motion
    129 *  3 : free scrolling + edge motion
    130 *  4 : disable
    131 */
    132#define BYD_CMD_SET_ONE_FINGER_SCROLL_FUNC	0x10d8
    133/*
    134 * Sliding speed
    135 *  1 - 5 : slowest to fastest
    136 */
    137#define BYD_CMD_SET_SLIDING_SPEED		0x10da
    138/*
    139 * Edge motion
    140 *  1 : disable
    141 *  2 : enable when dragging
    142 *  3 : enable when dragging and pointing
    143 */
    144#define BYD_CMD_SET_EDGE_MOTION			0x10db
    145/*
    146 * Left edge region size
    147 *  0 - 7 : smallest to largest width
    148 */
    149#define BYD_CMD_SET_LEFT_EDGE_REGION		0x10dc
    150/*
    151 * Top edge region size
    152 *  0 - 9 : smallest to largest height
    153 */
    154#define BYD_CMD_SET_TOP_EDGE_REGION		0x10dd
    155/*
    156 * Disregard palm press as clicks
    157 *  1 - 6 : smallest to largest
    158 */
    159#define BYD_CMD_SET_PALM_CHECK			0x10de
    160/*
    161 * Right edge region size
    162 *  0 - 7 : smallest to largest width
    163 */
    164#define BYD_CMD_SET_RIGHT_EDGE_REGION		0x10df
    165/*
    166 * Bottom edge region size
    167 *  0 - 9 : smallest to largest height
    168 */
    169#define BYD_CMD_SET_BOTTOM_EDGE_REGION		0x10e1
    170/*
    171 * Multitouch gestures
    172 *  1 : enable
    173 *  2 : disable
    174 */
    175#define BYD_CMD_SET_MULTITOUCH			0x10e3
    176/*
    177 * Edge motion speed
    178 *  0 : control with finger pressure
    179 *  1 - 9 : slowest to fastest
    180 */
    181#define BYD_CMD_SET_EDGE_MOTION_SPEED		0x10e4
    182/*
    183 * Two finger scolling function
    184 *  0 : free scrolling
    185 *  1 : free scrolling (with momentum)
    186 *  2 : edge motion
    187 *  3 : free scrolling (with momentum) + edge motion
    188 *  4 : disable
    189 */
    190#define BYD_CMD_SET_TWO_FINGER_SCROLL_FUNC	0x10e5
    191
    192/*
    193 * The touchpad generates a mixture of absolute and relative packets, indicated
    194 * by the last byte of each packet being set to one of the following:
    195 */
    196#define BYD_PACKET_ABSOLUTE			0xf8
    197#define BYD_PACKET_RELATIVE			0x00
    198/* Multitouch gesture packets */
    199#define BYD_PACKET_PINCH_IN			0xd8
    200#define BYD_PACKET_PINCH_OUT			0x28
    201#define BYD_PACKET_ROTATE_CLOCKWISE		0x29
    202#define BYD_PACKET_ROTATE_ANTICLOCKWISE		0xd7
    203#define BYD_PACKET_TWO_FINGER_SCROLL_RIGHT	0x2a
    204#define BYD_PACKET_TWO_FINGER_SCROLL_DOWN	0x2b
    205#define BYD_PACKET_TWO_FINGER_SCROLL_UP		0xd5
    206#define BYD_PACKET_TWO_FINGER_SCROLL_LEFT	0xd6
    207#define BYD_PACKET_THREE_FINGER_SWIPE_RIGHT	0x2c
    208#define BYD_PACKET_THREE_FINGER_SWIPE_DOWN	0x2d
    209#define BYD_PACKET_THREE_FINGER_SWIPE_UP	0xd3
    210#define BYD_PACKET_THREE_FINGER_SWIPE_LEFT	0xd4
    211#define BYD_PACKET_FOUR_FINGER_DOWN		0x33
    212#define BYD_PACKET_FOUR_FINGER_UP		0xcd
    213#define BYD_PACKET_REGION_SCROLL_RIGHT		0x35
    214#define BYD_PACKET_REGION_SCROLL_DOWN		0x36
    215#define BYD_PACKET_REGION_SCROLL_UP		0xca
    216#define BYD_PACKET_REGION_SCROLL_LEFT		0xcb
    217#define BYD_PACKET_RIGHT_CORNER_CLICK		0xd2
    218#define BYD_PACKET_LEFT_CORNER_CLICK		0x2e
    219#define BYD_PACKET_LEFT_AND_RIGHT_CORNER_CLICK	0x2f
    220#define BYD_PACKET_ONTO_PAD_SWIPE_RIGHT		0x37
    221#define BYD_PACKET_ONTO_PAD_SWIPE_DOWN		0x30
    222#define BYD_PACKET_ONTO_PAD_SWIPE_UP		0xd0
    223#define BYD_PACKET_ONTO_PAD_SWIPE_LEFT		0xc9
    224
    225struct byd_data {
    226	struct timer_list timer;
    227	struct psmouse *psmouse;
    228	s32 abs_x;
    229	s32 abs_y;
    230	typeof(jiffies) last_touch_time;
    231	bool btn_left;
    232	bool btn_right;
    233	bool touch;
    234};
    235
    236static void byd_report_input(struct psmouse *psmouse)
    237{
    238	struct byd_data *priv = psmouse->private;
    239	struct input_dev *dev = psmouse->dev;
    240
    241	input_report_key(dev, BTN_TOUCH, priv->touch);
    242	input_report_key(dev, BTN_TOOL_FINGER, priv->touch);
    243
    244	input_report_abs(dev, ABS_X, priv->abs_x);
    245	input_report_abs(dev, ABS_Y, priv->abs_y);
    246	input_report_key(dev, BTN_LEFT, priv->btn_left);
    247	input_report_key(dev, BTN_RIGHT, priv->btn_right);
    248
    249	input_sync(dev);
    250}
    251
    252static void byd_clear_touch(struct timer_list *t)
    253{
    254	struct byd_data *priv = from_timer(priv, t, timer);
    255	struct psmouse *psmouse = priv->psmouse;
    256
    257	serio_pause_rx(psmouse->ps2dev.serio);
    258	priv->touch = false;
    259
    260	byd_report_input(psmouse);
    261
    262	serio_continue_rx(psmouse->ps2dev.serio);
    263
    264	/*
    265	 * Move cursor back to center of pad when we lose touch - this
    266	 * specifically improves user experience when moving cursor with one
    267	 * finger, and pressing a button with another.
    268	 */
    269	priv->abs_x = BYD_PAD_WIDTH / 2;
    270	priv->abs_y = BYD_PAD_HEIGHT / 2;
    271}
    272
    273static psmouse_ret_t byd_process_byte(struct psmouse *psmouse)
    274{
    275	struct byd_data *priv = psmouse->private;
    276	u8 *pkt = psmouse->packet;
    277
    278	if (psmouse->pktcnt > 0 && !(pkt[0] & PS2_ALWAYS_1)) {
    279		psmouse_warn(psmouse, "Always_1 bit not 1. pkt[0] = %02x\n",
    280			     pkt[0]);
    281		return PSMOUSE_BAD_DATA;
    282	}
    283
    284	if (psmouse->pktcnt < psmouse->pktsize)
    285		return PSMOUSE_GOOD_DATA;
    286
    287	/* Otherwise, a full packet has been received */
    288	switch (pkt[3]) {
    289	case BYD_PACKET_ABSOLUTE:
    290		/* Only use absolute packets for the start of movement. */
    291		if (!priv->touch) {
    292			/* needed to detect tap */
    293			typeof(jiffies) tap_time =
    294				priv->last_touch_time + BYD_TOUCH_TIMEOUT;
    295			priv->touch = time_after(jiffies, tap_time);
    296
    297			/* init abs position */
    298			priv->abs_x = pkt[1] * (BYD_PAD_WIDTH / 256);
    299			priv->abs_y = (255 - pkt[2]) * (BYD_PAD_HEIGHT / 256);
    300		}
    301		break;
    302	case BYD_PACKET_RELATIVE: {
    303		/* Standard packet */
    304		/* Sign-extend if a sign bit is set. */
    305		u32 signx = pkt[0] & PS2_X_SIGN ? ~0xFF : 0;
    306		u32 signy = pkt[0] & PS2_Y_SIGN ? ~0xFF : 0;
    307		s32 dx = signx | (int) pkt[1];
    308		s32 dy = signy | (int) pkt[2];
    309
    310		/* Update position based on velocity */
    311		priv->abs_x += dx * BYD_DT;
    312		priv->abs_y -= dy * BYD_DT;
    313
    314		priv->touch = true;
    315		break;
    316	}
    317	default:
    318		psmouse_warn(psmouse,
    319			     "Unrecognized Z: pkt = %02x %02x %02x %02x\n",
    320			     psmouse->packet[0], psmouse->packet[1],
    321			     psmouse->packet[2], psmouse->packet[3]);
    322		return PSMOUSE_BAD_DATA;
    323	}
    324
    325	priv->btn_left = pkt[0] & PS2_LEFT;
    326	priv->btn_right = pkt[0] & PS2_RIGHT;
    327
    328	byd_report_input(psmouse);
    329
    330	/* Reset time since last touch. */
    331	if (priv->touch) {
    332		priv->last_touch_time = jiffies;
    333		mod_timer(&priv->timer, jiffies + BYD_TOUCH_TIMEOUT);
    334	}
    335
    336	return PSMOUSE_FULL_PACKET;
    337}
    338
    339static int byd_reset_touchpad(struct psmouse *psmouse)
    340{
    341	struct ps2dev *ps2dev = &psmouse->ps2dev;
    342	u8 param[4];
    343	size_t i;
    344
    345	static const struct {
    346		u16 command;
    347		u8 arg;
    348	} seq[] = {
    349		/*
    350		 * Intellimouse initialization sequence, to get 4-byte instead
    351		 * of 3-byte packets.
    352		 */
    353		{ PSMOUSE_CMD_SETRATE, 0xC8 },
    354		{ PSMOUSE_CMD_SETRATE, 0x64 },
    355		{ PSMOUSE_CMD_SETRATE, 0x50 },
    356		{ PSMOUSE_CMD_GETID, 0 },
    357		{ PSMOUSE_CMD_ENABLE, 0 },
    358		/*
    359		 * BYD-specific initialization, which enables absolute mode and
    360		 * (if desired), the touchpad's built-in gesture detection.
    361		 */
    362		{ 0x10E2, 0x00 },
    363		{ 0x10E0, 0x02 },
    364		/* The touchpad should reply with 4 seemingly-random bytes */
    365		{ 0x14E0, 0x01 },
    366		/* Pairs of parameters and values. */
    367		{ BYD_CMD_SET_HANDEDNESS, 0x01 },
    368		{ BYD_CMD_SET_PHYSICAL_BUTTONS, 0x04 },
    369		{ BYD_CMD_SET_TAP, 0x02 },
    370		{ BYD_CMD_SET_ONE_FINGER_SCROLL, 0x04 },
    371		{ BYD_CMD_SET_ONE_FINGER_SCROLL_FUNC, 0x04 },
    372		{ BYD_CMD_SET_EDGE_MOTION, 0x01 },
    373		{ BYD_CMD_SET_PALM_CHECK, 0x00 },
    374		{ BYD_CMD_SET_MULTITOUCH, 0x02 },
    375		{ BYD_CMD_SET_TWO_FINGER_SCROLL, 0x04 },
    376		{ BYD_CMD_SET_TWO_FINGER_SCROLL_FUNC, 0x04 },
    377		{ BYD_CMD_SET_LEFT_EDGE_REGION, 0x00 },
    378		{ BYD_CMD_SET_TOP_EDGE_REGION, 0x00 },
    379		{ BYD_CMD_SET_RIGHT_EDGE_REGION, 0x00 },
    380		{ BYD_CMD_SET_BOTTOM_EDGE_REGION, 0x00 },
    381		{ BYD_CMD_SET_ABSOLUTE_MODE, 0x02 },
    382		/* Finalize initialization. */
    383		{ 0x10E0, 0x00 },
    384		{ 0x10E2, 0x01 },
    385	};
    386
    387	for (i = 0; i < ARRAY_SIZE(seq); ++i) {
    388		memset(param, 0, sizeof(param));
    389		param[0] = seq[i].arg;
    390		if (ps2_command(ps2dev, param, seq[i].command))
    391			return -EIO;
    392	}
    393
    394	psmouse_set_state(psmouse, PSMOUSE_ACTIVATED);
    395	return 0;
    396}
    397
    398static int byd_reconnect(struct psmouse *psmouse)
    399{
    400	int retry = 0, error = 0;
    401
    402	psmouse_dbg(psmouse, "Reconnect\n");
    403	do {
    404		psmouse_reset(psmouse);
    405		if (retry)
    406			ssleep(1);
    407		error = byd_detect(psmouse, 0);
    408	} while (error && ++retry < 3);
    409
    410	if (error)
    411		return error;
    412
    413	psmouse_dbg(psmouse, "Reconnected after %d attempts\n", retry);
    414
    415	error = byd_reset_touchpad(psmouse);
    416	if (error) {
    417		psmouse_err(psmouse, "Unable to initialize device\n");
    418		return error;
    419	}
    420
    421	return 0;
    422}
    423
    424static void byd_disconnect(struct psmouse *psmouse)
    425{
    426	struct byd_data *priv = psmouse->private;
    427
    428	if (priv) {
    429		del_timer(&priv->timer);
    430		kfree(psmouse->private);
    431		psmouse->private = NULL;
    432	}
    433}
    434
    435int byd_detect(struct psmouse *psmouse, bool set_properties)
    436{
    437	struct ps2dev *ps2dev = &psmouse->ps2dev;
    438	u8 param[4] = {0x03, 0x00, 0x00, 0x00};
    439
    440	if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
    441		return -1;
    442	if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
    443		return -1;
    444	if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
    445		return -1;
    446	if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES))
    447		return -1;
    448	if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
    449		return -1;
    450
    451	if (param[1] != 0x03 || param[2] != 0x64)
    452		return -ENODEV;
    453
    454	psmouse_dbg(psmouse, "BYD touchpad detected\n");
    455
    456	if (set_properties) {
    457		psmouse->vendor = "BYD";
    458		psmouse->name = "TouchPad";
    459	}
    460
    461	return 0;
    462}
    463
    464int byd_init(struct psmouse *psmouse)
    465{
    466	struct input_dev *dev = psmouse->dev;
    467	struct byd_data *priv;
    468
    469	if (psmouse_reset(psmouse))
    470		return -EIO;
    471
    472	if (byd_reset_touchpad(psmouse))
    473		return -EIO;
    474
    475	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
    476	if (!priv)
    477		return -ENOMEM;
    478
    479	priv->psmouse = psmouse;
    480	timer_setup(&priv->timer, byd_clear_touch, 0);
    481
    482	psmouse->private = priv;
    483	psmouse->disconnect = byd_disconnect;
    484	psmouse->reconnect = byd_reconnect;
    485	psmouse->protocol_handler = byd_process_byte;
    486	psmouse->pktsize = 4;
    487	psmouse->resync_time = 0;
    488
    489	__set_bit(INPUT_PROP_POINTER, dev->propbit);
    490	/* Touchpad */
    491	__set_bit(BTN_TOUCH, dev->keybit);
    492	__set_bit(BTN_TOOL_FINGER, dev->keybit);
    493	/* Buttons */
    494	__set_bit(BTN_LEFT, dev->keybit);
    495	__set_bit(BTN_RIGHT, dev->keybit);
    496	__clear_bit(BTN_MIDDLE, dev->keybit);
    497
    498	/* Absolute position */
    499	__set_bit(EV_ABS, dev->evbit);
    500	input_set_abs_params(dev, ABS_X, 0, BYD_PAD_WIDTH, 0, 0);
    501	input_set_abs_params(dev, ABS_Y, 0, BYD_PAD_HEIGHT, 0, 0);
    502	input_abs_set_res(dev, ABS_X, BYD_PAD_RESOLUTION);
    503	input_abs_set_res(dev, ABS_Y, BYD_PAD_RESOLUTION);
    504	/* No relative support */
    505	__clear_bit(EV_REL, dev->evbit);
    506	__clear_bit(REL_X, dev->relbit);
    507	__clear_bit(REL_Y, dev->relbit);
    508
    509	return 0;
    510}