linux/drivers/input/mouse/trackpoint.c
<<
>>
Prefs
   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
  19static 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 */
  31static 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 */
  50static 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
  57static 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
  64static 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
  75static 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 */
  94struct trackpoint_attr_data {
  95        size_t field_offset;
  96        u8 command;
  97        u8 mask;
  98        bool inverted;
  99        u8 power_on_default;
 100};
 101
 102static 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
 115static 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
 144static 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)     \
 171static 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        };                                                              \
 179PSMOUSE_DEFINE_ATTR(_name, S_IWUSR | S_IRUGO,                           \
 180                    &trackpoint_attr_##_name,                           \
 181                    trackpoint_show_int_attr, trackpoint_set_bit_attr)
 182
 183TRACKPOINT_INT_ATTR(sensitivity, TP_SENS, TP_DEF_SENS);
 184TRACKPOINT_INT_ATTR(speed, TP_SPEED, TP_DEF_SPEED);
 185TRACKPOINT_INT_ATTR(inertia, TP_INERTIA, TP_DEF_INERTIA);
 186TRACKPOINT_INT_ATTR(reach, TP_REACH, TP_DEF_REACH);
 187TRACKPOINT_INT_ATTR(draghys, TP_DRAGHYS, TP_DEF_DRAGHYS);
 188TRACKPOINT_INT_ATTR(mindrag, TP_MINDRAG, TP_DEF_MINDRAG);
 189TRACKPOINT_INT_ATTR(thresh, TP_THRESH, TP_DEF_THRESH);
 190TRACKPOINT_INT_ATTR(upthresh, TP_UP_THRESH, TP_DEF_UP_THRESH);
 191TRACKPOINT_INT_ATTR(ztime, TP_Z_TIME, TP_DEF_Z_TIME);
 192TRACKPOINT_INT_ATTR(jenks, TP_JENKS_CURV, TP_DEF_JENKS_CURV);
 193TRACKPOINT_INT_ATTR(drift_time, TP_DRIFT_TIME, TP_DEF_DRIFT_TIME);
 194
 195TRACKPOINT_BIT_ATTR(press_to_select, TP_TOGGLE_PTSON, TP_MASK_PTSON, false,
 196                    TP_DEF_PTSON);
 197TRACKPOINT_BIT_ATTR(skipback, TP_TOGGLE_SKIPBACK, TP_MASK_SKIPBACK, false,
 198                    TP_DEF_SKIPBACK);
 199TRACKPOINT_BIT_ATTR(ext_dev, TP_TOGGLE_EXT_DEV, TP_MASK_EXT_DEV, true,
 200                    TP_DEF_EXT_DEV);
 201
 202static 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
 212static 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
 222static 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
 240static 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)              \
 246do {                                                                    \
 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)                     \
 263do {                                                                    \
 264        _tp->_name = trackpoint_attr_##_name.power_on_default;          \
 265} while (0)
 266
 267static 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 */
 299static 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
 343static 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
 363static 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
 372static 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
 392int 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
 472