Debian & Thinkpad Ultradocks

udev eventing and xrandr make this easy.

Posted by Ian Maxon on November 7, 2018

One of the best features Thinkpads have, in my opinion, is their docking station support. Being able to just set the laptop down in the dock, and having all your desk peripherals work with no cable hunting is great.

However, this does not happen out of the box on Linux. The foundations for making the dock work as intended exist, but that is all. To make it work, all we need is a bit of glue. There’s a lot of resources out there related to this that I had to alter and cobble together to make this work in my scenario, the main method I am using is similar to what’s described at Thinkwiki

The first task is to make a udev rule for a device, such that when it is added or removed, a sscript is run to take action based on that. This can be the dock device itself or something attached to it. In my case, I use a device attached to it, because I have 2 docks, one at home and the other at work.

sudo udevadm monitor --environment --udev
calling: monitor
monitor will print the received events for:
UDEV - the event which udev sends out after rule processing

Now if you dock or undock, you will see a lot of output similar to this

UDEV  [94579.321714] add      /devices/pci0000:00/0000:00:14.0/usb1/1-9/1-9.1 (usb)
ACTION=add
BUSNUM=001
DEVNAME=/dev/bus/usb/001/029
DEVNUM=029
DEVPATH=/devices/pci0000:00/0000:00:14.0/usb1/1-9/1-9.1
DEVTYPE=usb_device
DRIVER=usb
ID_BUS=usb
ID_MODEL=USB_Modi_Device
ID_MODEL_ENC=USB\x20Modi\x20Device
ID_MODEL_ID=1319
ID_REVISION=0105
ID_SERIAL=Schiit_Audio_USB_Modi_Device
ID_USB_INTERFACES=:010100:010200:030000:
ID_VENDOR=Schiit_Audio
ID_VENDOR_ENC=Schiit\x20Audio
ID_VENDOR_FROM_DATABASE=C-Media Electronics, Inc.
ID_VENDOR_ID=0d8c
MAJOR=189
MINOR=28
PRODUCT=d8c/1319/105
SEQNUM=5240
SUBSYSTEM=usb
TYPE=0/0/0
USEC_INITIALIZED=94574293726

We can use this info to formulate the udev rule. in /etc/udev/rules.d make a file with a name like 81-thinkpad-dock.rules. The number in front is the priority the rule takes, the name can be something else.

        SUBSYSTEMS=="usb", ACTION=="add|remove", ENV{ID_VENDOR_ID}=="0d8c", RUN+="/usr/sbin/work-dock > /var/log/messages"

Note the ENV{ID_VENDOR_ID} field. It is from the udevadm output earlier. The rule is simply to run a script, /usr/sbin/work-dock when a device matching that vendor ID is added or removed. Since this device is attached to the dock, it will acomplish the goal of determining whether or not the laptop is docked.

The script is somewhat dependent on how the displays on the dock are set up. In this scenario, there are 2 monitors attached to the dock (DP-2-1 and DP-2-2). eDP-1 is the integrated display.

#!/bin/sh
export DISPLAY=:0
export XAUTHORITY=/home/parshimers/.Xauthority

# wait for the dock state to change
set -x
sleep 5
xmodmap /home/parshimers/.Xmodmap

(xrandr -d :0.0 -q --current | grep " connected" | grep -q "DP-2-2")
DOCKED=$?
case "$DOCKED" in
   "1")
      #undocked event - lets remove all connected outputs apart from eDP-1
      for output in $(/usr/bin/xrandr -d :0.0 | grep -v "eDP-1" | awk '{print $1}')
      do
         /usr/bin/xrandr -d :0.0 --output $output --off
      done
      /usr/bin/xrandr -d :0.0 --output eDP-1 --auto
      ;;
   "0")
      #docked event - setup dual monitor
      /usr/bin/xrandr -d :0.0 --output DP-2-2 --rotate left --auto --output DP-2-1 --rotate left --right-of DP-2-2 --auto --output eDP-1 --auto --right-of DP-2-1
      ;;
esac
exit 0

More or less, this script uses the output of xrandr to determine if DP-2-2 is connected. If it is, the laptop has just been docked. If it isn’t, then the laptop has been undocked. For the former case, the action is to enable the two displays attached to the dock and rotate them. In the latter, the outputs are turned off and the laptop display reset to it’s native resolution. The only case that seems to be buggy for this, is to try turning of the laptop display when docked. Something about this does not play well with displayport on intel graphics. YMMV.