Loadable Kernel Modules

If you got to write a driver for the Linux kernel, you basically have two options: Either you compile it directly into the kernel or you use loadable kernel modules (LKM). The latter option has the advantage, as you might already have suggested, to load the driver dynamically during run time. I will focus in this post on some of the things one has to remember when creating a LKM. For a complete description please refer to Derek Molloy’s guide about LKM which has helped me a lot in understanding its concepts and implementation. All his code is also on GitHub.
The following code snippets can be useful in a LKM and give something like a starting point or show you what to remember; I tried to split them into topics so that individual information can be found without searching through a large piece of code.

The includes

#include <linux/init.h> // for macros for the markup functions as __init and __exit
#include <linux/module.h> // for core headers to load LKMs into the kernel
#include <linux/kernel.h> // for types, names and macros
//---- Following headers are not needed for a primitive hello world LKM ----
#include <linux/gpio.h> // for GPIO functions
#include <linux/kobject.h> // for sysfs bindings
#include <linux/kthread.h> // for kernel threads
#include <linux/delay.h> // for msleep

Module parameters

To add a description to you LKM use these lines:

MODULE_LICENSE("Your License");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("The Description");
MODULE_VERSION("Version Number");

All LKM arguments which should be modified or accessed from outside can be placed right below. Pay attention to not use global variables and to describe the LKM arguments.

static char *paramName = "hello_world"; // could also be e.g. a GPIO pin (int in that case)
module_param(paramName, charp, S_IRUGO); // variable name, type (here 'charp') and permission
MODULE_PARM_DESC(paramName, "Name to be displayed in /var/log/kern.log");

Init and exit

These two functions will be called when the LKM is loaded or unloaded. Remember to use the module_init and module_exit macros to declare the init and exit function and to pass them the exact names of your corresponding function.

static int __init example_init(void){
   printk(KERN_INFO "Hello, this is an example LKM\n", name);
   return 0;
}

static void __exit example_exit(void){
   printk(KERN_INFO "Bye\n", name);
}

module_init(example_init);
module_exit(example_exit);

Interrupts

To enable interrupts so that an event on a GPIO will tell the kernel to run your LKM next you have to implement an interrupt request handler which is called when the interrupt occurs and register it in the __init method of your module.

irqNumber = gpio_to_irq(gpioButton); // gets the irqNumber for the used gpio
result = request_irq(irqNumber,
 (irq_handler_t) example_irq_handler,
 INTERRUPT_FLAG,                     // provided in /usr/src/linux-headers…/include/linux/interrupt.h - see below for quick reference
 "example_irq_handler",              // used in /proc/interrupts
 NULL);
static irq_handler_t example_irq_handler(unsigned int irq, void *dev_id, struct pt_regs *regs){
    // handle the interrupt
    return (irq_handler_t) IRQ_HANDLED; // announce completion or errors
}

Interrupt flags as seen in /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 disabled
#define IRQF_SHARED 0x00000080 // allow sharing with multiple devices
#define IRQF_PROBE_SHARED 0x00000100 // set by callers expecting sharing mismatches
#define __IRQF_TIMER 0x00000200 // mark as timer interrupt
#define IRQF_PERCPU 0x00000400 // Interrupt per cpu
#define IRQF_NOBALANCING 0x00000800 // exclude from irq balancing
#define IRQF_IRQPOLL 0x00001000 // used for polling
#define IRQF_ONESHOT 0x00002000 // not reenabled after  hardening handler finished.
#define IRQF_NO_SUSPEND 0x00004000 // no disabling during suspend
#define IRQF_FORCE_RESUME 0x00008000 // force enabling it on resume
#define IRQF_NO_THREAD 0x00010000 // cannot be threaded
#define IRQF_EARLY_RESUME 0x00020000 // resume early during syscore
#define IRQF_TIMER (__IRQF_TIMER | IRQF_NO_SUSPEND | IRQF_NO_THREAD)

Using gpio

To be able to access the GPIOs you will have to use the functions from linux/gpio.h I picked the following excerpt from Derek Molloy’s site I linked above for quick reference. Remember to set up the GPIO in your module’s __init function and to reset them in the __exit function. For accessing the GPIOs from user space please see https://www.kernel.org/doc/Documentation/gpio/sysfs.txt and for GPIO interface in kernel space https://www.kernel.org/doc/Documentation/gpio/gpio.txt

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

Interacting with your driver

If you want to allow users to configure and interact with you driver using sysfs you need to use the kobject interface. The kobject looks like this:

struct kobject {
    char *k_name;
    char name[KOBJ_NAME_LEN]; // len is defined as 20
    struct kref kref; // reference count
    struct list_head entry; // pointer into a circularly linked list (members of the kset)
    struct kobject *parent; // parent kobject
    struct kset *kset; // pointer to the kobjet's kset or null
    struct kobj_type *ktype; // type of the kobject
    struct dentry *dentry; // sysfs directory entry
};

To map the kobject to the file system use

static struct kobject *my_kobj = kobject_create_and_add("sysfsFolderName", kernel_kobj); // kernel_kobj points to /sys/kernel; so its /sys/kernel/sysfsFolderName

This allows to access  the LKM attributes after implementing the _store and _show function for the attribute you want to allow to be configured by the user:

static ssize_t your_attribute_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf);
static ssize_t your_attribute_store(struct kobject *kobj, struct kobj_attribute *attr, char *buf);

For defining the attributes you can use the makros in the sysfs.h header as follows:

int yourVariable = 0; // sets the default; exposed as r/w later
int exampleReadOnlyVariable = 314; // exposed as ro later

/*
To pick up the _store and _show functions for this particular example from above again
it would look like this
*/
static ssize_t yourVariable_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) {
    return sprintf(buf, "%d\n", yourVariable);
}
static ssize_t yourVariable_store(struct kobject *kobj, struct kobj_attribute *attr, char *buf){
    sscanf(buf, "%du", &yourVariable);
    // maybe do some GPIO configuration etc. here
};

static ssize_t exampleReadOnlyVariable_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) {
    return sprintf(buf, "%d\n", yourVariable);
}


// define the LKM attributes
static struct kobj_attribute your_attr = __ATTR(yourVariable, MODE, yourVariable_show, yourVariable_store);
static struct kobj_attribute example_read_only_attr = __ATTR_RO(exampleReadOnlyVariable);

// declare an array of attributes the attribute group is created with
static struct attribute *your_attrs[] = {
    &your_attr.attr,
    &example_read_only_attr.attr,
    NULL,
}

// declare the attribute group along with the attribute array from above and a name which will be exposed on sysfs 
static struct attribute_group attr_group = {
    .name = "attrFolderName",
    .attrs = your_attrs,
}; 

Note that we used the _store and _show functions for the corresponding attribute from above. MODE is the mode used to expose yourVariable, e.g. 0666 for r/w access. When declaring the array of attributes we used the kobj_attribute created with the macros from sysfs.h which in turn know the variable to be exposed. We then can add the attributes to the /sys/kernel/aFolderName in our module __init function like so:

static struct kobject *my_kobj = kobject_create_and_add("sysfsFolderName", kernel_kobj); // also see above..
sysfs_create_group(my_kobj, &attr_group); // will map the attributes to /sys/kernel/sysfsFolderName/atrrFolderName

Using kernel threads

If you want to run a kernel thread to perform some action you will need the kthread.h header file. You can use your __init function for starting the thread:

static struct task_struct *task = kthread_run(yourThread, DATA, "threadName"); //

DATA is the data sent to the kthread and can be NULL. yourThread is a pointer to the thread function:

static int yourThread(void *arg){
    // do some init calls or what you need
    while(!kthread_should_stop()){ // true when kthread_stop() or similar is called
        set_current_state(TASK_RUNNING);
        // do your work
        set_current_state(TASK_INTERRUPTIBLE); 
        msleep(SLEEP_MILLIS);
    }
    // do some tear down call etc.
}

To stop the kthread in the __exit function use

kthread_stop(task);

Loading and unloading the lkm

Use sudo insmod example_lkm.ko and sudo rmmod example_lkm.ko to load or unload the LKM. The modinfo or lsmod calls let you find out information about the kernel module.

Loadable Kernel Modules