HOW-TO: Install ArchLinuxARM on an Acer Chromebook 13 CB5-311
Introduction
A couple of months ago, I bought an Acer Chromebook 13, Model CB5-311-T6R7, which in my opinion has some really nice specs, good enough to provide a lightweight, portable, and quite low-cost ultrabook alternative. Just enough for me to work while I am travelling:
- NVIDIA Tegra K1 Quad-core 2.10 GHz ARM SoC with Kepler GPU
- 4 GB, DDR3L SDRAM
- 32 GB Flash Memory
- 13.3" Full HD (1920 x 1080) 16:9 matte display
- Battery life of up to 10 hours
- Sleek design, fanless, thus absolutely quiet operation
- Fair price of about 300 Euros
Apart from the relatively low CPU performance (the ARM Cortex A15 can certainly not match a recent Intel or AMD CPU) the only drawback that comes with this product is the limitation of the operating system, ChromeOS. But this can be overcome easily by just installing another Linux distribution! Although such an attempt may be relatively easy on x86 based devices, it almost always is a real pain for ARM SoC powered ones. This is due to the fact that ARM devices do not provide a BIOS that just boots off an operating sytem kernel from an attached storage device. They rather have a (most often locked down) boot loader, that usually only boots a signed kernel from a certain storage device partition. And the manufacturers private key for signing the kernel is generally not given to the public. Fortunately, in contrast to other ARM device selling companies, Google not only allows you to enable Developer mode required for booting from internal, SD-Card, or USB storage, but actually also provides the private key to sign your own Linux kernel. Therefore, several Linux distributions are available for ARM powered Chromebooks today.
However, just beeing able to sign a Linux kernel is usually not enough, since the hardware of the device needs to be supported by the kernel too. And that is where most of the problems begin, since many ARM SoCs implement GPU and VPU designs where only proprietary driver support is available. And even worse, these drivers then most often require certain (older) kernel versions in order to work correctly. This is also the case for the NVidia Tegra K1 CPU with Kepler GPU, as present in the Acer CB5-311. But at least NVidia provides an Embedded Computing section on their homepage, where one finds the proprietary drivers, and other useful informtion on how to unleash the full power of the Tegra K1 SoC using Linux kernel 3.10 (At the time of writing this, the latest official Linux kernel version is 4.2).
Here, I want to demonstrate how to get ArchLinuxArm, a Linux distribution derived from ArchLinux specifically for ARM devices, onto this particular Chromebook of mine.
For this purpose, I've set up a shell script that does the (re)partitioning of the eMMC drive within the Chromebook, and the initial installation of ArchLinuxARM. At this point, contrary to regular ArchLinux installations, a complete system with graphical user interface (Xorg + XFCE 4) will be installed.
Furthermore, I provide the required software packages for the linux-nyan kernel, and the proprietary NVidia drivers, both available at my github account.
Installation
PreparationThe first step one has to do is to reinstall ChromeOS in Developer Mode, which is in fact very easy. Simply press esc + refresh (f3) + power to reboot. On the boot screen press ctrl + d, then ctrl + d again, and then ENTER. This will re-install Chrome OS in Developer Mode.
Once you have ChromeOS Developer mode installed, there is a rather long delay on each bootup. You can skip this delay by pressing ctrl + d on the boot screen.
Repartition MMC card (internal eMMC drive)
Open a Chrome window, press ctrl + alt + t to open a crosh
terminal window. Enter the following commands:
shell
cd ~/Downloads
wget https://raw.githubusercontent.com/RaumZeit/LinuxOnAcerCB5-311/archlinux/archlinux.sh
sudo bash archlinux.sh
This will ask you how much space you would like to reserve for ArchLinuxARM. I chose 16 GB. After
changing the partition table the script will reboot the device. The boot loader will then recreate
the ChromeOS partition used for user content on the smaller partition, leaving the newly created
partition for alarm untouched.
Installing ArchLinuxARM (ALARM)
After logging into ChromeOS once again, open a Chrome window, and press ctrl + alt + t
to open a crosh
terminal window. Enter the following commands (again):
shell
cd ~/Downloads
wget https://raw.githubusercontent.com/RaumZeit/LinuxOnAcerCB5-311/archlinux/archlinux.sh
sudo bash archlinux.sh
This time the script auto-detects that the target partition already exists and installs alarm.
Once you booted into alarm (user: alarm, password: alarm), create your own user and put
it into the wheel and video group, such that sudo
and
NVidia device access is granted.
The preexisting alarm user can then be savely removed. Also do not forget to
change the default passwords!
Basic Setup
Installing the linux-nyan Kernel Package
Next, you need to compile a kernel that is capable of supporting systemd
,
since this is the default in ArchLinuxARM. The kernel that was copied over from ChromeOS
by the installer script, however, does not fully support it. Therefore, as mentioned earlier, I've created
a package named linux-nyan, available through
my archlinuxarm/PKGBUILDs fork on github.
This package pulls the latest ChromeOS 3.10 kernel frome googles git repo, and patches
it to comply with gcc
version 5 and 6. Additionally, the Marvel Chipset used for
Wifi in this chromebook comes with two additional devices, an accesspoint (AP) and a
Peer2Peer device (P2P) which troubles NetworkManager
. Therefore, I've also
included a patch that deactivates these two. Just clone the
master branch of my PKGBUILDs repository, and build and install the core/linux-nyan
package using makepkg
, like so:
git clone https://github.com/RaumZeit/PKGBUILDs.git
cd PKGBUILDs/core/linux-nyan
makepkg -si
At the end of the installation, you will be prompted to copy the kernel to /dev/mmcblk0p6,
which should be the right partition in case you've been using my installer script with default options.
Answer this question with yes
, reboot and enjoy the working kernel.
Configuring ACPI event handlers
Unfortunately, even with a proper linux kernel, not all of the Chromebooks functionality is working out of the box. For instance, the headphone jack does not automagically switch the audio output from speakers to headphone. Furthermore, although closing the lid of the notebook triggers a suspend, this command is not executed properly from within Xorg, which results in a freeze of the system that requires a hard reboot.
Luckily, the above mentioned problems can be circumvented utilizing the acpi events issued by the hardware. In order to do that, you need to install and enable the acpid daemon via
sudo pacman -S acpid
sudo systemctl enable acpid
The handling of particular acpi events is done from the /etc/acpi/handler.sh script
that comes with the acpid package.
Enable headphone jack to switch pulseaudio output port
First of all you need to know that each logged-in user has its own pulseaudio
daemon running.
Therefore, setting the output port needs to be done for each user separately. The
pulseaudio daemon comes with a little commandline tools that allows to change its
settings without ever touching a GUI, pacmd
. Thats what we will make use of to switch
the output port from within the handler.sh script of acpid
.
First, lets see what pulseaudio knows about its output sink(s):
pacmd list-sinks
This should give you some result like this:
1 sink(s) available.
* index: 0
name: <alsa_output.platform-sound.8.analog-stereo>
driver: <module-alsa-card.c>
flags: HARDWARE HW_VOLUME_CTRL DECIBEL_VOLUME LATENCY FLAT_VOLUME DYNAMIC_LATENCY
state: SUSPENDED
suspend cause: IDLE
priority: 9009
volume: front-left: 32845 / 50% / -18.00 dB, front-right: 32845 / 50% / -18.00 dB
balance 0.00
base volume: 38295 / 58% / -14.00 dB
volume steps: 65537
muted: no
current latency: 0.00 ms
max request: 0 KiB
max rewind: 0 KiB
monitor source: 0
sample spec: s16le 2ch 44100Hz
channel map: front-left,front-right
Stereo
used by: 0
linked by: 0
configured latency: 0.00 ms; range is 0.50 .. 185.76 ms
card: 0 <alsa_card.platform-sound.8>
module: 7
properties:
alsa.resolution_bits = "16"
device.api = "alsa"
device.class = "sound"
alsa.class = "generic"
alsa.subclass = "generic-mix"
alsa.name = ""
alsa.id = "Playback HiFi-0"
alsa.subdevice = "0"
alsa.subdevice_name = "subdevice #0"
alsa.device = "0"
alsa.card = "1"
alsa.card_name = "NVIDIA Tegra Venice2"
alsa.long_card_name = "NVIDIA Tegra Venice2"
alsa.driver_name = "snd_soc_tegra_max98090"
device.bus_path = "platform-sound.8"
sysfs.path = "/devices/soc0/sound.8/sound/card1"
device.string = "hw:1"
device.buffering.buffer_size = "32768"
device.buffering.fragment_size = "4096"
device.access_mode = "mmap+timer"
device.profile.name = "analog-stereo"
device.profile.description = "Analog Stereo"
device.description = "NVIDIA Tegra Venice2 Analog Stereo"
module-udev-detect.discovered = "1"
device.icon_name = "audio-card"
ports:
analog-output-speaker: Speakers (priority 10000, latency offset 0 usec, available: unknown)
properties:
device.icon_name = "audio-speakers"
analog-output-headphones: Headphones (priority 9000, latency offset 0 usec, available: unknown)
properties:
device.icon_name = "audio-headphones"
active port: <analog-output-speaker>
In the last lines of the output, you see the two detected ports analog-output-speaker
and analog-output-headphone, as well as the currently active port.
Furthermore, in the very beginning of the output, the name of the sink,
alsa_output.platform-sound.8.analog-stereo, is listed. With this information in hand,
we can switch the output port to the headphones using
pacmd set-sink-port alsa_output.platform-sound.8.analog-stereo analog-output-headphones
To switch back to the build-in speakers, just substitute the port name analog-output-headphones
with analog-output-speaker and issue the command again.
Now, lets build an acpi rule to make pulseaudio switch the port automatically, each time you plug/unplug something to the headphone jack. Just add the following event rule to the big case conditionals in /etc/acpi/handler.sh
jack/headphone)
case "$3" in
plug)
logger 'Headphones plugged'
_SwitchPulsePort "$3"
;;
unplug)
logger 'Headphones unplugged'
_SwitchPulsePort "$3"
;;
*)
logger "ACPI action undefined: $3"
;;
esac
;;
This snippet handles the plug / unplug event, logs the corresponding event and subsequently
calls a _SwitchPulsePort function together with the events value. To make headphone jack
detection actually working, we just need to define the _SwitchPulsePort function:
function _SwitchPulsePort () {
sink="alsa_output.platform-sound.8.analog-stereo"
port="analog-output-speaker"
if [ "x$1" == "xplug" ] ; then
port="analog-output-headphones"
fi
for user in `ps axc -o user,command | grep pulseaudio | cut -f1 -d' ' | sort | uniq`
do
uid=`id -u "${user}"`
pulsepath="/run/user/${uid}/pulse/"
su "${user}" -c - "PULSE_RUNTIME_PATH=\"${pulsepath}\" pacmd set-sink-port ${sink} ${port}"
done
}
This function should be placed in the /etc/acpi/handler.sh script, right before the
line starting with
case "$1" in
Now, each time the headphone jack detects a change in its state, the acpi handler script
goes through the list of all users that own a running pulseaudio daemon, and routes the
audio output either to the speakers, or the headphones.
Since we use pulseaudio
to control sound output and mixing, one further step
is required to allow for an easy control of the volume of speakers or headphone. The
regular xfce4-mixer
plugin for XFCE4 panels can only control alsa mixer channels.
That is why I use the xfce4-pulseaudio-plugin
from AUR as a substitute.
Although I've read elsewhere that some people got suspend-to-ram by closing the lid working with their chrubuntu-fied Acer Chromebook 13, it took me a while to find a way to get it working (almost) properly. What I found is that suspending from within Xorg always resulted in a system freeze that could only be solved by hard-rebooting the Chromebook. Interestingly, suspending from a virtual terminal (VT) was no problem at all. The only thing I experienced was a black screen after resume, which went away whenever I switched the terminal back to Xorg, using ctrl + alt + F7. That's why I thought, that I could just switch to a VT right before suspending, and switching back to Xorg on resume with a corresponding acpi event handler.
First, I disabled logind
handling the lid switch event by replacing the line
#HandleLidSwitch=suspend
with
HandleLidSwitch=ignore
in its config file /etc/systemd/logind.conf. Next, the lid event handler of acpid
in /etc/acpi/handler.sh needed to be adjusted to look like this
button/lid)
case "$3" in
close)
logger 'LID closed'
chvt 1
pm-suspend
;;
open)
chvt 7
logger 'LID opened'
;;
*)
logger "ACPI action undefined: $3"
;;
esac
;;
I highlighted the commands that need to be added to the already provided handler skeleton.
Now, everytime the lid is closed, acpid
changes to VT 1, and calls pm-suspend
afterwards. On opening the lid, it changes back to Xorg running in VT 7. For me, this solution
works fine until another, more less hackeresk way emerges.
The Chromebook's keyboard does not offer dedicated function keys F1 - F12. Instead, the first row consists of the esc key, some forward and backward button, and other control buttons for display brightness, and audio settings. But these buttons are not assigned their actual function yet. The Page-Up / Page-Down, and some other keys are missing, too, and need to be augmented on top of other, already assigned ones. This can be done by specifying a so-called Super-Key that needs to be pressed simultaneously.
Here is a script with a set of commands that restore most of the missing functionalities:
#!/bin/sh
xmodmap -e 'keycode 133 = Super_L'
xmodmap -e 'keysym Super_L = Mode_switch'
xmodmap -e 'keycode 22 = BackSpace Delete BackSpace Delete BackSpace BackSpace'
xmodmap -e 'keycode 67 = XF86Back F1 XF86Back F1 F1 F1 XF86Switch_VT_1'
xmodmap -e 'keycode 68 = XF86Forward F2 XF86Forward F2 F2 F2 XF86Switch_VT_2'
xmodmap -e 'keycode 69 = XF86Reload F3 XF86Reload F3 F3 F3 XF86Switch_VT_3'
xmodmap -e 'keycode 70 = XF86Battery F4 XF86Battery F4 F4 F4 XF86Switch_VT_4'
xmodmap -e 'keycode 71 = XF86Display F5 XF86Display F5 F5 F5 XF86Switch_VT_5'
xmodmap -e 'keycode 72 = XF86MonBrightnessDown F6 XF86MonBrightnessDown F6 F6 F6 XF86Switch_VT_6'
xmodmap -e 'keycode 73 = XF86MonBrightnessUp F7 XF86MonBrightnessUp F7 F7 F7 XF86Switch_VT_7'
xmodmap -e 'keycode 74 = XF86AudioMute F8 XF86AudioMute F8 F8 F8 XF86Switch_VT_8'
xmodmap -e 'keycode 75 = XF86AudioLowerVolume F9 XF86AudioLowerVolume F9 F9 F9 XF86Switch_VT_9'
xmodmap -e 'keycode 76 = XF86AudioRaiseVolume F10 XF86AudioRaiseVolume F10 F10 F10 XF86Switch_VT_10'
xmodmap -e 'keycode 111 = Up NoSymbol Prior'
xmodmap -e 'keycode 113 = Left NoSymbol Home'
xmodmap -e 'keycode 114 = Right NoSymbol End'
xmodmap -e 'keycode 116 = Down NoSymbol Next'
You can just save this script somewhere itno your filesystem, and call it
after logging in to xfce4, e.g. by adding it to Settings/Session and Startup
of the xfce4-settings
.
I assigned the Super-Key function to the Search key, right below the Tab Key, which in turn allows one, for instance, to press Search + Down to simulate a Page-Down press. Furthermore, the Brighness, and audio keys are assigned their corresponding XF86 event, that should be automatically handled by Xorg, or xfce4.
How to control display brightness (a.k.a. backlight control)The Chromebooks display backlight can be controlled by adjusting the pwm device provided by the linux kernel. Luckily, this is as easy as writing a number to the corresponding backlight pwm device control, accessible through /sys/class/backlight/pwm-backlight/brightness. Reading from this file retrieves the current brightness level
cat /sys/class/backlight/pwm-backlight/brightness
The maximum brightness is available through
cat /sys/class/backlight/pwm-backlight/max_brightness
Writing a number between 1-max_brightness to this device finally changes the brightness of
the display backlight:
echo 100 > /sys/class/backlight/pwm-backlight/brightness
This device path is usually owned by the root user, so a regular user has no write permissions. However, one can add a udev rule to change this control's ownership and permission flags. For instance, adding
KERNEL=="pwm-backlight" SUBSYSTEM=="backlight" RUN+="/bin/chmod 664 /sys/class/backlight/pwm-backlight/brightness"
KERNEL=="pwm-backlight" SUBSYSTEM=="backlight" RUN+="/bin/chgrp video /sys/class/backlight/pwm-backlight/brightness"
instructs udev
to change the group identity to video
and make this
file writeable for owner and group. Simply put the above content into a text file
/etc/udev/rules.d/99-backlight-permissions.rules, add your user to the video group, and
reboot.
To get the brightness keys F6 and F7 to actually control the brightness you
probably need to bind these keys to their function manually. For that purpose, I just created a
bash
script /usr/bin/brightness.sh with the following content that
allows for increasing or decreasing the display brightness:
#!/bin/sh
#
# Script to change brightness for laptops which don't respond to hardware keys
# Needs to have write permissions to /sys/class/backlight/pwm-backlight/brightness
#
BRIGHT_INCREMENT=2
MIN_BRIGHT=1
read MAX_BRIGHT < /sys/class/backlight/pwm-backlight/max_brightness
read CURRENT_BRIGHT < /sys/class/backlight/pwm-backlight/brightness
case $1 in
"up")
CURRENT_BRIGHT=`expr $CURRENT_BRIGHT + $BRIGHT_INCREMENT`
;;
"down")
CURRENT_BRIGHT=`expr $CURRENT_BRIGHT - $BRIGHT_INCREMENT`
;;
*)
;;
esac
if [ $CURRENT_BRIGHT -ge $MIN_BRIGHT ] && [ $CURRENT_BRIGHT -le $MAX_BRIGHT ]
then
echo $CURRENT_BRIGHT > /sys/class/backlight/pwm-backlight/brightness
fi
To finally bind the display brightness to the corresponding keys, I use the Keyboard/Application Shortcuts
settings of XFCE4 to execute my script and pass either up or down as first
parameter.
Concluding Remarks
This part will be filled out soon.
Resources
- LinuxOnAcerCB5-311
(contains the
archlinux.sh
script for the initial installation) - linux-nyan kernel (linux-nyan kernel package)
- gpu-nvidia-tegra-k1 (proprietary NVidia drivers package)
- /etc/acpi/handler.sh (handler script for
acpid
) - chromebook-keymap.sh (Keyboard assignment script)
- 99-backlight-permissions.rules (udev rules for backlight control permissions)
- brightness.sh (backlight brightness control script)