linux/drivers/input/mouse/trackpoint.c
<<
>>
Prefs
   1/*
   2 * Stephen Evanchik <evanchsa@gmail.com>
   3 *
   4 * This program is free software; you can redistribute it and/or modify it
   5 * under the terms of the GNU General Public License version 2 as published by
   6 * the Free Software Foundation.
   7 *
   8 * Trademarks are the property of their respective owners.
   9 */
  10
  11#include <linux/delay.h>
  12#include <linux/serio.h>
  13#include <linux/module.h>
  14#include <linux/input.h>
  15#include <linux/libps2.h>
  16#include <linux/proc_fs.h>
  17#include <asm/uaccess.h>
  18#include "psmouse.h"
  19#include "trackpoint.h"
  20
  21/*
  22 * Device IO: read, write and toggle bit
  23 */
  24static int trackpoint_read(struct ps2dev *ps2dev, unsigned char loc, unsigned char *results)
  25{
  26        if (ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_COMMAND)) ||
  27            ps2_command(ps2dev, results, MAKE_PS2_CMD(0, 1, loc))) {
  28                return -1;
  29        }
  30
  31        return 0;
  32}
  33
  34static int trackpoint_write(struct ps2dev *ps2dev, unsigned char loc, unsigned char val)
  35{
  36        if (ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_COMMAND)) ||
  37            ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_WRITE_MEM)) ||
  38            ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, loc)) ||
  39            ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, val))) {
  40                return -1;
  41        }
  42
  43        return 0;
  44}
  45
  46static int trackpoint_toggle_bit(struct ps2dev *ps2dev, unsigned char loc, unsigned char mask)
  47{
  48        /* Bad things will happen if the loc param isn't in this range */
  49        if (loc < 0x20 || loc >= 0x2F)
  50                return -1;
  51
  52        if (ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_COMMAND)) ||
  53            ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_TOGGLE)) ||
  54            ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, loc)) ||
  55            ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, mask))) {
  56                return -1;
  57        }
  58
  59        return 0;
  60}
  61
  62
  63/*
  64 * Trackpoint-specific attributes
  65 */
  66struct trackpoint_attr_data {
  67        size_t field_offset;
  68        unsigned char command;
  69        unsigned char mask;
  70        unsigned char inverted;
  71};
  72
  73static ssize_t trackpoint_show_int_attr(struct psmouse *psmouse, void *data, char *buf)
  74{
  75        struct trackpoint_data *tp = psmouse->private;
  76        struct trackpoint_attr_data *attr = data;
  77        unsigned char value = *(unsigned char *)((char *)tp + attr->field_offset);
  78
  79        if (attr->inverted)
  80                value = !value;
  81
  82        return sprintf(buf, "%u\n", value);
  83}
  84
  85static ssize_t trackpoint_set_int_attr(struct psmouse *psmouse, void *data,
  86                                        const char *buf, size_t count)
  87{
  88        struct trackpoint_data *tp = psmouse->private;
  89        struct trackpoint_attr_data *attr = data;
  90        unsigned char *field = (unsigned char *)((char *)tp + attr->field_offset);
  91        unsigned long value;
  92
  93        if (strict_strtoul(buf, 10, &value) || value > 255)
  94                return -EINVAL;
  95
  96        *field = value;
  97        trackpoint_write(&psmouse->ps2dev, attr->command, value);
  98
  99        return count;
 100}
 101
 102#define TRACKPOINT_INT_ATTR(_name, _command)                                    \
 103        static struct trackpoint_attr_data trackpoint_attr_##_name = {          \
 104                .field_offset = offsetof(struct trackpoint_data, _name),        \
 105                .command = _command,                                            \
 106        };                                                                      \
 107        PSMOUSE_DEFINE_ATTR(_name, S_IWUSR | S_IRUGO,                           \
 108                            &trackpoint_attr_##_name,                           \
 109                            trackpoint_show_int_attr, trackpoint_set_int_attr)
 110
 111static ssize_t trackpoint_set_bit_attr(struct psmouse *psmouse, void *data,
 112                                        const char *buf, size_t count)
 113{
 114        struct trackpoint_data *tp = psmouse->private;
 115        struct trackpoint_attr_data *attr = data;
 116        unsigned char *field = (unsigned char *)((char *)tp + attr->field_offset);
 117        unsigned long value;
 118
 119        if (strict_strtoul(buf, 10, &value) || value > 1)
 120                return -EINVAL;
 121
 122        if (attr->inverted)
 123                value = !value;
 124
 125        if (*field != value) {
 126                *field = value;
 127                trackpoint_toggle_bit(&psmouse->ps2dev, attr->command, attr->mask);
 128        }
 129
 130        return count;
 131}
 132
 133
 134#define TRACKPOINT_BIT_ATTR(_name, _command, _mask, _inv)                               \
 135        static struct trackpoint_attr_data trackpoint_attr_##_name = {          \
 136                .field_offset   = offsetof(struct trackpoint_data, _name),      \
 137                .command        = _command,                                     \
 138                .mask           = _mask,                                        \
 139                .inverted       = _inv,                                         \
 140        };                                                                      \
 141        PSMOUSE_DEFINE_ATTR(_name, S_IWUSR | S_IRUGO,                           \
 142                            &trackpoint_attr_##_name,                           \
 143                            trackpoint_show_int_attr, trackpoint_set_bit_attr)
 144
 145TRACKPOINT_INT_ATTR(sensitivity, TP_SENS);
 146TRACKPOINT_INT_ATTR(speed, TP_SPEED);
 147TRACKPOINT_INT_ATTR(inertia, TP_INERTIA);
 148TRACKPOINT_INT_ATTR(reach, TP_REACH);
 149TRACKPOINT_INT_ATTR(draghys, TP_DRAGHYS);
 150TRACKPOINT_INT_ATTR(mindrag, TP_MINDRAG);
 151TRACKPOINT_INT_ATTR(thresh, TP_THRESH);
 152TRACKPOINT_INT_ATTR(upthresh, TP_UP_THRESH);
 153TRACKPOINT_INT_ATTR(ztime, TP_Z_TIME);
 154TRACKPOINT_INT_ATTR(jenks, TP_JENKS_CURV);
 155
 156TRACKPOINT_BIT_ATTR(press_to_select, TP_TOGGLE_PTSON, TP_MASK_PTSON, 0);
 157TRACKPOINT_BIT_ATTR(skipback, TP_TOGGLE_SKIPBACK, TP_MASK_SKIPBACK, 0);
 158TRACKPOINT_BIT_ATTR(ext_dev, TP_TOGGLE_EXT_DEV, TP_MASK_EXT_DEV, 1);
 159
 160static struct attribute *trackpoint_attrs[] = {
 161        &psmouse_attr_sensitivity.dattr.attr,
 162        &psmouse_attr_speed.dattr.attr,
 163        &psmouse_attr_inertia.dattr.attr,
 164        &psmouse_attr_reach.dattr.attr,
 165        &psmouse_attr_draghys.dattr.attr,
 166        &psmouse_attr_mindrag.dattr.attr,
 167        &psmouse_attr_thresh.dattr.attr,
 168        &psmouse_attr_upthresh.dattr.attr,
 169        &psmouse_attr_ztime.dattr.attr,
 170        &psmouse_attr_jenks.dattr.attr,
 171        &psmouse_attr_press_to_select.dattr.attr,
 172        &psmouse_attr_skipback.dattr.attr,
 173        &psmouse_attr_ext_dev.dattr.attr,
 174        NULL
 175};
 176
 177static struct attribute_group trackpoint_attr_group = {
 178        .attrs = trackpoint_attrs,
 179};
 180
 181static int trackpoint_start_protocol(struct psmouse *psmouse, unsigned char *firmware_id)
 182{
 183        unsigned char param[2] = { 0 };
 184
 185        if (ps2_command(&psmouse->ps2dev, param, MAKE_PS2_CMD(0, 2, TP_READ_ID)))
 186                return -1;
 187
 188        if (param[0] != TP_MAGIC_IDENT)
 189                return -1;
 190
 191        if (firmware_id)
 192                *firmware_id = param[1];
 193
 194        return 0;
 195}
 196
 197static int trackpoint_sync(struct psmouse *psmouse)
 198{
 199        struct trackpoint_data *tp = psmouse->private;
 200        unsigned char toggle;
 201
 202        /* Disable features that may make device unusable with this driver */
 203        trackpoint_read(&psmouse->ps2dev, TP_TOGGLE_TWOHAND, &toggle);
 204        if (toggle & TP_MASK_TWOHAND)
 205                trackpoint_toggle_bit(&psmouse->ps2dev, TP_TOGGLE_TWOHAND, TP_MASK_TWOHAND);
 206
 207        trackpoint_read(&psmouse->ps2dev, TP_TOGGLE_SOURCE_TAG, &toggle);
 208        if (toggle & TP_MASK_SOURCE_TAG)
 209                trackpoint_toggle_bit(&psmouse->ps2dev, TP_TOGGLE_SOURCE_TAG, TP_MASK_SOURCE_TAG);
 210
 211        trackpoint_read(&psmouse->ps2dev, TP_TOGGLE_MB, &toggle);
 212        if (toggle & TP_MASK_MB)
 213                trackpoint_toggle_bit(&psmouse->ps2dev, TP_TOGGLE_MB, TP_MASK_MB);
 214
 215        /* Push the config to the device */
 216        trackpoint_write(&psmouse->ps2dev, TP_SENS, tp->sensitivity);
 217        trackpoint_write(&psmouse->ps2dev, TP_INERTIA, tp->inertia);
 218        trackpoint_write(&psmouse->ps2dev, TP_SPEED, tp->speed);
 219
 220        trackpoint_write(&psmouse->ps2dev, TP_REACH, tp->reach);
 221        trackpoint_write(&psmouse->ps2dev, TP_DRAGHYS, tp->draghys);
 222        trackpoint_write(&psmouse->ps2dev, TP_MINDRAG, tp->mindrag);
 223
 224        trackpoint_write(&psmouse->ps2dev, TP_THRESH, tp->thresh);
 225        trackpoint_write(&psmouse->ps2dev, TP_UP_THRESH, tp->upthresh);
 226
 227        trackpoint_write(&psmouse->ps2dev, TP_Z_TIME, tp->ztime);
 228        trackpoint_write(&psmouse->ps2dev, TP_JENKS_CURV, tp->jenks);
 229
 230        trackpoint_read(&psmouse->ps2dev, TP_TOGGLE_PTSON, &toggle);
 231        if (((toggle & TP_MASK_PTSON) == TP_MASK_PTSON) != tp->press_to_select)
 232                 trackpoint_toggle_bit(&psmouse->ps2dev, TP_TOGGLE_PTSON, TP_MASK_PTSON);
 233
 234        trackpoint_read(&psmouse->ps2dev, TP_TOGGLE_SKIPBACK, &toggle);
 235        if (((toggle & TP_MASK_SKIPBACK) == TP_MASK_SKIPBACK) != tp->skipback)
 236                trackpoint_toggle_bit(&psmouse->ps2dev, TP_TOGGLE_SKIPBACK, TP_MASK_SKIPBACK);
 237
 238        trackpoint_read(&psmouse->ps2dev, TP_TOGGLE_EXT_DEV, &toggle);
 239        if (((toggle & TP_MASK_EXT_DEV) == TP_MASK_EXT_DEV) != tp->ext_dev)
 240                trackpoint_toggle_bit(&psmouse->ps2dev, TP_TOGGLE_EXT_DEV, TP_MASK_EXT_DEV);
 241
 242        return 0;
 243}
 244
 245static void trackpoint_defaults(struct trackpoint_data *tp)
 246{
 247        tp->press_to_select = TP_DEF_PTSON;
 248        tp->sensitivity = TP_DEF_SENS;
 249        tp->speed = TP_DEF_SPEED;
 250        tp->reach = TP_DEF_REACH;
 251
 252        tp->draghys = TP_DEF_DRAGHYS;
 253        tp->mindrag = TP_DEF_MINDRAG;
 254
 255        tp->thresh = TP_DEF_THRESH;
 256        tp->upthresh = TP_DEF_UP_THRESH;
 257
 258        tp->ztime = TP_DEF_Z_TIME;
 259        tp->jenks = TP_DEF_JENKS_CURV;
 260
 261        tp->inertia = TP_DEF_INERTIA;
 262        tp->skipback = TP_DEF_SKIPBACK;
 263        tp->ext_dev = TP_DEF_EXT_DEV;
 264}
 265
 266static void trackpoint_disconnect(struct psmouse *psmouse)
 267{
 268        sysfs_remove_group(&psmouse->ps2dev.serio->dev.kobj, &trackpoint_attr_group);
 269
 270        kfree(psmouse->private);
 271        psmouse->private = NULL;
 272}
 273
 274static int trackpoint_reconnect(struct psmouse *psmouse)
 275{
 276        if (trackpoint_start_protocol(psmouse, NULL))
 277                return -1;
 278
 279        if (trackpoint_sync(psmouse))
 280                return -1;
 281
 282        return 0;
 283}
 284
 285int trackpoint_detect(struct psmouse *psmouse, bool set_properties)
 286{
 287        struct trackpoint_data *priv;
 288        struct ps2dev *ps2dev = &psmouse->ps2dev;
 289        unsigned char firmware_id;
 290        unsigned char button_info;
 291        int error;
 292
 293        if (trackpoint_start_protocol(psmouse, &firmware_id))
 294                return -1;
 295
 296        if (!set_properties)
 297                return 0;
 298
 299        if (trackpoint_read(&psmouse->ps2dev, TP_EXT_BTN, &button_info)) {
 300                printk(KERN_WARNING "trackpoint.c: failed to get extended button data\n");
 301                button_info = 0;
 302        }
 303
 304        psmouse->private = priv = kzalloc(sizeof(struct trackpoint_data), GFP_KERNEL);
 305        if (!priv)
 306                return -1;
 307
 308        psmouse->vendor = "IBM";
 309        psmouse->name = "TrackPoint";
 310
 311        psmouse->reconnect = trackpoint_reconnect;
 312        psmouse->disconnect = trackpoint_disconnect;
 313
 314        trackpoint_defaults(priv);
 315        trackpoint_sync(psmouse);
 316
 317        error = sysfs_create_group(&ps2dev->serio->dev.kobj, &trackpoint_attr_group);
 318        if (error) {
 319                printk(KERN_ERR
 320                        "trackpoint.c: failed to create sysfs attributes, error: %d\n",
 321                        error);
 322                kfree(priv);
 323                return -1;
 324        }
 325
 326        printk(KERN_INFO "IBM TrackPoint firmware: 0x%02x, buttons: %d/%d\n",
 327                firmware_id, (button_info & 0xf0) >> 4, button_info & 0x0f);
 328
 329        return 0;
 330}
 331
 332