Path: blob/master/drivers/input/mouse/trackpoint.c
15109 views
/*1* Stephen Evanchik <[email protected]>2*3* This program is free software; you can redistribute it and/or modify it4* under the terms of the GNU General Public License version 2 as published by5* the Free Software Foundation.6*7* Trademarks are the property of their respective owners.8*/910#include <linux/slab.h>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"2021/*22* Device IO: read, write and toggle bit23*/24static int trackpoint_read(struct ps2dev *ps2dev, unsigned char loc, unsigned char *results)25{26if (ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_COMMAND)) ||27ps2_command(ps2dev, results, MAKE_PS2_CMD(0, 1, loc))) {28return -1;29}3031return 0;32}3334static int trackpoint_write(struct ps2dev *ps2dev, unsigned char loc, unsigned char val)35{36if (ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_COMMAND)) ||37ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_WRITE_MEM)) ||38ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, loc)) ||39ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, val))) {40return -1;41}4243return 0;44}4546static 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 */49if (loc < 0x20 || loc >= 0x2F)50return -1;5152if (ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_COMMAND)) ||53ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_TOGGLE)) ||54ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, loc)) ||55ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, mask))) {56return -1;57}5859return 0;60}616263/*64* Trackpoint-specific attributes65*/66struct trackpoint_attr_data {67size_t field_offset;68unsigned char command;69unsigned char mask;70unsigned char inverted;71};7273static ssize_t trackpoint_show_int_attr(struct psmouse *psmouse, void *data, char *buf)74{75struct trackpoint_data *tp = psmouse->private;76struct trackpoint_attr_data *attr = data;77unsigned char value = *(unsigned char *)((char *)tp + attr->field_offset);7879if (attr->inverted)80value = !value;8182return sprintf(buf, "%u\n", value);83}8485static ssize_t trackpoint_set_int_attr(struct psmouse *psmouse, void *data,86const char *buf, size_t count)87{88struct trackpoint_data *tp = psmouse->private;89struct trackpoint_attr_data *attr = data;90unsigned char *field = (unsigned char *)((char *)tp + attr->field_offset);91unsigned long value;9293if (strict_strtoul(buf, 10, &value) || value > 255)94return -EINVAL;9596*field = value;97trackpoint_write(&psmouse->ps2dev, attr->command, value);9899return count;100}101102#define TRACKPOINT_INT_ATTR(_name, _command) \103static struct trackpoint_attr_data trackpoint_attr_##_name = { \104.field_offset = offsetof(struct trackpoint_data, _name), \105.command = _command, \106}; \107PSMOUSE_DEFINE_ATTR(_name, S_IWUSR | S_IRUGO, \108&trackpoint_attr_##_name, \109trackpoint_show_int_attr, trackpoint_set_int_attr)110111static ssize_t trackpoint_set_bit_attr(struct psmouse *psmouse, void *data,112const char *buf, size_t count)113{114struct trackpoint_data *tp = psmouse->private;115struct trackpoint_attr_data *attr = data;116unsigned char *field = (unsigned char *)((char *)tp + attr->field_offset);117unsigned long value;118119if (strict_strtoul(buf, 10, &value) || value > 1)120return -EINVAL;121122if (attr->inverted)123value = !value;124125if (*field != value) {126*field = value;127trackpoint_toggle_bit(&psmouse->ps2dev, attr->command, attr->mask);128}129130return count;131}132133134#define TRACKPOINT_BIT_ATTR(_name, _command, _mask, _inv) \135static 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}; \141PSMOUSE_DEFINE_ATTR(_name, S_IWUSR | S_IRUGO, \142&trackpoint_attr_##_name, \143trackpoint_show_int_attr, trackpoint_set_bit_attr)144145TRACKPOINT_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);155156TRACKPOINT_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);159160static 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,174NULL175};176177static struct attribute_group trackpoint_attr_group = {178.attrs = trackpoint_attrs,179};180181static int trackpoint_start_protocol(struct psmouse *psmouse, unsigned char *firmware_id)182{183unsigned char param[2] = { 0 };184185if (ps2_command(&psmouse->ps2dev, param, MAKE_PS2_CMD(0, 2, TP_READ_ID)))186return -1;187188if (param[0] != TP_MAGIC_IDENT)189return -1;190191if (firmware_id)192*firmware_id = param[1];193194return 0;195}196197static int trackpoint_sync(struct psmouse *psmouse)198{199struct trackpoint_data *tp = psmouse->private;200unsigned char toggle;201202/* Disable features that may make device unusable with this driver */203trackpoint_read(&psmouse->ps2dev, TP_TOGGLE_TWOHAND, &toggle);204if (toggle & TP_MASK_TWOHAND)205trackpoint_toggle_bit(&psmouse->ps2dev, TP_TOGGLE_TWOHAND, TP_MASK_TWOHAND);206207trackpoint_read(&psmouse->ps2dev, TP_TOGGLE_SOURCE_TAG, &toggle);208if (toggle & TP_MASK_SOURCE_TAG)209trackpoint_toggle_bit(&psmouse->ps2dev, TP_TOGGLE_SOURCE_TAG, TP_MASK_SOURCE_TAG);210211trackpoint_read(&psmouse->ps2dev, TP_TOGGLE_MB, &toggle);212if (toggle & TP_MASK_MB)213trackpoint_toggle_bit(&psmouse->ps2dev, TP_TOGGLE_MB, TP_MASK_MB);214215/* Push the config to the device */216trackpoint_write(&psmouse->ps2dev, TP_SENS, tp->sensitivity);217trackpoint_write(&psmouse->ps2dev, TP_INERTIA, tp->inertia);218trackpoint_write(&psmouse->ps2dev, TP_SPEED, tp->speed);219220trackpoint_write(&psmouse->ps2dev, TP_REACH, tp->reach);221trackpoint_write(&psmouse->ps2dev, TP_DRAGHYS, tp->draghys);222trackpoint_write(&psmouse->ps2dev, TP_MINDRAG, tp->mindrag);223224trackpoint_write(&psmouse->ps2dev, TP_THRESH, tp->thresh);225trackpoint_write(&psmouse->ps2dev, TP_UP_THRESH, tp->upthresh);226227trackpoint_write(&psmouse->ps2dev, TP_Z_TIME, tp->ztime);228trackpoint_write(&psmouse->ps2dev, TP_JENKS_CURV, tp->jenks);229230trackpoint_read(&psmouse->ps2dev, TP_TOGGLE_PTSON, &toggle);231if (((toggle & TP_MASK_PTSON) == TP_MASK_PTSON) != tp->press_to_select)232trackpoint_toggle_bit(&psmouse->ps2dev, TP_TOGGLE_PTSON, TP_MASK_PTSON);233234trackpoint_read(&psmouse->ps2dev, TP_TOGGLE_SKIPBACK, &toggle);235if (((toggle & TP_MASK_SKIPBACK) == TP_MASK_SKIPBACK) != tp->skipback)236trackpoint_toggle_bit(&psmouse->ps2dev, TP_TOGGLE_SKIPBACK, TP_MASK_SKIPBACK);237238trackpoint_read(&psmouse->ps2dev, TP_TOGGLE_EXT_DEV, &toggle);239if (((toggle & TP_MASK_EXT_DEV) == TP_MASK_EXT_DEV) != tp->ext_dev)240trackpoint_toggle_bit(&psmouse->ps2dev, TP_TOGGLE_EXT_DEV, TP_MASK_EXT_DEV);241242return 0;243}244245static void trackpoint_defaults(struct trackpoint_data *tp)246{247tp->press_to_select = TP_DEF_PTSON;248tp->sensitivity = TP_DEF_SENS;249tp->speed = TP_DEF_SPEED;250tp->reach = TP_DEF_REACH;251252tp->draghys = TP_DEF_DRAGHYS;253tp->mindrag = TP_DEF_MINDRAG;254255tp->thresh = TP_DEF_THRESH;256tp->upthresh = TP_DEF_UP_THRESH;257258tp->ztime = TP_DEF_Z_TIME;259tp->jenks = TP_DEF_JENKS_CURV;260261tp->inertia = TP_DEF_INERTIA;262tp->skipback = TP_DEF_SKIPBACK;263tp->ext_dev = TP_DEF_EXT_DEV;264}265266static void trackpoint_disconnect(struct psmouse *psmouse)267{268sysfs_remove_group(&psmouse->ps2dev.serio->dev.kobj, &trackpoint_attr_group);269270kfree(psmouse->private);271psmouse->private = NULL;272}273274static int trackpoint_reconnect(struct psmouse *psmouse)275{276if (trackpoint_start_protocol(psmouse, NULL))277return -1;278279if (trackpoint_sync(psmouse))280return -1;281282return 0;283}284285int trackpoint_detect(struct psmouse *psmouse, bool set_properties)286{287struct ps2dev *ps2dev = &psmouse->ps2dev;288unsigned char firmware_id;289unsigned char button_info;290int error;291292if (trackpoint_start_protocol(psmouse, &firmware_id))293return -1;294295if (!set_properties)296return 0;297298if (trackpoint_read(&psmouse->ps2dev, TP_EXT_BTN, &button_info)) {299printk(KERN_WARNING "trackpoint.c: failed to get extended button data\n");300button_info = 0;301}302303psmouse->private = kzalloc(sizeof(struct trackpoint_data), GFP_KERNEL);304if (!psmouse->private)305return -ENOMEM;306307psmouse->vendor = "IBM";308psmouse->name = "TrackPoint";309310psmouse->reconnect = trackpoint_reconnect;311psmouse->disconnect = trackpoint_disconnect;312313if ((button_info & 0x0f) >= 3)314__set_bit(BTN_MIDDLE, psmouse->dev->keybit);315316trackpoint_defaults(psmouse->private);317trackpoint_sync(psmouse);318319error = sysfs_create_group(&ps2dev->serio->dev.kobj, &trackpoint_attr_group);320if (error) {321printk(KERN_ERR322"trackpoint.c: failed to create sysfs attributes, error: %d\n",323error);324kfree(psmouse->private);325psmouse->private = NULL;326return -1;327}328329printk(KERN_INFO "IBM TrackPoint firmware: 0x%02x, buttons: %d/%d\n",330firmware_id, (button_info & 0xf0) >> 4, button_info & 0x0f);331332return 0;333}334335336337