InterruptAttach(), InterruptAttach_r()

Attach an interrupt handler 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 );

Since:

BlackBerry 10.0.0

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. 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 function 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.

Before calling either of these functions, the thread must:

  • have the PROCMGR_AID_INTERRUPT and PROCMGR_AID_IO abilities enabled. For more information, see procmgr_ability().
  • request I/O privileges by calling:
    ThreadCtl( _NTO_TCTL_IO, 0 );
      

If the thread doesn't do all of the above, the attachment fails with an error code of EPERM.

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 line on the CPU. The following interrupt classes are present on all BlackBerry 10 OS systems:

_NTO_INTR_CLASS_EXTERNAL
Normal external interrupts (such as the ones generated by the INTR pin on x86 CPUs).
_NTO_INTR_CLASS_SYNTHETIC
Synthetic, kernel-generated interrupts.

_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:

  • some *intr.h files in the platform-specific directories under ${QNX_TARGET}/usr/include
  • the sample buildfiles in ${QNX_TARGET}/x86/boot/build
  • the buildfiles in the Board Support Package for your board

Interrupts and startup code

The mapping of logical interrupt vector numbers is completely dependent on the implementer of the startup code.

Device drivers must:

  • Let the user specify an interrupt number on the command line; don't use a hard-coded value. Eventually, the configuration manager will provide interrupt numbers for the device drivers.
  • Store interrupt numbers in an unsigned int variable; don't assume an interrupt number fits into a byte.

Typical x86 Interrupt vector numbers

The following list contains typical interrupt assignments for the 16 hardware interrupts on an x86-based PC using startup-bios:

Interrupt intr Description
0 A clock that runs at the resolution set by ClockPeriod()
1 Keyboard
2 Slave 8259 — you can't attach to this interrupt.
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.

Interrupt handler function

The function to call is specified by the handler argument. This function runs in the environment of your process, and the area and size arguments define a communications area in your process. This typically is a structure containing buffers and information needed by the handler and the process when it runs.

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:

  • 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. 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 the atomic*() functions 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().

    The handler can call TraceEvent(), but not all of its commands are valid.

  • It isn't safe to use floating-point operations in Interrupt Service Routines.

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:

  • 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.

    The thread that calls InterruptWait() must be the one that called InterruptAttach().

  • Using SIGEV_SIGNAL, SIGEV_SIGNAL_CODE, SIGEV_SIGNAL_THREAD, or SIGEV_THREAD is discouraged. It's less efficient than the other mechanisms for interrupt event delivery.

Flags

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.

_NTO_INTR_FLAGS_END

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 the _NTO_INTR_FLAGS_END flag in the flags argument. This adds the handler at the end of any existing handlers. Although the QNX Neutrino microkernel allows full interrupt sharing, your hardware might 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 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_PROCESS

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.

_NTO_INTR_FLAGS_TRK_MSK

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.

You should always set _NTO_INTR_FLAGS_TRK_MSK.

Blocking states

This call doesn't block.

Returns:

The only difference between these functions is the way they indicate errors:

InterruptAttach()
An interrupt function ID. If an error occurs, -1 is returned and errno is set.
InterruptAttach_r()
An interrupt function ID. This function does NOT set errno. If an error occurs, the negative of a value from the Errors section is returned.

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 I/O privileges, or it 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 your interrupt handler isn't SMP-safe, you must lock it to one processor using:

ThreadCtl( _NTO_TCTL_RUNMASK, ... );

Last modified: 2014-06-24



Got questions about leaving a comment? Get answers from our Disqus FAQ.

comments powered by Disqus