The HyperNews Linux KHG Discussion Pages

Feedback: Interrupt sharing 101

Forum: Device Drivers
Re: Question Interrupt Sharing ? (Frieder Löffler)
Re: More Interrupt sharing - How to do with Network Drivers? (Frieder Löffler)
Keywords: Interrupt sharing, PCI, Plug%0Aamp;Play
Date: Wed, 28 Aug 1996 16:48:01 GMT
From: Christophe Beauregard <chrisb@truespectra.com>

I guess this would be a handy thing to have in the knowledge base...

The key thing to sharing an interrupt is to make sure that you have separate context information for each instance of the driver. That is, no static global variables. For most network drivers you just use the ``struct device* dev'' for the context.

Pass this to request_irq() as the last argument:

request_irq( irq, interrupt_handler, SA_SHIRQ, "MyDevice",
             dev );

Note that the SA_INTERRUPT flag is significant here, since you can't share an IRQ if one driver uses fast interrupts and the other uses slow interrupts. This is a bug, IMHO, since long chains of interrupt handlers may alter the timing such that processing is no longer ``fast''. A better behaviour would be to just implicitly change to slow interrupts when more than one device is on the IRQ (and change back when the device is released down to one fast handler, of course).

Then your interrupt handler looks something like this:

static void interrupt_handler( int irq, void* dev_id, ...) {
    struct device* dev = (struct device*) dev_id;

    if( dev == NULL ) {
        ASSERT( 0 ); /* stupid programmer error.  Either
                     we passed a NULL dev to request_irq
                     or someone screwed up irq.c */
    }

    /* query the device to see if it caused the interrupt */
    if( !(inb(something)&something_else) ) {
        /* nope, not us - normally we'd call this a spurious
        interrupt, but it might belong to another device. */
        return;
    }

    /* now, using the dev structure, service the interrupt */
    ...

    /* tell the hardware device we're done (IMPORTANT)
    If this isn't done, the device will continue to hold
    the IRQ line high, and we go into a nasty interrupt
    loop.  Some devices might do this implicitly in the
    interrupt processing (i.e. by emptying a buffer) */
    outb(something, something);
}

Because you have a separate ``struct device*'' for each instance of the card, multiple cards can share the same IRQ. Of course, they can also share the IRQ with other card, assuming they all Do The Right Thing.

You can usually modify an existing device to do shared IRQs by simply finding the part of the code where it spews out a spurious interrupt message and replacing that with a `return' statement, adding SA_SHIRQ to the request_irq call, and removing references to irq2dev_map[]. I've had no problems doing this for drivers including drivers/char/psaux.c, drivers/net/tulip.c, drivers/scsi/aicxxx7.c and most of the MCA drivers.

c.