Touchpad not reliably working after hibernate

I’m using a Dell Latitude 5550 and have a weird situation where whenever the system wakes from hibernation/hybrid-sleep, the touchpad is no longer detected. Or when I use the touchpad, my cursor goes off the screen and just jumps around randomly. This system is a new install, so I can’t really track it down to any specific update. What I can say is in my config I have set mem_sleep_default=deep in my kernel params, and for systemd.sleep.settings.SleepI have the following defined: hibernateDelaySec = 30; and suspendState = “mem”;. To try to work around the issue, I have a SystemD service in place to try to unload and reload i2c_hid after a sleep, hybrid-sleep, or hibernate.

    services.reload-touchpad = {
      description = "Reload touchpad module after sleep";
      after = [
        "suspend.target"
        "hybrid-sleep.target"
        "hibernate.target"
      ];
      wantedBy = [
        "suspend.target"
        "hybrid-sleep.target"
        "hibernate.target"
      ];
      serviceConfig.Type = "simple";
      script = ''
        ${pkgs.kmod}/bin/modprobe --remove --remove-holders --force i2c_hid
        ${pkgs.kmod}/bin/modprobe i2c_hid
      '';
    };

The problem is this doesn’t seem to be doing anything.

What do you mean it doesn’t seem to do anything? Like the service isn’t running? Or reloading the module doesn’t do anything? Reloading a module is not a catchall solution to hardware problems, so it’s not hugely surprising if that’s not the solution to this problem.


FWIW, that service definition should be fine, because ordering after targets like suspend.target should work (unlike ordering after specifically sleep.target, because that’s technically before the sleep state is entered). But the systemd.special(7) manpage recommends ordering a service before sleep.target for pre-sleep operations, and adding StopWhenUnneeded=yes and ExecStop= for post-wake operations.

    services.reload-touchpad = {
      description = "Reload touchpad module after sleep";
      wantedBy = [ "sleep.target" ];
      before = [ "sleep.target" ];
      unitConfig.StopWhenUnneeded = true;
      serviceConfig.Type = "oneshot";
      serviceConfig.RemainAfterExit = true;
      preStop = ''
        ${pkgs.kmod}/bin/modprobe --remove --remove-holders --force i2c_hid
        ${pkgs.kmod}/bin/modprobe i2c_hid
      '';
    };

Type=oneshot means the ExecStart= (which we don’t have here) is the pre-sleep logic, and StopWhenUnneeded means it automatically runs the ExecStop= (the preStop here) when the service is no longer in the dependency chain of the service manager. sleep.target is ideal here because it works for all the different sleep states, and this StopWhenUnneeded method fixes the problem with sleep.target coming before the sleep state is actually entered.

Anyway, I don’t think this is your problem, just thought I’d pass it along.

Some trackpads might use different kernel drivers like

rmi_smbus
i2c_hid_acpi
psmouse

Could we get a dmesg if this still didn’t work?

( your trackpad might even be usb, which needs a usb specific driver to be reloaded ).

Yeah, sorry that was vague. It appears as if the service is running, but reloading the i2c_hid module doesn’t seem to have any effect, as the touchpad still either doesn’t work or causes the cursor to jump around erratically. I know reloading the module won’t magically resolve the issue, but I do hope that it will give me a good workaround for now.

The changes you suggested doesn’t seem to be valid though, after doing a rebuild SystemD now refuses to load the service:


vendion on aegir nix on  master [!] via ❄️  impure (nix-shell-env) took 1m4s
➜ systemctl status reload-touchpad.service
○ reload-touchpad.service - Reload touchpad module after sleep
Loaded: bad-setting (Reason: Unit reload-touchpad.service has a bad unit file setting.)
Active: inactive (dead)


Apr 07 07:19:59 aegir systemd[1]: reload-touchpad.service: Service has no ExecStart= and no SuccessAction= settings and does not have RemainAfterExit=yes set. Refusing.
```

oh right, yes, forgot to add serviceConfig.RemainAfterExit = true; (edited the previous post)