This week, the nature of my main task changed a little. From developing the remoteproc driver for X86 architecture I’m now required to do it for the raspberry pi which uses an ARM processor. This is because the main product of our company is not completely ready, and my supervisor wanted me to test my findings on something solid to really prove that the remoteproc framework is 100% usable. The compilation technique also changed because of this. Now I have to do a cross-compilation which means that I use my x86 platform to compile an ARM-compatible linux module. The process of kernel module cross-compilation is a bit complicated. So, I’ll just share my experience here so that everybody can use it and compile their kernel module more easily.
As we all know, to compile a module for a certain kernel version, we must have the pre-built kernel image and its source code ready. Then, we’ll just have to compile our module against it. It is the same here, we need to build a rasbian kernel image. Here are the steps:
- Download the proper kernel source (from raspbian github)
- Copy the “.config” file from our running raspbian os (on the raspberry pi) to the root of the kernel source
- Compile the kernel (using ARM compiler) use this line to compile our kernel module: make ARCH=arm CROSS_COMPILE=compiler_location -C /kernel_location M=$(PWD) modules
- Make sure when you write ONLY the prefix (without gcc at the end) of your compiler when specifying its location to CROSS_COMPILE option
- You are ready to cross-compile your kernel module using Make! The above are the general steps to cross-compile a kernel module.
But sometimes there will be some problem such as your module needs a symbol which doesn’t exist in the kernel. Let’s say you want to compile two kernel modules A and B. A will export a symbol FOR_B, and B needs this symbol for its operation. Both of them are not in the kernel, so the kernel doesn’t have FOR_B symbol. You can compile the module A without any problem but you WILL certainly have problem to compile kernel B because the symbol FOR_B is not available. What you can do is by copying a special file called “module.Symvers” (which is generated when you compile kernel A) into your kernel B source folder and it will compile flawlessly because it knows that the symbol FOR_B is available.
This is the technique that I used to compile my remoteproc module. I didn’t want to compile because certain symbols from the VirtIO module were missing. After building all the necessary modules, I copy them to the Raspberry Pi using scp, and insert the module from by SSHing into The Raspberry Pi. Now the remoteproc framework is running on the Raspberry Pi, I need to figure out how to do the interface part.
At first, my supervisor asked me to do the interface between 2 Raspberry Pi using the SPI. After doing some research on this, I found out that Raspberry Pi only supports SPI master mode. This means that a Rpi-Rpi connection using SPI interface is impossible because SPI requires one to be the master device and other to be the slave. In reality, Rpi does support SPI slave mode but the problem is that Rpi’s circuit board designer decided not to make all the SPI slave mode’s pins available to the users, maybe because of limited space.
With SPI being not usable, there are 2 choices left: GPIO bit banging and UART. I chose to go for the GPIO bit bang because UART’s speed is too slow to act as a data and address bus. There are 17 GPIOs that can be exploited on the Raspberry Pi. To build a usable data bus, we need at least a R/W bit and an EN (enable bit). Another 8 pins will be used to multiplex between data and bus (32 bits data will be sent in a series of 4 bytes). So, my next task is to develop a driver for this GPIO. I’ll start with something simple first and then progress to something more complicated!