Linux Interrupt API =================== Russell King The Linux Interrupt API provides a flexible mechanism to handle and control interrupts within the kernel. The design requirements for this API are: - must have as little overhead as possible for commodity hardware - must be easy and obvious to use - must allow complex multi-level interrupt implementations to exist transparently to device drivers - must be compatible with the existing API Essentially, this means that implementation of the existing API must be simple. ------------------------------------------------------------------------------ The API. ======== struct irq { /* architecture defined information */ /* must not be dereferenced by drivers */ }; #define NO_IRQ /** * irq_get - increment reference count on the IRQ descriptor * @irq: interrupt descriptor * * IRQ descriptor reference counting is mandatory for * implementations which provide dynamically allocated IRQ * descriptors. statically allocated IRQ descriptor * implementations may define these to be no-ops. */ struct irq *irq_get(struct irq *irq); /** * irq_put - decrement reference count on IRQ descriptor * @irq: interrupt descriptor * * Decrement the reference counter in an IRQ descriptor. * If the reference counter drops to zero, the IRQ descriptor * will be freed. * * IRQ descriptor reference counting is mandatory for * implementations which provide dynamically allocated IRQ * descriptors. statically allocated IRQ descriptor * implementations may define these to be no-ops. */ void irq_put(struct irq *irq); /** * irq_disable_nosync - disable an irq without waiting * @irq: Interrupt descriptor to disable * * Disable the selected interrupt line. Disables and Enables are * nested. * Unlike irq_disable(), this function does not ensure existing * instances of the IRQ handler have completed before returning. * * This function may be called from IRQ context. */ void irq_disable_nosync(struct irq *irq); /** * irq_disable - disable an irq and wait for completion * @irq: Interrupt descriptor to disable * * Disable the selected interrupt line. Enables and Disables are * nested. * This function waits for any pending IRQ handlers for this interrupt * to complete before returning. If you use this function while * holding a resource the IRQ handler may need you will deadlock. * * This function may be called - with care - from IRQ context. */ void irq_disable(struct irq *irq); /** * irq_enable - enable the specified IRQ source. * @irq: Interrupt descriptor to enable * * Undoes the effect of one call to irq_disable(). If this * matches the last disable, processing of interrupts on this * IRQ line is re-enabled. * * This function may be called from IRQ context. */ void irq_enable(struct irq *irq); /** * irq_request - request the use of an IRQ. * @irq: irq descriptor * @irqflags: interrupt type flags (SA_SHIRQ, SA_INTERRUPT) * @devname: ascii name for the claiming device * @dev_id: A cookie passed back to the handler function * * Request use of an interrupt line. We don't actually hook * the interrupt immediately; any resources necessary are * allocated. As such, this function may sleep. * * This function takes a reference on the IRQ descriptor. */ int irq_request(struct irq *irq, unsigned long irqflags, char *devname, void *dev_id); /** * irq_register - register an interrupt function to be called * @irq: interrupt descriptor * @fn: interrupt handler function * @dev_id: A cookie passed back to the handler function * * Hook a previously registered interrupt line. If the * interrupt has not been hooked by this instance, we fail. */ int irq_register(struct irq *irq, irqreturn_t (*handler)(void *dev_id, struct pt_regs *regs), void *dev_id); /** * irq_release - free an interrupt line * @irq: interrupt descriptor * @dev_id: device identity to free. * * Remove an interrupt handler. The handler is removed and if the * interrupt line is no longer in use by any driver it is disabled. * On a shared IRQ the caller must ensure the interrupt is disabled * on the card it drives before calling this function. The function * does not return until any executing interrupts for this IRQ * have completed. * * We drop a reference on the IRQ descriptor, and therefore may be * freed by irq_release. * * This function must not be called from interrupt context. */ void irq_release(struct irq *irq, void *devid); /** * irq_name - fill in and return the name of an IRQ * @buf: buffer to fill with name * @size: size of buffer * @irq: irq descriptor * * Create an ASCII name of an IRQ and place it in buf. * Returns the address of the buffer. */ const char *irq_name(char *buf, size_t size, struct irq *irq); /** * irqisa_to_desc - convert an ISA interrupt number to irq descriptor * @irqnr: ISA interrupt number * * Return the IRQ descriptor for a given "ISA" interrupt number. * Returns NULL if the "ISA" interrupt number is invalid. * * The returned IRQ has been "gotten" so irq_put will be required * to balance once the IRQ is no longer required. */ struct irq *irqisa_to_desc(int irqnr); /** * irqdesc_to_isa - convert an irq descriptor to an ISA interrupt number * @irq: Interrupt descriptor * * Returns the "ISA" interrupt number given an interrupt * descriptor. Other interrupts return NO_IRQ. */ int irqdesc_to_isa(struct irq *irq); /** * irq_probe_on - begin an interrupt autodetect * * Commence probing for an interrupt. The interrupts are scanned * and a cookie is returned. * * The IRQ probe semaphore is taken prior to the commencement of * interrupt probing. */ void *irq_probe_on(void); /** * irq_probe_mask - scan a bitmap of interrupt lines * @cookie: cookie returned from irq_probe_on * * Scan the ISA bus interrupt lines and return a bitmap of * active interrupts. The interrupt probe logic state is then * returned to its previous value. * * The IRQ probe semaphore is released and the cookie destroyed; * irq_probe_off must not be called. */ unsigned int irq_probe_mask(void *cookie); /** * irq_probe_off - end an interrupt autodetect * @cookie: cookie returned from irq_probe_on * * Scans the unused interrupt lines and returns the irq descriptor * which appears to have triggered the interrupt. If no interrupt * was found then NULL is returned. * * The IRQ probe semaphore is released and the cookie destroyed; * irq_probe_off must not be called. */ struct irq *irq_probe_off(void *cookie); ------------------------------------------------------------------------------ Example backwards-compatible IRQ code. ====================================== The following example code shows the expected back-compat code required for the x86 architecture. Since x86 uses a fixed table of interrupts, this is relatively straight forward and simple. static inline void disable_irq_nosync(int inr) { irq_disable_nosync(irq_desc + inr); } static inline void disable_irq(int inr) { irq_disable(irq_desc + inr); } static inline void enable_irq(int inr) { irq_enable(irq_desc + inr); } static inline int request_irq(int inr, irqreturn_t (*handler)(int, void *, struct pt_regs *), unsigned long irqflags, const char * devname, void *dev_id) { struct irq *irq = irq_desc + inr; int ret; ret = irq_request(irq, irqflags, devname, dev_id); if (ret == 0) { /* * irq_register_legacy is only implemented for * implementations providing this compatible * interface. */ ret = irq_register_legacy(irq, handler, dev_id); if (ret) irq_release(irq, dev_id); } return ret; } static inline void free_irq(int inr, void *dev_id) { irq_release(irq_desc + inr, dev_id); } static inline unsigned long probe_irq_on(void) { return (unsigned long)irq_probe_on(); } static inline unsigned int probe_irq_mask(unsigned long mask) { return irq_probe_mask((void *)mask); } static inline unsigned int probe_irq_off(unsigned long mask) { struct irq *irq = irq_probe_off((void *)mask); return irq ? irq - irq_desc : 0; } - o O o -