I just purchased a new computer, and suspend worked fine when I had it in my office connected to my monitor, but when I moved it to my front room and connected it to my TV, it started exhibiting this same behavior of waking immediately after it went into suspend mode.
I traced the problem down to CEC (Consumer Electronics Control) signaling between the TV and the computer over the HDMI cable. I did a lot of searching, and found a definitive and permanent solution.
Linux has a file /proc/acpi/wakeup that sets the state of devices that can wake up from the suspended state. You can issue the command:
cat /proc/acpi/wakeup
and this will display the different devices that are either enabled or disabled to execute wake from suspend mode. Here’s a partial example:
Device S-state Status Sysfs node
PEG1 S4 *disabled
PEGP S4 *disabled
PEG2 S4 *disabled
PEGP S4 *disabled
PEG3 S4 *disabled
PEGP S4 *disabled
PEG0 S4 *enabled pci:0000:00:06.0
PEGP S4 *disabled pci:0000:01:00.0
SIO1 S3 *disabled pnp:00:00
RP09 S4 *disabled pci:0000:00:1d.0
PXSX S4 *enabled pci:0000:64:00.0
RP10 S4 *disabled
RP11 S4 *disabled
RP12 S4 *disabled
RP13 S4 *disabled
[etc.]
The first column is the device identifier, and these are set by the manufacturer, and might or might not follow APCI protocol. https://unix.stackexchange.com/questions/236127/acpi-wakeup-4-letters-code-meaning
What the device identifiers refer to is only minimally important. You can get a quick list of those that are enabled by issuing the command:
cat /proc/acpi/wakeup | grep enabled
You can toggle between enabled and disabled state for a device by echoing the device identifier to the file (with root privileges). For instance you can issue the command
sudo sh -c “echo PEG0 > /proc/acpi/wakeup”
and the device PEG0 will be disabled. Issue the command again, and it will be enabled again.
My method was to write a script to disable the enabled devices one by one. Each time I disabled one, I went into suspend mode to see what happened, if it were to reawaken immediately or stay suspended. Once I disabled a device (one which didn’t solve the problem), I left it disabled and moved onto the next one, trying each one by one. In my script file, I just commented out each one as I tested it so it wouldn’t be re-enabled again. Once I found that I had achieved the proper suspend state and it stayed suspended, I went through the previous devices I had disabled and re-enabled them one by one, then going into suspend mode each time to verify it was still working correctly. If I found that suspend reverted to it’s previous mannerism of waking immediately, I disabled that device again and moved on to the next one. I found that there were two devices that had originally been enabled that I had to leave disabled for my suspend state to work correctly.
Now comes the big part. That was all just testing. Linux maintains a directory of files to execute when it goes into suspend state. This directory is
/usr/lib/systemd/system-sleep
Files in this directory have the owner:group of root:root and permissions 755.
Each file will be called in sequence and passed two parameters. The first parameter is the text either “pre” or “post,” depending on whether it is going into suspend mode or waking from it, respectively. The second parameter identifies the suspend state and can be one of “suspend” “hibernate” “hybrid-sleep” or “suspend-then-hibernate.” We are interested in the first parameter only, and only when we are going into suspend mode. The second parameter doesn’t matter – we want the system behavior on waking to be the same regardless.
We want to use this method of disabling a device from waking immediately before going into suspend mode, rather than on system startup, because the system might change the parameters in this wakeup file at some time during operations. This method ensures that the device(s) of interest will be in the proper state on suspend. So we need to create a script file and copy it to this directory. I call mine “disable-some-wake.” The format of this file is:
#! /bin/bash
case $1 in
pre)
declare -a devices=(RP09 PXSX) # <-- Add your entries here
for device in "${devices[@]}"; do
if $(grep -qw ^${device}.*enabled /proc/acpi/wakeup); then
echo ${device} > /proc/acpi/wakeup
fi
done
;;
esac
In this file, we’re only working with the case where we are “pre”-suspend mode. We’ve created an array of devices we want to be disabled, and loop through that array of devices. For each device, we look in the wakeup file to test if it is enabled, and if so, toggle it to be disabled.
Note that the list of devices that works for me might be different than what works for you. Use whatever worked for you in testing. You also can disable additional devices other than those impacted by CEC signals if so desired.
Now copy the file:
sudo cp disable-some-wake /usr/lib/systemd/system-sleep
and change the permissions:
sudo chmod 755 /usr/lib/systemd/system-sleep/disable-some-wake
That’s it. You should be good for all eternity.