Friday, October 17, 2014

Compiling and Installing the Linux Kernel

The first few times I tried to boot from a custom kernel, I faced a lot of problems and found documentation to be a spread out in different places and also somewhat incomplete depending on the specific distribution. This guide will document the steps to be followed to get a custom kernel running, focusing on Ubuntu and Debian derivatives. The steps followed however, are almost completely, distribution neutral, and hence should be applicable for whichever distro you may be using.


Pre-requisites

I will be using the kernel tree from Kernel.org for this post. If you have a specific version, then great! Otherwise, you can clone the kernel tree using:
$ git clone https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
(This requires git, but you have that installed, don't you?)

Compilation 

For the compilation process, we first need to create the configuration file. This file specifies the various properties of the kernel, including which modules to be compiled, which to be included in the image, and which to be loaded dynamically. You can, depending on your configuration, go for a modular or a monolithic kernel from here. You can either create one using the kernel utilities, or simply use the one that comes with your distro and which the current kernel is using.
$ ls /boot/*config*`uname -r` -a
Copy the config file to your kernel root directory and rename it to .config
$ cp /boot/*config*`uname -r` ./.config
Now, run make to verify the configuration file for the current distribution. You will see a number of new options that have been added into the new kernel. I would suggest simply going with the defaults on most of them, unless you know what you are doing.
$ make oldconfig
We are ready to start compiling. You simply need to run make. To make things go faster, we can make make spawn multiple jobs. Usually, 2 per core is a good number to go by. I also like to time how long it takes, hence my command is similar to:
$ time make -j$((`nproc`*2)) > ./.build-feedback
The output redirection will save all the build feedback into a file that you come back and read when compilation is over. Oh yes, it's gonna be awhile.

Installation

Assuming there weren't any errors during compile time, you are good to install your flashy new kernel. The installation process is simple and involves installing modules, copying files to the correct directories and updating your bootloader.

Installing modules is a one liner:
% make modules_install
Note at the end of the install process, you will see a version number. Copy it, or use the following command:
$ KVER=`ls /lib/modules -ht | head -n 1`
Now, we need to copy the kernel image to the /boot directory. You will find it under /arch corresponding to your machine architecture. For seeing your machine architecture, run:
$ uname -m
Or we can copy the correct image with this one liner:
% cp ./arch/`uname -m`/boot/bzImage /boot/vmlinuz-$KVER
Next, we need to copy the system map, which stores information regarding the built modules and their relative addresses for dynamic loading.
% cp System.map /boot/System.map-$KVER
Next, comes the config file.
% cp .config /boot/config-$KVER
If your distro is configured to use an initramfs, which can easily be confirmed by the presence (or absence) of initrd files in /boot, then you need to generate it using:
% update-initramfs -c -k $KVER
The last step is updating GRUB, so that the new kernel can be added to the boot list.
% update-grub2

Verification

Let's boot into the new kernel now. As the new kernel should be at the top of the GRUB boot list, you should automatically be booted into it. Once you have a usable desktop, which may or may not happen, open up a terminal, or switch to a virtual terminal, and verify your install version (should match with KVER).
$ uname -r 
Congratulations! You are now using your compiled kernel.


This process can take upto 2.5 hours the first time you attempt it, with successive attempts taking upto an hour depending on your workstation. Nonetheless, it is an exciting process and your first step transitioning towards kernel development.

Saturday, October 11, 2014

Raspbmc Controller using PiTFT



Installed PiTFT in LumenEd devices
While working at LumenEd, I got the chance to work on with a lot of different peripherals and chips that go with the Raspberry Pi. One of those items was the PiTFT touchscreen.

Our goal was to develop a control application, similar to a remote, that would handle all user inputs to RaspBMC, our OS of choice. After a few weeks of tinkering, design and development, we were able to put together a simple, yet sufficient, interface, that has been deployed on our devices. This post explores the bare minimum that was needed to get things working together. To see the device in action, take a look at an
early demo video.

The software application for PiTFT is available on Github. It has been developed in Python, due to the extensive support that it has on the Pi, as well as the reduced development time that it provides (which was a priority for us during the development phase). We have used Pygame as our GUI library, for similar reasons. Also, it depends on TSLIB, for touchscreen input, and fbcon, for output to the touchscreen frame buffer.


Moving to the touchscreen itself; getting it working turned out to be a pit of a puzzle at first. Over the period of 4 days however, we were able to figure out all that was needed for it to perform reasonably well in RaspBMC. Firstly, it is necessary to calibrate the touchscreen for optimal touch performance. This is easy to do once the necessary libraries have been installed. Next, we need to load the calibration profile from within the application. This requires the use of environment variables, specifically the first four as shown below. These variables specify the input driver to use, the configuration of the hardware, and the calibration constants. For Pygame to interpret and process the input correctly, we also need to specify the SDL parameters. The parameter names are self explanatory.

os.environ["TSLIB_TSDEVICE"] = "/dev/input/event0"
os.environ["TSLIB_TSEVENTTYPE"] = "INPUT"
os.environ["TSLIB_CONFFILE"] = "/etc/ts.conf"
os.environ["TSLIB_CALIBFILE"] = "/etc/pointercal"
os.environ["SDL_FBDEV"] = "/dev/fb1"
os.environ["SDL_MOUSEDEV"] = os.environ["TSLIB_TSDEVICE"]
os.environ["SDL_MOUSEDRV"] = "TSLIB"
os.environ["SDL_VIDEODRIVER"] = "fbcon"
os.environ["SDL_AUDIODRIVER"] = "alsa"

Another aspect of the GUI design, was the need for elements such as buttons. Pygame, surprisingly, lacks this basic GUI element. Hence, we wrote a custom class for buttons, and certain functions for some other elements as well.
Navigation Menu
Playback Control











At this point, we started to look at the performance of the application. We saw extremely high CPU usage which given the size and simplicity of the display elements and rendering, seemed counter intuitive. Nonetheless, two optimizations were incorporated and this resulted in a drop in the CPU usages to almost a eighth of the original values. One optimization was to reduce the refresh rate to the minimum possible which can be around 4-5 fps. This does not interfere with input responsiveness as the event generation is independent of the screen updation. The other optimization was to reduce the update area to only those sections that were actually changing on a per second basis. This in itself, resulted in a significant performance improvement.

 The last part of the application was forwarding the inputs to XBMC and querying specific parameters to update the touchscreen display. XBMC features an extensive JSON API accessible via the web server. With the help of a wrapper function, the API calls became short and simple. The latency for these calls was also reasonable short on the local server, making the application responsive.

def nav_down():
method = 'Input.Down'
getJsonRemote(method)

Overall, the application was interesting to develop and we are very happy with its performance till date. If you would like to experiment with this application, then download version 1.0. Later versions have been extended and integrated with device-specific hardware, and hence will not be usable externally. The current versions support a camera interface, and a battery display as well.

 Lastly, if you happen to be starting with the Raspberry Pi, I would certainly encourage you to get a hold of the PiTFT and start hacking. It will make for an interesting learning experience about embedded Linux architecture. Happy hacking!