Introduction

In this series of articles I describe how you can write a Linux loadable kernel module (LKM) for an embedded Linux device. This is the third article in the series — please read:

before moving on to this article, as those articles explain how to build, load and unload loadable kernel modules (LKMs) and character device drivers. Such detail is not repeated in this article.

This article describes how you can write kernel code that interfaces to custom electronics circuits that are attached to the GPIOs of a Linux embedded system, and how you can add enhanced functionality for such devices that is not available by default under embedded Linux. The BeagleBone is used as the deployment platform for this article, and while it is desirable that you use the BeagleBone in order to follow along with this article, it is not a requirement.

DoxygenHTML DoxygenPDF
Click for the HTML and PDF version of the auto-generated Doxygen code documentation

This article is focused on education and training rather than a deep practical need — I believe that if you can read a button press and flash an LED then you are well on your way to interfacing to most digital devices. Therefore, there are three different LKMs described in this article, each with its own individual purpose and needs:

  • Example 1: Button Press, LED Light: In this example an LED lights when a button is pressed — simple! (no not really). To perform this task, the concept of kernel interrupts is introduced and the use of the library of code that can be accessed using linux/gpio.h. This example is used to test the interrupt performance.
  • Example 2: Enhanced Button GPIO Driver: This example is used to introduce kobjects and a mechanism for adding your own entries to Sysfs. This allows you to send data to and receive data from the LKM at run time. The example also introduces the use of timing in kernel code.
  • Example 3: Enhanced LED GPIO Driver: This example is used to flash an LED, which allows for a discussion on Linux kthreads. Essentially, an LED is flashed at a frequency using a kernel module, which can be controlled from Linux user space.

There are easier ways to interface to buttons and flash LEDs but these examples introduce several concepts that are vital to more complex kernel programming tasks.

Video Demonstration

A short YouTube video is provided here that presents an overview of the functionality of the LKMs that are developed in this article.

Video 1: A video of the functionality of the LKMs that are described in this article.

The Circuit

A single circuit is used for this article, as illustrated in Figure 1. It is the same figure that is explained in Chapter 6 of my book and it is presented here for convenience. As described in detail within the chapter, a FET (or small-signal transistor) is used in order to gate the LED, which ensures that the current required to light the LED does not damage the BeagleBone. You can connect an LED directly to the GPIO with a strong current-limiting resistor, but it is not recommended. The push button does not have a pull-up or pull-down resistor, which is appropriate due to the fact that P9_27 is configured to have an internal pull-down resistor enabled by default. Always be very careful that you wire circuits like these correctly — a mistake may destroy your BeagleBone!

Figure 1: The LED and button circuit that is described in this article

Get Source Code

Source Code for this Discussion

Get Source Code

All of the code for this discussion is available in the GitHub repository for the book Exploring BeagleBone. The code can be viewed publicly at: the ExploringBB GitHub Kernel Project directory, and/or you can clone the repository on your BeagleBone (or other Linux device) by typing:

The /extras/kernel/ directories gpio_test, button, and led are the most important directories for this article. The auto-generated Doxygen documentation for these code examples is available in HTML format and PDF format.

Example 1: Button Press, LED Light LKM

When interfacing to electronics circuits under embedded Linux you are immediately exposed to Sysfs and the use of low-level file operations. This approach can appear to be inefficient (especially if you have experience of traditional embedded systems); however, these file entries are memory-mapped and the performance is sufficient for most applications. I have demonstrated in my book that it is possible to achieve response times of about one third of a millisecond, with negligible CPU overhead, from within Linux user space by using ptreads, callback functions and sys/poll.h.

Unlike Linux user space, the Linux kernel space has support for interrupts. The first example in this article demonstrates how you can write an LKM that uses GPIOs and interrupts to achieve a faster response time than is possible in user space. I am not suggesting that you write all of your GPIO code in kernel space, but these examples may provide inspiration for discrete tasks that you can perform in kernel space — the higher-level code can still be written in Linux user space.

GPIOs and the Kernel

General Purpose Input/Outputs (GPIOs) are described in detail in Chapter 6 and in my previous articles/videos. These software-controlled input/outputs can be controlled from Linux user space using the GPIO Sysfs Interface (using a Linux shell directly or from within an executable), which enables you to activate a GPIO and set its state. For example, to activate the LED in Figure 1 using sysfs and turn the LED on/off, you can perform the following steps (as superuser):
root@beaglebone:/sys/class/gpio# ls
export gpiochip0 gpiochip32 gpiochip64 gpiochip96 unexport
root@beaglebone:/sys/class/gpio# echo 49 > export
root@beaglebone:/sys/class/gpio# ls
export gpio49 gpiochip0 gpiochip32 gpiochip64 gpiochip96 unexport
root@beaglebone:/sys/class/gpio# cd gpio49
root@beaglebone:/sys/class/gpio/gpio49# ls
active_low direction edge power subsystem uevent value
root@beaglebone:/sys/class/gpio/gpio49# echo out > direction
root@beaglebone:/sys/class/gpio/gpio49# echo 1 > value
root@beaglebone:/sys/class/gpio/gpio49# echo 0 > value
root@beaglebone:/sys/class/gpio/gpio49# cd ..
root@beaglebone:/sys/class/gpio# echo 49 > unexport
root@beaglebone:/sys/class/gpio# ls
export gpiochip0 gpiochip32 gpiochip64 gpiochip96 unexport

Interestingly, the steps to control the GPIOs in Linux kernel space are very similar to the steps above. Linux GPIOs can easily be accessed and controlled from kernel space using the functions that are described in linux/gpio.h(Version 3.8.x). Here are some of the most important functions that are available through the inclusion of this header file:

Importantly, you can associate an interrupt request (IRQ) with a GPIO using the last function in the list above. IRQs enable you to build efficient, high-performance code that detects a change in the input state — we need to discuss interrupts and their use under the Linux OS next. For further information on GPIO use under Linux, see:

Interrupts

An interrupt is a signal that is sent to a microprocessor from an attached hardware device, software application, or circuit to indicate that an event has occurred which requires attention. Interrupts are high-priority conditions — the term essentially implies “interrupt what you are currently doing and do something instead.” The processor suspends its current activities, saves the current state, and executes an interrupt handler function, which is also known as an interrupt service routine (ISR). Once the handler function has run to completion, the processor reloads its previous state and continues with its previous activities.

The LKM driver must register a handler function for the interrupt, which defines the actions that the interrupt should perform. In this example the handler function is called ebbgpio_irq_handler() and it has the form:

This handler function is then registered with an interrupt request (IRQ) using the request_irq() function as follows:

The irqNumber is determined automatically in the code example in Listing 2 by using the interrupt number that is associated with the respective GPIO number — importantly, the GPIO number is not the interrupt number, however, there is a direct one-to-one mapping.

To undo the IRQ request, there is also a free_irq() function. In this first example, the free_irq() function is called from within the ebbgpio_exit() function, which is invoked when the LKM is unloaded.

In this example a simple momentary push button (as illustrated in Figure 1) is used to generate an interrupt on the rising edge of a button press. It is also possible to generate the interrupt on the falling edge — Listing 1 provides the set of interrupt definitions from /include/linux/interrupt.h. These flags can be combined using the bitwise OR operator to provide precise control over interrupt configuration.

Listing 1: The available interrupts flags (see: /usr/src/linux-headers…/include/linux/interrupt.h)

The full source code for the first LKM is provided in Listing 2. The comments in the code example provide a description of the role of each function. See the GitHub repository directory:/extras/kernel/gpio_test/gpio_test.c. This code example is heavily based on the code that is described in the first two articles in this series.

Listing 2: The GPIO LED Button Test LKM Source Code

The LKM that is described in Listing 2 can be built and loaded using the same steps as previously:
molloyd@beaglebone:~/exploringBB/extras/kernel/gpio_test$ make
make -C /lib/modules/3.8.13-bone70/build/ M=/home/molloyd/exploringBB/extras/kernel/gpio_test modules

molloyd@beaglebone:~/exploringBB/extras/kernel/gpio_test$ ls -l *.ko
-rw-r--r-- 1 molloyd molloyd 5898 Apr 19 18:04 gpio_test.ko
molloyd@beaglebone:~/exploringBB/extras/kernel/gpio_test$ sudo insmod gpio_test.ko
molloyd@beaglebone:~/exploringBB/extras/kernel/gpio_test$ lsmod
Module Size Used by
gpio_test 1379 0

At this point kern.log gives the following kernel log output:
root@beaglebone:/var/log# tail -f kern.log
Apr 19 18:25:23 beaglebone kernel: [174918.090753] GPIO_TEST: Initializing the GPIO_TEST LKM
Apr 19 18:25:23 beaglebone kernel: [174918.094457] GPIO_TEST: The button state is currently: 0
Apr 19 18:25:23 beaglebone kernel: [174918.094472] GPIO_TEST: The button is mapped to IRQ: 243
Apr 19 18:25:23 beaglebone kernel: [174918.094625] GPIO_TEST: The interrupt request result is: 0

Then when the physical momentary push button that is wired in Figure 1 is pressed, the kernel log instantly reacts as follows:
Apr 19 18:29:21 beaglebone kernel: [175156.710964] GPIO_TEST: Interrupt! (button state is 1)
Apr 19 18:29:21 beaglebone kernel: [175156.838235] GPIO_TEST: Interrupt! (button state is 1)
Apr 19 18:29:23 beaglebone kernel: [175157.907436] GPIO_TEST: Interrupt! (button state is 1)
Apr 19 18:29:23 beaglebone kernel: [175158.020004] GPIO_TEST: Interrupt! (button state is 1)

Out of interest, at this point you can view the /proc/interrupts entry and you can see that the name of the interrupt handler is listed as “ebb_gpio_handler“, as configured in the code in Listing 2. You can also see that the interrupt associated with the GPIO has number 243, which aligns with the value that is outputted in the kernel logs above.
molloyd@beaglebone:~/exploringBB/extras/kernel/gpio_test$ cd /proc/
molloyd@beaglebone:/proc$ cat interrupts
  CPU0

 42: 27721 INTC 4a100000.ethernet
 43: 0 INTC 4a100000.ethernet
 64: 0 INTC mmc0
 67: 2052095 INTC gp_timer
 70: 1116 INTC 44e0b000.i2c
 72: 8 INTC
 75: 0 INTC rtc0
 76: 0 INTC rtc0
109: 0 INTC 53100000.sham
134: 0 GPIO mmc0
243: 45 GPIO ebb_gpio_handler

Again, it is important to note that the interrupt number is not the GPIO number, which is 115 for the button and 49 for the LED in the circuit in Figure 1. You can also see that this GPIO number is exported for use by the GPIO functions in Listing 2 (the GPIOs are automatically unexported when the LKM is unloaded):
molloyd@beaglebone:~/exploringBB/extras/kernel$ ls -l /sys/class/gpio/
total 0
--w------- 1 root root 4096 Jan 1 2000 export
lrwxrwxrwx 1 root root 0 Apr 22 21:44 gpio115 -> ../../devices/virtual/gpio/gpio115
lrwxrwxrwx 1 root root 0 Apr 22 21:44 gpio49 -> ../../devices/virtual/gpio/gpio49
lrwxrwxrwx 1 root root 0 Jan 1 2000 gpiochip0 -> ../../devices/virtual/gpio/gpiochip0
lrwxrwxrwx 1 root root 0 Jan 1 2000 gpiochip32 -> ../../devices/virtual/gpio/gpiochip32
lrwxrwxrwx 1 root root 0 Jan 1 2000 gpiochip64 -> ../../devices/virtual/gpio/gpiochip64
lrwxrwxrwx 1 root root 0 Jan 1 2000 gpiochip96 -> ../../devices/virtual/gpio/gpiochip96
--w------- 1 root root 4096 Jan 1 2000 unexport

When the module is unloaded using the command sudo rmmod gpio_test, the logs output:
Apr 19 18:29:35 beaglebone kernel: [175170.252260] GPIO_TEST: The button state is currently: 0
Apr 19 18:29:35 beaglebone kernel: [175170.252278] GPIO_TEST: The button was pressed 4 times
Apr 19 18:29:35 beaglebone kernel: [175170.256753] GPIO_TEST: Goodbye from the LKM!

Performance

Figure 2: The performance of this LKM — button press to LED state change (click image)

One very useful feature of this LKM is that it allows you to evaluate the response time (interrupt latency time) of the system as a whole. A press of the momentary push button results in the inversion of the state of the LED — if the LED is off then it will turn on when the button is pressed. To measure this delay an oscilloscope is used, which is configured to trigger on the rising edge of the button signal. The oscilloscope provides an independent time measurement and its output is displayed in Figure 2.

The green signal represents the button signal and the blue signal represents the LED response signal. In this figure the latency is approximately 17 microseconds. On repeated testing this delay varies between a minimum of 15 microseconds to a maximum of 25 microseconds approximately. The image on the BeagleBone is a regular Debian distribution — this figure will improve in value and deviation if a real-time Linux kernel is used, such as Xenomai.

Debouncing Push Button Switches

Video 2: A video from my YouTube channel on debouncing a momentary push button switch

The mechanical nature of a push button means that when it is pressed, the button contacts will touch the switch contacts, making the circuit, and then bounce back a tiny amount, breaking the circuit momentarily. This mechanical bounce back can result in false pulses, and thus false button presses. Debouncing is the term used to describe the process of removing such false pulses. Importantly, the button in this example is debounced using a software approach. The code uses a gpio_set_bounce() function call to ignore repeated edge transitions for a time period (typically of the order of 100ms to 200ms), once a single transition is detected. You should remove the gpio_set_debounce() function call if you wish to use this code to detect multiple edge transitions on a “clean” digital signal, as software debouncing will severely limit performance. The YouTube video (See Video 2 on the right) describes a hardware approach to debouncing a mechanical switch.

Example 2: Enhanced Button GPIO Driver LKM

The second example builds on the first example to create an enhanced GPIO driver, which permits a user to configure and interact with a GPIO Button using Sysfs. This module allows a GPIO button to be mapped to Linux user space so that you can interact with it. The best way to explain this is with an example — in this case the button is attached to GPIO 115 and it can be accessed and manipulated as follows:
molloyd@beaglebone:/sys/ebb/gpio115$ ls -l
total 0
-r--r--r-- 1 root root 4096 Apr 24 11:52 diffTime
-rw-rw-rw- 1 root root 4096 Apr 24 11:54 isDebounce
-r--r--r-- 1 root root 4096 Apr 24 11:52 lastTime
-r--r--r-- 1 root root 4096 Apr 24 11:52 ledOn
-rw-rw-rw- 1 root root 4096 Apr 24 11:52 numberPresses
molloyd@beaglebone:/sys/ebb/gpio115$ cat numberPresses
55
molloyd@beaglebone:/sys/ebb/gpio115$ cat ledOn
0
molloyd@beaglebone:/sys/ebb/gpio115$ cat lastTime
10:54:48:487088428
molloyd@beaglebone:/sys/ebb/gpio115$ cat diffTime
0.628361750
molloyd@beaglebone:/sys/ebb/gpio115$ echo 0 > isDebounce
molloyd@beaglebone:/sys/ebb/gpio115$ cat isDebounce
0

Despite the complexity involved in creating this LKM, the user interface is very straightforward and can be utilized by an executable on your embedded system that can be written in any programming language.

Sysfs is a memory-based file system that provides a mechanism to export kernel data structures, attributes, and linkages to Linux user space. The earlier section in this article “GPIOs and the Kernel” provides an example of how the /sys/class/gpio entries could be used to manipulate the behavior of kernel data structures to turn on and off an LED. The infrastructure that enables sysfs to function is heavily based on the kobject interface.

The kobject Interface

The driver model in Linux uses a kobject abstraction — to understand this model you must first appreciate the following important concepts [From Greg Kroah-Hartman’s Guide]:

  • kobject: A kobject is a struct that consists of a name, a reference count, a type, a sysfs representation, and a pointer to a parent object (see Listing 3 below). Importantly, kobjects are not useful on their own, rather they are embedded within other data structures and used to control access. This is similar to the object-oriented concept of generalized top-level parent classes (e.g., such as the Object class in Java, or the QObject class in Qt).
  • ktype: A ktype is the type of the object that the kobject is embedded within. It controls what happens when the object is created and destroyed.
  • kset: A kset is a group of kobjects that can be of different ktypes. A kset of kobjects can be thought of as a sysfs directory that contains a collection of sub-directories (kobjects).
Listing 3: The kobject structure

For this example LKM, a single kobject is required, which is mapped to /sys/ebb on the file system. This single kobject contains all of the attributes that are required for the interaction that is demonstrated above (e.g., viewing the numberPresses entry). This is achieved in Listing 4 through the use of the kobject_create_and_add() function, as follows:

The kernel_kobj pointer provides you with a reference to /sys/kernel/. If you remove the call to ->parent then the ebb entry will be placed at /sys/kernel/ebb, but for clarity I have placed it at /sys/ebb — this is not best practice! (Also, sysfs_create_dir() performs the same role).

For this example LKM, a set of subsystem-specific callback functions must be implemented to expose its attributes via sysfs using functions of the form:

When a sysfs attribute is read from or written to, the _show and _store are called respectively. The sysfs.h header file defines the following helper macros that make defining the attributes much more straightforward.

  • __ATTR(_name,_mode,_show,_store): Long-hand version. You must pass the attribute variable name _name, the access mode _mode (e.g., 0666 for read/write access), the pointer to the show function _show, and the pointer to the store function _store.
  • __ATTR_RO(_name): Short-hand read-only attribute macro. You must pass the attribute variable name _name and the macro sets the _mode to be 0444 (read only), and the _show function to be _name_show.
  • __ATTR_WO(_name): and __ATTR_RW(_name) Write only and read/write. Not available in 3.8.x but added in 3.11.x.

The Source Code for this Example

Listing 4 provides the full source code for the Enhanced GPIO Button LKM. It may appear to be quite lengthy, but you will see that this is because I have added a lot of comment, and additional printk() calls so that you can see exactly what is happening as the code is executing. This example builds on the work in Listing 2 — it also includes an LED so that you can observe interaction at the circuit itself.

Listing 4: The Enhanced GPIO Button LKM

The code in Listing 4 is described by the comments throughout; however, there are a few more points that are worth mentioning:

  • Three module parameters are made available to be configured as the LKM is loaded (isRising, gpioButton, and gpioLED). The use of LKM parameters is described in the first article in this series. This allows you to define different GPIOs for the button input and LED output — their sysfs mount names are automatically adjusted. The code also allows for a falling-edge interrupt in place of the rising-edge interrupt that is used by default.
  • There are five attributes associated with the kobject entry (ebb). These are: diffTime, isDebounce, lastTime, ledOn, and numberPresses. They are all read-only, with the exception of isDebounce (i.e., rising-edge or falling-edge) and numberPresses (i.e., can be set to any value, e.g., reset to 0).
  • The ebbgpio_irq_handler() function performs the majority of the timing work. The clock time is stored and the inter-press time is determined each time that the interrupt is handled.

The module can be loaded in falling-edge mode and tested using:
molloyd@beaglebone:~/exploringBB/extras/kernel/button$ make

molloyd@beaglebone:~/exploringBB/extras/kernel/button$ ls -l *.ko
-rw-r--r-- 1 molloyd molloyd 10639 Apr 24 11:50 button.ko
molloyd@beaglebone:~/exploringBB/extras/kernel/button$ sudo insmod button.ko isRising=0
molloyd@beaglebone:~/exploringBB/extras/kernel/button$ cd /sys/ebb/gpio115/
molloyd@beaglebone:/sys/ebb/gpio115$ ls
diffTime isDebounce lastTime ledOn numberPresses
molloyd@beaglebone:/sys/ebb/gpio115$ cat numberPresses
4
molloyd@beaglebone:/sys/ebb/gpio115$ cat ledOn
1
molloyd@beaglebone:/sys/ebb/gpio115$ cat lastTime
16:18:04:249265532
molloyd@beaglebone:/sys/ebb/gpio115$ cat diffTime
1.126586000
molloyd@beaglebone:/sys/ebb/gpio115$ echo 0 > numberPresses
molloyd@beaglebone:/sys/ebb/gpio115$ cat numberPresses
0
molloyd@beaglebone:/sys/ebb/gpio115$ cd ~/exploringBB/extras/kernel/button/
molloyd@beaglebone:~/exploringBB/extras/kernel/button$ sudo rmmod button

The simultaneous output in the kernel logs (/var/log/kern.log) is as follows:
Apr 24 17:17:25 beaglebone kernel: [19844.622090] EBB Button: Initializing the EBB Button LKM
Apr 24 17:17:25 beaglebone kernel: [19844.625986] EBB Button: The button state is currently: 0
Apr 24 17:17:25 beaglebone kernel: [19844.626002] EBB Button: The button is mapped to IRQ: 243
Apr 24 17:18:01 beaglebone kernel: [19879.901265] EBB Button: The button state is currently: 0
Apr 24 17:18:02 beaglebone kernel: [19880.878163] EBB Button: The button state is currently: 0
Apr 24 17:18:03 beaglebone kernel: [19881.778028] EBB Button: The button state is currently: 0
Apr 24 17:18:04 beaglebone kernel: [19882.904615] EBB Button: The button state is currently: 0
Apr 24 17:18:49 beaglebone kernel: [19928.127631] EBB Button: The button was pressed 0 times
Apr 24 17:18:49 beaglebone kernel: [19928.132038] EBB Button: Goodbye from the EBB Button LKM!

Please note that the logs state that the button was pressed 0 times. This is as a result of the call to echo 0 > numberPresses at the Linux terminal above.

Here is some important further reading on this topic:

Warning: ensure that you leave the /sys/ebb directory before unloading the module, otherwise you will cause a kernel panic if you perform an operation such as ls.

Example 3: Enhanced LED GPIO Driver LKM

The final example in this article is a driver for controlling an LED using a LKM. This example is designed to introduce the use of kernel threads, kthreads, which we can start in response to an event that occurs in our LKM.

Kernel Threads

The general structure of the code in this example is provided in Listing 5. This is a reasonably unusual thread in the Linux kernel, as we require a specific sleep time in order to get a consistent flash period. The return of resources to the kthread scheduler is usually performed with a call to schedule().

The call to kthread_run() is quite similar to the user-space pthread function pthread_create() (see page 236 of the book). The kthread_run() call expects a pointer to the thread function (flash() in this case), the data to be sent to the thread (NULL in this case), and the name of the thread, which is displayed in the output from a call to top or ps. The kthread_run() function returns a task_struct which is shared between the various functions within this C file as *task.

Listing 5: An outline of the kthread implementation

The final source code for this LKM is available in Listing 6.

Listing 6: The Enhanced GPIO LED Controller LKM

The comments in Listing 6 provide a full description of the integration of all of the tasks, however, there are a few additional points:

  • An enumeration, called modes is used to define the three possible running states. When you are passing commands to a LKM you have to very carefully parse the data to ensure it is valid and within range. In this example the string command can only be one of three values (“on”, “off”, or “flash”) and the period value must be between 2 and 10,000 (ms).
  • The kthread_should_stop() evaluates to a bool. When a function such as kthread_stop() is called on the kthread then this function will wake and return true. This causes the kthread to run to completion, after which the return value from the kthread will be returned by the kthread_stop() function.

This example can be built and executed using the following steps:
molloyd@beaglebone:~/exploringBB/extras/kernel/led$ make

molloyd@beaglebone:~/exploringBB/extras/kernel/led$ sudo insmod led.ko
molloyd@beaglebone:~/exploringBB/extras/kernel/led$ cd /sys/ebb/led49/
molloyd@beaglebone:/sys/ebb/led49$ ls
blinkPeriod mode
molloyd@beaglebone:/sys/ebb/led49$ cat blinkPeriod
1000
molloyd@beaglebone:/sys/ebb/led49$ cat mode
flash
molloyd@beaglebone:/sys/ebb/led49$ echo 100 > blinkPeriod
molloyd@beaglebone:/sys/ebb/led49$ ps aux|grep LED
root 7042 0.0 0.0 0 0 ? D 18:36 0:00 [LED_flash_threa]
molloyd 7062 0.0 0.1 3100 616 pts/0 S+ 18:37 0:00 grep LED

We can increase the frequency of the flash by reducing the sleep period to be 1ms so that we can observe the CPU loading, using the following call:
molloyd@beaglebone:/sys/ebb/led49$ echo 2 > blinkPeriod
molloyd@beaglebone:/sys/ebb/led49$ ps aux|grep LED
root 7042 0.1 0.0 0 0 ? D 18:36 0:00 [LED_flash_threa]
molloyd 7070 0.0 0.1 3100 616 pts/0 S+ 18:38 0:00 grep LED
molloyd@beaglebone:/sys/ebb/led49$ echo off > mode
molloyd@beaglebone:/sys/ebb/led49$ echo on > mode
molloyd@beaglebone:/sys/ebb/led49$ cd ~/exploringBB/extras/kernel/led/
molloyd@beaglebone:~/exploringBB/extras/kernel/led$ sudo rmmod led

You can see that the loading is quite small at 0.1% of CPU when it is flashing with a sleep duration of 1ms. The signal output can be observed in Figure 3 to Figure 6 for different period values.

The kernel log output for this example is as follows:
Apr 24 18:36:30 beaglebone kernel: [24588.981157] EBB LED: Initializing the EBB LED LKM
Apr 24 18:36:30 beaglebone kernel: [24588.987821] EBB LED: Thread has started running
Apr 24 18:40:57 beaglebone kernel: [24856.188934] EBB LED: Thread has run to completion
Apr 24 18:40:57 beaglebone kernel: [24856.190471] EBB LED: Goodbye from the EBB LED LKM!

The results for this approach are quite impressive when compared to similar tests in Linux user space. The results have a consistent ~50% duty cycle and the range of frequency values is quite consistent. For example, Figure 6 represents the performance of the approach when the sleep delay is set to 1ms, which results in a period of approximately 7.8ms. The frequency ranges from 127.935Hz to 128.07Hz, which is a variation of +/- 0.05%. This takes place with a thread CPU load of less than 0.1%. Higher frequencies are possible but there will be greater variation in the pulse widths and perhaps the PRU-ICSS on the BeagleBone is better suited to such tasks.

Figure 4: The performance of the LED flasher LKM at 25ms sleep delay
Figure 3: The performance of the LED flasher LKM at 50ms sleep delay
Figure 6: The performance of the LED flasher LKM at 1ms sleep delay
Figure 5: The performance of the LED flasher LKM at 5ms sleep delay

Conclusions

DoxygenHTML DoxygenPDF
Click for the HTML and PDF version of the auto-generated Doxygen code documentation

Remember that the kernel is essentially a program — a big and complex program, but a program nevertheless. It is possible to make changes to the kernel code, recompile, redeploy, and then reboot — a lengthy process. Linux loadable kernel modules (LKMs) allow you to create binary code that can be loaded and unloaded from the kernel at runtime. Hopefully the last three articles have made it clear how you can build custom functionality into your Linux kernel using LKMs.

I will add further articles to this series over time as needs arise and suitable embedded systems applications are identified.