Interrupt handler thread

Single thread for handling interrupts that may trigger blocking functions and therefore may only be called in thread context. More...

Detailed Description

Single thread for handling interrupts that may trigger blocking functions and therefore may only be called in thread context.

Author
Gunar Schorcht gunar.nosp@m.@sch.nosp@m.orcht.nosp@m..net

Interrupt Context Problem

There are many devices connected to the host CPU via a bus system such as I2C or SPI. Such devices are, for example, sensors and actuators. Since these devices share the bus system, their access to the bus system has to be synchronized.

In addition, such devices often use interrupts to trigger the execution of certain functions, such as reading the sensor values. That is, when an interrupt occurs, the driver requires often access to the bus system, for example, to read the status registers.

The access to SPI and I2C interfaces is synchronized by mutual exclusion using mutexes. If one thread tries to access such an interface that is already being used by another thread, it will be blocked until the interface becomes available. Although this synchronization works in the thread context, it does not work in the interrupt context. Accessing such an interface within an ISR would interfere with an already existing interface access. This problem is called interrupt context problem.

The only solution to this problem is not to call any function that interacts with a device directly from interrupt context. Rather, the ISR should only indicate the occurrence of the interrupt. The interrupt is then handled asynchronously by a normal function within a thread context.

The problem now is that driver modules usually do not use their own thread, but run in the context of the calling thread. However, it can not be left to the application thread to handle the interrupts of driver modules. The only solution would be to have a separate interrupt handler thread for each driver module that uses interrupts along with SPI or I2C interfaces. However, as the number of such modules increases, many resources (thread contexts, including their thread stacks) are allocated only for interrupt handling.

Solution

The solution is to have a single interrupt handler thread which serializes the interrupts of such driver modules and calls the functions of the driver modules to handle the interrupts from its thread context.

For this purpose, each driver module that wants to use this interrupt handler thread has to define an interrupt event of type irq_event_t for each of its interrupt sources. The interrupt event contains a reference to the function to be called to handle the interrupt.

When an interrupt of the corresponding source occurs, the ISR of the driver module registers only the interrupt event associated with the interrupt source with the irq_event_add function on the handler. The handler places the interrupt event in an pending interrupt queue.

Each interrupt event can be registered on the handler only once. That is, if the same interrupt occurs multiple times, only its first occurence is placed to the pending interrupt queue and is handled.

When the interrupt handler thread gets the CPU, it processes all pending interrupt events in the order of their occurence before it yields.

Usage

The single-interrupt handler thread can be used not only for driver modules with bus access, but for any interrupt handling that may trigger a blocking function and therefore cannot be called in an interrupt context.

Note
All interrupts handled by the single interrupt handler thread are serialized.

To use the interrupt handler thread, using modules have to define a static interrupt event of type irq_event_t for each of their interrupt sources. These static interrupt events have to be initialized with the static initializer IRQ_EVENT_INIT. Furthermore, the interrupt handling function and optionally an argument, the context, have to be set.

Once the interrupt events have been initialized, they can be added to the pending interrupts queue of the interrupt handler thread by an ISR using the irq_event_add function which indicates that an interrupt has occurred and needs to be handled.

#include "foo_device.h"
#include "irq_handler.h"
// interrupt event structure with static initializer
static irq_event_t _int_event = IRQ_EVENT_INIT;
...
// non blocking ISR just adds the event and returns
static void _int_request(void *arg)
{
irq_event_add(&_int_event);
}
// example handler for the interrupt including blocking functions
static void _int_handler(void *ctx)
{
foo_device_t dev = (foo_device_t*)ctx;
uint8_t status;
// blocking access to the I2C
i2c_aquire(dev->i2c_device);
i2c_read_reg(dev->i2c_device, FOO_DEVICE_REG_STATUS, &status, 1);
i2c_release(dev->i2c_device);
// application thread callbacks
switch (status) {
case FOO_INT_TYPE1: dev->int_type1.isr(dev->int_type1.arg);
break;
case FOO_INT_TYPE2: dev->int_type2.isr(dev->int_type2.arg);
break;
...
}
...
}
...
int foo_device_init(foo_device_t *dev, foo_device_params_t *params)
{
// set the handler for the interrupt
_int_event.isr = _int_handler;
_int_event.ctx = (void*)dev;
// initialize the GPIO with ISR
gpio_init_int(GPIO_PIN(0, 1), GPIO_IN, GPIO_BOTH, int_request, 0);
...
}

Files

file  irq_handler.h
 

Data Structures

struct  irq_event_t
 Interrupt event structure. More...
 

Macros

#define IRQ_HANDLER_PRIO   0
 Default priority of the interrupt handler thread. More...
 
#define IRQ_EVENT_INIT
 Static initializer for irq_event_t. More...
 

Typedefs

typedef void(* irq_isr_t) (void *ctx)
 Interrupt handling function prototype. More...
 

Functions

static void irq_event_init (irq_event_t *irq)
 Initialize an interrupt event. More...
 
int irq_event_add (irq_event_t *irq)
 Add an interrupt event to the pending interrupt queue. More...
 

Macro Definition Documentation

◆ IRQ_EVENT_INIT

#define IRQ_EVENT_INIT
Value:
{ \
.event.handler = NULL, \
.event.list_node.next = NULL, \
.pending = false, \
.isr = NULL, \
.ctx = NULL, \
}

Static initializer for irq_event_t.

Definition at line 204 of file irq_handler.h.

◆ IRQ_HANDLER_PRIO

#define IRQ_HANDLER_PRIO   0

Default priority of the interrupt handler thread.

The priority of the interrupt handler thread has to be high enough that all pending interrupts are handled before other threads are executed.

Definition at line 171 of file irq_handler.h.

Typedef Documentation

◆ irq_isr_t

typedef void(* irq_isr_t) (void *ctx)

Interrupt handling function prototype.

Defines the prototype of the function that is registered together with an interrupt event and to be called when the interrupt is handled.

Definition at line 180 of file irq_handler.h.

Function Documentation

◆ irq_event_add()

int irq_event_add ( irq_event_t irq)

Add an interrupt event to the pending interrupt queue.

The interrupt event given by parameter irq will be placed at the end of the pending interrupt queue.

Each interrupt event can be added only once to the pending interrupt queue. That is, if the same interrupt occurs multiple times, only its first occurence is placed to the pending interrupt queue and is handled.

Parameters
[in]irqPreallocated interrupt event
Return values
0on success
-EALREADYif the given interrupt event is already pending

◆ irq_event_init()

static void irq_event_init ( irq_event_t irq)
inlinestatic

Initialize an interrupt event.

Initializes the given interrupt event structure.

Only use this function for dynamically allocated interrupt event structures. For the initialization of static interrupt event structures use IRQ_EVENT_INIT instead.

Parameters
[out]irqPre-allocated irq_event_t sturcture, must not be NULL

Definition at line 224 of file irq_handler.h.