Reader small image

You're reading from  Linux Device Drivers Development

Product typeBook
Published inOct 2017
Reading LevelIntermediate
PublisherPackt
ISBN-139781785280009
Edition1st Edition
Languages
Right arrow
Author (1)
John Madieu
John Madieu
author image
John Madieu

John Madieu is an embedded Linux and kernel engineer living in Paris, France. His main activities consist of developing device drivers and Board Support Packages (BSPs) for companies in domains such as IoT, automation, transport, healthcare, energy, and the military. John is the founder and chief consultant at LABCSMART, a company that provides training and services for embedded Linux and Linux kernel engineering. He is an open source and embedded systems enthusiast, convinced that it is only by sharing knowledge that we can learn more. He is passionate about boxing, which he practiced for 6 years professionally, and continues to channel this passion through training sessions that he provides voluntarily.
Read more about John Madieu

Right arrow

Kernel Facilities and Helper Functions

The kernel is a standalone piece of software, as you'll see in this chapter, that does not make use of any C library. It implements any mechanism you may encounter in modern libraries, and even more, such as compression, string functions, and so on. We will walk step by step through the most important aspects of such capabilities.

In this chapter, we will cover the following topics:

  • Introducing the kernel container data structure
  • Dealing with the kernel sleeping mechanism
  • Using timers
  • Delving into the kernel locking mechanism (mutex, spinlock)
  • Deferring work using a dedicated kernel API
  • Using IRQs

Understanding the container_of macro

When it comes to managing several data structures in the code, you'll almost always need to embed one structure into another and retrieve them at any moment without being asked questions about memory offset or boundaries. Let's say you have a struct person, as defined here:

 struct person { 
     int  age; 
     char *name; 
 } p;

By only having a pointer on age or name, you can retrieve the whole structure wrapping (containing) that pointer. As the name says, the container_of macro is used to find the container of the given field of a structure. The macro is defined in include/linux/kernel.h and looks like:

#define container_of(ptr, type, member) ({               \ 
   const typeof(((type *)0)->member) * __mptr = (ptr);   \ 
   (type *)((char *)__mptr - offsetof(type, member)); }) 

Don't be afraid of the pointers; just...

Linked lists

Imagine you have a driver that manages more than one device, let's say five devices. You may need to keep a track of each of them in your driver. What you need here is a linked list. Two types of linked list actually exist:

  • Simply linked list
  • Doubly linked list

Therefore, kernel developers only implement circular doubly linked lists because this structure allows you to implement FIFO and LIFO, and kernel developers take care to maintain a minimal set of code. The header to be added in the code in order to support lists is <linux/list.h>. The data structure at the core of list implementation in the kernel is the struct list_head structure, defined as the following:

struct list_head { 
    struct list_head *next, *prev; 
 }; 

The struct list_head is used in both the head of the list and each node. In the world of the kernel, before a data structure...

Kernel sleeping mechanism

Sleeping is the mechanism by which a process relaxes a processor, with the possibility of handling another process. The reason why a processor can sleep could be for sensing data availability, or waiting for a resource to be free.

The kernel scheduler manages a list of tasks to run, known as a run queue. Sleeping processes are not scheduled anymore, since they are removed from that run queue. Unless its state changes (that is, it wakes up), a sleeping process will never be executed. You may relax a processor as soon as you are waiting for something (a resource or anything else), and make sure a condition or someone else will wake it up. That said, the Linux kernel simplifies the implementation of the sleeping mechanism by providing a set of functions and data structures.

...

Delay and timer management

Time is one of the most used resources, right after memory. It is used to do almost everything: defer work, sleep, scheduling, timeout, and many other tasks.

There are two categories of time. The kernel uses absolute time to know what time it is, that is, the date and time of the day, whereas relative time is used by, for example, the kernel scheduler. For absolute time, there is a hardware chip called real-time clock (RTC). We will deal with such devices later in the book in Chapter 18, RTC Drivers. On the other side, to handle relative time, the kernel relies on a CPU feature (peripheral), called a timer, which, from the kernel's point of view, is called a kernel timer. Kernel timers are what we will talk about in this section.

Kernel timers are classified into two different parts:

  • Standard timers, or system timers
  • High-resolution timers
...

Kernel locking mechanism

Locking is a mechanism that helps shares resources between different threads or processes. A shared resource is data or a device that can be accessed by at least two users, simultaneously or not. Locking mechanisms prevent abusive access, for example, a process writing data when another one is reading in the same place, or two processes accessing the same device (the same GPIO for example). The kernel provides several locking mechanisms. The most important are:

  • Mutex
  • Semaphore
  • Spinlock

We will only learn about mutexes and spinlocks, since they are widely used in device drivers.

Mutex

Mutual exclusion (mutex) is the de facto most used locking mechanism. To understand how it works, let's see what...

Work deferring mechanism

Deferring is a method by which you schedule a piece of work to be executed in the future. It's a way to report an action later. Obviously, the kernel provides facilities to implement such a mechanism; it allows you to defer functions, whatever their type, to be called and executed later. There are three of them in the kernel:

  • SoftIRQs: Executed in an atomic context
  • Tasklets: Executed in an atomic context
  • Workqueues: Executed in a process context

Softirqs and ksoftirqd

Software IRQ (softirq), or software interrupt is a deferring mechanism used only for very fast processing, since it runs with a disabled scheduler (in an interrupt context). You'll rarely (almost never) want to deal with softirq...

Kernel interruption mechanism

An interrupt is the way a device halts the kernel, telling it that something interesting or important has happened. These are called IRQs on Linux systems. The main advantage interrupts offer is to avoid device polling. It is up to the device to tell if there is a change in its state; it is not up to us to poll it.

In order to get notified when an interrupt occurs, you need to register to that IRQ, providing a function called an interrupt handler that will be called every time that interrupt is raised.

Registering an interrupt handler

You can register a callback to be run when the interruption (or interrupt line) you are interested in gets fired. You can achieve that with the request_irq() ...

Threaded IRQs

The main goal of threaded IRQs is reducing the time spent with interrupts disabled to a bare minimum. With threaded IRQs, the way you register an interrupt handler is a bit simplified. You does not even have to schedule the bottom half yourself. The core does that for us. The bottom half is then executed in a dedicated kernel thread. We do not use request_irq() anymore, but request_threaded_irq():

int request_threaded_irq(unsigned int irq, irq_handler_t handler,\ 
                            irq_handler_t thread_fn, \ 
                            unsigned long irqflags, \ 
                            const char *devname, void *dev_id) 
 

The request_threaded_irq() function accepts two functions in its parameters:

  • @handler function: This is the same function as the one registered with request_irq(). It represents the top-half function, which runs in an atomic context...

Invoking user space applications from the kernel

User-space applications are, most of the time, called from within the user space by other applications. Without going deep into the details, let's see an example:

#include <linux/init.h> 
#include <linux/module.h> 
#include <linux/workqueue.h>    /* for work queue */ 
#include <linux/kmod.h> 
 
static struct delayed_work initiate_shutdown_work; 
static void delayed_shutdown( void ) 
{ 
   char *cmd = "/sbin/shutdown"; 
   char *argv[] = { 
         cmd, 
         "-h", 
         "now", 
         NULL, 
   }; 
   char *envp[] = { 
         "HOME=/", 
         "PATH=/sbin:/bin:/usr/sbin:/usr/bin", 
         NULL, 
   }; 
 
   call_usermodehelper(cmd, argv, envp, 0); 
} 
  
static int __init my_shutdown_init( void ) 
{ 
    schedule_delayed_work(&delayed_shutdown...

Summary

In this chapter, we discussed the fundamental elements to start driver development, presenting every mechanism frequently used in drivers. This chapter is very important, since it discusses topics other chapters in this book rely on. The next chapter, for example, dealing with character devices, will use some elements discussed in this chapter.

lock icon
The rest of the chapter is locked
You have been reading a chapter from
Linux Device Drivers Development
Published in: Oct 2017Publisher: PacktISBN-13: 9781785280009
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
undefined
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $15.99/month. Cancel anytime

Author (1)

author image
John Madieu

John Madieu is an embedded Linux and kernel engineer living in Paris, France. His main activities consist of developing device drivers and Board Support Packages (BSPs) for companies in domains such as IoT, automation, transport, healthcare, energy, and the military. John is the founder and chief consultant at LABCSMART, a company that provides training and services for embedded Linux and Linux kernel engineering. He is an open source and embedded systems enthusiast, convinced that it is only by sharing knowledge that we can learn more. He is passionate about boxing, which he practiced for 6 years professionally, and continues to channel this passion through training sessions that he provides voluntarily.
Read more about John Madieu