PkgBlog: acpid

Talk to the insane "power management" firmware on your computer
Package series/name: a/acpid-2.0.26-x86_64-1
Official release: source and package
Blog entry created: 2018-07-16
Tagged: series_a core-system hardware

Advanced Configuration and Power Interface (ACPI) is the firmware interface to the computer’s power management hardware. It’s what allows the OS to tell the PC to turn off and what allows the button on the front of the PC to tell the OS to go to sleep.

As you might expect, there are 4 different power states that go from a "Working" CPU to "Mechanical Off" wherein the computer is completely disconnected from the power source.

As you are probably also aware, there are also into different types of "sleeping" which range from suspending just the CPU to writing the current state of main memory to a disk and powering off.

ACPI is also used to handle events for hardware buttons such as screen brightness, volume control, and even camera and SD card readers. So it’s grown quite a bit beyond just the "power" aspect.

The way ACPI works seems completely bonkers to an outsider like myself: ACPI has its own machine language, AML, which is a bytecode stored in firmware "tables". To make use of it, the operating system has to have an AML interpreter built into it that runs whatever arbitrary AML the BIOS gives it.

So, you heard that right: the Linux kernel has to have an intepreter for a specialty language just to tell the damn computer to go to sleep! I don’t get it either.

It gets even worse. The ACPI code can even make a query to find out what OS you’re running. The idea here, apparently, is to patch bugs in the OS. Yeah, you can just imagine how wonderfully that works!

At any rate, the guts of Linux’s ACPI capabilities are obviously in the kernel, so this acpid package is the user-space daemon and interface to that functionality.

Accordingly, it’s a small package with just three binaries and some man pages.

The acpid daemon

The daemon itself is a binary sitting in /usr/sbin/acpid. It is started by the Slackware script /etc/rc.d/rc.acpid.

By the way, I was just now wondering what the "rc" in "rc.*" stands for in SysV-style init scripts. This superuser answer points us to the Jargon File entry for "rc file" in which we are told that "rc" is short for runcom files (as in "run commands") and that the naming dates back to the early 1960s. How’s that for some history?

The man page for acpid tells us that the daemon looks for a configuration directory at /etc/acpi/events. So if we take a look at that directory, sure enough, there is a single event/action configuration file there called default and it’s pretty short:

	cat /etc/acpi/events/default
	# This is the ACPID default configuration, it takes all
	# events and passes them to /etc/acpi/default.sh for further
	# processing.

	# ...<more comments snipped>...

	event=.*
	action=/etc/acpi/acpi_handler.sh %e

Ah, so we can clearly see that all events (matched with event=.*) are handled by /etc/acpi/acpi_handler.sh, so let’s look at that:

	cat /etc/acpi/acpi_handler.sh
	#!/bin/sh
	# Default acpi script that takes an entry for all actions

	IFS=${IFS}/
	set $@

	case "$1" in
	  button)
		case "$2" in
		  power) /sbin/init 0
			 ;;
		  *) logger "ACPI action $2 is not defined"
			 ;;
		esac
		;;
	  *)
		logger "ACPI group $1 / action $2 is not defined"
		;;
	esac

So it turns out that script’s quite short as well. In fact, it’s delightfully short - we seem to be matching "button" followed by "power" and calling /sbin/init 0 in response. Anything else is getting logged!

For something so beastly complex in the interaction between BIOS and the Linux kernel, the user-space stuff seems to be very manageable.

acpi_listen

The package also comes with a really simple command line utility to listen for events called acpi_listen.

The man page explains it very nicely.

My desktop computer has exactly one ACPI-enabled button: the power switch. So to test this utility properly, I turned on my Asus eeePC 701 palmtop, which has some function keys.

Sure enough, I was able to capture this log as I pressed the Fn key plus all of the labeled top row keys:

$ acpi_listen
hotkey ASUS010:00 0000002f 00000001
video/brightnessup BRTUP 00000086 00000000 K
hotkey ASUS010:00 0000002e 00000001
video/brightnessdown BRTDN 00000087 00000000 K
hotkey ASUS010:00 00000010 00000001
button/wlan WLAN 00000080 00000000 K
button/sleep SBTN 00000080 00000000
button/sleep PNP0C0E:00 00000080 00000001
hotkey ASUS010:00 00000030 00000000
video/switchmode VMOD 00000080 00000000 K
hotkey ASUS010:00 00000012 00000000
button/prog1 PROG1 00000080 00000000 K
hotkey ASUS010:00 00000013 00000000
button/mute MUTE 00000080 00000000 K
hotkey ASUS010:00 00000014 00000000
button/volumedown VOLDN 00000080 00000000 K
hotkey ASUS010:00 00000015 00000000
button/volumeup VOLUP 00000080 00000000 K

So I could, if I wanted, write a script to listen for these hardware buttons and attach any action I like! That’s cool.

kacpimon

The third binary is strictly a hard-core ACPI debugging tool.

The man page explains that to use it, you must first stop the acpid daemon. Again, I did this on my eeePC:

$ sudo /etc/rc.d/rc.acpid stop
$ sudo /usr/sbin/kacpimon
Kernel ACPI Event Monitor...
open for /proc/acpi/event: No such file or directory (2)
  (ACPI proc filesystem may not be present)
/dev/input/event0 (Speakup) opened successfully
/dev/input/event1 (AT Translated Set 2 keyboard) opened successfully
/dev/input/event10 (SynPS/2 Synaptics TouchPad) opened successfully
/dev/input/event11 (UVC Camera (eb1a:2761)) opened successfully
/dev/input/event2 (Lid Switch) opened successfully
/dev/input/event3 (Sleep Button) opened successfully
/dev/input/event4 (Power Button) opened successfully
/dev/input/event5 (Power Button) opened successfully
/dev/input/event6 (Video Bus) opened successfully
/dev/input/event7 (Asus EeePC extra buttons) opened successfully
/dev/input/event8 (HDA Intel Mic) opened successfully
/dev/input/event9 (HDA Intel Headphone) opened successfully
Netlink ACPI Family ID: 19
Netlink ACPI Multicast Group ID: 2
netlink opened successfully
Press Escape to exit, or Ctrl-C if that doesn't work.
Input Layer:  Type: 4  Code: 4  Value: 28
Input Layer:  Type: 1  Code: 28  Value: 0
Input Layer:  Sync
Input Layer:  Type: 4  Code: 4  Value: 16
netlink: hotkey ASUS010:00 00000010 00000002
Input Layer:  Type: 1  Code: 238  Value: 1
Input Layer:  Sync
Input Layer:  Type: 1  Code: 238  Value: 0
Input Layer:  Sync
netlink: hotkey ASUS010:00 0000002d 00000000
Input Layer:  Type: 4  Code: 4  Value: 32
Input Layer:  Type: 1  Code: 224  Value: 1
Input Layer:  Sync
Input Layer:  Type: 1  Code: 224  Value: 0
Input Layer:  Sync
Input Layer:  Type: 4  Code: 4  Value: 33
Input Layer:  Type: 1  Code: 33  Value: 1
Input Layer:  Sync
Input Layer:  Type: 4  Code: 4  Value: 33
Input Layer:  Type: 1  Code: 33  Value: 0
Input Layer:  Sync
Input Layer:  Type: 4  Code: 4  Value: 1
Escape key pressed
Input Layer:  Type: 1  Code: 1  Value: 1
Closing files...
Goodbye

As you can see, it captured the events (I pressed a couple of the function key combinations) and it also captured every key stroke I made!

By the way, the man page also points that you have to press the Esc key rather than stopping kacpimon with Ctrl+c in order to capture STDOUT to a file (which I was doing for this blog entry).

If nothing else, it was certainly interesting to see the listing of the available hardware events for the eeePC such as the lid switch and headphone jack!

Conclusion

I was not surprised to find out that the BIOS portion of ACPI is a vile, over-engineered mess. We all know that getting laptop hardware 100% working on Linux systems has been difficult for a long time and that the problem isn’t Linux.

But I was surprised at how accessible the acpid daemon and tools are! My hat’s off to ACPI Daemon v2 maintainer Ted Felix!