blob: 232f44be1586d74d867475f73ba4fded2b427188 [file] [log] [blame]
.. _microkernel_task_irqs:
Interrupt Services
##################
Concepts
********
The microkernel's :dfn:`task IRQ` objects allow interrupts to be serviced
by tasks, rather than only by interrupt service routines (ISRs). These allow
a microkernel project to have both task-level device drivers and interrupt-level
device drivers.
Any number of task IRQs can be defined in a microkernel system. Each task IRQ
has a numeric identifier that uniquely identifies it. These identifiers range
from 0 to N-1, where N is the total number of task IRQs in the system.
A task that wants to service interrupts from a device must first allocate a
task IRQ and bind it to the device's interrupt source by specifying the IRQ
and interrupt priority level assigned to that device by the system designer.
Once a task IRQ has been allocated by a task, it cannot be used by other
tasks; this prevents other tasks from interfering with the proper processing
of interrupts from that device.
When an interrupt is generated by the device, the kernel runs an ISR that
masks the interrupt and signals the occurrence of the interrupt to the
associated task IRQ. The task can then use the task IRQ to recognize that
an interrupt has occurred and take action to service the interrupt. At some
point during interrupt servicing, the task must instruct the task IRQ to
acknowledge the interrupt; this causes the kernel to unmask the interrupt so
that future interrupts can be detected.
Purpose
*******
Use a task IRQ when the work required to process an interrupt cannot be done
in an ISR -- either because it takes a long time, or because it requires the
processing routine to block.
Usage
*****
Configuring Task IRQs
=====================
Set the :option:`MAX_NUM_TASK_IRQS` configuration option to specify the number
of task IRQs allowed in the project.
The default value of zero for this option disables task IRQs.
.. note::
Unlike most other microkernel object types, task-level IRQs are defined
as a group using a configuration option, rather than as individual
public objects in an MDEF or private objects in a source file.
Example: Allocating a Task IRQ
==============================
This code associates a task IRQ with interrupts generated by a device.
Interrupts from that device are then enabled so they can be processed
using the task IRQ.
.. code-block:: c
#define FOO_DEVICE 2 /* device "foo" uses task IRQ object 2 */
#define FOO_IRQ 37 /* device "foo" uses IRQ 37 */
#define FOO_PRIO 3 /* device "foo" uses interrupt priority 3 */
#define FOO_IRQ_FLAGS 0 /* device "foo" IRQ flags. Unused on non-x86 */
if (task_irq_alloc(FOO_DEVICE, FOO_IRQ, FOO_PRIO, FOO_IRQ_FLAGS) ==
INVALID_VECTOR) {
/* The task IRQ or the interrupt source is not available */
printf("Task IRQ allocation failed!");
}
Example: Servicing Interrupts using a Task IRQ
==============================================
This code allows a task to wait for an interrupt from a device,
acknowledge the interrupt, and take the necessary steps to service it.
.. code-block:: c
task_irq_wait(FOO_DEVICE, TICKS_UNLIMITED);
/* Device interrupt is now masked */
/* Do pre-acknowledgement device processing (if any) */
task_irq_ack(FOO_DEVICE);
/* Device interrupt is now unmasked */
/* Do post-acknowledgement device processing (if any) */
The steps required to service a device are device-specific. In some cases
all processing may need to be completed before the interrupt is acknowledged,
while in other cases no processing at all should be done until the interrupt
is acknowledged. Some devices may require processing both before and after
acknowledgement.
APIs
****
Task IRQ APIs provided by :file:`microkernel.h`
===============================================
:cpp:func:`task_irq_alloc()`
Bind a task IRQ to a device and enable interrupts.
:cpp:func:`task_irq_ack()`
Acknowledge an interrupt and re-enable the interrupt.
:c:func:`task_irq_wait()`
Wait for an interrupt to occur within a specified time period.