Driver porting: dealing with interrupts
This article is part of the LWN Porting Drivers to 2.6 series. |
Interrupt handler return values
Prior to 2.5.69, interrupt handlers returned void. There is, however, one useful thing that interrupt handlers can tell the kernel: whether the interrupt was something they could handle or not. If a device starts generating spurious interrupts, the kernel would like to respond by blocking interrupts from that device. If no interrupt handler for a given IRQ has been registered, the kernel knows that any interrupt on that number is spurious. When interrupt handlers exist, however, they must tell the kernel about spurious interrupts.So, interrupt handlers now return an irqreturn_t value; void handlers will no longer compile. If your interrupt handler recognizes and handles a given interrupt, it should return IRQ_HANDLED. If it knows that the interrupt was not on a device it manages, it can return IRQ_NONE instead. The macro:
IRQ_RETVAL(handled)
can also be used; handled should be nonzero if the handler could deal with the interrupt. The "safe" value to return, if, for some reason you are not sure, is IRQ_HANDLED.
Disabling interrupts
In the 2.6 kernel, it is no longer possible to globally disable interrupts. In particular, the cli(), sti(), save_flags(), and restore_flags() functions are no longer available. Disabling interrupts across all processors in the system is simply no longer done. This behavior has been strongly discouraged for some time, so most code should have been converted by now.The proper way to do this fixing, of course, is to figure out exactly which resources were being protected by disabling interrupts. Those resources can then be explicitly protected with spinlocks instead. The change is usually fairly straightforward, but it does require an understanding of what is really going on.
It is still possible to disable all interrupts locally with local_save_flags() or local_irq_disable(). A single interrupt can be disabled globally with disable_irq(). Some of the spinlock operations also disable interrupts on the local processor, of course. None of these functions are changed (at least, with regard to their external interface) since 2.4.
Various small changes
One function that has changed is synchronize_irq(). In 2.6, this function takes an integer IRQ number as a parameter. It spins until no interrupt handler is running for the given IRQ. If the IRQ is disabled prior to calling synchronize_irq(), the caller will know that no interrupt handler can be running after that call. The 2.6 version of synchronize_irq() only waits for handlers for the given IRQ number; it is no longer possible to wait until no interrupt handlers at all are running.If your code has post-interrupt logic which runs as a bottom half, or out of a task queue, it will need to be changed for 2.6. Bottom halves are deprecated, and the task queue mechanism has been removed altogether. Post-interrupt processing should now be done using tasklets or work queues.
A new function was added in 2.6.1:
int can_request_irq(unsigned int irq, unsigned long flags);
This function returns a true value if the given interrupt allocation request would succeed, but does not actually allocate anything. Potential users should always be aware that the situation could change after calling can_request_irq().
Finally, the declarations of request_irq() and free_irq() have
moved from <linux/sched.h> to
<linux/interrupt.h>.