< ACPI

ACPI/ThinkPad-special-buttons

This article is a stub. You can help by expanding it.

Many modern computer keyboards contain special media keys or key combinations to control functions such as monitor brightness, keyboard backlight brightness, volume (sound) level, suspend (sleep mode), and more. Some special keys need additional configuration in order to control what they are intended to control. This additional configuration can be done through ACPI.

It is important to note that most users to not need ACPI to handle the buttons if using a major desktop environment; desktop environments like Gnome, KDE, and Xfce should be capable of handling special buttons. If the desktop environment has not detected special keys automatically, most of the time it can be configured to properly handle the special key events.

This article Article description::describes how to configure ACPI events and actions for the Lenovo ThinkPad T410 laptop.

Preparation

To have a functional ACPI make sure the kernel includes support for THINKPAD_ACPI. The zgrep command can be used to search through the /proc/config.gz file if .config support was built into the previous kernel:

user $zgrep -i thinkpad /proc/config.gz
CONFIG_THINKPAD_ACPI=m
CONFIG_THINKPAD_ACPI_ALSA_SUPPORT=y
# CONFIG_THINKPAD_ACPI_DEBUGFACILITIES is not set
# CONFIG_THINKPAD_ACPI_DEBUG is not set
CONFIG_THINKPAD_ACPI_UNSAFE_LEDS=y
CONFIG_THINKPAD_ACPI_VIDEO=y
CONFIG_THINKPAD_ACPI_HOTKEY_POLL=y

On boot it might be necessary to append the acpi_backlight=vendor kernel command-line option. Do not add this parameter to your kernel unless your ACPI events are not handled properly; one way to test this is by pressing the brightness up/down keys and looking for feedback from the kernel (see acpi_listen below). Most Lenovo ThinkPads should work without this setting and indeed it is encouraged by the kernel documentation (5.9 onwards).

To check what names ACPI uses on a specific button, start acpi_listen and press Fn+special key buttons to see the output from each key:

root #acpi_listen
button/fnf1 FNF1 00000080 00000000 K
button/screenlock SCRNLCK 00000080 00000000 K
^[[36~button/battery BAT 00000080 00000000 K
^[[37~button/sleep SBTN 00000080 00000000 K
Lenovo ThinkPad T410 selected ACPI key events
KeysEvent nameShort nameNumerical partNote
Fn+F1button/fnf1FNF100000080 00000000 K
Fn+F2button/screenlockSCRNLCK00000080 00000000 K
Fn+F3button/batteryBAT00000080 00000000 K
Fn+F4button/sleepSBTN00000080 00000000 K
Fn+F5button/wlanWLAN00000080 00000000 K
Fn+F6(not reported to handler)
Fn+F7video/switchmodeVMOD00000080 00000000 K
Fn+F8(not reported to handler)Touchpad toggle
Fn+F9button/f24F2400000080 00000000 K
Fn+F10(not reported to handler)
Fn+F11button/fnf11FF1100000080 00000000 K
Fn+F12button/suspendSUSP00000080 00000000 K
Fn+Spacebutton/zoomZOOM00000080 00000000 K
Mutebutton/f20F2000000080 00000000 K
VolumeDownbutton/volumedownVOLDN00000080 00000000 K
VolumeUpbotton/volumeupVOLUP00000080 00000000 K
MuteMicbutton/f20F2000000080 00000000 K
ThinkVantagebutton/prog1PROG100000080 00000000 K
Fn+Homevideo/brightnessupBRTUP00000086 00000000
Fn+Endvideo/brightnessdownBRTDN00000087 00000000
Fn+PgUp(not reported to handler)hardware light switch
Fn+PrtSc(not reported to handler)
Fn+ScrLk(not reported to handler)
Fn+Pause(not reported to handler)
Fn+ArrowUpcd/stopCDSTOP00000080 00000000 K
Fn+ArrowLeftcd/prevCDPREV00000080 00000000 K
Fn+ArrowRightcd/nextCDNEXT00000080 00000000 K
Fn+ArrowDowncd/playCDPLAY00000080 00000000 K

Collect the names printed in the file above. They will vary from system to system.

By default, system store key assignment can be found in the /lib/udev/hwdb.d/60-keyboard.hwdb file:

FILE /lib/udev/hwdb.d/60-keyboard.hwdb
...
# ThinkPad Keyboard with TrackPoint
keyboard:usb:v17EFp6009*
 KEYBOARD_KEY_090012=screenlock                         # Fn+F2
 KEYBOARD_KEY_090013=battery                            # Fn+F3
 KEYBOARD_KEY_090014=wlan                               # Fn+F5
 KEYBOARD_KEY_090016=switchvideomode                    # Fn+F7
 KEYBOARD_KEY_090017=f21                                # Fn+F8  touchpad toggle
 KEYBOARD_KEY_090019=suspend                            # Fn+F12
 KEYBOARD_KEY_09001a=brightnessup                       # Fn+Home
 KEYBOARD_KEY_09001b=brightnessdown                     # Fn+End
 KEYBOARD_KEY_09001d=zoom                               # Fn+Space
 KEYBOARD_KEY_090011=prog1                              # ThinkVantage button
 KEYBOARD_KEY_090015=camera                             # Fn+F6 headset/camera VoIP key  ??
 KEYBOARD_KEY_090010=f20                                # Microphone mute button; should be micmute
...

By default some buttons are mapped to key codes that X can not handle or has no keysym for. So the following might be useful. It modifies the udev defaults slightly to fix the key combinations that otherwise would not work in X:

FILE /etc/udev/hwdb.d/thinkpad_keyboard.hwdb
# To debug key presses and access scan code mapping data of 
# an input device use the commonly available tool: evtest(1). 

# A list of possible keycodes is available under 
# https://github.com/torvalds/linux/blob/master/include/uapi/linux/input.h 

### common 
keyboard:name:ThinkPad Extra Buttons:dmi:bvn*:bvr*:bd*:svnLENOVO*:pn* 
 KEYBOARD_KEY_00=prog2          # Fn+F1 
 KEYBOARD_KEY_08=media          # Fn+F9; should be undock, but X has no keysym 
 KEYBOARD_KEY_0a=prog3          # Fn+F11 
 KEYBOARD_KEY_13=search         # Fn+Space; should be zoom, but X can't handle keycode 
# KEYBOARD_KEY_18=              # Fn+1 (might need hotkey unmasking) 
# KEYBOARD_KEY_19=              # Fn+2 (same) 

### X61t display buttons 
keyboard:dmi:bvn*:bvr*:bd*:svnLENOVO*:pnThinkPad*X6*:pvr* 
 KEYBOARD_KEY_67=screenlock     # small unlabelled button 
 KEYBOARD_KEY_6C=f21            # rotate button (f21 = XF86TouchpadToggle) 
 KEYBOARD_KEY_68=config         # rectangle thing button (config = XF86Tools; was:screenlock)

Recipes

Create an event for each button in /etc/acpi/events/, the following example names them by location-function; use whatever naming scheme works best. After creating the events create corresponding actions for each event in /etc/acpi/actions/ See examples below for more information.

Screen lock button

FILE /etc/acpi/events/FnF2-screenlockEvent creation for screen lock button
<syntaxhighlight lang="bash">#Fn+F2 button/screenlock SCRNLCK 00000080 00000000 K
event=button/screenlock
action=/etc/acpi/actions/FnF2-screenlock.sh</syntaxhighlight>
FILE /etc/acpi/actions/FnF2-screenlock.shAction script for screen lock event
<syntaxhighlight lang="bash">#!/bin/sh
logger "[ACPI] Fn+F2 pressed, start Slimlock for logged-in user"
XUSER=$(ps aux | grep xinit | awk '{print $1}' | head -n1)
sudo -u $XUSER /usr/bin/slimlock&</syntaxhighlight>
root #chmod +x /etc/acpi/actions/FnF2-screenlock.sh

Battery button

FILE /etc/acpi/events/FnF3-batteryEvent creation for battery button
<syntaxhighlight lang="bash">#Fn+F3 button/battery BAT 00000080 00000000 K
event=button/battery
action=/etc/acpi/actions/FnF3-battery.sh</syntaxhighlight>
FILE /etc/acpi/actions/FnF3-battery.shAction script for battery button event
<syntaxhighlight lang="bash">#!/bin/sh
# Tried on Gentoo
# cpu throttling is in here it is the cpufreq-set lines
# turning swap off is only for those that feel comfortable
# doing something this nasty.
# comment out the laptop_mode line if it is not installed
# pcfe, 2008-10-28

# spindown time for HD (man hdparm for valid values)
# I prefer 2 hours for acad and 2 min for batt
ACAD_HD=244
BATT_HD=24
                                                                                                                                   
# Power management level
# 255 (off) on AC
# 128 (medium) on batt
# lowered to 32, pcfe, 2004-06-23
# upped to 64, pcfe, 2004-07-14
# upped to 96, pcfe, 2004-10-20
ACAD_PM=255
BATT_PM=96

logger "[ACPI] Fn+F3 pressed to toggle battery state"
# ac/battery event handler 
status=`awk '/^state: / { print $2 }' /proc/acpi/ac_adapter/AC/state` 
if [ "$status" = "off-line" ]
then
	logger "Running /sbin/laptop_mode start"
	/sbin/laptop_mode start
	logger "Setting HD spindown for BATT mode with hdparm -S $BATT_HD /dev/hda."
	/sbin/hdparm -S $BATT_HD /dev/hda > /dev/null 2>&1
	logger "Setting HD powersaving for BATT mode with hdparm -B $BATT_PM /dev/hda."
	/sbin/hdparm -B $BATT_PM /dev/hda > /dev/null 2>&1
	logger "Turning off swap."
	/sbin/swapoff -a
	/usr/bin/cpufreq-set -g conservative         
else   
	logger "Turning on swap."
	/sbin/swapon -a
	logger "Running /sbin/laptop_mode start"
	/sbin/laptop_mode stop
	logger "Setting HD spindown for AC mode with hdparm -S $ACAD_HD /dev/hda."
	/sbin/hdparm -S $ACAD_HD /dev/hda > /dev/null 2>&1
	logger "Setting HD powersaving for AC mode with hdparm -B $ACAD_PM /dev/hda."
	/sbin/hdparm -B $ACAD_PM /dev/hda > /dev/null 2>&1
	/usr/bin/cpufreq-set -g ondemand
fi</syntaxhighlight>

Be sure to make the action script executable:

root #chmod +x /etc/acpi/actions/FnF3-battery.sh

Source: ThinkWiki.org

Sleep button

FILE /etc/acpi/events/FnF4-sleepEvent creation for Sleep button
<syntaxhighlight lang="bash">#Fn+F4 button/sleep SBTN 00000080 00000000 K
event=button/sleep
action=/etc/acpi/actions/FnF4-sleep.sh</syntaxhighlight>
FILE /etc/acpi/actions/FnF4-sleep.shAction script for Sleep button event
<syntaxhighlight lang="bash">#!/bin/sh
logger "[ACPI] Fn+F4 pressed, start Slimlock for current user and suspend to ram"
XUSER=$(ps aux | grep xinit | awk '{print $1}' | head -n1)
sudo -u $XUSER /usr/bin/slimlock&
echo -n mem >/sys/power/state</syntaxhighlight>
root #chmod +x /etc/acpi/actions/FnF4-sleep.sh

Wifi button

FILE /etc/acpi/events/FnF5-wifiEvent creation for wifi button
<syntaxhighlight lang="bash">#Fn+F5 button/wlan WLAN 00000080 00000000 K
event=button/wlan
action=/etc/acpi/actions/FnF5-wlan.sh</syntaxhighlight>
FILE /etc/acpi/actions/FnF5-wifi.shAction script for wifi button event
<syntaxhighlight lang="bash">#!/bin/sh
logger "[ACPI] Fn+F5 pressed, WiFi rfkill state toggled"
rf=/sys/class/rfkill/rfkill0
case $(< $rf/state) in
    0) echo 1 >$rf/state;;
    1) echo 0 >$rf/state;;
esac</syntaxhighlight>
root #chmod +x /etc/acpi/actions/FnF5-wifi.sh


Backlight control

Brightness up
FILE /etc/acpi/events/FnHome-brightnessupEvent creation for brightness up button
<syntaxhighlight lang="bash">#FnHome video/brightnessup BRTUP 00000086 00000000
event=video/brightnessup
action=/etc/acpi/actions/FnHome-brightnessup.sh</syntaxhighlight>
FILE /etc/acpi/actions/FnHome-brightnessup.shAction script for brightness up button event
<syntaxhighlight lang="bash">#!/bin/bash 

# Set the static increment value.  Keep in mind that this will 
# be done twice. 
IncVal=20 

# Get the Maximum value for use. 
#MaxVal=$(cat /sys/class/backlight/intel_backlight/max_brightness); 
read -r MaxVal < "/sys/class/backlight/intel_backlight/max_brightness"

# Get the current brightness value. 
#CurrVal=$(cat /sys/class/backlight/intel_backlight/brightness); 
read -r CurrVal < "/sys/class/backlight/intel_backlight/brightness"

# Set the new value minus the decrement value. 
NewVal=$(($CurrVal + $IncVal)); 
echo $NewVal 

# Set it to the threshold of the max value. 
ThresholdVal=$(($NewVal<$MaxVal?$NewVal:$MaxVal)) 
echo $ThresholdVal 

# Set the new value directly. 
echo -n $ThresholdVal > /sys/class/backlight/intel_backlight/brightness 

logger "[ACPI] brightnessup |$CurrVal| |$NewVal| |$ThresholdVal|"</syntaxhighlight>

Remember to make the script executable:

root #chmod +x /etc/acpi/actions/FnHome-brightnessup.sh
Brightness down
FILE /etc/acpi/events/FnEnd-brightnessdownEvent creation for brightness down button
<syntaxhighlight lang="bash">#FnEnd video/brightnessdown BRTDN 00000087 00000000
event=video/brightnessdown
action=/etc/acpi/actions/FnEnd-brightnessdown.sh</syntaxhighlight>
FILE /etc/acpi/actions/FnEnd-brightnessdown.shAction script for brightness down button event
<syntaxhighlight lang="bash">#!/bin/bash 

# Set the static decrement value.  Keep in mind that this will 
# be done twice. 
DecVal=20 

# Set the Minimum we will accept. 
MinVal=0 

# Get the current brightness value. 
#CurrVal=$(cat /sys/class/backlight/intel_backlight/brightness); 
read -r CurrVal < "/sys/class/backlight/intel_backlight/brightness"

# Set the new value minus the decrement value. 
NewVal=$(($CurrVal - $DecVal)); 
echo $NewVal 

# Set it to the threshold of the min value. 
ThresholdVal=$(($NewVal>$MinVal?$NewVal:$MinVal)) 
echo $ThresholdVal 

# Set the new value directly. 
echo -n $ThresholdVal > /sys/class/backlight/intel_backlight/brightness 

logger "[ACPI] brightnessdown |$CurrVal| |$NewVal| |$ThresholdVal|"</syntaxhighlight>

Be sure to make the script executable:

root #chmod +x /etc/acpi/actions/FnEnd-brightnessdown.sh

Source: forums.gentoo.org

CD control

Instead of CD I would like to control media-sound/moc.

Initially I start mocp manually to populate playlist, quit qui and than use special keys to control mocp.

Stop
FILE /etc/acpi/events/FnArrowUp-stop
<syntaxhighlight lang="bash">#Fn+ArrowUp cd/stop CDSTOP 00000080 00000000 K
event=cd/stop
action=/etc/acpi/actions/FnArrowUp-stop.sh</syntaxhighlight>
FILE /etc/acpi/actions/FnArrowUp-stop.sh
<syntaxhighlight lang="bash">#!/bin/sh
XUSER=$(ps -eo uname,args --sort start_time | grep mocp | awk '{print $1}' | head -n1)
sudo -u $XUSER /usr/bin/mocp -s</syntaxhighlight>
root #chmod +x /etc/acpi/actions/FnArrowUp-stop.sh
Prev track
FILE /etc/acpi/events/FnArrowLeft-prev
<syntaxhighlight lang="bash">#Fn+ArrowUp cd/prev CDPREV 00000080 00000000 K
event=cd/prev
action=/etc/acpi/actions/FnArrowLeft-prev.sh</syntaxhighlight>
FILE /etc/acpi/actions/FnArrowLeft-prev.sh
<syntaxhighlight lang="bash">#!/bin/sh
XUSER=$(ps -eo uname,args --sort start_time | grep mocp | awk '{print $1}' | head -n1)
sudo -u $XUSER /usr/bin/mocp -r</syntaxhighlight>
root #chmod +x /etc/acpi/actions/FnArrowLeft-prev.sh
Next track
FILE /etc/acpi/events/FnArrowRight-next
<syntaxhighlight lang="bash">#Fn+ArrowUp cd/next CDNEXT 00000080 00000000 K
event=cd/next
action=/etc/acpi/actions/FnArrowRight-next.sh</syntaxhighlight>
FILE /etc/acpi/actions/FnArrowRight-next.sh
<syntaxhighlight lang="bash">#!/bin/sh
XUSER=$(ps -eo uname,args --sort start_time | grep mocp | awk '{print $1}' | head -n1)
sudo -u $XUSER /usr/bin/mocp -f</syntaxhighlight>
root #chmod +x /etc/acpi/actions/FnArrowRight-next.sh
Play/pause toggle
FILE /etc/acpi/events/FnArrowDown-play
<syntaxhighlight lang="bash">#Fn+ArrowUp cd/play CDPLAY 00000080 00000000 K
event=cd/play
action=/etc/acpi/actions/FnArrowDown-play.sh</syntaxhighlight>
FILE /etc/acpi/actions/FnArrowDown-play.sh
<syntaxhighlight lang="bash">#!/bin/sh
XUSER=$(ps -eo uname,args --sort start_time | grep mocp | awk '{print $1}' | head -n1)
sudo -u $XUSER /usr/bin/mocp -G</syntaxhighlight>
root #chmod +x /etc/acpi/actions/FnArrowDown-play.sh

Troubleshooting

To see output when a key is pressed:

root #acpid -c /etc/acpi/events/ -s /var/run/acpid.socket -f -d -l | tee tmp

Questions

  • Do I need to place all events in the same file, something like /etc/acpi/events/default.thinkpad-t410 and make it downloadable?
  • How to choose vlock vs slimlock screenlock action depending on console vs Xorg or for both altogether?

Another option is to use ScrLk key to lock console with app-misc/vlock, or it can be done through the same acpi action? dunno

This article is issued from Gentoo. The text is licensed under Creative Commons - Attribution - Sharealike. Additional terms may apply for the media files.