My previous laptop was an Asus that ran Linux beautifully, until, after a few years, several pieces of hardware stopped working (first battery, then wifi, then monitor). I had heard great things about Lenovo, specifically how nicely they play with Linux, so last summer I bought myself a Lenovo X140e. On the whole, I’m quite happy with it, but to my surprise (and sadness), it didn’t work out of the box like my Asus did. In this post I’ll detail some of the issues I had and my workarounds for dealing with them.
The main issues that I’ve managed to solve are:
- wifi (Arch-specific solution)
- brightness keys
- no insert key
There’s one issue that I haven’t manage to solve:
- jittery touchpad, but only while on AC power
Basically, there’s no issue while on battery power; but on AC power, touching the touchpad, even without moving your finger around, causes the cursor to jut around back and forth really fast. This (annoying) problem has been confirmed elsewhere, but I haven’t found a solution to it.
If you have a solution, please email me!
WiFi
By far the most serious (but luckily, easiest to solve) issue is the Broadcom wifi card BCM43228:
lspci -vnn | grep Broadcom
$ 01:00.0 Network controller [0280]: Broadcom Corporation BCM43228 802.11a/b/g/n [14e4:4359]
This card was not supported under the Linux kernel until kernel version 3.17. (We’re now on 3.18, as of this writing; when I bought the laptop, we were at 3.16.) The workaround for kernels below 3.17 is to use the AUR package broadcom-wl. However, even once we arrived at 3.17, I found the native support (with the b43 driver and firmware) to be lacking: weak wifi connections, constant dropping, etc. So I still use broadcom-wl. Here’s how it works.
First, download broadcom-wl with your favorite AUR helper, and install it. For example:
cower -d broadcom-wl
$ cd ~/aur/broadcom-wl
$ makepkg -csi $
Second, restart computer.
That’s all! Well, almost. Now, every time you update your kernel, you need to rebuild and reinstall broadcom-wl. For example:
cd ~/aur/broadcom-wl
$ makepkg -csif $
The -f
flag forces a rebuild and overwrites the current .pkg.tar.xz
file.
(Another option is to use broadcom-wl-dkms, which automatically rebuilds itself after a kernel update.)
(This solution is obviously specific to Arch Linux, but most major distros should have some analog of the broadcom-wl package available, which should likewise solve the issue.)
Brightness keys
You’re supposed to be able to change the brightness with <Fn-F8>
and <Fn-F9>
(that is, the function key together with F8
or F9
). For me, this works fine in console, but not in X11, where most people (including me) spend most of their time. I’ve read that a BIOS upgrade fixes this, but I haven’t tried that.
My workaround was to write a simple bash script, brt. The idea:
brt
(no argument): output current brightness level (0–255).brt [n]
: set brightness level to n (0–255).brt down
: decrease brightness level by 20 (bind this to<Fn-F8>
).brt up
: increase brightness level by 20 (bind this to<Fn-F9>
).
I’ll illustrate how the main part of it works, the brt_change()
function (the rest of the script should be pretty self-explanatory):
#!/bin/bash
brightness_file="/sys/class/backlight/radeon_bl0/brightness"
max_brightness=255
min_brightness=5
current_brightness=$(cat "$brightness_file")
up_amt=20
down_amt=20
brt_change() {
echo "$1" | sudo tee "$brightness_file"
}
brt_up() {
local new_brightness=$(($current_brightness + $up_amt))
if [[ $new_brightness -le $max_brightness ]]; then
brt_change "$new_brightness"
else
brt_change "$max_brightness"
fi
}
brt_down() {
local new_brightness=$(($current_brightness - $up_amt))
if [[ $new_brightness -ge $min_brightness ]]; then
brt_change "$new_brightness"
else
brt_change "$min_brightness"
fi
}
if [[ $# -eq 1 ]]; then
case "$1" in
) brt_change "$1" && exit 0;;
[0-9]*up) brt_up && exit 0;;
down) brt_down && exit 0;;
*) echo "Error: invalid argument. Pick a brightness level ($min_brightness-$max_brightness), or say 'up' or 'down'." && exit 1;;
esac
elif [[ $# -eq 0 ]]; then
echo "$current_brightness"
exit 0
else
echo "Error: too many arguments."
exit 1
fi
The file /sys/class/backlight/radeon_bl0/brightness
contains the current brightness level, which for my Lenovo X140e is between 0 and 255. To change the brightness, you just change this file. The problem is that since it’s located in /sys/...
, you need root permission to change it. That means that
echo "100" > /sys/...
won’t work, but neither will
sudo echo "100" > /sys/...
The reason is because in the latter, sudo
is only operating on the echo
command. It’s like saying, run echo
as root, and now (not as root) append the output to /sys/...
. To solve this, we use tee
, which allows piping from stdin to a file, as root:
echo "100" | sudo tee /sys/...
This command will successfully set the brightness level to 100, and that’s the crux of the script.
There’s one remaining issue, though: we don’t want to run this script in a terminal; we want to bind it to a key. But the script uses sudo
, which requires a password to be typed, which you can’t really do outside of a terminal. The solution is to allow tee
to be run as root without a password. To do this, you need to change the sudoers file by running visudo
(as root) and adding this line:
# Add `tee' to list of commands that user `brian' can run without password
brian ALL = NOPASSWD: /usr/bin/tee
What this does is allow the user “brian” (that’s me) to run tee
as root (sudo tee
) without a password.1 /usr/bin/tee
is of course the full path to tee
. To find that out on your system, run which tee
in a terminal.
Now the script can be run effectively. Just bind brt down
and brt up
to <Fn-F8>
and <Fn-F9>
(or whatever you want) in whatever way is required by your desktop environment or window manager. (For me, I bind keys in xmonad.hs
since I use xmonad.)
No insert key
The laptop keyboard does not come with any physical Insert
key. I guess that’s because most people nowadays don’t use it very often. But I do. One of the best features of Linux (well, X11) is the X clipboard: whenever you highlight something, it gets added to the X clipboard (no need to <Ctrl-C>
), and you can paste it with <Shift-Insert>
. (I also use Insert
to go into ignore-mode in Vimperator for Firefox.)
What I did was bind the Windows key (which was serving no purpose) to Insert
. Here’s how:
xmodmap -e "keycode 133 = Insert" # map windows button to insert
You can find the keycode of a key by running xev
(X event program) from a terminal, typing the key, and looking for “keycode” in the output. (Hit <Ctrl-C>
to exit xev
.)
Now you can run this xmodmap
command in a terminal, and it should work. But the best solution is to include it in your .xinitrc
file so that it’s run every time X starts. (I have a whole keyboard-adjust
script that adds the Dvorak layout, switches caps and control lock, etc., maps the Windows button to insert, etc.; I then call the whole script from my .xinitrc
file.)
NB: This is a (potential) security risk, as it allows “brian”, or anyone logged in as “brian”, to overwrite any file! (Example:
echo "all gone" | sudo tee /path/to/important/file
.) Only do this if you’re the only one with access to this user profile.↩︎