root/drivers/input/mouse/trackpoint.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. trackpoint_power_on_reset
  2. trackpoint_read
  3. trackpoint_write
  4. trackpoint_toggle_bit
  5. trackpoint_update_bit
  6. trackpoint_show_int_attr
  7. trackpoint_set_int_attr
  8. trackpoint_set_bit_attr
  9. trackpoint_is_attr_available
  10. trackpoint_is_attr_visible
  11. trackpoint_start_protocol
  12. trackpoint_sync
  13. trackpoint_defaults
  14. trackpoint_disconnect
  15. trackpoint_reconnect
  16. trackpoint_detect

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

/* [<][>][^][v][top][bottom][index][help] */