18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Stephen Evanchik <evanchsa@gmail.com> 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Trademarks are the property of their respective owners. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/slab.h> 98c2ecf20Sopenharmony_ci#include <linux/delay.h> 108c2ecf20Sopenharmony_ci#include <linux/serio.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/input.h> 138c2ecf20Sopenharmony_ci#include <linux/libps2.h> 148c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 158c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 168c2ecf20Sopenharmony_ci#include "psmouse.h" 178c2ecf20Sopenharmony_ci#include "trackpoint.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistatic const char * const trackpoint_variants[] = { 208c2ecf20Sopenharmony_ci [TP_VARIANT_IBM] = "IBM", 218c2ecf20Sopenharmony_ci [TP_VARIANT_ALPS] = "ALPS", 228c2ecf20Sopenharmony_ci [TP_VARIANT_ELAN] = "Elan", 238c2ecf20Sopenharmony_ci [TP_VARIANT_NXP] = "NXP", 248c2ecf20Sopenharmony_ci [TP_VARIANT_JYT_SYNAPTICS] = "JYT_Synaptics", 258c2ecf20Sopenharmony_ci [TP_VARIANT_SYNAPTICS] = "Synaptics", 268c2ecf20Sopenharmony_ci}; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* 298c2ecf20Sopenharmony_ci * Power-on Reset: Resets all trackpoint parameters, including RAM values, 308c2ecf20Sopenharmony_ci * to defaults. 318c2ecf20Sopenharmony_ci * Returns zero on success, non-zero on failure. 328c2ecf20Sopenharmony_ci */ 338c2ecf20Sopenharmony_cistatic int trackpoint_power_on_reset(struct ps2dev *ps2dev) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci u8 param[2] = { TP_POR }; 368c2ecf20Sopenharmony_ci int err; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci err = ps2_command(ps2dev, param, MAKE_PS2_CMD(1, 2, TP_COMMAND)); 398c2ecf20Sopenharmony_ci if (err) 408c2ecf20Sopenharmony_ci return err; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci /* Check for success response -- 0xAA00 */ 438c2ecf20Sopenharmony_ci if (param[0] != 0xAA || param[1] != 0x00) 448c2ecf20Sopenharmony_ci return -ENODEV; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci return 0; 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci/* 508c2ecf20Sopenharmony_ci * Device IO: read, write and toggle bit 518c2ecf20Sopenharmony_ci */ 528c2ecf20Sopenharmony_cistatic int trackpoint_read(struct ps2dev *ps2dev, u8 loc, u8 *results) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci results[0] = loc; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci return ps2_command(ps2dev, results, MAKE_PS2_CMD(1, 1, TP_COMMAND)); 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic int trackpoint_write(struct ps2dev *ps2dev, u8 loc, u8 val) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci u8 param[3] = { TP_WRITE_MEM, loc, val }; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci return ps2_command(ps2dev, param, MAKE_PS2_CMD(3, 0, TP_COMMAND)); 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic int trackpoint_toggle_bit(struct ps2dev *ps2dev, u8 loc, u8 mask) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci u8 param[3] = { TP_TOGGLE, loc, mask }; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci /* Bad things will happen if the loc param isn't in this range */ 718c2ecf20Sopenharmony_ci if (loc < 0x20 || loc >= 0x2F) 728c2ecf20Sopenharmony_ci return -EINVAL; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci return ps2_command(ps2dev, param, MAKE_PS2_CMD(3, 0, TP_COMMAND)); 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic int trackpoint_update_bit(struct ps2dev *ps2dev, 788c2ecf20Sopenharmony_ci u8 loc, u8 mask, u8 value) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci int retval; 818c2ecf20Sopenharmony_ci u8 data; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci retval = trackpoint_read(ps2dev, loc, &data); 848c2ecf20Sopenharmony_ci if (retval) 858c2ecf20Sopenharmony_ci return retval; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci if (((data & mask) == mask) != !!value) 888c2ecf20Sopenharmony_ci retval = trackpoint_toggle_bit(ps2dev, loc, mask); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci return retval; 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci/* 948c2ecf20Sopenharmony_ci * Trackpoint-specific attributes 958c2ecf20Sopenharmony_ci */ 968c2ecf20Sopenharmony_cistruct trackpoint_attr_data { 978c2ecf20Sopenharmony_ci size_t field_offset; 988c2ecf20Sopenharmony_ci u8 command; 998c2ecf20Sopenharmony_ci u8 mask; 1008c2ecf20Sopenharmony_ci bool inverted; 1018c2ecf20Sopenharmony_ci u8 power_on_default; 1028c2ecf20Sopenharmony_ci}; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic ssize_t trackpoint_show_int_attr(struct psmouse *psmouse, 1058c2ecf20Sopenharmony_ci void *data, char *buf) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci struct trackpoint_data *tp = psmouse->private; 1088c2ecf20Sopenharmony_ci struct trackpoint_attr_data *attr = data; 1098c2ecf20Sopenharmony_ci u8 value = *(u8 *)((void *)tp + attr->field_offset); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci if (attr->inverted) 1128c2ecf20Sopenharmony_ci value = !value; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", value); 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic ssize_t trackpoint_set_int_attr(struct psmouse *psmouse, void *data, 1188c2ecf20Sopenharmony_ci const char *buf, size_t count) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci struct trackpoint_data *tp = psmouse->private; 1218c2ecf20Sopenharmony_ci struct trackpoint_attr_data *attr = data; 1228c2ecf20Sopenharmony_ci u8 *field = (void *)tp + attr->field_offset; 1238c2ecf20Sopenharmony_ci u8 value; 1248c2ecf20Sopenharmony_ci int err; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci err = kstrtou8(buf, 10, &value); 1278c2ecf20Sopenharmony_ci if (err) 1288c2ecf20Sopenharmony_ci return err; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci *field = value; 1318c2ecf20Sopenharmony_ci err = trackpoint_write(&psmouse->ps2dev, attr->command, value); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci return err ?: count; 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci#define TRACKPOINT_INT_ATTR(_name, _command, _default) \ 1378c2ecf20Sopenharmony_ci static struct trackpoint_attr_data trackpoint_attr_##_name = { \ 1388c2ecf20Sopenharmony_ci .field_offset = offsetof(struct trackpoint_data, _name), \ 1398c2ecf20Sopenharmony_ci .command = _command, \ 1408c2ecf20Sopenharmony_ci .power_on_default = _default, \ 1418c2ecf20Sopenharmony_ci }; \ 1428c2ecf20Sopenharmony_ci PSMOUSE_DEFINE_ATTR(_name, S_IWUSR | S_IRUGO, \ 1438c2ecf20Sopenharmony_ci &trackpoint_attr_##_name, \ 1448c2ecf20Sopenharmony_ci trackpoint_show_int_attr, trackpoint_set_int_attr) 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic ssize_t trackpoint_set_bit_attr(struct psmouse *psmouse, void *data, 1478c2ecf20Sopenharmony_ci const char *buf, size_t count) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci struct trackpoint_data *tp = psmouse->private; 1508c2ecf20Sopenharmony_ci struct trackpoint_attr_data *attr = data; 1518c2ecf20Sopenharmony_ci bool *field = (void *)tp + attr->field_offset; 1528c2ecf20Sopenharmony_ci bool value; 1538c2ecf20Sopenharmony_ci int err; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci err = kstrtobool(buf, &value); 1568c2ecf20Sopenharmony_ci if (err) 1578c2ecf20Sopenharmony_ci return err; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci if (attr->inverted) 1608c2ecf20Sopenharmony_ci value = !value; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci if (*field != value) { 1638c2ecf20Sopenharmony_ci *field = value; 1648c2ecf20Sopenharmony_ci err = trackpoint_toggle_bit(&psmouse->ps2dev, 1658c2ecf20Sopenharmony_ci attr->command, attr->mask); 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci return err ?: count; 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci#define TRACKPOINT_BIT_ATTR(_name, _command, _mask, _inv, _default) \ 1738c2ecf20Sopenharmony_cistatic struct trackpoint_attr_data trackpoint_attr_##_name = { \ 1748c2ecf20Sopenharmony_ci .field_offset = offsetof(struct trackpoint_data, \ 1758c2ecf20Sopenharmony_ci _name), \ 1768c2ecf20Sopenharmony_ci .command = _command, \ 1778c2ecf20Sopenharmony_ci .mask = _mask, \ 1788c2ecf20Sopenharmony_ci .inverted = _inv, \ 1798c2ecf20Sopenharmony_ci .power_on_default = _default, \ 1808c2ecf20Sopenharmony_ci }; \ 1818c2ecf20Sopenharmony_ciPSMOUSE_DEFINE_ATTR(_name, S_IWUSR | S_IRUGO, \ 1828c2ecf20Sopenharmony_ci &trackpoint_attr_##_name, \ 1838c2ecf20Sopenharmony_ci trackpoint_show_int_attr, trackpoint_set_bit_attr) 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ciTRACKPOINT_INT_ATTR(sensitivity, TP_SENS, TP_DEF_SENS); 1868c2ecf20Sopenharmony_ciTRACKPOINT_INT_ATTR(speed, TP_SPEED, TP_DEF_SPEED); 1878c2ecf20Sopenharmony_ciTRACKPOINT_INT_ATTR(inertia, TP_INERTIA, TP_DEF_INERTIA); 1888c2ecf20Sopenharmony_ciTRACKPOINT_INT_ATTR(reach, TP_REACH, TP_DEF_REACH); 1898c2ecf20Sopenharmony_ciTRACKPOINT_INT_ATTR(draghys, TP_DRAGHYS, TP_DEF_DRAGHYS); 1908c2ecf20Sopenharmony_ciTRACKPOINT_INT_ATTR(mindrag, TP_MINDRAG, TP_DEF_MINDRAG); 1918c2ecf20Sopenharmony_ciTRACKPOINT_INT_ATTR(thresh, TP_THRESH, TP_DEF_THRESH); 1928c2ecf20Sopenharmony_ciTRACKPOINT_INT_ATTR(upthresh, TP_UP_THRESH, TP_DEF_UP_THRESH); 1938c2ecf20Sopenharmony_ciTRACKPOINT_INT_ATTR(ztime, TP_Z_TIME, TP_DEF_Z_TIME); 1948c2ecf20Sopenharmony_ciTRACKPOINT_INT_ATTR(jenks, TP_JENKS_CURV, TP_DEF_JENKS_CURV); 1958c2ecf20Sopenharmony_ciTRACKPOINT_INT_ATTR(drift_time, TP_DRIFT_TIME, TP_DEF_DRIFT_TIME); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ciTRACKPOINT_BIT_ATTR(press_to_select, TP_TOGGLE_PTSON, TP_MASK_PTSON, false, 1988c2ecf20Sopenharmony_ci TP_DEF_PTSON); 1998c2ecf20Sopenharmony_ciTRACKPOINT_BIT_ATTR(skipback, TP_TOGGLE_SKIPBACK, TP_MASK_SKIPBACK, false, 2008c2ecf20Sopenharmony_ci TP_DEF_SKIPBACK); 2018c2ecf20Sopenharmony_ciTRACKPOINT_BIT_ATTR(ext_dev, TP_TOGGLE_EXT_DEV, TP_MASK_EXT_DEV, true, 2028c2ecf20Sopenharmony_ci TP_DEF_EXT_DEV); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic bool trackpoint_is_attr_available(struct psmouse *psmouse, 2058c2ecf20Sopenharmony_ci struct attribute *attr) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci struct trackpoint_data *tp = psmouse->private; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci return tp->variant_id == TP_VARIANT_IBM || 2108c2ecf20Sopenharmony_ci attr == &psmouse_attr_sensitivity.dattr.attr || 2118c2ecf20Sopenharmony_ci attr == &psmouse_attr_press_to_select.dattr.attr; 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_cistatic umode_t trackpoint_is_attr_visible(struct kobject *kobj, 2158c2ecf20Sopenharmony_ci struct attribute *attr, int n) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci struct device *dev = container_of(kobj, struct device, kobj); 2188c2ecf20Sopenharmony_ci struct serio *serio = to_serio_port(dev); 2198c2ecf20Sopenharmony_ci struct psmouse *psmouse = serio_get_drvdata(serio); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci return trackpoint_is_attr_available(psmouse, attr) ? attr->mode : 0; 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic struct attribute *trackpoint_attrs[] = { 2258c2ecf20Sopenharmony_ci &psmouse_attr_sensitivity.dattr.attr, 2268c2ecf20Sopenharmony_ci &psmouse_attr_speed.dattr.attr, 2278c2ecf20Sopenharmony_ci &psmouse_attr_inertia.dattr.attr, 2288c2ecf20Sopenharmony_ci &psmouse_attr_reach.dattr.attr, 2298c2ecf20Sopenharmony_ci &psmouse_attr_draghys.dattr.attr, 2308c2ecf20Sopenharmony_ci &psmouse_attr_mindrag.dattr.attr, 2318c2ecf20Sopenharmony_ci &psmouse_attr_thresh.dattr.attr, 2328c2ecf20Sopenharmony_ci &psmouse_attr_upthresh.dattr.attr, 2338c2ecf20Sopenharmony_ci &psmouse_attr_ztime.dattr.attr, 2348c2ecf20Sopenharmony_ci &psmouse_attr_jenks.dattr.attr, 2358c2ecf20Sopenharmony_ci &psmouse_attr_drift_time.dattr.attr, 2368c2ecf20Sopenharmony_ci &psmouse_attr_press_to_select.dattr.attr, 2378c2ecf20Sopenharmony_ci &psmouse_attr_skipback.dattr.attr, 2388c2ecf20Sopenharmony_ci &psmouse_attr_ext_dev.dattr.attr, 2398c2ecf20Sopenharmony_ci NULL 2408c2ecf20Sopenharmony_ci}; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic struct attribute_group trackpoint_attr_group = { 2438c2ecf20Sopenharmony_ci .is_visible = trackpoint_is_attr_visible, 2448c2ecf20Sopenharmony_ci .attrs = trackpoint_attrs, 2458c2ecf20Sopenharmony_ci}; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci#define TRACKPOINT_UPDATE(_power_on, _psmouse, _tp, _name) \ 2488c2ecf20Sopenharmony_cido { \ 2498c2ecf20Sopenharmony_ci struct trackpoint_attr_data *_attr = &trackpoint_attr_##_name; \ 2508c2ecf20Sopenharmony_ci \ 2518c2ecf20Sopenharmony_ci if ((!_power_on || _tp->_name != _attr->power_on_default) && \ 2528c2ecf20Sopenharmony_ci trackpoint_is_attr_available(_psmouse, \ 2538c2ecf20Sopenharmony_ci &psmouse_attr_##_name.dattr.attr)) { \ 2548c2ecf20Sopenharmony_ci if (!_attr->mask) \ 2558c2ecf20Sopenharmony_ci trackpoint_write(&_psmouse->ps2dev, \ 2568c2ecf20Sopenharmony_ci _attr->command, _tp->_name); \ 2578c2ecf20Sopenharmony_ci else \ 2588c2ecf20Sopenharmony_ci trackpoint_update_bit(&_psmouse->ps2dev, \ 2598c2ecf20Sopenharmony_ci _attr->command, _attr->mask, \ 2608c2ecf20Sopenharmony_ci _tp->_name); \ 2618c2ecf20Sopenharmony_ci } \ 2628c2ecf20Sopenharmony_ci} while (0) 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci#define TRACKPOINT_SET_POWER_ON_DEFAULT(_tp, _name) \ 2658c2ecf20Sopenharmony_cido { \ 2668c2ecf20Sopenharmony_ci _tp->_name = trackpoint_attr_##_name.power_on_default; \ 2678c2ecf20Sopenharmony_ci} while (0) 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistatic int trackpoint_start_protocol(struct psmouse *psmouse, 2708c2ecf20Sopenharmony_ci u8 *variant_id, u8 *firmware_id) 2718c2ecf20Sopenharmony_ci{ 2728c2ecf20Sopenharmony_ci u8 param[2] = { 0 }; 2738c2ecf20Sopenharmony_ci int error; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci error = ps2_command(&psmouse->ps2dev, 2768c2ecf20Sopenharmony_ci param, MAKE_PS2_CMD(0, 2, TP_READ_ID)); 2778c2ecf20Sopenharmony_ci if (error) 2788c2ecf20Sopenharmony_ci return error; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci switch (param[0]) { 2818c2ecf20Sopenharmony_ci case TP_VARIANT_IBM: 2828c2ecf20Sopenharmony_ci case TP_VARIANT_ALPS: 2838c2ecf20Sopenharmony_ci case TP_VARIANT_ELAN: 2848c2ecf20Sopenharmony_ci case TP_VARIANT_NXP: 2858c2ecf20Sopenharmony_ci case TP_VARIANT_JYT_SYNAPTICS: 2868c2ecf20Sopenharmony_ci case TP_VARIANT_SYNAPTICS: 2878c2ecf20Sopenharmony_ci if (variant_id) 2888c2ecf20Sopenharmony_ci *variant_id = param[0]; 2898c2ecf20Sopenharmony_ci if (firmware_id) 2908c2ecf20Sopenharmony_ci *firmware_id = param[1]; 2918c2ecf20Sopenharmony_ci return 0; 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci return -ENODEV; 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci/* 2988c2ecf20Sopenharmony_ci * Write parameters to trackpad. 2998c2ecf20Sopenharmony_ci * in_power_on_state: Set to true if TP is in default / power-on state (ex. if 3008c2ecf20Sopenharmony_ci * power-on reset was run). If so, values will only be 3018c2ecf20Sopenharmony_ci * written to TP if they differ from power-on default. 3028c2ecf20Sopenharmony_ci */ 3038c2ecf20Sopenharmony_cistatic int trackpoint_sync(struct psmouse *psmouse, bool in_power_on_state) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci struct trackpoint_data *tp = psmouse->private; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci if (!in_power_on_state && tp->variant_id == TP_VARIANT_IBM) { 3088c2ecf20Sopenharmony_ci /* 3098c2ecf20Sopenharmony_ci * Disable features that may make device unusable 3108c2ecf20Sopenharmony_ci * with this driver. 3118c2ecf20Sopenharmony_ci */ 3128c2ecf20Sopenharmony_ci trackpoint_update_bit(&psmouse->ps2dev, TP_TOGGLE_TWOHAND, 3138c2ecf20Sopenharmony_ci TP_MASK_TWOHAND, TP_DEF_TWOHAND); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci trackpoint_update_bit(&psmouse->ps2dev, TP_TOGGLE_SOURCE_TAG, 3168c2ecf20Sopenharmony_ci TP_MASK_SOURCE_TAG, TP_DEF_SOURCE_TAG); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci trackpoint_update_bit(&psmouse->ps2dev, TP_TOGGLE_MB, 3198c2ecf20Sopenharmony_ci TP_MASK_MB, TP_DEF_MB); 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci /* 3238c2ecf20Sopenharmony_ci * These properties can be changed in this driver. Only 3248c2ecf20Sopenharmony_ci * configure them if the values are non-default or if the TP is in 3258c2ecf20Sopenharmony_ci * an unknown state. 3268c2ecf20Sopenharmony_ci */ 3278c2ecf20Sopenharmony_ci TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, sensitivity); 3288c2ecf20Sopenharmony_ci TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, inertia); 3298c2ecf20Sopenharmony_ci TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, speed); 3308c2ecf20Sopenharmony_ci TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, reach); 3318c2ecf20Sopenharmony_ci TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, draghys); 3328c2ecf20Sopenharmony_ci TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, mindrag); 3338c2ecf20Sopenharmony_ci TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, thresh); 3348c2ecf20Sopenharmony_ci TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, upthresh); 3358c2ecf20Sopenharmony_ci TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, ztime); 3368c2ecf20Sopenharmony_ci TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, jenks); 3378c2ecf20Sopenharmony_ci TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, drift_time); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci /* toggles */ 3408c2ecf20Sopenharmony_ci TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, press_to_select); 3418c2ecf20Sopenharmony_ci TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, skipback); 3428c2ecf20Sopenharmony_ci TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, ext_dev); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci return 0; 3458c2ecf20Sopenharmony_ci} 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_cistatic void trackpoint_defaults(struct trackpoint_data *tp) 3488c2ecf20Sopenharmony_ci{ 3498c2ecf20Sopenharmony_ci TRACKPOINT_SET_POWER_ON_DEFAULT(tp, sensitivity); 3508c2ecf20Sopenharmony_ci TRACKPOINT_SET_POWER_ON_DEFAULT(tp, speed); 3518c2ecf20Sopenharmony_ci TRACKPOINT_SET_POWER_ON_DEFAULT(tp, reach); 3528c2ecf20Sopenharmony_ci TRACKPOINT_SET_POWER_ON_DEFAULT(tp, draghys); 3538c2ecf20Sopenharmony_ci TRACKPOINT_SET_POWER_ON_DEFAULT(tp, mindrag); 3548c2ecf20Sopenharmony_ci TRACKPOINT_SET_POWER_ON_DEFAULT(tp, thresh); 3558c2ecf20Sopenharmony_ci TRACKPOINT_SET_POWER_ON_DEFAULT(tp, upthresh); 3568c2ecf20Sopenharmony_ci TRACKPOINT_SET_POWER_ON_DEFAULT(tp, ztime); 3578c2ecf20Sopenharmony_ci TRACKPOINT_SET_POWER_ON_DEFAULT(tp, jenks); 3588c2ecf20Sopenharmony_ci TRACKPOINT_SET_POWER_ON_DEFAULT(tp, drift_time); 3598c2ecf20Sopenharmony_ci TRACKPOINT_SET_POWER_ON_DEFAULT(tp, inertia); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci /* toggles */ 3628c2ecf20Sopenharmony_ci TRACKPOINT_SET_POWER_ON_DEFAULT(tp, press_to_select); 3638c2ecf20Sopenharmony_ci TRACKPOINT_SET_POWER_ON_DEFAULT(tp, skipback); 3648c2ecf20Sopenharmony_ci TRACKPOINT_SET_POWER_ON_DEFAULT(tp, ext_dev); 3658c2ecf20Sopenharmony_ci} 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_cistatic void trackpoint_disconnect(struct psmouse *psmouse) 3688c2ecf20Sopenharmony_ci{ 3698c2ecf20Sopenharmony_ci device_remove_group(&psmouse->ps2dev.serio->dev, 3708c2ecf20Sopenharmony_ci &trackpoint_attr_group); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci kfree(psmouse->private); 3738c2ecf20Sopenharmony_ci psmouse->private = NULL; 3748c2ecf20Sopenharmony_ci} 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_cistatic int trackpoint_reconnect(struct psmouse *psmouse) 3778c2ecf20Sopenharmony_ci{ 3788c2ecf20Sopenharmony_ci struct trackpoint_data *tp = psmouse->private; 3798c2ecf20Sopenharmony_ci int error; 3808c2ecf20Sopenharmony_ci bool was_reset; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci error = trackpoint_start_protocol(psmouse, NULL, NULL); 3838c2ecf20Sopenharmony_ci if (error) 3848c2ecf20Sopenharmony_ci return error; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci was_reset = tp->variant_id == TP_VARIANT_IBM && 3878c2ecf20Sopenharmony_ci trackpoint_power_on_reset(&psmouse->ps2dev) == 0; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci error = trackpoint_sync(psmouse, was_reset); 3908c2ecf20Sopenharmony_ci if (error) 3918c2ecf20Sopenharmony_ci return error; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci return 0; 3948c2ecf20Sopenharmony_ci} 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ciint trackpoint_detect(struct psmouse *psmouse, bool set_properties) 3978c2ecf20Sopenharmony_ci{ 3988c2ecf20Sopenharmony_ci struct ps2dev *ps2dev = &psmouse->ps2dev; 3998c2ecf20Sopenharmony_ci struct trackpoint_data *tp; 4008c2ecf20Sopenharmony_ci u8 variant_id; 4018c2ecf20Sopenharmony_ci u8 firmware_id; 4028c2ecf20Sopenharmony_ci u8 button_info; 4038c2ecf20Sopenharmony_ci int error; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci error = trackpoint_start_protocol(psmouse, &variant_id, &firmware_id); 4068c2ecf20Sopenharmony_ci if (error) 4078c2ecf20Sopenharmony_ci return error; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci if (!set_properties) 4108c2ecf20Sopenharmony_ci return 0; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci tp = kzalloc(sizeof(*tp), GFP_KERNEL); 4138c2ecf20Sopenharmony_ci if (!tp) 4148c2ecf20Sopenharmony_ci return -ENOMEM; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci trackpoint_defaults(tp); 4178c2ecf20Sopenharmony_ci tp->variant_id = variant_id; 4188c2ecf20Sopenharmony_ci tp->firmware_id = firmware_id; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci psmouse->private = tp; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci psmouse->vendor = trackpoint_variants[variant_id]; 4238c2ecf20Sopenharmony_ci psmouse->name = "TrackPoint"; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci psmouse->reconnect = trackpoint_reconnect; 4268c2ecf20Sopenharmony_ci psmouse->disconnect = trackpoint_disconnect; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci if (variant_id != TP_VARIANT_IBM) { 4298c2ecf20Sopenharmony_ci /* Newer variants do not support extended button query. */ 4308c2ecf20Sopenharmony_ci button_info = 0x33; 4318c2ecf20Sopenharmony_ci } else { 4328c2ecf20Sopenharmony_ci error = trackpoint_read(ps2dev, TP_EXT_BTN, &button_info); 4338c2ecf20Sopenharmony_ci if (error) { 4348c2ecf20Sopenharmony_ci psmouse_warn(psmouse, 4358c2ecf20Sopenharmony_ci "failed to get extended button data, assuming 3 buttons\n"); 4368c2ecf20Sopenharmony_ci button_info = 0x33; 4378c2ecf20Sopenharmony_ci } else if (!button_info) { 4388c2ecf20Sopenharmony_ci psmouse_warn(psmouse, 4398c2ecf20Sopenharmony_ci "got 0 in extended button data, assuming 3 buttons\n"); 4408c2ecf20Sopenharmony_ci button_info = 0x33; 4418c2ecf20Sopenharmony_ci } 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci if ((button_info & 0x0f) >= 3) 4458c2ecf20Sopenharmony_ci input_set_capability(psmouse->dev, EV_KEY, BTN_MIDDLE); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci __set_bit(INPUT_PROP_POINTER, psmouse->dev->propbit); 4488c2ecf20Sopenharmony_ci __set_bit(INPUT_PROP_POINTING_STICK, psmouse->dev->propbit); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci if (variant_id != TP_VARIANT_IBM || 4518c2ecf20Sopenharmony_ci trackpoint_power_on_reset(ps2dev) != 0) { 4528c2ecf20Sopenharmony_ci /* 4538c2ecf20Sopenharmony_ci * Write defaults to TP if we did not reset the trackpoint. 4548c2ecf20Sopenharmony_ci */ 4558c2ecf20Sopenharmony_ci trackpoint_sync(psmouse, false); 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci error = device_add_group(&ps2dev->serio->dev, &trackpoint_attr_group); 4598c2ecf20Sopenharmony_ci if (error) { 4608c2ecf20Sopenharmony_ci psmouse_err(psmouse, 4618c2ecf20Sopenharmony_ci "failed to create sysfs attributes, error: %d\n", 4628c2ecf20Sopenharmony_ci error); 4638c2ecf20Sopenharmony_ci kfree(psmouse->private); 4648c2ecf20Sopenharmony_ci psmouse->private = NULL; 4658c2ecf20Sopenharmony_ci return -1; 4668c2ecf20Sopenharmony_ci } 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci psmouse_info(psmouse, 4698c2ecf20Sopenharmony_ci "%s TrackPoint firmware: 0x%02x, buttons: %d/%d\n", 4708c2ecf20Sopenharmony_ci psmouse->vendor, firmware_id, 4718c2ecf20Sopenharmony_ci (button_info & 0xf0) >> 4, button_info & 0x0f); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci return 0; 4748c2ecf20Sopenharmony_ci} 4758c2ecf20Sopenharmony_ci 476