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.
If the thread that attached the interrupt handler terminates without detaching the handler,
the kernel removes it automatically.
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:
- some *intr.h files in the platform-specific directories under
${QNX_TARGET}/usr/include
-
the
init_intrinfo()
function in the startup code of your BSP
- the buildfiles in the Board Support Package for your board
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:
- A temporary interrupt stack of limited depth is provided at interrupt time,
so avoid placing large arrays or structures on the stack frame of the handler, and avoid recursion.
It's safe to assume that about 200 bytes of stack are available.
- The interrupt handler runs asynchronously with the threads in the process.
Any variables modified by the handler should be declared
with the volatile keyword and modified with interrupts
disabled or using atomic operations in any thread and ISR.
- The interrupt handler should be kept as short as possible.
If a significant amount of work needs to be done, the handler should
deliver an event to awaken a thread to do the work.
- The handler can't call library routines that contain kernel calls except for
InterruptDisable(),
InterruptEnable(),
InterruptLock(),
InterruptMask(),
InterruptUnlock(), and
InterruptUnmask().
For a list of the functions that you can call from the interrupt handler, see the
Full Safety Information
appendix.
- The handler can call
TraceEvent(),
but not all of its commands are valid; see the Caveats in its entry.
- (QNX Neutrino 7.1 or later) The kernel saves and restores the FPU context on entering and leaving ISRs,
so it's safe to use floating-point operations in them.
The handler function should return NULL or a pointer to a valid
sigevent
structure that the kernel delivers. The event doesn't need to be registered.
These events are defined in <signal.h>.
Consider the following when choosing the event type:
- Message-driven processes that block in a receive loop using
MsgReceivev()
should consider using
SIGEV_PULSE to trigger a pulse.
- Threads that block at a particular point in their code and don't go
back to a common receive point should consider using
SIGEV_INTR as the event notification type and
InterruptWait()
as the blocking call.
Note:
The thread that calls InterruptWait() must
be the one that called InterruptAttach().
- Using SIGEV_THREAD is very inefficient, because that would create a new thread
for every interrupt.
(QNX Neutrino 7.0.4 or later) In order to use an event of type SIGEV_THREAD,
your process must have the PROCMGR_AID_SIGEV_THREAD ability enabled.
- For SIGEV_SIGNAL, SIGEV_SIGNAL_CODE, and
SIGEV_SIGNAL_THREAD, handling a signal using a signal handler is noticeably inefficient as compared
to using pulses.
But, delivering a signal to a thread that's blocked on sigwaitinfo() is actually
more efficient than the pulse choice, though less efficient than the InterruptWait() choice.
The advantage is in flexibility, allowing the thread to be woken up both by interrupts and by
other threads in the process as needed.
Architecturally, this lets you create a thread that's dedicated to handling hardware.
- On a multicore system, the thread that receives the event returned by the interrupt handler runs on any CPU,
limited only by the scheduler and the runmask.
If you know which CPU the handler will run on and which thread will be handling the interrupt,
you may wish to consider using the runmask to make sure they're the same CPU,
in order to reduce cache-coherency operations on data shared between the interrupt handler and the thread.
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.
- _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:
- InterruptAttach() returns -1 and sets
errno.
- InterruptAttach_r() returns the negative of a value from the Errors section and
doesn't set errno.
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.