Kernel code is a moving target

As new and improved kernels go into use, some of the kernel module code has to be changed along with them. Code that compiled and ran on an older kernel might have contained items that have since been removed.

For example, the send_sig_info(signalnumber, SEND_SIG_FORCE, task_from_oid) call which sends a signal from the kernel to a process in user-space, failed to compile with kernel versions 5.x, because the SEND_SIG_FORCE has been deprecated and removed. So it will now have to use SEND_SIG_PRIV instead.

Kernel-hacking notes:

For just making modules, on the old longsleep systems, the necessary background kernel items were already present under /usr/src/ in one of the directory trees there.

For Armbian, there is a package that can be installed, via armbian-config or apt install linux-headers-next-sunxi64. This puts the required files into the /usr/src in a predictable directory there.

Once the files are in place, in order to access them, the Kbuild system expects there to exist an environment variable KERNEL_SRC that specifies the header root directory. To enable this, with the simple Kbuild files, add the following into ~/.profile and log-in again:

export KERNEL_SRC=/usr/src/linux-headers-`uname -r`

This will make the definition match the current kernel, for example, when seen via env:

KERNEL_SRC=/usr/src/linux-headers-3.10-104-1-pine64-longsleep

As the kernel gets updated, the corresponding files here get added as well so uname -a or uname -r will be usefult to determine which one is the right one.

The entire Kbuild file looks like the following:

MODULE=accumulator

obj-m:= ${MODULE}.o

all:
	make -C ${KERNEL_SRC} M=$(PWD) modules

clean:
	make -C ${KERNEL_SRC} M=$(PWD) clean

for the example accumulator device. I have others that are more sophisticated. But this makes the kernel development really easy, as there is no need for the runaround that is required with Raspbian.

Although the availability of this has been changing some recently, on the ayufan release 0.7.28 at least, maybe later ones, this is straighforward and in place.

Using source

Pulling kernel source and making custom drivers as modules is rather similar to what we do on Raspberry Pi, but the process here seems somewhat simpler, in that the latest kernel is readily available. Some ideas I found here: Info on compiling kernel other is based on previous work.

This presumes that the kernel in place is one maintained by longsleep, as can be found through the page http://forum.pine64.org/showthread.php?tid=497

Now here is what I have been doing here, to get the source of the kernel, and recompiling it so that I can make my own modules to work. In the following, # prefixed means the command is run as root, $ prefix means the command is run by me, the non-root user.

First, get the latest version of kernel and everything else:

# pine64_update_uboot.sh
# pine64_update_kernel.sh
# apt-get update
# apt-get dist_upgrade
# apt-get upgrade
# reboot

There are also some pre-requisites

# apt-get install bc curl gcc git libncurses5-dev make u-boot-tools

While at this, can also get documentation, all the various manpages describing functions both inside and outside the kernel.

# apt-get install manpages 
# apt-get install manpages-dev 
# apt-get install linux-manual-3.16

Now we can make space for the kernel. I made a directory, ~/kernel, then put the source in a directory tree below that.

$ cd
$ mkdir kernel
$ cd kernel
$ git clone --depth 1 --single-branch -b pine64-hacks-1.2 https://github.com/longsleep/linux-pine64

This places the most recent kernel source in a tree of directories from linux-pine64 on down. Now we pull the existing configuration

$ cp /proc/config.gz .
$ gunzip config.gz
$ cd linux-pine64
$ make mrproper
$ cp ../config .config
$ make oldconfig

I first tried just

$ make -j 4
$ make -j 4 modules

and received an error message about some module not fully supported, and that I should use CONFIG_DEBUG_SECTION_MISMATCH=y to get more info. So I tried with that:

$ make -j 4 CONFIG_DEBUG_SECTION_MISMATCH=y
$ make -j 4 modules

and although it worked now, I didn't get any further info. Nevermind, I have the Module.symvers file where it should be, and with the necessary info in it. In order to proceed, now the module-development has to be pointed to the place where these kernel source files are, and thus, in my ~/.profile I will have to add:

KERNEL_SRC=/home/kerneldev/kernel/linux-pine64

This takes effect after a log-out and subsequent log-in.

The module source and makefile are now in some subdirectory, called accumulator here

I prefer to have the makefile for the module called Kbuild and any associated non-kernel library code have its makefile called just makefile or Makefile.

For testing I used the accumulator-code that would receive interrupt signals on some GPIO wire and add some value to a register for each of these, then occasionally add some additional value. These are configurable, and the driver also allows us to read it, to see what the total is.

I originally used this for the filtered water dispenser, as the 1/100 L pulses from the flowmeter came in too fast to be handled by the sysfs code. Relay control of the solenoid valve remained with sysfs.

The entire Kbuild file looks like the following:

MODULE=accumulator

obj-m:= ${MODULE}.o

all:
	make -C ${KERNEL_SRC} M=$(PWD) modules

clean:
	make -C ${KERNEL_SRC} M=$(PWD) clean

The accumulator source is longer, but basically the same as the one I have been using for the Raspberry Pi. There is one interesting limitation though, my unmodified source referred to signals coming in on pin P15 which on the Raspberry Pi is GPIO22, and on the Pine64 is PC12 or GPIO76.

Kernel GPIO numbering follows the same pattern as we already saw with the sysfs. (/sys/class/gpio) earlier.

Sitting in my dsrc/accumulator directory, in order to compile this module, I do

$ make -f Kbuild

This should generate a bunch of new files, the interesting one is named accumulator.ko and this is the module file. Now we try inserting that using insmod. We have to be root for this -- I find it easiest to have two terminal windows up, one as root and one as non-root.

# insmod accumulator.ko
insmod: ERROR: could not insert module accumulator.ko: Invalid parameters

Oops? What happened here? does dmesg have anything? Yes...

[77363.692131] accumulator.c(161): GPIO 76 to IRQ mapping failure -22

So the good news is that the module code is the correct version and can be workable, the bad news is that it doesn't seem to support any interrupt action on PC12 equivalent to GPIO76. And indeed, as was found earlier, not all of the GPIOs do support interrupts. The ones in the PB, PG, PH and PL series do, while the ones in the PC, PD, PE, PF series do not. So let us try with PH9, GPIO233, which is pin P13.

# insmod accumulator.ko
# 

No news is good news?

# dmesg | tail
[78050.181610] accumulator.c(168): Mapped int 314 from gpio-line 233
[78050.189074] accumulator.c(186): Info: gpio-line 233
[78050.194531] accumulator.c(202): Inserting accumulator module major no 248
[78050.202305] accumulator.c(203): Now do 'mknod /dev/accumulator c 248 0'
#

Indeed. It looks like it was happy with me wanting to use this gpio line.

$ lsmod
Module                  Size  Used by
accumulator            12643  0 
...
$

And the module has made it in. Concept proven.

To Pine 64+ main page


Powered by Apache

Valid XHTML 1.0!