WRITING AN INTERRUPT SERVICE ROUTINE


Writing an interrupt service routine is relatively simple with QNX, since there are not a lot of setup and initialization issues as there are with many other operating systems. Any application (suitably privledged) can attach a function to an IRQ.

The general guidelines are:

QNX handles all the motherboard hardware, so don't mess with it. You may need to read or write a register of your hardware to acknowledge the interrupt and prime the hardware for the next one.

Your interrupt service routine function should be a separate .c file, since it needs to be compiled with additional options. Your executable is a little special, since it needs special privledges to access I/O ports.

Take a look at the function qnx_hint_attach().
int_id = qnx_hint_attach (irq, &isr_function, data_segment);
The only parameter that isn't obvious is the parameter that passes the value of your data segment register. Pass FP_SEG &global_variable) where global_variable is a variable you declared globally. The int_id is used later to disconnect from the IRQ, with qnx_hint_detach().

Declare the prototype of your interrupt service function something like:
pid_t far isr (void);
The far keyword is required, even for 32 bit code.

Return with the value 0, or the process id of a proxy to be Trigger()ed.

You can access your global variables from within your interrupt service routine. Declare them as volatile, so the compiler knows they can be changed without warning.

You cannot call any kernel calls. Very few functions are permitted. Most of the functions in the "List of Re-entrant Functions" are ok, except for the math ones.

Separate the interrupt service routine code into a different source module and to the cc command to compile the .c module, add these flags: -Wc,-zu -Wc,-s. They tell the compiler that the stack register is different than the dataregister and not to check for stack overflow.

When you link all the .o modules together, add the following flag to cc: -T1 which tells the linker to set the flag in the executable so that it is run at privity 1 rather than 3, so that I/O opcodes are permitted. Otherwise inp() and outp() will cause a SIGSEGV.

The executable must be run by root. A normal user can run it, but with permissions of root, if the SETUID flag is set on the executable:
chmod 4755 exe_name
Do this only for trusted code, since this program now accesses all files and resources as root.

The following is a complete example for an incredibly simple piece of fictitious hardware. There are two files, main.c and isr.c and they are complied with:
cc -Oentx -w9 -Wc,-we -Wc,-e6 -c main.c -o main.o
cc -Oentx -Wc,-zu -Wc,-s -w9 -Wc,-we -Wc,-e6 -c isr.c -o isr.o
cc -T1 -o example main.o isr.o

/* File: main.c */
#include <conio.h>
#include <process.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/irqinfo.h>
#include <sys/kernel.h>
#include <sys/proxy.h>
#include <sys/sched.h>

pid_t far isr_handler (void);  /* isr function prototype */
int portbase = 0x300;      /* base address of the hardware */
int irq_line = 5;          /* interrupt line of the hardware */
int priority = 29;         /* priority to run process at */
int isr_id = -1;           /* identifier of interrupt handler */
pid_t pid_sender = -1;     /* process id of message sender */
pid_t pid_int_proxy = -1;  /* proxy triggered by interrupt service routine */
volatile int data = 0;     /* temp data value passed by interrupt service */
char msgbuf[100];          /* message buffer */

/* Main Program */
void main()
{
    /* Set Priority Fairly High */
    if (setprio (0, priority) == -1){
        fprintf (stderr, "Failed to set priority\n");
        exit (EXIT_FAILURE);
    }
    
    /* Create Proxy, Zero Length */
    pid_int_proxy = qnx_proxy_attach (0, NULL, 0, -1);
    if (pid_int_proxy == -1){
        fprintf (stderr, "Failed to attach interrupt proxy\n");
        exit (EXIT_FAILURE);
    }
    
    /* Initialize The Fictious Hardware */
    outp (portbase + 1, 0x00);
    
    /* Attach Interrupt Routine */
    isr_id = qnx_hint_attach (irq_line, &isr_handler, FP_SEG (&portbase));
    if (isr_id == -1){
        fprintf (stderr, "Failed to attach interrupt handler\n");
        exit (EXIT_FAILURE);
    }
   
    /* Reset Fictitious Hardware Interrupt Logic */
    outp (portbase + 2, 0x10);
    
    /* Receive Loop */    
    for (;;){
        pid_sender = Receive (0, &msgbuf, sizeof (msgbuf));
        
        if (pid_sender == pid_int_proxy){
            /* Proxy From Interrupt Routine */
            printf ("New data is %d\n", data);
        }
        else {
            /* Handle Message From Another Process */
        }
    }
}

/* File: isr.c */
#include <conio.h>
#include <process.h>

extern volatile int data;
extern pid_t pid_int_proxy;
extern int portbase;

/* Interupt Handler */
pid_t far isr_handler ()
{
    /* Get The New Data */
    data = inp (portbase + 0);
    
    /* Reset The Interrupt Logic */
    outp (portbase + 2, 0x10);
    /* Trigger The Proxy */
    return (pid_int_proxy);
}


Note: These instructions are for typical situations. Individual configuration may differ. If you have any questions, please contact us for assistance.

Copyright © 1998 Qenesis Inc. All rights reserved.