InterruptHookIdle2()

Attach an "idle" interrupt handler

Synopsis:

#include <sys/neutrino.h>

int InterruptHookIdle2(
       void (*handler)( unsigned,
                        struct syspage_entry *,
                        struct _idle_hook *),
       unsigned flags );

Arguments:

handler
A pointer to the handler function; see below.
flags
There are currently no flags that apply; specify 0 for this argument.

Library:

libc

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

Description:

The InterruptHookIdle2() kernel call attaches the specified interrupt handler to the "idle" interrupt, which is called when a processor becomes idle. This handler is typically used to implement power management features. The simplest idle handler consists of a halt instruction.

Note: In order to attach the handler, your process must have the PROCMGR_AID_INTERRUPT ability enabled. For more information, see procmgr_ability() The thread doesn't need to have I/O privileges.

The arguments to the handler function are:

unsigned
The CPU number that this hook call is occurring on.
struct syspage_entry *
A pointer to a writable version of the system page. For more information, see "Structure of the system page" in the Customizing Image Startup Programs chapter of Building Embedded Systems, as well as the SYSPAGE_ENTRY() macro.
struct _idle_hook *
A pointer to an _idle_hook structure that the kernel and the handler use to exchange information.

The handler function is invoked with a very limited amount of stack space, so it shouldn't use large automatic arrays. It's also invoked in such a manner that it can be aborted at any time and restarted from the beginning, with no user notification. It isn't allowed to make any OS service calls (e.g., message passing).

The _idle_hook structure is defined in <sys/neutrino.h> as follows:

struct _idle_hook {
        unsigned                hook_size;
        unsigned                cmd;
        unsigned                mode;
        unsigned                latency;
        uint64_t                next_fire;
        uint64_t                curr_time;
        uint64_t                tod_adjust;
        unsigned                resp;
        struct {
                unsigned        length;
                unsigned        scale;
        } time;
        struct sigevent         trigger;
        unsigned                *intrs;
        unsigned                block_stack_size;
};

The members include:

hook_size
The size of the _idle_hook structure, in bytes.
cmd
Set by the kernel to indicate the phase of the call (see below for more details):
  • _NTO_IH_CMD_SLEEP_SETUP
  • _NTO_IH_CMD_SLEEP_BLOCK
  • _NTO_IH_CMD_SLEEP_WAKEUP
  • _NTO_IH_CMD_SLEEP_ONLINE
mode
The meaning of this member depends on cmd:
  • For _NTO_IH_CMD_SLEEP_SETUP, _NTO_IH_CMD_SLEEP_BLOCK, and _NTO_IH_CMD_SLEEP_WAKEUP, this value (if nonzero) is the sleep mode to set.
  • For _NTO_IH_CMD_SLEEP_ONLINE, it's the CPU number that's being returned to operation.
latency
The maximum latency, in nanoseconds, that the CPU is allowed to have when responding to an interrupt. If the kernel is taking the CPU offline, it sets this field to ~0, and the handler should choose the absolute lowest power mode, which should be off.
next_fire
The time, in nanoseconds since the system was booted, when the next timer is to fire.
curr_time
The current time of day, in nanoseconds since the system was booted.
tod_adjust
The number of nanoseconds to add to curr_time to convert it to the time of day.
resp
The response from the handler; a bitwise OR of zero or more of the following:
  • _NTO_IH_RESP_NEEDS_BLOCK
  • _NTO_IH_RESP_NEEDS_WAKEUP
  • _NTO_IH_RESP_NEEDS_ONLINE
  • _NTO_IH_RESP_SYNC_TIME
  • _NTO_IH_RESP_SYNC_TLB
  • _NTO_IH_RESP_SUGGEST_OFFLINE
  • _NTO_IH_RESP_SLEEP_MODE_REACHED
  • _NTO_IH_RESP_DELIVER_INTRS

See below for more details.

time.length
Filled in by the handler. The number of time units that the timer tick interrupt has been disabled.
time.scale
Filled in by the handler. A scale factor such that time.length * time.scale gives the number of nanoseconds that the timer tick interrupt has been disabled.
trigger
A sigevent that the handler wants the kernel to deliver (if the sigev_notify member isn't SIGEV_NONE). The target thread is the one that called InterruptHookIdle2().
intrs
A pointer to an array where each bit, if set to 1 by the handler, indicates an interrupt for which the kernel should arrange to invoke ISRs and deliver events specified by InterruptAttach() or InterruptAttachEvent](). The size of this array is based on the intrinfo section of the system page; it needs to be big enough to handle all the interrupts specified there (basically, total up the num_vector fields).

The _NTO_IH_RESP_DELIVER_INTRS bit must be set in the resp field for the kernel to check the contents of the array. See below for details on how to determine the bit(s) to be set.

block_stack_size
Before invoking the handler with _NTO_IH_CMD_SLEEP_SETUP, the kernel sets this field to the current stack size for _NTO_IH_CMD_SLEEP_BLOCK. On return, if the idle hook places a larger value in the field, the kernel attempts to grow the block stack to the newly requested value. On entry to the idle hook for _NTO_IH_CMD_SLEEP_BLOCK, you can check the size again. If it's less than the size that you requested, the grow request failed due to lack of memory.

The idle hook handler may be called up to three times per entry to or exit from the sleep state, depending on how the handler function responds to each of the previous calls:

For each of these calls, the handler fills in the resp field with a bitset indicating what the kernel needs to do:

Bit Description
_NTO_IH_RESP_NEEDS_BLOCK If set after the first handler call, the kernel will invoke the handler with the _NTO_IH_CMD_SLEEP_BLOCK state.
_NTO_IH_RESP_NEEDS_WAKEUP If set after the first handler call (or second, if the second call has been requested), the kernel will invoke the handler with the _NTO_IH_CMD_SLEEP_WAKEUP state.
_NTO_IH_RESP_NEEDS_ONLINE If the selected mode is such that an interprocess interrupt (IPI) isn't sufficient to cause the CPU to respond (e.g., it's offline), the handler should set this bit. The kernel, when it decides to online the CPU, invokes the idle hook (on a different CPU) with _NTO_IH_CMD_SLEEP_ONLINE in the cmd field. The mode field holds the CPU number that is being returned to operation.
_NTO_IH_RESP_SYNC_TIME After the handler returns, the kernel will add time.length * time.scale nanoseconds to the SYSPAGE_ENTRY(qtime)->nsec field.
_NTO_IH_RESP_SYNC_TLB After the handler returns, the kernel will resynchronize the translation lookaside buffer (TLB) state (the TLB wasn't being notified of modifications from other CPU's during the sleep state).

The kernel crashes if it gets this response.

_NTO_IH_RESP_SUGGEST_OFFLINE This allows the handler to suggest to the kernel that it might be a good idea to offline this CPU. The CPU must be marked as being available for dynamic offlining, and the kernel is free to ignore the suggestion (and it currently does).
_NTO_IH_RESP_SLEEP_MODE_REACHED This bit indicates that the sleep mode requested was actually entered by the hardware. The kernel doesn't do anything with this bit, but it can be used to gather statistics on how much power savings are being obtained.
_NTO_IH_RESP_DELIVER_INTRS If set, this bit indicates that the handler turned on bits in the intrs array, and the kernel needs to perform interrupt delivery processing for the indicated interrupts.

Indicating interrupts for delivery processing

If an unmasked interrupt occurs while the CPU is asleep and, for some reason, it will not cause CPU interrupt exception processing to occur when the CPU wakes up, the hook code must tell the kernel about the interrupt so that it can arrange to invoke the handlers (InterruptAttach()) or deliver the sigevents (InterruptAttachEvent()) for that level.

The handler indicates this by turning on the appropriate bit(s) in the intrs array and setting _NTO_IH_RESP_DELIVER_INTRS in the resp field.

To determine the corresponding bit for each interrupt, examine the system page's intrinfo section and look at the number of interrupts that each controller is responsible for. If the first controller is responsible for N interrupts, then the interrupt numbers are 0 ... (N-1). If the second controller has M interrupts, then its numbers are N ... (N + M - 1), and so on. If you want to indicate that interrupt X has occurred, set the following bit:

idle_hook_ptr->intrs[X / (sizeof(unsigned)*CHAR_BIT)] |=
   1U << (X % (sizeof(unsigned)*CHAR_BIT));

Blocking states

This call doesn't block.

Returns:

An interrupt function ID, or -1 if an error occurs (errno is set).

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

Errors:

EAGAIN
All kernel interrupt entries are in use.
EPERM
The calling 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