IBM and Lenovo Trackpoints on Linux

Updates

(2022/03/31) The systemd init script below can throw errors or get stuck because sysfs devices can do crazy things during suspend-resume. It is safer to write /usr/local/sbin/tpd.py as a notifying daemon. Note that it must issue both notify("READY=1") and notify("WATCHDOG=1") commands. If it does not notify systemd every so often, the systemd unit file /etc/systemd/system/trackpoint.service kills it and spawns a new instance. References: [1, 2, 3, 4].

(2022/03/01) As time passes, Lenovo makes old IBM Thinkpad features worse and worse. USB Trackpoint keyboards were hit-or-miss; some exposed sensitivity, speed and inertia through sysfs, some did not. Pressure to velocity mapping behavior has been getting to the point of unusability. The bluetooth/2.4GHz "Lenovo Trackpoint Keyboard II" is completely opaque to sysfs. But there are enterprising souls who wireshark USB and write planet-saving scripts to make Lenovo's non-trackpoint tolerable!

(2020/07/23) It is tempting to use sysfsutils to write values to sysfs files on boot, but it may not survive suspend-resume. Coupled with the extremely unreliable behavior of writes to Lenovo Trackpoint sysfs files, I still like my daemon that keeps checking sysfs values, although it gets wedged once out of some 20 suspend-resumes, requiring an additional suspend-resume.

Whatever works!

As of 2019, Lenovo Trackpoints are dodgy on Linux. A udev rule may be triggered with a device path like /sys/devices/platform/i8042/serio1/serio224/sensitivity, but an attempt to read or write the path may inexplicably fail. While most Lenovo Trackpoints behave OK after a reboot, they often default back to sensitivity=200 and speed=97. They also do this after a suspend-resume cycle. Here are some proposals that do not work reliably: one-time thaw/resume hander, using systemd. After many other failed attempts to fix this in a clean manner, I have just written a systemd demon. In /usr/local/sbin/tpd.py:

#! /usr/bin/python

import syslog, time, glob

def read_int(attr_path):
    try:
        with open(attr_path, "rb") as attr_file:
            return int(attr_file.read())
    except IOError as iox:
        syslog.syslog("TRACKPOINT {} {}".format(attr_path, iox))
        return -1

def write_int(attr_path, val):
    try:
        with open(attr_path, "wb") as attr_file:
            attr_file.write(str(val))
    except IOError as iox:
        syslog.syslog("TRACKPOINT {} {}".format(attr_path, iox))

def check_write_int(attr_path, val):
    source = read_int(attr_path)
    if source != target:
        write_int(attr_path, target)
        msg = "TRACKPOINT {} {} {}".format(attr_path, source, target)
        syslog.syslog(msg)

base_glob = "/sys/devices/platform/i8042/serio1/serio*/"
sense_glob = base_glob + "sensitivity"
speed_glob = base_glob + "speed"
target = 253

while True:
    time.sleep(30)
    for sense_path in glob.glob(sense_glob):
        check_write_int(sense_path, target)
    for speed_path in glob.glob(speed_glob):
        check_write_int(speed_path, target)
And in /etc/systemd/system/trackpoint.service:
[Unit]
Description=Trackpoint Service

[Service]
Type=simple
User=root
WorkingDirectory=/tmp
ExecStart=/usr/local/sbin/tpd.py
Restart=on-failure # or always, on-abort, etc

[Install]
WantedBy=multi-user.target

Using udev

To customize Trackpoint sensitivity from udev in Linux, follow these instructions. Unfortunately, with each passing year, Lenovo Thinkpads are becoming more and more unfriendly to the Linux Trackpoint driver. The T460 frequently freezes the Trackpoint on resume from suspend (4.9.0 kernel) until the psmouse module is reloaded or the machine suspended and resumed a couple times.

Using systemd and bash script

Debian testing, ca. Sept. 2017, with systemd. If your Lenovo Trackpoint is losing sensitivity on suspend-resume, create these two files /lib/systemd/system/trackpoint-resume.service

[Unit]
Description=Trackpoint resume actions
After=suspend.target

[Service]
Type=oneshot
ExecStart=/usr/local/sbin/trackpoint.bash

[Install]
WantedBy=suspend.target
and /usr/local/sbin/trackpoint.bash needs the following code to find the current device. As of 2017, the psmouse driver on many Lenovo laptops find device paths of the form /sys/devices/platform/i8042/serio1/serio224/sensitivity, where the `224' keeps clocking up with every unloading and loading of the module.
#!/bin/bash
wsens=254
find /sys/devices/platform/i8042 -name sensitivity -print0 | \
  while IFS= read -r -d $'\0' line; do
    while :
    do
      rsens=`cat "$line"`
        if [ "$rsens" -eq "$wsens" ]; then
          break
        fi
      echo -n "$wsens" > "$line"
      echo "$line $rsens"
    done
  done
Remember to chmod +x /usr/local/sbin/trackpoint.bash and systemctl enable root-resume.service. You can update speed the same way as sensitivity. If the inifinite loop above gets in trouble with Lenovo's sketchy Trackpoint implementation, limit to some number of loop iterations and then bail out. H/t AskUbuntu.

Early patches

I contributed a humble patch to the famous tp4utils Linux Trackpoint drivers by Till Straumann. As of 2007, this is very dated; on Ubuntu Feisty Fawn 7.04 with a stock 2.6.20-15 kernel, it is adequate to add this line:

echo -n 255 > /sys/devices/platform/i8042/serio1/sensitivity
to your /etc/rc.local. (Choose your comfort zone between roughly 100 and 255.)