Interrupt Sharing

A well-known ``feature'' of the PC is its inability to attach different devices to the same interrupt line. However, Linux 2.0 broke the spell. Even though my ISA hardware manual--a Linux-unaware book--says that ``at most one device'' can be attached to an IRQ line, the electrical signals don’t have these restrictions unless the device hardware is unfriendly by design. The problem is with the software.

Linux software support for sharing was developed for PCI devices, but it works with ISA boards as well. Needless to say, non-PC platforms and buses support interrupt sharing too.

In order to develop a driver that can manage a shared interrupt line, there are some details that need to be considered. As discussed below, some of the features described in this chapter are not available for devices using interrupt sharing. Whenever possible, it’s better to support sharing because it presents fewer problems for the final user.

Installing a Shared Handler

Shared interrupts are installed through request_irq just like owned ones, but there are two differences:

  • The SA_SHIRQ bit must be specified in the flags argument when requesting the interrupt.

  • The dev_id argument must be unique. Any pointer into the module’s address space will do, but dev_id definitely cannot be set to NULL.

The kernel keeps a list of shared handlers associated with the interrupt, and dev_id differentiates between them, like a driver’s signature. If two drivers were to register NULL as their signature on the same interrupt, things might get mixed up at unload time, causing the kernel to oops when an interrupt arrived. This happened to me when I was first testing with shared handlers (when I just thought ``let’s add SA_SHIRQ to those two drivers'').

Under these conditions, request_irq succeeds if either the interrupt line is free or both the following conditions are met:

  • The previously registered handler specified SA_SHIRQ in its flags.

  • Both the new handler and the old one are fast handlers, or both are slow.

The reasons behind these requirements should be quite apparent: fast and slow handlers live in different environments, and you can’t mix them. Similarly, you can’t force sharing with a handler that was installed standalone. This condition about fast and slow handlers has been removed in recent 2.1 kernels because the two types now behave in the same way.

Whenever two or more drivers are sharing an interrupt line and the hardware interrupts the processor on that line, the kernel invokes every handler registered for that interrupt, passing each its own dev_id. Therefore, a shared handler must be able to recognize its own interrupts.

If you need to probe for your device before requesting the IRQ line, the kernel can’t help you. No probing function is available for shared handlers. The standard probing mechanism works if the line being used is free; but if the line is already held by another driver with sharing capabilities, the probe will fail, even if your driver would have worked perfectly.

The only available technique for probing shared lines, then, is the do-it-yourself way. The driver should request every possible IRQ line as a shared handler and then see where interrupts are reported. The difference between that and Section 9.2.2.2 is that the probing handler must check that the interrupt actually occurred, as it could have been called in response to another device interrupting on a shared line.

Releasing the handler is performed in the normal way using release_irq. Here the dev_id argument is used to select the correct handler to release from the list of shared handlers for the interrupt. That’s why the dev_id pointer must be unique.

A driver using a shared handler needs to be careful about one more thing: it can’t play with enable_irq or disable_irq. If it does, things might go haywire for other devices sharing the line. In general, the programmer must remember that his driver doesn’t own the IRQ, and its behavior should be more ``social'' than is necessary if you own the interrupt line.

Running the Handler

As suggested above, when the kernel receives an interrupt, all the registered handlers are invoked. A shared handler must be able to distinguish between interrupts that it needs to handle and interrupts generated by other devices.

Loading short with the option shared=1 installs the following handler instead of the default:

void short_sh_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
    int value;
    struct timeval tv;

    /* If it wasn't short, return immediately */
    value = inb(short_base);
    if (!(value & 0x80)) return;
        
    /* clear the interrupting bit */
    outb(value & 0x7F, short_base);

    /* the rest is unchanged */

    do_gettimeofday(&tv);
    short_head += sprintf((char *)short_head,"%08u.%06u
",
                          (int)(tv.tv_sec % 100000000),
                          (int)(tv.tv_usec));
    if (short_head == short_buffer + PAGE_SIZE)
        short_head = short_buffer; /* wrap */

    wake_up_interruptible(&short_queue); /* awake any reading process */
}

An explanation is due here. Since the parallel port has no ``interrupt-pending'' bit to check, the handler uses the ACK bit for this purpose. If the bit is high, the interrupt being reported is for short, and the handler clears the bit.

The handler resets the bit by zeroing the high bit of the parallel interface’s data port--short knows that pins 9 and 10 are connected together. If one of the other devices sharing the IRQ with short generates an interrupt, short sees that its own line is still inactive and does nothing.

Obviously, a real handler does more; in particular, it uses the dev_id argument to refer to its own hardware structure.

A full-featured driver probably splits the work into top and bottom halves, but that’s easy to add and does not have any impact on the code that implements sharing.

The /proc Interface

Installing shared handlers in the system doesn’t affect /proc/stat (which doesn’t even know about handlers). However, /proc/interrupts changes slightly.

All the handlers installed for the same interrupt number appear on the same line of /proc/interrupts. The following snapshot was taken on my computer, after I loaded short and the driver for my frame grabber as shared handlers:

 0:    1153617   timer
 1:      13637   keyboard
 2:          0   cascade
 3:      14697 + serial
 5:     190762   NE2000
 7:       2094 + short, + cx100
13:          0   math error
14:      47995 + ide0
15:      12207 + ide1

The shared interrupt line here is IRQ 7; the active handlers are listed on one line, separated by commas. It’s apparent that the kernel is unable to distinguish short interrupts from grabber (cx100) interrupts.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.187.103