During the past few months, I've been working on bringing Replicant 4.2 support to the Goldelico GTA04, a montherboard replacement for the Openmoko FreeRunner (GTA02) that is manufactured by Golden Delicious with the intent of being free software friendly. The board design is also released under a free licence. There is an active community of developers and users dedicated to the GTA04 and other similar projects, brougt together under the hood of OpenPhoenux. Other similar projects include the upcoming Neo900, a motherboard replacement for the Nokia N900 using similar hardware as the GTA04 and also aiming to be free software friendly, with a particular emphasis on security and privacy features.
I recently published a blog post covering the status of the Replicant 4.2 port to the Goldelico GTA04 on the Replicant blog, with a nice video highlighting which features already work well. However, in the present post, I would like to cover some of the technical aspects of the port, especially regarding the use of a recent mainline Linux kernel with Android 4.2.
Currently, Android devices ship with Linux 3.4, after a long time of using version 3.0, which started with Android 4.0 Ice Cream Sandwich. To this day, the reference common Android kernel repos have branches with Linux 3.10 and experimental work is done on Linux 3.14. That's pretty close to the Linux 3.12 version we're using on the GTA04!
The first challenge to overcome was dealing with suspend and resume. Android uses a particular mechanism to implement opportunistic suspend, using wakelocks: both the kernel and userspace can register these locks to prevent the device from going to suspend. In Android kernels, there is another mechanism that allows some non-critical chips to reach suspend state before the rest of the system: this is earlysuspend. As the whole Android system is built around the concept of wakelocks to handle power management, something similar needed to be implemented in the mainline Linux kernel. After efforts from the Android kernel maintainers, wakelocks were implemented in a clean fashion some time ago. In order to implement opportunistic suspend, a separate interface was implemented on mainline Linux, known as autosleep, which uses different nodes than /sys/power/state (that the Android kernels use directly for opportunistic suspend). Starting in Android 4.1, a library was added to Android in order to detect and handle these different modes.
So thankfully, everything was already in place to use autosleep properly on Android 4.2. Except that it didn't work. This turned out to be because of a dedicated capability that was missing from the Android userspace: CAP_BLOCK_SUSPEND. It turned out to be easier to just revert the patch restricting access to wakelocks to users with that capability in the kernel.
Android USB Gadget and Android Debug Bridge
Android also went their own way in implementing USB device drivers for the various things that are used by Android: mass storage, rndis for USB networking, MTP for file access and a couple others, including ADB. The Android Debug Bridge (ADB) is a great way of debugging what's going on with an Android device, providing easy access to the logs, to a shell and file transfer (these are the features of ADB I use the most). All of that is not integrated at all in the mainline Linux kernel, so there was some substantial work to do here. The first thing to do was importing the related commits from the android-3.10 branch of the common Android Linux kernel. All of that built nicely with only minor code corrections, to follow some API changes in 3.12 and some features like rndis or MTP worked right away, but the most interesting part was left not working: that's the Android Debug Bridge. A few kernel versions back, there used to be a dedicated composite function driver for ADB, but a commit by one of Android's kernel maintainers totally gets rid of it, calling it obsolete with no further information. While attempts to restore it failed, I tried to find out in details why it is obsolete and if that meant the final death of ADB, that I found rather convenient. Thankfully, someone found out what happened and wrote about it: the dedicated ADB driver was being replaced by another one using FunctionFS, a more flexible and generic way of implementing such drivers, directly from userspace. It turns out that FunctionFS support for the userspace ADB server was merged nearly entirely in Android 4.2. I had to backport a missing fix to have it fully working properly and also had to import adaptation a patch in recovery to have sideload working with FunctionFS too. A few bits were also needed in the initialization procedure to have things set up right. Once all of that was done, it could finally run flawlessly!
Headset/headphones detection with SoC Jack
So Jack's a good guy. Everyone knows about that. Whether he's saving the homeland from yet another threat or just letting us know something of interest just got plugged in one hole or another, it always feels great to hear from Jack. However, the Android kernel guys didn't seem to appreciate his participation in the show as much as we all do, or at least until recently.
The traditional way of reporting a headset or headphones plug/unplug in Android kernels was using a switch called h2w, reporting these events to userspace. The mainline kernel prefers another approach, using our beloved Jack SoC architecture. It also provides a convenient way of reporting button pushes, which is quite nice. So instead of rewriting it all using the h2w switch, it struck me that there is a frameworks config option to politely ask Android to give Jack some consideration. And when it does that, everything works great, including button press reports!