Single thread for handling interrupts that may trigger blocking functions and therefore may only be called in thread context. More...
Single thread for handling interrupts that may trigger blocking functions and therefore may only be called in thread context.
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.
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 occurrence 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 occurrence before it yields.
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.
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.
|Interrupt event structure. More...|
|Default priority of the interrupt handler thread. More...|
|Static initializer for irq_event_t. More...|
|typedef void(*||irq_isr_t) (void *ctx)|
|Interrupt handling function prototype. More...|
|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...|
|#define IRQ_HANDLER_PRIO 0|
|typedef void(* irq_isr_t) (void *ctx)|
|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 occurrence is placed to the pending interrupt queue and is handled.
|[in]||irq||Preallocated interrupt event|
|-EALREADY||if the given interrupt event is already pending|
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.
|[out]||irq||Pre-allocated irq_event_t structure, must not be NULL|