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.
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!
As I'm moving towards studying at the university, I got myself a flat, which makes it perfect for hosting another server, with better capacities and a better Internet connectivity. I was (and still will be for probably about a week) worried of losing mails due to an electric/internet/disk failure on my only server. Thanks to this new opportunity, I'll have good-enough redundancy to stop caring.
My current server is a SheevaPlug, which embeds the kirkwood platform -- not so powerful, but apparently just enough for my needs. The biggest drawback of this device is its lack of connectivity: only one ethernet (not that I really care for more, but it can come in handy), one USB2 port and one sdcard port. Its internal memory is 512Mib NAND and there is no WiFi/bluetooth/audio: just fine for a home server with an external USB disk, but cannot do much more. It is also known to have a PSU over-heating problem (or something of the same sort) that is supposed to make it unusable soon enough. I didn't encounter the issue so far and I've been using it for about 2 or 3 years now. What I'm more concerned about however is the health of the external USB disk. Not that I've seen I/O errors, but it sometimes (maybe once every 4 or 5 months) seems to disconnect, and I have to reboot the plug.
So I decided to go with a DreamPlug for my new place: more connectivity (including e-SATA), more internal storage (on mmc instead of NAND) and a presumably safer PSU. Note that the UART/JTAG module is sold separately, while the SheevaPlug has it embedded. For more details and comparisons between the two devices, I'm maintaining the Plug Computers hardware resource page at LibrePlanet. The device ships with Debian pre-installed, but I prefer to reinstall a sane base and reflash the bootloader. Moreover, the DreamPlug (and the SheevaPlug as well) have full-blown Debian support, with bootable installer images.
Installing Debian Wheezy
Being very happy with the Debian support for both these devices, I started by installing Wheezy on the DreamPlug. Reflashing the bootloader went well: the pre-installed version was U-Boot 2011.06 (Oct 15 2011 - 02:02:08) and was upgraded to U-Boot 2012.04.01 (Jun 01 2012 - 02:17:08) which is the version in the wheezy u-boot package. Then I went on to booting the installer uImage and uInitrd (respectively the kernel and initramfs images in u-boot format) from TFTP: I used tftpd which uses the inetd superserver and can be configured from /etc/inetd.conf.
As the installation went through, it seemed like everything was going perfectly well, except that at some point, the whole thing simply hanged on Installed u-boot-tools. After some extensive research, it turned out that the problem was caused by the partition layout I chose: ext2 rootfs instead of ext4, which for some reason didn't call the script in charge of updating the /dev nodes after partitioning the disk. Hence, the nodes in /dev/disk/by-uuid/ were not updated and the flash-kernel script couldn't match the (correct) UUID from fstab. When that happens, the tool (in particular the part that generates the initramfs with the rootfs node) shows an error message and waits for input from stdin, which simply couldn't arrive in the installer context. The workaround I used was to run "udevadm trigger" on the installer shell, kill the script and start that last step again. Then it worked.
In the end, the only thing that still bothers me is the incredibly long boot time. It seems to be because the internal mmc is awfully slow: using an external usb disk reduces the boot time from more than a minute to roughly ten seconds.
Along with moving on to new studies (I'm done with High School), I got a brand new laptop: an Asus N56VB-S3055H. The hardware specs are really nice, with a quad-core i7-3630QM CPU, 6Gb RAM, 1Tb HDD. I was worried about the integrated nVidia card (GeForce GT 740M) at first, that I suspected to be a cause of problems on GNU/Linux. Thankfully, it does not bother me and the Intel Ivy-bridge card works just fine. I also had the great suprise of finding an ath9k WiFi card shipped with the laptop! You can find a complete hardware review at h-node: Asus N56VB-S3055H
I used to install every new machine with Trisquel GNU/Linux, however I don't really agree with the Linux-libre policy that is about blocking the firmwares load. Moreover, Debian recently made great efforts towards being a fully free distro. So basically, my Debian installation is fully free and the hardware doesn't require loaded non-free firmwares (which means I don't have to use the non-free Debian repo).
Setting up the BIOS
However the BIOS is non-free and it ships with Secure Mode enabled. The BIOS can be accessed by booting with the ESC key pressed and then selecting the option to access Setup. Make sure that you do not let it boot the preinstalled Windows 8 as there is no way to soft turn the computer off if you don't agree to the license terms.
In order to install GNU/Linux, make sure you disable the following options in the BIOS:
Intel Anti-theft technology
Secure Boot Control
Then enable the following options:
Launch CCSM (you might want to disable PXE then)
UEFI boot with the Debian installer
With such options in the BIOS, you should be able to boot a GNU/Linux installation disc (and probably an USB install media too). However, be very careful: the BIOS only support UEFI boot from the HDD, so you have to install GRUB with UEFI support. In the debian-installer, I only had to create an UEFI partition (at the begining of the disk) and the installer did the rest. It also set the mountpoint to /boot/uefi. Keeping the Windows UEFI partition (make sure to format it though) would probably work too. As a matter of fact, I failed the installation the first time because I removed the partition (and the Debian installer warned me that it was a bad idea). So things aren't so bad in the end, since there is free software support for UEFI booting.
Setting up the touchpad correctly
The laptop has a synaptics touchpad which didn't work correctly at first: drag'n'drop was not at all possible and both click buttons triggered a left click. After some online research, I found one solution that is to use one-finger click for left click and two-finger click for right click. However, this was far from perfect since this is not the expected behavior and drag'n'drop was still impossible. Here is some documentation about how to set that up though: Left-, Right-, and Middle-click on Clickpad .
The real complete solution was adding some options to the synaptics Xorg input driver so that it handles soft buttons, clickpad and multi-touch. It consists in creating /usr/share/X11/xorg.conf.d/75-synaptics.conf with the following contents:
Another feature that was missing was the external subwoofer. A couple of steps are required to enable it, described on the ArchWiki page for another Asus laptop: Asus N55SF Audio. However with strictly this, it still didn't work: I had to step up the Bass Speaker volume in alsamixer (make sure to select the Intel HDA card first) and obviously select the newly created 2.1 Output profile on the GNOME sound config app.
Tuning up GNOME
Since the main graphics chip is an Intel one, graphics acceleration worked out of the box on Debian, and I just ignore the nVidia card for now. That means I had the great pleasure of enjoying the use of GNOME-Shell for some time, but quickly went back to the traditional GNOME-Panel, that I find a lot more convenient for developing. Obviously, a lot of tuning up is needed to make the experience perfect.
I was very surprised with the way GNOME handles both the panel backlight and the keyboard backlight. Basically, these can be dynamically tuned with the media keys (Fn+Something combination keys), which works fine, but none of these changes are kept between sessions and reboots. That means I have to change these after every single login: that is so annying. So my solution was to create an init.d script that stores the backlight values at poweroff and to use a script launched at the GNOME session startup to bring back the previously stored values. It's a shame I have to do this, such basic support should come out of the box with software as popular as GNOME is.
Themes, icons, compositing
Even though it has glitches, I always prefer enabling compositing in metacity so that stuff looks better (transparency, round angles, etc). Since GNOME switched to dconf, here is how I enabled compositing: using dconf-editor: org > gnome > metacity > compositing-manager.
As for the themes I use: Shiki-Colors-Metacity, which is packaged in Debian for window borders, Gnome-brave for icons (packaged as well) and Zukitwo-Sark for GTK+. For the latter, make sure to install its dependencies to avoid unexpected results:
I recently acquired an Allwinner A13 unbranded tablet in order to port Replicant to it: this platform is well supported by free software (the Linux kenrel and the u-boot bootloader) and there is an active community of developers working on free software for the Allwinner A1x platforms: linux-sunxi.
The tablet I ended up with contains an Elan EKTF2000 touchscreen, but I couldn't find any touchscreen driver for it in the linux-sunxi kernel tree: the source code was just not released, even though it's marked as being GPL-licensed. Moreover, since the tablet is unbranded, there was no one I could contact to request source code. So I asked around, and it turned out that nobody knew about source code that would have been released for that touchscreen. However, the tablet came with Android preinstalled and there was an ektf2k.ko module.
After some research, I finally found a driver for elan ktf2000 touchscreens written by HTC. It seemed to match mine (both use I2C) and preliminary tests revealed that the same protocol (on top of I2C) was used by my touchscreen. However, it was not quite enough to write an usable implementation for my device: as a matter of fact, the returned coordinates from my touchscreen did not match the screen size: it reported values up to 896x576 while the screen size is 800x640. So the whole issue was about figuring out these values (896 and 576) at run time in order to scale down to the actual screen size.
The preinstalled Android system came with a kernel module called ektf2k.so which is the actual driver. When loaded, I saw this message on the kernel logs:
[elan] __fw_packet_handler: x resolution: 576, y resolution: 896
Which meant that this driver had the code to get the values from the touchscreen chip.
I quickly understood how the touchscreen protocol works by reading the HTC driver, and it turned out that requests were arrays of 4 bytes, with the first one set to 0x53 (indicating a request) and the second one set to a particular command (indicating what we request). Now considering that requests are usually static tables that are defined in code (that's the way it's done in the HTC driver), declared at the beginning of the function, I knew that the static array of 4 bytes corresponding to the request for the size I needed to find out was held somewhere in the ektf2k.ko module.
Thanks to objdump, I decompiled the module (it is legal to perform such reverse engineering in Europe) and looked at the assembly code for the function __fw_packet_handler. I clearly saw the different calls to elan_ktf2k_ts_get_data and printk, but no sign of the data packets. I then looked at the .rodata section, that contains, as its name suggests, the read-only data, where the packets would likely be stored. The string “__fw_packet_handler” is stored at offset 0170 In this section. Right before, I found the following data:
Looks very much like static arrays of data with the first byte set to 0x53! So I tried issuing requests with the commands 0x00, 0x60, 0x63 and 0xf0 and received the height with 0x60 and the width with 0x63! It was not in the most obvious format but 576 is 0x240 and 896 is 0x380, so it was easy to see that the responses were containing these values.
Quite some time ago, I was given the opportunity to receive a crowd-funded Galaxy S2 phone. Even though I was very thankful for it, I couldn't really focus on it at first since I had to handle other things on various other devices I was working on. It left me somehow sad as I felt that it was my duty to add proper Replicant support for it. Today, I'm proud to announce that the biggest part of the work to support it is over.
The modem (XMM6260)
At first, we had to add support for the modem, an XMM 6260 modem with a custom Samsung firmware. The modem protocol is what we call Samsung IPC, the very same as the one used in the Nexus S or Galaxy S. Our lower-layer library to handle it is libsamsung-ipc, that is shared between Replicant and SHR. So We had to add support for XMM6260 in libsamsung-ipc, along with Galaxy S2-specific bits. Thoughtfully, we designed the upper layer, Samsung-RIL (that is specific to Replicant) to work with libsamsung-ipc regardless of the device it's running on. Nowadays, the modem support is complete and we have working calls, messages and data. Anyway, modem features support is up to Samsung-RIL, so it's not Galaxy S2-specific.
The Audio CODEC (Yamaha MC1N2)
After doing a break in Galaxy S2 development, I finally got back to it, and started the 4.0 Replicant version for the occasion. Since the audio module was non-free in CyanogenMod, it was one of the key components to add support for. (What good is a phone if you can't get any sound out of it?) So after digging a little in the kernel code, it turned out that the Audio CODEC had an ALSA interface driver. That means PCM In/Out interfaces as well as Mixer controls. Only problem was that I still couldn't get any sound out of it using the TinyALSA test utils. After doing a bit of research, I found out about the /dev/snd/hwC0D0 node, that was implementing hardware-specific controls (via ioctl). After adding debug prints to it and with the help of some CyanogenMod developers, I was able to reimplement it on my Yamaha-MC1N2-Audio library. The ALSA part was done with a 4.0 update (call it a complete rewrite) of my TinyALSA-Audio library. The combination of the two made it possible to have sound with Replicant (including during calls). It is even used by CyanogenMod since version 10.1!
The sensors (K3DH accelerometer)
With modem and audio support, the Galaxy S2 was made usable as a phone. Thanks to the free hwcomposer module, it's very fast too, so I decided to use it as a main phone for a time, and frankly quite enjoyed the ride. The sensors were also relying on a non-free library, the one called libakm: AKM is the compass manufacturer. Nonetheless, it includes the bits to properly handle the K3DH accelerometer chip too. The situation is quite similar to the Nexus S sensors, and I was able to figure out the accelerometer part back then (it was a KR3DH) and implemented it in the libakm_free library. Since it was quite easy for Nexus S (libakm was just a passthrough), I gave it a try on the Galaxy S2. After tracing the K3DH kernel driver, I figured that the values returned by libakm were just the result of linear functions applied to the data returned by the kernel. I renamed libakm_free to Samsung-Sensors and added support for the K3DH there.
The cameras (M5MO/S5K5BAFX)
Galaxy S2 support was then already pretty decent, and I was kind of proud of myself. Though, it take a look at the Galaxy S2 characteristics, you'll see that one of its key features is the 8MP camera it embeds. And sadly, there was no usable camera module around. Though, it appeared to have a V4L2 driver, which is pretty standard and easy to implement. However, I feared that I'd have to face the same situation as audio: standard interface but only usable with a non-trivial interface aside. Once again, I traced the kernel driver and started implementing, step by step. After a couple weeks of work (I wrote the implementation from scratch and obviously couldn't spend time on it everyday), it appeared that the original non-free camera module was doing a lot of unnecessary output/overlay operations. So I decided to cut out the crap and get to the essential, that is only using the capture V4L2 interface. This comes with some issues such as the inability to resize/crop the output buffer, but I think I found acceptable workarounds for that. In the end, my camera module turned out to work quite well and is now fully-featured (except EXIF that is currently broken, but it's such a pain in the ass that I don't really want to get into it and fix things). I pushed the code on the Galaxy S2 device tree as well as on my personal Exynos Camera git repo.
Now the Galaxy S2 is supported as well as the Nexus S in Replicant and the missing (and doable) parts left are mainly GPS and compass. The compass is an AKM8975 chip. Some code was released by AKM for this chipset and even though my first attempts to make it work failed, I guess there is a way to have it working properly. I didn't renew my attempts since this is quite a detail and there is probably more important things to work on at the moment. That's for instance the GPS: it's a GSD4t chip, the very same as the Galaxy Nexus. It needs a firmware upload and uses a SiRF-derived protocol that does not seem to be documented anywhere. I hope we'll be able to figure it out somehow: it would be very nice to have GPS support on these two devices!