InterruptAttach(), InterruptAttach_r()

Updated: April 19, 2023

Attach an Interrupt Service Routine (ISR) to an interrupt source

Synopsis:

#include <sys/neutrino.h>

int InterruptAttach( int intr,
       const struct sigevent * (* handler)(void *, int),
       const void * area,
       int size,
       unsigned flags );

int InterruptAttach_r( int intr,
       const struct sigevent * (* handler)(void *, int),
       const void * area,
       int size,
       unsigned flags );

Arguments:

intr
The interrupt that you want to attach a handler to; see Interrupt vector numbers,” below.
handler
A pointer to the handler function; see Interrupt handler function,” below.
area
A pointer to a communications area in your process, or NULL if you don't want a communications area.
size
The size of the communications area; this should be 0 if area is NULL. InterruptAttach() currently ignores this argument.
flags
Flags that specify how you want to attach the interrupt handler; a bitwise OR of zero or more of the following:
  • _NTO_INTR_FLAGS_END
  • _NTO_INTR_FLAGS_NO_UNMASK (QNX Neutrino 6.6 or later)
  • _NTO_INTR_FLAGS_PROCESS
  • _NTO_INTR_FLAGS_TRK_MSK
  • _NTO_INTR_FLAGS_EXCLUSIVE (QNX Neutrino 7.0.4 or later)

For more information, see Flags,” below.

Library:

libc

Use the -l c option to qcc to link against this library. This library is usually included automatically.

Description:

The InterruptAttach() and InterruptAttach_r() kernel calls attach the Interrupt Service Routine (ISR) identified by handler to the hardware interrupt specified by intr. They automatically enable (i.e., unmask) the interrupt level. These functions are identical except in the way they indicate errors; see the Returns section for details. Attaching an ISR superlocks the process's memory (see Locking memory in the “Process Manager” chapter of the System Architecture guide).

Before calling either of these functions, the process must have the PROCMGR_AID_INTERRUPT ability enabled. Otherwise the attachment fails with an error code of EPERM. For more information, see procmgr_ability().

On a multicore system, the interrupt handler runs on the CPU that takes the interrupt.

Interrupt vector numbers

The interrupt values for intr are logical interrupt vector numbers grouped into related “interrupt classes” that generally correspond to a particular interrupt controller on the CPU.

There can be additional interrupt classes defined for specific CPUs or embedded systems. For the interrupt assignments for specific boards, see:

The mapping of logical interrupt vector numbers is completely dependent on the implementer of the startup code. Device drivers should generally allow runtime configuration of interrupt numbers.

Interrupt handler function

The function to call is specified by the handler argument. Its prototype is:

const struct sigevent* handler( void* area, int id );

The arguments are:

area
The same as the area passed to InterruptAttach(). This pointer—which could be NULL—and the size argument to InterruptAttach() define a communications area in your process. This typically is a structure containing buffers and information that the handler and the process need to share.
id
The ID returned by InterruptAttach().

The handler function runs in the environment of your process.

Follow the following guidelines when writing your handler:

The handler function should return NULL or a pointer to a valid sigevent structure that the kernel delivers. These events are defined in <signal.h>. Consider the following when choosing the event type:

Flags

The flags argument is a bitwise OR zero or more of the following values:

_NTO_INTR_FLAGS_END
Put the new handler at the end of the list of existing handlers (for shared interrupts) instead of the start.

The interrupt structure allows hardware interrupts to be shared. For example, if two processes take over the same physical interrupt, both handlers are invoked consecutively. When a handler attaches, it's placed in front of any existing handlers for that interrupt and is called first. You can change this behavior by setting _NTO_INTR_FLAGS_END. Although the microkernel allows full interrupt sharing, your hardware might not.

Processor interrupts are enabled during the execution of the handler. Don't attempt to talk to the interrupt controller chip. The operating system issues the end-of-interrupt command to the chip after processing all handlers at a given level.

The first process to attach to an interrupt unmasks the interrupt. When the last process detaches from an interrupt, the system masks it.

If the thread that attached the interrupt handler terminates without detaching the handler, the kernel does it automatically.

_NTO_INTR_FLAGS_NO_UNMASK
(QNX Neutrino 6.6 or later) Leave the interrupt masked.

Normally, InterruptAttach() and InterruptAttachEvent() automatically unmask an interrupt the first time something is attached to it. If you specify _NTO_INTR_FLAGS_NO_UNMASK, the kernel leaves the interrupt masked, and you must specifically call InterruptUnmask() to enable it.

_NTO_INTR_FLAGS_PROCESS
Associate the handler with the process instead of the attaching thread. The interrupt handler is removed when the process exits, instead of when the attaching thread exits.
_NTO_INTR_FLAGS_TRK_MSK
Track calls to InterruptMask() and InterruptUnmask() to make detaching the interrupt handler safer.

The _NTO_INTR_FLAGS_TRK_MSK flag and the id argument to InterruptMask() and InterruptUnmask() let the kernel track the number of times a particular interrupt handler or event has been masked. Then, when an application detaches from the interrupt, the kernel can perform the proper number of unmasks to ensure that the interrupt functions normally. This is important for shared interrupt levels.

Note: You should always set _NTO_INTR_FLAGS_TRK_MSK.
_NTO_INTR_FLAGS_EXCLUSIVE
(QNX Neutrino 7.0.4 or later) Request exclusive access to the interrupt vector. If another thread already called an InterruptAttach*() function with the same vector, this kernel call fails with an EBUSY error. Similarly, if this call succeeds but another thread later calls an InterruptAttach*() function with the same vector, that call fails with the same error.

Blocking states

These calls don't block.

Returns:

An interrupt function ID. If an error occurs:

Use the function ID with the InterruptDetach() function to detach this interrupt handler.

Errors:

EAGAIN
All kernel interrupt entries are in use.
EFAULT
A fault occurred when the kernel tried to access the buffers provided.
EINVAL
The value of intr isn't a valid interrupt number.
EPERM
The process doesn't have the required permission; see procmgr_ability().

Classification:

QNX Neutrino

Safety:  
Cancellation point No
Interrupt handler No
Signal handler Yes
Thread Yes

Caveats:

If you're writing a multithreaded resource manager using the thread_pool_*() functions, and the thread that has called InterruptAttach() is also joining the thread pool by calling thread_pool_start() with POOL_FLAG_USE_SELF set, then that thread must also set _NTO_INTR_FLAGS_PROCESS in the flags argument when calling InterruptAttach(). If it doesn't, the thread may exit as part of the thread pool's dynamic threading behavior, and the interrupt handling will then be lost.