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:
- Writing a Linux Kernel Module — Part 1: Introduction, and
- Writing a Linux Kernel Module — Part 2: A Character Device,
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.
This article is focused on education and training rather than a deep practical need — I believe that if you can read a button press and flash an LED then you are well on your way to interfacing to most digital devices. Therefore, there are three different LKMs described in this article, each with its own individual purpose and needs:
- Example 1: Button Press, LED Light: In this example an LED lights when a button is pressed — simple! (no not really). To perform this task, the concept of kernel interrupts is introduced and the use of the library of code that can be accessed using linux/gpio.h. This example is used to test the interrupt performance.
- Example 2: Enhanced Button GPIO Driver: This example is used to introduce kobjects and a mechanism for adding your own entries to Sysfs. This allows you to send data to and receive data from the LKM at run time. The example also introduces the use of timing in kernel code.
- Example 3: Enhanced LED GPIO Driver: This example is used to flash an LED, which allows for a discussion on Linux kthreads. Essentially, an LED is flashed at a frequency using a kernel module, which can be controlled from Linux user space.
There are easier ways to interface to buttons and flash LEDs but these examples introduce several concepts that are vital to more complex kernel programming tasks.
Video Demonstration
A short YouTube video is provided here that presents an overview of the functionality of the LKMs that are developed in this article.
[youtube id=”ltCZydX_zmk” width=”800″ height=”467″ autoplay=”no” api_params=”” class=””]
Video 1: A video of the functionality of the LKMs that are described in this article.
The Circuit
A single circuit is used for this article, as illustrated in Figure 1. It is the same figure that is explained in Chapter 6 of my book and it is presented here for convenience. As described in detail within the chapter, a FET (or small-signal transistor) is used in order to gate the LED, which ensures that the current required to light the LED does not damage the BeagleBone. You can connect an LED directly to the GPIO with a strong current-limiting resistor, but it is not recommended. The push button does not have a pull-up or pull-down resistor, which is appropriate due to the fact that P9_27 is configured to have an internal pull-down resistor enabled by default. Always be very careful that you wire circuits like these correctly — a mistake may destroy your BeagleBone!
Figure 1: The LED and button circuit that is described in this article
[tagline_box backgroundcolor=”” shadow=”yes” shadowopacity=”0.7″ border=”0px” bordercolor=”” highlightposition=”top” content_alignment=”left” link=”https://github.com/derekmolloy/exploringBB/tree/master/extras/kernel/” linktarget=”_blank” modal=”” button_size=”” button_shape=”” button_type=”” buttoncolor=”” button=”Get Source Code” title=”Source Code for this Discussion” description=”” animation_type=”0″ animation_direction=”down” animation_speed=”0.1″ class=”” id=””]
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:
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.
[/tagline_box]
Example 1: Button Press, LED Light LKM
When interfacing to electronics circuits under embedded Linux you are immediately exposed to Sysfs and the use of low-level file operations. This approach can appear to be inefficient (especially if you have experience of traditional embedded systems); however, these file entries are memory-mapped and the performance is sufficient for most applications. I have demonstrated in my book that it is possible to achieve response times of about one third of a millisecond, with negligible CPU overhead, from within Linux user space by using ptreads, callback functions and sys/poll.h.
Unlike Linux user space, the Linux kernel space has support for interrupts. The first example in this article demonstrates how you can write an LKM that uses GPIOs and interrupts to achieve a faster response time than is possible in user space. I am not suggesting that you write all of your GPIO code in kernel space, but these examples may provide inspiration for discrete tasks that you can perform in kernel space — the higher-level code can still be written in Linux user space.
GPIOs and the Kernel
General Purpose Input/Outputs (GPIOs) are described in detail in Chapter 6 and in my previous articles/videos. These software-controlled input/outputs can be controlled from Linux user space using the GPIO Sysfs Interface (using a Linux shell directly or from within an executable), which enables you to activate a GPIO and set its state. For example, to activate the LED in Figure 1 using sysfs and turn the LED on/off, you can perform the following steps (as superuser):
root@beaglebone:/sys/class/gpio# ls
export gpiochip0 gpiochip32 gpiochip64 gpiochip96 unexport
root@beaglebone:/sys/class/gpio# echo 49 > export
root@beaglebone:/sys/class/gpio# ls
export gpio49 gpiochip0 gpiochip32 gpiochip64 gpiochip96 unexport
root@beaglebone:/sys/class/gpio# cd gpio49
root@beaglebone:/sys/class/gpio/gpio49# ls
active_low direction edge power subsystem uevent value
root@beaglebone:/sys/class/gpio/gpio49# echo out > direction
root@beaglebone:/sys/class/gpio/gpio49# echo 1 > value
root@beaglebone:/sys/class/gpio/gpio49# echo 0 > value
root@beaglebone:/sys/class/gpio/gpio49# cd ..
root@beaglebone:/sys/class/gpio# echo 49 > unexport
root@beaglebone:/sys/class/gpio# ls
export gpiochip0 gpiochip32 gpiochip64 gpiochip96 unexport
Interestingly, the steps to control the GPIOs in Linux kernel space are very similar to the steps above. Linux GPIOs can easily be accessed and controlled from kernel space using the functions that are described in linux/gpio.h(Version 3.8.x). Here are some of the most important functions that are available through the inclusion of this header file:
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:
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:
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.
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.
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\n"); // 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\n"); 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\n", 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\n", 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\n", 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\n", gpio_get_value(gpioButton)); printk(KERN_INFO "GPIO_TEST: The button was pressed %d times\n", 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!\n"); } /** @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)\n", 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
…
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
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
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.
[tagline_box backgroundcolor=”” shadow=”yes” shadowopacity=”0.7″ border=”0px” bordercolor=”” highlightposition=”top” content_alignment=”left” link=”” linktarget=”_blank” modal=”” button_size=”” button_shape=”” button_type=”” buttoncolor=”” button=”” title=”Debouncing Push Button Switches” description=”” animation_type=”0″ animation_direction=”down” animation_speed=”0.1″ class=”” id=””]
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.
[/tagline_box]
Example 2: Enhanced Button GPIO Driver LKM
The second example builds on the first example to create an enhanced GPIO driver, which permits a user to configure and interact with a GPIO Button using Sysfs. This module allows a GPIO button to be mapped to Linux user space so that you can interact with it. The best way to explain this is with an example — in this case the button is attached to GPIO 115 and it can be accessed and manipulated as follows:
molloyd@beaglebone:/sys/ebb/gpio115$ ls -l
total 0
-r--r--r-- 1 root root 4096 Apr 24 11:52 diffTime
-rw-rw-rw- 1 root root 4096 Apr 24 11:54 isDebounce
-r--r--r-- 1 root root 4096 Apr 24 11:52 lastTime
-r--r--r-- 1 root root 4096 Apr 24 11:52 ledOn
-rw-rw-rw- 1 root root 4096 Apr 24 11:52 numberPresses
molloyd@beaglebone:/sys/ebb/gpio115$ cat numberPresses
55
molloyd@beaglebone:/sys/ebb/gpio115$ cat ledOn
0
molloyd@beaglebone:/sys/ebb/gpio115$ cat lastTime
10:54:48:487088428
molloyd@beaglebone:/sys/ebb/gpio115$ cat diffTime
0.628361750
molloyd@beaglebone:/sys/ebb/gpio115$ echo 0 > isDebounce
molloyd@beaglebone:/sys/ebb/gpio115$ cat isDebounce
0
Despite the complexity involved in creating this LKM, the user interface is very straightforward and can be utilized by an executable on your embedded system that can be written in any programming language.
Sysfs is a memory-based file system that provides a mechanism to export kernel data structures, attributes, and linkages to Linux user space. The earlier section in this article “GPIOs and the Kernel” provides an example of how the /sys/class/gpio entries could be used to manipulate the behavior of kernel data structures to turn on and off an LED. The infrastructure that enables sysfs to function is heavily based on the kobject interface.
The kobject Interface
The driver model in Linux uses a kobject abstraction — to understand this model you must first appreciate the following important concepts [From Greg Kroah-Hartman’s Guide]:
- kobject: A kobject is a struct that consists of a name, a reference count, a type, a sysfs representation, and a pointer to a parent object (see Listing 3 below). Importantly, kobjects are not useful on their own, rather they are embedded within other data structures and used to control access. This is similar to the object-oriented concept of generalized top-level parent classes (e.g., such as the Object class in Java, or the QObject class in Qt).
- ktype: A ktype is the type of the object that the kobject is embedded within. It controls what happens when the object is created and destroyed.
- kset: A kset is a group of kobjects that can be of different ktypes. A kset of kobjects can be thought of as a sysfs directory that contains a collection of sub-directories (kobjects).
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:
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:
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
.__ATTR_RO(_name)
: Short-hand read-only attribute macro. You must pass the attribute variable name_name
and the macro sets the_mode
to be 0444 (read only), and the_show
function to be_name_show
.__ATTR_WO(_name)
: and__ATTR_RW(_name)
Write only and read/write. Not available in 3.8.x but added in 3.11.x.
The Source Code for this Example
Listing 4 provides the full source code for the Enhanced GPIO Button LKM. It may appear to be quite lengthy, but you will see that this is because I have added a lot of comment, and additional printk()
calls so that you can see exactly what is happening as the code is executing. This example builds on the work in Listing 2 — it also includes an LED so that you can observe interaction at the circuit itself.
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\n", 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\n", 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 \n", (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\n", 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\n", 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\n"); } else { gpio_set_debounce(gpioButton, 0); // set the debounce time to 0 printk(KERN_INFO "EBB Button: Debounce off\n"); } 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\n"); 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\n"); 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\n"); 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\n", 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\n", 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\n", 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!\n"); } /** @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\n", 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
, andgpioLED
). The use of LKM parameters is described in the first article in this series. This allows you to define different GPIOs for the button input and LED output — their sysfs mount names are automatically adjusted. The code also allows for a falling-edge interrupt in place of the rising-edge interrupt that is used by default. - There are five attributes associated with the kobject entry (ebb). These are: diffTime, isDebounce, lastTime, ledOn, and numberPresses. They are all read-only, with the exception of isDebounce (i.e., rising-edge or falling-edge) and numberPresses (i.e., can be set to any value, e.g., reset to 0).
- The
ebbgpio_irq_handler()
function performs the majority of the timing work. The clock time is stored and the inter-press time is determined each time that the interrupt is handled.
The module can be loaded in falling-edge mode and tested using:
molloyd@beaglebone:~/exploringBB/extras/kernel/button$ make
…
molloyd@beaglebone:~/exploringBB/extras/kernel/button$ ls -l *.ko
-rw-r--r-- 1 molloyd molloyd 10639 Apr 24 11:50 button.ko
molloyd@beaglebone:~/exploringBB/extras/kernel/button$ sudo insmod button.ko
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:
- The Sysfs filesystem for exporting kernel objects
- Everything you never wanted to know about kobjects, ksets, and ktypes
[tagline_box backgroundcolor=”” shadow=”yes” shadowopacity=”0.7″ border=”0px” bordercolor=”” highlightposition=”top” content_alignment=”left” link=”” linktarget=”_blank” modal=”” button_size=”” button_shape=”” button_type=”” buttoncolor=”” button=”” title=”” description=”” animation_type=”0″ animation_direction=”down” animation_speed=”0.1″ class=”” id=””]
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.[/tagline_box]
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
.
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.
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\n"); // Display the state -- simplistic approach case ON: return sprintf(buf, "on\n"); case FLASH: return sprintf(buf, "flash\n"); default: return sprintf(buf, "LKM Error\n"); // 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 \n 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\n", 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 \n"); 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 \n"); 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\n"); 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\n"); 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\n"); 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\n"); 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!\n"); } /// 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). - The
kthread_should_stop()
evaluates to a bool. When a function such askthread_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 thekthread_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
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
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.
Conclusions
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.
Derek,
nice tutorial! Thank you for all the help.
As I am making my own custom board based on the AM3358. I had to move the usr0-3 LEDs to other GPIOS, and also had to use the UART0 pins for other devices. At boot time I would like to set the new LED GPIOs and UART2 as the defaults so that I can get some sign of life and debugging information if I power the board and there are problems. I started looking into doing kernel loadable drivers, or device tree overlays to modify the pin muxes and the like, but I feel like I missing a bunch of information or possibly a better approach. How would you recommend I go about doing this? Any points in the right direction would be great.
Very much appreciated,
Mark
Update,
as a starting place, I have been able to swap the USER0 and USER1 LEDs on the BBB (pins 21 & 22) so that the heartbeat function now happens on USER1 instead of USER0. This process worked without issue by modifying the pin numbers within the “am335x-bone-common.dtsi” file and recompiling all the associated device tree files- then moving them onto the BBB uSD card. Next I would like to modify the pins associated with UART0 or at least which UART is the default so that the Boot process and Kernel debug info can be displayed in the terminal.
Is this the preferred process, or should I be paying attention to creating LKMs instead?
Thanks,
Mark
Hi Mark, I would start with the device tree configuration files (as you have done) to define the precise hardware for your custom design. You will then need to distribute a custom kernel build for your board. I think that makes the most sense. The kernel modules come later after you have defined your hardware. I would be interested to hear how you get on in building your own custom board. Kind regards, Derek.
Thank you for your wonderful selfless contributions. You are obviously an exceptional teacher and fine human being. Really appreciate your efforts
Thanks Mat.
Hi Derek,
I was wondering if I connected a LCD (or any sensor) in Place of the led and wanted to write some data to Lcd once interrupted by the button, how would I approach this. what are the functions that I need to apply Can you please guide on this??
Hi, Derek
When you are echoing commands like “on”,”off” which part in the program takes these arguments and stores them
Derek,
Nice tutorial.
I have a doubt about set_current_state(TASK_RUNNING) & set_current_state(TASK_INTERRUPTIBLE) what are they used for.
This a complex topic, but there is a very good article at: http://www.linuxjournal.com/article/8144 that may help you. Kind regards, Derek.
gpio_request(gpioLED, “sysfs”);
i have a confusion over this , the kernel code says “sysfs” here is label , what are we trying to do with this step exactly?
a brief explanation is appreciated
sysfs is kind of a driver. we have three types of drivers for a gpio one is gpio-keys, gpio-leds and another one is through sysfs. When you do menuconfig you will be choosing how the gpio’s needs to accessed in one of the three ways.
Nice tutorial! Thanks
Hi Derek,
Great tutorial. In my job, I do a lot of embedded work on ARM processors but never with Linux so I’m trying to get a handle on all this OS infrastructure. Wondering about the irq number that is retrieved by the call irqNumber = gpio_to_irq(gpioButton). When I call the function I get a value of 243. What is that related to? For example, if I look at the chip’s Technical reference manual there is no interrupt number that high.
Thanks,
Andy …
Hi Derek,excellent tutorial.,can i use BJT instead of mosfet in same circuit? i have bc547 available
Can you share some article on Linux device driver development.
Derek, Thank you very much for sharing your knowledge with us. You explain concepts in great detail. Keep posting videos I have learnt so many things from it.
Thanks again.
Mitul
Great tutorials,
Thank you.
Explaining I2C client drivers in another tutorial will be great.
regards
Hello,
I may be wrong but I could not understand the following line,
if(isDebounce) { gpio_set_debounce(gpioButton, DEBOUNCE_TIME);
It may be changed with
if(isDebounce) { gpio_set_debounce(gpioButton, isDebounce);
regards
Dear Derek,
Overall a good embedded book, but certain topics on embedded kernel / device drivers are missing the impact.
An embedded linux book without much of internals and drivers is similar to other embedded books with some linux flavour.
I was expecting something more in embedded linux with more
embedded kernel issues mixed with embedded hw.
May be, you can add more articles on these.
Best Regards,
babu krishnamurthy
Hi i have built a gpio module to handle my gpio button presses, i have compiled n built it, but when i try to load on my target device >> insmod madiva.ko
i get this error>>
Error: could not insert module madiva.ko: Invalid parameters
my .c file is over here >> http://www.4shared.com/file/RmyQpo6mba/madiva.html
my makefile over here >> http://www.4shared.com/file/6GAs0oewba/Makefile.html
could u please look at it then give me some direction, n point out the problem. FYI am building for a different board not a RPi.
Hi just found out the GPIOs i was using dont have EINT capabilities. is that why am getting the above errors. Is there a work around for this cause those are the only ports i have access to right now.
Hi. Great Tutorial, thanks for the effort.
One small question: This sample handles a single LED, what would be the approach to handle Multiple LED’s. Define all LED’s in the one LKM or load individual LKM for each LED ?
Thanks.
Phil.
If anyone else is interested, one approach would be to define the GPIO number through a module parameter loaded at runtime.
> insmod led.ko gpio=46
a simple example of module parameters is given here:
http://www.tldp.org/LDP/lkmpg/2.6/html/x323.html
Hi Derek,
This is really a useful article. It helps me a lot.
I am developing a I2C client driver and in that I am creating a sysfs interface for it. For that I am calling function “kobject_create_and_add” in the ‘probe’ function of the driver. But in that case it is not creating the objects like ‘blinkPeriod’, ‘mode’ etc. What is the reason?
Thanks,
Dhaval
Hi Derek,
Really great stuff! It is immediately useful for things we are doing with the BeagleBone Black to run a printer prototype here in our R&D lab.
One comment:
In the exit routine, the IRQ is freed after the LED GPIO is unexported. In the IRQ service routine, the LED GPIO is written. There is a tiny (but non-zero) chance that an IRQ could come in the short time interval after the LED GPIO is unexported.
Perhaps it would be best to free the IRQ before unexporting the LED GPIO?
Not that you would push the button during the exit routine, but this code is really useful as a template for lots of things.
Hi Derek,
I did exactly the same steps of Example 1, but the performance on my Beaglebone Black are too different from yours. The latency in your test was approximately between 15 and 25 microseconds. My test on the oscilloscope show a latency of 260 microseconds, and even more. The cpu frequency is set to 1GHz (performance mode). What else did you set to have this great performance?
I really hope to hearing from you.
Thanks a lot
Mike
Hi derek,
This is the second time I try to post (the first comment was deleted). Speaking about Example 1, I got different latency results (between 140-200microseconds with a cpu frequency of 1GHz). I wonder, did you do something else to have that great results?
Hope to hearing from you,
Best Regards
Mike
(PS. I love Dublin!)
Hi Mike, apologies there is a problem with the website at the moment — it appears to be the target of some quite aggressive spam bots. I didn’t do anything additional to get this level of performance on the BBB at 1GHz. It is running using a standard Debian image. Granted, I only ran the test a handful of times but I didn’t do any rigorous testing. I’ll be doing similar work soon on the RPi and I will compare the results. Kind regards, Derek.
Hi Derek,
Great document and tutorial.
Still i am not getting test.c file for led program. In led directory there are only three files – 1. led.c, 2. Makefile and 3. led.ko but as we know that to test the driver we need test.c file as you have included for others.
So, if you answer this regarding my query then it will be great.
Thank you
Hi Derek,
Thank you very much for sharing your knowledge with us. do you write the new article on your blog?
Derek –
Thank you so much for posting this. I’ve been searching for days on how to “properly” use the GPIOs. Brilliant, just brilliant that you posted this much help.
Thanks!
Hi Derek,
Thanks for all your great works firstly.
I’m doing a projet with BBB which involve the kernel module programming. After all, i wanna using the SPI protocol in my kernel module. I enabled the BBB-SPIDEV1.0 and running the SPI programs (write data to I/O expander MCP23s17) in User-Space without problem. So i wonder if there is a way to use SPI to do the same things in kernel module.
Thanks!
Hi Derek
Firstly, thanks for the great tutorial.
I’m in the process of writing a kernel module to manage timers on the BeagleBone Black. What I’d really like to be able to do is toggle a GPIO between input and output modes, preserving the internal pullup. I could do this statically in the following way:
fragment@0 {
target = ;
__overlay__ {
qot_am335x_pins: qot_am335x_pins {
pinctrl-single,pins = ;
};
};
};
However, I would like to be able to do this from within a kernel module. What I am finding rather confusing is the relationship between the pinmux overlay fragments (above), the beaglebone-pinmux-helper kernel code and the headers.
Also, I’m working on 4.1.12 right now, so maybe a little ahead of the official branch.
I have the following fragment in my overlay:
…
pin1 {
label = “TIMER5”;
timer = ;
gpios = ;
};
….
And I use the devm_get_gpiod_from_child(dev, NULL, child) to grab a struct gpio_desc pointer to the GPIO descriptor. I have confirmed that this points to the correct pin. I can then theoretically call gpiod_direction_output(…) or gpiod_direction_input(…) to toggle from one mode to the other. However, the pullup gets lost on switching to input. Is there a way to achieve this?
Many thanks
Andrew
Dear Derek,
Your tutorial is excellent and thank you for sharing your knowledge!
I tried to compile the button.c and got a compile time error with permissions 0666:
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);
if I set the permissions as 0555 it compiles but I get a run-time error of invalid permissions.
Any idea why 0666 is not working?
Thanks,
Asim
Hi Asim, I noticed this recently too. It appears that a decision has been made to restrict the use of 0666 on recent kernels, but the documentation on this issue is sparse. You are correct though, it is down to the permissions alone. Kind regards, Derek.
Is there anyway to do the write from user space in the recent kernels? I am using 4.4.0-41-generic and 4.4.38-tegra.
Changing to 0660 worked for me
As a linux novice I’d like to understand are there anything that I should do to be able to control e.g. “led.c” (listing 6) params “mode” or “blinkPeriod” as __ATTR permission is limited to 0660 e.g. in the kernel 3.18.3+ (Raspbian).
I cannot control params even when trying these as sudo. I’m getting the following response (fyi: led is connected to gpio pin #7):
pi@raspberrypi ~ $ sudo echo off > /sys/ebb/led7/mode
-bash: /sys/ebb/led7/mode: Permission denied
@TimoV: I had the same problem but there’s a fix.
Still compile with 0660.
Then change permissions on those files using chmod.
chmod 666 /sys/ebb/led7/*
After I did this, I was able to access (r/w) these files as a regular user (not root).
Hello Derek!
One thing im really interested to learn is how to make drivers compatible with device tree. How to use probe function, compatible string. How to describe the driver so I can enable it in the device tree. Maybe you could extend this article to a part 4? That would be great.
I really like your work on explaining the embedded linux world for us! Thank you!
Hear Hear!
Hello Derek,
Thank you for this great series of tutorials regarding LKMs.
Here are some wishes/ideas about new articles bridging the material covered in your book and the LKM articles:
– Platform drivers, platform devices and how they relate to Device Trees
– udev as the Linux hotplug manager
– How to use the BBB as a platform for getting a grasp of the Linux Device Model
I look forward with enthusiasm to building more know-how based on your clear, structured way of explaining things. Are you BTW working on a new book?
Best Regards
Panagiotis
Hi Derek,
Another thanks on a great write-up. All you tutorials are great help.
Question for you here. If you wanted more buttons and have interrupts each with their own led to light up would you extend this LKM for each button/LED combination or would you have a standalone LKM for each button/LED combo?
Kind Regards,
Matt
how to give command line argument??
Very nice example !!
Minor:
sscanf(buf, “%du”, &numberPresses);
Regarding to:
http://www.cplusplus.com/reference/cstdio/scanf/
the %d and %u are not going together and you probably mean %u
Another minor:
“They are all read-only, with the exception of isDebounce (i.e., rising-edge or falling-edge)”
The Debounce is not about the rising-edge or falling-edge but enable or disable “noise suppression” on the push button
And another minor:
“MODULE_PARM_DESC(blinkPeriod, ” LED blink period in ms (min=1, default=1000, max=10000)”);”
The minimal use in the code for the period is 2 instead of 1
Sorry to keep you busy
/** @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;
}
In this case you return the period instead of the count
In an earlier use case you mention:
"return return should return the total number of characters used from the buffer"
So that suggests to return count instead
Hi,
The flash period time is “msleep(blinkPeriod/2);” + time of lines before the msleep function.
So the total period will be slightly higer (and the frequency slightly lower)
Is it possible to exlude time of the other code from the period time.
E.g. by using a timer or setting the sleep time as soon as the function is entered (and then somehow perform the functional code)
Hi,
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.
You do not mention that kthread_stop() is waiting until kthread is completed.
This could mean that the kthread is still running (msleep(..) not yet expired) while the module is being removed.
Is there some kind of handshake mechanism in place to prevent unexpected behaviour?
Hi,
Another miror.
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.
But your code will not update the period if you use 1ms.
In case you tested with e.g. 2ms. Any idea why the period time is 7.8ms instea of 4.0ms?
Anyway your example helped me a lot, and to support your work i will definitely buy your Raspberry PI book
Thanks.
Hai derek can you tell me how to make it as a part of kernel source.
I tried to make it as part of kernel source and compiled. The source got conmpiled. I place the source in drivers directory. I added the config in defconfig. I am able to see the option in menuconfig. But after building the source my .ko file is not included in modules.order file. But it is listed in modules.builtin.
Thank you very much Derek. A couple of hours spent on your site have been more “profitable” than several days spent on several other books/sites. Much more than a starting point!
Hello, please delete all my previous posting. Could fix the issue – I have been changing permission at wrong place.
Somewhere you have a fixed coded 0666 and Linux seems not like it to be writable as user. Just change it to 0664.
#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))
I was long stuck on this construct above, which gives me the error, as I didn’t get what this is doing: double negation and bitfield operator (never heard about it before). Even in a few huge C books not mentioned. Now I now ….
One question:
If I need a longer action in user space, as a result from a change of a GPIO – how can I do this best? Polling the gpio value is not an option, as it is to slow and has high load.
How do I access the attributes (like numberPresses and dffTime) from a userspace C++ program? Do I have to incorporate a file_operations struct as in the character device driver in Part 2?
Thanks for the great source!
If you want to let the interrupt trigger on rising and falling edge, inside the interrupt handler can you distinguish which case it is?
Hi,
Really great tutorials, clean explanations and codes. Thank you!!! I am looking forward to see more tutorials from you.
It would be perfect to see server-client tcp application in kernel modules =)
One question:
If I need a longer action in user space, as a result from a change of a GPIO – how can I do this best? Polling the gpio value is not an option, as it is to slow and has high load.
Thanks for your article.
Learning from master.
How should I modify the LKM to instantiate multiple instances in order to drive multiple sets of button/led?
Thank you so much!
Antonio
Hey Derek!
First of all let me congratulate you for the effort that you have put in your article series about LKM’s: they’re terrific! You perfectly describe what is being done and why, which it sometimes unusual when looking out for information. For this matter, bravo!
I am implementing a character device driver (based actually on the ebbchar device in your second article) where I want to be able to read the analog pin AIN1 in a similar way to what you do with GPIOs using the header. Do you know any header that provides access to raw-reads from the ADC that I can include from my LKM so that I will get a sample from a given channel from kernel space? (It can be done by using SysFs from user-space but I am interested in retrieving these ADC samples directly from the LKM)
Many thanks in advance, and any advice is highly appreciated!
Cheers,
/Santiago
Hi Derek,
I have been running into an issue porting a driver from a 3.8 kernel to 4.9.30-bone4 (baseline kernel) on a Beaglebone Green.
In short the problem is that requesting the interrupt for the eCAP0 unit straight from the am335x-techref table 6.3, i.e. 31, no longer works. The interrupt routine does not get called an no interrupts are listed in /proc/interrupts. What caught my attention and possible connection to your article is the content of the line I get after registering:
31: 0 44e07000.gpio 4 Edge ecap
I frankly don’t know what all columns show from /proc/interrupts, except the IRQ number, count, detection type and module, but “44e07000.gpio” looks suspiciously like the interrupt is somehow mapped from some GPIO controller and not INTC as most other interrupts are.
Also, using a probing algorithm with probe_irq_on and probe_irq_off yields no interrupts even though I force it from the capture unit.
If you could shed some light on this I would be most grateful!
Best regards,
– Jan.
Why does it print many times GPIO_TEST: Interrupt! (button state is 1) , even If I press only once?
Really an informative post. Thanx for sharing. Could you tell me How to connecet WIFI usb for Beaglebone?
Hi Derek,
The same tutorial can be used for Raspberry pi.
Hi Derek!
Thanks a lot for this set . Maybe it is time for part 4 ?
I interested in how i could to know wich interrupt has occured ? If i use raising and falling trigger ?
Hi Derek,
I want to detect long button press, about 3-5 seconds. Kindly help me regarding this.
Hi Derek,
I want to detect long press on GPIO pin, how can we do that.
Great post!
Do you have a post showing how to write an ISR that handles the clock interrupt, in order to generate a constant frequency in the GPIOs (for a certain number of steps)?
So a module that will get an instruction from the user space like: “output 100khz, for 100 steps”?
The content is useful. This information very helpful for everyone !! Thanks for this awesome deal and keep updating us in future…You make a lot of good points in your article. Keep sharing.
17.0 Amps Variable frequency drives
Thanks Derek for this nice write-up. A small remark, should not
ebbgpio_irq_handler() return irqreturn_t instead of irq_handler_t. irq_handler_t is the typedef for the handler signature
in linux/interrupt.h
typedef irqreturn_t (*irq_handler_t)(int, void *);
and irqreturn_t is defined in linux/irqreturn.h
enum irqreturn {
IRQ_NONE = (0 << 0),
IRQ_HANDLED = (1 << 0),
IRQ_WAKE_THREAD = (1 << 1),
};
typedef enum irqreturn irqreturn_t;
Thanks
Ali Mohamad Qasimi
Hello Derek, the information you provide is essential in the development that I am doing on the beaglebone black. I am having a problem compiling the LKM ,LED and Button projects. I get errors. The version of linux I have on my beaglebone is 4.9.78-ti-r94. I can compile the other LKM’s from part 1 and 2 fine.
Please help
Thanks
Hi Derek,
Thank you very much for sharing your knowledge with us. do you write the new article on your blog?
Thanks
Hi Derek, thank you very much for your efforts. They have been teaching us lot.
I am trying to port opentherm protocol, that is available for arduino on the net you might probably see it, to BBB.
There are functions to disable and enable the global interrupts in arduino.
They had been used to not to disturb data capturing, i guess.
Is there a way to implement them for BBB? I find the local_irq_enable()/disable() functions in linux headers,
but I guess that i cannot use them in user space app. Regards.
Awesome and very well expressed.
Shows a selfless human who wants to help people learn.
Awesome.
Hello derek,
It was soo good. Can you explain me about printing the data instead of LED on.
Hi Derek,
Value added information!!. Thanks for your time to sharing the knowledge and making other to understand.
Simple and understandable with real example.
Can you share the link for you book to download or purchase ?
It would be great if you provide information regarding the calculation of GPIO49 and GPIO115:
Button: GPIO3_19 = ( 3 x 32 ) + 19 = 115
LED: GPIO1_17 = (1 x 32 ) + 17 = 49
Regards,
Malatesh
Hi Derek – Nice 3-part guide, really. I followed this since I wanted to make an LKM for my ISR. However, there are some user-space handling that I need to do whenever the ISR in the kernel space (via LKM) is triggered. Is there a way, that in the LKM’s ISR, I will make the LKM call a user space function? If there is, how do I do it? If there is none, what alternatives can I do to have this use case? I believe this use case is quite… necessary in the sense that, some complicated algorithm implementation can only be done in the userspace (with all the linked libraries and stuff). The user space just wanted to be informed when that particular interrupt was received by the LKM. Please advise your insights on this, thank you. 🙂
I’m having trouble finding a way how to boot from the SD card having only u-boot: MLO and u-boot.img files.
Has anyone tried this ?