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.



Click for the HTML and PDF version of the auto-generated Doxygen code documentation 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.

: 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 . 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.

: 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.

The Circuit

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

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!

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: molloyd@beaglebone:~$ sudo apt-get install git molloyd@beaglebone:~$ git clone https://github.com/derekmolloy/exploringBB.git 1 2 molloyd@beaglebone:~$ sudo apt-get install git molloyd@beaglebone:~$ git clone https://github.com/derekmolloy/exploringBB.git 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

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

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:

static inline bool gpio_is_valid(int number) // check validity of GPIO number (max on BBB is 127) static inline int gpio_request(unsigned gpio, const char *label) // allocate the GPIO number, the label is for sysfs static inline int gpio_export(unsigned gpio, bool direction_may_change) // make available via sysfs and decide if it can change from input to output and vice versa static inline int gpio_direction_input(unsigned gpio) // an input line (as usual, return of 0 is success) static inline int gpio_get_value(unsigned gpio) // get the value of the GPIO line static inline int gpio_direction_output(unsigned gpio, int value) // value is the state static inline int gpio_set_debounce(unsigned gpio, unsigned debounce) // set debounce time in ms (platform dependent) static inline int gpio_sysfs_set_active_low(unsigned gpio, int value) // set active low (invert operation states) static inline void gpio_unexport(unsigned gpio) // remove from sysfs static inline void gpio_free(unsigned gpio) // deallocate the GPIO line static inline int gpio_to_irq(unsigned gpio) // associate with an IRQ 1 2 3 4 5 6 7 8 9 10 11 static inline bool gpio_is_valid ( int number ) // check validity of GPIO number (max on BBB is 127) static inline int gpio_request ( unsigned gpio , const char * label ) // allocate the GPIO number, the label is for sysfs static inline int gpio_export ( unsigned gpio , bool direction_may_change ) // make available via sysfs and decide if it can change from input to output and vice versa static inline int gpio_direction_input ( unsigned gpio ) // an input line (as usual, return of 0 is success) static inline int gpio_get_value ( unsigned gpio ) // get the value of the GPIO line static inline int gpio_direction_output ( unsigned gpio , int value ) // value is the state static inline int gpio_set_debounce ( unsigned gpio , unsigned debounce ) // set debounce time in ms (platform dependent) static inline int gpio_sysfs_set_active_low ( unsigned gpio , int value ) // set active low (invert operation states) static inline void gpio_unexport ( unsigned gpio ) // remove from sysfs static inline void gpio_free ( unsigned gpio ) // deallocate the GPIO line static inline int gpio_to_irq ( unsigned gpio ) // associate with an IRQ

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:

static irq_handler_t ebbgpio_irq_handler(unsigned int irq, void *dev_id, struct pt_regs *regs) { // the actions that the interrupt should perform … } 1 2 3 static irq_handler_t ebbgpio_irq_handler ( unsigned int irq , void * dev_id , struct pt_regs * regs ) { // the actions that the interrupt should perform … }

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

result = request_irq(irqNumber, // The interrupt number requested (irq_handler_t) ebbgpio_irq_handler, // The pointer to the handler function (above) IRQF_TRIGGER_RISING, // Interrupt is on rising edge (button press in Fig.1) "ebb_gpio_handler", // Used in /proc/interrupts to identify the owner NULL); // The *dev_id for shared interrupt lines, NULL here 1 2 3 4 5 result = request_irq ( irqNumber , // The interrupt number requested ( irq_handler_t ) ebbgpio_irq_handler , // The pointer to the handler function (above) IRQF_TRIGGER_RISING , // Interrupt is on rising edge (button press in Fig.1) "ebb_gpio_handler" , // Used in /proc/interrupts to identify the owner NULL ) ; // The *dev_id for shared interrupt lines, NULL here

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)

#define IRQF_TRIGGER_NONE 0x00000000 #define IRQF_TRIGGER_RISING 0x00000001 #define IRQF_TRIGGER_FALLING 0x00000002 #define IRQF_TRIGGER_HIGH 0x00000004 #define IRQF_TRIGGER_LOW 0x00000008 #define IRQF_TRIGGER_MASK (IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW | \ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING) #define IRQF_TRIGGER_PROBE 0x00000010 #define IRQF_DISABLED 0x00000020 // keep irqs disabled when calling the action handler. #define IRQF_SHARED 0x00000080 // allow sharing the irq among several devices #define IRQF_PROBE_SHARED 0x00000100 // set by callers when they expect sharing mismatches to occur #define __IRQF_TIMER 0x00000200 // Flag to mark this interrupt as timer interrupt #define IRQF_PERCPU 0x00000400 // Interrupt is per cpu #define IRQF_NOBALANCING 0x00000800 // Flag to exclude this interrupt from irq balancing #define IRQF_IRQPOLL 0x00001000 // Interrupt is used for polling #define IRQF_ONESHOT 0x00002000 // Interrupt is not reenabled after the hardirq handler finished. #define IRQF_NO_SUSPEND 0x00004000 // Do not disable this IRQ during suspend #define IRQF_FORCE_RESUME 0x00008000 // Force enable it on resume even if IRQF_NO_SUSPEND is set #define IRQF_NO_THREAD 0x00010000 // Interrupt cannot be threaded #define IRQF_EARLY_RESUME 0x00020000 // Resume IRQ early during syscore instead of at device resume time. #define IRQF_TIMER (__IRQF_TIMER | IRQF_NO_SUSPEND | IRQF_NO_THREAD) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #define IRQF_TRIGGER_NONE 0x00000000 #define IRQF_TRIGGER_RISING 0x00000001 #define IRQF_TRIGGER_FALLING 0x00000002 #define IRQF_TRIGGER_HIGH 0x00000004 #define IRQF_TRIGGER_LOW 0x00000008 #define IRQF_TRIGGER_MASK (IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW | \ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING ) #define IRQF_TRIGGER_PROBE 0x00000010 #define IRQF_DISABLED 0x00000020 // keep irqs disabled when calling the action handler. #define IRQF_SHARED 0x00000080 // allow sharing the irq among several devices #define IRQF_PROBE_SHARED 0x00000100 // set by callers when they expect sharing mismatches to occur #define __IRQF_TIMER 0x00000200 // Flag to mark this interrupt as timer interrupt #define IRQF_PERCPU 0x00000400 // Interrupt is per cpu #define IRQF_NOBALANCING 0x00000800 // Flag to exclude this interrupt from irq balancing #define IRQF_IRQPOLL 0x00001000 // Interrupt is used for polling #define IRQF_ONESHOT 0x00002000 // Interrupt is not reenabled after the hardirq handler finished. #define IRQF_NO_SUSPEND 0x00004000 // Do not disable this IRQ during suspend #define IRQF_FORCE_RESUME 0x00008000 // Force enable it on resume even if IRQF_NO_SUSPEND is set #define IRQF_NO_THREAD 0x00010000 // Interrupt cannot be threaded #define IRQF_EARLY_RESUME 0x00020000 // Resume IRQ early during syscore instead of at device resume time. #define IRQF_TIMER (__IRQF_TIMER | IRQF_NO_SUSPEND | IRQF_NO_THREAD)

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 GPIO LED Button Test LKM Source Code /** * @file gpio_test.c * @author Derek Molloy * @date 19 April 2015 * @brief A kernel module for controlling a GPIO LED/button pair. The device mounts devices via * sysfs /sys/class/gpio/gpio115 and gpio49. Therefore, this test LKM circuit assumes that an LED * is attached to GPIO 49 which is on P9_23 and the button is attached to GPIO 115 on P9_27. There * is no requirement for a custom overlay, as the pins are in their default mux mode states. * @see http://www.derekmolloy.ie/ */ #include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/gpio.h> // Required for the GPIO functions #include <linux/interrupt.h> // Required for the IRQ code MODULE_LICENSE("GPL"); MODULE_AUTHOR("Derek Molloy"); MODULE_DESCRIPTION("A Button/LED test driver for the BBB"); MODULE_VERSION("0.1"); static unsigned int gpioLED = 49; ///< hard coding the LED gpio for this example to P9_23 (GPIO49) static unsigned int gpioButton = 115; ///< hard coding the button gpio for this example to P9_27 (GPIO115) static unsigned int irqNumber; ///< Used to share the IRQ number within this file static unsigned int numberPresses = 0; ///< For information, store the number of button presses static bool ledOn = 0; ///< Is the LED on or off? Used to invert its state (off by default) /// Function prototype for the custom IRQ handler function -- see below for the implementation static irq_handler_t ebbgpio_irq_handler(unsigned int irq, void *dev_id, struct pt_regs *regs); /** @brief The LKM initialization function * The static keyword restricts the visibility of the function to within this C file. The __init * macro means that for a built-in driver (not a LKM) the function is only used at initialization * time and that it can be discarded and its memory freed up after that point. In this example this * function sets up the GPIOs and the IRQ * @return returns 0 if successful */ static int __init ebbgpio_init(void){ int result = 0; printk(KERN_INFO "GPIO_TEST: Initializing the GPIO_TEST LKM

"); // Is the GPIO a valid GPIO number (e.g., the BBB has 4x32 but not all available) if (!gpio_is_valid(gpioLED)){ printk(KERN_INFO "GPIO_TEST: invalid LED GPIO

"); return -ENODEV; } // Going to set up the LED. It is a GPIO in output mode and will be on by default ledOn = true; gpio_request(gpioLED, "sysfs"); // gpioLED is hardcoded to 49, request it gpio_direction_output(gpioLED, ledOn); // Set the gpio to be in output mode and on // gpio_set_value(gpioLED, ledOn); // Not required as set by line above (here for reference) gpio_export(gpioLED, false); // Causes gpio49 to appear in /sys/class/gpio // the bool argument prevents the direction from being changed gpio_request(gpioButton, "sysfs"); // Set up the gpioButton gpio_direction_input(gpioButton); // Set the button GPIO to be an input gpio_set_debounce(gpioButton, 200); // Debounce the button with a delay of 200ms gpio_export(gpioButton, false); // Causes gpio115 to appear in /sys/class/gpio // the bool argument prevents the direction from being changed // Perform a quick test to see that the button is working as expected on LKM load printk(KERN_INFO "GPIO_TEST: The button state is currently: %d

", gpio_get_value(gpioButton)); // GPIO numbers and IRQ numbers are not the same! This function performs the mapping for us irqNumber = gpio_to_irq(gpioButton); printk(KERN_INFO "GPIO_TEST: The button is mapped to IRQ: %d

", irqNumber); // This next call requests an interrupt line result = request_irq(irqNumber, // The interrupt number requested (irq_handler_t) ebbgpio_irq_handler, // The pointer to the handler function below IRQF_TRIGGER_RISING, // Interrupt on rising edge (button press, not release) "ebb_gpio_handler", // Used in /proc/interrupts to identify the owner NULL); // The *dev_id for shared interrupt lines, NULL is okay printk(KERN_INFO "GPIO_TEST: The interrupt request result is: %d

", result); return result; } /** @brief The LKM cleanup function * Similar to the initialization function, it is static. The __exit macro notifies that if this * code is used for a built-in driver (not a LKM) that this function is not required. Used to release the * GPIOs and display cleanup messages. */ static void __exit ebbgpio_exit(void){ printk(KERN_INFO "GPIO_TEST: The button state is currently: %d

", gpio_get_value(gpioButton)); printk(KERN_INFO "GPIO_TEST: The button was pressed %d times

", numberPresses); gpio_set_value(gpioLED, 0); // Turn the LED off, makes it clear the device was unloaded gpio_unexport(gpioLED); // Unexport the LED GPIO free_irq(irqNumber, NULL); // Free the IRQ number, no *dev_id required in this case gpio_unexport(gpioButton); // Unexport the Button GPIO gpio_free(gpioLED); // Free the LED GPIO gpio_free(gpioButton); // Free the Button GPIO printk(KERN_INFO "GPIO_TEST: Goodbye from the LKM!

"); } /** @brief The GPIO IRQ Handler function * This function is a custom interrupt handler that is attached to the GPIO above. The same interrupt * handler cannot be invoked concurrently as the interrupt line is masked out until the function is complete. * This function is static as it should not be invoked directly from outside of this file. * @param irq the IRQ number that is associated with the GPIO -- useful for logging. * @param dev_id the *dev_id that is provided -- can be used to identify which device caused the interrupt * Not used in this example as NULL is passed. * @param regs h/w specific register values -- only really ever used for debugging. * return returns IRQ_HANDLED if successful -- should return IRQ_NONE otherwise. */ static irq_handler_t ebbgpio_irq_handler(unsigned int irq, void *dev_id, struct pt_regs *regs){ ledOn = !ledOn; // Invert the LED state on each button press gpio_set_value(gpioLED, ledOn); // Set the physical LED accordingly printk(KERN_INFO "GPIO_TEST: Interrupt! (button state is %d)

", gpio_get_value(gpioButton)); numberPresses++; // Global counter, will be outputted when the module is unloaded return (irq_handler_t) IRQ_HANDLED; // Announce that the IRQ has been handled correctly } /// This next calls are mandatory -- they identify the initialization function /// and the cleanup function (as above). module_init(ebbgpio_init); module_exit(ebbgpio_exit); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 /** * @file gpio_test.c * @author Derek Molloy * @date 19 April 2015 * @brief A kernel module for controlling a GPIO LED/button pair. The device mounts devices via * sysfs /sys/class/gpio/gpio115 and gpio49. Therefore, this test LKM circuit assumes that an LED * is attached to GPIO 49 which is on P9_23 and the button is attached to GPIO 115 on P9_27. There * is no requirement for a custom overlay, as the pins are in their default mux mode states. * @see http://www.derekmolloy.ie/ */ #include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/gpio.h> // Required for the GPIO functions #include <linux/interrupt.h> // Required for the IRQ code MODULE_LICENSE ( "GPL" ) ; MODULE_AUTHOR ( "Derek Molloy" ) ; MODULE_DESCRIPTION ( "A Button/LED test driver for the BBB" ) ; MODULE_VERSION ( "0.1" ) ; static unsigned int gpioLED = 49 ; ///< hard coding the LED gpio for this example to P9_23 (GPIO49) static unsigned int gpioButton = 115 ; ///< hard coding the button gpio for this example to P9_27 (GPIO115) static unsigned int irqNumber ; ///< Used to share the IRQ number within this file static unsigned int numberPresses = 0 ; ///< For information, store the number of button presses static bool ledOn = 0 ; ///< Is the LED on or off? Used to invert its state (off by default) /// Function prototype for the custom IRQ handler function -- see below for the implementation static irq_handler_t ebbgpio_irq_handler ( unsigned int irq , void * dev_id , struct pt_regs * regs ) ; /** @brief The LKM initialization function * The static keyword restricts the visibility of the function to within this C file. The __init * macro means that for a built-in driver (not a LKM) the function is only used at initialization * time and that it can be discarded and its memory freed up after that point. In this example this * function sets up the GPIOs and the IRQ * @return returns 0 if successful */ static int __init ebbgpio_init ( void ) { int result = 0 ; printk ( KERN _ INFO "GPIO_TEST: Initializing the GPIO_TEST LKM

" ) ; // Is the GPIO a valid GPIO number (e.g., the BBB has 4x32 but not all available) if ( ! gpio_is_valid ( gpioLED ) ) { printk ( KERN _ INFO "GPIO_TEST: invalid LED GPIO

" ) ; return - ENODEV ; } // Going to set up the LED. It is a GPIO in output mode and will be on by default ledOn = true ; gpio_request ( gpioLED , "sysfs" ) ; // gpioLED is hardcoded to 49, request it gpio_direction_output ( gpioLED , ledOn ) ; // Set the gpio to be in output mode and on // gpio_set_value(gpioLED, ledOn); // Not required as set by line above (here for reference) gpio_export ( gpioLED , false ) ; // Causes gpio49 to appear in /sys/class/gpio // the bool argument prevents the direction from being changed gpio_request ( gpioButton , "sysfs" ) ; // Set up the gpioButton gpio_direction_input ( gpioButton ) ; // Set the button GPIO to be an input gpio_set_debounce ( gpioButton , 200 ) ; // Debounce the button with a delay of 200ms gpio_export ( gpioButton , false ) ; // Causes gpio115 to appear in /sys/class/gpio // the bool argument prevents the direction from being changed // Perform a quick test to see that the button is working as expected on LKM load printk ( KERN _ INFO "GPIO_TEST: The button state is currently: %d

" , gpio_get_value ( gpioButton ) ) ; // GPIO numbers and IRQ numbers are not the same! This function performs the mapping for us irqNumber = gpio_to_irq ( gpioButton ) ; printk ( KERN _ INFO "GPIO_TEST: The button is mapped to IRQ: %d

" , irqNumber ) ; // This next call requests an interrupt line result = request_irq ( irqNumber , // The interrupt number requested ( irq_handler_t ) ebbgpio_irq_handler , // The pointer to the handler function below IRQF_TRIGGER_RISING , // Interrupt on rising edge (button press, not release) "ebb_gpio_handler" , // Used in /proc/interrupts to identify the owner NULL ) ; // The *dev_id for shared interrupt lines, NULL is okay printk ( KERN _ INFO "GPIO_TEST: The interrupt request result is: %d

" , result ) ; return result ; } /** @brief The LKM cleanup function * Similar to the initialization function, it is static. The __exit macro notifies that if this * code is used for a built-in driver (not a LKM) that this function is not required. Used to release the * GPIOs and display cleanup messages. */ static void __exit ebbgpio_exit ( void ) { printk ( KERN _ INFO "GPIO_TEST: The button state is currently: %d

" , gpio_get_value ( gpioButton ) ) ; printk ( KERN _ INFO "GPIO_TEST: The button was pressed %d times

" , numberPresses ) ; gpio_set_value ( gpioLED , 0 ) ; // Turn the LED off, makes it clear the device was unloaded gpio_unexport ( gpioLED ) ; // Unexport the LED GPIO free_irq ( irqNumber , NULL ) ; // Free the IRQ number, no *dev_id required in this case gpio_unexport ( gpioButton ) ; // Unexport the Button GPIO gpio_free ( gpioLED ) ; // Free the LED GPIO gpio_free ( gpioButton ) ; // Free the Button GPIO printk ( KERN _ INFO "GPIO_TEST: Goodbye from the LKM!

" ) ; } /** @brief The GPIO IRQ Handler function * This function is a custom interrupt handler that is attached to the GPIO above. The same interrupt * handler cannot be invoked concurrently as the interrupt line is masked out until the function is complete. * This function is static as it should not be invoked directly from outside of this file. * @param irq the IRQ number that is associated with the GPIO -- useful for logging. * @param dev_id the *dev_id that is provided -- can be used to identify which device caused the interrupt * Not used in this example as NULL is passed. * @param regs h/w specific register values -- only really ever used for debugging. * return returns IRQ_HANDLED if successful -- should return IRQ_NONE otherwise. */ static irq_handler_t ebbgpio_irq_handler ( unsigned int irq , void * dev_id , struct pt_regs * regs ) { ledOn = ! ledOn ; // Invert the LED state on each button press gpio_set_value ( gpioLED , ledOn ) ; // Set the physical LED accordingly printk ( KERN _ INFO "GPIO_TEST: Interrupt! (button state is %d)

" , gpio_get_value ( gpioButton ) ) ; numberPresses ++ ; // Global counter, will be outputted when the module is unloaded return ( irq_handler_t ) IRQ_HANDLED ; // Announce that the IRQ has been handled correctly } /// This next calls are mandatory -- they identify the initialization function /// and the cleanup function (as above). module_init ( ebbgpio_init ) ; module_exit ( ebbgpio_exit ) ;

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) : 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).

: 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.

: 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

#define KOBJ_NAME_LEN 20 struct kobject { char *k_name; // kobject name pointer (must not be NULL) char name[KOBJ_NAME_LEN]; // short kobject internal name data (can kmalloc() longer names) struct kref kref; // the reference count struct list_head entry; // circularly linked list to members of the kset struct kobject *parent; // the parent kobject struct kset *kset; // kobject can be a member of a set (otherwise NULL) struct kobj_type *ktype; // kobj_type is a struct that describes the type of the object struct dentry *dentry; // the sysfs directory entry }; 1 2 3 4 5 6 7 8 9 10 11 12 #define KOBJ_NAME_LEN 20 struct kobject { char * k_name ; // kobject name pointer (must not be NULL) char name [ KOBJ_NAME_LEN ] ; // short kobject internal name data (can kmalloc() longer names) struct kref kref ; // the reference count struct list_head entry ; // circularly linked list to members of the kset struct kobject * parent ; // the parent kobject struct kset * kset ; // kobject can be a member of a set (otherwise NULL) struct kobj_type * ktype ; // kobj_type is a struct that describes the type of the object struct dentry * dentry ; // the sysfs directory entry } ;

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:

static struct kobject *ebb_kobj; ebb_kobj = kobject_create_and_add("ebb", kernel_kobj->parent); 1 2 static struct kobject * ebb_kobj ; ebb_kobj = kobject_create_and_add ( "ebb" , kernel_kobj -> parent ) ;

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:

static ssize_t dev_attribute_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf); static ssize_t dev_attribute_store(struct kobject *kobj, struct kobj_attribute *attr, char *buf); 1 2 static ssize_t dev_attribute_show ( struct kobject * kobj , struct kobj_attribute * attr , char * buf ) ; static ssize_t dev_attribute_store ( struct kobject * kobj , struct kobj_attribute * attr , char * buf ) ;

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 .

: Long-hand version. You must pass the attribute variable name , the access mode (e.g., 0666 for read/write access), the pointer to the show function , and the pointer to the store function . __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 .

: Short-hand read-only attribute macro. You must pass the attribute variable name and the macro sets the to be 0444 (read only), and the function to be . __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 Enhanced GPIO Button LKM /** * @file button.c * @author Derek Molloy * @date 19 April 2015 * @brief A kernel module for controlling a button (or any signal) that is connected to * a GPIO. It has full support for interrupts and for sysfs entries so that an interface * can be created to the button or the button can be configured from Linux userspace. * The sysfs entry appears at /sys/ebb/gpio115 * @see http://www.derekmolloy.ie/ */ #include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/gpio.h> // Required for the GPIO functions #include <linux/interrupt.h> // Required for the IRQ code #include <linux/kobject.h> // Using kobjects for the sysfs bindings #include <linux/time.h> // Using the clock to measure time between button presses #define DEBOUNCE_TIME 200 ///< The default bounce time -- 200ms MODULE_LICENSE("GPL"); MODULE_AUTHOR("Derek Molloy"); MODULE_DESCRIPTION("A simple Linux GPIO Button LKM for the BBB"); MODULE_VERSION("0.1"); static bool isRising = 1; ///< Rising edge is the default IRQ property module_param(isRising, bool, S_IRUGO); ///< Param desc. S_IRUGO can be read/not changed MODULE_PARM_DESC(isRising, " Rising edge = 1 (default), Falling edge = 0"); ///< parameter description static unsigned int gpioButton = 115; ///< Default GPIO is 115 module_param(gpioButton, uint, S_IRUGO); ///< Param desc. S_IRUGO can be read/not changed MODULE_PARM_DESC(gpioButton, " GPIO Button number (default=115)"); ///< parameter description static unsigned int gpioLED = 49; ///< Default GPIO is 49 module_param(gpioLED, uint, S_IRUGO); ///< Param desc. S_IRUGO can be read/not changed MODULE_PARM_DESC(gpioLED, " GPIO LED number (default=49)"); ///< parameter description static char gpioName[8] = "gpioXXX"; ///< Null terminated default string -- just in case static int irqNumber; ///< Used to share the IRQ number within this file static int numberPresses = 0; ///< For information, store the number of button presses static bool ledOn = 0; ///< Is the LED on or off? Used to invert its state (off by default) static bool isDebounce = 1; ///< Use to store the debounce state (on by default) static struct timespec ts_last, ts_current, ts_diff; ///< timespecs from linux/time.h (has nano precision) /// Function prototype for the custom IRQ handler function -- see below for the implementation static irq_handler_t ebbgpio_irq_handler(unsigned int irq, void *dev_id, struct pt_regs *regs); /** @brief A callback function to output the numberPresses variable * @param kobj represents a kernel object device that appears in the sysfs filesystem * @param attr the pointer to the kobj_attribute struct * @param buf the buffer to which to write the number of presses * @return return the total number of characters written to the buffer (excluding null) */ static ssize_t numberPresses_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf){ return sprintf(buf, "%d

", numberPresses); } /** @brief A callback function to read in the numberPresses variable * @param kobj represents a kernel object device that appears in the sysfs filesystem * @param attr the pointer to the kobj_attribute struct * @param buf the buffer from which to read the number of presses (e.g., reset to 0). * @param count the number characters in the buffer * @return return should return the total number of characters used from the buffer */ static ssize_t numberPresses_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count){ sscanf(buf, "%du", &numberPresses); return count; } /** @brief Displays if the LED is on or off */ static ssize_t ledOn_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf){ return sprintf(buf, "%d

", ledOn); } /** @brief Displays the last time the button was pressed -- manually output the date (no localization) */ static ssize_t lastTime_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf){ return sprintf(buf, "%.2lu:%.2lu:%.2lu:%.9lu

", (ts_last.tv_sec/3600)%24, (ts_last.tv_sec/60) % 60, ts_last.tv_sec % 60, ts_last.tv_nsec ); } /** @brief Display the time difference in the form secs.nanosecs to 9 places */ static ssize_t diffTime_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf){ return sprintf(buf, "%lu.%.9lu

", ts_diff.tv_sec, ts_diff.tv_nsec); } /** @brief Displays if button debouncing is on or off */ static ssize_t isDebounce_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf){ return sprintf(buf, "%d

", isDebounce); } /** @brief Stores and sets the debounce state */ static ssize_t isDebounce_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count){ unsigned int temp; sscanf(buf, "%du", &temp); // use a temp varable for correct int->bool gpio_set_debounce(gpioButton,0); isDebounce = temp; if(isDebounce) { gpio_set_debounce(gpioButton, DEBOUNCE_TIME); printk(KERN_INFO "EBB Button: Debounce on

"); } else { gpio_set_debounce(gpioButton, 0); // set the debounce time to 0 printk(KERN_INFO "EBB Button: Debounce off

"); } return count; } /** Use these helper macros to define the name and access levels of the kobj_attributes * The kobj_attribute has an attribute attr (name and mode), show and store function pointers * The count variable is associated with the numberPresses variable and it is to be exposed * with mode 0666 using the numberPresses_show and numberPresses_store functions above */ static struct kobj_attribute count_attr = __ATTR(numberPresses, 0666, numberPresses_show, numberPresses_store); static struct kobj_attribute debounce_attr = __ATTR(isDebounce, 0666, isDebounce_show, isDebounce_store); /** The __ATTR_RO macro defines a read-only attribute. There is no need to identify that the * function is called _show, but it must be present. __ATTR_WO can be used for a write-only * attribute but only in Linux 3.11.x on. */ static struct kobj_attribute ledon_attr = __ATTR_RO(ledOn); ///< the ledon kobject attr static struct kobj_attribute time_attr = __ATTR_RO(lastTime); ///< the last time pressed kobject attr static struct kobj_attribute diff_attr = __ATTR_RO(diffTime); ///< the difference in time attr /** The ebb_attrs[] is an array of attributes that is used to create the attribute group below. * The attr property of the kobj_attribute is used to extract the attribute struct */ static struct attribute *ebb_attrs[] = { &count_attr.attr, ///< The number of button presses &ledon_attr.attr, ///< Is the LED on or off? &time_attr.attr, ///< Time of the last button press in HH:MM:SS:NNNNNNNNN &diff_attr.attr, ///< The difference in time between the last two presses &debounce_attr.attr, ///< Is the debounce state true or false NULL, }; /** The attribute group uses the attribute array and a name, which is exposed on sysfs -- in this * case it is gpio115, which is automatically defined in the ebbButton_init() function below * using the custom kernel parameter that can be passed when the module is loaded. */ static struct attribute_group attr_group = { .name = gpioName, ///< The name is generated in ebbButton_init() .attrs = ebb_attrs, ///< The attributes array defined just above }; static struct kobject *ebb_kobj; /** @brief The LKM initialization function * The static keyword restricts the visibility of the function to within this C file. The __init * macro means that for a built-in driver (not a LKM) the function is only used at initialization * time and that it can be discarded and its memory freed up after that point. In this example this * function sets up the GPIOs and the IRQ * @return returns 0 if successful */ static int __init ebbButton_init(void){ int result = 0; unsigned long IRQflags = IRQF_TRIGGER_RISING; // The default is a rising-edge interrupt printk(KERN_INFO "EBB Button: Initializing the EBB Button LKM

"); sprintf(gpioName, "gpio%d", gpioButton); // Create the gpio115 name for /sys/ebb/gpio115 // create the kobject sysfs entry at /sys/ebb -- probably not an ideal location! ebb_kobj = kobject_create_and_add("ebb", kernel_kobj->parent); // kernel_kobj points to /sys/kernel if(!ebb_kobj){ printk(KERN_ALERT "EBB Button: failed to create kobject mapping

"); return -ENOMEM; } // add the attributes to /sys/ebb/ -- for example, /sys/ebb/gpio115/numberPresses result = sysfs_create_group(ebb_kobj, &attr_group); if(result) { printk(KERN_ALERT "EBB Button: failed to create sysfs group

"); kobject_put(ebb_kobj); // clean up -- remove the kobject sysfs entry return result; } getnstimeofday(&ts_last); // set the last time to be the current time ts_diff = timespec_sub(ts_last, ts_last); // set the initial time difference to be 0 // Going to set up the LED. It is a GPIO in output mode and will be on by default ledOn = true; gpio_request(gpioLED, "sysfs"); // gpioLED is hardcoded to 49, request it gpio_direction_output(gpioLED, ledOn); // Set the gpio to be in output mode and on // gpio_set_value(gpioLED, ledOn); // Not required as set by line above (here for reference) gpio_export(gpioLED, false); // Causes gpio49 to appear in /sys/class/gpio // the bool argument prevents the direction from being changed gpio_request(gpioButton, "sysfs"); // Set up the gpioButton gpio_direction_input(gpioButton); // Set the button GPIO to be an input gpio_set_debounce(gpioButton, DEBOUNCE_TIME); // Debounce the button with a delay of 200ms gpio_export(gpioButton, false); // Causes gpio115 to appear in /sys/class/gpio // the bool argument prevents the direction from being changed // Perform a quick test to see that the button is working as expected on LKM load printk(KERN_INFO "EBB Button: The button state is currently: %d

", gpio_get_value(gpioButton)); /// GPIO numbers and IRQ numbers are not the same! This function performs the mapping for us irqNumber = gpio_to_irq(gpioButton); printk(KERN_INFO "EBB Button: The button is mapped to IRQ: %d

", irqNumber); if(!isRising){ // If the kernel parameter isRising=0 is supplied IRQflags = IRQF_TRIGGER_FALLING; // Set the interrupt to be on the falling edge } // This next call requests an interrupt line result = request_irq(irqNumber, // The interrupt number requested (irq_handler_t) ebbgpio_irq_handler, // The pointer to the handler function below IRQflags, // Use the custom kernel param to set interrupt type "ebb_button_handler", // Used in /proc/interrupts to identify the owner NULL); // The *dev_id for shared interrupt lines, NULL is okay return result; } /** @brief The LKM cleanup function * Similar to the initialization function, it is static. The __exit macro notifies that if this * code is used for a built-in driver (not a LKM) that this function is not required. */ static void __exit ebbButton_exit(void){ printk(KERN_INFO "EBB Button: The button was pressed %d times

", numberPresses); kobject_put(ebb_kobj); // clean up -- remove the kobject sysfs entry gpio_set_value(gpioLED, 0); // Turn the LED off, makes it clear the device was unloaded gpio_unexport(gpioLED); // Unexport the LED GPIO free_irq(irqNumber, NULL); // Free the IRQ number, no *dev_id required in this case gpio_unexport(gpioButton); // Unexport the Button GPIO gpio_free(gpioLED); // Free the LED GPIO gpio_free(gpioButton); // Free the Button GPIO printk(KERN_INFO "EBB Button: Goodbye from the EBB Button LKM!

"); } /** @brief The GPIO IRQ Handler function * This function is a custom interrupt handler that is attached to the GPIO above. The same interrupt * handler cannot be invoked concurrently as the interrupt line is masked out until the function is complete. * This function is static as it should not be invoked directly from outside of this file. * @param irq the IRQ number that is associated with the GPIO -- useful for logging. * @param dev_id the *dev_id that is provided -- can be used to identify which device caused the interrupt * Not used in this example as NULL is passed. * @param regs h/w specific register values -- only really ever used for debugging. * return returns IRQ_HANDLED if successful -- should return IRQ_NONE otherwise. */ static irq_handler_t ebbgpio_irq_handler(unsigned int irq, void *dev_id, struct pt_regs *regs){ ledOn = !ledOn; // Invert the LED state on each button press gpio_set_value(gpioLED, ledOn); // Set the physical LED accordingly getnstimeofday(&ts_current); // Get the current time as ts_current ts_diff = timespec_sub(ts_current, ts_last); // Determine the time difference between last 2 presses ts_last = ts_current; // Store the current time as the last time ts_last printk(KERN_INFO "EBB Button: The button state is currently: %d

", gpio_get_value(gpioButton)); numberPresses++; // Global counter, will be outputted when the module is unloaded return (irq_handler_t) IRQ_HANDLED; // Announce that the IRQ has been handled correctly } // This next calls are mandatory -- they identify the initialization function // and the cleanup function (as above). module_init(ebbButton_init); module_exit(ebbButton_exit); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 /** * @file button.c * @author Derek Molloy * @date 19 April 2015 * @brief A kernel module for controlling a button (or any signal) that is connected to * a GPIO. It has full support for interrupts and for sysfs entries so that an interface * can be created to the button or the button can be configured from Linux userspace. * The sysfs entry appears at /sys/ebb/gpio115 * @see http://www.derekmolloy.ie/ */ #include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/gpio.h> // Required for the GPIO functions #include <linux/interrupt.h> // Required for the IRQ code #include <linux/kobject.h> // Using kobjects for the sysfs bindings #include <linux/time.h> // Using the clock to measure time between button presses #define DEBOUNCE_TIME 200 ///< The default bounce time -- 200ms MODULE_LICENSE ( "GPL" ) ; MODULE_AUTHOR ( "Derek Molloy" ) ; MODULE_DESCRIPTION ( "A simple Linux GPIO Button LKM for the BBB" ) ; MODULE_VERSION ( "0.1" ) ; static bool isRising = 1 ; ///< Rising edge is the default IRQ property module_param ( isRising , bool , S_IRUGO ) ; ///< Param desc. S_IRUGO can be read/not changed MODULE_PARM_DESC ( isRising , " Rising edge = 1 (default), Falling edge = 0" ) ; ///< parameter description static unsigned int gpioButton = 115 ; ///< Default GPIO is 115 module_param ( gpioButton , uint , S_IRUGO ) ; ///< Param desc. S_IRUGO can be read/not changed MODULE_PARM_DESC ( gpioButton , " GPIO Button number (default=115)" ) ; ///< parameter description static unsigned int gpioLED = 49 ; ///< Default GPIO is 49 module_param ( gpioLED , uint , S_IRUGO ) ; ///< Param desc. S_IRUGO can be read/not changed MODULE_PARM_DESC ( gpioLED , " GPIO LED number (default=49)" ) ; ///< parameter description static char gpioName [ 8 ] = "gpioXXX" ; ///< Null terminated default string -- just in case static int irqNumber ; ///< Used to share the IRQ number within this file static int numberPresses = 0 ; ///< For information, store the number of button presses static bool ledOn = 0 ; ///< Is the LED on or off? Used to invert its state (off by default) static bool isDebounce = 1 ; ///< Use to store the debounce state (on by default) static struct timespec ts_last , ts_current , ts_diff ; ///< timespecs from linux/time.h (has nano precision) /// Function prototype for the custom IRQ handler function -- see below for the implementation static irq_handler_t ebbgpio_irq_handler ( unsigned int irq , void * dev_id , struct pt_regs * regs ) ; /** @brief A callback function to output the numberPresses variable * @param kobj represents a kernel object device that appears in the sysfs filesystem * @param attr the pointer to the kobj_attribute struct * @param buf the buffer to which to write the number of presses * @return return the total number of characters written to the buffer (excluding null) */ static ssize_t numberPresses_show ( struct kobject * kobj , struct kobj_attribute * attr , char * buf ) { return sprintf ( buf , "%d

" , numberPresses ) ; } /** @brief A callback function to read in the numberPresses variable * @param kobj represents a kernel object device that appears in the sysfs filesystem * @param attr the pointer to the kobj_attribute struct * @param buf the buffer from which to read the number of presses (e.g., reset to 0). * @param count the number characters in the buffer * @return return should return the total number of characters used from the buffer */ static ssize_t numberPresses_store ( struct kobject * kobj , struct kobj_attribute * attr , const char * buf , size_t count ) { sscanf ( buf , "%du" , & numberPresses ) ; return count ; } /** @brief Displays if the LED is on or off */ static ssize_t ledOn_show ( struct kobject * kobj , struct kobj_attribute * attr , char * buf ) { return sprintf ( buf , "%d

" , ledOn ) ; } /** @brief Displays the last time the button was pressed -- manually output the date (no localization) */ static ssize_t lastTime_show ( struct kobject * kobj , struct kobj_attribute * attr , char * buf ) { return sprintf ( buf , "%.2lu:%.2lu:%.2lu:%.9lu

" , ( ts_last . tv_sec / 3600 ) % 24 , ( ts_last . tv_sec / 60 ) % 60 , ts_last . tv_sec % 60 , ts_last . tv _ nsec ) ; } /** @brief Display the time difference in the form secs.nanosecs to 9 places */ static ssize_t diffTime_show ( struct kobject * kobj , struct kobj_attribute * attr , char * buf ) { return sprintf ( buf , "%lu.%.9lu

" , ts_diff . tv_sec , ts_diff . tv_nsec ) ; } /** @brief Displays if button debouncing is on or off */ static ssize_t isDebounce_show ( struct kobject * kobj , struct kobj_attribute * attr , char * buf ) { return sprintf ( buf , "%d

" , isDebounce ) ; } /** @brief Stores and sets the debounce state */ static ssize_t isDebounce_store ( struct kobject * kobj , struct kobj_attribute * attr , const char * buf , size_t count ) { unsigned int temp ; sscanf ( buf , "%du" , & temp ) ; // use a temp varable for correct int->bool gpio_set_debounce ( gpioButton , 0 ) ; isDebounce = temp ; if ( isDebounce ) { gpio_set_debounce ( gpioButton , DEBOUNCE_TIME ) ; printk ( KERN _ INFO "EBB Button: Debounce on

" ) ; } else { gpio_set_debounce ( gpioButton , 0 ) ; // set the debounce time to 0 printk ( KERN _ INFO "EBB Button: Debounce off

" ) ; } return count ; } /** Use these helper macros to define the name and access levels of the kobj_attributes * The kobj_attribute has an attribute attr (name and mode), show and store function pointers * The count variable is associated with the numberPresses variable and it is to be exposed * with mode 0666 using the numberPresses_show and numberPresses_store functions above */ static struct kobj_attribute count_attr = __ATTR ( numberPresses , 0666 , numberPresses_show , numberPresses_store ) ; static struct kobj_attribute debounce_attr = __ATTR ( isDebounce , 0666 , isDebounce_show , isDebounce_store ) ; /** The __ATTR_RO macro defines a read-only attribute. There is no need to identify that the * function is called _show, but it must be present. __ATTR_WO can be used for a write-only * attribute but only in Linux 3.11.x on. */ static struct kobj_attribute ledon_attr = __ATTR_RO ( ledOn ) ; ///< the ledon kobject attr static struct kobj_attribute time_attr = __ATTR_RO ( lastTime ) ; ///< the last time pressed kobject attr static struct kobj_attribute diff_attr = __ATTR_RO ( diffTime ) ; ///< the difference in time attr /** The ebb_attrs[] is an array of attributes that is used to create the attribute group below. * The attr property of the kobj_attribute is used to extract the attribute struct */ static struct attribute * ebb_attrs [ ] = { & count_attr . attr , ///< The number of button presses & ledon_attr . attr , ///< Is the LED on or off? & time_attr . attr , ///< Time of the last button press in HH:MM:SS:NNNNNNNNN & diff_attr . attr , ///< The difference in time between the last two presses & debounce_attr . attr , ///< Is the debounce state true or false NULL , } ; /** The attribute group uses the attribute array and a name, which is exposed on sysfs -- in this * case it is gpio115, which is automatically defined in the ebbButton_init() function below * using the custom kernel parameter that can be passed when the module is loaded. */ static struct attribute_group attr_group = { . name = gpioName , ///< The name is generated in ebbButton_init() . attrs = ebb_attrs , ///< The attributes array defined just above } ; static struct kobject * ebb_kobj ; /** @brief The LKM initialization function * The static keyword restricts the visibility of the function to within this C file. The __init * macro means that for a built-in driver (not a LKM) the function is only used at initialization * time and that it can be discarded and its memory freed up after that point. In this example this * function sets up the GPIOs and the IRQ * @return returns 0 if successful */ static int __init ebbButton_init ( void ) { int result = 0 ; unsigned long IRQflags = IRQF_TRIGGER_RISING ; // The default is a rising-edge interrupt printk ( KERN _ INFO "EBB Button: Initializing the EBB Button LKM

" ) ; sprintf ( gpioName , "gpio%d" , gpioButton ) ; // Create the gpio115 name for /sys/ebb/gpio115 // create the kobject sysfs entry at /sys/ebb -- probably not an ideal location! ebb_kobj = kobject_create_and_add ( "ebb" , kernel_kobj -> parent ) ; // kernel_kobj points to /sys/kernel if ( ! ebb_kobj ) { printk ( KERN _ ALERT "EBB Button: failed to create kobject mapping

" ) ; return - ENOMEM ; } // add the attributes to /sys/ebb/ -- for example, /sys/ebb/gpio115/numberPresses result = sysfs_create_group ( ebb_kobj , & attr_group ) ; if ( result ) { printk ( KERN _ ALERT "EBB Button: failed to create sysfs group

" ) ; kobject_put ( ebb_kobj ) ; // clean up -- remove the kobject sysfs entry return result ; } getnstimeofday ( & ts_last ) ; // set the last time to be the current time ts_diff = timespec_sub ( ts_last , ts_last ) ; // set the initial time difference to be 0 // Going to set up the LED. It is a GPIO in output mode and will be on by default ledOn = true ; gpio_request ( gpioLED , "sysfs" ) ; // gpioLED is hardcoded to 49, request it gpio_direction_output ( gpioLED , ledOn ) ; // Set the gpio to be in output mode and on // gpio_set_value(gpioLED, ledOn); // Not required as set by line above (here for reference) gpio_export ( gpioLED , false ) ; // Causes gpio49 to appear in /sys/class/gpio // the bool argument prevents the direction from being changed gpio_request ( gpioButton , "sysfs" ) ; // Set up the gpioButton gpio_direction_input ( gpioButton ) ; // Set the button GPIO to be an input gpio_set_debounce ( gpioButton , DEBOUNCE_TIME ) ; // Debounce the button with a delay of 200ms gpio_export ( gpioButton , false ) ; // Causes gpio115 to appear in /sys/class/gpio // the bool argument prevents the direction from being changed // Perform a quick test to see that the button is working as expected on LKM load printk ( KERN _ INFO "EBB Button: The button state is currently: %d

" , gpio_get_value ( gpioButton ) ) ; /// GPIO numbers and IRQ numbers are not the same! This function performs the mapping for us irqNumber = gpio_to_irq ( gpioButton ) ; printk ( KERN _ INFO "EBB Button: The button is mapped to IRQ: %d

" , irqNumber ) ; if ( ! isRising ) { // If the kernel parameter isRising=0 is supplied IRQflags = IRQF_TRIGGER_FALLING ; // Set the interrupt to be on the falling edge } // This next call requests an interrupt line result = request_irq ( irqNumber , // The interrupt number requested ( irq_handler_t ) ebbgpio_irq_handler , // The pointer to the handler function below IRQflags , // Use the custom kernel param to set interrupt type "ebb_button_handler" , // Used in /proc/interrupts to identify the owner NULL ) ; // The *dev_id for shared interrupt lines, NULL is okay return result ; } /** @brief The LKM cleanup function * Similar to the initialization function, it is static. The __exit macro notifies that if this * code is used for a built-in driver (not a LKM) that this function is not required. */ static void __exit ebbButton_exit ( void ) { printk ( KERN _ INFO "EBB Button: The button was pressed %d times

" , numberPresses ) ; kobject_put ( ebb_kobj ) ; // clean up -- remove the kobject sysfs entry gpio_set_value ( gpioLED , 0 ) ; // Turn the LED off, makes it clear the device was unloaded gpio_unexport ( gpioLED ) ; // Unexport the LED GPIO free_irq ( irqNumber , NULL ) ; // Free the IRQ number, no *dev_id required in this case gpio_unexport ( gpioButton ) ; // Unexport the Button GPIO gpio_free ( gpioLED ) ; // Free the LED GPIO gpio_free ( gpioButton ) ; // Free the Button GPIO printk ( KERN _ INFO "EBB Button: Goodbye from the EBB Button LKM!

" ) ; } /** @brief The GPIO IRQ Handler function * This function is a custom interrupt handler that is attached to the GPIO above. The same interrupt * handler cannot be invoked concurrently as the interrupt line is masked out until the function is complete. * This function is static as it should not be invoked directly from outside of this file. * @param irq the IRQ number that is associated with the GPIO -- useful for logging. * @param dev_id the *dev_id that is provided -- can be used to identify which device caused the interrupt * Not used in this example as NULL is passed. * @param regs h/w specific register values -- only really ever used for debugging. * return returns IRQ_HANDLED if successful -- should return IRQ_NONE otherwise. */ static irq_handler_t ebbgpio_irq_handler ( unsigned int irq , void * dev_id , struct pt_regs * regs ) { ledOn = ! ledOn ; // Invert the LED state on each button press gpio_set_value ( gpioLED , ledOn ) ; // Set the physical LED accordingly getnstimeofday ( & ts_current ) ; // Get the current time as ts_current ts_diff = timespec_sub ( ts_current , ts_last ) ; // Determine the time difference between last 2 presses ts_last = ts_current ; // Store the current time as the last time ts_last printk ( KERN _ INFO "EBB Button: The button state is currently: %d

" , gpio_get_value ( gpioButton ) ) ; numberPresses ++ ; // Global counter, will be outputted when the module is unloaded return ( irq_handler_t ) IRQ_HANDLED ; // Announce that the IRQ has been handled correctly } // This next calls are mandatory -- they identify the initialization function // and the cleanup function (as above). module_init ( ebbButton_init ) ; module_exit ( ebbButton_exit ) ;

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.

, , and ). 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).

). These are: , , , , and . They are all read-only, with the exception of (i.e., rising-edge or falling-edge) and (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

#include <linux/kthread.h> static struct task_struct *task; // The pointer to the thread task static int flash(void *arg){ … while(!kthread_should_stop()){ // Returns true when kthread_stop() or equivalent is called set_current_state(TASK_RUNNING); // prevent inadvertent sleeps temporarily (just an example) … // perform state change instructions (e.g., flash) set_current_state(TASK_INTERRUPTIBLE); // going to sleep but can be awoken if required msleep(…); // millisecond sleep } … } static int __init ebbLED_init(void){ task = kthread_run(flash, NULL, "LED_flash_thread"); // Start the LED flashing kthread … } static void __exit ebbLED_exit(void){ kthread_stop(task); // Stop the LED flashing kthread … } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #include <linux/kthread.h> static struct task_struct * task ; // The pointer to the thread task static int flash ( void * arg ) { … while ( ! kthread_should_stop ( ) ) { // Returns true when kthread_stop() or equivalent is called set_current_state ( TASK_RUNNING ) ; // prevent inadvertent sleeps temporarily (just an example) … // perform state change instructions (e.g., flash) set_current_state ( TASK_INTERRUPTIBLE ) ; // going to sleep but can be awoken if required msleep ( … ) ; // millisecond sleep } … } static int __init ebbLED_init ( void ) { task = kthread_run ( flash , NULL , "LED_flash_thread" ) ; // Start the LED flashing kthread … } static void __exit ebbLED_exit ( void ) { kthread_stop ( task ) ; // Stop the LED flashing kthread … }

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

Listing 6: The Enhanced GPIO LED Controller LKM

The Enhanced LED Controller LKM /** * @file led.c * @author Derek Molloy * @date 19 April 2015 * @brief A kernel module for controlling a simple LED (or any signal) that is connected to * a GPIO. It is threaded in order that it can flash the LED. * The sysfs entry appears at /sys/ebb/led49 * @see http://www.derekmolloy.ie/ */ #include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/gpio.h> // Required for the GPIO functions #include <linux/kobject.h> // Using kobjects for the sysfs bindings #include <linux/kthread.h> // Using kthreads for the flashing functionality #include <linux/delay.h> // Using this header for the msleep() function MODULE_LICENSE("GPL"); MODULE_AUTHOR("Derek Molloy"); MODULE_DESCRIPTION("A simple Linux LED driver LKM for the BBB"); MODULE_VERSION("0.1"); static unsigned int gpioLED = 49; ///< Default GPIO for the LED is 49 module_param(gpioLED, uint, S_IRUGO); ///< Param desc. S_IRUGO can be read/not changed MODULE_PARM_DESC(gpioLED, " GPIO LED number (default=49)"); ///< parameter description static unsigned int blinkPeriod = 1000; ///< The blink period in ms module_param(blinkPeriod, uint, S_IRUGO); ///< Param desc. S_IRUGO can be read/not changed MODULE_PARM_DESC(blinkPeriod, " LED blink period in ms (min=1, default=1000, max=10000)"); static char ledName[7] = "ledXXX"; ///< Null terminated default string -- just in case static bool ledOn = 0; ///< Is the LED on or off? Used for flashing enum modes { OFF, ON, FLASH }; ///< The available LED modes -- static not useful here static enum modes mode = FLASH; ///< Default mode is flashing /** @brief A callback function to display the LED mode * @param kobj represents a kernel object device that appears in the sysfs filesystem * @param attr the pointer to the kobj_attribute struct * @param buf the buffer to which to write the number of presses * @return return the number of characters of the mode string successfully displayed */ static ssize_t mode_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf){ switch(mode){ case OFF: return sprintf(buf, "off

"); // Display the state -- simplistic approach case ON: return sprintf(buf, "on

"); case FLASH: return sprintf(buf, "flash

"); default: return sprintf(buf, "LKM Error

"); // Cannot get here } } /** @brief A callback function to store the LED mode using the enum above */ static ssize_t mode_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count){ // the count-1 is important as otherwise the

is used in the comparison if (strncmp(buf,"on",count-1)==0) { mode = ON; } // strncmp() compare with fixed number chars else if (strncmp(buf,"off",count-1)==0) { mode = OFF; } else if (strncmp(buf,"flash",count-1)==0) { mode = FLASH; } return count; } /** @brief A callback function to display the LED period */ static ssize_t period_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf){ return sprintf(buf, "%d

", blinkPeriod); } /** @brief A callback function to store the LED period value */ static ssize_t period_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count){ unsigned int period; // Using a variable to validate the data sent sscanf(buf, "%du", &period); // Read in the period as an unsigned int if ((period>1)&&(period<=10000)){ // Must be 2ms or greater, 10secs or less blinkPeriod = period; // Within range, assign to blinkPeriod variable } return period; } /** Use these helper macros to define the name and access levels of the kobj_attributes * The kobj_attribute has an attribute attr (name and mode), show and store function pointers * The period variable is associated with the blinkPeriod variable and it is to be exposed * with mode 0666 using the period_show and period_store functions above */ static struct kobj_attribute period_attr = __ATTR(blinkPeriod, 0666, period_show, period_store); static struct kobj_attribute mode_attr = __ATTR(mode, 0666, mode_show, mode_store); /** The ebb_attrs[] is an array of attributes that is used to create the attribute group below. * The attr property of the kobj_attribute is used to extract the attribute struct */ static struct attribute *ebb_attrs[] = { &period_attr.attr, // The period at which the LED flashes &mode_attr.attr, // Is the LED on or off? NULL, }; /** The attribute group uses the attribute array and a name, which is exposed on sysfs -- in this * case it is gpio49, which is automatically defined in the ebbLED_init() function below * using the custom kernel parameter that can be passed when the module is loaded. */ static struct attribute_group attr_group = { .name = ledName, // The name is generated in ebbLED_init() .attrs = ebb_attrs, // The attributes array defined just above }; static struct kobject *ebb_kobj; /// The pointer to the kobject static struct task_struct *task; /// The pointer to the thread task /** @brief The LED Flasher main kthread loop * * @param arg A void pointer used in order to pass data to the thread * @return returns 0 if successful */ static int flash(void *arg){ printk(KERN_INFO "EBB LED: Thread has started running

"); while(!kthread_should_stop()){ // Returns true when kthread_stop() is called set_current_state(TASK_RUNNING); if (mode==FLASH) ledOn = !ledOn; // Invert the LED state else if (mode==ON) ledOn = true; else ledOn = false; gpio_set_value(gpioLED, ledOn); // Use the LED state to light/turn off the LED set_current_state(TASK_INTERRUPTIBLE); msleep(blinkPeriod/2); // millisecond sleep for half of the period } printk(KERN_INFO "EBB LED: Thread has run to completion

"); return 0; } /** @brief The LKM initialization function * The static keyword restricts the visibility of the function to within this C file. The __init * macro means that for a built-in driver (not a LKM) the function is only used at initialization * time and that it can be discarded and its memory freed up after that point. In this example this * function sets up the GPIOs and the IRQ * @return returns 0 if successful */ static int __init ebbLED_init(void){ int result = 0; printk(KERN_INFO "EBB LED: Initializing the EBB LED LKM

"); sprintf(ledName, "led%d", gpioLED); // Create the gpio115 name for /sys/ebb/led49 ebb_kobj = kobject_create_and_add("ebb", kernel_kobj->parent); // kernel_kobj points to /sys/kernel if(!ebb_kobj){ printk(KERN_ALERT "EBB LED: failed to create kobject

"); return -ENOMEM; } // add the attributes to /sys/ebb/ -- for example, /sys/ebb/led49/ledOn result = sysfs_create_group(ebb_kobj, &attr_group); if(result) { printk(KERN_ALERT "EBB LED: failed to create sysfs group

"); kobject_put(ebb_kobj); // clean up -- remove the kobject sysfs entry return result; } ledOn = true; gpio_request(gpioLED, "sysfs"); // gpioLED is 49 by default, request it gpio_direction_output(gpioLED, ledOn); // Set the gpio to be in output mode and turn on gpio_export(gpioLED, false); // causes gpio49 to appear in /sys/class/gpio // the second argument prevents the direction from being changed task = kthread_run(flash, NULL, "LED_flash_thread"); // Start the LED flashing thread if(IS_ERR(task)){ // Kthread name is LED_flash_thread printk(KERN_ALERT "EBB LED: failed to create the task

"); return PTR_ERR(task); } return result; } /** @brief The LKM cleanup function * Similar to the initialization function, it is static. The __exit macro notifies that if this * code is used for a built-in driver (not a LKM) that this function is not required. */ static void __exit ebbLED_exit(void){ kthread_stop(task); // Stop the LED flashing thread kobject_put(ebb_kobj); // clean up -- remove the kobject sysfs entry gpio_set_value(gpioLED, 0); // Turn the LED off, indicates device was unloaded gpio_unexport(gpioLED); // Unexport the Button GPIO gpio_free(gpioLED); // Free the LED GPIO printk(KERN_INFO "EBB LED: Goodbye from the EBB LED LKM!

"); } /// This next calls are mandatory -- they identify the initialization function /// and the cleanup function (as above). module_init(ebbLED_init); module_exit(ebbLED_exit); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 /** * @file led.c * @author Derek Molloy * @date 19 April 2015 * @brief A kernel module for controlling a simple LED (or any signal) that is connected to * a GPIO. It is threaded in order that it can flash the LED. * The sysfs entry appears at /sys/ebb/led49 * @see http://www.derekmolloy.ie/ */ #include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/gpio.h> // Required for the GPIO functions #include <linux/kobject.h> // Using kobjects for the sysfs bindings #include <linux/kthread.h> // Using kthreads for the flashing functionality #include <linux/delay.h> // Using this header for the msleep() function MODULE_LICENSE ( "GPL" ) ; MODULE_AUTHOR ( "Derek Molloy" ) ; MODULE_DESCRIPTION ( "A simple Linux LED driver LKM for the BBB" ) ; MODULE_VERSION ( "0.1" ) ; static unsigned int gpioLED = 49 ; ///< Default GPIO for the LED is 49 module_param ( gpioLED , uint , S_IRUGO ) ; ///< Param desc. S_IRUGO can be read/not changed MODULE_PARM_DESC ( gpioLED , " GPIO LED number (default=49)" ) ; ///< parameter description static unsigned int blinkPeriod = 1000 ; ///< The blink period in ms module_param ( blinkPeriod , uint , S_IRUGO ) ; ///< Param desc. S_IRUGO can be read/not changed MODULE_PARM_DESC ( blinkPeriod , " LED blink period in ms (min=1, default=1000, max=10000)" ) ; static char ledName [ 7 ] = "ledXXX" ; ///< Null terminated default string -- just in case static bool ledOn = 0 ; ///< Is the LED on or off? Used for flashing enum modes { OFF , ON , FLASH } ; ///< The available LED modes -- static not useful here static enum modes mode = FLASH ; ///< Default mode is flashing /** @brief A callback function to display the LED mode * @param kobj represents a kernel object device that appears in the sysfs filesystem * @param attr the pointer to the kobj_attribute struct * @param buf the buffer to which to write the number of presses * @return return the number of characters of the mode string successfully displayed */ static ssize_t mode_show ( struct kobject * kobj , struct kobj_attribute * attr , char * buf ) { switch ( mode ) { case OFF : return sprintf ( buf , "off

" ) ; // Display the state -- simplistic approach case ON : return sprintf ( buf , "on

" ) ; case FLASH : return sprintf ( buf , "flash

" ) ; default : return sprintf ( buf , "LKM Error

" ) ; // Cannot get here } } /** @brief A callback function to store the LED mode using the enum above */ static ssize_t mode_store ( struct kobject * kobj , struct kobj_attribute * attr , const char * buf , size_t count ) { // the count-1 is important as otherwise the

is used in the comparison if ( strncmp ( buf , "on" , count - 1 ) == 0 ) { mode = ON ; } // strncmp() compare with fixed number chars else if ( strncmp ( buf , "off" , count - 1 ) == 0 ) { mode = OFF ; } else if ( strncmp ( buf , "flash" , count - 1 ) == 0 ) { mode = FLASH ; } return count ; } /** @brief A callback function to display the LED period */ static ssize_t period_show ( struct kobject * kobj , struct kobj_attribute * attr , char * buf ) { return sprintf ( buf , "%d

" , blinkPeriod ) ; } /** @brief A callback function to store the LED period value */ static ssize_t period_store ( struct kobject * kobj , struct kobj_attribute * attr , const char * buf , size_t count ) { unsigned int period ; // Using a variable to validate the data sent sscanf ( buf , "%du" , & period ) ; // Read in the period as an unsigned int if ( ( period > 1 ) && ( period <= 10000 ) ) { // Must be 2ms or greater, 10secs or less blinkPeriod = period ; // Within range, assign to blinkPeriod variable } return period ; } /** Use these helper macros to define the name and access levels of the kobj_attributes * The kobj_attribute has an attribute attr (name and mode), show and store function pointers * The period variable is associated with the blinkPeriod variable and it is to be exposed * with mode 0666 using the period_show and period_store functions above */ static struct kobj_attribute period_attr = __ATTR ( blinkPeriod , 0666 , period_show , period_store ) ; static struct kobj_attribute mode_attr = __ATTR ( mode , 0666 , mode_show , mode_store ) ; /** The ebb_attrs[] is an array of attributes that is used to create the attribute group below. * The attr property of the kobj_attribute is used to extract the attribute struct */ static struct attribute * ebb_attrs [ ] = { & period_attr . attr , // The period at which the LED flashes & mode_attr . attr , // Is the LED on or off? NULL , } ; /** The attribute group uses the attribute array and a name, which is exposed on sysfs -- in this * case it is gpio49, which is automatically defined in the ebbLED_init() function below * using the custom kernel parameter that can be passed when the module is loaded. */ static struct attribute_group attr_group = { . name = ledName , // The name is generated in ebbLED_init() . attrs = ebb_attrs , // The attributes array defined just above } ; static struct kobject * ebb_kobj ; /// The pointer to the kobject static struct task_struct * task ; /// The pointer to the thread task /** @brief The LED Flasher main kthread loop * * @param arg A void pointer used in order to pass data to the thread * @return returns 0 if successful */ static int flash ( void * arg ) { printk ( KERN _ INFO "EBB LED: Thread has started running

" ) ; while ( ! kthread_should_stop ( ) ) { // Returns true when kthread_stop() is called set_current_state ( TASK_RUNNING ) ; if ( mode == FLASH ) ledOn = ! ledOn ; // Invert the LED state else if ( mode == ON ) ledOn = true ; else ledOn = false ; gpio_set_value ( gpioLED , ledOn ) ; // Use the LED state to light/turn off the LED set_current_state ( TASK_INTERRUPTIBLE ) ; msleep ( blinkPeriod / 2 ) ; // millisecond sleep for half of the period } printk ( KERN _ INFO "EBB LED: Thread has run to completion

" ) ; return 0 ; } /** @brief The LKM initialization function * The static keyword restricts the visibility of the function to within this C file. The __init * macro means that for a built-in driver (not a LKM) the function is only used at initialization * time and that it can be discarded and its memory freed up after that point. In this example this * function sets up the GPIOs and the IRQ * @return returns 0 if successful */ static int __init ebbLED_init ( void ) { int result = 0 ; printk ( KERN _ INFO "EBB LED: Initializing the EBB LED LKM

" ) ; sprintf ( ledName , "led%d" , gpioLED ) ; // Create the gpio115 name for /sys/ebb/led49 ebb_kobj = kobject_create_and_add ( "ebb" , kernel_kobj -> parent ) ; // kernel_kobj points to /sys/kernel if ( ! ebb_kobj ) { printk ( KERN _ ALERT "EBB LED: failed to create kobject

" ) ; return - ENOMEM ; } // add the attributes to /sys/ebb/ -- for example, /sys/ebb/led49/ledOn result = sysfs_create_group ( ebb_kobj , & attr_group ) ; if ( result ) { printk ( KERN _ ALERT "EBB LED: failed to create sysfs group

" ) ; kobject_put ( ebb_kobj ) ; // clean up -- remove the kobject sysfs entry return result ; } ledOn = true ; gpio_request ( gpioLED , "sysfs" ) ; // gpioLED is 49 by default, request it gpio_direction_output ( gpioLED , ledOn ) ; // Set the gpio to be in output mode and turn on gpio_export ( gpioLED , false ) ; // causes gpio49 to appear in /sys/class/gpio // the second argument prevents the direction from being changed task = kthread_run ( flash , NULL , "LED_flash_thread" ) ; // Start the LED flashing thread if ( IS_ERR ( task ) ) { // Kthread name is LED_flash_thread printk ( KERN _ ALERT "EBB LED: failed to create the task

" ) ; return PTR_ERR ( task ) ; } return result ; } /** @brief The LKM cleanup function * Similar to the initialization function, it is static. The __exit macro notifies that if this * code is used for a built-in driver (not a LKM) that this function is not required. */ static void __exit ebbLED_exit ( void ) { kthread_stop ( task ) ; // Stop the LED flashing thread kobject_put ( ebb_kobj ) ; // clean up -- remove the kobject sysfs entry gpio_set_value ( gpioLED , 0 ) ; // Turn the LED off, indicates device was unloaded gpio_unexport ( gpioLED ) ; // Unexport the Button GPIO gpio_free ( gpioLED ) ; // Free the LED GPIO printk ( KERN _ INFO "EBB LED: Goodbye from the EBB LED LKM!

" ) ; } /// This next calls are mandatory -- they identify the initialization function /// and the cleanup function (as above). module_init ( ebbLED_init ) ; module_exit ( ebbLED_exit ) ;

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).

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 : The performance of the LED flasher LKM at 25ms sleep delay

Figure 3: The performance of the LED flasher LKM at 50ms sleep delay : The performance of the LED flasher LKM at 50ms sleep delay

Figure 6: The performance of the LED flasher LKM at 1ms sleep delay : The performance of the LED flasher LKM at 1ms sleep delay

Figure 5: The performance of the LED flasher LKM at 5ms sleep delay : The performance of the LED flasher LKM at 5ms sleep delay

Conclusions



Click for the HTML and PDF version of the auto-generated Doxygen code documentation 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.