Setup a USB-C Alt Mode dock on linux
Introduction
I've recently bought an ASUS Zephyrus M to replace my ZenBook Pro as laptop & my Z820 in the second room, so the idea is to use a docking to instantly connect my screens/keyboard/mouse/lan, i choosed the Kensington SD4500.
The SD4500 is working great out of the box on Windows, but on linux it's an other story.
The problem is, the built-in screen is linked to the iGPU (through a multiplexer) but the USB-C screens are directly linked to the discrete GPU.
Which means, when the laptop is connected to the dock, the nvidia gpu will be always on.
Environment
The setup is made on Archlinux, xf86-video-intel & nvidia driver with bumblebee, gnome3 on xorg.
Installation
First off all, we need to setup xorg to use intel virtual screens
/etc/X11/xorg.conf.d/20-intel.conf
Section "Device"
Identifier "intelgpu0"
Driver "intel"
Option "VirtualHeads" "2"
EndSection
Replace the content of the bumblebee xorg.conf file with this config
/etc/bumblebee/xorg.conf.nvidia
Section "ServerLayout"
Identifier "Layout0"
Option "AutoAddDevices" "true"
Option "AutoAddGPU" "false"
EndSection
Section "Device"
Identifier "DiscreteNvidia"
Driver "nvidia"
VendorName "NVIDIA Corporation"
Option "ProbeAllGpus" "false"
Option "NoLogo" "true"
Option "AllowEmptyInitialConfiguration"
EndSection
Section "Screen"
Identifier "Screen0"
Device "DiscreteNVidia"
EndSection
At this state, you can try if everything is working as it should be by manually starting intel-virtual-output as a normal user (considering your user is in the bumblebee group).
intel-virtual-output -f
This command will start a second xorg screen :8 on the discrete gpu with bumblebee and redirect the content to the virtual screens. (Correct me if i'm wrong).
If the previous command works great, we need to configure systemd to automatically start the intel-virtual-output service when we connect the dock.
This involve udev & systemd.
On my setup i've created a udev rule to rename the interface from eth0 to dock0 (better management with NetworkManager in the future and avoid false-positive if we use usb-ethernet adapter in portable mode).
/etc/udev/rules.d/99-docking.rules
# Replace 00:00:00:00:00:00 with your current MAC address
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="00:00:00:00:00:00",ATTR{dev_id}=="0x0", ATTR{type}=="1", KERNEL=="eth*", NAME="dock0", SYMLINK="dock0", TAG+="systemd"
Check if the udev rule is working by plugging your dock, the interface should appear as dock0
ip a
...
5: dock0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
...
Check if systemd correctly see the interface
systemctl | grep dock0
sys-subsystem-net-devices-dock0.device loaded active plugged K38230_03
Create the systemd unit files as a normal user
~/.local/share/systemd/user/docked.target
[Unit]
Description=dock to SD4500
Requisite=sys-subsystem-net-devices-dock0.device
BindsTo=sys-subsystem-net-devices-dock0.device
After=sys-subsystem-net-devices-dock0.device
JobTimeoutSec=3
[Install]
WantedBy=sys-subsystem-net-devices-dock0.device
~/.local/share/systemd/user/intel-virtual-output.service
[Unit]
Description=intel-virtual-output for external displays
Requisite=docked.target
After=docked.target
PartOf=docked.target
Conflicts=sleep.target
Before=sleep.target
[Service]
Type=forking
ExecStartPre=/usr/bin/nmcli radio wifi off
ExecStart=/usr/bin/intel-virtual-output -d :0
ExecStopPost=/usr/bin/nmcli radio wifi on
ExecStop=/usr/bin/sudo /usr/local/sbin/killbumblebee.sh
[Install]
WantedBy=docked.target
As you can see in the intel-virtual-output service, i use Pre & Post extra commands to disable the wifi when the dock is connected.
I created a small script in /usr/local/sbin/killbumblebee.sh to kill the remaining xorg screen (:8) after i disconnect the laptop.
/usr/local/sbin/killbumblebee.sh
#!/bin/bash
export DISPLAY=:0
export XAUTHORITY=/run/user/1000/gdm/Xauthority
export XDG_RUNTIME_DIR="/run/user/1000"
export DBUS_SESSION_BUS_ADDRESS="unix:path=${XDG_RUNTIME_DIR}/bus"
# Kill useless xorg server
PIDOFXORG=$(ps aux | grep xorg.conf.nvidia | grep -v grep | awk '{ print $2 }')
kill $PIDOFXORG
# Disconnect virtual screen (to force gnome going back to one screen only)
xrandr --output VIRTUAL1 --off --output VIRTUAL2 --off --output VIRTUAL3 --off --output VIRTUAL4 --off --output VIRTUAL5 --off
As the script need to be run as root, you'll need to allow this command to be run as root without password
/etc/sudoers.d/dock
ALL ALL=(ALL) NOPASSWD: /usr/local/sbin/killbumblebee.sh
Enable the target & service
systemctl --user daemon-reload
systemctl --user enable docked.target
systemctl --user enable intel-virtual-output.service
Disconnect/Reconnect your dock and your external screens should turn on few seconds after.