Suspend on lid closed + battery

If you use FreeBSD 11 on the good ol’ ThinkPad X201, you’ve probably noticed now that suspend and resume work perfectly flawlessly with the latest FreeBSD release.

You probably wish to take advantage of this and suspend your laptop automatically when the lid is closed. Nothing could be easier:

sysctl hw.acpi.lid_switch_state=S3

Save this option /etc/sysctl.conf for it to be permanent.
Close the lid, the laptop goes to sleep, you’re done!
It’s that simple.

But, with this method it will go to sleep each time the lid is closed, completely ignoring whether the laptop is on battery or not. And if you’re like me you probably don’t want this. Instead you want it to suspend itself when the lid is closed and the laptop is on battery. Easy enough! We just have to invoke the mighty power of devd along with a very little shell script.

First we need to tell devd how to react when the lid is closed. Put this in /etc/devd/lid.conf, then restart (service devd restart):

# Notify lid close/open events.
notify 10 {
  match  "system"    "ACPI";
  match  "subsystem" "Lid";
  action "/etc/acpi/lid.sh $notify";
};

Now we will make a script that checks the lid and AC line states and chooses to suspend the laptop when both the lid is closed and AC line is disconnected. This goes in /etc/acpi/lid.sh:

#!/bin/sh

lid="$1" # 0x00 is closed, 0x01 is open (see devd.conf)
acline=$(sysctl -n hw.acpi.acline) # 0 is battery, 1 is online (man acpi)

if [ \( "$lid" = "0x00" \) -a \( "$acline" -eq 0 \) ]
then
  logger "Lid closed on battery. Going to sleep."
  acpiconf -s3
fi

Try it out! Disconnect the AC line, close the lid, it should go to sleep within seconds. However if it doesn’t work you might want to modify the script above a little to check whether lid events are received correctly or not.

Now you may use a modified version of this script to lock xscreensaver when the lid is closed and before the actual suspend. Well OK, I’ve done that for you:

#!/bin/sh
# XScreensaver should be called BEFORE going to sleep to avoid the desktop to be
# shown for a few seconds when the system resumes from sleep.
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin

lid="$1" # 0x00 is closed, 0x01 is open (see devd.conf)
acline=$(sysctl -n hw.acpi.acline) # 0 is battery, 1 is online (man acpi)

lock_display() (
  socket="$1"
  display=$(echo "$socket" | tr "X" ":")

  # Temporary pid file for the watching command
  tpid=$(mktemp)

  # Wait until the display is actually locked.
  (timeout 2s xscreensaver-command -display "$display" -watch & echo $! > $tpid) | (
    # Issue the lock command only when we know that
    # the watching pipe is ready.
    xscreensaver-command -display "$display" -lock

    while read line
    do
      line=$(echo $line | cut -d' ' -f 1)

      if [ "$line" = LOCK ]
      then
        # We have to kill the watching command manually before breaking.
        kill -TERM $(cat $tpid)
        break
      fi
    done
  )

  rm $tpid
)

# The X server may not be running
if [ ! -d /tmp/.X11-unix ]
then
  exit 0
fi

# Lock each available display
for socket in $(ls /tmp/.X11-unix)
do
  # Lock the display
  logger "Locking xscreensaver on $socket"
  lock_display $socket &
done

# Wait until every displays are locked
wait

# Now we can suspend if needed
if [ \( "$lid" = "0x00" \) -a \( "$acline" -eq 0 \) ]
then
  logger "Lid closed on battery. Going to sleep."
  acpiconf -s3
fi

9 thoughts on “Suspend on lid closed + battery

  1. It’s good information to know that someone’s X201 can resume on 11.0.

    Video doesn’t come back up for me on 11.0 or the 11.1 beta. I get the impression that bringing my bios settings closer to default can help, and I’ll see about that…

  2. What’s your sysctl.conf, kldstat, loader.conf? Can post mine if that helps. Also are you running xf86-video-intel? For some time I had xorg fall back to vesa, can’t remember if that caused problems with resume tho. I had no problem with suspend/resume on my two X201 since 11.0 released.

    IIRC some X201 use a Realtek WiFi card instead of the Intel one. So that would be my guess.

  3. Ooh, I didn’t have xf86-video-intel installed… I’d thought X was just slow on FreeBSD. Oops.

    It comes back up from suspend now! (At least as long as I’m in X or have been in X.)

    Thank you! Having suspend+resume makes FreeBSD much more comfortable for me. I’m one of the systemd refugees, trying to get away from ancient LTS Debian.

  4. Hi, I have added the rule for lid to also check power events. My scenario is: machine is on dock (plugged in) and lid closed, should be awake since I’m using external monitor. However, I disconnect from the dock, but lid is still closed, and I want it to go to sleep right away (ie putting it in backpack). This triggers an ACAD event, but there are 2 ‘notify 10’ ACAD rules in /etc/devd.conf already, and the top one (switching power profile) gets called first. I made my notify rule higher priority (20) and called the power profile command from within it, first thing (better change power state before sleeping).
    Thanks for the idea, this is really great.

  5. Hello,

    Thank you for posting these instructions, it works like a charm. I’m using Freebsd 11.1 on a Thinkpad X1 Carbon Gen1.

    Might be worth mentioning I’m using coreboot 4.7 and tianocore payload instead of the stock BIOS

  6. Hey. I’ve set up SDDM login manager and it breaks the scripts.

    1) It will go to S3 even if plugged in. When I run sysctl -n hw.acpi.acline it reports 1, but still goes to sleep.
    2) Half the time when coming out of sleep, the laptop will shutdown.

    This only happens with SDDM enabled. If I turn it off, and startx from shell, then everything works as expected. I’m keeping it off for now until I can find more info about why it causes this.

    • OK, not sure why SDDM is handling this differently, but change the syntax in the if condition fixed the problem. My diff is below:

      6c6
      if [ “$lid” == “0x00” ] && [ “$acline” == 0 ]

      As for shutting down coming out of standby, it happened even with SDDM disabled. So that’s a different issue, maybe related to the fact I’m using Coreboot/Tianocore

Leave a Reply