Using InterruptAttach()

Continuing our example, here's the ISR intHandler(). It looks at the 8250 serial port chip that we assume is attached to HW_SERIAL_IRQ:

/*
 * int1.c
*/

#include <stdio.h>
#include <sys/neutrino.h>

#define REG_RX             0
#define REG_II             2
#define REG_LS             5
#define REG_MS             6
#define IIR_MASK           0x07
#define IIR_MSR            0x00
#define IIR_THE            0x02
#define IIR_RX             0x04
#define IIR_LSR            0x06
#define IIR_MASK           0x07

volatile int serial_msr;   // saved contents of Modem Status Reg
volatile int serial_rx;    // saved contents of RX register
volatile int serial_lsr;   // saved contents of Line Status Reg
static int base_reg = 0x2f8;

const struct sigevent *
intHandler (void *arg, int id)
{
    int  iir;
    struct sigevent *event = (struct sigevent *)arg;

    /*
     * determine the source of the interrupt
     * by reading the Interrupt Identification Register
    */

    iir = in8 (base_reg + REG_II) & IIR_MASK;

    /* no interrupt? */
    if (iir & 1) {
        /* then no event */
        return (NULL);
    }

    /*
     * figure out which interrupt source caused the interrupt,
     * and determine if a thread needs to do something about it.
     * (The constants are based on the 8250 serial port's interrupt
     * identification register.)
    */

    switch (iir) {
    case    IIR_MSR:
        serial_msr = in8 (base_reg + REG_MS);

        /* wake up thread */
        return (event);
        break;

    case    IIR_THE:
        /* do nothing */
        break;

    case    IIR_RX:
        /* note the character */
        serial_rx = in8 (base_reg + REG_RX);
        break;

    case    IIR_LSR:
        /* note the line status reg. */
        serial_lsr = in8 (base_reg + REG_LS);
        break;

    default:
        break;
    }

    /* don't bother anyone */
    return (NULL);
}

The first thing we notice is that any variable that the ISR touches must be declared volatile. On a single-processor box, this isn't for the ISR's benefit, but rather for the benefit of the thread-level code, which can be interrupted at any point by the ISR. Of course, on an SMP box, we could have the ISR running concurrently with the thread-level code, in which case we have to be very careful about these sorts of things.

With the volatile keyword, we're telling the compiler not to cache the value of any of these variables, because they can change at any point during execution.

The next thing we notice is the prototype for the interrupt service routine itself. It's marked as const struct sigevent *. This says that the routine intHandler() returns a struct sigevent pointer. This is standard for all interrupt service routines.

Finally, notice that the ISR decides if the thread will or won't be sent an event. Only in the case of a Modem Status Register (MSR) interrupt do we want the event to be delivered (the event is identified by the variable event, which was conveniently passed to the ISR when we attached it). In all other cases, we ignore the interrupt (and update some global variables). In all cases, however, we clear the source of the interrupt. This is done by reading the I/O port via in8().