Voyage on ALIX

This page is about installing and configuring Voyage Linux (which is based on Debian GNU/Linux) on a PC Engines ALIX embedded board.


  • Board: PC Engines ALIX.2D3
  • CPU: 500 MHz AMD Geode LX800 (256KB cache)
  • RAM: 256 MB DDR DRAM
  • Storage: CompactFlash Card and IDE
  • Other: 3 x LAN (10/100 Mbit/s), 2 x USB (2.0), 1 x serial, 1 x miniPCI
  • Power consumption: ~5W


Installation was done using a USB card reader which is really easy using the installation script provided by voyage. After installing voyage on the cf card, you can just boot the system and it will start sending DHCP requests on eth0.


Here are some configuration snippets. Most of the customization is already done by the installer which asks for the board type and configures the system accordingly.

RW Mount

Initially voyage mounts the root partition as read-only to reduce disk writes. To mount it writable on boot, do the following:

  • /etc/init.d/voyage-util on line ~90 (at the 'start' label) :
    • remove /usr/local/sbin/remountro or change it to /usr/local/sbin/remountrw

Deactivate LEDs On Poweroff

The board includes three configurable LEDs which are already customized by voyage. The first one just works as a heartbeat and it blinks according to the system load. The second one shows the hard disk activity and the third one traffic on eth0.

Because the board doesn't support ACPI, it just stays on when trying to power it off, and so do the LEDs. But you can modify voyage's init script to deactivate the LEDs on poweroff to see when the system is really shut down:

  • /etc/init.d/voyage-util on line ~109 (at the 'stop' label) add:
    • echo none > /sys/class/leds/alix\:1/trigger
    • echo none > /sys/class/leds/alix\:2/trigger
    • echo none > /sys/class/leds/alix\:3/trigger

Note: it seems like even after the LEDs have been deactivated, the system needs a few more seconds to shut down completely.

Temperature Sensors

Reading from the temperature sensors is also really easy. One has just to install lm-sensors, run sensors-detect and then show the values with sensors. It requires both the modules scx200_acb lm90 (put them into /etc/modules) and will show CPU and motherboard temperatures.

Reducing Disk Access

If using a flash device as hard disk, it is preferable to reduce the disk access in order to increase lifetime. There are many ways for doing this, some are listed below:

  • disable unused daemons
  • in /etc/logrotate.d/ and /etc/logrotate.conf:
    • decrease rotate count
  • mount /tmp as tmpfs and set noatime on / (which is already the default on voyage)
  • in /etc/syslog.conf:
    • remove unneeded log files
    • prepend „-“ to paths to reduce syncing (e.g. -/var/log/syslog, already done for some log files)
  • in /etc/default/syslogd:
    • add -m 0 to SYSLOGD_OPTS to disable syslogd's MARK messages
  • in /etc/default/cron:
    • set EXTRA_OPTS=„-L 0“ to disable logging of cronjobs
  • combine write cycles and reduce the writeout interval (place the following commands in /etc/crontab with a @reboot job or use a init script):
    • echo 60000 > /proc/sys/vm/dirty_writeback_centisecs
    • echo 60000 > /proc/sys/vm/dirty_expire_centisecs
    • echo 60 > /proc/sys/vm/dirty_ratio
    • echo 1 > /proc/sys/vm/dirty_background_ratio

Note 1: By combining write cycles and reducing their interval you increase the risk of loosing data on crashes or power failure (here: up to 10min of work). Additionally, memory usage increases.

Note 2: You can monitor application's read and write operations by executing echo 1 > /proc/sys/vm/block_dump and watching the output of dmesg or /var/log/syslog. echo 0 > /proc/sys/vm/block_dump disables the logging again.

Note 3: Activating the kernel's laptop mode by setting /proc/sys/vm/laptop_mode to a value >0 should have no effect for flash drives as they don't need to be spun down. As the kernel documentation says: „When the knob is set, any physical disk I/O (that might have caused the hard disk to spin up) causes Linux to flush all dirty blocks. The result of this is that after a disk has spun down, it will not be spun up anymore to write dirty blocks, because those blocks had already been written immediately after the most recent read operation.“

Time & Date

The ALIX.2D3 doesn't ship with a CMOS battery (but the 2D13 does and you can easily expand the 2D3 with a battery), so the current time has to be set at boot with ntpdate. Voyage already includes this, but there is a small issue with the configuration. When activating a network interface, ntpdate is automatically run. Because the board has multiple interfaces, ntpdate is also run multiple times. To prevent setting the date at the same time (which results in adding the offset several times to the current date and thus being somewhere far in the future), the ntpdate script has a lockfile. Unfortunately, this lockfile can't be created at bootup because of the partition being still mounted read-only. So one has to point it to a different place, e.g. to /tmp:

  • /etc/network/if-up.d/ntpdate on line 33:
    • change LOCKFILE to /tmp/ntpdate.lock

Alternatively the script can be modified to run ntpdate only if eth0 gets up.

WLAN Access Point with WPA Encryption

For this scenario we used a TP-Link TL-WN360G miniPCI which features an Atheros AR2425 chip. Running a wireless access point with the atheros driver requires a relatively new kernel (>= 2.6.31?, currently available via voyage's unstable/experimental repository), otherwise Master mode won't be supported. Also, hostapd needs to be recompiled with madwifi support. For the setup we need:

  • hostapd
  • dnsmasq
  • madwifi-modules-2.6.xy matching your (voyage) kernel version

First, load the atheros module via modprobe ath5k and set up the wlan interface via /etc/network/interfaces:

auto ath0
iface ath0 inet static
      madwifi-base wifi0
      wireless-mode Master
      up iwpriv ath0 mode 3
      up iwconfig ath0 mode master
      up iwconfig ath0 essid voyage
      up iwconfig ath0 txpower auto
      up iwconfig ath0 enc off
      up iwconfig ath0 rate auto

Then, we need to rebuild hostapd to support madwifi, as the version shipped with voyage (currently 0.7.3-1) doesn't include it. Grab the source from, install the madwifi-source package via apt (while making sure you have a deb-src line in your sources.list) and unpack /usr/src/madwifi.tar.bz2. Inside the hostapd source directory, open hostapd/defconfig and edit CONFIG_DRIVER_MADWIFI and CFLAGS. To build hostapd, run:

$ cp defconfig .config
$ make
$ checkinstall (to build a .deb, or just use make install)

To configure hostapd, copy the example config:

$ zcat /usr/share/doc/hostapd/examples/hostapd.conf.gz > /etc/hostapd/hostapd.conf

Now, edit /etc/hostapd/hostapd.conf and change the following values:


We used TKIP because we couldn't get CCMP to work. Finally, edit /etc/default/hostapd and set DAEMON_CONF=/etc/hostapd/hostapd.conf.

Now, dnsmasq needs to be configured via /etc/dnsmasq.more.conf to provide DHCP on the ath0 interface:


and restarted: /etc/init.d/dnsmasq restart

Run hostapd and try your new access point. If hostapd aborts, try to debug it by running hostapd -dd /etc/hostapd/hostapd.conf.

Mode Switch Button

The ALIX board has a nice mode switch button whose state can be checked via a GPIO port. I wrote a tiny program to execute a configurable command (default: shutdown) when the button is pressed and to toggle some LEDs. Just save it as alix_gpio.cpp, compile it by executing g++ -o alix_gpio alix_gpio.cpp and run alix_gpio at system startup (it will automatically put itself into the background).

/*      alix_gpio.cpp
 *      Copyright 2012 Alexander Heinlein
 *      Watches the mode switch on a PCEngines ALIX board (tested on ALIX.2D13)
 *      and executes a predefined action if pressed.
 *      This program forks into the background and waits for the mode switch
 *      button to be pressed. When pressed, it activates the second (=middle)
 *      led and executes the predefined action by replacing itself with the
 *      specified process (thus quitting).
 *      Note: You need to run this program as root in order to get access to
 *            the GPIO port.
 *      Note: If you want to execute the action every time the switch is
 *            pressed without quitting, just execute a fork right before the
 *            execvp call.
 *      This program is free software; you can redistribute it and/or modify
 *      it under the terms of the GNU General Public License as published by
 *      the Free Software Foundation; either version 3 of the License, or
 *      (at your option) any later version.
 *      This program is distributed in the hope that it will be useful,
 *      but WITHOUT ANY WARRANTY; without even the implied warranty of
 *      GNU General Public License for more details.
 *      You should have received a copy of the GNU General Public License
 *      along with this program; if not, write to the Free Software
 *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 *      MA 02110-1301, USA.
#include <cstdio>
#include <ctime>
#include <sys/io.h>
#include <unistd.h>
const unsigned short int GPIO_MODESWITCH_ADDR = 0x061b0;
const unsigned short int GPIO_MODESWITCH_BIT  = 8;
const unsigned short int GPIO_LEDTWO_ADDR     = 0x06180;
const unsigned short int GPIO_LEDTWO_BIT_ON   = 25;
const unsigned short int GPIO_LEDTWO_BIT_OFF  = 9;
const time_t   SLEEP_SECONDS     = 0;
const long int SLEEP_NANOSECONDS = 500 * 1000 * 1000; // 0.5 seconds
//const char* PRESS_ACTION[] = { "ls", "-l", "-h", NULL }; // for debugging :)
const char* PRESS_ACTION[] = { "poweroff", NULL }; // last argument must be NULL!
int main(int argc, char* argv[]) {
    // fork into background
    const pid_t pid = fork();
    if (pid == -1) {
        perror("Could not fork into background");
        return -1;
    } else if (pid != 0) {
        return 0; // we are the parent
    // request access permissions for mode switch port
    // note: according to the manpage of ioperm this has to be done in the child process as
    //       permissions are not inherited by the child. but it still works when requesting
    //       permissions before the fork on my system. better don't rely on it though :).
    if (ioperm(GPIO_MODESWITCH_ADDR, 4, 1) == -1) {
        perror("Could not set access permissions for modeswitch port");
        return -1;
    const timespec ts = { SLEEP_SECONDS, SLEEP_NANOSECONDS };    
    while (true) {
        // open port and check mode switch bit
        unsigned int val = inl(GPIO_MODESWITCH_ADDR);
        bool pressed = !(val & (1 << GPIO_MODESWITCH_BIT)); // active low, 0 = pressed
        if (pressed) {
            ioperm(GPIO_MODESWITCH_ADDR, 4, 0); // remove mode switch port access permissions
            if (ioperm(GPIO_LEDTWO_ADDR, 4, 1) != 1) { // request access permissions for led port
                // switching a led on requires to set one bit and to clear another one, yay
                val = inl(GPIO_LEDTWO_ADDR);
                val |= 1 << GPIO_LEDTWO_BIT_ON;
                val &= ~ (1 << GPIO_LEDTWO_BIT_OFF);
                outl(val, GPIO_LEDTWO_ADDR);
                ioperm(GPIO_LEDTWO_ADDR, 4, 0); // remove led port access permissions
            // execute press action
            if (execvp(PRESS_ACTION[0], const_cast<char* const*>(PRESS_ACTION)) == -1) {
                perror("Could not execute press action");
                return -1;
        nanosleep(&ts, NULL);
    return 0;


Here is a small script for creating a full backup and directly transfering it to a remote host without storing anything on the hard disk. HOST, DIR and USER need to be adjusted to your needs. We use only gzip compression and arcfour (aka RC4) encryption to reduce CPU load.

if ! [ $(id -u) = 0 ]
    echo "warning: you should be root to run this script."
    sleep 5
for i in date time nice tar ssh
    if ! which $i >/dev/null
        echo "$i not found."
        exit 1              
NAME="backup-$(hostname)-$(date +%Y-%m-%d).tar.gz"
export EXCLUDES="       --exclude=/dev/*
echo "starting time: $(date)"
time nice -19 tar -cpzf - $EXCLUDES / | ssh -c arcfour $USER@$HOST "cat >$DIR/$NAME"
echo "ending time: $(date)"

Creating a backup from 400MB roughly takes 4min and gets compressed to 120MB (using 95% CPU).


img_3262_thb.jpg img_3265_thb.jpg img_3266_thb.jpg img_3269_thb.jpg


voyage_on_alix.txt · Zuletzt geändert: 2014/03/01 17:13 (Externe Bearbeitung)