![]() |
![]() |
![]() |
Attach an interrupt handler to an interrupt source
#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 );
libc
The InterruptAttach() and InterruptAttach_r() functions are identical except in the way they indicate errors. See the Returns section for details.
These kernel calls attach the interrupt function handler to an interrupt specified by intr. They automatically enable (i.e unmask) the interrupt level.
Before calling this function, the thread must request I/O privity by calling:
ThreadCtl( _NTO_TCTL_IO, 0 );
If the thread doesn't do this, the attach fails with EPERM.
The interrupt values for intr are logical interrupt vector numbers grouped into related "interrupt classes" that generally correspond to a particular interrupt line on the CPU. The following interrupt classes are present on all QNX systems:
_NTO_INTR_SPARE is usually the only _NTO_INTR_CLASS_SYNTHETIC interrupt you'll use; _NTO_INTR_SPARE is guaranteed not to match any valid logical interrupt vector number.
There can be additional interrupt classes defined for specific CPUs or embedded systems. For the interrupt assignments for specific boards, see the sample build files in ${QNX_TARGET}/${PROCESSOR}/boot/build.
The mapping of logical interrupt vector numbers is completely dependent on the implementor of the startup code.
Device drivers must:
The following list contains typical interrupt assignments for the 16 hardware interrupts on an x86-based PC using startup-bios:
Interrupt intr | Description |
---|---|
0 | Clock that runs at resolution set by ClockPeriod() |
1 | Keyboard |
2 | Slave 8259 |
3 | Com2 |
4 | Com1 |
5 | Net card / sound card / other |
6 | Floppy |
7 | Parallel printer / sound card / other |
8 | |
9 | Remapped interrupt 2 |
10 | |
11 | |
12 | |
13 | Co-processor |
14 | Primary disk controller |
15 | Secondary disk controller |
![]() |
The interrupt assignments are different for other boards. |
The function to call is specified by the handler argument. This function runs in the environment of your process. If a pager is running that swaps pages out of memory, the possibility exists for your handler to reference a variable in the process address space that isn't present. This results in a kernel shutdown. The area and size arguments define a communications area in your process that the handler can assume is never paged out. This typically is a structure containing buffers and information needed by the handler and the process when it runs. In a paging system, lock the memory pointed to by area using the mlock() function before attaching the handler. In a nonpaging system, the call to mlock() can be omitted (but you should still call it for compatibility with future versions of QNX). When the handler runs it's passed area as a single argument.
![]() |
The area argument can be NULL to indicate no communications area. If area is NULL, size should be 0. |
The handler function's prototype is:
const struct sigevent* handler( void* area, int id );
Where area is a pointer to the area specified by the call to InterruptAttach(), and id is the ID returned by InterruptAttach().
Follow the following guidelines when writing your handler:
The return value of the handler function should be 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:
![]() |
The thread that calls InterruptWait() must be the one that called InterruptAttach(). |
The flags argument is a bitwise OR of the following values, or 0:
Flag | Description |
---|---|
_NTO_INTR_FLAGS_END | Put the new handler at the end of the list of existing handlers (for shared interrupts) instead of the start. |
_NTO_INTR_FLAGS_PROCESS | Associate the handler with the process instead of the attaching thread. |
_NTO_INTR_FLAGS_TRK_MSK | Track calls to InterruptMask() and InterruptUnmask() to make detaching the interrupt handler safer. |
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. This behavior can be changed by setting the _NTO_INTR_FLAGS_END flag in the flags argument. This adds the handler at the end of any existing handlers. Although the Neutrino microkernel allows full interrupt sharing, your hardware may not. For example, the ISA bus doesn't allow interrupt sharing, while the PCI bus does.
Processor interrupts are enabled during the execution of the handler. Don't attempt to talk to the interrupt controller chip. The end of interrupt command is issued to the chip by the operating system 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.
Adding _NTO_INTR_FLAGS_PROCESS to flags associates the interrupt 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.
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.
This call doesn't block.
The only difference between these functions is the way they indicate errors:
Use the function ID with the InterruptDetach() function to detach this interrupt handler.
Safety: | |
---|---|
Cancellation point | No |
Interrupt handler | No |
Signal handler | Yes |
Thread | Yes |
If you're writing a resource manager and using the resmgr_*() functions with multiple threads, a thread that attaches to an interrupt must use _NTO_INTR_FLAGS_PROCESS in the flags argument when calling InterruptAttach().
If your interrupt handler isn't SMP-safe, you must lock it to one processor using:
ThreadCtl( _NTO_TCTL_RUNMASK, ... );
atomic_add(), atomic_clr(), atomic_set(), atomic_sub(), atomic_toggle(), InterruptAttachEvent(), InterruptDetach(), InterruptDisable(), InterruptEnable(), InterruptLock(), InterruptMask(), InterruptUnlock(), InterruptUnmask(), InterruptWait(), sigevent, ThreadCtl()
![]() |
![]() |
![]() |