Font size: Increase font size Decrease font size Switch style sheet from default to highcontrast and back

HOW-TO: Install ArchLinuxARM on an Acer Chromebook 13 CB5-311

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

Preparation

The 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.

Fixing suspend-to-ram on closing the lid

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.

Adjust the keyboard functions (for Xfce4)

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
Here are the scripts and configuration files that implement the setup I described above:
Acknowledgments
Some of the solutions for making the Chromebook working, for instance the keyboard mapping, I got from my experiences with the Samsung Chromebook 11. Many thanks to the folks of the ArchLinuxARM forum who worked hard on getting a smooth Linux experience on all these nice ARM devices. I also want to thank Clifford Wolf who managed to get a properly working ChrUbuntu script for the Acer CB5-311, which I could use to get ArchLinuxARM installed onto this Chromebook. Furthermore, thank you Metalab for organizing the hackathon and providing a nice place to work on such things.