Android embedded in 5 easy steps
November 17, 2014
Android has reached a very widespread deployment not only in the smartphone and tablet area, but also more and more in the embedded space. This articl...
Android has reached a very widespread deployment not only in the smartphone and tablet area, but also more and more in the embedded space. This article concentrates on a hands-on approach toward running Android on your Freescale i.MX 6 SoC based hardware. Just for demonstration purpose I used a Toradex Apalis iMX6Q module on its accompanying Apalis Evaluation carrier board, but most steps shown apply equally well to any other i.MX 6-based hardware or even any embedded SoC based hardware in general.
1. Initial BSP from SoC vendor
First let us have a look what is available from the SoC vendor. Most embedded SoC vendors do have a more or less well customised Android BSP available for free. In Freescale’s case, this means any embedded developer previously registered on their website can download such BSP with accompanying documentation and even sample images for some of their standard development kits. At the time of this writing, android_kk4.4.2_1.0.0-ga released July 2014 is available.
Freescale did a pretty good job at porting Android KitKat to the i.MX 6, but what do they provide in particular? One can find some demo images, the sources, as well as a complete documentation package. The sources basically consist of a bunch of patches with an accompanying and_patch.sh shell script to get your source tree all nicely patched up.
But let us not step ahead too much and rather have a look at the documentation first.
The Android_Quick_Start_Guide.pdf document outlines what exact images for what hardware are available. After a little consideration I choose to have a look at their SABRE board for smart devices (SD) offering detailed in chapter 3. So I downloaded android_kk4.4.2_1.0.0ga_images_6qsabresd.tar.gz and inspected what is in there. Somehow Freescale offers two versions, one called core and another called full. No further explanation about the differences is given. So I just chose core for now:
[user@host ~]$ tar xzvf
android_kk4.4.2_1.0.0-ga_core_image_6qsabresd.tar.gz
...
android_kk4.4.2_1.0.0-ga_core_image_6qsabresd/eMMC/system.img
android_kk4.4.2_1.0.0-ga_core_image_6qsabresd/eMMC/boot.img
...
[user@host eMMC]$ file system.img
system.img: Linux rev 1.0 ext4 filesystem data,
UUID=57f8f4bc-abf4-655f-bf67-946fc0f9f25b (extents) (large files)
[zim@localhost eMMC]$ file boot.img
boot.img: Android bootimg, kernel (0x10808000), ramdisk (0x11800000),
page size: 2048, cmdline (console=ttymxc0,115200 init=/init
video=mxcfb0:dev=ldb,bpp=32 v)
Very well, so we have a complete ext4 system image as well as a boot image including the Linux kernel as well as an accompanying initial ramdisk. Of course the kernel won’t be of much use on any hardware other than the SabreSD. So how do we get a suitable kernel for our hardware?
2. Linux kernel integration
A customised kernel tailored to the specific hardware is at the heart of any embedded Linux BSP. While Toradex does have such kernels available for their modules those do not include support for Android per se. Further integration of the Android specific parts will be required.
The kernel sources to Freescale’s BSPs can be found on their git server. This is no different in the Android case where one can find them tagged kk4.4.2_1.0.0-ga. So let us get them cloned our way:
[user@host ~]$ git clone -b kk4.4.2_1.0.0-ga
git://git.freescale.com/imx/linux-2.6-imx.git
...
[user@host ~]$ cd linux-2.6-imx/
[user@host linux-2.6-imx]$ head Makefile
VERSION = 3
PATCHLEVEL = 0
SUBLEVEL = 35
...
Amazingly that stuff is all still based on the good ole Linux 3.0.35 kernel. Lucky us, my co-worker, Max, once started his bring-up on that ancient stuff which we successfully demoed at this year’s embedded world exhibition in Nuremberg, Germany. So let’s get started then:
[user@host linux-2.6-imx]$ git remote add toradex git://git.toradex.com/linux-toradex.git
[user@host linux-2.6-imx]$ git remote update
[user@host linux-2.6-imx]$ git merge remotes/toradex/toradex_imx6
...
This leaves us with a few merge conflicts – basically all the files both Freescale and Toradex touched during the course of their customisation. Luckily those only involved some rather trivial areas like the SGTL5000 codec both used on the SabreSD as well as our hardware.
The next step involves the kernel configuration. Freescale’s generic i.MX 6 based configuration is called imx6_android_defconfig, which we use as a start and then merge our board specifics from apalis_imx6_defconfig into it.
And last ,but not least, we also need an initial ramdisk to go with the kernel. This one can be extracted from above mentioned boot.img using a tool available here as follows:
[user@host eMMC]$ ./unpack-bootimg.pl boot.img
kernel written to boot.img-kernel.gz
ramdisk written to boot.img-ramdisk.cpio.gz
1733 blocks
extracted ramdisk contents to directory boot.img-ramdisk/
[user@host eMMC]$ gunzip boot.img-ramdisk.cpio.gz
For now we will just go with boot.img-ramdisk.cpio, but further customisation could be done to the files extracted to the boot.img-ramdisk directory as needed. The initial ramdisk can directly be included into the kernel by configuring it as follows:
[user@host linux-2.6-imx]$ make nconfig
General setup --->
[*] Initial RAM filesystem and RAM disk (initramfs/initrd)
support
() Initramfs source file(s)
The final work of merging both the sources as well as the configuration including the initial ramdisk is on our git server for your convenience.
Now let’s have a look at actually compiling that kernel. For more information concerning toolchain and generic setup advice, please head over to our developer website. The major steps are as follows:
[user@host linux-2.6-imx]$ make apalis_imx6_android_defconfig
[user@host linux-2.6-imx]$ make -j5 uImage
...
The final U-Boot image including the initial ramdisk can be found at the following location: arch/arm/boot/uImage.
3. Flashing
The following assumes you are already running the latest Embedded Linux BSP on your Apalis iMX6Q module as outlined in the release notes.
Section 4.1.1 Storage partitions in the Android_User’s_Guide.pdf document outlines the exact partition layout to be used. For now the easiest is to either NFS boot into our regular BSP or mount the root file system from an external SD card or USB memory stick. That way we can manually do the partitioning/flashing of the eMMC from there:
root@apalis-imx6:~# fdisk /dev/mmcblk0
...
Command (m for help): p
Disk /dev/mmcblk0: 7818 MB, 7818182656 bytes
4 heads, 32 sectors/track, 119296 cylinders
Units = cylinders of 128 * 512 = 65536 bytes
Device Boot Start End Blocks Id System
/dev/mmcblk0p1 65 320 16384 c Win95 FAT32 (LBA)
/dev/mmcblk0p2 321 118880 7587840 83 Linux
Command (m for help): d
Partition number (1-4): 2
Command (m for help): n
Command action
e extended
p primary partition (1-4)
p
Partition number (1-4): 2
First cylinder (1-119296, default 1): 321
Last cylinder or +size or +sizeM or +sizeK (321-119296, default 119296): +16M
Command (m for help): n
Command action
e extended
p primary partition (1-4)
e
Partition number (1-4): 3
First cylinder (1-119296, default 1): 566
Last cylinder or +size or +sizeM or +sizeK (566-119296, default 119296): +1036M
Command (m for help): n
Command action
l logical (5 or over)
p primary partition (1-4)
l
First cylinder (8380-16374, default 8380): Using default value 8380 Last cylinder or +size or +sizeM or +sizeK (8380-16374, default 16374): +512M
Command (m for help): n
Command action
l logical (5 or over)
p primary partition (1-4)
l
First cylinder (16194-16374, default 16194): Using default value 16194
Last cylinder or +size or +sizeM or +sizeK (16194-16374, default 16374): +8M
Command (m for help): n
Command action
l logical (5 or over)
p primary partition (1-4)
l
First cylinder (16317-16374, default 16317): Using default value 16317
Last cylinder or +size or +sizeM or +sizeK (16317-16374, default 16374): Using default value 16374
Command (m for help): n
Command action
l logical (5 or over)
p primary partition (1-4)
p
Selected partition 4
First cylinder (1-119296, default 1): 16375
Last cylinder or +size or +sizeM or +sizeK (16375-119296, default 119296): Using default value 119296
Command (m for help): p
Disk /dev/mmcblk0: 7818 MB, 7818182656 bytes
4 heads, 32 sectors/track, 119296 cylinders
Units = cylinders of 128 * 512 = 65536 bytes
Device Boot Start End Blocks Id System
/dev/mmcblk0p1 65 320 16384 c Win95 FAT32
(LBA)
/dev/mmcblk0p2 321 565 15680 83 Linux
/dev/mmcblk0p3 566 16374 1011776 5 Extended
/dev/mmcblk0p4 16375 119296 6587008 83 Linux
/dev/mmcblk0p5 566 8379 500080 83 Linux
/dev/mmcblk0p6 8380 16193 500080 83 Linux
/dev/mmcblk0p7 16194 16316 7856 83 Linux
/dev/mmcblk0p8 16317 16374 3696 83 Linux
Command (m for help): w
The partition table has been altered.
Calling ioctl() to re-read partition table
[ 678.577353] mmcblk0: p1 p2 p3 p4
The next step involves copying the kernel previously built as well as the system.img onto the eMMC:
root@apalis-imx6:~# cp uImage /media/mmcblk0p1
root@apalis-imx6:~# dd if=system.img of=/media/mmcblk0p5
Unfortunately, Freescale’s Android does not seem to auto format all the additional partitions; we therefore pre-format the data (mmcblk0p4) and cache (mmcblk0p6) partitions as follows:
root@apalis-imx6:~# mkfs.ext4 /dev/mmcblk0p4
root@apalis-imx6:~# mkfs.ext4 /dev/mmcblk0p6
4. Booting
Now that we flashed Android onto the module the final step will be to get the boot arguments right. Section 3.4.2 Booting with single display: HDMI display in the Android_Quick_Start_Guide.pdf document talks about this briefly. Translated to our U-Boot environment, this boils down to the following:
Apalis iMX6 # setenv defargs 'enable_wait_mode=off vmalloc=400M androidboot.console=ttymxc0 androidboot.hardware=freescale'
Apalis iMX6 # setenv setup 'setenv setupargs fec_mac=${ethaddr} no_console_suspend=1 console=${console},${baudrate}n8'
Apalis iMX6 # setenv vidargs 'mxc_hdmi.only_cea=1 video=mxcfb0:dev=hdmi,1920x1080M@60,bpp=32 video=mxcfb1:off video=mxcfb2:off video=mxcfb3:off fbmem=28M'
Apalis iMX6 # saveenv
Apalis iMX6 # boot
Here you go, finally booting into Android 4.4.2 using a full HD HDMI screen and a USB mouse. On first impression it is quite snappy. Freescale even included a custom Ethernet app to allow easy configuration of the wired Ethernet settings otherwise not available on regular Android. The Android debug bridge (ADB) via USB also seems to come up well:
[ 10.287195] android_work: sent uevent USB_STATE=CONNECTED
[ 10.298658] android_usb gadget: high speed config #1: android
[ 10.304731] android_work: sent uevent USB_STATE=CONFIGURED
Plugging in a USB memory stick it gets detected and the regular Gallery app can be used to view pictures and movies. Even full HD movie playback, including HDMI audio, works out-of-the-box – sweet.
5. Wrapping up
It is quite easy to use a SoC vendor’s Android BSP and run it on its own hardware given a reasonable Embedded Linux BSP for that hardware has already been ported. This can even be done without having to re-compile the whole Android stack by relying on some pre-built demo images if available. However, most serious customisation will require developers to go the extra mile, download the full sources, and get them all patched up and built, which is explained in Chapter 3 Building Android for i.MX in the Android_User’s_Guide.pdf document in the Freescale i.MX 6 case.
Marcel Ziswiler is Platform Manager of Embedded Linux at Toradex.