Migrating an embedded Android setup: Porting the Kernel Driver (Part 2)

After getting the display up and running, we’ll have a look at the kernel drivers. It would be way too much work describing each kernel driver in detail, so I will concentrate on the changes needed to port them to the newer kernel version, 3.14 to be exact. A more thorough introduction to the sensor driver and the whole sensor integration can be found here. Of course I learned a lot since I wrote my previous series of articles so I improved the driver quite a bit. Both devices are connected to the Wandboard via the I2C-bus, so they are working really similar at this level. Just controlling it, reading data and sleep management differs for each device.

The LCD in question

Let’s continue with the changes we needed to apply to port each driver to the newer kernel version 3.14. In total, there are three drivers, one for the ultrasonic sensor, one for the Grove LCD RGB backlight and one for its text. As an example, we’ll use the RGB backlight driver.

The first issue is ARM-related. Almost every single board has another layout, other interfaces available and nothing has defined addresses as compared to x86. Older Linux Kernels used so-called board description files, hard coded to the specific board kernel to handle this situation. But because that method is quite inflexible and each board needs a unique, non-reusable kernel, the device tree has now replaced it.

The idea behind the device tree is to be able to use one Linux Kernel compiled for ARM (e.g. v7) on each board with dynamically loadable files describing the board’s hardware interfaces, connected devices and so on. It is a very interesting and powerful system that enables you, amongst others, to expand or change a development board’s connections at run-time with so called device tree overlays. In short, that means it is e.g. possible to enable or disable an UART port without rebooting the board.

This also influences the way we match our I2C devices with the right drivers. Luckily, the changes are small and easy to understand.

This is a snippet from an old board description file, describing a fixed allocation between I2C bus no. 2, the I2C device with the address 0x62 and a driver with the name „rgb1313M1“.

In the device tree system, this structure moved into the driver file, while driver’s init function get extended by this parts:

The first one indicates the I2C bus we hope to find in the device, the second one matches the information about the device’s address, the right driver and the right I2C bus. This bus information was previously coded in the board description file at the board info structure. You see, to change something at this point, less files than before need to be touched. In my opinion, the device tree is a significant improvement as long as you do not have to modify it.

Sleep, where did you go?

The second, really big change in kernel space depends on sleep management. As you may know, there were significant modifications in Android 5 and 6 regarding this issue, but especially in the kernel everything depending on sleep changed.

If you missed this development, you may ask yourself what happened to the old android power management system. It seems everything sleep related vanished.

Previously, we had earlysuspend as a exclusive power management system in the Android kernel patch set. After the power management was completely rewritten, we now use runtime_pm infrastructure for all kernel related power management. With this, we got a consistent power management for both, Linux and Android, which is more flexible than earlysuspend. Because runtime_pm is not able to handle power management while a device is in use, there is a setInteractive hook in the HAL (Hardware Abstraction Layer) power module that could be used. Our use case however only requires runtime_pm, so I will just write about the kernel space changes. If you are more interested in the power management changes, just look at these sources:

The old system needed a lot of preprocessor directives due to being a patch set exclusively for Android. Instead of this, runtime_pm is used in both, Linux and Android, and we do not have to implement a complete fallback system.

With a structure, we specify pointers to the implemented suspend/resume functions. The whole power management for our driver is activated in its probe function. While it is not very impressive, at least it is clearer than earlysuspend. The tricky part is that runtime_pm works like a queue. Any instance that operates on the driver is registered in the queue. As long as this queue is not empty, the driver stays awake. Once an instance releases the driver, it tests if the queue is empty. If that is the case, the suspend function is called and the driver goes to sleep. You can take a look at this below.

Read on

In the next article we’ll have a look at HAL and Android itself. In the meantime, have a look at our Smart Devices division and learn how you can join us as a software developer.

comments powered by Disqus