Thomas Gleixner | d2912cb | 2019-06-04 10:11:33 +0200 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0-only |
Stephen Evanchik | 541e316 | 2005-08-08 01:26:18 -0500 | [diff] [blame] | 2 | /* |
| 3 | * Stephen Evanchik <evanchsa@gmail.com> |
| 4 | * |
Stephen Evanchik | 541e316 | 2005-08-08 01:26:18 -0500 | [diff] [blame] | 5 | * Trademarks are the property of their respective owners. |
| 6 | */ |
| 7 | |
Tejun Heo | 5a0e3ad | 2010-03-24 17:04:11 +0900 | [diff] [blame] | 8 | #include <linux/slab.h> |
Stephen Evanchik | 541e316 | 2005-08-08 01:26:18 -0500 | [diff] [blame] | 9 | #include <linux/delay.h> |
| 10 | #include <linux/serio.h> |
| 11 | #include <linux/module.h> |
Stephen Evanchik | 541e316 | 2005-08-08 01:26:18 -0500 | [diff] [blame] | 12 | #include <linux/input.h> |
| 13 | #include <linux/libps2.h> |
| 14 | #include <linux/proc_fs.h> |
Linus Torvalds | 7c0f6ba | 2016-12-24 11:46:01 -0800 | [diff] [blame] | 15 | #include <linux/uaccess.h> |
Stephen Evanchik | 541e316 | 2005-08-08 01:26:18 -0500 | [diff] [blame] | 16 | #include "psmouse.h" |
| 17 | #include "trackpoint.h" |
| 18 | |
Dmitry Torokhov | 2a924d7 | 2018-01-05 13:28:47 -0800 | [diff] [blame] | 19 | static const char * const trackpoint_variants[] = { |
Vincent Huang | 6c77545 | 2020-09-14 12:19:08 -0700 | [diff] [blame] | 20 | [TP_VARIANT_IBM] = "IBM", |
| 21 | [TP_VARIANT_ALPS] = "ALPS", |
| 22 | [TP_VARIANT_ELAN] = "Elan", |
| 23 | [TP_VARIANT_NXP] = "NXP", |
| 24 | [TP_VARIANT_JYT_SYNAPTICS] = "JYT_Synaptics", |
| 25 | [TP_VARIANT_SYNAPTICS] = "Synaptics", |
Dmitry Torokhov | 2a924d7 | 2018-01-05 13:28:47 -0800 | [diff] [blame] | 26 | }; |
| 27 | |
Stephen Evanchik | 541e316 | 2005-08-08 01:26:18 -0500 | [diff] [blame] | 28 | /* |
Shawn Nematbakhsh | 0c6a616 | 2013-04-15 13:49:34 -0700 | [diff] [blame] | 29 | * Power-on Reset: Resets all trackpoint parameters, including RAM values, |
| 30 | * to defaults. |
| 31 | * Returns zero on success, non-zero on failure. |
| 32 | */ |
| 33 | static int trackpoint_power_on_reset(struct ps2dev *ps2dev) |
| 34 | { |
Dmitry Torokhov | 3aceaa3 | 2018-01-04 22:52:55 -0800 | [diff] [blame] | 35 | u8 param[2] = { TP_POR }; |
| 36 | int err; |
Shawn Nematbakhsh | 0c6a616 | 2013-04-15 13:49:34 -0700 | [diff] [blame] | 37 | |
Dmitry Torokhov | 3aceaa3 | 2018-01-04 22:52:55 -0800 | [diff] [blame] | 38 | err = ps2_command(ps2dev, param, MAKE_PS2_CMD(1, 2, TP_COMMAND)); |
| 39 | if (err) |
| 40 | return err; |
Shawn Nematbakhsh | 0c6a616 | 2013-04-15 13:49:34 -0700 | [diff] [blame] | 41 | |
| 42 | /* Check for success response -- 0xAA00 */ |
Dmitry Torokhov | 3aceaa3 | 2018-01-04 22:52:55 -0800 | [diff] [blame] | 43 | if (param[0] != 0xAA || param[1] != 0x00) |
Dmitry Torokhov | 2a924d7 | 2018-01-05 13:28:47 -0800 | [diff] [blame] | 44 | return -ENODEV; |
Shawn Nematbakhsh | 0c6a616 | 2013-04-15 13:49:34 -0700 | [diff] [blame] | 45 | |
| 46 | return 0; |
| 47 | } |
| 48 | |
| 49 | /* |
Stephen Evanchik | 541e316 | 2005-08-08 01:26:18 -0500 | [diff] [blame] | 50 | * Device IO: read, write and toggle bit |
| 51 | */ |
Dmitry Torokhov | 2a924d7 | 2018-01-05 13:28:47 -0800 | [diff] [blame] | 52 | static int trackpoint_read(struct ps2dev *ps2dev, u8 loc, u8 *results) |
Stephen Evanchik | 541e316 | 2005-08-08 01:26:18 -0500 | [diff] [blame] | 53 | { |
Dmitry Torokhov | 3aceaa3 | 2018-01-04 22:52:55 -0800 | [diff] [blame] | 54 | results[0] = loc; |
Stephen Evanchik | 541e316 | 2005-08-08 01:26:18 -0500 | [diff] [blame] | 55 | |
Dmitry Torokhov | 3aceaa3 | 2018-01-04 22:52:55 -0800 | [diff] [blame] | 56 | return ps2_command(ps2dev, results, MAKE_PS2_CMD(1, 1, TP_COMMAND)); |
Stephen Evanchik | 541e316 | 2005-08-08 01:26:18 -0500 | [diff] [blame] | 57 | } |
| 58 | |
Dmitry Torokhov | 2a924d7 | 2018-01-05 13:28:47 -0800 | [diff] [blame] | 59 | static int trackpoint_write(struct ps2dev *ps2dev, u8 loc, u8 val) |
Stephen Evanchik | 541e316 | 2005-08-08 01:26:18 -0500 | [diff] [blame] | 60 | { |
Dmitry Torokhov | 3aceaa3 | 2018-01-04 22:52:55 -0800 | [diff] [blame] | 61 | u8 param[3] = { TP_WRITE_MEM, loc, val }; |
Stephen Evanchik | 541e316 | 2005-08-08 01:26:18 -0500 | [diff] [blame] | 62 | |
Dmitry Torokhov | 3aceaa3 | 2018-01-04 22:52:55 -0800 | [diff] [blame] | 63 | return ps2_command(ps2dev, param, MAKE_PS2_CMD(3, 0, TP_COMMAND)); |
Stephen Evanchik | 541e316 | 2005-08-08 01:26:18 -0500 | [diff] [blame] | 64 | } |
| 65 | |
Dmitry Torokhov | 2a924d7 | 2018-01-05 13:28:47 -0800 | [diff] [blame] | 66 | static int trackpoint_toggle_bit(struct ps2dev *ps2dev, u8 loc, u8 mask) |
Stephen Evanchik | 541e316 | 2005-08-08 01:26:18 -0500 | [diff] [blame] | 67 | { |
Dmitry Torokhov | 3aceaa3 | 2018-01-04 22:52:55 -0800 | [diff] [blame] | 68 | u8 param[3] = { TP_TOGGLE, loc, mask }; |
| 69 | |
Stephen Evanchik | 541e316 | 2005-08-08 01:26:18 -0500 | [diff] [blame] | 70 | /* Bad things will happen if the loc param isn't in this range */ |
| 71 | if (loc < 0x20 || loc >= 0x2F) |
Dmitry Torokhov | 3aceaa3 | 2018-01-04 22:52:55 -0800 | [diff] [blame] | 72 | return -EINVAL; |
Stephen Evanchik | 541e316 | 2005-08-08 01:26:18 -0500 | [diff] [blame] | 73 | |
Dmitry Torokhov | 3aceaa3 | 2018-01-04 22:52:55 -0800 | [diff] [blame] | 74 | return ps2_command(ps2dev, param, MAKE_PS2_CMD(3, 0, TP_COMMAND)); |
Stephen Evanchik | 541e316 | 2005-08-08 01:26:18 -0500 | [diff] [blame] | 75 | } |
| 76 | |
Dmitry Torokhov | 2a924d7 | 2018-01-05 13:28:47 -0800 | [diff] [blame] | 77 | static int trackpoint_update_bit(struct ps2dev *ps2dev, |
| 78 | u8 loc, u8 mask, u8 value) |
Shawn Nematbakhsh | 0c6a616 | 2013-04-15 13:49:34 -0700 | [diff] [blame] | 79 | { |
Dmitry Torokhov | 3aceaa3 | 2018-01-04 22:52:55 -0800 | [diff] [blame] | 80 | int retval; |
Dmitry Torokhov | 2a924d7 | 2018-01-05 13:28:47 -0800 | [diff] [blame] | 81 | u8 data; |
Shawn Nematbakhsh | 0c6a616 | 2013-04-15 13:49:34 -0700 | [diff] [blame] | 82 | |
Dmitry Torokhov | 3aceaa3 | 2018-01-04 22:52:55 -0800 | [diff] [blame] | 83 | retval = trackpoint_read(ps2dev, loc, &data); |
| 84 | if (retval) |
| 85 | return retval; |
| 86 | |
Shawn Nematbakhsh | 0c6a616 | 2013-04-15 13:49:34 -0700 | [diff] [blame] | 87 | if (((data & mask) == mask) != !!value) |
| 88 | retval = trackpoint_toggle_bit(ps2dev, loc, mask); |
| 89 | |
| 90 | return retval; |
| 91 | } |
Stephen Evanchik | 541e316 | 2005-08-08 01:26:18 -0500 | [diff] [blame] | 92 | |
Dmitry Torokhov | cfe9e88 | 2005-09-04 01:40:20 -0500 | [diff] [blame] | 93 | /* |
| 94 | * Trackpoint-specific attributes |
| 95 | */ |
| 96 | struct trackpoint_attr_data { |
| 97 | size_t field_offset; |
Dmitry Torokhov | 2a924d7 | 2018-01-05 13:28:47 -0800 | [diff] [blame] | 98 | u8 command; |
| 99 | u8 mask; |
| 100 | bool inverted; |
| 101 | u8 power_on_default; |
Dmitry Torokhov | cfe9e88 | 2005-09-04 01:40:20 -0500 | [diff] [blame] | 102 | }; |
Stephen Evanchik | 541e316 | 2005-08-08 01:26:18 -0500 | [diff] [blame] | 103 | |
Dmitry Torokhov | 2a924d7 | 2018-01-05 13:28:47 -0800 | [diff] [blame] | 104 | static ssize_t trackpoint_show_int_attr(struct psmouse *psmouse, |
| 105 | void *data, char *buf) |
Dmitry Torokhov | cfe9e88 | 2005-09-04 01:40:20 -0500 | [diff] [blame] | 106 | { |
| 107 | struct trackpoint_data *tp = psmouse->private; |
| 108 | struct trackpoint_attr_data *attr = data; |
Dmitry Torokhov | 2a924d7 | 2018-01-05 13:28:47 -0800 | [diff] [blame] | 109 | u8 value = *(u8 *)((void *)tp + attr->field_offset); |
Stephen Evanchik | 541e316 | 2005-08-08 01:26:18 -0500 | [diff] [blame] | 110 | |
Dmitry Torokhov | b8044c7 | 2006-02-15 00:49:09 -0500 | [diff] [blame] | 111 | if (attr->inverted) |
| 112 | value = !value; |
| 113 | |
| 114 | return sprintf(buf, "%u\n", value); |
Dmitry Torokhov | cfe9e88 | 2005-09-04 01:40:20 -0500 | [diff] [blame] | 115 | } |
Stephen Evanchik | 541e316 | 2005-08-08 01:26:18 -0500 | [diff] [blame] | 116 | |
Dmitry Torokhov | cfe9e88 | 2005-09-04 01:40:20 -0500 | [diff] [blame] | 117 | static ssize_t trackpoint_set_int_attr(struct psmouse *psmouse, void *data, |
| 118 | const char *buf, size_t count) |
| 119 | { |
| 120 | struct trackpoint_data *tp = psmouse->private; |
| 121 | struct trackpoint_attr_data *attr = data; |
Dmitry Torokhov | 2a924d7 | 2018-01-05 13:28:47 -0800 | [diff] [blame] | 122 | u8 *field = (void *)tp + attr->field_offset; |
| 123 | u8 value; |
JJ Ding | 76496e7 | 2011-11-09 10:20:14 -0800 | [diff] [blame] | 124 | int err; |
Stephen Evanchik | 541e316 | 2005-08-08 01:26:18 -0500 | [diff] [blame] | 125 | |
JJ Ding | 76496e7 | 2011-11-09 10:20:14 -0800 | [diff] [blame] | 126 | err = kstrtou8(buf, 10, &value); |
| 127 | if (err) |
| 128 | return err; |
Stephen Evanchik | 541e316 | 2005-08-08 01:26:18 -0500 | [diff] [blame] | 129 | |
Dmitry Torokhov | cfe9e88 | 2005-09-04 01:40:20 -0500 | [diff] [blame] | 130 | *field = value; |
Dmitry Torokhov | 3aceaa3 | 2018-01-04 22:52:55 -0800 | [diff] [blame] | 131 | err = trackpoint_write(&psmouse->ps2dev, attr->command, value); |
Stephen Evanchik | 541e316 | 2005-08-08 01:26:18 -0500 | [diff] [blame] | 132 | |
Dmitry Torokhov | 3aceaa3 | 2018-01-04 22:52:55 -0800 | [diff] [blame] | 133 | return err ?: count; |
Dmitry Torokhov | cfe9e88 | 2005-09-04 01:40:20 -0500 | [diff] [blame] | 134 | } |
Stephen Evanchik | 541e316 | 2005-08-08 01:26:18 -0500 | [diff] [blame] | 135 | |
Shawn Nematbakhsh | 0c6a616 | 2013-04-15 13:49:34 -0700 | [diff] [blame] | 136 | #define TRACKPOINT_INT_ATTR(_name, _command, _default) \ |
Dmitry Torokhov | cfe9e88 | 2005-09-04 01:40:20 -0500 | [diff] [blame] | 137 | static struct trackpoint_attr_data trackpoint_attr_##_name = { \ |
| 138 | .field_offset = offsetof(struct trackpoint_data, _name), \ |
| 139 | .command = _command, \ |
Shawn Nematbakhsh | 0c6a616 | 2013-04-15 13:49:34 -0700 | [diff] [blame] | 140 | .power_on_default = _default, \ |
Dmitry Torokhov | cfe9e88 | 2005-09-04 01:40:20 -0500 | [diff] [blame] | 141 | }; \ |
| 142 | PSMOUSE_DEFINE_ATTR(_name, S_IWUSR | S_IRUGO, \ |
| 143 | &trackpoint_attr_##_name, \ |
| 144 | trackpoint_show_int_attr, trackpoint_set_int_attr) |
Stephen Evanchik | 541e316 | 2005-08-08 01:26:18 -0500 | [diff] [blame] | 145 | |
Dmitry Torokhov | cfe9e88 | 2005-09-04 01:40:20 -0500 | [diff] [blame] | 146 | static ssize_t trackpoint_set_bit_attr(struct psmouse *psmouse, void *data, |
| 147 | const char *buf, size_t count) |
| 148 | { |
| 149 | struct trackpoint_data *tp = psmouse->private; |
| 150 | struct trackpoint_attr_data *attr = data; |
Dmitry Torokhov | 2a924d7 | 2018-01-05 13:28:47 -0800 | [diff] [blame] | 151 | bool *field = (void *)tp + attr->field_offset; |
| 152 | bool value; |
JJ Ding | 76496e7 | 2011-11-09 10:20:14 -0800 | [diff] [blame] | 153 | int err; |
Stephen Evanchik | 541e316 | 2005-08-08 01:26:18 -0500 | [diff] [blame] | 154 | |
Dmitry Torokhov | 2a924d7 | 2018-01-05 13:28:47 -0800 | [diff] [blame] | 155 | err = kstrtobool(buf, &value); |
JJ Ding | 76496e7 | 2011-11-09 10:20:14 -0800 | [diff] [blame] | 156 | if (err) |
| 157 | return err; |
| 158 | |
Dmitry Torokhov | b8044c7 | 2006-02-15 00:49:09 -0500 | [diff] [blame] | 159 | if (attr->inverted) |
| 160 | value = !value; |
| 161 | |
Dmitry Torokhov | cfe9e88 | 2005-09-04 01:40:20 -0500 | [diff] [blame] | 162 | if (*field != value) { |
| 163 | *field = value; |
Dmitry Torokhov | 3aceaa3 | 2018-01-04 22:52:55 -0800 | [diff] [blame] | 164 | err = trackpoint_toggle_bit(&psmouse->ps2dev, |
| 165 | attr->command, attr->mask); |
Dmitry Torokhov | cfe9e88 | 2005-09-04 01:40:20 -0500 | [diff] [blame] | 166 | } |
Stephen Evanchik | 541e316 | 2005-08-08 01:26:18 -0500 | [diff] [blame] | 167 | |
Dmitry Torokhov | 3aceaa3 | 2018-01-04 22:52:55 -0800 | [diff] [blame] | 168 | return err ?: count; |
Dmitry Torokhov | cfe9e88 | 2005-09-04 01:40:20 -0500 | [diff] [blame] | 169 | } |
| 170 | |
| 171 | |
Shawn Nematbakhsh | 0c6a616 | 2013-04-15 13:49:34 -0700 | [diff] [blame] | 172 | #define TRACKPOINT_BIT_ATTR(_name, _command, _mask, _inv, _default) \ |
| 173 | static struct trackpoint_attr_data trackpoint_attr_##_name = { \ |
| 174 | .field_offset = offsetof(struct trackpoint_data, \ |
| 175 | _name), \ |
| 176 | .command = _command, \ |
| 177 | .mask = _mask, \ |
| 178 | .inverted = _inv, \ |
| 179 | .power_on_default = _default, \ |
| 180 | }; \ |
| 181 | PSMOUSE_DEFINE_ATTR(_name, S_IWUSR | S_IRUGO, \ |
| 182 | &trackpoint_attr_##_name, \ |
| 183 | trackpoint_show_int_attr, trackpoint_set_bit_attr) |
Dmitry Torokhov | cfe9e88 | 2005-09-04 01:40:20 -0500 | [diff] [blame] | 184 | |
Shawn Nematbakhsh | 0c6a616 | 2013-04-15 13:49:34 -0700 | [diff] [blame] | 185 | TRACKPOINT_INT_ATTR(sensitivity, TP_SENS, TP_DEF_SENS); |
| 186 | TRACKPOINT_INT_ATTR(speed, TP_SPEED, TP_DEF_SPEED); |
| 187 | TRACKPOINT_INT_ATTR(inertia, TP_INERTIA, TP_DEF_INERTIA); |
| 188 | TRACKPOINT_INT_ATTR(reach, TP_REACH, TP_DEF_REACH); |
| 189 | TRACKPOINT_INT_ATTR(draghys, TP_DRAGHYS, TP_DEF_DRAGHYS); |
| 190 | TRACKPOINT_INT_ATTR(mindrag, TP_MINDRAG, TP_DEF_MINDRAG); |
| 191 | TRACKPOINT_INT_ATTR(thresh, TP_THRESH, TP_DEF_THRESH); |
| 192 | TRACKPOINT_INT_ATTR(upthresh, TP_UP_THRESH, TP_DEF_UP_THRESH); |
| 193 | TRACKPOINT_INT_ATTR(ztime, TP_Z_TIME, TP_DEF_Z_TIME); |
| 194 | TRACKPOINT_INT_ATTR(jenks, TP_JENKS_CURV, TP_DEF_JENKS_CURV); |
haarp | 2ba3532 | 2014-12-17 15:22:08 -0800 | [diff] [blame] | 195 | TRACKPOINT_INT_ATTR(drift_time, TP_DRIFT_TIME, TP_DEF_DRIFT_TIME); |
Shawn Nematbakhsh | 0c6a616 | 2013-04-15 13:49:34 -0700 | [diff] [blame] | 196 | |
Dmitry Torokhov | 2a924d7 | 2018-01-05 13:28:47 -0800 | [diff] [blame] | 197 | TRACKPOINT_BIT_ATTR(press_to_select, TP_TOGGLE_PTSON, TP_MASK_PTSON, false, |
Shawn Nematbakhsh | 0c6a616 | 2013-04-15 13:49:34 -0700 | [diff] [blame] | 198 | TP_DEF_PTSON); |
Dmitry Torokhov | 2a924d7 | 2018-01-05 13:28:47 -0800 | [diff] [blame] | 199 | TRACKPOINT_BIT_ATTR(skipback, TP_TOGGLE_SKIPBACK, TP_MASK_SKIPBACK, false, |
Shawn Nematbakhsh | 0c6a616 | 2013-04-15 13:49:34 -0700 | [diff] [blame] | 200 | TP_DEF_SKIPBACK); |
Dmitry Torokhov | 2a924d7 | 2018-01-05 13:28:47 -0800 | [diff] [blame] | 201 | TRACKPOINT_BIT_ATTR(ext_dev, TP_TOGGLE_EXT_DEV, TP_MASK_EXT_DEV, true, |
Shawn Nematbakhsh | 0c6a616 | 2013-04-15 13:49:34 -0700 | [diff] [blame] | 202 | TP_DEF_EXT_DEV); |
Stephen Evanchik | 541e316 | 2005-08-08 01:26:18 -0500 | [diff] [blame] | 203 | |
Dmitry Torokhov | 2a924d7 | 2018-01-05 13:28:47 -0800 | [diff] [blame] | 204 | static bool trackpoint_is_attr_available(struct psmouse *psmouse, |
| 205 | struct attribute *attr) |
| 206 | { |
| 207 | struct trackpoint_data *tp = psmouse->private; |
| 208 | |
| 209 | return tp->variant_id == TP_VARIANT_IBM || |
| 210 | attr == &psmouse_attr_sensitivity.dattr.attr || |
| 211 | attr == &psmouse_attr_press_to_select.dattr.attr; |
| 212 | } |
| 213 | |
| 214 | static umode_t trackpoint_is_attr_visible(struct kobject *kobj, |
| 215 | struct attribute *attr, int n) |
| 216 | { |
YueHaibing | eacacdb | 2021-06-19 22:13:29 -0700 | [diff] [blame] | 217 | struct device *dev = kobj_to_dev(kobj); |
Dmitry Torokhov | 2a924d7 | 2018-01-05 13:28:47 -0800 | [diff] [blame] | 218 | struct serio *serio = to_serio_port(dev); |
| 219 | struct psmouse *psmouse = serio_get_drvdata(serio); |
| 220 | |
| 221 | return trackpoint_is_attr_available(psmouse, attr) ? attr->mode : 0; |
| 222 | } |
| 223 | |
Stephen Evanchik | 541e316 | 2005-08-08 01:26:18 -0500 | [diff] [blame] | 224 | static struct attribute *trackpoint_attrs[] = { |
Dmitry Torokhov | cfe9e88 | 2005-09-04 01:40:20 -0500 | [diff] [blame] | 225 | &psmouse_attr_sensitivity.dattr.attr, |
| 226 | &psmouse_attr_speed.dattr.attr, |
| 227 | &psmouse_attr_inertia.dattr.attr, |
| 228 | &psmouse_attr_reach.dattr.attr, |
| 229 | &psmouse_attr_draghys.dattr.attr, |
| 230 | &psmouse_attr_mindrag.dattr.attr, |
| 231 | &psmouse_attr_thresh.dattr.attr, |
| 232 | &psmouse_attr_upthresh.dattr.attr, |
| 233 | &psmouse_attr_ztime.dattr.attr, |
| 234 | &psmouse_attr_jenks.dattr.attr, |
haarp | 2ba3532 | 2014-12-17 15:22:08 -0800 | [diff] [blame] | 235 | &psmouse_attr_drift_time.dattr.attr, |
Dmitry Torokhov | cfe9e88 | 2005-09-04 01:40:20 -0500 | [diff] [blame] | 236 | &psmouse_attr_press_to_select.dattr.attr, |
| 237 | &psmouse_attr_skipback.dattr.attr, |
| 238 | &psmouse_attr_ext_dev.dattr.attr, |
Stephen Evanchik | 541e316 | 2005-08-08 01:26:18 -0500 | [diff] [blame] | 239 | NULL |
| 240 | }; |
| 241 | |
| 242 | static struct attribute_group trackpoint_attr_group = { |
Dmitry Torokhov | 2a924d7 | 2018-01-05 13:28:47 -0800 | [diff] [blame] | 243 | .is_visible = trackpoint_is_attr_visible, |
| 244 | .attrs = trackpoint_attrs, |
Stephen Evanchik | 541e316 | 2005-08-08 01:26:18 -0500 | [diff] [blame] | 245 | }; |
| 246 | |
Dmitry Torokhov | 2a924d7 | 2018-01-05 13:28:47 -0800 | [diff] [blame] | 247 | #define TRACKPOINT_UPDATE(_power_on, _psmouse, _tp, _name) \ |
| 248 | do { \ |
| 249 | struct trackpoint_attr_data *_attr = &trackpoint_attr_##_name; \ |
| 250 | \ |
| 251 | if ((!_power_on || _tp->_name != _attr->power_on_default) && \ |
| 252 | trackpoint_is_attr_available(_psmouse, \ |
| 253 | &psmouse_attr_##_name.dattr.attr)) { \ |
| 254 | if (!_attr->mask) \ |
| 255 | trackpoint_write(&_psmouse->ps2dev, \ |
| 256 | _attr->command, _tp->_name); \ |
| 257 | else \ |
| 258 | trackpoint_update_bit(&_psmouse->ps2dev, \ |
| 259 | _attr->command, _attr->mask, \ |
| 260 | _tp->_name); \ |
| 261 | } \ |
| 262 | } while (0) |
| 263 | |
| 264 | #define TRACKPOINT_SET_POWER_ON_DEFAULT(_tp, _name) \ |
| 265 | do { \ |
| 266 | _tp->_name = trackpoint_attr_##_name.power_on_default; \ |
| 267 | } while (0) |
| 268 | |
| 269 | static int trackpoint_start_protocol(struct psmouse *psmouse, |
| 270 | u8 *variant_id, u8 *firmware_id) |
Stephen Evanchik | 541e316 | 2005-08-08 01:26:18 -0500 | [diff] [blame] | 271 | { |
Dmitry Torokhov | 2a924d7 | 2018-01-05 13:28:47 -0800 | [diff] [blame] | 272 | u8 param[2] = { 0 }; |
| 273 | int error; |
Stephen Evanchik | 541e316 | 2005-08-08 01:26:18 -0500 | [diff] [blame] | 274 | |
Dmitry Torokhov | 2a924d7 | 2018-01-05 13:28:47 -0800 | [diff] [blame] | 275 | error = ps2_command(&psmouse->ps2dev, |
| 276 | param, MAKE_PS2_CMD(0, 2, TP_READ_ID)); |
| 277 | if (error) |
| 278 | return error; |
Dmitry Torokhov | 184dd27 | 2006-08-04 22:52:26 -0400 | [diff] [blame] | 279 | |
Dmitry Torokhov | 2a924d7 | 2018-01-05 13:28:47 -0800 | [diff] [blame] | 280 | switch (param[0]) { |
| 281 | case TP_VARIANT_IBM: |
| 282 | case TP_VARIANT_ALPS: |
| 283 | case TP_VARIANT_ELAN: |
| 284 | case TP_VARIANT_NXP: |
Vincent Huang | 996d585 | 2020-09-28 16:19:05 -0700 | [diff] [blame] | 285 | case TP_VARIANT_JYT_SYNAPTICS: |
| 286 | case TP_VARIANT_SYNAPTICS: |
Dmitry Torokhov | 2a924d7 | 2018-01-05 13:28:47 -0800 | [diff] [blame] | 287 | if (variant_id) |
| 288 | *variant_id = param[0]; |
| 289 | if (firmware_id) |
| 290 | *firmware_id = param[1]; |
| 291 | return 0; |
| 292 | } |
Dmitry Torokhov | 184dd27 | 2006-08-04 22:52:26 -0400 | [diff] [blame] | 293 | |
Dmitry Torokhov | 2a924d7 | 2018-01-05 13:28:47 -0800 | [diff] [blame] | 294 | return -ENODEV; |
Stephen Evanchik | 541e316 | 2005-08-08 01:26:18 -0500 | [diff] [blame] | 295 | } |
| 296 | |
Shawn Nematbakhsh | 0c6a616 | 2013-04-15 13:49:34 -0700 | [diff] [blame] | 297 | /* |
| 298 | * Write parameters to trackpad. |
| 299 | * in_power_on_state: Set to true if TP is in default / power-on state (ex. if |
| 300 | * power-on reset was run). If so, values will only be |
| 301 | * written to TP if they differ from power-on default. |
| 302 | */ |
| 303 | static int trackpoint_sync(struct psmouse *psmouse, bool in_power_on_state) |
Stephen Evanchik | 541e316 | 2005-08-08 01:26:18 -0500 | [diff] [blame] | 304 | { |
Stephen Evanchik | 541e316 | 2005-08-08 01:26:18 -0500 | [diff] [blame] | 305 | struct trackpoint_data *tp = psmouse->private; |
Stephen Evanchik | 541e316 | 2005-08-08 01:26:18 -0500 | [diff] [blame] | 306 | |
Dmitry Torokhov | 2a924d7 | 2018-01-05 13:28:47 -0800 | [diff] [blame] | 307 | if (!in_power_on_state && tp->variant_id == TP_VARIANT_IBM) { |
Shawn Nematbakhsh | 0c6a616 | 2013-04-15 13:49:34 -0700 | [diff] [blame] | 308 | /* |
| 309 | * Disable features that may make device unusable |
| 310 | * with this driver. |
| 311 | */ |
| 312 | trackpoint_update_bit(&psmouse->ps2dev, TP_TOGGLE_TWOHAND, |
| 313 | TP_MASK_TWOHAND, TP_DEF_TWOHAND); |
Stephen Evanchik | 541e316 | 2005-08-08 01:26:18 -0500 | [diff] [blame] | 314 | |
Shawn Nematbakhsh | 0c6a616 | 2013-04-15 13:49:34 -0700 | [diff] [blame] | 315 | trackpoint_update_bit(&psmouse->ps2dev, TP_TOGGLE_SOURCE_TAG, |
| 316 | TP_MASK_SOURCE_TAG, TP_DEF_SOURCE_TAG); |
Stephen Evanchik | 541e316 | 2005-08-08 01:26:18 -0500 | [diff] [blame] | 317 | |
Shawn Nematbakhsh | 0c6a616 | 2013-04-15 13:49:34 -0700 | [diff] [blame] | 318 | trackpoint_update_bit(&psmouse->ps2dev, TP_TOGGLE_MB, |
| 319 | TP_MASK_MB, TP_DEF_MB); |
| 320 | } |
Stephen Evanchik | 541e316 | 2005-08-08 01:26:18 -0500 | [diff] [blame] | 321 | |
Shawn Nematbakhsh | 0c6a616 | 2013-04-15 13:49:34 -0700 | [diff] [blame] | 322 | /* |
| 323 | * These properties can be changed in this driver. Only |
| 324 | * configure them if the values are non-default or if the TP is in |
| 325 | * an unknown state. |
| 326 | */ |
| 327 | TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, sensitivity); |
| 328 | TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, inertia); |
| 329 | TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, speed); |
| 330 | TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, reach); |
| 331 | TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, draghys); |
| 332 | TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, mindrag); |
| 333 | TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, thresh); |
| 334 | TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, upthresh); |
| 335 | TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, ztime); |
| 336 | TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, jenks); |
haarp | 2ba3532 | 2014-12-17 15:22:08 -0800 | [diff] [blame] | 337 | TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, drift_time); |
Stephen Evanchik | 541e316 | 2005-08-08 01:26:18 -0500 | [diff] [blame] | 338 | |
Shawn Nematbakhsh | 0c6a616 | 2013-04-15 13:49:34 -0700 | [diff] [blame] | 339 | /* toggles */ |
| 340 | TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, press_to_select); |
| 341 | TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, skipback); |
| 342 | TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, ext_dev); |
Stephen Evanchik | 541e316 | 2005-08-08 01:26:18 -0500 | [diff] [blame] | 343 | |
| 344 | return 0; |
| 345 | } |
| 346 | |
| 347 | static void trackpoint_defaults(struct trackpoint_data *tp) |
| 348 | { |
Shawn Nematbakhsh | 0c6a616 | 2013-04-15 13:49:34 -0700 | [diff] [blame] | 349 | TRACKPOINT_SET_POWER_ON_DEFAULT(tp, sensitivity); |
| 350 | TRACKPOINT_SET_POWER_ON_DEFAULT(tp, speed); |
| 351 | TRACKPOINT_SET_POWER_ON_DEFAULT(tp, reach); |
| 352 | TRACKPOINT_SET_POWER_ON_DEFAULT(tp, draghys); |
| 353 | TRACKPOINT_SET_POWER_ON_DEFAULT(tp, mindrag); |
| 354 | TRACKPOINT_SET_POWER_ON_DEFAULT(tp, thresh); |
| 355 | TRACKPOINT_SET_POWER_ON_DEFAULT(tp, upthresh); |
| 356 | TRACKPOINT_SET_POWER_ON_DEFAULT(tp, ztime); |
| 357 | TRACKPOINT_SET_POWER_ON_DEFAULT(tp, jenks); |
haarp | 2ba3532 | 2014-12-17 15:22:08 -0800 | [diff] [blame] | 358 | TRACKPOINT_SET_POWER_ON_DEFAULT(tp, drift_time); |
Shawn Nematbakhsh | 0c6a616 | 2013-04-15 13:49:34 -0700 | [diff] [blame] | 359 | TRACKPOINT_SET_POWER_ON_DEFAULT(tp, inertia); |
Stephen Evanchik | 541e316 | 2005-08-08 01:26:18 -0500 | [diff] [blame] | 360 | |
Shawn Nematbakhsh | 0c6a616 | 2013-04-15 13:49:34 -0700 | [diff] [blame] | 361 | /* toggles */ |
| 362 | TRACKPOINT_SET_POWER_ON_DEFAULT(tp, press_to_select); |
| 363 | TRACKPOINT_SET_POWER_ON_DEFAULT(tp, skipback); |
| 364 | TRACKPOINT_SET_POWER_ON_DEFAULT(tp, ext_dev); |
Stephen Evanchik | 541e316 | 2005-08-08 01:26:18 -0500 | [diff] [blame] | 365 | } |
| 366 | |
Dmitry Torokhov | 184dd27 | 2006-08-04 22:52:26 -0400 | [diff] [blame] | 367 | static void trackpoint_disconnect(struct psmouse *psmouse) |
| 368 | { |
Dmitry Torokhov | 2a924d7 | 2018-01-05 13:28:47 -0800 | [diff] [blame] | 369 | device_remove_group(&psmouse->ps2dev.serio->dev, |
| 370 | &trackpoint_attr_group); |
Dmitry Torokhov | 184dd27 | 2006-08-04 22:52:26 -0400 | [diff] [blame] | 371 | |
| 372 | kfree(psmouse->private); |
| 373 | psmouse->private = NULL; |
| 374 | } |
| 375 | |
| 376 | static int trackpoint_reconnect(struct psmouse *psmouse) |
| 377 | { |
Dmitry Torokhov | 2a924d7 | 2018-01-05 13:28:47 -0800 | [diff] [blame] | 378 | struct trackpoint_data *tp = psmouse->private; |
| 379 | int error; |
| 380 | bool was_reset; |
Shawn Nematbakhsh | 0c6a616 | 2013-04-15 13:49:34 -0700 | [diff] [blame] | 381 | |
Dmitry Torokhov | 2a924d7 | 2018-01-05 13:28:47 -0800 | [diff] [blame] | 382 | error = trackpoint_start_protocol(psmouse, NULL, NULL); |
| 383 | if (error) |
| 384 | return error; |
Dmitry Torokhov | 184dd27 | 2006-08-04 22:52:26 -0400 | [diff] [blame] | 385 | |
Dmitry Torokhov | 2a924d7 | 2018-01-05 13:28:47 -0800 | [diff] [blame] | 386 | was_reset = tp->variant_id == TP_VARIANT_IBM && |
| 387 | trackpoint_power_on_reset(&psmouse->ps2dev) == 0; |
| 388 | |
| 389 | error = trackpoint_sync(psmouse, was_reset); |
| 390 | if (error) |
| 391 | return error; |
Dmitry Torokhov | 184dd27 | 2006-08-04 22:52:26 -0400 | [diff] [blame] | 392 | |
| 393 | return 0; |
| 394 | } |
| 395 | |
Dmitry Torokhov | b7802c5 | 2009-09-09 19:13:20 -0700 | [diff] [blame] | 396 | int trackpoint_detect(struct psmouse *psmouse, bool set_properties) |
Stephen Evanchik | 541e316 | 2005-08-08 01:26:18 -0500 | [diff] [blame] | 397 | { |
Stephen Evanchik | 541e316 | 2005-08-08 01:26:18 -0500 | [diff] [blame] | 398 | struct ps2dev *ps2dev = &psmouse->ps2dev; |
Dmitry Torokhov | 2a924d7 | 2018-01-05 13:28:47 -0800 | [diff] [blame] | 399 | struct trackpoint_data *tp; |
| 400 | u8 variant_id; |
| 401 | u8 firmware_id; |
| 402 | u8 button_info; |
Jeff Garzik | 8ff22ea | 2006-11-02 23:59:30 -0500 | [diff] [blame] | 403 | int error; |
Stephen Evanchik | 541e316 | 2005-08-08 01:26:18 -0500 | [diff] [blame] | 404 | |
Dmitry Torokhov | 2a924d7 | 2018-01-05 13:28:47 -0800 | [diff] [blame] | 405 | error = trackpoint_start_protocol(psmouse, &variant_id, &firmware_id); |
| 406 | if (error) |
| 407 | return error; |
Stephen Evanchik | 541e316 | 2005-08-08 01:26:18 -0500 | [diff] [blame] | 408 | |
| 409 | if (!set_properties) |
| 410 | return 0; |
| 411 | |
Dmitry Torokhov | 2a924d7 | 2018-01-05 13:28:47 -0800 | [diff] [blame] | 412 | tp = kzalloc(sizeof(*tp), GFP_KERNEL); |
| 413 | if (!tp) |
Davidlohr Bueso | 6792cbb | 2010-09-29 18:53:35 -0700 | [diff] [blame] | 414 | return -ENOMEM; |
Stephen Evanchik | 541e316 | 2005-08-08 01:26:18 -0500 | [diff] [blame] | 415 | |
Dmitry Torokhov | 2a924d7 | 2018-01-05 13:28:47 -0800 | [diff] [blame] | 416 | trackpoint_defaults(tp); |
| 417 | tp->variant_id = variant_id; |
| 418 | tp->firmware_id = firmware_id; |
| 419 | |
| 420 | psmouse->private = tp; |
| 421 | |
| 422 | psmouse->vendor = trackpoint_variants[variant_id]; |
Stephen Evanchik | 541e316 | 2005-08-08 01:26:18 -0500 | [diff] [blame] | 423 | psmouse->name = "TrackPoint"; |
| 424 | |
Dmitry Torokhov | 184dd27 | 2006-08-04 22:52:26 -0400 | [diff] [blame] | 425 | psmouse->reconnect = trackpoint_reconnect; |
Stephen Evanchik | 541e316 | 2005-08-08 01:26:18 -0500 | [diff] [blame] | 426 | psmouse->disconnect = trackpoint_disconnect; |
| 427 | |
Dmitry Torokhov | 2a924d7 | 2018-01-05 13:28:47 -0800 | [diff] [blame] | 428 | if (variant_id != TP_VARIANT_IBM) { |
| 429 | /* Newer variants do not support extended button query. */ |
| 430 | button_info = 0x33; |
| 431 | } else { |
| 432 | error = trackpoint_read(ps2dev, TP_EXT_BTN, &button_info); |
| 433 | if (error) { |
| 434 | psmouse_warn(psmouse, |
| 435 | "failed to get extended button data, assuming 3 buttons\n"); |
| 436 | button_info = 0x33; |
| 437 | } else if (!button_info) { |
| 438 | psmouse_warn(psmouse, |
| 439 | "got 0 in extended button data, assuming 3 buttons\n"); |
| 440 | button_info = 0x33; |
| 441 | } |
| 442 | } |
| 443 | |
Dmitry Torokhov | 315eb99 | 2009-11-16 22:12:21 -0800 | [diff] [blame] | 444 | if ((button_info & 0x0f) >= 3) |
Dmitry Torokhov | 2a924d7 | 2018-01-05 13:28:47 -0800 | [diff] [blame] | 445 | input_set_capability(psmouse->dev, EV_KEY, BTN_MIDDLE); |
Dmitry Torokhov | 315eb99 | 2009-11-16 22:12:21 -0800 | [diff] [blame] | 446 | |
Hans de Goede | 01d4cd5 | 2014-09-08 14:44:05 -0700 | [diff] [blame] | 447 | __set_bit(INPUT_PROP_POINTER, psmouse->dev->propbit); |
Hans de Goede | 7611392 | 2014-09-08 14:42:12 -0700 | [diff] [blame] | 448 | __set_bit(INPUT_PROP_POINTING_STICK, psmouse->dev->propbit); |
| 449 | |
Dmitry Torokhov | 2a924d7 | 2018-01-05 13:28:47 -0800 | [diff] [blame] | 450 | if (variant_id != TP_VARIANT_IBM || |
| 451 | trackpoint_power_on_reset(ps2dev) != 0) { |
| 452 | /* |
| 453 | * Write defaults to TP if we did not reset the trackpoint. |
| 454 | */ |
Shawn Nematbakhsh | 0c6a616 | 2013-04-15 13:49:34 -0700 | [diff] [blame] | 455 | trackpoint_sync(psmouse, false); |
Dmitry Torokhov | 2a924d7 | 2018-01-05 13:28:47 -0800 | [diff] [blame] | 456 | } |
Stephen Evanchik | 541e316 | 2005-08-08 01:26:18 -0500 | [diff] [blame] | 457 | |
Dmitry Torokhov | 2a924d7 | 2018-01-05 13:28:47 -0800 | [diff] [blame] | 458 | error = device_add_group(&ps2dev->serio->dev, &trackpoint_attr_group); |
Jeff Garzik | 8ff22ea | 2006-11-02 23:59:30 -0500 | [diff] [blame] | 459 | if (error) { |
JJ Ding | fb16395 | 2012-04-10 00:25:01 -0700 | [diff] [blame] | 460 | psmouse_err(psmouse, |
| 461 | "failed to create sysfs attributes, error: %d\n", |
| 462 | error); |
Dmitry Torokhov | 315eb99 | 2009-11-16 22:12:21 -0800 | [diff] [blame] | 463 | kfree(psmouse->private); |
| 464 | psmouse->private = NULL; |
Jeff Garzik | 8ff22ea | 2006-11-02 23:59:30 -0500 | [diff] [blame] | 465 | return -1; |
| 466 | } |
Stephen Evanchik | 541e316 | 2005-08-08 01:26:18 -0500 | [diff] [blame] | 467 | |
JJ Ding | fb16395 | 2012-04-10 00:25:01 -0700 | [diff] [blame] | 468 | psmouse_info(psmouse, |
Dmitry Torokhov | 2a924d7 | 2018-01-05 13:28:47 -0800 | [diff] [blame] | 469 | "%s TrackPoint firmware: 0x%02x, buttons: %d/%d\n", |
| 470 | psmouse->vendor, firmware_id, |
JJ Ding | fb16395 | 2012-04-10 00:25:01 -0700 | [diff] [blame] | 471 | (button_info & 0xf0) >> 4, button_info & 0x0f); |
Stephen Evanchik | 541e316 | 2005-08-08 01:26:18 -0500 | [diff] [blame] | 472 | |
| 473 | return 0; |
| 474 | } |
| 475 | |